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