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