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