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