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