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