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