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