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