serv_rssclient.c: use array instead of linked list to avoid crashy bug
[citadel.git] / libcitadel / lib / stringbuf.c
1 // Copyright (c) 1987-2023 by the citadel.org team
2 //
3 // This program is open source software.  Use, duplication, or disclosure
4 // is subject to the terms of the GNU General Public License, version 3.
5
6 #define _GNU_SOURCE
7 #include "sysdep.h"
8 #include <ctype.h>
9 #include <errno.h>
10 #include <string.h>
11 #include <unistd.h>
12 #include <string.h>
13 #include <stdio.h>
14 #include <sys/select.h>
15 #include <fcntl.h>
16 #include <sys/types.h>
17 #define SHOW_ME_VAPPEND_PRINTF
18 #include <stdarg.h>
19
20 #include "libcitadel.h"
21
22 #ifdef HAVE_ICONV
23 #include <iconv.h>
24 #endif
25
26 #ifdef HAVE_BACKTRACE
27 #include <execinfo.h>
28 #endif
29
30 #ifdef UNDEF_MEMCPY
31 #undef memcpy
32 #endif
33
34 #ifdef HAVE_ZLIB
35 #include <zlib.h>
36 int ZEXPORT compress_gzip(Bytef * dest, size_t * destLen, const Bytef * source, uLong sourceLen, int level);
37 #endif
38 int BaseStrBufSize = 64;
39 int EnableSplice = 0;
40 int ZLibCompressionRatio = -1; /* defaults to 6 */
41 #ifdef HAVE_ZLIB
42 #define DEF_MEM_LEVEL 8 /*< memlevel??? */
43 #define OS_CODE 0x03    /*< unix */
44 const int gz_magic[2] = { 0x1f, 0x8b }; /* gzip magic header */
45 #endif
46
47 const char *StrBufNOTNULL = ((char*) NULL) - 1;
48
49 const char HexList[256][3] = {
50         "00","01","02","03","04","05","06","07","08","09","0A","0B","0C","0D","0E","0F",
51         "10","11","12","13","14","15","16","17","18","19","1A","1B","1C","1D","1E","1F",
52         "20","21","22","23","24","25","26","27","28","29","2A","2B","2C","2D","2E","2F",
53         "30","31","32","33","34","35","36","37","38","39","3A","3B","3C","3D","3E","3F",
54         "40","41","42","43","44","45","46","47","48","49","4A","4B","4C","4D","4E","4F",
55         "50","51","52","53","54","55","56","57","58","59","5A","5B","5C","5D","5E","5F",
56         "60","61","62","63","64","65","66","67","68","69","6A","6B","6C","6D","6E","6F",
57         "70","71","72","73","74","75","76","77","78","79","7A","7B","7C","7D","7E","7F",
58         "80","81","82","83","84","85","86","87","88","89","8A","8B","8C","8D","8E","8F",
59         "90","91","92","93","94","95","96","97","98","99","9A","9B","9C","9D","9E","9F",
60         "A0","A1","A2","A3","A4","A5","A6","A7","A8","A9","AA","AB","AC","AD","AE","AF",
61         "B0","B1","B2","B3","B4","B5","B6","B7","B8","B9","BA","BB","BC","BD","BE","BF",
62         "C0","C1","C2","C3","C4","C5","C6","C7","C8","C9","CA","CB","CC","CD","CE","CF",
63         "D0","D1","D2","D3","D4","D5","D6","D7","D8","D9","DA","DB","DC","DD","DE","DF",
64         "E0","E1","E2","E3","E4","E5","E6","E7","E8","E9","EA","EB","EC","ED","EE","EF",
65         "F0","F1","F2","F3","F4","F5","F6","F7","F8","F9","FA","FB","FC","FD","FE","FF"};
66
67
68 // Private Structure for the Stringbuffer
69 struct StrBuf {
70         char *buf;              // the pointer to the dynamic buffer
71         long BufSize;           // how many spcae do we optain
72         long BufUsed;           // Number of Chars used excluding the trailing \\0
73         int ConstBuf;           // are we just a wrapper arround a static buffer and musn't we be changed?
74 #ifdef SIZE_DEBUG
75         long nIncreases;        // for profiling; cound how many times we needed more
76         char bt [SIZ];          // Stacktrace of last increase
77         char bt_lastinc[SIZ];   // How much did we increase last time?
78 #endif
79 };
80
81
82 static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE);
83 static inline int Ctdl_IsUtf8SequenceStart(const char Char);
84
85 #ifdef SIZE_DEBUG
86 #ifdef HAVE_BACKTRACE
87 static void StrBufBacktrace(StrBuf *Buf, int which) {
88         int n;
89         char *pstart, *pch;
90         void *stack_frames[50];
91         size_t size, i;
92         char **strings;
93
94         if (which) {
95                 pstart = pch = Buf->bt;
96         }
97         else {
98                 pstart = pch = Buf->bt_lastinc;
99         }
100         size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
101         strings = backtrace_symbols(stack_frames, size);
102         for (i = 0; i < size; i++) {
103                 if (strings != NULL) {
104                         n = snprintf(pch, SIZ - (pch - pstart), "%s\\n", strings[i]);
105                 }
106                 else {
107                         n = snprintf(pch, SIZ - (pch - pstart), "%p\\n", stack_frames[i]);
108                 }
109                 pch += n;
110         }
111         free(strings);
112 }
113 #endif
114
115
116 void dbg_FreeStrBuf(StrBuf *FreeMe, char *FromWhere) {
117         if (hFreeDbglog == -1) {
118                 pid_t pid = getpid();
119                 char path [SIZ];
120                 snprintf(path, SIZ, "/tmp/libcitadel_strbuf_realloc.log.%d", pid);
121                 hFreeDbglog = open(path, O_APPEND|O_CREAT|O_WRONLY);
122         }
123         if ((*FreeMe)->nIncreases > 0) {
124                 char buf[SIZ * 3];
125                 long n;
126                 n = snprintf(buf, SIZ * 3, "%c+|%ld|%ld|%ld|%s|%s|\n",
127                              FromWhere,
128                              (*FreeMe)->nIncreases,
129                              (*FreeMe)->BufUsed,
130                              (*FreeMe)->BufSize,
131                              (*FreeMe)->bt,
132                              (*FreeMe)->bt_lastinc);
133                 n = write(hFreeDbglog, buf, n);
134         }
135         else {
136                 char buf[128];
137                 long n;
138                 n = snprintf(buf, 128, "%c_|0|%ld%ld|\n",
139                              FromWhere,
140                              (*FreeMe)->BufUsed,
141                              (*FreeMe)->BufSize);
142                 n = write(hFreeDbglog, buf, n);
143         }
144 }
145
146
147 void dbg_IncreaseBuf(StrBuf *IncMe) {
148         Buf->nIncreases++;
149 #ifdef HAVE_BACKTRACE
150         StrBufBacktrace(Buf, 1);
151 #endif
152 }
153
154
155 void dbg_Init(StrBuf *Buf) {
156         Buf->nIncreases = 0;
157         Buf->bt[0] = '\0';
158         Buf->bt_lastinc[0] = '\0';
159 #ifdef HAVE_BACKTRACE
160         StrBufBacktrace(Buf, 0);
161 #endif
162 }
163
164 #else
165 /* void it... */
166 #define dbg_FreeStrBuf(a, b)
167 #define dbg_IncreaseBuf(a)
168 #define dbg_Init(a)
169
170 #endif
171
172 /*
173  *  swaps the contents of two StrBufs
174  * this is to be used to have cheap switched between a work-buffer and a target buffer 
175  *  A First one
176  *  B second one
177  */
178 static inline void iSwapBuffers(StrBuf *A, StrBuf *B) {
179         StrBuf C;
180
181         memcpy(&C, A, sizeof(*A));
182         memcpy(A, B, sizeof(*B));
183         memcpy(B, &C, sizeof(C));
184
185 }
186
187
188 void SwapBuffers(StrBuf *A, StrBuf *B) {
189         iSwapBuffers(A, B);
190 }
191
192
193 /* 
194  *  Cast operator to Plain String 
195  * @note if the buffer is altered by StrBuf operations, this pointer may become 
196  *  invalid. So don't lean on it after altering the buffer!
197  *  Since this operation is considered cheap, rather call it often than risking
198  *  your pointer to become invalid!
199  *  Str the string we want to get the c-string representation for
200  * @returns the Pointer to the Content. Don't mess with it!
201  */
202 inline const char *ChrPtr(const StrBuf *Str) {
203         if (Str == NULL)
204                 return "";
205         return Str->buf;
206 }
207
208
209 /*
210  *  since we know strlen()'s result, provide it here.
211  *  Str the string to return the length to
212  * @returns contentlength of the buffer
213  */
214 inline int StrLength(const StrBuf *Str) {
215         return (Str != NULL) ? Str->BufUsed : 0;
216 }
217
218
219 // local utility function to resize the buffer
220 // Buf          the buffer whichs storage we should increase
221 // KeepOriginal should we copy the original buffer or just start over with a new one
222 // DestSize     what should fit in after?
223 static int IncreaseBuf(StrBuf *Buf, int KeepOriginal, int DestSize) {
224         char *NewBuf;
225         size_t NewSize = Buf->BufSize * 2;
226
227         if (Buf->ConstBuf) {
228                 return -1;
229         }
230                 
231         if (DestSize > 0) {
232                 while ((NewSize <= DestSize) && (NewSize != 0)) {
233                         NewSize *= 2;
234                 }
235         }
236
237         if (NewSize == 0) {
238                 return -1;
239         }
240
241         NewBuf = (char*) malloc(NewSize);
242         if (NewBuf == NULL) {
243                 return -1;
244         }
245
246         if (KeepOriginal && (Buf->BufUsed > 0)) {
247                 memcpy(NewBuf, Buf->buf, Buf->BufUsed);
248         }
249         else {
250                 NewBuf[0] = '\0';
251                 Buf->BufUsed = 0;
252         }
253         free (Buf->buf);
254         Buf->buf = NewBuf;
255         Buf->BufSize = NewSize;
256
257         dbg_IncreaseBuf(Buf);
258
259         return Buf->BufSize;
260 }
261
262
263 // shrink / increase an _EMPTY_ buffer to NewSize. Buffercontent is thoroughly ignored and flushed.
264 // Buf          Buffer to shrink (has to be empty)
265 // ThreshHold   if the buffer is bigger then this, its readjusted
266 // NewSize      if we Shrink it, how big are we going to be afterwards?
267 void ReAdjustEmptyBuf(StrBuf *Buf, long ThreshHold, long NewSize) {
268         if ((Buf != NULL) && (Buf->BufUsed == 0) && (Buf->BufSize < ThreshHold)) {
269                 free(Buf->buf);
270                 Buf->buf = (char*) malloc(NewSize);
271                 Buf->BufUsed = 0;
272                 Buf->BufSize = NewSize;
273         }
274 }
275
276
277 /*
278  *  shrink long term buffers to their real size so they don't waste memory
279  *  Buf buffer to shrink
280  *  Force if not set, will just executed if the buffer is much to big; set for lifetime strings
281  * @returns physical size of the buffer
282  */
283 long StrBufShrinkToFit(StrBuf *Buf, int Force) {
284         if (Buf == NULL)
285                 return -1;
286         if (Force || (Buf->BufUsed + (Buf->BufUsed / 3) > Buf->BufSize)) {
287                 char *TmpBuf;
288
289                 TmpBuf = (char*) malloc(Buf->BufUsed + 1);
290                 if (TmpBuf == NULL)
291                         return -1;
292
293                 memcpy (TmpBuf, Buf->buf, Buf->BufUsed + 1);
294                 Buf->BufSize = Buf->BufUsed + 1;
295                 free(Buf->buf);
296                 Buf->buf = TmpBuf;
297         }
298         return Buf->BufUsed;
299 }
300
301
302 /*
303  *  Allocate a new buffer with default buffer size
304  * @returns the new stringbuffer
305  */
306 StrBuf *NewStrBuf(void) {
307         StrBuf *NewBuf;
308
309         NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
310         if (NewBuf == NULL)
311                 return NULL;
312
313         NewBuf->buf = (char*) malloc(BaseStrBufSize);
314         if (NewBuf->buf == NULL) {
315                 free(NewBuf);
316                 return NULL;
317         }
318         NewBuf->buf[0] = '\0';
319         NewBuf->BufSize = BaseStrBufSize;
320         NewBuf->BufUsed = 0;
321         NewBuf->ConstBuf = 0;
322
323         dbg_Init (NewBuf);
324         return NewBuf;
325 }
326
327
328 /* 
329  *  Copy Constructor; returns a duplicate of CopyMe
330  *  CopyMe Buffer to faxmilate
331  * @returns the new stringbuffer
332  */
333 StrBuf *NewStrBufDup(const StrBuf *CopyMe) {
334         StrBuf *NewBuf;
335         
336         if (CopyMe == NULL)
337                 return NewStrBuf();
338
339         NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
340         if (NewBuf == NULL)
341                 return NULL;
342
343         NewBuf->buf = (char*) malloc(CopyMe->BufSize);
344         if (NewBuf->buf == NULL) {
345                 free(NewBuf);
346                 return NULL;
347         }
348
349         memcpy(NewBuf->buf, CopyMe->buf, CopyMe->BufUsed + 1);
350         NewBuf->BufUsed = CopyMe->BufUsed;
351         NewBuf->BufSize = CopyMe->BufSize;
352         NewBuf->ConstBuf = 0;
353
354         dbg_Init(NewBuf);
355
356         return NewBuf;
357 }
358
359
360 /* 
361  *  Copy Constructor; CreateRelpaceMe will contain CopyFlushMe afterwards.
362  *  NoMe if non-NULL, we will use that buffer as value; KeepOriginal will abused as len.
363  *  CopyFlushMe Buffer to faxmilate if KeepOriginal, or to move into CreateRelpaceMe if !KeepOriginal.
364  *  CreateRelpaceMe If NULL, will be created, else Flushed and filled CopyFlushMe 
365  *  KeepOriginal should CopyFlushMe remain intact? or may we Steal its buffer?
366  * @returns the new stringbuffer
367  */
368 void NewStrBufDupAppendFlush(StrBuf **CreateRelpaceMe, StrBuf *CopyFlushMe, const char *NoMe, int KeepOriginal) {
369         StrBuf *NewBuf;
370         
371         if (CreateRelpaceMe == NULL)
372                 return;
373
374         if (NoMe != NULL) {
375                 if (*CreateRelpaceMe != NULL)
376                         StrBufPlain(*CreateRelpaceMe, NoMe, KeepOriginal);
377                 else 
378                         *CreateRelpaceMe = NewStrBufPlain(NoMe, KeepOriginal);
379                 return;
380         }
381
382         if (CopyFlushMe == NULL) {
383                 if (*CreateRelpaceMe != NULL)
384                         FlushStrBuf(*CreateRelpaceMe);
385                 else 
386                         *CreateRelpaceMe = NewStrBuf();
387                 return;
388         }
389
390         /* 
391          * Randomly Chosen: bigger than 64 chars is cheaper to swap the buffers instead of copying.
392          * else *CreateRelpaceMe may use more memory than needed in a longer term, CopyFlushMe might
393          * be a big IO-Buffer...
394          */
395         if (KeepOriginal || (StrLength(CopyFlushMe) < 256)) {
396                 if (*CreateRelpaceMe == NULL) {
397                         *CreateRelpaceMe = NewBuf = NewStrBufPlain(NULL, CopyFlushMe->BufUsed);
398                         dbg_Init(NewBuf);
399                 }
400                 else {
401                         NewBuf = *CreateRelpaceMe;
402                         FlushStrBuf(NewBuf);
403                 }
404                 StrBufAppendBuf(NewBuf, CopyFlushMe, 0);
405         }
406         else {
407                 if (*CreateRelpaceMe == NULL) {
408                         *CreateRelpaceMe = NewBuf = NewStrBufPlain(NULL, CopyFlushMe->BufUsed);
409                         dbg_Init(NewBuf);
410                 }
411                 else  {
412                         NewBuf = *CreateRelpaceMe;
413                 }
414                 iSwapBuffers (NewBuf, CopyFlushMe);
415         }
416         if (!KeepOriginal) {
417                 FlushStrBuf(CopyFlushMe);
418         }
419         return;
420 }
421
422
423 /*
424  *  create a new Buffer using an existing c-string
425  * this function should also be used if you want to pre-suggest
426  * the buffer size to allocate in conjunction with ptr == NULL
427  *  ptr the c-string to copy; may be NULL to create a blank instance
428  *  nChars How many chars should we copy; -1 if we should measure the length ourselves
429  * @returns the new stringbuffer
430  */
431 StrBuf *NewStrBufPlain(const char* ptr, int nChars) {
432         StrBuf *NewBuf;
433         size_t Siz = BaseStrBufSize;
434         size_t CopySize;
435
436         NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
437         if (NewBuf == NULL)
438                 return NULL;
439
440         if (nChars < 0)
441                 CopySize = strlen((ptr != NULL)?ptr:"");
442         else
443                 CopySize = nChars;
444
445         while ((Siz <= CopySize) && (Siz != 0))
446                 Siz *= 2;
447
448         if (Siz == 0) {
449                 free(NewBuf);
450                 return NULL;
451         }
452
453         NewBuf->buf = (char*) malloc(Siz);
454         if (NewBuf->buf == NULL) {
455                 free(NewBuf);
456                 return NULL;
457         }
458         NewBuf->BufSize = Siz;
459         if (ptr != NULL) {
460                 memcpy(NewBuf->buf, ptr, CopySize);
461                 NewBuf->buf[CopySize] = '\0';
462                 NewBuf->BufUsed = CopySize;
463         }
464         else {
465                 NewBuf->buf[0] = '\0';
466                 NewBuf->BufUsed = 0;
467         }
468         NewBuf->ConstBuf = 0;
469
470         dbg_Init(NewBuf);
471
472         return NewBuf;
473 }
474
475
476 /*
477  *  Set an existing buffer from a c-string
478  *  Buf buffer to load
479  *  ptr c-string to put into 
480  *  nChars set to -1 if we should work 0-terminated
481  * @returns the new length of the string
482  */
483 int StrBufPlain(StrBuf *Buf, const char* ptr, int nChars) {
484         size_t Siz;
485         size_t CopySize;
486
487         if (Buf == NULL)
488                 return -1;
489         if (ptr == NULL) {
490                 FlushStrBuf(Buf);
491                 return -1;
492         }
493
494         Siz = Buf->BufSize;
495
496         if (nChars < 0)
497                 CopySize = strlen(ptr);
498         else
499                 CopySize = nChars;
500
501         while ((Siz <= CopySize) && (Siz != 0))
502                 Siz *= 2;
503
504         if (Siz == 0) {
505                 FlushStrBuf(Buf);
506                 return -1;
507         }
508
509         if (Siz != Buf->BufSize)
510                 IncreaseBuf(Buf, 0, Siz);
511         memcpy(Buf->buf, ptr, CopySize);
512         Buf->buf[CopySize] = '\0';
513         Buf->BufUsed = CopySize;
514         Buf->ConstBuf = 0;
515         return CopySize;
516 }
517
518
519 /*
520  *  use strbuf as wrapper for a string constant for easy handling
521  *  StringConstant a string to wrap
522  *  SizeOfStrConstant should be sizeof(StringConstant)-1
523  */
524 StrBuf* _NewConstStrBuf(const char* StringConstant, size_t SizeOfStrConstant)
525 {
526         StrBuf *NewBuf;
527
528         NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
529         if (NewBuf == NULL)
530                 return NULL;
531         NewBuf->buf = (char*) StringConstant;
532         NewBuf->BufSize = SizeOfStrConstant;
533         NewBuf->BufUsed = SizeOfStrConstant;
534         NewBuf->ConstBuf = 1;
535
536         dbg_Init(NewBuf);
537
538         return NewBuf;
539 }
540
541
542 /*
543  *  flush the content of a Buf; keep its struct
544  *  buf Buffer to flush
545  */
546 int FlushStrBuf(StrBuf *buf)
547 {
548         if ((buf == NULL) || (buf->buf == NULL))
549                 return -1;
550         if (buf->ConstBuf)
551                 return -1;       
552         buf->buf[0] ='\0';
553         buf->BufUsed = 0;
554         return 0;
555 }
556
557 /*
558  *  wipe the content of a Buf thoroughly (overwrite it -> expensive); keep its struct
559  *  buf Buffer to wipe
560  */
561 int FLUSHStrBuf(StrBuf *buf)
562 {
563         if (buf == NULL)
564                 return -1;
565         if (buf->ConstBuf)
566                 return -1;
567         if (buf->BufUsed > 0) {
568                 memset(buf->buf, 0, buf->BufUsed);
569                 buf->BufUsed = 0;
570         }
571         return 0;
572 }
573
574 #ifdef SIZE_DEBUG
575 int hFreeDbglog = -1;
576 #endif
577 /*
578  *  Release a Buffer
579  * Its a double pointer, so it can NULL your pointer
580  * so fancy SIG11 appear instead of random results
581  *  FreeMe Pointer Pointer to the buffer to free
582  */
583 void FreeStrBuf (StrBuf **FreeMe)
584 {
585         if (*FreeMe == NULL)
586                 return;
587
588         dbg_FreeStrBuf(FreeMe, 'F');
589
590         if (!(*FreeMe)->ConstBuf) 
591                 free((*FreeMe)->buf);
592         free(*FreeMe);
593         *FreeMe = NULL;
594 }
595
596 /*
597  *  flatten a Buffer to the Char * we return 
598  * Its a double pointer, so it can NULL your pointer
599  * so fancy SIG11 appear instead of random results
600  * The Callee then owns the buffer and is responsible for freeing it.
601  *  SmashMe Pointer Pointer to the buffer to release Buf from and free
602  * @returns the pointer of the buffer; Callee owns the memory thereafter.
603  */
604 char *SmashStrBuf (StrBuf **SmashMe) {
605         char *Ret;
606
607         if ((SmashMe == NULL) || (*SmashMe == NULL))
608                 return NULL;
609         
610         dbg_FreeStrBuf(SmashMe, 'S');
611
612         Ret = (*SmashMe)->buf;
613         free(*SmashMe);
614         *SmashMe = NULL;
615         return Ret;
616 }
617
618
619 /*
620  *  Release the buffer
621  * If you want put your StrBuf into a Hash, use this as Destructor.
622  *  VFreeMe untyped pointer to a StrBuf. be shure to do the right thing [TM]
623  */
624 void HFreeStrBuf (void *VFreeMe) {
625         StrBuf *FreeMe = (StrBuf*)VFreeMe;
626         if (FreeMe == NULL)
627                 return;
628
629         dbg_FreeStrBuf(SmashMe, 'H');
630
631         if (!FreeMe->ConstBuf) 
632                 free(FreeMe->buf);
633         free(FreeMe);
634 }
635
636
637 /*******************************************************************************
638  *                      Simple string transformations                          *
639  *******************************************************************************/
640
641 /*
642  *  Wrapper around atol
643  */
644 long StrTol(const StrBuf *Buf)
645 {
646         if (Buf == NULL)
647                 return 0;
648         if(Buf->BufUsed > 0)
649                 return atol(Buf->buf);
650         else
651                 return 0;
652 }
653
654 /*
655  *  Wrapper around atoi
656  */
657 int StrToi(const StrBuf *Buf)
658 {
659         if (Buf == NULL)
660                 return 0;
661         if (Buf->BufUsed > 0)
662                 return atoi(Buf->buf);
663         else
664                 return 0;
665 }
666
667 /*
668  *  Checks to see if the string is a pure number 
669  *  Buf The buffer to inspect
670  * @returns 1 if its a pure number, 0, if not.
671  */
672 int StrBufIsNumber(const StrBuf *Buf) {
673         char * pEnd;
674         if ((Buf == NULL) || (Buf->BufUsed == 0)) {
675                 return 0;
676         }
677         strtoll(Buf->buf, &pEnd, 10);
678         if (pEnd == Buf->buf)
679                 return 0;
680         if ((pEnd != NULL) && (pEnd == Buf->buf + Buf->BufUsed))
681                 return 1;
682         if (Buf->buf == pEnd)
683                 return 0;
684         return 0;
685
686
687 /**
688  *  modifies a Single char of the Buf
689  * You can point to it via char* or a zero-based integer
690  *  Buf The buffer to manipulate
691  *  ptr char* to zero; use NULL if unused
692  *  nThChar zero based pointer into the string; use -1 if unused
693  *  PeekValue The Character to place into the position
694  */
695 long StrBufPeek(StrBuf *Buf, const char* ptr, long nThChar, char PeekValue)
696 {
697         if (Buf == NULL)
698                 return -1;
699         if (ptr != NULL)
700                 nThChar = ptr - Buf->buf;
701         if ((nThChar < 0) || (nThChar > Buf->BufUsed))
702                 return -1;
703         Buf->buf[nThChar] = PeekValue;
704         return nThChar;
705 }
706
707 /**
708  *  modifies a range of chars of the Buf
709  * You can point to it via char* or a zero-based integer
710  *  Buf The buffer to manipulate
711  *  ptr char* to zero; use NULL if unused
712  *  nThChar zero based pointer into the string; use -1 if unused
713  *  nChars how many chars are to be flushed?
714  *  PookValue The Character to place into that area
715  */
716 long StrBufPook(StrBuf *Buf, const char* ptr, long nThChar, long nChars, char PookValue)
717 {
718         if (Buf == NULL)
719                 return -1;
720         if (ptr != NULL)
721                 nThChar = ptr - Buf->buf;
722         if ((nThChar < 0) || (nThChar > Buf->BufUsed))
723                 return -1;
724         if (nThChar + nChars > Buf->BufUsed)
725                 nChars =  Buf->BufUsed - nThChar;
726
727         memset(Buf->buf + nThChar, PookValue, nChars);
728         /* just to be shure... */
729         Buf->buf[Buf->BufUsed] = 0;
730         return nChars;
731 }
732
733 /**
734  *  Append a StringBuffer to the buffer
735  *  Buf Buffer to modify
736  *  AppendBuf Buffer to copy at the end of our buffer
737  *  Offset Should we start copying from an offset?
738  */
739 void StrBufAppendBuf(StrBuf *Buf, const StrBuf *AppendBuf, unsigned long Offset)
740 {
741         if ((AppendBuf == NULL) || (AppendBuf->buf == NULL) ||
742             (Buf == NULL) || (Buf->buf == NULL))
743                 return;
744
745         if (Buf->BufSize - Offset < AppendBuf->BufUsed + Buf->BufUsed + 1)
746                 IncreaseBuf(Buf, (Buf->BufUsed > 0), AppendBuf->BufUsed + Buf->BufUsed);
747
748         memcpy(Buf->buf + Buf->BufUsed, AppendBuf->buf + Offset, AppendBuf->BufUsed - Offset);
749         Buf->BufUsed += AppendBuf->BufUsed - Offset;
750         Buf->buf[Buf->BufUsed] = '\0';
751 }
752
753
754 // Append a C-String to the buffer
755 // Buf          Buffer to modify
756 // AppendBuf    Buffer to copy at the end of our buffer
757 // AppendSize   number of bytes to copy; set to -1 if we should count it in advance
758 // Offset       Should we start copying from an offset?
759 void StrBufAppendBufPlain(StrBuf *Buf, const char *AppendBuf, long AppendSize, unsigned long Offset) {
760         long aps;
761         long BufSizeRequired;
762
763         if ((AppendBuf == NULL) || (Buf == NULL))
764                 return;
765
766         if (AppendSize < 0) {
767                 aps = strlen(AppendBuf + Offset);
768         }
769         else {
770                 aps = AppendSize - Offset;
771         }
772
773         BufSizeRequired = Buf->BufUsed + aps + 1;
774         if (Buf->BufSize <= BufSizeRequired) {
775                 IncreaseBuf(Buf, (Buf->BufUsed > 0), BufSizeRequired);
776         }
777
778         memcpy(Buf->buf + Buf->BufUsed, 
779                AppendBuf + Offset, 
780                aps);
781         Buf->BufUsed += aps;
782         Buf->buf[Buf->BufUsed] = '\0';
783 }
784
785 /*
786  *  sprintf like function appending the formated string to the buffer
787  * vsnprintf version to wrap into own calls
788  *  Buf Buffer to extend by format and Params
789  *  format printf alike format to add
790  *  ap va_list containing the items for format
791  */
792 void StrBufVAppendPrintf(StrBuf *Buf, const char *format, va_list ap)
793 {
794         va_list apl;
795         size_t BufSize;
796         size_t nWritten;
797         size_t Offset;
798         size_t newused;
799
800         if ((Buf == NULL)  || (format == NULL))
801                 return;
802
803         BufSize = Buf->BufSize;
804         nWritten = Buf->BufSize + 1;
805         Offset = Buf->BufUsed;
806         newused = Offset + nWritten;
807         
808         while (newused >= BufSize) {
809                 va_copy(apl, ap);
810                 nWritten = vsnprintf(Buf->buf + Offset, 
811                                      Buf->BufSize - Offset, 
812                                      format, apl);
813                 va_end(apl);
814                 newused = Offset + nWritten;
815                 if (newused >= Buf->BufSize) {
816                         if (IncreaseBuf(Buf, 1, newused) == -1)
817                                 return; /* TODO: error handling? */
818                         newused = Buf->BufSize + 1;
819                 }
820                 else {
821                         Buf->BufUsed = Offset + nWritten;
822                         BufSize = Buf->BufSize;
823                 }
824
825         }
826 }
827
828 /**
829  *  sprintf like function appending the formated string to the buffer
830  *  Buf Buffer to extend by format and Params
831  *  format printf alike format to add
832  */
833 void StrBufAppendPrintf(StrBuf *Buf, const char *format, ...)
834 {
835         size_t BufSize;
836         size_t nWritten;
837         size_t Offset;
838         size_t newused;
839         va_list arg_ptr;
840         
841         if ((Buf == NULL)  || (format == NULL))
842                 return;
843
844         BufSize = Buf->BufSize;
845         nWritten = Buf->BufSize + 1;
846         Offset = Buf->BufUsed;
847         newused = Offset + nWritten;
848
849         while (newused >= BufSize) {
850                 va_start(arg_ptr, format);
851                 nWritten = vsnprintf(Buf->buf + Buf->BufUsed, 
852                                      Buf->BufSize - Buf->BufUsed, 
853                                      format, arg_ptr);
854                 va_end(arg_ptr);
855                 newused = Buf->BufUsed + nWritten;
856                 if (newused >= Buf->BufSize) {
857                         if (IncreaseBuf(Buf, 1, newused) == -1)
858                                 return; /* TODO: error handling? */
859                         newused = Buf->BufSize + 1;
860                 }
861                 else {
862                         Buf->BufUsed += nWritten;
863                         BufSize = Buf->BufSize;
864                 }
865
866         }
867 }
868
869 /**
870  *  sprintf like function putting the formated string into the buffer
871  *  Buf Buffer to extend by format and Parameters
872  *  format printf alike format to add
873  */
874 void StrBufPrintf(StrBuf *Buf, const char *format, ...)
875 {
876         size_t nWritten;
877         va_list arg_ptr;
878         
879         if ((Buf == NULL)  || (format == NULL))
880                 return;
881
882         nWritten = Buf->BufSize + 1;
883         while (nWritten >= Buf->BufSize) {
884                 va_start(arg_ptr, format);
885                 nWritten = vsnprintf(Buf->buf, Buf->BufSize, format, arg_ptr);
886                 va_end(arg_ptr);
887                 if (nWritten >= Buf->BufSize) {
888                         if (IncreaseBuf(Buf, 0, 0) == -1)
889                                 return; /* TODO: error handling? */
890                         nWritten = Buf->BufSize + 1;
891                         continue;
892                 }
893                 Buf->BufUsed = nWritten ;
894         }
895 }
896
897 /**
898  *  Callback for cURL to append the webserver reply to a buffer
899  *  ptr pre-defined by the cURL API; see man 3 curl for mre info
900  *  size pre-defined by the cURL API; see man 3 curl for mre info
901  *  nmemb pre-defined by the cURL API; see man 3 curl for mre info
902  *  stream pre-defined by the cURL API; see man 3 curl for mre info
903  */
904 size_t CurlFillStrBuf_callback(void *ptr, size_t size, size_t nmemb, void *stream)
905 {
906
907         StrBuf *Target;
908
909         Target = stream;
910         if (ptr == NULL)
911                 return 0;
912
913         StrBufAppendBufPlain(Target, ptr, size * nmemb, 0);
914         return size * nmemb;
915 }
916
917
918 /**
919  *  extracts a substring from Source into dest
920  *  dest buffer to place substring into
921  *  Source string to copy substring from
922  *  Offset chars to skip from start
923  *  nChars number of chars to copy
924  * @returns the number of chars copied; may be different from nChars due to the size of Source
925  */
926 int StrBufSub(StrBuf *dest, const StrBuf *Source, unsigned long Offset, size_t nChars)
927 {
928         size_t NCharsRemain;
929         if (Offset > Source->BufUsed)
930         {
931                 if (dest != NULL)
932                         FlushStrBuf(dest);
933                 return 0;
934         }
935         if (Offset + nChars < Source->BufUsed)
936         {
937                 if ((nChars >= dest->BufSize) && 
938                     (IncreaseBuf(dest, 0, nChars + 1) == -1))
939                         return 0;
940                 memcpy(dest->buf, Source->buf + Offset, nChars);
941                 dest->BufUsed = nChars;
942                 dest->buf[dest->BufUsed] = '\0';
943                 return nChars;
944         }
945         NCharsRemain = Source->BufUsed - Offset;
946         if ((NCharsRemain  >= dest->BufSize) && 
947             (IncreaseBuf(dest, 0, NCharsRemain + 1) == -1))
948                 return 0;
949         memcpy(dest->buf, Source->buf + Offset, NCharsRemain);
950         dest->BufUsed = NCharsRemain;
951         dest->buf[dest->BufUsed] = '\0';
952         return NCharsRemain;
953 }
954
955 /**
956  *  Cut nChars from the start of the string
957  *  Buf Buffer to modify
958  *  nChars how many chars should be skipped?
959  */
960 void StrBufCutLeft(StrBuf *Buf, int nChars)
961 {
962         if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
963         if (nChars >= Buf->BufUsed) {
964                 FlushStrBuf(Buf);
965                 return;
966         }
967         memmove(Buf->buf, Buf->buf + nChars, Buf->BufUsed - nChars);
968         Buf->BufUsed -= nChars;
969         Buf->buf[Buf->BufUsed] = '\0';
970 }
971
972 /**
973  *  Cut the trailing n Chars from the string
974  *  Buf Buffer to modify
975  *  nChars how many chars should be trunkated?
976  */
977 void StrBufCutRight(StrBuf *Buf, int nChars)
978 {
979         if ((Buf == NULL) || (Buf->BufUsed == 0) || (Buf->buf == NULL))
980                 return;
981
982         if (nChars >= Buf->BufUsed) {
983                 FlushStrBuf(Buf);
984                 return;
985         }
986         Buf->BufUsed -= nChars;
987         Buf->buf[Buf->BufUsed] = '\0';
988 }
989
990 /**
991  *  Cut the string after n Chars
992  *  Buf Buffer to modify
993  *  AfternChars after how many chars should we trunkate the string?
994  *  At if non-null and points inside of our string, cut it there.
995  */
996 void StrBufCutAt(StrBuf *Buf, int AfternChars, const char *At)
997 {
998         if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
999         if (At != NULL){
1000                 AfternChars = At - Buf->buf;
1001         }
1002
1003         if ((AfternChars < 0) || (AfternChars >= Buf->BufUsed))
1004                 return;
1005         Buf->BufUsed = AfternChars;
1006         Buf->buf[Buf->BufUsed] = '\0';
1007 }
1008
1009
1010 /**
1011  *  Strip leading and trailing spaces from a string; with premeasured and adjusted length.
1012  *  Buf the string to modify
1013  */
1014 void StrBufTrim(StrBuf *Buf)
1015 {
1016         int delta = 0;
1017         if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1018
1019         while ((Buf->BufUsed > 0) &&
1020                isspace(Buf->buf[Buf->BufUsed - 1]))
1021         {
1022                 Buf->BufUsed --;
1023         }
1024         Buf->buf[Buf->BufUsed] = '\0';
1025
1026         if (Buf->BufUsed == 0) return;
1027
1028         while ((Buf->BufUsed > delta) && (isspace(Buf->buf[delta]))){
1029                 delta ++;
1030         }
1031         if (delta > 0) StrBufCutLeft(Buf, delta);
1032 }
1033 /**
1034  *  changes all spaces in the string  (tab, linefeed...) to Blank (0x20)
1035  *  Buf the string to modify
1036  */
1037 void StrBufSpaceToBlank(StrBuf *Buf)
1038 {
1039         char *pche, *pch;
1040
1041         if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1042
1043         pch = Buf->buf;
1044         pche = pch + Buf->BufUsed;
1045         while (pch < pche) 
1046         {
1047                 if (isspace(*pch))
1048                         *pch = ' ';
1049                 pch ++;
1050         }
1051 }
1052
1053 void StrBufStripAllBut(StrBuf *Buf, char leftboundary, char rightboundary)
1054 {
1055         const char *pLeft;
1056         const char *pRight;
1057
1058         if ((Buf == NULL) || (Buf->buf == NULL)) {
1059                 return;
1060         }
1061
1062         pRight = strchr(Buf->buf, rightboundary);
1063         if (pRight != NULL) {
1064                 StrBufCutAt(Buf, 0, pRight);
1065         }
1066
1067         pLeft = strrchr(ChrPtr(Buf), leftboundary);
1068         if (pLeft != NULL) {
1069                 StrBufCutLeft(Buf, pLeft - Buf->buf + 1);
1070         }
1071 }
1072
1073
1074 /**
1075  *  uppercase the contents of a buffer
1076  *  Buf the buffer to translate
1077  */
1078 void StrBufUpCase(StrBuf *Buf) 
1079 {
1080         char *pch, *pche;
1081
1082         if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1083
1084         pch = Buf->buf;
1085         pche = pch + Buf->BufUsed;
1086         while (pch < pche) {
1087                 *pch = toupper(*pch);
1088                 pch ++;
1089         }
1090 }
1091
1092
1093 /**
1094  *  lowercase the contents of a buffer
1095  *  Buf the buffer to translate
1096  */
1097 void StrBufLowerCase(StrBuf *Buf) 
1098 {
1099         char *pch, *pche;
1100
1101         if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1102
1103         pch = Buf->buf;
1104         pche = pch + Buf->BufUsed;
1105         while (pch < pche) {
1106                 *pch = tolower(*pch);
1107                 pch ++;
1108         }
1109 }
1110
1111
1112 /*******************************************************************************
1113  *           a tokenizer that kills, maims, and destroys                       *
1114  *******************************************************************************/
1115
1116 /**
1117  *  Replace a token at a given place with a given length by another token with given length
1118  *  Buf String where to work on
1119  *  where where inside of the Buf is the search-token
1120  *  HowLong How long is the token to be replaced
1121  *  Repl Token to insert at 'where'
1122  *  ReplLen Length of repl
1123  * @returns -1 if fail else length of resulting Buf
1124  */
1125 int StrBufReplaceToken(StrBuf *Buf, long where, long HowLong, 
1126                        const char *Repl, long ReplLen)
1127 {
1128
1129         if ((Buf == NULL) || 
1130             (where > Buf->BufUsed) ||
1131             (where + HowLong > Buf->BufUsed))
1132                 return -1;
1133
1134         if (where + ReplLen - HowLong > Buf->BufSize)
1135                 if (IncreaseBuf(Buf, 1, Buf->BufUsed + ReplLen) < 0)
1136                         return -1;
1137
1138         memmove(Buf->buf + where + ReplLen, 
1139                 Buf->buf + where + HowLong,
1140                 Buf->BufUsed - where - HowLong);
1141                                                 
1142         memcpy(Buf->buf + where, 
1143                Repl, ReplLen);
1144
1145         Buf->BufUsed += ReplLen - HowLong;
1146
1147         return Buf->BufUsed;
1148 }
1149
1150 /**
1151  *  Counts the numbmer of tokens in a buffer
1152  *  source String to count tokens in
1153  *  tok    Tokenizer char to count
1154  * @returns numbers of tokenizer chars found
1155  */
1156 int StrBufNum_tokens(const StrBuf *source, char tok)
1157 {
1158         char *pch, *pche;
1159         long NTokens;
1160         if ((source == NULL) || (source->BufUsed == 0))
1161                 return 0;
1162         if ((source->BufUsed == 1) && (*source->buf == tok))
1163                 return 2;
1164         NTokens = 1;
1165         pch = source->buf;
1166         pche = pch + source->BufUsed;
1167         while (pch < pche)
1168         {
1169                 if (*pch == tok)
1170                         NTokens ++;
1171                 pch ++;
1172         }
1173         return NTokens;
1174 }
1175
1176 /**
1177  *  a string tokenizer
1178  *  Source StringBuffer to read into
1179  *  parmnum n'th Parameter to remove
1180  *  separator tokenizer character
1181  * @returns -1 if not found, else length of token.
1182  */
1183 int StrBufRemove_token(StrBuf *Source, int parmnum, char separator)
1184 {
1185         int ReducedBy;
1186         char *d, *s, *end;              /* dest, source */
1187         int count = 0;
1188
1189         /* Find desired eter */
1190         end = Source->buf + Source->BufUsed;
1191         d = Source->buf;
1192         while ((d <= end) && 
1193                (count < parmnum))
1194         {
1195                 /* End of string, bail! */
1196                 if (!*d) {
1197                         d = NULL;
1198                         break;
1199                 }
1200                 if (*d == separator) {
1201                         count++;
1202                 }
1203                 d++;
1204         }
1205         if ((d == NULL) || (d >= end))
1206                 return 0;               /* @Parameter not found */
1207
1208         /* Find next eter */
1209         s = d;
1210         while ((s <= end) && 
1211                (*s && *s != separator))
1212         {
1213                 s++;
1214         }
1215         if (*s == separator)
1216                 s++;
1217         ReducedBy = d - s;
1218
1219         /* Hack and slash */
1220         if (s >= end) {
1221                 return 0;
1222         }
1223         else if (*s) {
1224                 memmove(d, s, Source->BufUsed - (s - Source->buf));
1225                 Source->BufUsed += ReducedBy;
1226                 Source->buf[Source->BufUsed] = '\0';
1227         }
1228         else if (d == Source->buf) {
1229                 *d = 0;
1230                 Source->BufUsed = 0;
1231         }
1232         else {
1233                 *--d = '\0';
1234                 Source->BufUsed += ReducedBy;
1235         }
1236         /*
1237         while (*s) {
1238                 *d++ = *s++;
1239         }
1240         *d = 0;
1241         */
1242         return ReducedBy;
1243 }
1244
1245 int StrBufExtract_tokenFromStr(StrBuf *dest, const char *Source, long SourceLen, int parmnum, char separator)
1246 {
1247         const StrBuf Temp = {
1248                 (char*)Source,
1249                 SourceLen,
1250                 SourceLen,
1251                 1
1252 #ifdef SIZE_DEBUG
1253                 ,
1254                 0,
1255                 "",
1256                 ""
1257 #endif
1258         };
1259
1260         return StrBufExtract_token(dest, &Temp, parmnum, separator);
1261 }
1262
1263 /**
1264  *  a string tokenizer
1265  *  dest Destination StringBuffer
1266  *  Source StringBuffer to read into
1267  *  parmnum n'th Parameter to extract
1268  *  separator tokenizer character
1269  * @returns -1 if not found, else length of token.
1270  */
1271 int StrBufExtract_token(StrBuf *dest, const StrBuf *Source, int parmnum, char separator)
1272 {
1273         const char *s, *e;              //* source * /
1274         int len = 0;                    //* running total length of extracted string * /
1275         int current_token = 0;          //* token currently being processed * /
1276          
1277         if (dest != NULL) {
1278                 dest->buf[0] = '\0';
1279                 dest->BufUsed = 0;
1280         }
1281         else
1282                 return(-1);
1283
1284         if ((Source == NULL) || (Source->BufUsed ==0)) {
1285                 return(-1);
1286         }
1287         s = Source->buf;
1288         e = s + Source->BufUsed;
1289
1290         //cit_backtrace();
1291         //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
1292
1293         while ((s < e) && !IsEmptyStr(s)) {
1294                 if (*s == separator) {
1295                         ++current_token;
1296                 }
1297                 if (len >= dest->BufSize) {
1298                         dest->BufUsed = len;
1299                         if (IncreaseBuf(dest, 1, -1) < 0) {
1300                                 dest->BufUsed --;
1301                                 break;
1302                         }
1303                 }
1304                 if ( (current_token == parmnum) && 
1305                      (*s != separator)) {
1306                         dest->buf[len] = *s;
1307                         ++len;
1308                 }
1309                 else if (current_token > parmnum) {
1310                         break;
1311                 }
1312                 ++s;
1313         }
1314         
1315         dest->buf[len] = '\0';
1316         dest->BufUsed = len;
1317                 
1318         if (current_token < parmnum) {
1319                 //lprintf (CTDL_DEBUG,"test <!: %s\n", dest);
1320                 return(-1);
1321         }
1322         //lprintf (CTDL_DEBUG,"test <: %d; %s\n", len, dest);
1323         return(len);
1324 }
1325
1326
1327
1328
1329
1330 /**
1331  *  a string tokenizer to fetch an integer
1332  *  Source String containing tokens
1333  *  parmnum n'th Parameter to extract
1334  *  separator tokenizer character
1335  * @returns 0 if not found, else integer representation of the token
1336  */
1337 int StrBufExtract_int(const StrBuf* Source, int parmnum, char separator)
1338 {
1339         StrBuf tmp;
1340         char buf[64];
1341         
1342         tmp.buf = buf;
1343         buf[0] = '\0';
1344         tmp.BufSize = 64;
1345         tmp.BufUsed = 0;
1346         tmp.ConstBuf = 1;
1347         if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
1348                 return(atoi(buf));
1349         else
1350                 return 0;
1351 }
1352
1353 /**
1354  *  a string tokenizer to fetch a long integer
1355  *  Source String containing tokens
1356  *  parmnum n'th Parameter to extract
1357  *  separator tokenizer character
1358  * @returns 0 if not found, else long integer representation of the token
1359  */
1360 long StrBufExtract_long(const StrBuf* Source, int parmnum, char separator)
1361 {
1362         StrBuf tmp;
1363         char buf[64];
1364         
1365         tmp.buf = buf;
1366         buf[0] = '\0';
1367         tmp.BufSize = 64;
1368         tmp.BufUsed = 0;
1369         tmp.ConstBuf = 1;
1370         if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
1371                 return(atoi(buf));
1372         else
1373                 return 0;
1374 }
1375
1376
1377 /**
1378  *  a string tokenizer to fetch an unsigned long
1379  *  Source String containing tokens
1380  *  parmnum n'th Parameter to extract
1381  *  separator tokenizer character
1382  * @returns 0 if not found, else unsigned long representation of the token
1383  */
1384 unsigned long StrBufExtract_unsigned_long(const StrBuf* Source, int parmnum, char separator)
1385 {
1386         StrBuf tmp;
1387         char buf[64];
1388         char *pnum;
1389         
1390         tmp.buf = buf;
1391         buf[0] = '\0';
1392         tmp.BufSize = 64;
1393         tmp.BufUsed = 0;
1394         tmp.ConstBuf = 1;
1395         if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0) {
1396                 pnum = &buf[0];
1397                 if (*pnum == '-')
1398                         pnum ++;
1399                 return (unsigned long) atol(pnum);
1400         }
1401         else 
1402                 return 0;
1403 }
1404
1405
1406
1407 /**
1408  *  a string tokenizer; Bounds checker
1409  *  function to make shure whether StrBufExtract_NextToken and friends have reached the end of the string.
1410  *  Source our tokenbuffer
1411  *  pStart the token iterator pointer to inspect
1412  * @returns whether the revolving pointer is inside of the search range
1413  */
1414 int StrBufHaveNextToken(const StrBuf *Source, const char **pStart)
1415 {
1416         if ((Source == NULL) || 
1417             (*pStart == StrBufNOTNULL) ||
1418             (Source->BufUsed == 0))
1419         {
1420                 return 0;
1421         }
1422         if (*pStart == NULL)
1423         {
1424                 return 1;
1425         }
1426         else if (*pStart > Source->buf + Source->BufUsed)
1427         {
1428                 return 0;
1429         }
1430         else if (*pStart <= Source->buf)
1431         {
1432                 return 0;
1433         }
1434
1435         return 1;
1436 }
1437
1438 /**
1439  *  a string tokenizer
1440  *  dest Destination StringBuffer
1441  *  Source StringBuffer to read into
1442  *  pStart pointer to the end of the last token. Feed with NULL on start.
1443  *  separator tokenizer 
1444  * @returns -1 if not found, else length of token.
1445  */
1446 int StrBufExtract_NextToken(StrBuf *dest, const StrBuf *Source, const char **pStart, char separator)
1447 {
1448         const char *s;          /* source */
1449         const char *EndBuffer;  /* end stop of source buffer */
1450         int current_token = 0;  /* token currently being processed */
1451         int len = 0;            /* running total length of extracted string */
1452
1453         if ((Source          == NULL) || 
1454             (Source->BufUsed == 0)      ) 
1455         {
1456                 *pStart = StrBufNOTNULL;
1457                 if (dest != NULL)
1458                         FlushStrBuf(dest);
1459                 return -1;
1460         }
1461          
1462         EndBuffer = Source->buf + Source->BufUsed;
1463
1464         if (dest != NULL) 
1465         {
1466                 dest->buf[0] = '\0';
1467                 dest->BufUsed = 0;
1468         }
1469         else
1470         {
1471                 *pStart = EndBuffer + 1;
1472                 return -1;
1473         }
1474
1475         if (*pStart == NULL)
1476         {
1477                 *pStart = Source->buf; /* we're starting to examine this buffer. */
1478         }
1479         else if ((*pStart < Source->buf) || 
1480                  (*pStart > EndBuffer  )   ) 
1481         {
1482                 return -1; /* no more tokens to find. */
1483         }
1484
1485         s = *pStart;
1486         /* start to find the next token */
1487         while ((s <= EndBuffer)      && 
1488                (current_token == 0) ) 
1489         {
1490                 if (*s == separator) 
1491                 {
1492                         /* we found the next token */
1493                         ++current_token;
1494                 }
1495
1496                 if (len >= dest->BufSize) 
1497                 {
1498                         /* our Dest-buffer isn't big enough, increase it. */
1499                         dest->BufUsed = len;
1500
1501                         if (IncreaseBuf(dest, 1, -1) < 0) {
1502                                 /* WHUT? no more mem? bail out. */
1503                                 s = EndBuffer;
1504                                 dest->BufUsed --;
1505                                 break;
1506                         }
1507                 }
1508
1509                 if ( (current_token == 0 ) &&   /* are we in our target token? */
1510                      (!IsEmptyStr(s)     ) &&
1511                      (separator     != *s)    ) /* don't copy the token itself */
1512                 {
1513                         dest->buf[len] = *s;    /* Copy the payload */
1514                         ++len;                  /* remember the bigger size. */
1515                 }
1516
1517                 ++s;
1518         }
1519
1520         /* did we reach the end? */
1521         if ((s > EndBuffer)) {
1522                 EndBuffer = StrBufNOTNULL;
1523                 *pStart = EndBuffer;
1524         }
1525         else {
1526                 *pStart = s;  /* remember the position for the next run */
1527         }
1528
1529         /* sanitize our extracted token */
1530         dest->buf[len] = '\0';
1531         dest->BufUsed  = len;
1532
1533         return (len);
1534 }
1535
1536
1537 /**
1538  *  a string tokenizer
1539  *  Source StringBuffer to read from
1540  *  pStart pointer to the end of the last token. Feed with NULL.
1541  *  separator tokenizer character
1542  *  nTokens number of tokens to fastforward over
1543  * @returns -1 if not found, else length of token.
1544  */
1545 int StrBufSkip_NTokenS(const StrBuf *Source, const char **pStart, char separator, int nTokens)
1546 {
1547         const char *s, *EndBuffer;      //* source * /
1548         int len = 0;                    //* running total length of extracted string * /
1549         int current_token = 0;          //* token currently being processed * /
1550
1551         if ((Source == NULL) || 
1552             (Source->BufUsed ==0)) {
1553                 return(-1);
1554         }
1555         if (nTokens == 0)
1556                 return Source->BufUsed;
1557
1558         if (*pStart == NULL)
1559                 *pStart = Source->buf;
1560
1561         EndBuffer = Source->buf + Source->BufUsed;
1562
1563         if ((*pStart < Source->buf) || 
1564             (*pStart >  EndBuffer)) {
1565                 return (-1);
1566         }
1567
1568
1569         s = *pStart;
1570
1571         //cit_backtrace();
1572         //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
1573
1574         while ((s < EndBuffer) && !IsEmptyStr(s)) {
1575                 if (*s == separator) {
1576                         ++current_token;
1577                 }
1578                 if (current_token >= nTokens) {
1579                         break;
1580                 }
1581                 ++s;
1582         }
1583         *pStart = s;
1584         (*pStart) ++;
1585
1586         return(len);
1587 }
1588
1589 /**
1590  *  a string tokenizer to fetch an integer
1591  *  Source StringBuffer to read from
1592  *  pStart Cursor on the tokenstring
1593  *  separator tokenizer character
1594  * @returns 0 if not found, else integer representation of the token
1595  */
1596 int StrBufExtractNext_int(const StrBuf* Source, const char **pStart, char separator)
1597 {
1598         StrBuf tmp;
1599         char buf[64];
1600         
1601         tmp.buf = buf;
1602         buf[0] = '\0';
1603         tmp.BufSize = 64;
1604         tmp.BufUsed = 0;
1605         tmp.ConstBuf = 1;
1606         if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0)
1607                 return(atoi(buf));
1608         else
1609                 return 0;
1610 }
1611
1612 /**
1613  *  a string tokenizer to fetch a long integer
1614  *  Source StringBuffer to read from
1615  *  pStart Cursor on the tokenstring
1616  *  separator tokenizer character
1617  * @returns 0 if not found, else long integer representation of the token
1618  */
1619 long StrBufExtractNext_long(const StrBuf* Source, const char **pStart, char separator)
1620 {
1621         StrBuf tmp;
1622         char buf[64];
1623         
1624         tmp.buf = buf;
1625         buf[0] = '\0';
1626         tmp.BufSize = 64;
1627         tmp.BufUsed = 0;
1628         tmp.ConstBuf = 1;
1629         if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0)
1630                 return(atoi(buf));
1631         else
1632                 return 0;
1633 }
1634
1635
1636 /**
1637  *  a string tokenizer to fetch an unsigned long
1638  *  Source StringBuffer to read from
1639  *  pStart Cursor on the tokenstring
1640  *  separator tokenizer character
1641  * @returns 0 if not found, else unsigned long representation of the token
1642  */
1643 unsigned long StrBufExtractNext_unsigned_long(const StrBuf* Source, const char **pStart, char separator)
1644 {
1645         StrBuf tmp;
1646         char buf[64];
1647         char *pnum;
1648         
1649         tmp.buf = buf;
1650         buf[0] = '\0';
1651         tmp.BufSize = 64;
1652         tmp.BufUsed = 0;
1653         tmp.ConstBuf = 1;
1654         if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0) {
1655                 pnum = &buf[0];
1656                 if (*pnum == '-')
1657                         pnum ++;
1658                 return (unsigned long) atol(pnum);
1659         }
1660         else 
1661                 return 0;
1662 }
1663
1664
1665
1666
1667
1668 /*******************************************************************************
1669  *                             Escape Appending                                *
1670  *******************************************************************************/
1671
1672 /** 
1673  *  Escape a string for feeding out as a URL while appending it to a Buffer
1674  *  OutBuf the output buffer
1675  *  In Buffer to encode
1676  *  PlainIn way in from plain old c strings
1677  */
1678 void StrBufUrlescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
1679 {
1680         const char *pch, *pche;
1681         char *pt, *pte;
1682         int len;
1683         
1684         if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1685                 return;
1686         if (PlainIn != NULL) {
1687                 len = strlen(PlainIn);
1688                 pch = PlainIn;
1689                 pche = pch + len;
1690         }
1691         else {
1692                 pch = In->buf;
1693                 pche = pch + In->BufUsed;
1694                 len = In->BufUsed;
1695         }
1696
1697         if (len == 0) 
1698                 return;
1699
1700         pt = OutBuf->buf + OutBuf->BufUsed;
1701         pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1702
1703         while (pch < pche) {
1704                 if (pt >= pte) {
1705                         IncreaseBuf(OutBuf, 1, -1);
1706                         pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1707                         pt = OutBuf->buf + OutBuf->BufUsed;
1708                 }
1709
1710                 if((*pch >= 'a' && *pch <= 'z') ||
1711                    (*pch >= '@' && *pch <= 'Z') || /* @ A-Z */
1712                    (*pch >= '0' && *pch <= ':') || /* 0-9 : */
1713                    (*pch == '!') || (*pch == '_') || 
1714                    (*pch == ',') || (*pch == '.'))
1715                 {
1716                         *(pt++) = *(pch++);
1717                         OutBuf->BufUsed++;
1718                 }                       
1719                 else {
1720                         *pt = '%';
1721                         *(pt + 1) = HexList[(unsigned char)*pch][0];
1722                         *(pt + 2) = HexList[(unsigned char)*pch][1];
1723                         pt += 3;
1724                         OutBuf->BufUsed += 3;
1725                         pch ++;
1726                 }
1727         }
1728         *pt = '\0';
1729 }
1730
1731
1732 /*
1733  *  Escape a string for feeding out as a the username/password part of an URL while appending it to a Buffer
1734  *  OutBuf the output buffer
1735  *  In Buffer to encode
1736  *  PlainIn way in from plain old c strings
1737  */
1738 void StrBufUrlescUPAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn) {
1739         const char *pch, *pche;
1740         char *pt, *pte;
1741         int len;
1742         
1743         if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1744                 return;
1745         if (PlainIn != NULL) {
1746                 len = strlen(PlainIn);
1747                 pch = PlainIn;
1748                 pche = pch + len;
1749         }
1750         else {
1751                 pch = In->buf;
1752                 pche = pch + In->BufUsed;
1753                 len = In->BufUsed;
1754         }
1755
1756         if (len == 0) 
1757                 return;
1758
1759         pt = OutBuf->buf + OutBuf->BufUsed;
1760         pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1761
1762         while (pch < pche) {
1763                 if (pt >= pte) {
1764                         IncreaseBuf(OutBuf, 1, -1);
1765                         pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1766                         pt = OutBuf->buf + OutBuf->BufUsed;
1767                 }
1768
1769                 if((*pch >= 'a' && *pch <= 'z') ||
1770                    (*pch >= 'A' && *pch <= 'Z') || /* A-Z */
1771                    (*pch >= '0' && *pch <= ':') || /* 0-9 : */
1772                    (*pch == '!') || (*pch == '_') || 
1773                    (*pch == ',') || (*pch == '.'))
1774                 {
1775                         *(pt++) = *(pch++);
1776                         OutBuf->BufUsed++;
1777                 }                       
1778                 else {
1779                         *pt = '%';
1780                         *(pt + 1) = HexList[(unsigned char)*pch][0];
1781                         *(pt + 2) = HexList[(unsigned char)*pch][1];
1782                         pt += 3;
1783                         OutBuf->BufUsed += 3;
1784                         pch ++;
1785                 }
1786         }
1787         *pt = '\0';
1788 }
1789
1790 /* 
1791  *  append a string with characters having a special meaning in xml encoded to the buffer
1792  *  OutBuf the output buffer
1793  *  In Buffer to encode
1794  *  PlainIn way in from plain old c strings
1795  *  PlainInLen way in from plain old c strings; maybe you've got binary data or know the length?
1796  *  OverrideLowChars should chars < 0x20 be replaced by _ or escaped as xml entity?
1797  */
1798 void StrBufXMLEscAppend(StrBuf *OutBuf,
1799                         const StrBuf *In,
1800                         const char *PlainIn,
1801                         long PlainInLen,
1802                         int OverrideLowChars)
1803 {
1804         const char *pch, *pche;
1805         char *pt, *pte;
1806         int IsUtf8Sequence;
1807         int len;
1808
1809         if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1810                 return;
1811         if (PlainIn != NULL) {
1812                 if (PlainInLen < 0)
1813                         len = strlen((const char*)PlainIn);
1814                 else
1815                         len = PlainInLen;
1816                 pch = PlainIn;
1817                 pche = pch + len;
1818         }
1819         else {
1820                 pch = (const char*)In->buf;
1821                 pche = pch + In->BufUsed;
1822                 len = In->BufUsed;
1823         }
1824
1825         if (len == 0)
1826                 return;
1827
1828         pt = OutBuf->buf + OutBuf->BufUsed;
1829         /**< we max append 6 chars at once plus the \0 */
1830         pte = OutBuf->buf + OutBuf->BufSize - 6;
1831
1832         while (pch < pche) {
1833                 if (pt >= pte) {
1834                         OutBuf->BufUsed = pt - OutBuf->buf;
1835                         IncreaseBuf(OutBuf, 1, -1);
1836                         pte = OutBuf->buf + OutBuf->BufSize - 6;
1837                         /**< we max append 3 chars at once plus the \0 */
1838
1839                         pt = OutBuf->buf + OutBuf->BufUsed;
1840                 }
1841
1842                 if (*pch == '<') {
1843                         memcpy(pt, HKEY("&lt;"));
1844                         pt += 4;
1845                         pch ++;
1846                 }
1847                 else if (*pch == '>') {
1848                         memcpy(pt, HKEY("&gt;"));
1849                         pt += 4;
1850                         pch ++;
1851                 }
1852                 else if (*pch == '&') {
1853                         memcpy(pt, HKEY("&amp;"));
1854                         pt += 5;
1855                         pch++;
1856                 }
1857                 else if ((*pch >= 0x20) && (*pch <= 0x7F)) {
1858                         *pt = *pch;
1859                         pt++; pch++;
1860                 }
1861                 else {
1862                         IsUtf8Sequence =  Ctdl_GetUtf8SequenceLength(pch, pche);
1863                         if (IsUtf8Sequence)
1864                         {
1865                                 while ((IsUtf8Sequence > 0) && 
1866                                        (pch < pche))
1867                                 {
1868                                         *pt = *pch;
1869                                         pt ++;
1870                                         pch ++;
1871                                         --IsUtf8Sequence;
1872                                 }
1873                         }
1874                         else
1875                         {
1876                                 *pt = '&';
1877                                 pt++;
1878                                 *pt = HexList[*(unsigned char*)pch][0];
1879                                 pt ++;
1880                                 *pt = HexList[*(unsigned char*)pch][1];
1881                                 pt ++; pch ++;
1882                                 *pt = '&';
1883                                 pt++;
1884                                 pch ++;
1885                         }
1886                 }
1887         }
1888         *pt = '\0';
1889         OutBuf->BufUsed = pt - OutBuf->buf;
1890 }
1891
1892
1893 /** 
1894  *  append a string in hex encoding to the buffer
1895  *  OutBuf the output buffer
1896  *  In Buffer to encode
1897  *  PlainIn way in from plain old c strings
1898  *  PlainInLen way in from plain old c strings; maybe you've got binary data or know the length?
1899  */
1900 void StrBufHexEscAppend(StrBuf *OutBuf, const StrBuf *In, const unsigned char *PlainIn, long PlainInLen)
1901 {
1902         const unsigned char *pch, *pche;
1903         char *pt, *pte;
1904         int len;
1905         
1906         if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1907                 return;
1908         if (PlainIn != NULL) {
1909                 if (PlainInLen < 0)
1910                         len = strlen((const char*)PlainIn);
1911                 else
1912                         len = PlainInLen;
1913                 pch = PlainIn;
1914                 pche = pch + len;
1915         }
1916         else {
1917                 pch = (const unsigned char*)In->buf;
1918                 pche = pch + In->BufUsed;
1919                 len = In->BufUsed;
1920         }
1921
1922         if (len == 0) 
1923                 return;
1924
1925         pt = OutBuf->buf + OutBuf->BufUsed;
1926         pte = OutBuf->buf + OutBuf->BufSize - 3; /**< we max append 3 chars at once plus the \0 */
1927
1928         while (pch < pche) {
1929                 if (pt >= pte) {
1930                         IncreaseBuf(OutBuf, 1, -1);
1931                         pte = OutBuf->buf + OutBuf->BufSize - 3; /**< we max append 3 chars at once plus the \0 */
1932                         pt = OutBuf->buf + OutBuf->BufUsed;
1933                 }
1934
1935                 *pt = HexList[*pch][0];
1936                 pt ++;
1937                 *pt = HexList[*pch][1];
1938                 pt ++; pch ++; OutBuf->BufUsed += 2;
1939         }
1940         *pt = '\0';
1941 }
1942
1943
1944 void StrBufBase64Append(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn, long PlainInLen, int linebreaks) {
1945         const char *pch;
1946         char *pt;
1947         int len;
1948         long ExpectLen;
1949         
1950         if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1951                 return;
1952         if (PlainIn != NULL) {
1953                 if (PlainInLen < 0)
1954                         len = strlen(PlainIn);
1955                 else
1956                         len = PlainInLen;
1957                 pch = PlainIn;
1958         }
1959         else {
1960                 pch = In->buf;
1961                 len = In->BufUsed;
1962         }
1963
1964         if (len == 0) 
1965                 return;
1966
1967         ExpectLen = ((len * 134) / 100) + OutBuf->BufUsed;
1968
1969         if (ExpectLen > OutBuf->BufSize)
1970                 if (IncreaseBuf(OutBuf, 1, ExpectLen) < ExpectLen)
1971                         return;
1972
1973         pt = OutBuf->buf + OutBuf->BufUsed;
1974
1975         len = CtdlEncodeBase64(pt, pch, len, linebreaks);
1976
1977         pt += len;
1978         OutBuf->BufUsed += len;
1979         *pt = '\0';
1980 }
1981
1982
1983 // append a string in hex encoding to the buffer
1984 // OutBuf       the output buffer
1985 // In           Buffer to encode
1986 // PlainIn      way in from plain old c strings
1987 void StrBufHexescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn) {
1988         StrBufHexEscAppend(OutBuf, In, (const unsigned char*) PlainIn, -1);
1989 }
1990
1991 /*
1992  *  Append a string, escaping characters which have meaning in HTML.  
1993  *
1994  *  Target      target buffer
1995  *  Source      source buffer; set to NULL if you just have a C-String
1996  *  PlainIn       Plain-C string to append; set to NULL if unused
1997  *  nbsp                If nonzero, spaces are converted to non-breaking spaces.
1998  *  nolinebreaks        if set to 1, linebreaks are removed from the string.
1999  *                      if set to 2, linebreaks are replaced by &ltbr/&gt
2000  */
2001 long StrEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
2002 {
2003         const char *aptr, *eiptr;
2004         char *bptr, *eptr;
2005         long len;
2006
2007         if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2008                 return -1;
2009
2010         if (PlainIn != NULL) {
2011                 aptr = PlainIn;
2012                 len = strlen(PlainIn);
2013                 eiptr = aptr + len;
2014         }
2015         else {
2016                 aptr = Source->buf;
2017                 eiptr = aptr + Source->BufUsed;
2018                 len = Source->BufUsed;
2019         }
2020
2021         if (len == 0) 
2022                 return -1;
2023
2024         bptr = Target->buf + Target->BufUsed;
2025         eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in...  */
2026
2027         while (aptr < eiptr){
2028                 if(bptr >= eptr) {
2029                         IncreaseBuf(Target, 1, -1);
2030                         eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in...  */
2031                         bptr = Target->buf + Target->BufUsed;
2032                 }
2033                 if (*aptr == '<') {
2034                         memcpy(bptr, "&lt;", 4);
2035                         bptr += 4;
2036                         Target->BufUsed += 4;
2037                 }
2038                 else if (*aptr == '>') {
2039                         memcpy(bptr, "&gt;", 4);
2040                         bptr += 4;
2041                         Target->BufUsed += 4;
2042                 }
2043                 else if (*aptr == '&') {
2044                         memcpy(bptr, "&amp;", 5);
2045                         bptr += 5;
2046                         Target->BufUsed += 5;
2047                 }
2048                 else if (*aptr == '"') {
2049                         memcpy(bptr, "&quot;", 6);
2050                         bptr += 6;
2051                         Target->BufUsed += 6;
2052                 }
2053                 else if (*aptr == '\'') {
2054                         memcpy(bptr, "&#39;", 5);
2055                         bptr += 5;
2056                         Target->BufUsed += 5;
2057                 }
2058                 else if (*aptr == LB) {
2059                         *bptr = '<';
2060                         bptr ++;
2061                         Target->BufUsed ++;
2062                 }
2063                 else if (*aptr == RB) {
2064                         *bptr = '>';
2065                         bptr ++;
2066                         Target->BufUsed ++;
2067                 }
2068                 else if (*aptr == QU) {
2069                         *bptr ='"';
2070                         bptr ++;
2071                         Target->BufUsed ++;
2072                 }
2073                 else if ((*aptr == 32) && (nbsp == 1)) {
2074                         memcpy(bptr, "&nbsp;", 6);
2075                         bptr += 6;
2076                         Target->BufUsed += 6;
2077                 }
2078                 else if ((*aptr == '\n') && (nolinebreaks == 1)) {
2079                         *bptr='\0';     /* nothing */
2080                 }
2081                 else if ((*aptr == '\n') && (nolinebreaks == 2)) {
2082                         memcpy(bptr, "&lt;br/&gt;", 11);
2083                         bptr += 11;
2084                         Target->BufUsed += 11;
2085                 }
2086
2087
2088                 else if ((*aptr == '\r') && (nolinebreaks != 0)) {
2089                         *bptr='\0';     /* nothing */
2090                 }
2091                 else{
2092                         *bptr = *aptr;
2093                         bptr++;
2094                         Target->BufUsed ++;
2095                 }
2096                 aptr ++;
2097         }
2098         *bptr = '\0';
2099         if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
2100                 return -1;
2101         return Target->BufUsed;
2102 }
2103
2104 /**
2105  *  Append a string, escaping characters which have meaning in HTML.  
2106  * Converts linebreaks into blanks; escapes single quotes
2107  *  Target      target buffer
2108  *  Source      source buffer; set to NULL if you just have a C-String
2109  *  PlainIn       Plain-C string to append; set to NULL if unused
2110  */
2111 void StrMsgEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2112 {
2113         const char *aptr, *eiptr;
2114         char *tptr, *eptr;
2115         long len;
2116
2117         if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2118                 return ;
2119
2120         if (PlainIn != NULL) {
2121                 aptr = PlainIn;
2122                 len = strlen(PlainIn);
2123                 eiptr = aptr + len;
2124         }
2125         else {
2126                 aptr = Source->buf;
2127                 eiptr = aptr + Source->BufUsed;
2128                 len = Source->BufUsed;
2129         }
2130
2131         if (len == 0) 
2132                 return;
2133
2134         eptr = Target->buf + Target->BufSize - 8; 
2135         tptr = Target->buf + Target->BufUsed;
2136         
2137         while (aptr < eiptr){
2138                 if(tptr >= eptr) {
2139                         IncreaseBuf(Target, 1, -1);
2140                         eptr = Target->buf + Target->BufSize - 8; 
2141                         tptr = Target->buf + Target->BufUsed;
2142                 }
2143                
2144                 if (*aptr == '\n') {
2145                         *tptr = ' ';
2146                         Target->BufUsed++;
2147                 }
2148                 else if (*aptr == '\r') {
2149                         *tptr = ' ';
2150                         Target->BufUsed++;
2151                 }
2152                 else if (*aptr == '\'') {
2153                         *(tptr++) = '&';
2154                         *(tptr++) = '#';
2155                         *(tptr++) = '3';
2156                         *(tptr++) = '9';
2157                         *tptr = ';';
2158                         Target->BufUsed += 5;
2159                 } else {
2160                         *tptr = *aptr;
2161                         Target->BufUsed++;
2162                 }
2163                 tptr++; aptr++;
2164         }
2165         *tptr = '\0';
2166 }
2167
2168
2169
2170 /**
2171  *  Append a string, escaping characters which have meaning in ICAL.  
2172  * [\n,] 
2173  *  Target      target buffer
2174  *  Source      source buffer; set to NULL if you just have a C-String
2175  *  PlainIn       Plain-C string to append; set to NULL if unused
2176  */
2177 void StrIcalEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2178 {
2179         const char *aptr, *eiptr;
2180         char *tptr, *eptr;
2181         long len;
2182
2183         if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2184                 return ;
2185
2186         if (PlainIn != NULL) {
2187                 aptr = PlainIn;
2188                 len = strlen(PlainIn);
2189                 eiptr = aptr + len;
2190         }
2191         else {
2192                 aptr = Source->buf;
2193                 eiptr = aptr + Source->BufUsed;
2194                 len = Source->BufUsed;
2195         }
2196
2197         if (len == 0) 
2198                 return;
2199
2200         eptr = Target->buf + Target->BufSize - 8; 
2201         tptr = Target->buf + Target->BufUsed;
2202         
2203         while (aptr < eiptr){
2204                 if(tptr + 3 >= eptr) {
2205                         IncreaseBuf(Target, 1, -1);
2206                         eptr = Target->buf + Target->BufSize - 8; 
2207                         tptr = Target->buf + Target->BufUsed;
2208                 }
2209                
2210                 if (*aptr == '\n') {
2211                         *tptr = '\\';
2212                         Target->BufUsed++;
2213                         tptr++;
2214                         *tptr = 'n';
2215                         Target->BufUsed++;
2216                 }
2217                 else if (*aptr == '\r') {
2218                         *tptr = '\\';
2219                         Target->BufUsed++;
2220                         tptr++;
2221                         *tptr = 'r';
2222                         Target->BufUsed++;
2223                 }
2224                 else if (*aptr == ',') {
2225                         *tptr = '\\';
2226                         Target->BufUsed++;
2227                         tptr++;
2228                         *tptr = ',';
2229                         Target->BufUsed++;
2230                 } else {
2231                         *tptr = *aptr;
2232                         Target->BufUsed++;
2233                 }
2234                 tptr++; aptr++;
2235         }
2236         *tptr = '\0';
2237 }
2238
2239 /**
2240  *  Append a string, escaping characters which have meaning in JavaScript strings .  
2241  *
2242  *  Target      target buffer
2243  *  Source      source buffer; set to NULL if you just have a C-String
2244  *  PlainIn       Plain-C string to append; set to NULL if unused
2245  * @returns size of result or -1
2246  */
2247 long StrECMAEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2248 {
2249         const char *aptr, *eiptr;
2250         char *bptr, *eptr;
2251         long len;
2252         int IsUtf8Sequence;
2253
2254         if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2255                 return -1;
2256
2257         if (PlainIn != NULL) {
2258                 aptr = PlainIn;
2259                 len = strlen(PlainIn);
2260                 eiptr = aptr + len;
2261         }
2262         else {
2263                 aptr = Source->buf;
2264                 eiptr = aptr + Source->BufUsed;
2265                 len = Source->BufUsed;
2266         }
2267
2268         if (len == 0) 
2269                 return -1;
2270
2271         bptr = Target->buf + Target->BufUsed;
2272         eptr = Target->buf + Target->BufSize - 7; /* our biggest unit to put in...  */
2273
2274         while (aptr < eiptr){
2275                 if(bptr >= eptr) {
2276                         IncreaseBuf(Target, 1, -1);
2277                         eptr = Target->buf + Target->BufSize - 7; /* our biggest unit to put in...  */
2278                         bptr = Target->buf + Target->BufUsed;
2279                 }
2280                 switch (*aptr) {
2281                 case '\n':
2282                         memcpy(bptr, HKEY("\\n"));
2283                         bptr += 2;
2284                         Target->BufUsed += 2;                           
2285                         break;
2286                 case '\r':
2287                         memcpy(bptr, HKEY("\\r"));
2288                         bptr += 2;
2289                         Target->BufUsed += 2;
2290                         break;
2291                 case '"':
2292                         *bptr = '\\';
2293                         bptr ++;
2294                         *bptr = '"';
2295                         bptr ++;
2296                         Target->BufUsed += 2;
2297                         break;
2298                 case '\\':
2299                         if ((*(aptr + 1) == 'u') &&
2300                             isxdigit(*(aptr + 2)) &&
2301                             isxdigit(*(aptr + 3)) &&
2302                             isxdigit(*(aptr + 4)) &&
2303                             isxdigit(*(aptr + 5)))
2304                         { /* oh, a unicode escaper. let it pass through. */
2305                                 memcpy(bptr, aptr, 6);
2306                                 aptr += 5;
2307                                 bptr +=6;
2308                                 Target->BufUsed += 6;
2309                         }
2310                         else 
2311                         {
2312                                 *bptr = '\\';
2313                                 bptr ++;
2314                                 *bptr = '\\';
2315                                 bptr ++;
2316                                 Target->BufUsed += 2;
2317                         }
2318                         break;
2319                 case '\b':
2320                         *bptr = '\\';
2321                         bptr ++;
2322                         *bptr = 'b';
2323                         bptr ++;
2324                         Target->BufUsed += 2;
2325                         break;
2326                 case '\f':
2327                         *bptr = '\\';
2328                         bptr ++;
2329                         *bptr = 'f';
2330                         bptr ++;
2331                         Target->BufUsed += 2;
2332                         break;
2333                 case '\t':
2334                         *bptr = '\\';
2335                         bptr ++;
2336                         *bptr = 't';
2337                         bptr ++;
2338                         Target->BufUsed += 2;
2339                         break;
2340                 default:
2341                         IsUtf8Sequence =  Ctdl_GetUtf8SequenceLength(aptr, eiptr);
2342                         while (IsUtf8Sequence > 0){
2343                                 *bptr = *aptr;
2344                                 Target->BufUsed ++;
2345                                 if (--IsUtf8Sequence)
2346                                         aptr++;
2347                                 bptr++;
2348                         }
2349                 }
2350                 aptr ++;
2351         }
2352         *bptr = '\0';
2353         if ((bptr == eptr - 1 ) && !IsEmptyStr(aptr) )
2354                 return -1;
2355         return Target->BufUsed;
2356 }
2357
2358 /**
2359  *  Append a string, escaping characters which have meaning in HTML + json.  
2360  *
2361  *  Target      target buffer
2362  *  Source      source buffer; set to NULL if you just have a C-String
2363  *  PlainIn       Plain-C string to append; set to NULL if unused
2364  *  nbsp                If nonzero, spaces are converted to non-breaking spaces.
2365  *  nolinebreaks        if set to 1, linebreaks are removed from the string.
2366  *                      if set to 2, linebreaks are replaced by &ltbr/&gt
2367  */
2368 long StrHtmlEcmaEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
2369 {
2370         const char *aptr, *eiptr;
2371         char *bptr, *eptr;
2372         long len;
2373         int IsUtf8Sequence = 0;
2374
2375         if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2376                 return -1;
2377
2378         if (PlainIn != NULL) {
2379                 aptr = PlainIn;
2380                 len = strlen(PlainIn);
2381                 eiptr = aptr + len;
2382         }
2383         else {
2384                 aptr = Source->buf;
2385                 eiptr = aptr + Source->BufUsed;
2386                 len = Source->BufUsed;
2387         }
2388
2389         if (len == 0) 
2390                 return -1;
2391
2392         bptr = Target->buf + Target->BufUsed;
2393         eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in...  */
2394
2395         while (aptr < eiptr){
2396                 if(bptr >= eptr) {
2397                         IncreaseBuf(Target, 1, -1);
2398                         eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in...  */
2399                         bptr = Target->buf + Target->BufUsed;
2400                 }
2401                 switch (*aptr) {
2402                 case '<':
2403                         memcpy(bptr, HKEY("&lt;"));
2404                         bptr += 4;
2405                         Target->BufUsed += 4;
2406                         break;
2407                 case '>':
2408                         memcpy(bptr, HKEY("&gt;"));
2409                         bptr += 4;
2410                         Target->BufUsed += 4;
2411                         break;
2412                 case '&':
2413                         memcpy(bptr, HKEY("&amp;"));
2414                         bptr += 5;
2415                         Target->BufUsed += 5;
2416                         break;
2417                 case LB:
2418                         *bptr = '<';
2419                         bptr ++;
2420                         Target->BufUsed ++;
2421                         break;
2422                 case RB:
2423                         *bptr = '>';
2424                         bptr ++;
2425                         Target->BufUsed ++;
2426                         break;
2427                 case '\n':
2428                         switch (nolinebreaks) {
2429                         case 1:
2430                                 *bptr='\0';     /* nothing */
2431                                 break;
2432                         case 2:
2433                                 memcpy(bptr, HKEY("&lt;br/&gt;"));
2434                                 bptr += 11;
2435                                 Target->BufUsed += 11;
2436                                 break;
2437                         default:
2438                                 memcpy(bptr, HKEY("\\n"));
2439                                 bptr += 2;
2440                                 Target->BufUsed += 2;                           
2441                         }
2442                         break;
2443                 case '\r':
2444                         switch (nolinebreaks) {
2445                         case 1:
2446                         case 2:
2447                                 *bptr='\0';     /* nothing */
2448                                 break;
2449                         default:
2450                                 memcpy(bptr, HKEY("\\r"));
2451                                 bptr += 2;
2452                                 Target->BufUsed += 2;
2453                                 break;
2454                         }
2455                         break;
2456                 case '"':
2457                 case QU:
2458                         *bptr = '\\';
2459                         bptr ++;
2460                         *bptr = '"';
2461                         bptr ++;
2462                         Target->BufUsed += 2;
2463                         break;
2464                 case '\\':
2465                         if ((*(aptr + 1) == 'u') &&
2466                             isxdigit(*(aptr + 2)) &&
2467                             isxdigit(*(aptr + 3)) &&
2468                             isxdigit(*(aptr + 4)) &&
2469                             isxdigit(*(aptr + 5)))
2470                         { /* oh, a unicode escaper. let it pass through. */
2471                                 memcpy(bptr, aptr, 6);
2472                                 aptr += 5;
2473                                 bptr +=6;
2474                                 Target->BufUsed += 6;
2475                         }
2476                         else 
2477                         {
2478                                 *bptr = '\\';
2479                                 bptr ++;
2480                                 *bptr = '\\';
2481                                 bptr ++;
2482                                 Target->BufUsed += 2;
2483                         }
2484                         break;
2485                 case '\b':
2486                         *bptr = '\\';
2487                         bptr ++;
2488                         *bptr = 'b';
2489                         bptr ++;
2490                         Target->BufUsed += 2;
2491                         break;
2492                 case '\f':
2493                         *bptr = '\\';
2494                         bptr ++;
2495                         *bptr = 'f';
2496                         bptr ++;
2497                         Target->BufUsed += 2;
2498                         break;
2499                 case '\t':
2500                         *bptr = '\\';
2501                         bptr ++;
2502                         *bptr = 't';
2503                         bptr ++;
2504                         Target->BufUsed += 2;
2505                         break;
2506                 case  32:
2507                         if (nbsp == 1) {
2508                                 memcpy(bptr, HKEY("&nbsp;"));
2509                                 bptr += 6;
2510                                 Target->BufUsed += 6;
2511                                 break;
2512                         }
2513                 default:
2514                         IsUtf8Sequence =  Ctdl_GetUtf8SequenceLength(aptr, eiptr);
2515                         while (IsUtf8Sequence > 0){
2516                                 *bptr = *aptr;
2517                                 Target->BufUsed ++;
2518                                 if (--IsUtf8Sequence)
2519                                         aptr++;
2520                                 bptr++;
2521                         }
2522                 }
2523                 aptr ++;
2524         }
2525         *bptr = '\0';
2526         if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
2527                 return -1;
2528         return Target->BufUsed;
2529 }
2530
2531
2532 /*
2533  *  replace all non-Ascii characters by another
2534  *  Buf buffer to inspect
2535  *  repl charater to stamp over non ascii chars
2536  */
2537 void StrBufAsciify(StrBuf *Buf, const char repl) {
2538         long offset;
2539
2540         for (offset = 0; offset < Buf->BufUsed; offset ++)
2541                 if (!isascii(Buf->buf[offset]))
2542                         Buf->buf[offset] = repl;
2543         
2544 }
2545
2546 /*
2547  *  unhide special chars hidden to the HTML escaper
2548  *  target buffer to put the unescaped string in
2549  *  source buffer to unescape
2550  */
2551 void StrBufEUid_unescapize(StrBuf *target, const StrBuf *source) {
2552         int a, b, len;
2553         char hex[3];
2554
2555         if ((source == NULL) || (target == NULL) || (target->buf == NULL)) {
2556                 return;
2557         }
2558
2559         if (target != NULL)
2560                 FlushStrBuf(target);
2561
2562         len = source->BufUsed;
2563         for (a = 0; a < len; ++a) {
2564                 if (target->BufUsed >= target->BufSize)
2565                         IncreaseBuf(target, 1, -1);
2566
2567                 if (source->buf[a] == '=') {
2568                         hex[0] = source->buf[a + 1];
2569                         hex[1] = source->buf[a + 2];
2570                         hex[2] = 0;
2571                         b = 0;
2572                         sscanf(hex, "%02x", &b);
2573                         target->buf[target->BufUsed] = b;
2574                         target->buf[++target->BufUsed] = 0;
2575                         a += 2;
2576                 }
2577                 else {
2578                         target->buf[target->BufUsed] = source->buf[a];
2579                         target->buf[++target->BufUsed] = 0;
2580                 }
2581         }
2582 }
2583
2584
2585 /*
2586  *  hide special chars from the HTML escapers and friends
2587  *  target buffer to put the escaped string in
2588  *  source buffer to escape
2589  */
2590 void StrBufEUid_escapize(StrBuf *target, const StrBuf *source) {
2591         int i, len;
2592
2593         if (target != NULL)
2594                 FlushStrBuf(target);
2595
2596         if ((source == NULL) || (target == NULL) || (target->buf == NULL))
2597         {
2598                 return;
2599         }
2600
2601         len = source->BufUsed;
2602         for (i=0; i<len; ++i) {
2603                 if (target->BufUsed + 4 >= target->BufSize)
2604                         IncreaseBuf(target, 1, -1);
2605                 if ( (isalnum(source->buf[i])) || 
2606                      (source->buf[i]=='-') || 
2607                      (source->buf[i]=='_') ) {
2608                         target->buf[target->BufUsed++] = source->buf[i];
2609                 }
2610                 else {
2611                         sprintf(&target->buf[target->BufUsed], 
2612                                 "=%02X", 
2613                                 (0xFF &source->buf[i]));
2614                         target->BufUsed += 3;
2615                 }
2616         }
2617         target->buf[target->BufUsed + 1] = '\0';
2618 }
2619
2620
2621 /*******************************************************************************
2622  *                      Quoted Printable de/encoding                           *
2623  *******************************************************************************/
2624
2625 /*
2626  *  decode a buffer from base 64 encoding; destroys original
2627  *  Buf Buffor to transform
2628  */
2629 int StrBufDecodeBase64(StrBuf *Buf) {
2630         char *xferbuf;
2631         size_t siz;
2632
2633         if (Buf == NULL) {
2634                 return -1;
2635         }
2636
2637         xferbuf = (char*) malloc(Buf->BufSize);
2638         if (xferbuf == NULL)
2639                 return -1;
2640
2641         *xferbuf = '\0';
2642         siz = CtdlDecodeBase64(xferbuf, Buf->buf, Buf->BufUsed);
2643         free(Buf->buf);
2644         Buf->buf = xferbuf;
2645         Buf->BufUsed = siz;
2646
2647         Buf->buf[Buf->BufUsed] = '\0';
2648         return siz;
2649 }
2650
2651
2652 /*
2653  *  decode a buffer from base 64 encoding; expects targetbuffer
2654  *  BufIn Buffor to transform
2655  *  BufOut Buffer to put result into
2656  */
2657 int StrBufDecodeBase64To(const StrBuf *BufIn, StrBuf *BufOut) {
2658         if ((BufIn == NULL) || (BufOut == NULL))
2659                 return -1;
2660
2661         if (BufOut->BufSize < BufIn->BufUsed) {
2662                 IncreaseBuf(BufOut, 0, BufIn->BufUsed);
2663         }
2664
2665         BufOut->BufUsed = CtdlDecodeBase64(BufOut->buf, BufIn->buf, BufIn->BufUsed);
2666         return BufOut->BufUsed;
2667 }
2668
2669 typedef struct __z_enc_stream {
2670         StrBuf OutBuf;
2671         z_stream zstream;
2672 } z_enc_stream;
2673
2674
2675 vStreamT *StrBufNewStreamContext(eStreamType type, const char **Err) {
2676         //base64_decodestate *state;;
2677         *Err = NULL;
2678
2679         switch (type) {
2680
2681                 //case eBase64Decode:
2682                 //case eBase64Encode:
2683                         //state = (base64_decodestate*) malloc(sizeof(base64_decodestate));
2684                         //base64_init_decodestate(state);
2685                         //return (vStreamT*) state;
2686                         //break;
2687
2688                 case eZLibDecode: {
2689
2690                         z_enc_stream *stream;
2691                         int err;
2692         
2693                         stream = (z_enc_stream *) malloc(sizeof(z_enc_stream));
2694                         memset(stream, 0, sizeof(z_enc_stream));
2695                         stream->OutBuf.BufSize = 4*SIZ; /// TODO 64
2696                         stream->OutBuf.buf = (char*)malloc(stream->OutBuf.BufSize);
2697                 
2698                         err = inflateInit(&stream->zstream);
2699
2700                         if (err != Z_OK) {
2701                                 StrBufDestroyStreamContext(type, (vStreamT**) &stream, Err);
2702                                 *Err = zError(err);
2703                                 return NULL;
2704                         }
2705                         return (vStreamT*) stream;
2706         
2707                 }
2708
2709                 case eZLibEncode: {
2710                         z_enc_stream *stream;
2711                         int err;
2712         
2713                         stream = (z_enc_stream *) malloc(sizeof(z_enc_stream));
2714                         memset(stream, 0, sizeof(z_enc_stream));
2715                         stream->OutBuf.BufSize = 4*SIZ; /// todo 64
2716                         stream->OutBuf.buf = (char*)malloc(stream->OutBuf.BufSize);
2717                         /* write gzip header */
2718                         stream->OutBuf.BufUsed = snprintf
2719                                 (stream->OutBuf.buf,
2720                                 stream->OutBuf.BufSize, 
2721                                 "%c%c%c%c%c%c%c%c%c%c",
2722                                 gz_magic[0], gz_magic[1], Z_DEFLATED,
2723                                 0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /* xflags */ ,
2724                                 OS_CODE);
2725         
2726                         err = deflateInit2(&stream->zstream,
2727                                         ZLibCompressionRatio,
2728                                         Z_DEFLATED,
2729                                         -MAX_WBITS,
2730                                         DEF_MEM_LEVEL,
2731                                         Z_DEFAULT_STRATEGY);
2732                         if (err != Z_OK) {
2733                                 StrBufDestroyStreamContext(type, (vStreamT**) &stream, Err);
2734                                 *Err = zError(err);
2735                                 return NULL;
2736                         }
2737                         return (vStreamT*) stream;
2738                 }
2739
2740                 case eEmtyCodec:
2741                         /// TODO
2742                         break;
2743                 }
2744         return NULL;
2745 }
2746
2747
2748 int StrBufDestroyStreamContext(eStreamType type, vStreamT **vStream, const char **Err) {
2749         int err;
2750         int rc = 0;
2751         *Err = NULL;
2752         
2753         if ((vStream == NULL) || (*vStream==NULL)) {
2754                 *Err = strerror(EINVAL);
2755                 return EINVAL;
2756         }
2757         switch (type)
2758         {
2759         //case eBase64Encode:
2760         //case eBase64Decode:
2761                 //free(*vStream);
2762                 //*vStream = NULL;
2763                 //break;
2764         case eZLibDecode:
2765         {
2766                 z_enc_stream *stream = (z_enc_stream *)*vStream;
2767                 (void)inflateEnd(&stream->zstream);
2768                 free(stream->OutBuf.buf);
2769                 free(stream);
2770         }
2771         case eZLibEncode:
2772         {
2773                 z_enc_stream *stream = (z_enc_stream *)*vStream;
2774                 err = deflateEnd(&stream->zstream);
2775                 if (err != Z_OK) {
2776                         *Err = zError(err);
2777                         rc = -1;
2778                 }
2779                 free(stream->OutBuf.buf);
2780                 free(stream);
2781                 *vStream = NULL;
2782                 break;
2783         }
2784         case eEmtyCodec: 
2785                 break; /// TODO
2786         }
2787         return rc;
2788 }
2789
2790 int StrBufStreamTranscode(eStreamType type, IOBuffer *Target, IOBuffer *In, const char* pIn, long pInLen, vStreamT *vStream, int LastChunk, const char **Err) {
2791         int rc = 0;
2792         switch (type)
2793         {
2794         //case eBase64Encode:
2795         //{
2796                 // ???
2797         //}
2798         //break;
2799         //case eBase64Decode:
2800         //{
2801                 //// ???
2802         //}
2803         //break;
2804         case eZLibEncode:
2805         {
2806                 z_enc_stream *stream = (z_enc_stream *)vStream;
2807                 int org_outbuf_len = stream->OutBuf.BufUsed;
2808                 int err;
2809                 unsigned int chunkavail;
2810
2811                 if (In->ReadWritePointer != NULL)
2812                 {
2813                         stream->zstream.next_in = (Bytef *) In->ReadWritePointer;
2814                         stream->zstream.avail_in = (uInt) In->Buf->BufUsed - 
2815                                 (In->ReadWritePointer - In->Buf->buf);
2816                 }
2817                 else
2818                 {
2819                         stream->zstream.next_in = (Bytef *) In->Buf->buf;
2820                         stream->zstream.avail_in = (uInt) In->Buf->BufUsed;
2821                 }
2822                 
2823                 stream->zstream.next_out = (unsigned char*)stream->OutBuf.buf + stream->OutBuf.BufUsed;
2824                 stream->zstream.avail_out = chunkavail = (uInt) stream->OutBuf.BufSize - stream->OutBuf.BufUsed;
2825
2826                 err = deflate(&stream->zstream,  (LastChunk) ? Z_FINISH : Z_NO_FLUSH);
2827
2828                 stream->OutBuf.BufUsed += (chunkavail - stream->zstream.avail_out);
2829
2830                 if (Target && (LastChunk || (stream->OutBuf.BufUsed != org_outbuf_len))) {
2831                         iSwapBuffers(Target->Buf, &stream->OutBuf);
2832                 }
2833
2834                 if (stream->zstream.avail_in == 0) {
2835                         FlushStrBuf(In->Buf);
2836                         In->ReadWritePointer = NULL;
2837                 }
2838                 else {
2839                         if (stream->zstream.avail_in < 64) {
2840                                 memmove(In->Buf->buf,
2841                                         In->Buf->buf + In->Buf->BufUsed - stream->zstream.avail_in,
2842                                         stream->zstream.avail_in);
2843
2844                                 In->Buf->BufUsed = stream->zstream.avail_in;
2845                                 In->Buf->buf[In->Buf->BufUsed] = '\0';
2846                         }
2847                         else {
2848                                 In->ReadWritePointer = In->Buf->buf + (In->Buf->BufUsed - stream->zstream.avail_in);
2849                         }
2850                 }
2851                 rc = (LastChunk && (err != Z_FINISH));
2852                 if (!rc && (err != Z_OK)) {
2853                         *Err = zError(err);
2854                 }
2855                 
2856         }
2857         break;
2858         case eZLibDecode: {
2859                 z_enc_stream *stream = (z_enc_stream *)vStream;
2860                 int org_outbuf_len = stream->zstream.total_out;
2861                 int err;
2862
2863                 if ((stream->zstream.avail_out != 0) && (stream->zstream.next_in != NULL)) {
2864                         if (In->ReadWritePointer != NULL) {
2865                                 stream->zstream.next_in = (Bytef *) In->ReadWritePointer;
2866                                 stream->zstream.avail_in = (uInt) In->Buf->BufUsed - (In->ReadWritePointer - In->Buf->buf);
2867                         }
2868                         else {
2869                                 stream->zstream.next_in = (Bytef *) In->Buf->buf;
2870                                 stream->zstream.avail_in = (uInt) In->Buf->BufUsed;
2871                         }
2872                 }
2873
2874                 stream->zstream.next_out = (unsigned char*)stream->OutBuf.buf + stream->OutBuf.BufUsed;
2875                 stream->zstream.avail_out = (uInt) stream->OutBuf.BufSize - stream->OutBuf.BufUsed;
2876
2877                 err = inflate(&stream->zstream, Z_NO_FLUSH);
2878
2879                 ///assert(ret != Z_STREAM_ERROR);  /* state not clobbered * /
2880                 switch (err) {
2881                 case Z_NEED_DICT:
2882                         err = Z_DATA_ERROR;     /* and fall through */
2883
2884                 case Z_DATA_ERROR:
2885                         *Err = zError(err);
2886                 case Z_MEM_ERROR:
2887                         (void)inflateEnd(&stream->zstream);
2888                         return err;
2889                 }
2890
2891                 stream->OutBuf.BufUsed += stream->zstream.total_out + org_outbuf_len;
2892
2893                 if (Target) iSwapBuffers(Target->Buf, &stream->OutBuf);
2894
2895                 if (stream->zstream.avail_in == 0) {
2896                         FlushStrBuf(In->Buf);
2897                         In->ReadWritePointer = NULL;
2898                 }
2899                 else {
2900                         if (stream->zstream.avail_in < 64) {
2901                                 memmove(In->Buf->buf,
2902                                         In->Buf->buf + In->Buf->BufUsed - stream->zstream.avail_in,
2903                                         stream->zstream.avail_in);
2904
2905                                 In->Buf->BufUsed = stream->zstream.avail_in;
2906                                 In->Buf->buf[In->Buf->BufUsed] = '\0';
2907                         }
2908                         else {
2909                                 
2910                                 In->ReadWritePointer = In->Buf->buf + (In->Buf->BufUsed - stream->zstream.avail_in);
2911                         }
2912                 }
2913         }
2914                 break;
2915         case eEmtyCodec: {
2916
2917         }
2918                 break; /// TODO
2919         }
2920         return rc;
2921 }
2922
2923 /**
2924  *  decode a buffer from base 64 encoding; destroys original
2925  *  Buf Buffor to transform
2926  */
2927 int StrBufDecodeHex(StrBuf *Buf) {
2928         unsigned int ch;
2929         char *pch, *pche, *pchi;
2930
2931         if (Buf == NULL) return -1;
2932
2933         pch = pchi = Buf->buf;
2934         pche = pch + Buf->BufUsed;
2935
2936         while (pchi < pche){
2937                 ch = decode_hex(pchi);
2938                 *pch = ch;
2939                 pch ++;
2940                 pchi += 2;
2941         }
2942
2943         *pch = '\0';
2944         Buf->BufUsed = pch - Buf->buf;
2945         return Buf->BufUsed;
2946 }
2947
2948 /**
2949  *  replace all chars >0x20 && < 0x7F with Mute
2950  *  Mute char to put over invalid chars
2951  *  Buf Buffor to transform
2952  */
2953 int StrBufSanitizeAscii(StrBuf *Buf, const char Mute)
2954 {
2955         unsigned char *pch;
2956
2957         if (Buf == NULL) return -1;
2958         pch = (unsigned char *)Buf->buf;
2959         while (pch < (unsigned char *)Buf->buf + Buf->BufUsed) {
2960                 if ((*pch < 0x20) || (*pch > 0x7F))
2961                         *pch = Mute;
2962                 pch ++;
2963         }
2964         return Buf->BufUsed;
2965 }
2966
2967
2968 /**
2969  *  remove escaped strings from i.e. the url string (like %20 for blanks)
2970  *  Buf Buffer to translate
2971  *  StripBlanks Reduce several blanks to one?
2972  */
2973 long StrBufUnescape(StrBuf *Buf, int StripBlanks)
2974 {
2975         int a, b;
2976         char hex[3];
2977         long len;
2978
2979         if (Buf == NULL)
2980                 return -1;
2981
2982         while ((Buf->BufUsed > 0) && (isspace(Buf->buf[Buf->BufUsed - 1]))){
2983                 Buf->buf[Buf->BufUsed - 1] = '\0';
2984                 Buf->BufUsed --;
2985         }
2986
2987         a = 0; 
2988         while (a < Buf->BufUsed) {
2989                 if (Buf->buf[a] == '+')
2990                         Buf->buf[a] = ' ';
2991                 else if (Buf->buf[a] == '%') {
2992                         /* don't let % chars through, rather truncate the input. */
2993                         if (a + 2 > Buf->BufUsed) {
2994                                 Buf->buf[a] = '\0';
2995                                 Buf->BufUsed = a;
2996                         }
2997                         else {                  
2998                                 hex[0] = Buf->buf[a + 1];
2999                                 hex[1] = Buf->buf[a + 2];
3000                                 hex[2] = 0;
3001                                 b = 0;
3002                                 sscanf(hex, "%02x", &b);
3003                                 Buf->buf[a] = (char) b;
3004                                 len = Buf->BufUsed - a - 2;
3005                                 if (len > 0)
3006                                         memmove(&Buf->buf[a + 1], &Buf->buf[a + 3], len);
3007                         
3008                                 Buf->BufUsed -=2;
3009                         }
3010                 }
3011                 a++;
3012         }
3013         return a;
3014 }
3015
3016
3017 /**
3018  *      RFC2047-encode a header field if necessary.
3019  *              If no non-ASCII characters are found, the string
3020  *              will be copied verbatim without encoding.
3021  *
3022  *      target          Target buffer.
3023  *      source          Source string to be encoded.
3024  * @returns     encoded length; -1 if non success.
3025  */
3026 int StrBufRFC2047encode(StrBuf **target, const StrBuf *source)
3027 {
3028         const char headerStr[] = "=?UTF-8?Q?";
3029         int need_to_encode = 0;
3030         int i = 0;
3031         unsigned char ch;
3032
3033         if ((source == NULL) || 
3034             (target == NULL))
3035             return -1;
3036
3037         while ((i < source->BufUsed) &&
3038                (!IsEmptyStr (&source->buf[i])) &&
3039                (need_to_encode == 0)) {
3040                 if (((unsigned char) source->buf[i] < 32) || 
3041                     ((unsigned char) source->buf[i] > 126)) {
3042                         need_to_encode = 1;
3043                 }
3044                 i++;
3045         }
3046
3047         if (!need_to_encode) {
3048                 if (*target == NULL) {
3049                         *target = NewStrBufPlain(source->buf, source->BufUsed);
3050                 }
3051                 else {
3052                         FlushStrBuf(*target);
3053                         StrBufAppendBuf(*target, source, 0);
3054                 }
3055                 if (*target != 0)
3056                         return (*target)->BufUsed;
3057                 else
3058                         return 0;
3059         }
3060         if (*target == NULL)
3061                 *target = NewStrBufPlain(NULL, sizeof(headerStr) + source->BufUsed * 2);
3062         else if (sizeof(headerStr) + source->BufUsed >= (*target)->BufSize)
3063                 IncreaseBuf(*target, sizeof(headerStr) + source->BufUsed, 0);
3064         memcpy ((*target)->buf, headerStr, sizeof(headerStr) - 1);
3065         (*target)->BufUsed = sizeof(headerStr) - 1;
3066         for (i=0; (i < source->BufUsed); ++i) {
3067                 if ((*target)->BufUsed + 4 >= (*target)->BufSize)
3068                         IncreaseBuf(*target, 1, 0);
3069                 ch = (unsigned char) source->buf[i];
3070                 if ((ch  <  32) || 
3071                     (ch  > 126) || 
3072                     (ch == '=') ||
3073                     (ch == '?') ||
3074                     (ch == '_') ||
3075                     (ch == '[') ||
3076                     (ch == ']')   )
3077                 {
3078                         sprintf(&(*target)->buf[(*target)->BufUsed], "=%02X", ch);
3079                         (*target)->BufUsed += 3;
3080                 }
3081                 else {
3082                         if (ch == ' ')
3083                                 (*target)->buf[(*target)->BufUsed] = '_';
3084                         else
3085                                 (*target)->buf[(*target)->BufUsed] = ch;
3086                         (*target)->BufUsed++;
3087                 }
3088         }
3089         
3090         if ((*target)->BufUsed + 4 >= (*target)->BufSize)
3091                 IncreaseBuf(*target, 1, 0);
3092
3093         (*target)->buf[(*target)->BufUsed++] = '?';
3094         (*target)->buf[(*target)->BufUsed++] = '=';
3095         (*target)->buf[(*target)->BufUsed] = '\0';
3096         return (*target)->BufUsed;;
3097 }
3098
3099 // Quoted-Printable encode a message; make it < 80 columns width.
3100 // source       Source string to be encoded.
3101 // returns      buffer with encoded message.
3102 StrBuf *StrBufQuotedPrintableEncode(const StrBuf *EncodeMe) {
3103         StrBuf *OutBuf;
3104         char *Optr, *OEptr;
3105         const char *ptr, *eptr;
3106         unsigned char ch;
3107         int LinePos;
3108
3109         OutBuf = NewStrBufPlain(NULL, StrLength(EncodeMe) * 4);
3110         Optr = OutBuf->buf;
3111         OEptr = OutBuf->buf + OutBuf->BufSize;
3112         ptr = EncodeMe->buf;
3113         eptr = EncodeMe->buf + EncodeMe->BufUsed;
3114         LinePos = 0;
3115
3116         while (ptr < eptr) {
3117                 if (Optr + 4 >= OEptr) {
3118                         long Offset;
3119                         Offset = Optr - OutBuf->buf;
3120                         OutBuf->BufUsed = Optr - OutBuf->buf;
3121                         IncreaseBuf(OutBuf, 1, 0);
3122                         Optr = OutBuf->buf + Offset;
3123                         OEptr = OutBuf->buf + OutBuf->BufSize;
3124                 }
3125                 if (*ptr == '\r') {             // ignore carriage returns
3126                         ptr ++;
3127                 }
3128                 else if (*ptr == '\n') {        // hard line break
3129                         memcpy(Optr, HKEY("=0A"));
3130                         Optr += 3;
3131                         LinePos += 3;
3132                         ptr ++;
3133                 }
3134                 else if (( (*ptr >= 32) && (*ptr <= 60) ) || ( (*ptr >= 62) && (*ptr <= 126) )) {
3135                         *Optr = *ptr;
3136                         Optr ++;
3137                         ptr ++;
3138                         LinePos ++;
3139                 }
3140                 else {
3141                         ch = *ptr;
3142                         *Optr = '=';
3143                         Optr ++;
3144                         *Optr = HexList[ch][0];
3145                         Optr ++;
3146                         *Optr = HexList[ch][1];
3147                         Optr ++;
3148                         LinePos += 3;
3149                         ptr ++;
3150                 }
3151
3152                 if (LinePos > 72) {             // soft line break
3153                         if (isspace(*(Optr - 1))) {
3154                                 ch = *(Optr - 1);
3155                                 Optr --;
3156                                 *Optr = '=';
3157                                 Optr ++;
3158                                 *Optr = HexList[ch][0];
3159                                 Optr ++;
3160                                 *Optr = HexList[ch][1];
3161                                 Optr ++;
3162                                 LinePos += 3;
3163                         }
3164                         *Optr = '=';
3165                         Optr ++;
3166                         *Optr = '\n';
3167                         Optr ++;
3168                         LinePos = 0;
3169                 }
3170         }
3171         *Optr = '\0';
3172         OutBuf->BufUsed = Optr - OutBuf->buf;
3173
3174         return OutBuf;
3175 }
3176
3177
3178 static void AddRecipient(StrBuf *Target, StrBuf *UserName, StrBuf *EmailAddress, StrBuf *EncBuf) {
3179         int QuoteMe = 0;
3180
3181         if (StrLength(Target) > 0) StrBufAppendBufPlain(Target, HKEY(", "), 0);
3182         if (strchr(ChrPtr(UserName), ',') != NULL) QuoteMe = 1;
3183
3184         if (QuoteMe)  StrBufAppendBufPlain(Target, HKEY("\""), 0);
3185         StrBufRFC2047encode(&EncBuf, UserName);
3186         StrBufAppendBuf(Target, EncBuf, 0);
3187         if (QuoteMe)  StrBufAppendBufPlain(Target, HKEY("\" "), 0);
3188         else          StrBufAppendBufPlain(Target, HKEY(" "), 0);
3189
3190         if (StrLength(EmailAddress) > 0){
3191                 StrBufAppendBufPlain(Target, HKEY("<"), 0);
3192                 StrBufAppendBuf(Target, EmailAddress, 0); /* TODO: what about IDN???? */
3193                 StrBufAppendBufPlain(Target, HKEY(">"), 0);
3194         }
3195 }
3196
3197
3198 /**
3199  * \brief QP encode parts of an email TO/CC/BCC vector, and strip/filter invalid parts
3200  * \param Recp Source list of email recipients
3201  * \param UserName Temporary buffer for internal use; Please provide valid buffer.
3202  * \param EmailAddress Temporary buffer for internal use; Please provide valid buffer.
3203  * \param EncBuf Temporary buffer for internal use; Please provide valid buffer.
3204  * \returns encoded & sanitized buffer with the contents of Recp; Caller owns this memory.
3205  */
3206 StrBuf *StrBufSanitizeEmailRecipientVector(const StrBuf *Recp, StrBuf *UserName, StrBuf *EmailAddress, StrBuf *EncBuf) {
3207         StrBuf *Target;
3208         const char *pch, *pche;
3209         const char *UserStart, *UserEnd, *EmailStart, *EmailEnd, *At;
3210
3211         if ((Recp == NULL) || (StrLength(Recp) == 0))
3212                 return NULL;
3213
3214         pch = ChrPtr(Recp);
3215         pche = pch + StrLength(Recp);
3216
3217         if (!CheckEncode(pch, -1, pche))
3218                 return NewStrBufDup(Recp);
3219
3220         Target = NewStrBufPlain(NULL, StrLength(Recp));
3221
3222         while ((pch != NULL) && (pch < pche))
3223         {
3224                 while (isspace(*pch)) pch++;
3225                 UserEnd = EmailStart = EmailEnd = NULL;
3226                 
3227                 if ((*pch == '"') || (*pch == '\'')) {
3228                         UserStart = pch + 1;
3229                         
3230                         UserEnd = strchr(UserStart, *pch);
3231                         if (UserEnd == NULL) 
3232                                 break; ///TODO: Userfeedback??
3233                         EmailStart = UserEnd + 1;
3234                         while (isspace(*EmailStart))
3235                                 EmailStart++;
3236                         if (UserEnd == UserStart) {
3237                                 UserStart = UserEnd = NULL;
3238                         }
3239                         
3240                         if (*EmailStart == '<') {
3241                                 EmailStart++;
3242                                 EmailEnd = strchr(EmailStart, '>');
3243                                 if (EmailEnd == NULL)
3244                                         EmailEnd = strchr(EmailStart, ',');
3245                                 
3246                         }
3247                         else {
3248                                 EmailEnd = strchr(EmailStart, ',');
3249                         }
3250                         if (EmailEnd == NULL)
3251                                 EmailEnd = pche;
3252                         pch = EmailEnd + 1;
3253                 }
3254                 else {
3255                         int gt = 0;
3256                         UserStart = pch;
3257                         EmailEnd = strchr(UserStart, ',');
3258                         if (EmailEnd == NULL) {
3259                                 EmailEnd = strchr(pch, '>');
3260                                 pch = NULL;
3261                                 if (EmailEnd != NULL) {
3262                                         gt = 1;
3263                                 }
3264                                 else {
3265                                         EmailEnd = pche;
3266                                 }
3267                         }
3268                         else {
3269
3270                                 pch = EmailEnd + 1;
3271                                 while ((EmailEnd > UserStart) && !gt &&
3272                                        ((*EmailEnd == ',') ||
3273                                         (*EmailEnd == '>') ||
3274                                         (isspace(*EmailEnd))))
3275                                 {
3276                                         if (*EmailEnd == '>')
3277                                                 gt = 1;
3278                                         else 
3279                                                 EmailEnd--;
3280                                 }
3281                                 if (EmailEnd == UserStart)
3282                                         break;
3283                         }
3284                         if (gt) {
3285                                 EmailStart = strchr(UserStart, '<');
3286                                 if ((EmailStart == NULL) || (EmailStart > EmailEnd))
3287                                         break;
3288                                 UserEnd = EmailStart;
3289
3290                                 while ((UserEnd > UserStart) && 
3291                                        isspace (*(UserEnd - 1)))
3292                                         UserEnd --;
3293                                 EmailStart ++;
3294                                 if (UserStart >= UserEnd)
3295                                         UserStart = UserEnd = NULL;
3296                         }
3297                         else { /* this is a local recipient... no domain, just a realname */
3298                                 EmailStart = UserStart;
3299                                 At = strchr(EmailStart, '@');
3300                                 if (At == NULL) {
3301                                         UserEnd = EmailEnd;
3302                                         EmailEnd = NULL;
3303                                 }
3304                                 else {
3305                                         EmailStart = UserStart;
3306                                         UserStart = NULL;
3307                                 }
3308                         }
3309                 }
3310
3311                 if ((UserStart != NULL) && (UserEnd != NULL))
3312                         StrBufPlain(UserName, UserStart, UserEnd - UserStart);
3313                 else if ((UserStart != NULL) && (UserEnd == NULL))
3314                         StrBufPlain(UserName, UserStart, UserEnd - UserStart);
3315                 else
3316                         FlushStrBuf(UserName);
3317
3318                 if ((EmailStart != NULL) && (EmailEnd != NULL))
3319                         StrBufPlain(EmailAddress, EmailStart, EmailEnd - EmailStart);
3320                 else if ((EmailStart != NULL) && (EmailEnd == NULL))
3321                         StrBufPlain(EmailAddress, EmailStart, EmailEnd - pche);
3322                 else 
3323                         FlushStrBuf(EmailAddress);
3324
3325                 AddRecipient(Target, UserName, EmailAddress, EncBuf);
3326
3327                 if (pch == NULL)
3328                         break;
3329                 
3330                 if ((pch != NULL) && (*pch == ','))
3331                         pch ++;
3332                 if (pch != NULL) while (isspace(*pch))
3333                         pch ++;
3334         }
3335         return Target;
3336 }
3337
3338
3339 /**
3340  *  replaces all occurances of 'search' by 'replace'
3341  *  buf Buffer to modify
3342  *  search character to search
3343  *  replace character to replace search by
3344  */
3345 void StrBufReplaceChars(StrBuf *buf, char search, char replace) {
3346         long i;
3347         if (buf == NULL)
3348                 return;
3349         for (i=0; i<buf->BufUsed; i++)
3350                 if (buf->buf[i] == search)
3351                         buf->buf[i] = replace;
3352
3353 }
3354
3355 /**
3356  *  removes all \\r s from the string, or replaces them with \n if its not a combination of both.
3357  *  buf Buffer to modify
3358  */
3359 void StrBufToUnixLF(StrBuf *buf)
3360 {
3361         char *pche, *pchS, *pchT;
3362         if (buf == NULL)
3363                 return;
3364
3365         pche = buf->buf + buf->BufUsed;
3366         pchS = pchT = buf->buf;
3367         while (pchS < pche)
3368         {
3369                 if (*pchS == '\r')
3370                 {
3371                         pchS ++;
3372                         if (*pchS != '\n') {
3373                                 *pchT = '\n';
3374                                 pchT++;
3375                         }
3376                 }
3377                 *pchT = *pchS;
3378                 pchT++; pchS++;
3379         }
3380         *pchT = '\0';
3381         buf->BufUsed = pchT - buf->buf;
3382 }
3383
3384
3385 /*******************************************************************************
3386  *                 Iconv Wrapper; RFC822 de/encoding                           *
3387  *******************************************************************************/
3388
3389 /**
3390  *  Wrapper around iconv_open()
3391  * Our version adds aliases for non-standard Microsoft charsets
3392  * such as 'MS950', aliasing them to names like 'CP950'
3393  *
3394  *  tocode      Target encoding
3395  *  fromcode    Source encoding
3396  *  pic           anonimized pointer to iconv struct
3397  */
3398 void  ctdl_iconv_open(const char *tocode, const char *fromcode, void *pic)
3399 {
3400 #ifdef HAVE_ICONV
3401         iconv_t ic = (iconv_t)(-1) ;
3402         ic = iconv_open(tocode, fromcode);
3403         if (ic == (iconv_t)(-1) ) {
3404                 char alias_fromcode[64];
3405                 if ( (strlen(fromcode) == 5) && (!strncasecmp(fromcode, "MS", 2)) ) {
3406                         safestrncpy(alias_fromcode, fromcode, sizeof alias_fromcode);
3407                         alias_fromcode[0] = 'C';
3408                         alias_fromcode[1] = 'P';
3409                         ic = iconv_open(tocode, alias_fromcode);
3410                 }
3411         }
3412         *(iconv_t *)pic = ic;
3413 #endif
3414 }
3415
3416
3417 /**
3418  *  find one chunk of a RFC822 encoded string
3419  *  Buffer where to search
3420  *  bptr where to start searching
3421  * @returns found position, NULL if none.
3422  */
3423 static inline const char *FindNextEnd (const StrBuf *Buf, const char *bptr)
3424 {
3425         const char * end;
3426         /* Find the next ?Q? */
3427         if (Buf->BufUsed - (bptr - Buf->buf)  < 6)
3428                 return NULL;
3429
3430         end = strchr(bptr + 2, '?');
3431
3432         if (end == NULL)
3433                 return NULL;
3434
3435         if ((Buf->BufUsed - (end - Buf->buf) > 3) &&
3436             (((*(end + 1) == 'B') || (*(end + 1) == 'Q')) ||
3437              ((*(end + 1) == 'b') || (*(end + 1) == 'q'))) && 
3438             (*(end + 2) == '?')) {
3439                 /* skip on to the end of the cluster, the next ?= */
3440                 end = strstr(end + 3, "?=");
3441         }
3442         else
3443                 /* sort of half valid encoding, try to find an end. */
3444                 end = strstr(bptr, "?=");
3445         return end;
3446 }
3447
3448
3449
3450 /**
3451  *  convert one buffer according to the preselected iconv pointer PIC
3452  *  ConvertBuf buffer we need to translate
3453  *  TmpBuf To share a workbuffer over several iterations. prepare to have it filled with useless stuff afterwards.
3454  *  pic Pointer to the iconv-session Object
3455  */
3456 void StrBufConvert(StrBuf *ConvertBuf, StrBuf *TmpBuf, void *pic)
3457 {
3458 #ifdef HAVE_ICONV
3459         long trycount = 0;
3460         size_t siz;
3461         iconv_t ic;
3462         char *ibuf;                     /**< Buffer of characters to be converted */
3463         char *obuf;                     /**< Buffer for converted characters */
3464         size_t ibuflen;                 /**< Length of input buffer */
3465         size_t obuflen;                 /**< Length of output buffer */
3466
3467
3468         if ((ConvertBuf == NULL) || (TmpBuf == NULL))
3469                 return;
3470
3471         /* since we're converting to utf-8, one glyph may take up to 6 bytes */
3472         if (ConvertBuf->BufUsed * 6 >= TmpBuf->BufSize)
3473                 IncreaseBuf(TmpBuf, 0, ConvertBuf->BufUsed * 6);
3474 TRYAGAIN:
3475         ic = *(iconv_t*)pic;
3476         ibuf = ConvertBuf->buf;
3477         ibuflen = ConvertBuf->BufUsed;
3478         obuf = TmpBuf->buf;
3479         obuflen = TmpBuf->BufSize;
3480         
3481         siz = iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
3482
3483         if (siz < 0) {
3484                 if (errno == E2BIG) {
3485                         trycount ++;                    
3486                         IncreaseBuf(TmpBuf, 0, 0);
3487                         if (trycount < 5) 
3488                                 goto TRYAGAIN;
3489
3490                 }
3491                 else if (errno == EILSEQ){ 
3492                         /* hm, invalid utf8 sequence... what to do now? */
3493                         /* An invalid multibyte sequence has been encountered in the input */
3494                 }
3495                 else if (errno == EINVAL) {
3496                         /* An incomplete multibyte sequence has been encountered in the input. */
3497                 }
3498
3499                 FlushStrBuf(TmpBuf);
3500         }
3501         else {
3502                 TmpBuf->BufUsed = TmpBuf->BufSize - obuflen;
3503                 TmpBuf->buf[TmpBuf->BufUsed] = '\0';
3504                 
3505                 /* little card game: wheres the red lady? */
3506                 iSwapBuffers(ConvertBuf, TmpBuf);
3507                 FlushStrBuf(TmpBuf);
3508         }
3509 #endif
3510 }
3511
3512
3513 /**
3514  *  catches one RFC822 encoded segment, and decodes it.
3515  *  Target buffer to fill with result
3516  *  DecodeMe buffer with stuff to process
3517  *  SegmentStart points to our current segment in DecodeMe
3518  *  SegmentEnd Points to the end of our current segment in DecodeMe
3519  *  ConvertBuf Workbuffer shared between several iterations. Random content; needs to be valid
3520  *  ConvertBuf2 Workbuffer shared between several iterations. Random content; needs to be valid
3521  *  FoundCharset Characterset to default decoding to; if we find another we will overwrite it.
3522  */
3523 inline static void DecodeSegment(StrBuf *Target, 
3524                                  const StrBuf *DecodeMe, 
3525                                  const char *SegmentStart, 
3526                                  const char *SegmentEnd, 
3527                                  StrBuf *ConvertBuf,
3528                                  StrBuf *ConvertBuf2, 
3529                                  StrBuf *FoundCharset)
3530 {
3531         StrBuf StaticBuf;
3532         char charset[128];
3533         char encoding[16];
3534 #ifdef HAVE_ICONV
3535         iconv_t ic = (iconv_t)(-1);
3536 #else
3537         void *ic = NULL;
3538 #endif
3539         /* Now we handle foreign character sets properly encoded
3540          * in RFC2047 format.
3541          */
3542         StaticBuf.buf = (char*) SegmentStart; /*< it will just be read there... */
3543         StaticBuf.BufUsed = SegmentEnd - SegmentStart;
3544         StaticBuf.BufSize = DecodeMe->BufSize - (SegmentStart - DecodeMe->buf);
3545         extract_token(charset, SegmentStart, 1, '?', sizeof charset);
3546         if (FoundCharset != NULL) {
3547                 FlushStrBuf(FoundCharset);
3548                 StrBufAppendBufPlain(FoundCharset, charset, -1, 0);
3549         }
3550         extract_token(encoding, SegmentStart, 2, '?', sizeof encoding);
3551         StrBufExtract_token(ConvertBuf, &StaticBuf, 3, '?');
3552         
3553         *encoding = toupper(*encoding);
3554         if (*encoding == 'B') { /**< base64 */
3555                 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3556                         IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3557                 ConvertBuf2->BufUsed = CtdlDecodeBase64(ConvertBuf2->buf, 
3558                                                         ConvertBuf->buf, 
3559                                                         ConvertBuf->BufUsed);
3560         }
3561         else if (*encoding == 'Q') {    /**< quoted-printable */
3562                 long pos;
3563                 
3564                 pos = 0;
3565                 while (pos < ConvertBuf->BufUsed)
3566                 {
3567                         if (ConvertBuf->buf[pos] == '_') 
3568                                 ConvertBuf->buf[pos] = ' ';
3569                         pos++;
3570                 }
3571                 
3572                 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3573                         IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3574
3575                 ConvertBuf2->BufUsed = CtdlDecodeQuotedPrintable(
3576                         ConvertBuf2->buf, 
3577                         ConvertBuf->buf,
3578                         ConvertBuf->BufUsed);
3579         }
3580         else {
3581                 StrBufAppendBuf(ConvertBuf2, ConvertBuf, 0);
3582         }
3583 #ifdef HAVE_ICONV
3584         ctdl_iconv_open("UTF-8", charset, &ic);
3585         if (ic != (iconv_t)(-1) ) {             
3586 #endif
3587                 StrBufConvert(ConvertBuf2, ConvertBuf, &ic);
3588                 StrBufAppendBuf(Target, ConvertBuf2, 0);
3589 #ifdef HAVE_ICONV
3590                 iconv_close(ic);
3591         }
3592         else {
3593                 StrBufAppendBufPlain(Target, HKEY("(unreadable)"), 0);
3594         }
3595 #endif
3596 }
3597
3598 /**
3599  *  Handle subjects with RFC2047 encoding such as: [deprecated old syntax!]
3600  * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3601  *  Target where to put the decoded string to 
3602  *  DecodeMe buffer with encoded string
3603  *  DefaultCharset if we don't find one, which should we use?
3604  *  FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string, 
3605  *        put it here for later use where no string might be known.
3606  */
3607 void StrBuf_RFC822_to_Utf8(StrBuf *Target, const StrBuf *DecodeMe, const StrBuf* DefaultCharset, StrBuf *FoundCharset)
3608 {
3609         StrBuf *ConvertBuf;
3610         StrBuf *ConvertBuf2;
3611         ConvertBuf = NewStrBufPlain(NULL, StrLength(DecodeMe));
3612         ConvertBuf2 = NewStrBufPlain(NULL, StrLength(DecodeMe));
3613         
3614         StrBuf_RFC822_2_Utf8(Target, 
3615                              DecodeMe, 
3616                              DefaultCharset, 
3617                              FoundCharset, 
3618                              ConvertBuf, 
3619                              ConvertBuf2);
3620         FreeStrBuf(&ConvertBuf);
3621         FreeStrBuf(&ConvertBuf2);
3622 }
3623
3624 /**
3625  *  Handle subjects with RFC2047 encoding such as:
3626  * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3627  *  Target where to put the decoded string to 
3628  *  DecodeMe buffer with encoded string
3629  *  DefaultCharset if we don't find one, which should we use?
3630  *  FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string, 
3631  *        put it here for later use where no string might be known.
3632  *  ConvertBuf workbuffer. feed in, you shouldn't care about its content.
3633  *  ConvertBuf2 workbuffer. feed in, you shouldn't care about its content.
3634  */
3635 void StrBuf_RFC822_2_Utf8(StrBuf *Target, 
3636                           const StrBuf *DecodeMe, 
3637                           const StrBuf* DefaultCharset, 
3638                           StrBuf *FoundCharset, 
3639                           StrBuf *ConvertBuf, 
3640                           StrBuf *ConvertBuf2)
3641 {
3642         StrBuf *DecodedInvalidBuf = NULL;
3643         const StrBuf *DecodeMee = DecodeMe;
3644         const char *start, *end, *next, *nextend, *ptr = NULL;
3645 #ifdef HAVE_ICONV
3646         iconv_t ic = (iconv_t)(-1) ;
3647 #endif
3648         const char *eptr;
3649         int passes = 0;
3650         int i;
3651         int illegal_non_rfc2047_encoding = 0;
3652
3653
3654         if (DecodeMe == NULL)
3655                 return;
3656         /* Sometimes, badly formed messages contain strings which were simply
3657          *  written out directly in some foreign character set instead of
3658          *  using RFC2047 encoding.  This is illegal but we will attempt to
3659          *  handle it anyway by converting from a user-specified default
3660          *  charset to UTF-8 if we see any nonprintable characters.
3661          */
3662         
3663         for (i=0; i<DecodeMe->BufUsed; ++i) {
3664                 if ((DecodeMe->buf[i] < 32) || (DecodeMe->buf[i] > 126)) {
3665                         illegal_non_rfc2047_encoding = 1;
3666                         break;
3667                 }
3668         }
3669
3670         if ((illegal_non_rfc2047_encoding) &&
3671             (strcasecmp(ChrPtr(DefaultCharset), "UTF-8")) && 
3672             (strcasecmp(ChrPtr(DefaultCharset), "us-ascii")) )
3673         {
3674 #ifdef HAVE_ICONV
3675                 ctdl_iconv_open("UTF-8", ChrPtr(DefaultCharset), &ic);
3676                 if (ic != (iconv_t)(-1) ) {
3677                         DecodedInvalidBuf = NewStrBufDup(DecodeMe);
3678                         StrBufConvert(DecodedInvalidBuf, ConvertBuf, &ic);///TODO: don't void const?
3679                         DecodeMee = DecodedInvalidBuf;
3680                         iconv_close(ic);
3681                 }
3682 #endif
3683         }
3684
3685         /* pre evaluate the first pair */
3686         end = NULL;
3687         start = strstr(DecodeMee->buf, "=?");
3688         eptr = DecodeMee->buf + DecodeMee->BufUsed;
3689         if (start != NULL) 
3690                 end = FindNextEnd (DecodeMee, start + 2);
3691         else {
3692                 StrBufAppendBuf(Target, DecodeMee, 0);
3693                 FreeStrBuf(&DecodedInvalidBuf);
3694                 return;
3695         }
3696
3697
3698         if (start != DecodeMee->buf) {
3699                 long nFront;
3700                 
3701                 nFront = start - DecodeMee->buf;
3702                 StrBufAppendBufPlain(Target, DecodeMee->buf, nFront, 0);
3703         }
3704         /*
3705          * Since spammers will go to all sorts of absurd lengths to get their
3706          * messages through, there are LOTS of corrupt headers out there.
3707          * So, prevent a really badly formed RFC2047 header from throwing
3708          * this function into an infinite loop.
3709          */
3710         while ((start != NULL) && 
3711                (end != NULL) && 
3712                (start < eptr) && 
3713                (end < eptr) && 
3714                (passes < 20))
3715         {
3716                 passes++;
3717                 DecodeSegment(Target, 
3718                               DecodeMee, 
3719                               start, 
3720                               end, 
3721                               ConvertBuf,
3722                               ConvertBuf2,
3723                               FoundCharset);
3724                 
3725                 next = strstr(end, "=?");
3726                 nextend = NULL;
3727                 if ((next != NULL) && 
3728                     (next < eptr))
3729                         nextend = FindNextEnd(DecodeMee, next);
3730                 if (nextend == NULL)
3731                         next = NULL;
3732
3733                 /* did we find two partitions */
3734                 if ((next != NULL) && 
3735                     ((next - end) > 2))
3736                 {
3737                         ptr = end + 2;
3738                         while ((ptr < next) && 
3739                                (isspace(*ptr) ||
3740                                 (*ptr == '\r') ||
3741                                 (*ptr == '\n') || 
3742                                 (*ptr == '\t')))
3743                                 ptr ++;
3744                         /* 
3745                          * did we find a gab just filled with blanks?
3746                          * if not, copy its stuff over.
3747                          */
3748                         if (ptr != next)
3749                         {
3750                                 StrBufAppendBufPlain(Target, 
3751                                                      end + 2, 
3752                                                      next - end - 2,
3753                                                      0);
3754                         }
3755                 }
3756                 /* our next-pair is our new first pair now. */
3757                 ptr = end + 2;
3758                 start = next;
3759                 end = nextend;
3760         }
3761         end = ptr;
3762         nextend = DecodeMee->buf + DecodeMee->BufUsed;
3763         if ((end != NULL) && (end < nextend)) {
3764                 ptr = end;
3765                 while ( (ptr < nextend) &&
3766                         (isspace(*ptr) ||
3767                          (*ptr == '\r') ||
3768                          (*ptr == '\n') || 
3769                          (*ptr == '\t')))
3770                         ptr ++;
3771                 if (ptr < nextend)
3772                         StrBufAppendBufPlain(Target, end, nextend - end, 0);
3773         }
3774         FreeStrBuf(&DecodedInvalidBuf);
3775 }
3776
3777 /*******************************************************************************
3778  *                   Manipulating UTF-8 Strings                                *
3779  *******************************************************************************/
3780
3781 /**
3782  *  evaluate the length of an utf8 special character sequence
3783  *  Char the character to examine
3784  * @returns width of utf8 chars in bytes; if the sequence is broken 0 is returned; 1 if its simply ASCII.
3785  */
3786 static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE)
3787 {
3788         int n = 0;
3789         unsigned char test = (1<<7);
3790
3791         if ((*CharS & 0xC0) != 0xC0) 
3792                 return 1;
3793
3794         while ((n < 8) && 
3795                ((test & ((unsigned char)*CharS)) != 0)) 
3796         {
3797                 test = test >> 1;
3798                 n ++;
3799         }
3800         if ((n > 6) || ((CharE - CharS) < n))
3801                 n = 0;
3802         return n;
3803 }
3804
3805 /**
3806  *  detect whether this char starts an utf-8 encoded char
3807  *  Char character to inspect
3808  * @returns yes or no
3809  */
3810 static inline int Ctdl_IsUtf8SequenceStart(const char Char)
3811 {
3812 /** 11??.???? indicates an UTF8 Sequence. */
3813         return ((Char & 0xC0) == 0xC0);
3814 }
3815
3816 /**
3817  *  measure the number of glyphs in an UTF8 string...
3818  *  Buf string to measure
3819  * @returns the number of glyphs in Buf
3820  */
3821 long StrBuf_Utf8StrLen(StrBuf *Buf)
3822 {
3823         int n = 0;
3824         int m = 0;
3825         char *aptr, *eptr;
3826
3827         if ((Buf == NULL) || (Buf->BufUsed == 0))
3828                 return 0;
3829         aptr = Buf->buf;
3830         eptr = Buf->buf + Buf->BufUsed;
3831         while ((aptr < eptr) && (*aptr != '\0')) {
3832                 if (Ctdl_IsUtf8SequenceStart(*aptr)){
3833                         m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
3834                         while ((aptr < eptr) && (*aptr++ != '\0')&& (m-- > 0) );
3835                         n ++;
3836                 }
3837                 else {
3838                         n++;
3839                         aptr++;
3840                 }
3841         }
3842         return n;
3843 }
3844
3845 /**
3846  *  cuts a string after maxlen glyphs
3847  *  Buf string to cut to maxlen glyphs
3848  *  maxlen how long may the string become?
3849  * @returns current length of the string
3850  */
3851 long StrBuf_Utf8StrCut(StrBuf *Buf, int maxlen)
3852 {
3853         char *aptr, *eptr;
3854         int n = 0, m = 0;
3855
3856         aptr = Buf->buf;
3857         eptr = Buf->buf + Buf->BufUsed;
3858         while ((aptr < eptr) && (*aptr != '\0')) {
3859                 if (Ctdl_IsUtf8SequenceStart(*aptr)){
3860                         m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
3861                         while ((*aptr++ != '\0') && (m-- > 0));
3862                         n ++;
3863                 }
3864                 else {
3865                         n++;
3866                         aptr++;
3867                 }
3868                 if (n >= maxlen) {
3869                         *aptr = '\0';
3870                         Buf->BufUsed = aptr - Buf->buf;
3871                         return Buf->BufUsed;
3872                 }
3873         }
3874         return Buf->BufUsed;
3875
3876 }
3877
3878
3879
3880
3881
3882 /*******************************************************************************
3883  *                               wrapping ZLib                                 *
3884  *******************************************************************************/
3885
3886 /**
3887  *  uses the same calling syntax as compress2(), but it
3888  *   creates a stream compatible with HTTP "Content-encoding: gzip"
3889  *  dest compressed buffer
3890  *  destLen length of the compresed data 
3891  *  source source to encode
3892  *  sourceLen length of source to encode 
3893  *  level compression level
3894  */
3895 #ifdef HAVE_ZLIB
3896 int ZEXPORT compress_gzip(Bytef * dest,
3897                           size_t * destLen,
3898                           const Bytef * source,
3899                           uLong sourceLen,     
3900                           int level)
3901 {
3902         /* write gzip header */
3903         snprintf((char *) dest, *destLen, 
3904                  "%c%c%c%c%c%c%c%c%c%c",
3905                  gz_magic[0], gz_magic[1], Z_DEFLATED,
3906                  0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /* xflags */ ,
3907                  OS_CODE);
3908
3909         /* normal deflate */
3910         z_stream stream;
3911         int err;
3912         stream.next_in = (Bytef *) source;
3913         stream.avail_in = (uInt) sourceLen;
3914         stream.next_out = dest + 10L;   // after header
3915         stream.avail_out = (uInt) * destLen;
3916         if ((uLong) stream.avail_out != *destLen)
3917                 return Z_BUF_ERROR;
3918
3919         stream.zalloc = (alloc_func) 0;
3920         stream.zfree = (free_func) 0;
3921         stream.opaque = (voidpf) 0;
3922
3923         err = deflateInit2(&stream, level, Z_DEFLATED, -MAX_WBITS,
3924                            DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
3925         if (err != Z_OK)
3926                 return err;
3927
3928         err = deflate(&stream, Z_FINISH);
3929         if (err != Z_STREAM_END) {
3930                 deflateEnd(&stream);
3931                 return err == Z_OK ? Z_BUF_ERROR : err;
3932         }
3933         *destLen = stream.total_out + 10L;
3934
3935         /* write CRC and Length */
3936         uLong crc = crc32(0L, source, sourceLen);
3937         int n;
3938         for (n = 0; n < 4; ++n, ++*destLen) {
3939                 dest[*destLen] = (int) (crc & 0xff);
3940                 crc >>= 8;
3941         }
3942         uLong len = stream.total_in;
3943         for (n = 0; n < 4; ++n, ++*destLen) {
3944                 dest[*destLen] = (int) (len & 0xff);
3945                 len >>= 8;
3946         }
3947         err = deflateEnd(&stream);
3948         return err;
3949 }
3950 #endif
3951
3952
3953 /**
3954  *  compress the buffer with gzip
3955  * Attention! If you feed this a Const String, you must maintain the uncompressed buffer yourself!
3956  *  Buf buffer whose content is to be gzipped
3957  */
3958 int CompressBuffer(StrBuf *Buf)
3959 {
3960 #ifdef HAVE_ZLIB
3961         char *compressed_data = NULL;
3962         size_t compressed_len, bufsize;
3963         int i = 0;
3964
3965         bufsize = compressed_len = Buf->BufUsed +  (Buf->BufUsed / 100) + 100;
3966         compressed_data = malloc(compressed_len);
3967         
3968         if (compressed_data == NULL)
3969                 return -1;
3970         /* Flush some space after the used payload so valgrind shuts up... */
3971         while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
3972                 Buf->buf[Buf->BufUsed + i++] = '\0';
3973         if (compress_gzip((Bytef *) compressed_data,
3974                           &compressed_len,
3975                           (Bytef *) Buf->buf,
3976                           (uLongf) Buf->BufUsed, Z_BEST_SPEED) == Z_OK) {
3977                 if (!Buf->ConstBuf)
3978                         free(Buf->buf);
3979                 Buf->buf = compressed_data;
3980                 Buf->BufUsed = compressed_len;
3981                 Buf->BufSize = bufsize;
3982                 /* Flush some space after the used payload so valgrind shuts up... */
3983                 i = 0;
3984                 while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
3985                         Buf->buf[Buf->BufUsed + i++] = '\0';
3986                 return 1;
3987         } else {
3988                 free(compressed_data);
3989         }
3990 #endif  /* HAVE_ZLIB */
3991         return 0;
3992 }
3993
3994 /*******************************************************************************
3995  *           File I/O; Callbacks to libevent                                   *
3996  *******************************************************************************/
3997
3998 long StrBuf_read_one_chunk_callback (int fd, short event, IOBuffer *FB)
3999 {
4000         long bufremain = 0;
4001         int n;
4002         
4003         if ((FB == NULL) || (FB->Buf == NULL))
4004                 return -1;
4005
4006         /*
4007          * check whether the read pointer is somewhere in a range 
4008          * where a cut left is inexpensive
4009          */
4010
4011         if (FB->ReadWritePointer != NULL)
4012         {
4013                 long already_read;
4014                 
4015                 already_read = FB->ReadWritePointer - FB->Buf->buf;
4016                 bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
4017
4018                 if (already_read != 0) {
4019                         long unread;
4020                         
4021                         unread = FB->Buf->BufUsed - already_read;
4022
4023                         /* else nothing to compact... */
4024                         if (unread == 0) {
4025                                 FB->ReadWritePointer = FB->Buf->buf;
4026                                 bufremain = FB->Buf->BufSize;                   
4027                         }
4028                         else if ((unread < 64) || 
4029                                  (bufremain < already_read))
4030                         {
4031                                 /* 
4032                                  * if its just a tiny bit remaining, or we run out of space... 
4033                                  * lets tidy up.
4034                                  */
4035                                 FB->Buf->BufUsed = unread;
4036                                 if (unread < already_read)
4037                                         memcpy(FB->Buf->buf, FB->ReadWritePointer, unread);
4038                                 else
4039                                         memmove(FB->Buf->buf, FB->ReadWritePointer, unread);
4040                                 FB->ReadWritePointer = FB->Buf->buf;
4041                                 bufremain = FB->Buf->BufSize - unread - 1;
4042                         }
4043                         else if (bufremain < (FB->Buf->BufSize / 10))
4044                         {
4045                                 /* get a bigger buffer */ 
4046
4047                                 IncreaseBuf(FB->Buf, 0, FB->Buf->BufUsed + 1);
4048
4049                                 FB->ReadWritePointer = FB->Buf->buf + unread;
4050
4051                                 bufremain = FB->Buf->BufSize - unread - 1;
4052 /*TODO: special increase function that won't copy the already read! */
4053                         }
4054                 }
4055                 else if (bufremain < 10) {
4056                         IncreaseBuf(FB->Buf, 1, FB->Buf->BufUsed + 10);
4057                         
4058                         FB->ReadWritePointer = FB->Buf->buf;
4059                         
4060                         bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
4061                 }
4062                 
4063         }
4064         else {
4065                 FB->ReadWritePointer = FB->Buf->buf;
4066                 bufremain = FB->Buf->BufSize - 1;
4067         }
4068
4069         n = read(fd, FB->Buf->buf + FB->Buf->BufUsed, bufremain);
4070
4071         if (n > 0) {
4072                 FB->Buf->BufUsed += n;
4073                 FB->Buf->buf[FB->Buf->BufUsed] = '\0';
4074         }
4075         return n;
4076 }
4077
4078 int StrBuf_write_one_chunk_callback(int fd, short event, IOBuffer *FB)
4079 {
4080         long WriteRemain;
4081         int n;
4082
4083         if ((FB == NULL) || (FB->Buf == NULL))
4084                 return -1;
4085
4086         if (FB->ReadWritePointer != NULL)
4087         {
4088                 WriteRemain = FB->Buf->BufUsed - 
4089                         (FB->ReadWritePointer - 
4090                          FB->Buf->buf);
4091         }
4092         else {
4093                 FB->ReadWritePointer = FB->Buf->buf;
4094                 WriteRemain = FB->Buf->BufUsed;
4095         }
4096
4097         n = write(fd, FB->ReadWritePointer, WriteRemain);
4098         if (n > 0) {
4099                 FB->ReadWritePointer += n;
4100
4101                 if (FB->ReadWritePointer == 
4102                     FB->Buf->buf + FB->Buf->BufUsed)
4103                 {
4104                         FlushStrBuf(FB->Buf);
4105                         FB->ReadWritePointer = NULL;
4106                         return 0;
4107                 }
4108         // check whether we've got something to write
4109         // get the maximum chunk plus the pointer we can send
4110         // write whats there
4111         // if not all was sent, remember the send pointer for the next time
4112                 return FB->ReadWritePointer - FB->Buf->buf + FB->Buf->BufUsed;
4113         }
4114         return n;
4115 }
4116
4117 /**
4118  *  extract a "next line" from Buf; Ptr to persist across several iterations
4119  *  LineBuf your line will be copied here.
4120  *  FB BLOB with lines of text...
4121  *  Ptr moved arround to keep the next-line across several iterations
4122  *        has to be &NULL on start; will be &NotNULL on end of buffer
4123  * @returns size of copied buffer
4124  */
4125 eReadState StrBufChunkSipLine(StrBuf *LineBuf, IOBuffer *FB)
4126 {
4127         const char *aptr, *ptr, *eptr;
4128         char *optr, *xptr;
4129
4130         if ((FB == NULL) || (LineBuf == NULL) || (LineBuf->buf == NULL))
4131                 return eReadFail;
4132         
4133
4134         if ((FB->Buf == NULL) || (FB->ReadWritePointer == StrBufNOTNULL)) {
4135                 FB->ReadWritePointer = StrBufNOTNULL;
4136                 return eReadFail;
4137         }
4138
4139         FlushStrBuf(LineBuf);
4140         if (FB->ReadWritePointer == NULL)
4141                 ptr = aptr = FB->Buf->buf;
4142         else
4143                 ptr = aptr = FB->ReadWritePointer;
4144
4145         optr = LineBuf->buf;
4146         eptr = FB->Buf->buf + FB->Buf->BufUsed;
4147         xptr = LineBuf->buf + LineBuf->BufSize - 1;
4148
4149         while ((ptr <= eptr) && 
4150                (*ptr != '\n') &&
4151                (*ptr != '\r') )
4152         {
4153                 *optr = *ptr;
4154                 optr++; ptr++;
4155                 if (optr == xptr) {
4156                         LineBuf->BufUsed = optr - LineBuf->buf;
4157                         IncreaseBuf(LineBuf,  1, LineBuf->BufUsed + 1);
4158                         optr = LineBuf->buf + LineBuf->BufUsed;
4159                         xptr = LineBuf->buf + LineBuf->BufSize - 1;
4160                 }
4161         }
4162
4163         if (ptr >= eptr) {
4164                 if (optr > LineBuf->buf)
4165                         optr --;
4166                 if ((*(ptr - 1) != '\r') && (*(ptr - 1) != '\n')) {
4167                         LineBuf->BufUsed = optr - LineBuf->buf;
4168                         *optr = '\0';
4169                         if ((FB->ReadWritePointer != NULL) && 
4170                             (FB->ReadWritePointer != FB->Buf->buf))
4171                         {
4172                                 /* Ok, the client application read all the data 
4173                                    it was interested in so far. Since there is more to read, 
4174                                    we now shrink the buffer, and move the rest over.
4175                                 */
4176                                 StrBufCutLeft(FB->Buf, 
4177                                               FB->ReadWritePointer - FB->Buf->buf);
4178                                 FB->ReadWritePointer = FB->Buf->buf;
4179                         }
4180                         return eMustReadMore;
4181                 }
4182         }
4183         LineBuf->BufUsed = optr - LineBuf->buf;
4184         *optr = '\0';       
4185         if ((ptr <= eptr) && (*ptr == '\r'))
4186                 ptr ++;
4187         if ((ptr <= eptr) && (*ptr == '\n'))
4188                 ptr ++;
4189         
4190         if (ptr < eptr) {
4191                 FB->ReadWritePointer = ptr;
4192         }
4193         else {
4194                 FlushStrBuf(FB->Buf);
4195                 FB->ReadWritePointer = NULL;
4196         }
4197
4198         return eReadSuccess;
4199 }
4200
4201 /**
4202  *  check whether the chunk-buffer has more data waiting or not.
4203  *  FB Chunk-Buffer to inspect
4204  */
4205 eReadState StrBufCheckBuffer(IOBuffer *FB)
4206 {
4207         if (FB == NULL)
4208                 return eReadFail;
4209         if (FB->Buf->BufUsed == 0)
4210                 return eReadSuccess;
4211         if (FB->ReadWritePointer == NULL)
4212                 return eBufferNotEmpty;
4213         if (FB->Buf->buf + FB->Buf->BufUsed > FB->ReadWritePointer)
4214                 return eBufferNotEmpty;
4215         return eReadSuccess;
4216 }
4217
4218 long IOBufferStrLength(IOBuffer *FB)
4219 {
4220         if ((FB == NULL) || (FB->Buf == NULL))
4221                 return 0;
4222         if (FB->ReadWritePointer == NULL)
4223                 return StrLength(FB->Buf);
4224         
4225         return StrLength(FB->Buf) - (FB->ReadWritePointer - FB->Buf->buf);
4226 }
4227
4228
4229 /*******************************************************************************
4230  *           File I/O; Prefer buffered read since its faster!                  *
4231  *******************************************************************************/
4232
4233 /**
4234  *  Read a line from socket
4235  * flushes and closes the FD on error
4236  *  buf the buffer to get the input to
4237  *  fd pointer to the filedescriptor to read
4238  *  append Append to an existing string or replace?
4239  *  Error strerror() on error 
4240  * @returns numbers of chars read
4241  */
4242 int StrBufTCP_read_line(StrBuf *buf, int *fd, int append, const char **Error)
4243 {
4244         int len, rlen, slen;
4245
4246         if ((buf == NULL) || (buf->buf == NULL)) {
4247                 *Error = strerror(EINVAL);
4248                 return -1;
4249         }
4250
4251         if (!append)
4252                 FlushStrBuf(buf);
4253
4254         slen = len = buf->BufUsed;
4255         while (1) {
4256                 rlen = read(*fd, &buf->buf[len], 1);
4257                 if (rlen < 1) {
4258                         *Error = strerror(errno);
4259                         
4260                         close(*fd);
4261                         *fd = -1;
4262                         
4263                         return -1;
4264                 }
4265                 if (buf->buf[len] == '\n')
4266                         break;
4267                 if (buf->buf[len] != '\r')
4268                         len ++;
4269                 if (len + 2 >= buf->BufSize) {
4270                         buf->BufUsed = len;
4271                         buf->buf[len+1] = '\0';
4272                         IncreaseBuf(buf, 1, -1);
4273                 }
4274         }
4275         buf->BufUsed = len;
4276         buf->buf[len] = '\0';
4277         return len - slen;
4278 }
4279
4280
4281 /**
4282  *  Read a line from socket
4283  * flushes and closes the FD on error
4284  *  Line the line to read from the fd / I/O Buffer
4285  *  buf the buffer to get the input to
4286  *  fd pointer to the filedescriptor to read
4287  *  timeout number of successless selects until we bail out
4288  *  selectresolution how long to wait on each select
4289  *  Error strerror() on error 
4290  * @returns numbers of chars read
4291  */
4292 int StrBufTCP_read_buffered_line(StrBuf *Line, 
4293                                  StrBuf *buf, 
4294                                  int *fd, 
4295                                  int timeout, 
4296                                  int selectresolution, 
4297                                  const char **Error)
4298 {
4299         int len, rlen;
4300         int nSuccessLess = 0;
4301         fd_set rfds;
4302         char *pch = NULL;
4303         int fdflags;
4304         int IsNonBlock;
4305         struct timeval tv;
4306
4307         if (buf->BufUsed > 0) {
4308                 pch = strchr(buf->buf, '\n');
4309                 if (pch != NULL) {
4310                         rlen = 0;
4311                         len = pch - buf->buf;
4312                         if (len > 0 && (*(pch - 1) == '\r') )
4313                                 rlen ++;
4314                         StrBufSub(Line, buf, 0, len - rlen);
4315                         StrBufCutLeft(buf, len + 1);
4316                         return len - rlen;
4317                 }
4318         }
4319         
4320         if (buf->BufSize - buf->BufUsed < 10)
4321                 IncreaseBuf(buf, 1, -1);
4322
4323         fdflags = fcntl(*fd, F_GETFL);
4324         IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4325
4326         while ((nSuccessLess < timeout) && (pch == NULL)) {
4327                 if (IsNonBlock){
4328                         tv.tv_sec = selectresolution;
4329                         tv.tv_usec = 0;
4330                         
4331                         FD_ZERO(&rfds);
4332                         FD_SET(*fd, &rfds);
4333                         if (select(*fd + 1, NULL, &rfds, NULL, &tv) == -1) {
4334                                 *Error = strerror(errno);
4335                                 close (*fd);
4336                                 *fd = -1;
4337                                 return -1;
4338                         }
4339                 }
4340                 if (IsNonBlock && !  FD_ISSET(*fd, &rfds)) {
4341                         nSuccessLess ++;
4342                         continue;
4343                 }
4344                 rlen = read(*fd, 
4345                             &buf->buf[buf->BufUsed], 
4346                             buf->BufSize - buf->BufUsed - 1);
4347                 if (rlen < 1) {
4348                         *Error = strerror(errno);
4349                         close(*fd);
4350                         *fd = -1;
4351                         return -1;
4352                 }
4353                 else if (rlen > 0) {
4354                         nSuccessLess = 0;
4355                         buf->BufUsed += rlen;
4356                         buf->buf[buf->BufUsed] = '\0';
4357                         pch = strchr(buf->buf, '\n');
4358                         if ((pch == NULL) &&
4359                             (buf->BufUsed + 10 > buf->BufSize) &&
4360                             (IncreaseBuf(buf, 1, -1) == -1))
4361                                 return -1;
4362                         continue;
4363                 }
4364                 
4365         }
4366         if (pch != NULL) {
4367                 rlen = 0;
4368                 len = pch - buf->buf;
4369                 if (len > 0 && (*(pch - 1) == '\r') )
4370                         rlen ++;
4371                 StrBufSub(Line, buf, 0, len - rlen);
4372                 StrBufCutLeft(buf, len + 1);
4373                 return len - rlen;
4374         }
4375         return -1;
4376
4377 }
4378
4379 static const char *ErrRBLF_PreConditionFailed="StrBufTCP_read_buffered_line_fast: Wrong arguments or invalid Filedescriptor";
4380 static const char *ErrRBLF_SelectFailed="StrBufTCP_read_buffered_line_fast: Select failed without reason";
4381 static const char *ErrRBLF_NotEnoughSentFromServer="StrBufTCP_read_buffered_line_fast: No complete line was sent from peer";
4382 /**
4383  *  Read a line from socket
4384  * flushes and closes the FD on error
4385  *  Line where to append our Line read from the fd / I/O Buffer; 
4386  *  IOBuf the buffer to get the input to; lifetime pair to FD
4387  *  Pos pointer to the current read position, should be NULL initialized on opening the FD it belongs to.!
4388  *  fd pointer to the filedescriptor to read
4389  *  timeout number of successless selects until we bail out
4390  *  selectresolution how long to wait on each select
4391  *  Error strerror() on error 
4392  * @returns numbers of chars read or -1 in case of error. "\n" will become 0
4393  */
4394 int StrBufTCP_read_buffered_line_fast(StrBuf *Line, 
4395                                       StrBuf *IOBuf, 
4396                                       const char **Pos,
4397                                       int *fd, 
4398                                       int timeout, 
4399                                       int selectresolution, 
4400                                       const char **Error)
4401 {
4402         const char *pche = NULL;
4403         const char *pos = NULL;
4404         const char *pLF;
4405         int len, rlen, retlen;
4406         int nSuccessLess = 0;
4407         fd_set rfds;
4408         const char *pch = NULL;
4409         int fdflags;
4410         int IsNonBlock;
4411         struct timeval tv;
4412         
4413         retlen = 0;
4414         if ((Line == NULL) ||
4415             (Pos == NULL) ||
4416             (IOBuf == NULL) ||
4417             (*fd == -1))
4418         {
4419                 if (Pos != NULL)
4420                         *Pos = NULL;
4421                 *Error = ErrRBLF_PreConditionFailed;
4422                 return -1;
4423         }
4424
4425         pos = *Pos;
4426         if ((IOBuf->BufUsed > 0) && 
4427             (pos != NULL) && 
4428             (pos < IOBuf->buf + IOBuf->BufUsed)) 
4429         {
4430                 char *pcht;
4431
4432                 pche = IOBuf->buf + IOBuf->BufUsed;
4433                 pch = pos;
4434                 pcht = Line->buf;
4435
4436                 while ((pch < pche) && (*pch != '\n'))
4437                 {
4438                         if (Line->BufUsed + 10 > Line->BufSize)
4439                         {
4440                                 long apos;
4441                                 apos = pcht - Line->buf;
4442                                 *pcht = '\0';
4443                                 IncreaseBuf(Line, 1, -1);
4444                                 pcht = Line->buf + apos;
4445                         }
4446                         *pcht++ = *pch++;
4447                         Line->BufUsed++;
4448                         retlen++;
4449                 }
4450
4451                 len = pch - pos;
4452                 if (len > 0 && (*(pch - 1) == '\r') )
4453                 {
4454                         retlen--;
4455                         len --;
4456                         pcht --;
4457                         Line->BufUsed --;
4458                 }
4459                 *pcht = '\0';
4460
4461                 if ((pch >= pche) || (*pch == '\0'))
4462                 {
4463                         FlushStrBuf(IOBuf);
4464                         *Pos = NULL;
4465                         pch = NULL;
4466                         pos = 0;
4467                 }
4468
4469                 if ((pch != NULL) && 
4470                     (pch <= pche)) 
4471                 {
4472                         if (pch + 1 >= pche) {
4473                                 *Pos = NULL;
4474                                 FlushStrBuf(IOBuf);
4475                         }
4476                         else
4477                                 *Pos = pch + 1;
4478                         
4479                         return retlen;
4480                 }
4481                 else 
4482                         FlushStrBuf(IOBuf);
4483         }
4484
4485         /* If we come here, Pos is Unset since we read everything into Line, and now go for more. */
4486         
4487         if (IOBuf->BufSize - IOBuf->BufUsed < 10)
4488                 IncreaseBuf(IOBuf, 1, -1);
4489
4490         fdflags = fcntl(*fd, F_GETFL);
4491         IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4492
4493         pLF = NULL;
4494         while ((nSuccessLess < timeout) && 
4495                (pLF == NULL) &&
4496                (*fd != -1)) {
4497                 if (IsNonBlock)
4498                 {
4499                         tv.tv_sec = 1;
4500                         tv.tv_usec = 0;
4501                 
4502                         FD_ZERO(&rfds);
4503                         FD_SET(*fd, &rfds);
4504                         if (select((*fd) + 1, &rfds, NULL, NULL, &tv) == -1) {
4505                                 *Error = strerror(errno);
4506                                 close (*fd);
4507                                 *fd = -1;
4508                                 if (*Error == NULL)
4509                                         *Error = ErrRBLF_SelectFailed;
4510                                 return -1;
4511                         }
4512                         if (! FD_ISSET(*fd, &rfds) != 0) {
4513                                 nSuccessLess ++;
4514                                 continue;
4515                         }
4516                 }
4517                 rlen = read(*fd, 
4518                             &IOBuf->buf[IOBuf->BufUsed], 
4519                             IOBuf->BufSize - IOBuf->BufUsed - 1);
4520                 if (rlen < 1) {
4521                         *Error = strerror(errno);
4522                         close(*fd);
4523                         *fd = -1;
4524                         return -1;
4525                 }
4526                 else if (rlen > 0) {
4527                         nSuccessLess = 0;
4528                         pLF = IOBuf->buf + IOBuf->BufUsed;
4529                         IOBuf->BufUsed += rlen;
4530                         IOBuf->buf[IOBuf->BufUsed] = '\0';
4531                         
4532                         pche = IOBuf->buf + IOBuf->BufUsed;
4533                         
4534                         while ((pLF < pche) && (*pLF != '\n'))
4535                                 pLF ++;
4536                         if ((pLF >= pche) || (*pLF == '\0'))
4537                                 pLF = NULL;
4538
4539                         if (IOBuf->BufUsed + 10 > IOBuf->BufSize)
4540                         {
4541                                 long apos = 0;
4542
4543                                 if (pLF != NULL) apos = pLF - IOBuf->buf;
4544                                 IncreaseBuf(IOBuf, 1, -1);      
4545                                 if (pLF != NULL) pLF = IOBuf->buf + apos;
4546                         }
4547
4548                         continue;
4549                 }
4550                 else
4551                 {
4552                         nSuccessLess++;
4553                 }
4554         }
4555         *Pos = NULL;
4556         if (pLF != NULL) {
4557                 pos = IOBuf->buf;
4558                 len = pLF - pos;
4559                 if (len > 0 && (*(pLF - 1) == '\r') )
4560                         len --;
4561                 StrBufAppendBufPlain(Line, ChrPtr(IOBuf), len, 0);
4562                 if (pLF + 1 >= IOBuf->buf + IOBuf->BufUsed)
4563                 {
4564                         FlushStrBuf(IOBuf);
4565                 }
4566                 else 
4567                         *Pos = pLF + 1;
4568                 return retlen + len;
4569         }
4570         *Error = ErrRBLF_NotEnoughSentFromServer;
4571         return -1;
4572
4573 }
4574
4575 static const char *ErrRBLF_BLOBPreConditionFailed="StrBufReadBLOB: Wrong arguments or invalid Filedescriptor";
4576 /**
4577  *  Input binary data from socket
4578  * flushes and closes the FD on error
4579  *  Buf the buffer to get the input to
4580  *  fd pointer to the filedescriptor to read
4581  *  append Append to an existing string or replace?
4582  *  nBytes the maximal number of bytes to read
4583  *  Error strerror() on error 
4584  * @returns numbers of chars read
4585  */
4586 int StrBufReadBLOB(StrBuf *Buf, int *fd, int append, long nBytes, const char **Error)
4587 {
4588         int fdflags;
4589         int rlen;
4590         int nSuccessLess;
4591         int nRead = 0;
4592         char *ptr;
4593         int IsNonBlock;
4594         struct timeval tv;
4595         fd_set rfds;
4596
4597         if ((Buf == NULL) || (Buf->buf == NULL) || (*fd == -1))
4598         {
4599                 *Error = ErrRBLF_BLOBPreConditionFailed;
4600                 return -1;
4601         }
4602         if (!append)
4603                 FlushStrBuf(Buf);
4604         if (Buf->BufUsed + nBytes >= Buf->BufSize)
4605                 IncreaseBuf(Buf, 1, Buf->BufUsed + nBytes);
4606
4607         ptr = Buf->buf + Buf->BufUsed;
4608
4609         fdflags = fcntl(*fd, F_GETFL);
4610         IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4611         nSuccessLess = 0;
4612         while ((nRead < nBytes) && 
4613                (*fd != -1)) 
4614         {
4615                 if (IsNonBlock)
4616                 {
4617                         tv.tv_sec = 1;
4618                         tv.tv_usec = 0;
4619                 
4620                         FD_ZERO(&rfds);
4621                         FD_SET(*fd, &rfds);
4622                         if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
4623                                 *Error = strerror(errno);
4624                                 close (*fd);
4625                                 *fd = -1;
4626                                 if (*Error == NULL)
4627                                         *Error = ErrRBLF_SelectFailed;
4628                                 return -1;
4629                         }
4630                         if (! FD_ISSET(*fd, &rfds) != 0) {
4631                                 nSuccessLess ++;
4632                                 continue;
4633                         }
4634                 }
4635
4636                 if ((rlen = read(*fd, 
4637                                  ptr,
4638                                  nBytes - nRead)) == -1) {
4639                         close(*fd);
4640                         *fd = -1;
4641                         *Error = strerror(errno);
4642                         return rlen;
4643                 }
4644                 nRead += rlen;
4645                 ptr += rlen;
4646                 Buf->BufUsed += rlen;
4647         }
4648         Buf->buf[Buf->BufUsed] = '\0';
4649         return nRead;
4650 }
4651
4652 const char *ErrRBB_BLOBFPreConditionFailed = "StrBufReadBLOBBuffered: to many selects; aborting.";
4653 const char *ErrRBB_too_many_selects        = "StrBufReadBLOBBuffered: to many selects; aborting.";
4654 /**
4655  *  Input binary data from socket
4656  * flushes and closes the FD on error
4657  *  Blob put binary thing here
4658  *  IOBuf the buffer to get the input to
4659  *  Pos offset inside of IOBuf
4660  *  fd pointer to the filedescriptor to read
4661  *  append Append to an existing string or replace?
4662  *  nBytes the maximal number of bytes to read
4663  *  check whether we should search for '000\n' terminators in case of timeouts
4664  *  Error strerror() on error 
4665  * @returns numbers of chars read
4666  */
4667 int StrBufReadBLOBBuffered(StrBuf *Blob, 
4668                            StrBuf *IOBuf, 
4669                            const char **Pos,
4670                            int *fd, 
4671                            int append, 
4672                            long nBytes, 
4673                            int check, 
4674                            const char **Error)
4675 {
4676         const char *pos;
4677         int fdflags;
4678         int rlen = 0;
4679         int nRead = 0;
4680         int nAlreadyRead = 0;
4681         int IsNonBlock;
4682         char *ptr;
4683         fd_set rfds;
4684         struct timeval tv;
4685         int nSuccessLess = 0;
4686         int MaxTries;
4687
4688         if ((Blob == NULL)  ||
4689             (*fd == -1)     ||
4690             (IOBuf == NULL) ||
4691             (Pos == NULL))
4692         {
4693                 if (Pos != NULL)
4694                         *Pos = NULL;
4695                 *Error = ErrRBB_BLOBFPreConditionFailed;
4696                 return -1;
4697         }
4698
4699         if (!append)
4700                 FlushStrBuf(Blob);
4701         if (Blob->BufUsed + nBytes >= Blob->BufSize) 
4702                 IncreaseBuf(Blob, append, Blob->BufUsed + nBytes);
4703         
4704         pos = *Pos;
4705
4706         if (pos != NULL) {
4707                 rlen = pos - IOBuf->buf;
4708         }
4709         rlen = IOBuf->BufUsed - rlen;
4710
4711
4712         if ((IOBuf->BufUsed > 0) && (pos != NULL) && (pos < IOBuf->buf + IOBuf->BufUsed)) 
4713         {
4714                 if (rlen < nBytes) {
4715                         memcpy(Blob->buf + Blob->BufUsed, pos, rlen);
4716                         Blob->BufUsed += rlen;
4717                         Blob->buf[Blob->BufUsed] = '\0';
4718                         nAlreadyRead = nRead = rlen;
4719                         *Pos = NULL; 
4720                 }
4721                 if (rlen >= nBytes) {
4722                         memcpy(Blob->buf + Blob->BufUsed, pos, nBytes);
4723                         Blob->BufUsed += nBytes;
4724                         Blob->buf[Blob->BufUsed] = '\0';
4725                         if (rlen == nBytes) {
4726                                 *Pos = NULL; 
4727                                 FlushStrBuf(IOBuf);
4728                         }
4729                         else 
4730                                 *Pos += nBytes;
4731                         return nBytes;
4732                 }
4733         }
4734
4735         FlushStrBuf(IOBuf);
4736         *Pos = NULL;
4737         if (IOBuf->BufSize < nBytes - nRead) {
4738                 IncreaseBuf(IOBuf, 0, nBytes - nRead);
4739         }
4740         ptr = IOBuf->buf;
4741
4742         fdflags = fcntl(*fd, F_GETFL);
4743         IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4744         if (IsNonBlock)
4745                 MaxTries =   1000;
4746         else
4747                 MaxTries = 100000;
4748
4749         nBytes -= nRead;
4750         nRead = 0;
4751         while ((nSuccessLess < MaxTries) && (nRead < nBytes) && (*fd != -1)) {
4752                 if (IsNonBlock) {
4753                         tv.tv_sec = 1;
4754                         tv.tv_usec = 0;
4755                 
4756                         FD_ZERO(&rfds);
4757                         FD_SET(*fd, &rfds);
4758                         if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
4759                                 *Error = strerror(errno);
4760                                 close (*fd);
4761                                 *fd = -1;
4762                                 if (*Error == NULL) {
4763                                         *Error = ErrRBLF_SelectFailed;
4764                                 }
4765                                 return -1;
4766                         }
4767                         if (! FD_ISSET(*fd, &rfds) != 0) {
4768                                 nSuccessLess ++;
4769                                 continue;
4770                         }
4771                 }
4772                 rlen = read(*fd, ptr, IOBuf->BufSize - (ptr - IOBuf->buf));
4773                 // if (rlen == -1) {            2021feb27 ajc changed this, apparently we will always get at least 1 byte unless the connection is broken
4774                 if (rlen < 1) {
4775                         close(*fd);
4776                         *fd = -1;
4777                         *Error = strerror(errno);
4778                         return rlen;
4779                 }
4780                 else if (rlen == 0){
4781                         if ((check == NNN_TERM) && (nRead > 5) && (strncmp(IOBuf->buf + IOBuf->BufUsed - 5, "\n000\n", 5) == 0)) {
4782                                 StrBufPlain(Blob, HKEY("\n000\n"));
4783                                 StrBufCutRight(Blob, 5);
4784                                 return Blob->BufUsed;
4785                         }
4786                         else if (!IsNonBlock) 
4787                                 nSuccessLess ++;
4788                         else if (nSuccessLess > MaxTries) {
4789                                 FlushStrBuf(IOBuf);
4790                                 *Error = ErrRBB_too_many_selects;
4791                                 return -1;
4792                         }
4793                 }
4794                 else if (rlen > 0) {
4795                         nSuccessLess = 0;
4796                         nRead += rlen;
4797                         ptr += rlen;
4798                         IOBuf->BufUsed += rlen;
4799                 }
4800         }
4801         if (nSuccessLess >= MaxTries) {
4802                 FlushStrBuf(IOBuf);
4803                 *Error = ErrRBB_too_many_selects;
4804                 return -1;
4805         }
4806
4807         if (nRead > nBytes) {
4808                 *Pos = IOBuf->buf + nBytes;
4809         }
4810         Blob->buf[Blob->BufUsed] = '\0';
4811         StrBufAppendBufPlain(Blob, IOBuf->buf, nBytes, 0);
4812         if (*Pos == NULL) {
4813                 FlushStrBuf(IOBuf);
4814         }
4815         return nRead + nAlreadyRead;
4816 }
4817
4818 /**
4819  *  extract a "next line" from Buf; Ptr to persist across several iterations
4820  *  LineBuf your line will be copied here.
4821  *  Buf BLOB with lines of text...
4822  *  Ptr moved arround to keep the next-line across several iterations
4823  *        has to be &NULL on start; will be &NotNULL on end of buffer
4824  * @returns size of remaining buffer
4825  */
4826 int StrBufSipLine(StrBuf *LineBuf, const StrBuf *Buf, const char **Ptr)
4827 {
4828         const char *aptr, *ptr, *eptr;
4829         char *optr, *xptr;
4830
4831         if ((Buf == NULL) ||
4832             (*Ptr == StrBufNOTNULL) ||
4833             (LineBuf == NULL)||
4834             (LineBuf->buf == NULL))
4835         {
4836                 *Ptr = StrBufNOTNULL;
4837                 return 0;
4838         }
4839
4840         FlushStrBuf(LineBuf);
4841         if (*Ptr==NULL)
4842                 ptr = aptr = Buf->buf;
4843         else
4844                 ptr = aptr = *Ptr;
4845
4846         optr = LineBuf->buf;
4847         eptr = Buf->buf + Buf->BufUsed;
4848         xptr = LineBuf->buf + LineBuf->BufSize - 1;
4849
4850         while ((ptr <= eptr) && 
4851                (*ptr != '\n') &&
4852                (*ptr != '\r') )
4853         {
4854                 *optr = *ptr;
4855                 optr++; ptr++;
4856                 if (optr == xptr) {
4857                         LineBuf->BufUsed = optr - LineBuf->buf;
4858                         IncreaseBuf(LineBuf,  1, LineBuf->BufUsed + 1);
4859                         optr = LineBuf->buf + LineBuf->BufUsed;
4860                         xptr = LineBuf->buf + LineBuf->BufSize - 1;
4861                 }
4862         }
4863
4864         if ((ptr >= eptr) && (optr > LineBuf->buf))
4865                 optr --;
4866         LineBuf->BufUsed = optr - LineBuf->buf;
4867         *optr = '\0';       
4868         if ((ptr <= eptr) && (*ptr == '\r'))
4869                 ptr ++;
4870         if ((ptr <= eptr) && (*ptr == '\n'))
4871                 ptr ++;
4872         
4873         if (ptr < eptr) {
4874                 *Ptr = ptr;
4875         }
4876         else {
4877                 *Ptr = StrBufNOTNULL;
4878         }
4879
4880         return Buf->BufUsed - (ptr - Buf->buf);
4881 }
4882
4883
4884 /**
4885  *  removes double slashes from pathnames
4886  *  Dir directory string to filter
4887  *  RemoveTrailingSlash allows / disallows trailing slashes
4888  */
4889 void StrBufStripSlashes(StrBuf *Dir, int RemoveTrailingSlash)
4890 {
4891         char *a, *b;
4892
4893         a = b = Dir->buf;
4894
4895         while (!IsEmptyStr(a)) {
4896                 if (*a == '/') {
4897                         while (*a == '/')
4898                                 a++;
4899                         *b = '/';
4900                         b++;
4901                 }
4902                 else {
4903                         *b = *a;
4904                         b++; a++;
4905                 }
4906         }
4907         if ((RemoveTrailingSlash) &&
4908             (b > Dir->buf) && 
4909             (*(b - 1) == '/')){
4910                 b--;
4911         }
4912         *b = '\0';
4913         Dir->BufUsed = b - Dir->buf;
4914 }
4915
4916
4917 /*
4918  * Decode a quoted-printable encoded StrBuf buffer "in place"
4919  * This is possible because the decoded will always be shorter than the encoded
4920  * so we don't have to worry about the buffer being to small.
4921  */
4922 void StrBufDecodeQP(StrBuf *Buf)
4923 {
4924         if (!Buf) {                             // sanity check #1
4925                 return;
4926         }
4927
4928         int source_len = StrLength(Buf);
4929         if (source_len < 1) {                   // sanity check #2
4930                 return;
4931         }
4932
4933         int spos = 0;                           // source position
4934         int tpos = 0;                           // target position
4935
4936         while (spos < source_len) {
4937                 if (!strncmp(&Buf->buf[spos], "=\r\n", 3)) {
4938                         spos += 3;
4939                 }
4940                 else if (!strncmp(&Buf->buf[spos], "=\n", 2)) {
4941                         spos += 2;
4942                 }
4943                 else if (Buf->buf[spos] == '=') {
4944                         ++spos;
4945                         int ch;
4946                         sscanf(&Buf->buf[spos], "%02x", &ch);
4947                         Buf->buf[tpos++] = ch;
4948                         spos +=2;
4949                 }
4950                 else {
4951                         Buf->buf[tpos++] = Buf->buf[spos++];
4952                 }
4953         }
4954
4955         Buf->buf[tpos] = 0;
4956         Buf->BufUsed = tpos;
4957 }