3709effb84b86b9d9fd2d37bacc20ef5d23a693b
[citadel.git] / libcitadel / lib / stringbuf.c
1 #include "../sysdep.h"
2 #include <ctype.h>
3 #include <errno.h>
4 #include <string.h>
5 #include <unistd.h>
6 #include <string.h>
7 #include <stdio.h>
8 #include <sys/select.h>
9 #include <fcntl.h>
10 #include <sys/types.h>
11 #define SHOW_ME_VAPPEND_PRINTF
12 #include <stdarg.h>
13 #include "libcitadel.h"
14
15 #ifdef HAVE_ICONV
16 #include <iconv.h>
17 #endif
18
19 #ifdef HAVE_BACKTRACE
20 #include <execinfo.h>
21 #endif
22
23 #ifdef HAVE_ZLIB
24 #include <zlib.h>
25 int ZEXPORT compress_gzip(Bytef * dest, size_t * destLen,
26                           const Bytef * source, uLong sourceLen, int level);
27 #endif
28 int BaseStrBufSize = 64;
29
30 const char *StrBufNOTNULL = ((char*) NULL) - 1;
31
32 /**
33  * @defgroup StrBuf Stringbuffer, A class for manipulating strings with dynamic buffers
34  */
35
36 /**
37  * Private Structure for the Stringbuffer
38  */
39 struct StrBuf {
40         char *buf;         /**< the pointer to the dynamic buffer */
41         long BufSize;      /**< how many spcae do we optain */
42         long BufUsed;      /**< StNumber of Chars used excluding the trailing \\0 */
43         int ConstBuf;      /**< are we just a wrapper arround a static buffer and musn't we be changed? */
44 #ifdef SIZE_DEBUG
45         long nIncreases;
46         char bt [SIZ];
47         char bt_lastinc [SIZ];
48 #endif
49 };
50
51
52 static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE);
53 static inline int Ctdl_IsUtf8SequenceStart(const char Char);
54
55 #ifdef SIZE_DEBUG
56 #ifdef HAVE_BACKTRACE
57 static void StrBufBacktrace(StrBuf *Buf, int which)
58 {
59         int n;
60         char *pstart, *pch;
61         void *stack_frames[50];
62         size_t size, i;
63         char **strings;
64
65         if (which)
66                 pstart = pch = Buf->bt;
67         else
68                 pstart = pch = Buf->bt_lastinc;
69         size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
70         strings = backtrace_symbols(stack_frames, size);
71         for (i = 0; i < size; i++) {
72                 if (strings != NULL)
73                         n = snprintf(pch, SIZ - (pch - pstart), "%s\\n", strings[i]);
74                 else
75                         n = snprintf(pch, SIZ - (pch - pstart), "%p\\n", stack_frames[i]);
76                 pch += n;
77         }
78         free(strings);
79
80
81 }
82 #endif
83 #endif
84
85 /** 
86  * @ingroup StrBuf
87  * @brief Cast operator to Plain String 
88  * @note if the buffer is altered by StrBuf operations, this pointer may become 
89  *  invalid. So don't lean on it after altering the buffer!
90  *  Since this operation is considered cheap, rather call it often than risking
91  *  your pointer to become invalid!
92  * @param Str the string we want to get the c-string representation for
93  * @returns the Pointer to the Content. Don't mess with it!
94  */
95 inline const char *ChrPtr(const StrBuf *Str)
96 {
97         if (Str == NULL)
98                 return "";
99         return Str->buf;
100 }
101
102 /**
103  * @ingroup StrBuf
104  * @brief since we know strlen()'s result, provide it here.
105  * @param Str the string to return the length to
106  * @returns contentlength of the buffer
107  */
108 inline int StrLength(const StrBuf *Str)
109 {
110         return (Str != NULL) ? Str->BufUsed : 0;
111 }
112
113 /**
114  * @ingroup StrBuf
115  * @brief local utility function to resize the buffer
116  * @param Buf the buffer whichs storage we should increase
117  * @param KeepOriginal should we copy the original buffer or just start over with a new one
118  * @param DestSize what should fit in after?
119  */
120 static int IncreaseBuf(StrBuf *Buf, int KeepOriginal, int DestSize)
121 {
122         char *NewBuf;
123         size_t NewSize = Buf->BufSize * 2;
124
125         if (Buf->ConstBuf)
126                 return -1;
127                 
128         if (DestSize > 0)
129                 while (NewSize <= DestSize)
130                         NewSize *= 2;
131
132         NewBuf= (char*) malloc(NewSize);
133         if (NewBuf == NULL)
134                 return -1;
135
136         if (KeepOriginal && (Buf->BufUsed > 0))
137         {
138                 memcpy(NewBuf, Buf->buf, Buf->BufUsed);
139         }
140         else
141         {
142                 NewBuf[0] = '\0';
143                 Buf->BufUsed = 0;
144         }
145         free (Buf->buf);
146         Buf->buf = NewBuf;
147         Buf->BufSize = NewSize;
148 #ifdef SIZE_DEBUG
149         Buf->nIncreases++;
150 #ifdef HAVE_BACKTRACE
151         StrBufBacktrace(Buf, 1);
152 #endif
153 #endif
154         return Buf->BufSize;
155 }
156
157 /**
158  * @ingroup StrBuf
159  * @brief shrink an _EMPTY_ buffer if its Buffer superseeds threshhold to NewSize. Buffercontent is thoroughly ignored and flushed.
160  * @param Buf Buffer to shrink (has to be empty)
161  * @param ThreshHold if the buffer is bigger then this, its readjusted
162  * @param NewSize if we Shrink it, how big are we going to be afterwards?
163  */
164 void ReAdjustEmptyBuf(StrBuf *Buf, long ThreshHold, long NewSize)
165 {
166         if (Buf->BufUsed > ThreshHold) {
167                 free(Buf->buf);
168                 Buf->buf = (char*) malloc(NewSize);
169                 Buf->BufUsed = 0;
170                 Buf->BufSize = NewSize;
171         }
172 }
173
174 /**
175  * @ingroup StrBuf
176  * @brief shrink long term buffers to their real size so they don't waste memory
177  * @param Buf buffer to shrink
178  * @param Force if not set, will just executed if the buffer is much to big; set for lifetime strings
179  * @returns physical size of the buffer
180  */
181 long StrBufShrinkToFit(StrBuf *Buf, int Force)
182 {
183         if (Force || 
184             (Buf->BufUsed + (Buf->BufUsed / 3) > Buf->BufSize))
185         {
186                 char *TmpBuf = (char*) malloc(Buf->BufUsed + 1);
187                 memcpy (TmpBuf, Buf->buf, Buf->BufUsed + 1);
188                 Buf->BufSize = Buf->BufUsed + 1;
189                 free(Buf->buf);
190                 Buf->buf = TmpBuf;
191         }
192         return Buf->BufUsed;
193 }
194
195 /**
196  * @ingroup StrBuf
197  * @brief Allocate a new buffer with default buffer size
198  * @returns the new stringbuffer
199  */
200 StrBuf* NewStrBuf(void)
201 {
202         StrBuf *NewBuf;
203
204         NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
205         NewBuf->buf = (char*) malloc(BaseStrBufSize);
206         NewBuf->buf[0] = '\0';
207         NewBuf->BufSize = BaseStrBufSize;
208         NewBuf->BufUsed = 0;
209         NewBuf->ConstBuf = 0;
210 #ifdef SIZE_DEBUG
211         NewBuf->nIncreases = 0;
212         NewBuf->bt[0] = '\0';
213         NewBuf->bt_lastinc[0] = '\0';
214 #ifdef HAVE_BACKTRACE
215         StrBufBacktrace(NewBuf, 0);
216 #endif
217 #endif
218         return NewBuf;
219 }
220
221 /** 
222  * @ingroup StrBuf
223  * @brief Copy Constructor; returns a duplicate of CopyMe
224  * @param CopyMe Buffer to faxmilate
225  * @returns the new stringbuffer
226  */
227 StrBuf* NewStrBufDup(const StrBuf *CopyMe)
228 {
229         StrBuf *NewBuf;
230         
231         if (CopyMe == NULL)
232                 return NewStrBuf();
233
234         NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
235         NewBuf->buf = (char*) malloc(CopyMe->BufSize);
236         memcpy(NewBuf->buf, CopyMe->buf, CopyMe->BufUsed + 1);
237         NewBuf->BufUsed = CopyMe->BufUsed;
238         NewBuf->BufSize = CopyMe->BufSize;
239         NewBuf->ConstBuf = 0;
240 #ifdef SIZE_DEBUG
241         NewBuf->nIncreases = 0;
242         NewBuf->bt[0] = '\0';
243         NewBuf->bt_lastinc[0] = '\0';
244 #ifdef HAVE_BACKTRACE
245         StrBufBacktrace(NewBuf, 0);
246 #endif
247 #endif
248         return NewBuf;
249 }
250
251 /**
252  * @ingroup StrBuf
253  * @brief create a new Buffer using an existing c-string
254  * this function should also be used if you want to pre-suggest
255  * the buffer size to allocate in conjunction with ptr == NULL
256  * @param ptr the c-string to copy; may be NULL to create a blank instance
257  * @param nChars How many chars should we copy; -1 if we should measure the length ourselves
258  * @returns the new stringbuffer
259  */
260 StrBuf* NewStrBufPlain(const char* ptr, int nChars)
261 {
262         StrBuf *NewBuf;
263         size_t Siz = BaseStrBufSize;
264         size_t CopySize;
265
266         NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
267         if (nChars < 0)
268                 CopySize = strlen((ptr != NULL)?ptr:"");
269         else
270                 CopySize = nChars;
271
272         while (Siz <= CopySize)
273                 Siz *= 2;
274
275         NewBuf->buf = (char*) malloc(Siz);
276         NewBuf->BufSize = Siz;
277         if (ptr != NULL) {
278                 memcpy(NewBuf->buf, ptr, CopySize);
279                 NewBuf->buf[CopySize] = '\0';
280                 NewBuf->BufUsed = CopySize;
281         }
282         else {
283                 NewBuf->buf[0] = '\0';
284                 NewBuf->BufUsed = 0;
285         }
286         NewBuf->ConstBuf = 0;
287 #ifdef SIZE_DEBUG
288         NewBuf->nIncreases = 0;
289         NewBuf->bt[0] = '\0';
290         NewBuf->bt_lastinc[0] = '\0';
291 #ifdef HAVE_BACKTRACE
292         StrBufBacktrace(NewBuf, 0);
293 #endif
294 #endif
295         return NewBuf;
296 }
297
298 /**
299  * @ingroup StrBuf
300  * @brief Set an existing buffer from a c-string
301  * @param ptr c-string to put into 
302  * @param nChars set to -1 if we should work 0-terminated
303  * @returns the new length of the string
304  */
305 int StrBufPlain(StrBuf *Buf, const char* ptr, int nChars)
306 {
307         size_t Siz = Buf->BufSize;
308         size_t CopySize;
309
310         if (nChars < 0)
311                 CopySize = strlen(ptr);
312         else
313                 CopySize = nChars;
314
315         while (Siz <= CopySize)
316                 Siz *= 2;
317
318         if (Siz != Buf->BufSize)
319                 IncreaseBuf(Buf, 0, Siz);
320         memcpy(Buf->buf, ptr, CopySize);
321         Buf->buf[CopySize] = '\0';
322         Buf->BufUsed = CopySize;
323         Buf->ConstBuf = 0;
324         return CopySize;
325 }
326
327
328 /**
329  * @ingroup StrBuf
330  * @brief use strbuf as wrapper for a string constant for easy handling
331  * @param StringConstant a string to wrap
332  * @param SizeOfStrConstant should be sizeof(StringConstant)-1
333  */
334 StrBuf* _NewConstStrBuf(const char* StringConstant, size_t SizeOfStrConstant)
335 {
336         StrBuf *NewBuf;
337
338         NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
339         NewBuf->buf = (char*) StringConstant;
340         NewBuf->BufSize = SizeOfStrConstant;
341         NewBuf->BufUsed = SizeOfStrConstant;
342         NewBuf->ConstBuf = 1;
343 #ifdef SIZE_DEBUG
344         NewBuf->nIncreases = 0;
345         NewBuf->bt[0] = '\0';
346         NewBuf->bt_lastinc[0] = '\0';
347 #endif
348         return NewBuf;
349 }
350
351
352 /**
353  * @ingroup StrBuf
354  * @brief flush the content of a Buf; keep its struct
355  * @param buf Buffer to flush
356  */
357 int FlushStrBuf(StrBuf *buf)
358 {
359         if (buf == NULL)
360                 return -1;
361         if (buf->ConstBuf)
362                 return -1;       
363         buf->buf[0] ='\0';
364         buf->BufUsed = 0;
365         return 0;
366 }
367
368 /**
369  * @brief wipe the content of a Buf thoroughly (overwrite it -> expensive); keep its struct
370  * @param buf Buffer to wipe
371  */
372 int FLUSHStrBuf(StrBuf *buf)
373 {
374         if (buf == NULL)
375                 return -1;
376         if (buf->ConstBuf)
377                 return -1;
378         if (buf->BufUsed > 0) {
379                 memset(buf->buf, 0, buf->BufUsed);
380                 buf->BufUsed = 0;
381         }
382         return 0;
383 }
384
385 #ifdef SIZE_DEBUG
386 int hFreeDbglog = -1;
387 #endif
388 /**
389  * @ingroup StrBuf
390  * @brief Release a Buffer
391  * Its a double pointer, so it can NULL your pointer
392  * so fancy SIG11 appear instead of random results
393  * @param FreeMe Pointer Pointer to the buffer to free
394  */
395 void FreeStrBuf (StrBuf **FreeMe)
396 {
397         if (*FreeMe == NULL)
398                 return;
399 #ifdef SIZE_DEBUG
400         if (hFreeDbglog == -1){
401                 pid_t pid = getpid();
402                 char path [SIZ];
403                 snprintf(path, SIZ, "/tmp/libcitadel_strbuf_realloc.log.%d", pid);
404                 hFreeDbglog = open(path, O_APPEND|O_CREAT|O_WRONLY);
405         }
406         if ((*FreeMe)->nIncreases > 0)
407         {
408                 char buf[SIZ * 3];
409                 long n;
410                 n = snprintf(buf, SIZ * 3, "+|%ld|%ld|%ld|%s|%s|\n",
411                              (*FreeMe)->nIncreases,
412                              (*FreeMe)->BufUsed,
413                              (*FreeMe)->BufSize,
414                              (*FreeMe)->bt,
415                              (*FreeMe)->bt_lastinc);
416                 n = write(hFreeDbglog, buf, n);
417         }
418         else
419         {
420                 char buf[128];
421                 long n;
422                 n = snprintf(buf, 128, "_|0|%ld%ld|\n",
423                              (*FreeMe)->BufUsed,
424                              (*FreeMe)->BufSize);
425                 n = write(hFreeDbglog, buf, n);
426         }
427 #endif
428         if (!(*FreeMe)->ConstBuf) 
429                 free((*FreeMe)->buf);
430         free(*FreeMe);
431         *FreeMe = NULL;
432 }
433
434 /**
435  * @ingroup StrBuf
436  * @brief flatten a Buffer to the Char * we return 
437  * Its a double pointer, so it can NULL your pointer
438  * so fancy SIG11 appear instead of random results
439  * The Callee then owns the buffer and is responsible for freeing it.
440  * @param SmashMe Pointer Pointer to the buffer to release Buf from and free
441  * @returns the pointer of the buffer; Callee owns the memory thereafter.
442  */
443 char *SmashStrBuf (StrBuf **SmashMe)
444 {
445         char *Ret;
446
447         if (*SmashMe == NULL)
448                 return NULL;
449 #ifdef SIZE_DEBUG
450         if (hFreeDbglog == -1){
451                 pid_t pid = getpid();
452                 char path [SIZ];
453                 snprintf(path, SIZ, "/tmp/libcitadel_strbuf_realloc.log.%d", pid);
454                 hFreeDbglog = open(path, O_APPEND|O_CREAT|O_WRONLY);
455         }
456         if ((*SmashMe)->nIncreases > 0)
457         {
458                 char buf[SIZ * 3];
459                 long n;
460                 n = snprintf(buf, SIZ * 3, "S+|%ld|%ld|%ld|%s|%s|\n",
461                              (*SmashMe)->nIncreases,
462                              (*SmashMe)->BufUsed,
463                              (*SmashMe)->BufSize,
464                              (*SmashMe)->bt,
465                              (*SmashMe)->bt_lastinc);
466                 n = write(hFreeDbglog, buf, n);
467         }
468         else
469         {
470                 char buf[128];
471                 long n;
472                 n = snprintf(buf, 128, "S_|0|%ld%ld|\n",
473                              (*SmashMe)->BufUsed,
474                              (*SmashMe)->BufSize);
475                 n = write(hFreeDbglog, buf, n);
476         }
477 #endif
478         Ret = (*SmashMe)->buf;
479         free(*SmashMe);
480         *SmashMe = NULL;
481         return Ret;
482 }
483
484 /**
485  * @ingroup StrBuf
486  * @brief Release the buffer
487  * If you want put your StrBuf into a Hash, use this as Destructor.
488  * @param VFreeMe untyped pointer to a StrBuf. be shure to do the right thing [TM]
489  */
490 void HFreeStrBuf (void *VFreeMe)
491 {
492         StrBuf *FreeMe = (StrBuf*)VFreeMe;
493         if (FreeMe == NULL)
494                 return;
495 #ifdef SIZE_DEBUG
496         if (hFreeDbglog == -1){
497                 pid_t pid = getpid();
498                 char path [SIZ];
499                 snprintf(path, SIZ, "/tmp/libcitadel_strbuf_realloc.log.%d", pid);
500                 hFreeDbglog = open(path, O_APPEND|O_CREAT|O_WRONLY);
501         }
502         if (FreeMe->nIncreases > 0)
503         {
504                 char buf[SIZ * 3];
505                 long n;
506                 n = snprintf(buf, SIZ * 3, "+|%ld|%ld|%ld|%s|%s|\n",
507                              FreeMe->nIncreases,
508                              FreeMe->BufUsed,
509                              FreeMe->BufSize,
510                              FreeMe->bt,
511                              FreeMe->bt_lastinc);
512                 write(hFreeDbglog, buf, n);
513         }
514         else
515         {
516                 char buf[128];
517                 long n;
518                 n = snprintf(buf, 128, "_|%ld|%ld%ld|\n",
519                              FreeMe->nIncreases,
520                              FreeMe->BufUsed,
521                              FreeMe->BufSize);
522         }
523 #endif
524         if (!FreeMe->ConstBuf) 
525                 free(FreeMe->buf);
526         free(FreeMe);
527 }
528
529 /**
530  * @ingroup StrBuf
531  * @brief Wrapper around atol
532  */
533 long StrTol(const StrBuf *Buf)
534 {
535         if (Buf == NULL)
536                 return 0;
537         if(Buf->BufUsed > 0)
538                 return atol(Buf->buf);
539         else
540                 return 0;
541 }
542
543 /**
544  * @ingroup StrBuf
545  * @brief Wrapper around atoi
546  */
547 int StrToi(const StrBuf *Buf)
548 {
549         if (Buf == NULL)
550                 return 0;
551         if (Buf->BufUsed > 0)
552                 return atoi(Buf->buf);
553         else
554                 return 0;
555 }
556
557 /**
558  * @ingroup StrBuf
559  * @brief Checks to see if the string is a pure number 
560  */
561 int StrBufIsNumber(const StrBuf *Buf) {
562   char * pEnd;
563   if (Buf == NULL) {
564         return 0;
565   }
566   strtoll(Buf->buf, &pEnd, 10);
567   if (pEnd == NULL && ((Buf->buf)-pEnd) != 0) {
568     return 1;
569   }
570   return 0;
571
572 /**
573  * @ingroup StrBuf
574  * @brief modifies a Single char of the Buf
575  * You can point to it via char* or a zero-based integer
576  * @param Buf The buffer to manipulate
577  * @param ptr char* to zero; use NULL if unused
578  * @param nThChar zero based pointer into the string; use -1 if unused
579  * @param PeekValue The Character to place into the position
580  */
581 long StrBufPeek(StrBuf *Buf, const char* ptr, long nThChar, char PeekValue)
582 {
583         if (Buf == NULL)
584                 return -1;
585         if (ptr != NULL)
586                 nThChar = ptr - Buf->buf;
587         if ((nThChar < 0) || (nThChar > Buf->BufUsed))
588                 return -1;
589         Buf->buf[nThChar] = PeekValue;
590         return nThChar;
591 }
592
593 /**
594  * @ingroup StrBuf
595  * @brief Append a StringBuffer to the buffer
596  * @param Buf Buffer to modify
597  * @param AppendBuf Buffer to copy at the end of our buffer
598  * @param Offset Should we start copying from an offset?
599  */
600 void StrBufAppendBuf(StrBuf *Buf, const StrBuf *AppendBuf, unsigned long Offset)
601 {
602         if ((AppendBuf == NULL) || (Buf == NULL) || (AppendBuf->buf == NULL))
603                 return;
604
605         if (Buf->BufSize - Offset < AppendBuf->BufUsed + Buf->BufUsed + 1)
606                 IncreaseBuf(Buf, 
607                             (Buf->BufUsed > 0), 
608                             AppendBuf->BufUsed + Buf->BufUsed);
609
610         memcpy(Buf->buf + Buf->BufUsed, 
611                AppendBuf->buf + Offset, 
612                AppendBuf->BufUsed - Offset);
613         Buf->BufUsed += AppendBuf->BufUsed - Offset;
614         Buf->buf[Buf->BufUsed] = '\0';
615 }
616
617
618 /**
619  * @ingroup StrBuf
620  * @brief Append a C-String to the buffer
621  * @param Buf Buffer to modify
622  * @param AppendBuf Buffer to copy at the end of our buffer
623  * @param AppendSize number of bytes to copy; set to -1 if we should count it in advance
624  * @param Offset Should we start copying from an offset?
625  */
626 void StrBufAppendBufPlain(StrBuf *Buf, const char *AppendBuf, long AppendSize, unsigned long Offset)
627 {
628         long aps;
629         long BufSizeRequired;
630
631         if ((AppendBuf == NULL) || (Buf == NULL))
632                 return;
633
634         if (AppendSize < 0 )
635                 aps = strlen(AppendBuf + Offset);
636         else
637                 aps = AppendSize - Offset;
638
639         BufSizeRequired = Buf->BufUsed + aps + 1;
640         if (Buf->BufSize <= BufSizeRequired)
641                 IncreaseBuf(Buf, (Buf->BufUsed > 0), BufSizeRequired);
642
643         memcpy(Buf->buf + Buf->BufUsed, 
644                AppendBuf + Offset, 
645                aps);
646         Buf->BufUsed += aps;
647         Buf->buf[Buf->BufUsed] = '\0';
648 }
649
650 /**
651  * @ingroup StrBuf
652  * @brief Callback for cURL to append the webserver reply to a buffer
653  * @param ptr pre-defined by the cURL API; see man 3 curl for mre info
654  * @param size pre-defined by the cURL API; see man 3 curl for mre info
655  * @param nmemb pre-defined by the cURL API; see man 3 curl for mre info
656  * @param stream pre-defined by the cURL API; see man 3 curl for mre info
657  */
658 size_t CurlFillStrBuf_callback(void *ptr, size_t size, size_t nmemb, void *stream)
659 {
660
661         StrBuf *Target;
662
663         Target = stream;
664         if (ptr == NULL)
665                 return 0;
666
667         StrBufAppendBufPlain(Target, ptr, size * nmemb, 0);
668         return size * nmemb;
669 }
670
671
672 /** 
673  * @ingroup StrBuf
674  * @brief Escape a string for feeding out as a URL while appending it to a Buffer
675  * @param outbuf the output buffer
676  * @param oblen the size of outbuf to sanitize
677  * @param strbuf the input buffer
678  */
679 void StrBufUrlescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
680 {
681         const char *pch, *pche;
682         char *pt, *pte;
683         int b, c, len;
684         const char ec[] = " +#&;`'|*?-~<>^()[]{}/$\"\\";
685         int eclen = sizeof(ec) -1;
686
687         if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
688                 return;
689         if (PlainIn != NULL) {
690                 len = strlen(PlainIn);
691                 pch = PlainIn;
692                 pche = pch + len;
693         }
694         else {
695                 pch = In->buf;
696                 pche = pch + In->BufUsed;
697                 len = In->BufUsed;
698         }
699
700         if (len == 0) 
701                 return;
702
703         pt = OutBuf->buf + OutBuf->BufUsed;
704         pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
705
706         while (pch < pche) {
707                 if (pt >= pte) {
708                         IncreaseBuf(OutBuf, 1, -1);
709                         pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
710                         pt = OutBuf->buf + OutBuf->BufUsed;
711                 }
712                 
713                 c = 0;
714                 for (b = 0; b < eclen; ++b) {
715                         if (*pch == ec[b]) {
716                                 c = 1;
717                                 b += eclen;
718                         }
719                 }
720                 if (c == 1) {
721                         sprintf(pt,"%%%02X", *pch);
722                         pt += 3;
723                         OutBuf->BufUsed += 3;
724                         pch ++;
725                 }
726                 else {
727                         *(pt++) = *(pch++);
728                         OutBuf->BufUsed++;
729                 }
730         }
731         *pt = '\0';
732 }
733
734 /**
735  * @ingroup StrBuf
736  * @brief Append a string, escaping characters which have meaning in HTML.  
737  *
738  * @param Target        target buffer
739  * @param Source        source buffer; set to NULL if you just have a C-String
740  * @param PlainIn       Plain-C string to append; set to NULL if unused
741  * @param nbsp          If nonzero, spaces are converted to non-breaking spaces.
742  * @param nolinebreaks  if set to 1, linebreaks are removed from the string.
743  *                      if set to 2, linebreaks are replaced by &ltbr/&gt
744  */
745 long StrEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
746 {
747         const char *aptr, *eiptr;
748         char *bptr, *eptr;
749         long len;
750
751         if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
752                 return -1;
753
754         if (PlainIn != NULL) {
755                 aptr = PlainIn;
756                 len = strlen(PlainIn);
757                 eiptr = aptr + len;
758         }
759         else {
760                 aptr = Source->buf;
761                 eiptr = aptr + Source->BufUsed;
762                 len = Source->BufUsed;
763         }
764
765         if (len == 0) 
766                 return -1;
767
768         bptr = Target->buf + Target->BufUsed;
769         eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in...  */
770
771         while (aptr < eiptr){
772                 if(bptr >= eptr) {
773                         IncreaseBuf(Target, 1, -1);
774                         eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in...  */
775                         bptr = Target->buf + Target->BufUsed;
776                 }
777                 if (*aptr == '<') {
778                         memcpy(bptr, "&lt;", 4);
779                         bptr += 4;
780                         Target->BufUsed += 4;
781                 }
782                 else if (*aptr == '>') {
783                         memcpy(bptr, "&gt;", 4);
784                         bptr += 4;
785                         Target->BufUsed += 4;
786                 }
787                 else if (*aptr == '&') {
788                         memcpy(bptr, "&amp;", 5);
789                         bptr += 5;
790                         Target->BufUsed += 5;
791                 }
792                 else if (*aptr == '"') {
793                         memcpy(bptr, "&quot;", 6);
794                         bptr += 6;
795                         Target->BufUsed += 6;
796                 }
797                 else if (*aptr == '\'') {
798                         memcpy(bptr, "&#39;", 5);
799                         bptr += 5;
800                         Target->BufUsed += 5;
801                 }
802                 else if (*aptr == LB) {
803                         *bptr = '<';
804                         bptr ++;
805                         Target->BufUsed ++;
806                 }
807                 else if (*aptr == RB) {
808                         *bptr = '>';
809                         bptr ++;
810                         Target->BufUsed ++;
811                 }
812                 else if (*aptr == QU) {
813                         *bptr ='"';
814                         bptr ++;
815                         Target->BufUsed ++;
816                 }
817                 else if ((*aptr == 32) && (nbsp == 1)) {
818                         memcpy(bptr, "&nbsp;", 6);
819                         bptr += 6;
820                         Target->BufUsed += 6;
821                 }
822                 else if ((*aptr == '\n') && (nolinebreaks == 1)) {
823                         *bptr='\0';     /* nothing */
824                 }
825                 else if ((*aptr == '\n') && (nolinebreaks == 2)) {
826                         memcpy(bptr, "&lt;br/&gt;", 11);
827                         bptr += 11;
828                         Target->BufUsed += 11;
829                 }
830
831
832                 else if ((*aptr == '\r') && (nolinebreaks != 0)) {
833                         *bptr='\0';     /* nothing */
834                 }
835                 else{
836                         *bptr = *aptr;
837                         bptr++;
838                         Target->BufUsed ++;
839                 }
840                 aptr ++;
841         }
842         *bptr = '\0';
843         if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
844                 return -1;
845         return Target->BufUsed;
846 }
847
848 /**
849  * @ingroup StrBuf
850  * @brief Append a string, escaping characters which have meaning in HTML.  
851  * Converts linebreaks into blanks; escapes single quotes
852  * @param Target        target buffer
853  * @param Source        source buffer; set to NULL if you just have a C-String
854  * @param PlainIn       Plain-C string to append; set to NULL if unused
855  */
856 void StrMsgEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
857 {
858         const char *aptr, *eiptr;
859         char *tptr, *eptr;
860         long len;
861
862         if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
863                 return ;
864
865         if (PlainIn != NULL) {
866                 aptr = PlainIn;
867                 len = strlen(PlainIn);
868                 eiptr = aptr + len;
869         }
870         else {
871                 aptr = Source->buf;
872                 eiptr = aptr + Source->BufUsed;
873                 len = Source->BufUsed;
874         }
875
876         if (len == 0) 
877                 return;
878
879         eptr = Target->buf + Target->BufSize - 8; 
880         tptr = Target->buf + Target->BufUsed;
881         
882         while (aptr < eiptr){
883                 if(tptr >= eptr) {
884                         IncreaseBuf(Target, 1, -1);
885                         eptr = Target->buf + Target->BufSize - 8; 
886                         tptr = Target->buf + Target->BufUsed;
887                 }
888                
889                 if (*aptr == '\n') {
890                         *tptr = ' ';
891                         Target->BufUsed++;
892                 }
893                 else if (*aptr == '\r') {
894                         *tptr = ' ';
895                         Target->BufUsed++;
896                 }
897                 else if (*aptr == '\'') {
898                         *(tptr++) = '&';
899                         *(tptr++) = '#';
900                         *(tptr++) = '3';
901                         *(tptr++) = '9';
902                         *tptr = ';';
903                         Target->BufUsed += 5;
904                 } else {
905                         *tptr = *aptr;
906                         Target->BufUsed++;
907                 }
908                 tptr++; aptr++;
909         }
910         *tptr = '\0';
911 }
912
913
914
915 /**
916  * @ingroup StrBuf
917  * @brief Append a string, escaping characters which have meaning in ICAL.  
918  * [\n,] 
919  * @param Target        target buffer
920  * @param Source        source buffer; set to NULL if you just have a C-String
921  * @param PlainIn       Plain-C string to append; set to NULL if unused
922  */
923 void StrIcalEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
924 {
925         const char *aptr, *eiptr;
926         char *tptr, *eptr;
927         long len;
928
929         if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
930                 return ;
931
932         if (PlainIn != NULL) {
933                 aptr = PlainIn;
934                 len = strlen(PlainIn);
935                 eiptr = aptr + len;
936         }
937         else {
938                 aptr = Source->buf;
939                 eiptr = aptr + Source->BufUsed;
940                 len = Source->BufUsed;
941         }
942
943         if (len == 0) 
944                 return;
945
946         eptr = Target->buf + Target->BufSize - 8; 
947         tptr = Target->buf + Target->BufUsed;
948         
949         while (aptr < eiptr){
950                 if(tptr + 3 >= eptr) {
951                         IncreaseBuf(Target, 1, -1);
952                         eptr = Target->buf + Target->BufSize - 8; 
953                         tptr = Target->buf + Target->BufUsed;
954                 }
955                
956                 if (*aptr == '\n') {
957                         *tptr = '\\';
958                         Target->BufUsed++;
959                         tptr++;
960                         *tptr = 'n';
961                         Target->BufUsed++;
962                 }
963                 else if (*aptr == '\r') {
964                         *tptr = '\\';
965                         Target->BufUsed++;
966                         tptr++;
967                         *tptr = 'r';
968                         Target->BufUsed++;
969                 }
970                 else if (*aptr == ',') {
971                         *tptr = '\\';
972                         Target->BufUsed++;
973                         tptr++;
974                         *tptr = ',';
975                         Target->BufUsed++;
976                 } else {
977                         *tptr = *aptr;
978                         Target->BufUsed++;
979                 }
980                 tptr++; aptr++;
981         }
982         *tptr = '\0';
983 }
984
985 /**
986  * @ingroup StrBuf
987  * @brief Append a string, escaping characters which have meaning in JavaScript strings .  
988  *
989  * @param Target        target buffer
990  * @param Source        source buffer; set to NULL if you just have a C-String
991  * @param PlainIn       Plain-C string to append; set to NULL if unused
992  * @returns size of result or -1
993  */
994 long StrECMAEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
995 {
996         const char *aptr, *eiptr;
997         char *bptr, *eptr;
998         long len;
999
1000         if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
1001                 return -1;
1002
1003         if (PlainIn != NULL) {
1004                 aptr = PlainIn;
1005                 len = strlen(PlainIn);
1006                 eiptr = aptr + len;
1007         }
1008         else {
1009                 aptr = Source->buf;
1010                 eiptr = aptr + Source->BufUsed;
1011                 len = Source->BufUsed;
1012         }
1013
1014         if (len == 0) 
1015                 return -1;
1016
1017         bptr = Target->buf + Target->BufUsed;
1018         eptr = Target->buf + Target->BufSize - 3; /* our biggest unit to put in...  */
1019
1020         while (aptr < eiptr){
1021                 if(bptr >= eptr) {
1022                         IncreaseBuf(Target, 1, -1);
1023                         eptr = Target->buf + Target->BufSize - 3; 
1024                         bptr = Target->buf + Target->BufUsed;
1025                 }
1026                 if (*aptr == '"') {
1027                         *bptr = '\\';
1028                         bptr ++;
1029                         *bptr = '"';
1030                         bptr ++;
1031                         Target->BufUsed += 2;
1032                 } else if (*aptr == '\\') {
1033                         *bptr = '\\';
1034                         bptr ++;
1035                         *bptr = '\\';
1036                         bptr ++;
1037                         Target->BufUsed += 2;
1038                 }
1039                 else{
1040                         *bptr = *aptr;
1041                         bptr++;
1042                         Target->BufUsed ++;
1043                 }
1044                 aptr ++;
1045         }
1046         *bptr = '\0';
1047         if ((bptr == eptr - 1 ) && !IsEmptyStr(aptr) )
1048                 return -1;
1049         return Target->BufUsed;
1050 }
1051
1052 /**
1053  * @ingroup StrBuf
1054  * @brief Append a string, escaping characters which have meaning in HTML + json.  
1055  *
1056  * @param Target        target buffer
1057  * @param Source        source buffer; set to NULL if you just have a C-String
1058  * @param PlainIn       Plain-C string to append; set to NULL if unused
1059  * @param nbsp          If nonzero, spaces are converted to non-breaking spaces.
1060  * @param nolinebreaks  if set to 1, linebreaks are removed from the string.
1061  *                      if set to 2, linebreaks are replaced by &ltbr/&gt
1062  */
1063 long StrHtmlEcmaEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
1064 {
1065         const char *aptr, *eiptr;
1066         char *bptr, *eptr;
1067         long len;
1068         int IsUtf8Sequence = 0;
1069
1070         if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
1071                 return -1;
1072
1073         if (PlainIn != NULL) {
1074                 aptr = PlainIn;
1075                 len = strlen(PlainIn);
1076                 eiptr = aptr + len;
1077         }
1078         else {
1079                 aptr = Source->buf;
1080                 eiptr = aptr + Source->BufUsed;
1081                 len = Source->BufUsed;
1082         }
1083
1084         if (len == 0) 
1085                 return -1;
1086
1087         bptr = Target->buf + Target->BufUsed;
1088         eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in...  */
1089
1090         while (aptr < eiptr){
1091                 if(bptr >= eptr) {
1092                         IncreaseBuf(Target, 1, -1);
1093                         eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in...  */
1094                         bptr = Target->buf + Target->BufUsed;
1095                 }
1096                 if (*aptr == '<') {
1097                         memcpy(bptr, "&lt;", 4);
1098                         bptr += 4;
1099                         Target->BufUsed += 4;
1100                 }
1101                 else if (*aptr == '>') {
1102                         memcpy(bptr, "&gt;", 4);
1103                         bptr += 4;
1104                         Target->BufUsed += 4;
1105                 }
1106                 else if (*aptr == '&') {
1107                         memcpy(bptr, "&amp;", 5);
1108                         bptr += 5;
1109                         Target->BufUsed += 5;
1110                 }
1111                 else if (*aptr == LB) {
1112                         *bptr = '<';
1113                         bptr ++;
1114                         Target->BufUsed ++;
1115                 }
1116                 else if (*aptr == RB) {
1117                         *bptr = '>';
1118                         bptr ++;
1119                         Target->BufUsed ++;
1120                 }
1121                 else if ((*aptr == 32) && (nbsp == 1)) {
1122                         memcpy(bptr, "&nbsp;", 6);
1123                         bptr += 6;
1124                         Target->BufUsed += 6;
1125                 }
1126                 else if ((*aptr == '\n') && (nolinebreaks == 1)) {
1127                         *bptr='\0';     /* nothing */
1128                 }
1129                 else if ((*aptr == '\n') && (nolinebreaks == 2)) {
1130                         memcpy(bptr, "&lt;br/&gt;", 11);
1131                         bptr += 11;
1132                         Target->BufUsed += 11;
1133                 }
1134
1135                 else if ((*aptr == '\r') && (nolinebreaks != 0)) {
1136                         *bptr='\0';     /* nothing */
1137                 }
1138
1139                 else if ((*aptr == '"') || (*aptr == QU)) {
1140                         *bptr = '\\';
1141                         bptr ++;
1142                         *bptr = '"';
1143                         bptr ++;
1144                         Target->BufUsed += 2;
1145                 } else if (*aptr == '\\') {
1146                         *bptr = '\\';
1147                         bptr ++;
1148                         *bptr = '\\';
1149                         bptr ++;
1150                         Target->BufUsed += 2;
1151                 }
1152                 else {
1153                         if (IsUtf8Sequence != 0) {
1154                                 IsUtf8Sequence --;
1155                                 *bptr = *aptr;
1156                                 bptr++;
1157                                 Target->BufUsed ++;
1158                         }
1159                         else {
1160                                 if (*aptr >= 0x20)
1161                                 {
1162                                         IsUtf8Sequence =  Ctdl_GetUtf8SequenceLength(aptr, eiptr);
1163
1164                                         *bptr = *aptr;
1165                                         bptr++;
1166                                         Target->BufUsed ++;
1167                                 }
1168                         }
1169
1170                 }
1171                 aptr ++;
1172         }
1173         *bptr = '\0';
1174         if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
1175                 return -1;
1176         return Target->BufUsed;
1177 }
1178
1179
1180 /**
1181  * @ingroup StrBuf
1182  * @brief extracts a substring from Source into dest
1183  * @param dest buffer to place substring into
1184  * @param Source string to copy substring from
1185  * @param Offset chars to skip from start
1186  * @param nChars number of chars to copy
1187  * @returns the number of chars copied; may be different from nChars due to the size of Source
1188  */
1189 int StrBufSub(StrBuf *dest, const StrBuf *Source, unsigned long Offset, size_t nChars)
1190 {
1191         size_t NCharsRemain;
1192         if (Offset > Source->BufUsed)
1193         {
1194                 FlushStrBuf(dest);
1195                 return 0;
1196         }
1197         if (Offset + nChars < Source->BufUsed)
1198         {
1199                 if (nChars >= dest->BufSize)
1200                         IncreaseBuf(dest, 0, nChars + 1);
1201                 memcpy(dest->buf, Source->buf + Offset, nChars);
1202                 dest->BufUsed = nChars;
1203                 dest->buf[dest->BufUsed] = '\0';
1204                 return nChars;
1205         }
1206         NCharsRemain = Source->BufUsed - Offset;
1207         if (NCharsRemain  >= dest->BufSize)
1208                 IncreaseBuf(dest, 0, NCharsRemain + 1);
1209         memcpy(dest->buf, Source->buf + Offset, NCharsRemain);
1210         dest->BufUsed = NCharsRemain;
1211         dest->buf[dest->BufUsed] = '\0';
1212         return NCharsRemain;
1213 }
1214
1215 /**
1216  * @ingroup StrBuf
1217  * @brief sprintf like function appending the formated string to the buffer
1218  * vsnprintf version to wrap into own calls
1219  * @param Buf Buffer to extend by format and @params
1220  * @param format printf alike format to add
1221  * @param ap va_list containing the items for format
1222  */
1223 void StrBufVAppendPrintf(StrBuf *Buf, const char *format, va_list ap)
1224 {
1225         va_list apl;
1226         size_t BufSize;
1227         size_t nWritten;
1228         size_t Offset;
1229         size_t newused;
1230
1231         if ((Buf == NULL)  || (format == NULL))
1232                 return;
1233
1234         BufSize = Buf->BufSize;
1235         nWritten = Buf->BufSize + 1;
1236         Offset = Buf->BufUsed;
1237         newused = Offset + nWritten;
1238         
1239         while (newused >= BufSize) {
1240                 va_copy(apl, ap);
1241                 nWritten = vsnprintf(Buf->buf + Offset, 
1242                                      Buf->BufSize - Offset, 
1243                                      format, apl);
1244                 va_end(apl);
1245                 newused = Offset + nWritten;
1246                 if (newused >= Buf->BufSize) {
1247                         IncreaseBuf(Buf, 1, newused);
1248                         newused = Buf->BufSize + 1;
1249                 }
1250                 else {
1251                         Buf->BufUsed = Offset + nWritten;
1252                         BufSize = Buf->BufSize;
1253                 }
1254
1255         }
1256 }
1257
1258 /**
1259  * @ingroup StrBuf
1260  * @brief sprintf like function appending the formated string to the buffer
1261  * @param Buf Buffer to extend by format and Params
1262  * @param format printf alike format to add
1263  */
1264 void StrBufAppendPrintf(StrBuf *Buf, const char *format, ...)
1265 {
1266         size_t BufSize;
1267         size_t nWritten;
1268         size_t Offset;
1269         size_t newused;
1270         va_list arg_ptr;
1271         
1272         if ((Buf == NULL)  || (format == NULL))
1273                 return;
1274
1275         BufSize = Buf->BufSize;
1276         nWritten = Buf->BufSize + 1;
1277         Offset = Buf->BufUsed;
1278         newused = Offset + nWritten;
1279
1280         while (newused >= BufSize) {
1281                 va_start(arg_ptr, format);
1282                 nWritten = vsnprintf(Buf->buf + Buf->BufUsed, 
1283                                      Buf->BufSize - Buf->BufUsed, 
1284                                      format, arg_ptr);
1285                 va_end(arg_ptr);
1286                 newused = Buf->BufUsed + nWritten;
1287                 if (newused >= Buf->BufSize) {
1288                         IncreaseBuf(Buf, 1, newused);
1289                         newused = Buf->BufSize + 1;
1290                 }
1291                 else {
1292                         Buf->BufUsed += nWritten;
1293                         BufSize = Buf->BufSize;
1294                 }
1295
1296         }
1297 }
1298
1299 /**
1300  * @ingroup StrBuf
1301  * @brief sprintf like function putting the formated string into the buffer
1302  * @param Buf Buffer to extend by format and Parameters
1303  * @param format printf alike format to add
1304  * @param ap va_list containing the items for format
1305  */
1306 void StrBufPrintf(StrBuf *Buf, const char *format, ...)
1307 {
1308         size_t nWritten;
1309         va_list arg_ptr;
1310         
1311         if ((Buf == NULL)  || (format == NULL))
1312                 return;
1313
1314         nWritten = Buf->BufSize + 1;
1315         while (nWritten >= Buf->BufSize) {
1316                 va_start(arg_ptr, format);
1317                 nWritten = vsnprintf(Buf->buf, Buf->BufSize, format, arg_ptr);
1318                 va_end(arg_ptr);
1319                 if (nWritten >= Buf->BufSize) {
1320                         IncreaseBuf(Buf, 0, 0);
1321                         nWritten = Buf->BufSize + 1;
1322                         continue;
1323                 }
1324                 Buf->BufUsed = nWritten ;
1325         }
1326 }
1327
1328
1329 /**
1330  * @ingroup StrBuf
1331  * @brief Counts the numbmer of tokens in a buffer
1332  * @param source String to count tokens in
1333  * @param tok    Tokenizer char to count
1334  * @returns numbers of tokenizer chars found
1335  */
1336 int StrBufNum_tokens(const StrBuf *source, char tok)
1337 {
1338         if (source == NULL)
1339                 return 0;
1340         return num_tokens(source->buf, tok);
1341 }
1342
1343 /*
1344  * remove_token() - a tokenizer that kills, maims, and destroys
1345  */
1346 /**
1347  * @ingroup StrBuf
1348  * @brief a string tokenizer
1349  * @param Source StringBuffer to read into
1350  * @param parmnum n'th @parameter to remove
1351  * @param separator tokenizer @param
1352  * @returns -1 if not found, else length of token.
1353  */
1354 int StrBufRemove_token(StrBuf *Source, int parmnum, char separator)
1355 {
1356         int ReducedBy;
1357         char *d, *s, *end;              /* dest, source */
1358         int count = 0;
1359
1360         /* Find desired @parameter */
1361         end = Source->buf + Source->BufUsed;
1362         d = Source->buf;
1363         while ((count < parmnum) &&
1364                (d <= end))
1365         {
1366                 /* End of string, bail! */
1367                 if (!*d) {
1368                         d = NULL;
1369                         break;
1370                 }
1371                 if (*d == separator) {
1372                         count++;
1373                 }
1374                 d++;
1375         }
1376         if ((d == NULL) || (d >= end))
1377                 return 0;               /* @Parameter not found */
1378
1379         /* Find next @parameter */
1380         s = d;
1381         while ((*s && *s != separator) &&
1382                (s <= end))
1383         {
1384                 s++;
1385         }
1386         if (*s == separator)
1387                 s++;
1388         ReducedBy = d - s;
1389
1390         /* Hack and slash */
1391         if (*s) {
1392                 memmove(d, s, Source->BufUsed - (s - Source->buf));
1393                 Source->BufUsed += ReducedBy;
1394         }
1395         else if (d == Source->buf) {
1396                 *d = 0;
1397                 Source->BufUsed = 0;
1398         }
1399         else {
1400                 *--d = 0;
1401                 Source->BufUsed += ReducedBy;
1402         }
1403         /*
1404         while (*s) {
1405                 *d++ = *s++;
1406         }
1407         *d = 0;
1408         */
1409         return ReducedBy;
1410 }
1411
1412
1413 /**
1414  * @ingroup StrBuf
1415  * @brief a string tokenizer
1416  * @param dest Destination StringBuffer
1417  * @param Source StringBuffer to read into
1418  * @param parmnum n'th Parameter to extract
1419  * @param separator tokenizer character
1420  * @returns -1 if not found, else length of token.
1421  */
1422 int StrBufExtract_token(StrBuf *dest, const StrBuf *Source, int parmnum, char separator)
1423 {
1424         const char *s, *e;              //* source * /
1425         int len = 0;                    //* running total length of extracted string * /
1426         int current_token = 0;          //* token currently being processed * /
1427          
1428         if (dest != NULL) {
1429                 dest->buf[0] = '\0';
1430                 dest->BufUsed = 0;
1431         }
1432         else
1433                 return(-1);
1434
1435         if ((Source == NULL) || (Source->BufUsed ==0)) {
1436                 return(-1);
1437         }
1438         s = Source->buf;
1439         e = s + Source->BufUsed;
1440
1441         //cit_backtrace();
1442         //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
1443
1444         while ((s<e) && !IsEmptyStr(s)) {
1445                 if (*s == separator) {
1446                         ++current_token;
1447                 }
1448                 if (len >= dest->BufSize) {
1449                         dest->BufUsed = len;
1450                         if (IncreaseBuf(dest, 1, -1) < 0) {
1451                                 dest->BufUsed --;
1452                                 break;
1453                         }
1454                 }
1455                 if ( (current_token == parmnum) && 
1456                      (*s != separator)) {
1457                         dest->buf[len] = *s;
1458                         ++len;
1459                 }
1460                 else if (current_token > parmnum) {
1461                         break;
1462                 }
1463                 ++s;
1464         }
1465         
1466         dest->buf[len] = '\0';
1467         dest->BufUsed = len;
1468                 
1469         if (current_token < parmnum) {
1470                 //lprintf (CTDL_DEBUG,"test <!: %s\n", dest);
1471                 return(-1);
1472         }
1473         //lprintf (CTDL_DEBUG,"test <: %d; %s\n", len, dest);
1474         return(len);
1475 }
1476
1477
1478
1479
1480
1481 /**
1482  * @ingroup StrBuf
1483  * @brief a string tokenizer to fetch an integer
1484  * @param Source String containing tokens
1485  * @param parmnum n'th Parameter to extract
1486  * @param separator tokenizer character
1487  * @returns 0 if not found, else integer representation of the token
1488  */
1489 int StrBufExtract_int(const StrBuf* Source, int parmnum, char separator)
1490 {
1491         StrBuf tmp;
1492         char buf[64];
1493         
1494         tmp.buf = buf;
1495         buf[0] = '\0';
1496         tmp.BufSize = 64;
1497         tmp.BufUsed = 0;
1498         tmp.ConstBuf = 1;
1499         if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
1500                 return(atoi(buf));
1501         else
1502                 return 0;
1503 }
1504
1505 /**
1506  * @ingroup StrBuf
1507  * @brief a string tokenizer to fetch a long integer
1508  * @param Source String containing tokens
1509  * @param parmnum n'th Parameter to extract
1510  * @param separator tokenizer character
1511  * @returns 0 if not found, else long integer representation of the token
1512  */
1513 long StrBufExtract_long(const StrBuf* Source, int parmnum, char separator)
1514 {
1515         StrBuf tmp;
1516         char buf[64];
1517         
1518         tmp.buf = buf;
1519         buf[0] = '\0';
1520         tmp.BufSize = 64;
1521         tmp.BufUsed = 0;
1522         tmp.ConstBuf = 1;
1523         if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
1524                 return(atoi(buf));
1525         else
1526                 return 0;
1527 }
1528
1529
1530 /**
1531  * @ingroup StrBuf
1532  * @brief a string tokenizer to fetch an unsigned long
1533  * @param Source String containing tokens
1534  * @param parmnum n'th Parameter to extract
1535  * @param separator tokenizer character
1536  * @returns 0 if not found, else unsigned long representation of the token
1537  */
1538 unsigned long StrBufExtract_unsigned_long(const StrBuf* Source, int parmnum, char separator)
1539 {
1540         StrBuf tmp;
1541         char buf[64];
1542         char *pnum;
1543         
1544         tmp.buf = buf;
1545         buf[0] = '\0';
1546         tmp.BufSize = 64;
1547         tmp.BufUsed = 0;
1548         tmp.ConstBuf = 1;
1549         if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0) {
1550                 pnum = &buf[0];
1551                 if (*pnum == '-')
1552                         pnum ++;
1553                 return (unsigned long) atol(pnum);
1554         }
1555         else 
1556                 return 0;
1557 }
1558
1559
1560
1561 /**
1562  * @ingroup StrBuf
1563  * @brief a string tokenizer; Bounds checker
1564  *  function to make shure whether StrBufExtract_NextToken and friends have reached the end of the string.
1565  * @param Source our tokenbuffer
1566  * @param pStart the token iterator pointer to inspect
1567  * @returns whether the revolving pointer is inside of the search range
1568  */
1569 int StrBufHaveNextToken(const StrBuf *Source, const char **pStart)
1570 {
1571         if ((Source == NULL) || 
1572             (*pStart == StrBufNOTNULL) ||
1573             (Source->BufUsed == 0))
1574         {
1575                 return 0;
1576         }
1577         if (*pStart == NULL)
1578         {
1579                 return 1;
1580         }
1581         else if (*pStart > Source->buf + Source->BufUsed)
1582         {
1583                 return 0;
1584         }
1585         else if (*pStart <= Source->buf)
1586         {
1587                 return 0;
1588         }
1589
1590         return 1;
1591 }
1592
1593 /**
1594  * @ingroup StrBuf
1595  * @brief a string tokenizer
1596  * @param dest Destination StringBuffer
1597  * @param Source StringBuffer to read into
1598  * @param pStart pointer to the end of the last token. Feed with NULL on start.
1599  * @param separator tokenizer 
1600  * @returns -1 if not found, else length of token.
1601  */
1602 int StrBufExtract_NextToken(StrBuf *dest, const StrBuf *Source, const char **pStart, char separator)
1603 {
1604         const char *s;          /* source */
1605         const char *EndBuffer;  /* end stop of source buffer */
1606         int current_token = 0;  /* token currently being processed */
1607         int len = 0;            /* running total length of extracted string */
1608
1609         if ((Source          == NULL) || 
1610             (Source->BufUsed == 0)      ) 
1611         {
1612                 *pStart = StrBufNOTNULL;
1613                 return -1;
1614         }
1615          
1616         EndBuffer = Source->buf + Source->BufUsed;
1617
1618         if (dest != NULL) 
1619         {
1620                 dest->buf[0] = '\0';
1621                 dest->BufUsed = 0;
1622         }
1623         else
1624         {
1625                 *pStart = EndBuffer + 1;
1626                 return -1;
1627         }
1628
1629         if (*pStart == NULL)
1630         {
1631                 *pStart = Source->buf; /* we're starting to examine this buffer. */
1632         }
1633         else if ((*pStart < Source->buf) || 
1634                  (*pStart > EndBuffer  )   ) 
1635         {
1636                 return -1; /* no more tokens to find. */
1637         }
1638
1639         s = *pStart;
1640         /* start to find the next token */
1641         while ((s <= EndBuffer)      && 
1642                (current_token == 0) ) 
1643         {
1644                 if (*s == separator) 
1645                 {
1646                         /* we found the next token */
1647                         ++current_token;
1648                 }
1649
1650                 if (len >= dest->BufSize) 
1651                 {
1652                         /* our Dest-buffer isn't big enough, increase it. */
1653                         dest->BufUsed = len;
1654
1655                         if (IncreaseBuf(dest, 1, -1) < 0) {
1656                                 /* WHUT? no more mem? bail out. */
1657                                 s = EndBuffer;
1658                                 dest->BufUsed --;
1659                                 break;
1660                         }
1661                 }
1662
1663                 if ( (current_token == 0 ) &&   /* are we in our target token? */
1664                      (!IsEmptyStr(s)     ) &&
1665                      (separator     != *s)    ) /* don't copy the token itself */
1666                 {
1667                         dest->buf[len] = *s;    /* Copy the payload */
1668                         ++len;                  /* remember the bigger size. */
1669                 }
1670
1671                 ++s;
1672         }
1673
1674         /* did we reach the end? */
1675         if ((s > EndBuffer)) {
1676                 EndBuffer = StrBufNOTNULL;
1677                 *pStart = EndBuffer;
1678         }
1679         else {
1680                 *pStart = s;  /* remember the position for the next run */
1681         }
1682
1683         /* sanitize our extracted token */
1684         dest->buf[len] = '\0';
1685         dest->BufUsed  = len;
1686
1687         return (len);
1688 }
1689
1690
1691 /**
1692  * @ingroup StrBuf
1693  * @brief a string tokenizer
1694  * @param dest Destination StringBuffer
1695  * @param Source StringBuffer to read from
1696  * @param pStart pointer to the end of the last token. Feed with NULL.
1697  * @param separator tokenizer @param
1698  * @returns -1 if not found, else length of token.
1699  */
1700 int StrBufSkip_NTokenS(const StrBuf *Source, const char **pStart, char separator, int nTokens)
1701 {
1702         const char *s, *EndBuffer;      //* source * /
1703         int len = 0;                    //* running total length of extracted string * /
1704         int current_token = 0;          //* token currently being processed * /
1705
1706         if ((Source == NULL) || 
1707             (Source->BufUsed ==0)) {
1708                 return(-1);
1709         }
1710         if (nTokens == 0)
1711                 return Source->BufUsed;
1712
1713         if (*pStart == NULL)
1714                 *pStart = Source->buf;
1715
1716         EndBuffer = Source->buf + Source->BufUsed;
1717
1718         if ((*pStart < Source->buf) || 
1719             (*pStart >  EndBuffer)) {
1720                 return (-1);
1721         }
1722
1723
1724         s = *pStart;
1725
1726         //cit_backtrace();
1727         //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
1728
1729         while ((s<EndBuffer) && !IsEmptyStr(s)) {
1730                 if (*s == separator) {
1731                         ++current_token;
1732                 }
1733                 if (current_token >= nTokens) {
1734                         break;
1735                 }
1736                 ++s;
1737         }
1738         *pStart = s;
1739         (*pStart) ++;
1740
1741         return(len);
1742 }
1743
1744 /**
1745  * @ingroup StrBuf
1746  * @brief a string tokenizer to fetch an integer
1747  * @param Source StringBuffer to read from
1748  * @param pStart Cursor on the tokenstring
1749  * @param separator tokenizer character
1750  * @returns 0 if not found, else integer representation of the token
1751  */
1752 int StrBufExtractNext_int(const StrBuf* Source, const char **pStart, char separator)
1753 {
1754         StrBuf tmp;
1755         char buf[64];
1756         
1757         tmp.buf = buf;
1758         buf[0] = '\0';
1759         tmp.BufSize = 64;
1760         tmp.BufUsed = 0;
1761         tmp.ConstBuf = 1;
1762         if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0)
1763                 return(atoi(buf));
1764         else
1765                 return 0;
1766 }
1767
1768 /**
1769  * @ingroup StrBuf
1770  * @brief a string tokenizer to fetch a long integer
1771  * @param Source StringBuffer to read from
1772  * @param pStart Cursor on the tokenstring
1773  * @param separator tokenizer character
1774  * @returns 0 if not found, else long integer representation of the token
1775  */
1776 long StrBufExtractNext_long(const StrBuf* Source, const char **pStart, char separator)
1777 {
1778         StrBuf tmp;
1779         char buf[64];
1780         
1781         tmp.buf = buf;
1782         buf[0] = '\0';
1783         tmp.BufSize = 64;
1784         tmp.BufUsed = 0;
1785         tmp.ConstBuf = 1;
1786         if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0)
1787                 return(atoi(buf));
1788         else
1789                 return 0;
1790 }
1791
1792
1793 /**
1794  * @ingroup StrBuf
1795  * @brief a string tokenizer to fetch an unsigned long
1796  * @param Source StringBuffer to read from
1797  * @param pStart Cursor on the tokenstring
1798  * @param separator tokenizer character
1799  * @returns 0 if not found, else unsigned long representation of the token
1800  */
1801 unsigned long StrBufExtractNext_unsigned_long(const StrBuf* Source, const char **pStart, char separator)
1802 {
1803         StrBuf tmp;
1804         char buf[64];
1805         char *pnum;
1806         
1807         tmp.buf = buf;
1808         buf[0] = '\0';
1809         tmp.BufSize = 64;
1810         tmp.BufUsed = 0;
1811         tmp.ConstBuf = 1;
1812         if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0) {
1813                 pnum = &buf[0];
1814                 if (*pnum == '-')
1815                         pnum ++;
1816                 return (unsigned long) atol(pnum);
1817         }
1818         else 
1819                 return 0;
1820 }
1821
1822
1823
1824 /**
1825  * @ingroup StrBuf
1826  * @brief Read a line from socket
1827  * flushes and closes the FD on error
1828  * @param buf the buffer to get the input to
1829  * @param fd pointer to the filedescriptor to read
1830  * @param append Append to an existing string or replace?
1831  * @param Error strerror() on error 
1832  * @returns numbers of chars read
1833  */
1834 int StrBufTCP_read_line(StrBuf *buf, int *fd, int append, const char **Error)
1835 {
1836         int len, rlen, slen;
1837
1838         if (!append)
1839                 FlushStrBuf(buf);
1840
1841         slen = len = buf->BufUsed;
1842         while (1) {
1843                 rlen = read(*fd, &buf->buf[len], 1);
1844                 if (rlen < 1) {
1845                         *Error = strerror(errno);
1846                         
1847                         close(*fd);
1848                         *fd = -1;
1849                         
1850                         return -1;
1851                 }
1852                 if (buf->buf[len] == '\n')
1853                         break;
1854                 if (buf->buf[len] != '\r')
1855                         len ++;
1856                 if (len + 2 >= buf->BufSize) {
1857                         buf->BufUsed = len;
1858                         buf->buf[len+1] = '\0';
1859                         IncreaseBuf(buf, 1, -1);
1860                 }
1861         }
1862         buf->BufUsed = len;
1863         buf->buf[len] = '\0';
1864         return len - slen;
1865 }
1866
1867 /**
1868  * @ingroup StrBuf
1869  * @brief Read a line from socket
1870  * flushes and closes the FD on error
1871  * @param buf the buffer to get the input to
1872  * @param fd pointer to the filedescriptor to read
1873  * @param append Append to an existing string or replace?
1874  * @param Error strerror() on error 
1875  * @returns numbers of chars read
1876  */
1877 int StrBufTCP_read_buffered_line(StrBuf *Line, 
1878                                  StrBuf *buf, 
1879                                  int *fd, 
1880                                  int timeout, 
1881                                  int selectresolution, 
1882                                  const char **Error)
1883 {
1884         int len, rlen;
1885         int nSuccessLess = 0;
1886         fd_set rfds;
1887         char *pch = NULL;
1888         int fdflags;
1889         int IsNonBlock;
1890         struct timeval tv;
1891
1892         if (buf->BufUsed > 0) {
1893                 pch = strchr(buf->buf, '\n');
1894                 if (pch != NULL) {
1895                         rlen = 0;
1896                         len = pch - buf->buf;
1897                         if (len > 0 && (*(pch - 1) == '\r') )
1898                                 rlen ++;
1899                         StrBufSub(Line, buf, 0, len - rlen);
1900                         StrBufCutLeft(buf, len + 1);
1901                         return len - rlen;
1902                 }
1903         }
1904         
1905         if (buf->BufSize - buf->BufUsed < 10)
1906                 IncreaseBuf(buf, 1, -1);
1907
1908         fdflags = fcntl(*fd, F_GETFL);
1909         IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
1910
1911         while ((nSuccessLess < timeout) && (pch == NULL)) {
1912                 if (IsNonBlock){
1913                         tv.tv_sec = selectresolution;
1914                         tv.tv_usec = 0;
1915                         
1916                         FD_ZERO(&rfds);
1917                         FD_SET(*fd, &rfds);
1918                         if (select(*fd + 1, NULL, &rfds, NULL, &tv) == -1) {
1919                                 *Error = strerror(errno);
1920                                 close (*fd);
1921                                 *fd = -1;
1922                                 return -1;
1923                         }
1924                 }
1925                 if (IsNonBlock && !  FD_ISSET(*fd, &rfds)) {
1926                         nSuccessLess ++;
1927                         continue;
1928                 }
1929                 rlen = read(*fd, 
1930                             &buf->buf[buf->BufUsed], 
1931                             buf->BufSize - buf->BufUsed - 1);
1932                 if (rlen < 1) {
1933                         *Error = strerror(errno);
1934                         close(*fd);
1935                         *fd = -1;
1936                         return -1;
1937                 }
1938                 else if (rlen > 0) {
1939                         nSuccessLess = 0;
1940                         buf->BufUsed += rlen;
1941                         buf->buf[buf->BufUsed] = '\0';
1942                         if (buf->BufUsed + 10 > buf->BufSize) {
1943                                 IncreaseBuf(buf, 1, -1);
1944                         }
1945                         pch = strchr(buf->buf, '\n');
1946                         continue;
1947                 }
1948                 
1949         }
1950         if (pch != NULL) {
1951                 rlen = 0;
1952                 len = pch - buf->buf;
1953                 if (len > 0 && (*(pch - 1) == '\r') )
1954                         rlen ++;
1955                 StrBufSub(Line, buf, 0, len - rlen);
1956                 StrBufCutLeft(buf, len + 1);
1957                 return len - rlen;
1958         }
1959         return -1;
1960
1961 }
1962
1963 static const char *ErrRBLF_SelectFailed="StrBufTCP_read_buffered_line_fast: Select failed without reason";
1964 static const char *ErrRBLF_NotEnoughSentFromServer="StrBufTCP_read_buffered_line_fast: No complete line was sent from peer";
1965 /**
1966  * @ingroup StrBuf
1967  * @brief Read a line from socket
1968  * flushes and closes the FD on error
1969  * @param buf the buffer to get the input to
1970  * @param Pos pointer to the current read position, should be NULL initialized!
1971  * @param fd pointer to the filedescriptor to read
1972  * @param append Append to an existing string or replace?
1973  * @param Error strerror() on error 
1974  * @returns numbers of chars read
1975  */
1976 int StrBufTCP_read_buffered_line_fast(StrBuf *Line, 
1977                                       StrBuf *IOBuf, 
1978                                       const char **Pos,
1979                                       int *fd, 
1980                                       int timeout, 
1981                                       int selectresolution, 
1982                                       const char **Error)
1983 {
1984         const char *pche = NULL;
1985         const char *pos = NULL;
1986         int len, rlen;
1987         int nSuccessLess = 0;
1988         fd_set rfds;
1989         const char *pch = NULL;
1990         int fdflags;
1991         int IsNonBlock;
1992         struct timeval tv;
1993         
1994         pos = *Pos;
1995         if ((IOBuf->BufUsed > 0) && 
1996             (pos != NULL) && 
1997             (pos < IOBuf->buf + IOBuf->BufUsed)) 
1998         {
1999                 pche = IOBuf->buf + IOBuf->BufUsed;
2000                 pch = pos;
2001                 while ((pch < pche) && (*pch != '\n'))
2002                         pch ++;
2003                 if ((pch >= pche) || (*pch == '\0'))
2004                         pch = NULL;
2005                 if ((pch != NULL) && 
2006                     (pch <= pche)) 
2007                 {
2008                         rlen = 0;
2009                         len = pch - pos;
2010                         if (len > 0 && (*(pch - 1) == '\r') )
2011                                 rlen ++;
2012                         StrBufSub(Line, IOBuf, (pos - IOBuf->buf), len - rlen);
2013                         *Pos = pch + 1;
2014                         return len - rlen;
2015                 }
2016         }
2017         
2018         if (pos != NULL) {
2019                 if (pos > pche)
2020                         FlushStrBuf(IOBuf);
2021                 else 
2022                         StrBufCutLeft(IOBuf, (pos - IOBuf->buf));
2023                 *Pos = NULL;
2024         }
2025         
2026         if (IOBuf->BufSize - IOBuf->BufUsed < 10) {
2027                 IncreaseBuf(IOBuf, 1, -1);
2028         }
2029
2030         fdflags = fcntl(*fd, F_GETFL);
2031         IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
2032
2033         pch = NULL;
2034         while ((nSuccessLess < timeout) && 
2035                (pch == NULL) &&
2036                (*fd != -1)) {
2037                 if (IsNonBlock)
2038                 {
2039                         tv.tv_sec = 1;
2040                         tv.tv_usec = 0;
2041                 
2042                         FD_ZERO(&rfds);
2043                         FD_SET(*fd, &rfds);
2044                         if (select((*fd) + 1, &rfds, NULL, NULL, &tv) == -1) {
2045                                 *Error = strerror(errno);
2046                                 close (*fd);
2047                                 *fd = -1;
2048                                 if (*Error == NULL)
2049                                         *Error = ErrRBLF_SelectFailed;
2050                                 return -1;
2051                         }
2052                         if (! FD_ISSET(*fd, &rfds) != 0) {
2053                                 nSuccessLess ++;
2054                                 continue;
2055                         }
2056                 }
2057                 rlen = read(*fd, 
2058                             &IOBuf->buf[IOBuf->BufUsed], 
2059                             IOBuf->BufSize - IOBuf->BufUsed - 1);
2060                 if (rlen < 1) {
2061                         *Error = strerror(errno);
2062                         close(*fd);
2063                         *fd = -1;
2064                         return -1;
2065                 }
2066                 else if (rlen > 0) {
2067                         nSuccessLess = 0;
2068                         IOBuf->BufUsed += rlen;
2069                         IOBuf->buf[IOBuf->BufUsed] = '\0';
2070                         if (IOBuf->BufUsed + 10 > IOBuf->BufSize) {
2071                                 IncreaseBuf(IOBuf, 1, -1);
2072                         }
2073                         
2074                         pche = IOBuf->buf + IOBuf->BufUsed;
2075                         pch = IOBuf->buf;
2076                         while ((pch < pche) && (*pch != '\n'))
2077                                 pch ++;
2078                         if ((pch >= pche) || (*pch == '\0'))
2079                                 pch = NULL;
2080                         continue;
2081                 }
2082         }
2083         if (pch != NULL) {
2084                 pos = IOBuf->buf;
2085                 rlen = 0;
2086                 len = pch - pos;
2087                 if (len > 0 && (*(pch - 1) == '\r') )
2088                         rlen ++;
2089                 StrBufSub(Line, IOBuf, 0, len - rlen);
2090                 *Pos = pos + len + 1;
2091                 return len - rlen;
2092         }
2093         *Error = ErrRBLF_NotEnoughSentFromServer;
2094         return -1;
2095
2096 }
2097
2098 /**
2099  * @brief Input binary data from socket
2100  * flushes and closes the FD on error
2101  * @param buf the buffer to get the input to
2102  * @param fd pointer to the filedescriptor to read
2103  * @param append Append to an existing string or replace?
2104  * @param nBytes the maximal number of bytes to read
2105  * @param Error strerror() on error 
2106  * @returns numbers of chars read
2107  */
2108 int StrBufReadBLOB(StrBuf *Buf, int *fd, int append, long nBytes, const char **Error)
2109 {
2110         int fdflags;
2111         int len, rlen, slen;
2112         int nSuccessLess;
2113         int nRead = 0;
2114         char *ptr;
2115         int IsNonBlock;
2116         struct timeval tv;
2117         fd_set rfds;
2118         if ((Buf == NULL) || (*fd == -1))
2119                 return -1;
2120         if (!append)
2121                 FlushStrBuf(Buf);
2122         if (Buf->BufUsed + nBytes >= Buf->BufSize)
2123                 IncreaseBuf(Buf, 1, Buf->BufUsed + nBytes);
2124
2125         ptr = Buf->buf + Buf->BufUsed;
2126
2127         slen = len = Buf->BufUsed;
2128
2129         fdflags = fcntl(*fd, F_GETFL);
2130         IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
2131         nSuccessLess = 0;
2132         while ((nRead < nBytes) && 
2133                (*fd != -1)) 
2134         {
2135                 if (IsNonBlock)
2136                 {
2137                         tv.tv_sec = 1;
2138                         tv.tv_usec = 0;
2139                 
2140                         FD_ZERO(&rfds);
2141                         FD_SET(*fd, &rfds);
2142                         if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
2143                                 *Error = strerror(errno);
2144                                 close (*fd);
2145                                 *fd = -1;
2146                                 if (*Error == NULL)
2147                                         *Error = ErrRBLF_SelectFailed;
2148                                 return -1;
2149                         }
2150                         if (! FD_ISSET(*fd, &rfds) != 0) {
2151                                 nSuccessLess ++;
2152                                 continue;
2153                         }
2154                 }
2155
2156                 if ((rlen = read(*fd, 
2157                                  ptr,
2158                                  nBytes - nRead)) == -1) {
2159                         close(*fd);
2160                         *fd = -1;
2161                         *Error = strerror(errno);
2162                         return rlen;
2163                 }
2164                 nRead += rlen;
2165                 ptr += rlen;
2166                 Buf->BufUsed += rlen;
2167         }
2168         Buf->buf[Buf->BufUsed] = '\0';
2169         return nRead;
2170 }
2171
2172 const char *ErrRBB_too_many_selects = "StrBufReadBLOBBuffered: to many selects; aborting.";
2173 /**
2174  * @brief Input binary data from socket
2175  * flushes and closes the FD on error
2176  * @param buf the buffer to get the input to
2177  * @param fd pointer to the filedescriptor to read
2178  * @param append Append to an existing string or replace?
2179  * @param nBytes the maximal number of bytes to read
2180  * @param Error strerror() on error 
2181  * @returns numbers of chars read
2182  */
2183 int StrBufReadBLOBBuffered(StrBuf *Blob, 
2184                            StrBuf *IOBuf, 
2185                            const char **Pos,
2186                            int *fd, 
2187                            int append, 
2188                            long nBytes, 
2189                            int check, 
2190                            const char **Error)
2191 {
2192         const char *pche;
2193         const char *pos;
2194         int nSelects = 0;
2195         int SelRes;
2196         int fdflags;
2197         int len = 0;
2198         int rlen, slen;
2199         int nRead = 0;
2200         int nAlreadyRead = 0;
2201         int IsNonBlock;
2202         char *ptr;
2203         fd_set rfds;
2204         const char *pch;
2205         struct timeval tv;
2206         int nSuccessLess;
2207
2208         if ((Blob == NULL) || (*fd == -1) || (IOBuf == NULL) || (Pos == NULL))
2209                 return -1;
2210         if (!append)
2211                 FlushStrBuf(Blob);
2212         if (Blob->BufUsed + nBytes >= Blob->BufSize) 
2213                 IncreaseBuf(Blob, append, Blob->BufUsed + nBytes);
2214         
2215         pos = *Pos;
2216
2217         if (pos > 0)
2218                 len = pos - IOBuf->buf;
2219         rlen = IOBuf->BufUsed - len;
2220
2221
2222         if ((IOBuf->BufUsed > 0) && 
2223             (pos != NULL) && 
2224             (pos < IOBuf->buf + IOBuf->BufUsed)) 
2225         {
2226                 pche = IOBuf->buf + IOBuf->BufUsed;
2227                 pch = pos;
2228
2229                 if (rlen < nBytes) {
2230                         memcpy(Blob->buf + Blob->BufUsed, pos, rlen);
2231                         Blob->BufUsed += rlen;
2232                         Blob->buf[Blob->BufUsed] = '\0';
2233                         nAlreadyRead = nRead = rlen;
2234                         *Pos = NULL; 
2235                 }
2236                 if (rlen >= nBytes) {
2237                         memcpy(Blob->buf + Blob->BufUsed, pos, nBytes);
2238                         Blob->BufUsed += nBytes;
2239                         Blob->buf[Blob->BufUsed] = '\0';
2240                         if (rlen == nBytes) {
2241                                 *Pos = NULL; 
2242                                 FlushStrBuf(IOBuf);
2243                         }
2244                         else 
2245                                 *Pos += nBytes;
2246                         return nBytes;
2247                 }
2248         }
2249
2250         FlushStrBuf(IOBuf);
2251         if (IOBuf->BufSize < nBytes - nRead)
2252                 IncreaseBuf(IOBuf, 0, nBytes - nRead);
2253         ptr = IOBuf->buf;
2254
2255         slen = len = Blob->BufUsed;
2256
2257         fdflags = fcntl(*fd, F_GETFL);
2258         IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
2259
2260         SelRes = 1;
2261         nBytes -= nRead;
2262         nRead = 0;
2263         while ((nRead < nBytes) &&
2264                (*fd != -1)) {
2265                 if (IsNonBlock)
2266                 {
2267                         tv.tv_sec = 1;
2268                         tv.tv_usec = 0;
2269                 
2270                         FD_ZERO(&rfds);
2271                         FD_SET(*fd, &rfds);
2272                         if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
2273                                 *Error = strerror(errno);
2274                                 close (*fd);
2275                                 *fd = -1;
2276                                 if (*Error == NULL)
2277                                         *Error = ErrRBLF_SelectFailed;
2278                                 return -1;
2279                         }
2280                         if (! FD_ISSET(*fd, &rfds) != 0) {
2281                                 nSuccessLess ++;
2282                                 continue;
2283                         }
2284                 }
2285                 nSuccessLess = 0;
2286                 rlen = read(*fd, 
2287                             ptr,
2288                             nBytes - nRead);
2289                 if (rlen == -1) {
2290                         close(*fd);
2291                         *fd = -1;
2292                         *Error = strerror(errno);
2293                         return rlen;
2294                 }
2295                 else if (rlen == 0){
2296                         nSuccessLess ++;
2297                         if ((check == NNN_TERM) && 
2298                             (nRead > 5) &&
2299                             (strncmp(IOBuf->buf + IOBuf->BufUsed - 5, "\n000\n", 5) == 0)) 
2300                         {
2301                                 StrBufPlain(Blob, HKEY("\n000\n"));
2302                                 StrBufCutRight(Blob, 5);
2303                                 return Blob->BufUsed;
2304                         }
2305                         if (nSelects > 10) {
2306                                 FlushStrBuf(IOBuf);
2307                                 *Error = ErrRBB_too_many_selects;
2308                                 return -1;
2309                         }
2310                 }
2311                 else if (rlen > 0) {
2312                         nRead += rlen;
2313                         ptr += rlen;
2314                         IOBuf->BufUsed += rlen;
2315                 }
2316         }
2317         if (nRead > nBytes) {
2318                 *Pos = IOBuf->buf + nBytes;
2319         }
2320         Blob->buf[Blob->BufUsed] = '\0';
2321         StrBufAppendBufPlain(Blob, IOBuf->buf, nBytes, 0);
2322         if (*Pos == NULL) {
2323                 FlushStrBuf(IOBuf);
2324         }
2325         return nRead + nAlreadyRead;
2326 }
2327
2328 /**
2329  * @brief Cut nChars from the start of the string
2330  * @param Buf Buffer to modify
2331  * @param nChars how many chars should be skipped?
2332  */
2333 void StrBufCutLeft(StrBuf *Buf, int nChars)
2334 {
2335         if (nChars >= Buf->BufUsed) {
2336                 FlushStrBuf(Buf);
2337                 return;
2338         }
2339         memmove(Buf->buf, Buf->buf + nChars, Buf->BufUsed - nChars);
2340         Buf->BufUsed -= nChars;
2341         Buf->buf[Buf->BufUsed] = '\0';
2342 }
2343
2344 /**
2345  * @brief Cut the trailing n Chars from the string
2346  * @param Buf Buffer to modify
2347  * @param nChars how many chars should be trunkated?
2348  */
2349 void StrBufCutRight(StrBuf *Buf, int nChars)
2350 {
2351         if (nChars >= Buf->BufUsed) {
2352                 FlushStrBuf(Buf);
2353                 return;
2354         }
2355         Buf->BufUsed -= nChars;
2356         Buf->buf[Buf->BufUsed] = '\0';
2357 }
2358
2359 /**
2360  * @brief Cut the string after n Chars
2361  * @param Buf Buffer to modify
2362  * @param AfternChars after how many chars should we trunkate the string?
2363  * @param At if non-null and points inside of our string, cut it there.
2364  */
2365 void StrBufCutAt(StrBuf *Buf, int AfternChars, const char *At)
2366 {
2367         if (At != NULL){
2368                 AfternChars = At - Buf->buf;
2369         }
2370
2371         if ((AfternChars < 0) || (AfternChars >= Buf->BufUsed))
2372                 return;
2373         Buf->BufUsed = AfternChars;
2374         Buf->buf[Buf->BufUsed] = '\0';
2375 }
2376
2377
2378 /**
2379  * @brief Strip leading and trailing spaces from a string; with premeasured and adjusted length.
2380  * @param buf the string to modify
2381  * @param len length of the string. 
2382  */
2383 void StrBufTrim(StrBuf *Buf)
2384 {
2385         int delta = 0;
2386         if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
2387
2388         while ((Buf->BufUsed > delta) && (isspace(Buf->buf[delta]))){
2389                 delta ++;
2390         }
2391         if (delta > 0) StrBufCutLeft(Buf, delta);
2392
2393         if (Buf->BufUsed == 0) return;
2394         while (isspace(Buf->buf[Buf->BufUsed - 1])){
2395                 Buf->BufUsed --;
2396         }
2397         Buf->buf[Buf->BufUsed] = '\0';
2398 }
2399
2400 /**
2401  * @brief uppercase the contents of a buffer
2402  * @param Buf the buffer to translate
2403  */
2404 void StrBufUpCase(StrBuf *Buf) 
2405 {
2406         char *pch, *pche;
2407
2408         pch = Buf->buf;
2409         pche = pch + Buf->BufUsed;
2410         while (pch < pche) {
2411                 *pch = toupper(*pch);
2412                 pch ++;
2413         }
2414 }
2415
2416
2417 /**
2418  * @brief lowercase the contents of a buffer
2419  * @param Buf the buffer to translate
2420  */
2421 void StrBufLowerCase(StrBuf *Buf) 
2422 {
2423         char *pch, *pche;
2424
2425         pch = Buf->buf;
2426         pche = pch + Buf->BufUsed;
2427         while (pch < pche) {
2428                 *pch = tolower(*pch);
2429                 pch ++;
2430         }
2431 }
2432
2433 /**
2434  * @brief removes double slashes from pathnames
2435  * @param Dir directory string to filter
2436  * @param RemoveTrailingSlash allows / disallows trailing slashes
2437  */
2438 void StrBufStripSlashes(StrBuf *Dir, int RemoveTrailingSlash)
2439 {
2440         char *a, *b;
2441
2442         a = b = Dir->buf;
2443
2444         while (!IsEmptyStr(a)) {
2445                 if (*a == '/') {
2446                         while (*a == '/')
2447                                 a++;
2448                         *b = '/';
2449                         b++;
2450                 }
2451                 else {
2452                         *b = *a;
2453                         b++; a++;
2454                 }
2455         }
2456         if ((RemoveTrailingSlash) && (*(b - 1) != '/')){
2457                 *b = '/';
2458                 b++;
2459         }
2460         *b = '\0';
2461         Dir->BufUsed = b - Dir->buf;
2462 }
2463
2464 /**
2465  * @brief unhide special chars hidden to the HTML escaper
2466  * @param target buffer to put the unescaped string in
2467  * @param source buffer to unescape
2468  */
2469 void StrBufEUid_unescapize(StrBuf *target, const StrBuf *source) 
2470 {
2471         int a, b, len;
2472         char hex[3];
2473
2474         if (target != NULL)
2475                 FlushStrBuf(target);
2476
2477         if (source == NULL ||target == NULL)
2478         {
2479                 return;
2480         }
2481
2482         len = source->BufUsed;
2483         for (a = 0; a < len; ++a) {
2484                 if (target->BufUsed >= target->BufSize)
2485                         IncreaseBuf(target, 1, -1);
2486
2487                 if (source->buf[a] == '=') {
2488                         hex[0] = source->buf[a + 1];
2489                         hex[1] = source->buf[a + 2];
2490                         hex[2] = 0;
2491                         b = 0;
2492                         sscanf(hex, "%02x", &b);
2493                         target->buf[target->BufUsed] = b;
2494                         target->buf[++target->BufUsed] = 0;
2495                         a += 2;
2496                 }
2497                 else {
2498                         target->buf[target->BufUsed] = source->buf[a];
2499                         target->buf[++target->BufUsed] = 0;
2500                 }
2501         }
2502 }
2503
2504
2505 /**
2506  * @brief hide special chars from the HTML escapers and friends
2507  * @param target buffer to put the escaped string in
2508  * @param source buffer to escape
2509  */
2510 void StrBufEUid_escapize(StrBuf *target, const StrBuf *source) 
2511 {
2512         int i, len;
2513
2514         if (target != NULL)
2515                 FlushStrBuf(target);
2516
2517         if (source == NULL ||target == NULL)
2518         {
2519                 return;
2520         }
2521
2522         len = source->BufUsed;
2523         for (i=0; i<len; ++i) {
2524                 if (target->BufUsed + 4 >= target->BufSize)
2525                         IncreaseBuf(target, 1, -1);
2526                 if ( (isalnum(source->buf[i])) || 
2527                      (source->buf[i]=='-') || 
2528                      (source->buf[i]=='_') ) {
2529                         target->buf[target->BufUsed++] = source->buf[i];
2530                 }
2531                 else {
2532                         sprintf(&target->buf[target->BufUsed], 
2533                                 "=%02X", 
2534                                 (0xFF &source->buf[i]));
2535                         target->BufUsed += 3;
2536                 }
2537         }
2538         target->buf[target->BufUsed + 1] = '\0';
2539 }
2540
2541 /**
2542  * @brief uses the same calling syntax as compress2(), but it
2543  *   creates a stream compatible with HTTP "Content-encoding: gzip"
2544  */
2545 #ifdef HAVE_ZLIB
2546 #define DEF_MEM_LEVEL 8 /*< memlevel??? */
2547 #define OS_CODE 0x03    /*< unix */
2548 int ZEXPORT compress_gzip(Bytef * dest,         /*< compressed buffer*/
2549                           size_t * destLen,     /*< length of the compresed data */
2550                           const Bytef * source, /*< source to encode */
2551                           uLong sourceLen,      /*< length of source to encode */
2552                           int level)            /*< compression level */
2553 {
2554         const int gz_magic[2] = { 0x1f, 0x8b }; /* gzip magic header */
2555
2556         /* write gzip header */
2557         snprintf((char *) dest, *destLen, 
2558                  "%c%c%c%c%c%c%c%c%c%c",
2559                  gz_magic[0], gz_magic[1], Z_DEFLATED,
2560                  0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /* xflags */ ,
2561                  OS_CODE);
2562
2563         /* normal deflate */
2564         z_stream stream;
2565         int err;
2566         stream.next_in = (Bytef *) source;
2567         stream.avail_in = (uInt) sourceLen;
2568         stream.next_out = dest + 10L;   // after header
2569         stream.avail_out = (uInt) * destLen;
2570         if ((uLong) stream.avail_out != *destLen)
2571                 return Z_BUF_ERROR;
2572
2573         stream.zalloc = (alloc_func) 0;
2574         stream.zfree = (free_func) 0;
2575         stream.opaque = (voidpf) 0;
2576
2577         err = deflateInit2(&stream, level, Z_DEFLATED, -MAX_WBITS,
2578                            DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
2579         if (err != Z_OK)
2580                 return err;
2581
2582         err = deflate(&stream, Z_FINISH);
2583         if (err != Z_STREAM_END) {
2584                 deflateEnd(&stream);
2585                 return err == Z_OK ? Z_BUF_ERROR : err;
2586         }
2587         *destLen = stream.total_out + 10L;
2588
2589         /* write CRC and Length */
2590         uLong crc = crc32(0L, source, sourceLen);
2591         int n;
2592         for (n = 0; n < 4; ++n, ++*destLen) {
2593                 dest[*destLen] = (int) (crc & 0xff);
2594                 crc >>= 8;
2595         }
2596         uLong len = stream.total_in;
2597         for (n = 0; n < 4; ++n, ++*destLen) {
2598                 dest[*destLen] = (int) (len & 0xff);
2599                 len >>= 8;
2600         }
2601         err = deflateEnd(&stream);
2602         return err;
2603 }
2604 #endif
2605
2606
2607 /**
2608  * Attention! If you feed this a Const String, you must maintain the uncompressed buffer yourself!
2609  */
2610 int CompressBuffer(StrBuf *Buf)
2611 {
2612 #ifdef HAVE_ZLIB
2613         char *compressed_data = NULL;
2614         size_t compressed_len, bufsize;
2615         int i = 0;
2616
2617         bufsize = compressed_len = Buf->BufUsed +  (Buf->BufUsed / 100) + 100;
2618         compressed_data = malloc(compressed_len);
2619         
2620         if (compressed_data == NULL)
2621                 return -1;
2622         /* Flush some space after the used payload so valgrind shuts up... */
2623         while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
2624                 Buf->buf[Buf->BufUsed + i++] = '\0';
2625         if (compress_gzip((Bytef *) compressed_data,
2626                           &compressed_len,
2627                           (Bytef *) Buf->buf,
2628                           (uLongf) Buf->BufUsed, Z_BEST_SPEED) == Z_OK) {
2629                 if (!Buf->ConstBuf)
2630                         free(Buf->buf);
2631                 Buf->buf = compressed_data;
2632                 Buf->BufUsed = compressed_len;
2633                 Buf->BufSize = bufsize;
2634                 /* Flush some space after the used payload so valgrind shuts up... */
2635                 i = 0;
2636                 while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
2637                         Buf->buf[Buf->BufUsed + i++] = '\0';
2638                 return 1;
2639         } else {
2640                 free(compressed_data);
2641         }
2642 #endif  /* HAVE_ZLIB */
2643         return 0;
2644 }
2645
2646 /**
2647  * @brief decode a buffer from base 64 encoding; destroys original
2648  * @param Buf Buffor to transform
2649  */
2650 int StrBufDecodeBase64(StrBuf *Buf)
2651 {
2652         char *xferbuf;
2653         size_t siz;
2654         if (Buf == NULL) return -1;
2655
2656         xferbuf = (char*) malloc(Buf->BufSize);
2657         siz = CtdlDecodeBase64(xferbuf,
2658                                Buf->buf,
2659                                Buf->BufUsed);
2660         free(Buf->buf);
2661         Buf->buf = xferbuf;
2662         Buf->BufUsed = siz;
2663         return siz;
2664 }
2665
2666 /**
2667  * @brief decode a buffer from base 64 encoding; destroys original
2668  * @param Buf Buffor to transform
2669  */
2670 int StrBufDecodeHex(StrBuf *Buf)
2671 {
2672         unsigned int ch;
2673         char *pch, *pche, *pchi;
2674
2675         if (Buf == NULL) return -1;
2676
2677         pch = pchi = Buf->buf;
2678         pche = pch + Buf->BufUsed;
2679
2680         while (pchi < pche){
2681                 ch = decode_hex(pchi);
2682                 *pch = ch;
2683                 pch ++;
2684                 pchi += 2;
2685         }
2686
2687         *pch = '\0';
2688         Buf->BufUsed = pch - Buf->buf;
2689         return Buf->BufUsed;
2690 }
2691
2692 /**
2693  * @brief replace all chars >0x20 && < 0x7F with Mute
2694  * @param Mute char to put over invalid chars
2695  * @param Buf Buffor to transform
2696  */
2697 int StrBufSanitizeAscii(StrBuf *Buf, const char Mute)
2698 {
2699         unsigned char *pch;
2700
2701         if (Buf == NULL) return -1;
2702         pch = (unsigned char *)Buf->buf;
2703         while (pch < (unsigned char *)Buf->buf + Buf->BufUsed) {
2704                 if ((*pch < 0x20) || (*pch > 0x7F))
2705                         *pch = Mute;
2706                 pch ++;
2707         }
2708         return Buf->BufUsed;
2709 }
2710
2711
2712 /**
2713  * @brief remove escaped strings from i.e. the url string (like %20 for blanks)
2714  * @param Buf Buffer to translate
2715  * @param StripBlanks Reduce several blanks to one?
2716  */
2717 long StrBufUnescape(StrBuf *Buf, int StripBlanks)
2718 {
2719         int a, b;
2720         char hex[3];
2721         long len;
2722
2723         while ((Buf->BufUsed > 0) && (isspace(Buf->buf[Buf->BufUsed - 1]))){
2724                 Buf->buf[Buf->BufUsed - 1] = '\0';
2725                 Buf->BufUsed --;
2726         }
2727
2728         a = 0; 
2729         while (a < Buf->BufUsed) {
2730                 if (Buf->buf[a] == '+')
2731                         Buf->buf[a] = ' ';
2732                 else if (Buf->buf[a] == '%') {
2733                         /* don't let % chars through, rather truncate the input. */
2734                         if (a + 2 > Buf->BufUsed) {
2735                                 Buf->buf[a] = '\0';
2736                                 Buf->BufUsed = a;
2737                         }
2738                         else {                  
2739                                 hex[0] = Buf->buf[a + 1];
2740                                 hex[1] = Buf->buf[a + 2];
2741                                 hex[2] = 0;
2742                                 b = 0;
2743                                 sscanf(hex, "%02x", &b);
2744                                 Buf->buf[a] = (char) b;
2745                                 len = Buf->BufUsed - a - 2;
2746                                 if (len > 0)
2747                                         memmove(&Buf->buf[a + 1], &Buf->buf[a + 3], len);
2748                         
2749                                 Buf->BufUsed -=2;
2750                         }
2751                 }
2752                 a++;
2753         }
2754         return a;
2755 }
2756
2757
2758 /**
2759  * @brief       RFC2047-encode a header field if necessary.
2760  *              If no non-ASCII characters are found, the string
2761  *              will be copied verbatim without encoding.
2762  *
2763  * @param       target          Target buffer.
2764  * @param       source          Source string to be encoded.
2765  * @returns     encoded length; -1 if non success.
2766  */
2767 int StrBufRFC2047encode(StrBuf **target, const StrBuf *source)
2768 {
2769         const char headerStr[] = "=?UTF-8?Q?";
2770         int need_to_encode = 0;
2771         int i = 0;
2772         unsigned char ch;
2773
2774         if ((source == NULL) || 
2775             (target == NULL))
2776             return -1;
2777
2778         while ((i < source->BufUsed) &&
2779                (!IsEmptyStr (&source->buf[i])) &&
2780                (need_to_encode == 0)) {
2781                 if (((unsigned char) source->buf[i] < 32) || 
2782                     ((unsigned char) source->buf[i] > 126)) {
2783                         need_to_encode = 1;
2784                 }
2785                 i++;
2786         }
2787
2788         if (!need_to_encode) {
2789                 if (*target == NULL) {
2790                         *target = NewStrBufPlain(source->buf, source->BufUsed);
2791                 }
2792                 else {
2793                         FlushStrBuf(*target);
2794                         StrBufAppendBuf(*target, source, 0);
2795                 }
2796                 return (*target)->BufUsed;
2797         }
2798         if (*target == NULL)
2799                 *target = NewStrBufPlain(NULL, sizeof(headerStr) + source->BufUsed * 2);
2800         else if (sizeof(headerStr) + source->BufUsed >= (*target)->BufSize)
2801                 IncreaseBuf(*target, sizeof(headerStr) + source->BufUsed, 0);
2802         memcpy ((*target)->buf, headerStr, sizeof(headerStr) - 1);
2803         (*target)->BufUsed = sizeof(headerStr) - 1;
2804         for (i=0; (i < source->BufUsed); ++i) {
2805                 if ((*target)->BufUsed + 4 >= (*target)->BufSize)
2806                         IncreaseBuf(*target, 1, 0);
2807                 ch = (unsigned char) source->buf[i];
2808                 if ((ch < 32) || (ch > 126) || (ch == 61)) {
2809                         sprintf(&(*target)->buf[(*target)->BufUsed], "=%02X", ch);
2810                         (*target)->BufUsed += 3;
2811                 }
2812                 else {
2813                         (*target)->buf[(*target)->BufUsed] = ch;
2814                         (*target)->BufUsed++;
2815                 }
2816         }
2817         
2818         if ((*target)->BufUsed + 4 >= (*target)->BufSize)
2819                 IncreaseBuf(*target, 1, 0);
2820
2821         (*target)->buf[(*target)->BufUsed++] = '?';
2822         (*target)->buf[(*target)->BufUsed++] = '=';
2823         (*target)->buf[(*target)->BufUsed] = '\0';
2824         return (*target)->BufUsed;;
2825 }
2826
2827 /**
2828  * @brief replaces all occurances of 'search' by 'replace'
2829  * @param buf Buffer to modify
2830  * @param search character to search
2831  * @param relpace character to replace search by
2832  */
2833 void StrBufReplaceChars(StrBuf *buf, char search, char replace)
2834 {
2835         long i;
2836         if (buf == NULL)
2837                 return;
2838         for (i=0; i<buf->BufUsed; i++)
2839                 if (buf->buf[i] == search)
2840                         buf->buf[i] = replace;
2841
2842 }
2843
2844
2845
2846 /**
2847  * @brief Wrapper around iconv_open()
2848  * Our version adds aliases for non-standard Microsoft charsets
2849  * such as 'MS950', aliasing them to names like 'CP950'
2850  *
2851  * @param tocode        Target encoding
2852  * @param fromcode      Source encoding
2853  */
2854 void  ctdl_iconv_open(const char *tocode, const char *fromcode, void *pic)
2855 {
2856 #ifdef HAVE_ICONV
2857         iconv_t ic = (iconv_t)(-1) ;
2858         ic = iconv_open(tocode, fromcode);
2859         if (ic == (iconv_t)(-1) ) {
2860                 char alias_fromcode[64];
2861                 if ( (strlen(fromcode) == 5) && (!strncasecmp(fromcode, "MS", 2)) ) {
2862                         safestrncpy(alias_fromcode, fromcode, sizeof alias_fromcode);
2863                         alias_fromcode[0] = 'C';
2864                         alias_fromcode[1] = 'P';
2865                         ic = iconv_open(tocode, alias_fromcode);
2866                 }
2867         }
2868         *(iconv_t *)pic = ic;
2869 #endif
2870 }
2871
2872
2873 /**
2874  * @brief find one chunk of a RFC822 encoded string
2875  * @param Buffer where to search
2876  * @param bptr where to start searching
2877  * @returns found position, NULL if none.
2878  */
2879 static inline char *FindNextEnd (const StrBuf *Buf, char *bptr)
2880 {
2881         char * end;
2882         /* Find the next ?Q? */
2883         if (Buf->BufUsed - (bptr - Buf->buf)  < 6)
2884                 return NULL;
2885
2886         end = strchr(bptr + 2, '?');
2887
2888         if (end == NULL)
2889                 return NULL;
2890
2891         if ((Buf->BufUsed - (end - Buf->buf) > 3) &&
2892             ((*(end + 1) == 'B') || (*(end + 1) == 'Q')) && 
2893             (*(end + 2) == '?')) {
2894                 /* skip on to the end of the cluster, the next ?= */
2895                 end = strstr(end + 3, "?=");
2896         }
2897         else
2898                 /* sort of half valid encoding, try to find an end. */
2899                 end = strstr(bptr, "?=");
2900         return end;
2901 }
2902
2903 /**
2904  * @brief swaps the contents of two StrBufs
2905  * this is to be used to have cheap switched between a work-buffer and a target buffer 
2906  * @param A First one
2907  * @param B second one
2908  */
2909 static inline void SwapBuffers(StrBuf *A, StrBuf *B)
2910 {
2911         StrBuf C;
2912
2913         memcpy(&C, A, sizeof(*A));
2914         memcpy(A, B, sizeof(*B));
2915         memcpy(B, &C, sizeof(C));
2916
2917 }
2918
2919
2920 /**
2921  * @brief convert one buffer according to the preselected iconv pointer PIC
2922  * @param ConvertBuf buffer we need to translate
2923  * @param TmpBuf To share a workbuffer over several iterations. prepare to have it filled with useless stuff afterwards.
2924  * @param pic Pointer to the iconv-session Object
2925  */
2926 void StrBufConvert(StrBuf *ConvertBuf, StrBuf *TmpBuf, void *pic)
2927 {
2928 #ifdef HAVE_ICONV
2929         long trycount = 0;
2930         size_t siz;
2931         iconv_t ic;
2932         char *ibuf;                     /**< Buffer of characters to be converted */
2933         char *obuf;                     /**< Buffer for converted characters */
2934         size_t ibuflen;                 /**< Length of input buffer */
2935         size_t obuflen;                 /**< Length of output buffer */
2936
2937
2938         /* since we're converting to utf-8, one glyph may take up to 6 bytes */
2939         if (ConvertBuf->BufUsed * 6 >= TmpBuf->BufSize)
2940                 IncreaseBuf(TmpBuf, 0, ConvertBuf->BufUsed * 6);
2941 TRYAGAIN:
2942         ic = *(iconv_t*)pic;
2943         ibuf = ConvertBuf->buf;
2944         ibuflen = ConvertBuf->BufUsed;
2945         obuf = TmpBuf->buf;
2946         obuflen = TmpBuf->BufSize;
2947         
2948         siz = iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
2949
2950         if (siz < 0) {
2951                 if (errno == E2BIG) {
2952                         trycount ++;                    
2953                         IncreaseBuf(TmpBuf, 0, 0);
2954                         if (trycount < 5) 
2955                                 goto TRYAGAIN;
2956
2957                 }
2958                 else if (errno == EILSEQ){ 
2959                         /* hm, invalid utf8 sequence... what to do now? */
2960                         /* An invalid multibyte sequence has been encountered in the input */
2961                 }
2962                 else if (errno == EINVAL) {
2963                         /* An incomplete multibyte sequence has been encountered in the input. */
2964                 }
2965
2966                 FlushStrBuf(TmpBuf);
2967         }
2968         else {
2969                 TmpBuf->BufUsed = TmpBuf->BufSize - obuflen;
2970                 TmpBuf->buf[TmpBuf->BufUsed] = '\0';
2971                 
2972                 /* little card game: wheres the red lady? */
2973                 SwapBuffers(ConvertBuf, TmpBuf);
2974                 FlushStrBuf(TmpBuf);
2975         }
2976 #endif
2977 }
2978
2979
2980 /**
2981  * @brief catches one RFC822 encoded segment, and decodes it.
2982  * @param Target buffer to fill with result
2983  * @param DecodeMe buffer with stuff to process
2984  * @param SegmentStart points to our current segment in DecodeMe
2985  * @param SegmentEnd Points to the end of our current segment in DecodeMe
2986  * @param ConvertBuf Workbuffer shared between several iterations. Random content; needs to be valid
2987  * @param ConvertBuf2 Workbuffer shared between several iterations. Random content; needs to be valid
2988  * @param FoundCharset Characterset to default decoding to; if we find another we will overwrite it.
2989  */
2990 inline static void DecodeSegment(StrBuf *Target, 
2991                                  const StrBuf *DecodeMe, 
2992                                  char *SegmentStart, 
2993                                  char *SegmentEnd, 
2994                                  StrBuf *ConvertBuf,
2995                                  StrBuf *ConvertBuf2, 
2996                                  StrBuf *FoundCharset)
2997 {
2998         StrBuf StaticBuf;
2999         char charset[128];
3000         char encoding[16];
3001 #ifdef HAVE_ICONV
3002         iconv_t ic = (iconv_t)(-1);
3003 #else
3004         void *ic = NULL;
3005 #endif
3006         /* Now we handle foreign character sets properly encoded
3007          * in RFC2047 format.
3008          */
3009         StaticBuf.buf = SegmentStart;
3010         StaticBuf.BufUsed = SegmentEnd - SegmentStart;
3011         StaticBuf.BufSize = DecodeMe->BufSize - (SegmentStart - DecodeMe->buf);
3012         extract_token(charset, SegmentStart, 1, '?', sizeof charset);
3013         if (FoundCharset != NULL) {
3014                 FlushStrBuf(FoundCharset);
3015                 StrBufAppendBufPlain(FoundCharset, charset, -1, 0);
3016         }
3017         extract_token(encoding, SegmentStart, 2, '?', sizeof encoding);
3018         StrBufExtract_token(ConvertBuf, &StaticBuf, 3, '?');
3019         
3020         *encoding = toupper(*encoding);
3021         if (*encoding == 'B') { /**< base64 */
3022                 ConvertBuf2->BufUsed = CtdlDecodeBase64(ConvertBuf2->buf, 
3023                                                         ConvertBuf->buf, 
3024                                                         ConvertBuf->BufUsed);
3025         }
3026         else if (*encoding == 'Q') {    /**< quoted-printable */
3027                 long pos;
3028                 
3029                 pos = 0;
3030                 while (pos < ConvertBuf->BufUsed)
3031                 {
3032                         if (ConvertBuf->buf[pos] == '_') 
3033                                 ConvertBuf->buf[pos] = ' ';
3034                         pos++;
3035                 }
3036                 
3037                 ConvertBuf2->BufUsed = CtdlDecodeQuotedPrintable(
3038                         ConvertBuf2->buf, 
3039                         ConvertBuf->buf,
3040                         ConvertBuf->BufUsed);
3041         }
3042         else {
3043                 StrBufAppendBuf(ConvertBuf2, ConvertBuf, 0);
3044         }
3045 #ifdef HAVE_ICONV
3046         ctdl_iconv_open("UTF-8", charset, &ic);
3047         if (ic != (iconv_t)(-1) ) {             
3048 #endif
3049                 StrBufConvert(ConvertBuf2, ConvertBuf, &ic);
3050                 StrBufAppendBuf(Target, ConvertBuf2, 0);
3051 #ifdef HAVE_ICONV
3052                 iconv_close(ic);
3053         }
3054         else {
3055                 StrBufAppendBufPlain(Target, HKEY("(unreadable)"), 0);
3056         }
3057 #endif
3058 }
3059
3060 /**
3061  * @brief Handle subjects with RFC2047 encoding such as:
3062  * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3063  * @param Target where to put the decoded string to 
3064  * @param DecodeMe buffer with encoded string
3065  * @param DefaultCharset if we don't find one, which should we use?
3066  * @param FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string, 
3067  *        put it here for later use where no string might be known.
3068  */
3069 void StrBuf_RFC822_to_Utf8(StrBuf *Target, const StrBuf *DecodeMe, const StrBuf* DefaultCharset, StrBuf *FoundCharset)
3070 {
3071         StrBuf *DecodedInvalidBuf = NULL;
3072         StrBuf *ConvertBuf, *ConvertBuf2;
3073         const StrBuf *DecodeMee = DecodeMe;
3074         char *start, *end, *next, *nextend, *ptr = NULL;
3075 #ifdef HAVE_ICONV
3076         iconv_t ic = (iconv_t)(-1) ;
3077 #endif
3078         const char *eptr;
3079         int passes = 0;
3080         int i, len, delta;
3081         int illegal_non_rfc2047_encoding = 0;
3082
3083         /* Sometimes, badly formed messages contain strings which were simply
3084          *  written out directly in some foreign character set instead of
3085          *  using RFC2047 encoding.  This is illegal but we will attempt to
3086          *  handle it anyway by converting from a user-specified default
3087          *  charset to UTF-8 if we see any nonprintable characters.
3088          */
3089         
3090         len = StrLength(DecodeMe);
3091         for (i=0; i<DecodeMe->BufUsed; ++i) {
3092                 if ((DecodeMe->buf[i] < 32) || (DecodeMe->buf[i] > 126)) {
3093                         illegal_non_rfc2047_encoding = 1;
3094                         break;
3095                 }
3096         }
3097
3098         ConvertBuf = NewStrBufPlain(NULL, StrLength(DecodeMe));
3099         if ((illegal_non_rfc2047_encoding) &&
3100             (strcasecmp(ChrPtr(DefaultCharset), "UTF-8")) && 
3101             (strcasecmp(ChrPtr(DefaultCharset), "us-ascii")) )
3102         {
3103 #ifdef HAVE_ICONV
3104                 ctdl_iconv_open("UTF-8", ChrPtr(DefaultCharset), &ic);
3105                 if (ic != (iconv_t)(-1) ) {
3106                         DecodedInvalidBuf = NewStrBufDup(DecodeMe);
3107                         StrBufConvert(DecodedInvalidBuf, ConvertBuf, &ic);///TODO: don't void const?
3108                         DecodeMee = DecodedInvalidBuf;
3109                         iconv_close(ic);
3110                 }
3111 #endif
3112         }
3113
3114         /* pre evaluate the first pair */
3115         nextend = end = NULL;
3116         len = StrLength(DecodeMee);
3117         start = strstr(DecodeMee->buf, "=?");
3118         eptr = DecodeMee->buf + DecodeMee->BufUsed;
3119         if (start != NULL) 
3120                 end = FindNextEnd (DecodeMee, start);
3121         else {
3122                 StrBufAppendBuf(Target, DecodeMee, 0);
3123                 FreeStrBuf(&ConvertBuf);
3124                 FreeStrBuf(&DecodedInvalidBuf);
3125                 return;
3126         }
3127
3128         ConvertBuf2 = NewStrBufPlain(NULL, StrLength(DecodeMee));
3129
3130         if (start != DecodeMee->buf) {
3131                 long nFront;
3132                 
3133                 nFront = start - DecodeMee->buf;
3134                 StrBufAppendBufPlain(Target, DecodeMee->buf, nFront, 0);
3135                 len -= nFront;
3136         }
3137         /*
3138          * Since spammers will go to all sorts of absurd lengths to get their
3139          * messages through, there are LOTS of corrupt headers out there.
3140          * So, prevent a really badly formed RFC2047 header from throwing
3141          * this function into an infinite loop.
3142          */
3143         while ((start != NULL) && 
3144                (end != NULL) && 
3145                (start < eptr) && 
3146                (end < eptr) && 
3147                (passes < 20))
3148         {
3149                 passes++;
3150                 DecodeSegment(Target, 
3151                               DecodeMee, 
3152                               start, 
3153                               end, 
3154                               ConvertBuf,
3155                               ConvertBuf2,
3156                               FoundCharset);
3157                 
3158                 next = strstr(end, "=?");
3159                 nextend = NULL;
3160                 if ((next != NULL) && 
3161                     (next < eptr))
3162                         nextend = FindNextEnd(DecodeMee, next);
3163                 if (nextend == NULL)
3164                         next = NULL;
3165
3166                 /* did we find two partitions */
3167                 if ((next != NULL) && 
3168                     ((next - end) > 2))
3169                 {
3170                         ptr = end + 2;
3171                         while ((ptr < next) && 
3172                                (isspace(*ptr) ||
3173                                 (*ptr == '\r') ||
3174                                 (*ptr == '\n') || 
3175                                 (*ptr == '\t')))
3176                                 ptr ++;
3177                         /* did we find a gab just filled with blanks? */
3178                         if (ptr == next)
3179                         {
3180                                 long gap = next - start;
3181                                 memmove (end + 2,
3182                                          next,
3183                                          len - (gap));
3184                                 len -= gap;
3185                                 /* now terminate the gab at the end */
3186                                 delta = (next - end) - 2; ////TODO: const! 
3187                                 ((StrBuf*)DecodeMee)->BufUsed -= delta;
3188                                 ((StrBuf*)DecodeMee)->buf[DecodeMee->BufUsed] = '\0';
3189
3190                                 /* move next to its new location. */
3191                                 next -= delta;
3192                                 nextend -= delta;
3193                         }
3194                 }
3195                 /* our next-pair is our new first pair now. */
3196                 ptr = end + 2;
3197                 start = next;
3198                 end = nextend;
3199         }
3200         end = ptr;
3201         nextend = DecodeMee->buf + DecodeMee->BufUsed;
3202         if ((end != NULL) && (end < nextend)) {
3203                 ptr = end;
3204                 while ( (ptr < nextend) &&
3205                         (isspace(*ptr) ||
3206                          (*ptr == '\r') ||
3207                          (*ptr == '\n') || 
3208                          (*ptr == '\t')))
3209                         ptr ++;
3210                 if (ptr < nextend)
3211                         StrBufAppendBufPlain(Target, end, nextend - end, 0);
3212         }
3213         FreeStrBuf(&ConvertBuf);
3214         FreeStrBuf(&ConvertBuf2);
3215         FreeStrBuf(&DecodedInvalidBuf);
3216 }
3217
3218 /**
3219  * @brief evaluate the length of an utf8 special character sequence
3220  * @param Char the character to examine
3221  * @returns width of utf8 chars in bytes
3222  */
3223 static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE)
3224 {
3225         int n = 1;
3226         char test = (1<<7);
3227         
3228         while ((n < 8) && ((test & *CharS) != 0)) {
3229                 test = test << 1;
3230                 n ++;
3231         }
3232         if ((n > 6) || ((CharE - CharS) > n))
3233                 n = 1;
3234         return n;
3235 }
3236
3237 /**
3238  * @brief detect whether this char starts an utf-8 encoded char
3239  * @param Char character to inspect
3240  * @returns yes or no
3241  */
3242 static inline int Ctdl_IsUtf8SequenceStart(const char Char)
3243 {
3244 /** 11??.???? indicates an UTF8 Sequence. */
3245         return ((Char & 0xC0) != 0);
3246 }
3247
3248 /**
3249  * @brief measure the number of glyphs in an UTF8 string...
3250  * @param str string to measure
3251  * @returns the length of str
3252  */
3253 long StrBuf_Utf8StrLen(StrBuf *Buf)
3254 {
3255         int n = 0;
3256         int m = 0;
3257         char *aptr, *eptr;
3258
3259         if ((Buf == NULL) || (Buf->BufUsed == 0))
3260                 return 0;
3261         aptr = Buf->buf;
3262         eptr = Buf->buf + Buf->BufUsed;
3263         while ((aptr < eptr) && (*aptr != '\0')) {
3264                 if (Ctdl_IsUtf8SequenceStart(*aptr)){
3265                         m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
3266                         while ((aptr < eptr) && (m-- > 0) && (*aptr++ != '\0'))
3267                                 n ++;
3268                 }
3269                 else {
3270                         n++;
3271                         aptr++;
3272                 }
3273                         
3274         }
3275         return n;
3276 }
3277
3278 /**
3279  * @brief cuts a string after maxlen glyphs
3280  * @param str string to cut to maxlen glyphs
3281  * @param maxlen how long may the string become?
3282  * @returns pointer to maxlen or the end of the string
3283  */
3284 long StrBuf_Utf8StrCut(StrBuf *Buf, int maxlen)
3285 {
3286         char *aptr, *eptr;
3287         int n = 0, m = 0;
3288
3289         aptr = Buf->buf;
3290         eptr = Buf->buf + Buf->BufUsed;
3291         while ((aptr < eptr) && (*aptr != '\0')) {
3292                 if (Ctdl_IsUtf8SequenceStart(*aptr)){
3293                         m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
3294                         while ((m-- > 0) && (*aptr++ != '\0'))
3295                                 n ++;
3296                 }
3297                 else {
3298                         n++;
3299                         aptr++;
3300                 }
3301                 if (n > maxlen) {
3302                         *aptr = '\0';
3303                         Buf->BufUsed = aptr - Buf->buf;
3304                         return Buf->BufUsed;
3305                 }                       
3306         }
3307         return Buf->BufUsed;
3308
3309 }
3310
3311
3312 /**
3313  * @brief extract a "next line" from Buf; Ptr to persist across several iterations
3314  * @param LineBuf your line will be copied here.
3315  * @param Buf BLOB with lines of text...
3316  * @param Ptr moved arround to keep the next-line across several iterations
3317  *        has to be &NULL on start; will be &NotNULL on end of buffer
3318  * @returns size of copied buffer
3319  */
3320 int StrBufSipLine(StrBuf *LineBuf, StrBuf *Buf, const char **Ptr)
3321 {
3322         const char *aptr, *ptr, *eptr;
3323         char *optr, *xptr;
3324
3325         if ((Buf == NULL) || (*Ptr == StrBufNOTNULL)) {
3326                 *Ptr = StrBufNOTNULL;
3327                 return 0;
3328         }
3329
3330         FlushStrBuf(LineBuf);
3331         if (*Ptr==NULL)
3332                 ptr = aptr = Buf->buf;
3333         else
3334                 ptr = aptr = *Ptr;
3335
3336         optr = LineBuf->buf;
3337         eptr = Buf->buf + Buf->BufUsed;
3338         xptr = LineBuf->buf + LineBuf->BufSize - 1;
3339
3340         while ((ptr <= eptr) && 
3341                (*ptr != '\n') &&
3342                (*ptr != '\r') )
3343         {
3344                 *optr = *ptr;
3345                 optr++; ptr++;
3346                 if (optr == xptr) {
3347                         LineBuf->BufUsed = optr - LineBuf->buf;
3348                         IncreaseBuf(LineBuf,  1, LineBuf->BufUsed + 1);
3349                         optr = LineBuf->buf + LineBuf->BufUsed;
3350                         xptr = LineBuf->buf + LineBuf->BufSize - 1;
3351                 }
3352         }
3353
3354         if ((ptr >= eptr) && (optr > LineBuf->buf))
3355                 optr --;
3356         LineBuf->BufUsed = optr - LineBuf->buf;
3357         *optr = '\0';       
3358         if ((ptr <= eptr) && (*ptr == '\r'))
3359                 ptr ++;
3360         if ((ptr <= eptr) && (*ptr == '\n'))
3361                 ptr ++;
3362         
3363         if (ptr < eptr) {
3364                 *Ptr = ptr;
3365         }
3366         else {
3367                 *Ptr = StrBufNOTNULL;
3368         }
3369
3370         return Buf->BufUsed - (ptr - Buf->buf);
3371 }