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