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