90c8cf3525b4c71c5deee20441eb26567a66ac90
[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                 return;
1181         }
1182
1183         pRight = strchr(Buf->buf, rightboundary);
1184         if (pRight != NULL) {
1185                 StrBufCutAt(Buf, 0, pRight);
1186         }
1187
1188         pLeft = strrchr(ChrPtr(Buf), leftboundary);
1189         if (pLeft != NULL) {
1190                 StrBufCutLeft(Buf, pLeft - Buf->buf + 1);
1191         }
1192 }
1193
1194
1195 /**
1196  * @ingroup StrBuf_Filler
1197  * @brief uppercase the contents of a buffer
1198  * @param Buf the buffer to translate
1199  */
1200 void StrBufUpCase(StrBuf *Buf) 
1201 {
1202         char *pch, *pche;
1203
1204         if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1205
1206         pch = Buf->buf;
1207         pche = pch + Buf->BufUsed;
1208         while (pch < pche) {
1209                 *pch = toupper(*pch);
1210                 pch ++;
1211         }
1212 }
1213
1214
1215 /**
1216  * @ingroup StrBuf_Filler
1217  * @brief lowercase the contents of a buffer
1218  * @param Buf the buffer to translate
1219  */
1220 void StrBufLowerCase(StrBuf *Buf) 
1221 {
1222         char *pch, *pche;
1223
1224         if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1225
1226         pch = Buf->buf;
1227         pche = pch + Buf->BufUsed;
1228         while (pch < pche) {
1229                 *pch = tolower(*pch);
1230                 pch ++;
1231         }
1232 }
1233
1234
1235 /*******************************************************************************
1236  *           a tokenizer that kills, maims, and destroys                       *
1237  *******************************************************************************/
1238
1239 /**
1240  * @ingroup StrBuf_Tokenizer
1241  * @brief Replace a token at a given place with a given length by another token with given length
1242  * @param Buf String where to work on
1243  * @param where where inside of the Buf is the search-token
1244  * @param HowLong How long is the token to be replaced
1245  * @param Repl Token to insert at 'where'
1246  * @param ReplLen Length of repl
1247  * @returns -1 if fail else length of resulting Buf
1248  */
1249 int StrBufReplaceToken(StrBuf *Buf, long where, long HowLong, 
1250                        const char *Repl, long ReplLen)
1251 {
1252
1253         if ((Buf == NULL) || 
1254             (where > Buf->BufUsed) ||
1255             (where + HowLong > Buf->BufUsed))
1256                 return -1;
1257
1258         if (where + ReplLen - HowLong > Buf->BufSize)
1259                 if (IncreaseBuf(Buf, 1, Buf->BufUsed + ReplLen) < 0)
1260                         return -1;
1261
1262         memmove(Buf->buf + where + ReplLen, 
1263                 Buf->buf + where + HowLong,
1264                 Buf->BufUsed - where - HowLong);
1265                                                 
1266         memcpy(Buf->buf + where, 
1267                Repl, ReplLen);
1268
1269         Buf->BufUsed += ReplLen - HowLong;
1270
1271         return Buf->BufUsed;
1272 }
1273
1274 /**
1275  * @ingroup StrBuf_Tokenizer
1276  * @brief Counts the numbmer of tokens in a buffer
1277  * @param source String to count tokens in
1278  * @param tok    Tokenizer char to count
1279  * @returns numbers of tokenizer chars found
1280  */
1281 int StrBufNum_tokens(const StrBuf *source, char tok)
1282 {
1283         char *pch, *pche;
1284         long NTokens;
1285         if ((source == NULL) || (source->BufUsed == 0))
1286                 return 0;
1287         if ((source->BufUsed == 1) && (*source->buf == tok))
1288                 return 2;
1289         NTokens = 1;
1290         pch = source->buf;
1291         pche = pch + source->BufUsed;
1292         while (pch < pche)
1293         {
1294                 if (*pch == tok)
1295                         NTokens ++;
1296                 pch ++;
1297         }
1298         return NTokens;
1299 }
1300
1301 /**
1302  * @ingroup StrBuf_Tokenizer
1303  * @brief a string tokenizer
1304  * @param Source StringBuffer to read into
1305  * @param parmnum n'th Parameter to remove
1306  * @param separator tokenizer character
1307  * @returns -1 if not found, else length of token.
1308  */
1309 int StrBufRemove_token(StrBuf *Source, int parmnum, char separator)
1310 {
1311         int ReducedBy;
1312         char *d, *s, *end;              /* dest, source */
1313         int count = 0;
1314
1315         /* Find desired @parameter */
1316         end = Source->buf + Source->BufUsed;
1317         d = Source->buf;
1318         while ((d <= end) && 
1319                (count < parmnum))
1320         {
1321                 /* End of string, bail! */
1322                 if (!*d) {
1323                         d = NULL;
1324                         break;
1325                 }
1326                 if (*d == separator) {
1327                         count++;
1328                 }
1329                 d++;
1330         }
1331         if ((d == NULL) || (d >= end))
1332                 return 0;               /* @Parameter not found */
1333
1334         /* Find next @parameter */
1335         s = d;
1336         while ((s <= end) && 
1337                (*s && *s != separator))
1338         {
1339                 s++;
1340         }
1341         if (*s == separator)
1342                 s++;
1343         ReducedBy = d - s;
1344
1345         /* Hack and slash */
1346         if (s >= end) {
1347                 return 0;
1348         }
1349         else if (*s) {
1350                 memmove(d, s, Source->BufUsed - (s - Source->buf));
1351                 Source->BufUsed += ReducedBy;
1352                 Source->buf[Source->BufUsed] = '\0';
1353         }
1354         else if (d == Source->buf) {
1355                 *d = 0;
1356                 Source->BufUsed = 0;
1357         }
1358         else {
1359                 *--d = '\0';
1360                 Source->BufUsed += ReducedBy;
1361         }
1362         /*
1363         while (*s) {
1364                 *d++ = *s++;
1365         }
1366         *d = 0;
1367         */
1368         return ReducedBy;
1369 }
1370
1371 int StrBufExtract_tokenFromStr(StrBuf *dest, const char *Source, long SourceLen, int parmnum, char separator)
1372 {
1373         const StrBuf Temp = {
1374                 (char*)Source,
1375                 SourceLen,
1376                 SourceLen,
1377                 1
1378 #ifdef SIZE_DEBUG
1379                 ,
1380                 0,
1381                 "",
1382                 ""
1383 #endif
1384         };
1385
1386         return StrBufExtract_token(dest, &Temp, parmnum, separator);
1387 }
1388
1389 /**
1390  * @ingroup StrBuf_Tokenizer
1391  * @brief a string tokenizer
1392  * @param dest Destination StringBuffer
1393  * @param Source StringBuffer to read into
1394  * @param parmnum n'th Parameter to extract
1395  * @param separator tokenizer character
1396  * @returns -1 if not found, else length of token.
1397  */
1398 int StrBufExtract_token(StrBuf *dest, const StrBuf *Source, int parmnum, char separator)
1399 {
1400         const char *s, *e;              //* source * /
1401         int len = 0;                    //* running total length of extracted string * /
1402         int current_token = 0;          //* token currently being processed * /
1403          
1404         if (dest != NULL) {
1405                 dest->buf[0] = '\0';
1406                 dest->BufUsed = 0;
1407         }
1408         else
1409                 return(-1);
1410
1411         if ((Source == NULL) || (Source->BufUsed ==0)) {
1412                 return(-1);
1413         }
1414         s = Source->buf;
1415         e = s + Source->BufUsed;
1416
1417         //cit_backtrace();
1418         //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
1419
1420         while ((s < e) && !IsEmptyStr(s)) {
1421                 if (*s == separator) {
1422                         ++current_token;
1423                 }
1424                 if (len >= dest->BufSize) {
1425                         dest->BufUsed = len;
1426                         if (IncreaseBuf(dest, 1, -1) < 0) {
1427                                 dest->BufUsed --;
1428                                 break;
1429                         }
1430                 }
1431                 if ( (current_token == parmnum) && 
1432                      (*s != separator)) {
1433                         dest->buf[len] = *s;
1434                         ++len;
1435                 }
1436                 else if (current_token > parmnum) {
1437                         break;
1438                 }
1439                 ++s;
1440         }
1441         
1442         dest->buf[len] = '\0';
1443         dest->BufUsed = len;
1444                 
1445         if (current_token < parmnum) {
1446                 //lprintf (CTDL_DEBUG,"test <!: %s\n", dest);
1447                 return(-1);
1448         }
1449         //lprintf (CTDL_DEBUG,"test <: %d; %s\n", len, dest);
1450         return(len);
1451 }
1452
1453
1454
1455
1456
1457 /**
1458  * @ingroup StrBuf_Tokenizer
1459  * @brief a string tokenizer to fetch an integer
1460  * @param Source String containing tokens
1461  * @param parmnum n'th Parameter to extract
1462  * @param separator tokenizer character
1463  * @returns 0 if not found, else integer representation of the token
1464  */
1465 int StrBufExtract_int(const StrBuf* Source, int parmnum, char separator)
1466 {
1467         StrBuf tmp;
1468         char buf[64];
1469         
1470         tmp.buf = buf;
1471         buf[0] = '\0';
1472         tmp.BufSize = 64;
1473         tmp.BufUsed = 0;
1474         tmp.ConstBuf = 1;
1475         if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
1476                 return(atoi(buf));
1477         else
1478                 return 0;
1479 }
1480
1481 /**
1482  * @ingroup StrBuf_Tokenizer
1483  * @brief a string tokenizer to fetch a long integer
1484  * @param Source String containing tokens
1485  * @param parmnum n'th Parameter to extract
1486  * @param separator tokenizer character
1487  * @returns 0 if not found, else long integer representation of the token
1488  */
1489 long StrBufExtract_long(const StrBuf* Source, int parmnum, char separator)
1490 {
1491         StrBuf tmp;
1492         char buf[64];
1493         
1494         tmp.buf = buf;
1495         buf[0] = '\0';
1496         tmp.BufSize = 64;
1497         tmp.BufUsed = 0;
1498         tmp.ConstBuf = 1;
1499         if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
1500                 return(atoi(buf));
1501         else
1502                 return 0;
1503 }
1504
1505
1506 /**
1507  * @ingroup StrBuf_Tokenizer
1508  * @brief a string tokenizer to fetch an unsigned long
1509  * @param Source String containing tokens
1510  * @param parmnum n'th Parameter to extract
1511  * @param separator tokenizer character
1512  * @returns 0 if not found, else unsigned long representation of the token
1513  */
1514 unsigned long StrBufExtract_unsigned_long(const StrBuf* Source, int parmnum, char separator)
1515 {
1516         StrBuf tmp;
1517         char buf[64];
1518         char *pnum;
1519         
1520         tmp.buf = buf;
1521         buf[0] = '\0';
1522         tmp.BufSize = 64;
1523         tmp.BufUsed = 0;
1524         tmp.ConstBuf = 1;
1525         if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0) {
1526                 pnum = &buf[0];
1527                 if (*pnum == '-')
1528                         pnum ++;
1529                 return (unsigned long) atol(pnum);
1530         }
1531         else 
1532                 return 0;
1533 }
1534
1535
1536
1537 /**
1538  * @ingroup StrBuf_NextTokenizer
1539  * @brief a string tokenizer; Bounds checker
1540  *  function to make shure whether StrBufExtract_NextToken and friends have reached the end of the string.
1541  * @param Source our tokenbuffer
1542  * @param pStart the token iterator pointer to inspect
1543  * @returns whether the revolving pointer is inside of the search range
1544  */
1545 int StrBufHaveNextToken(const StrBuf *Source, const char **pStart)
1546 {
1547         if ((Source == NULL) || 
1548             (*pStart == StrBufNOTNULL) ||
1549             (Source->BufUsed == 0))
1550         {
1551                 return 0;
1552         }
1553         if (*pStart == NULL)
1554         {
1555                 return 1;
1556         }
1557         else if (*pStart > Source->buf + Source->BufUsed)
1558         {
1559                 return 0;
1560         }
1561         else if (*pStart <= Source->buf)
1562         {
1563                 return 0;
1564         }
1565
1566         return 1;
1567 }
1568
1569 /**
1570  * @ingroup StrBuf_NextTokenizer
1571  * @brief a string tokenizer
1572  * @param dest Destination StringBuffer
1573  * @param Source StringBuffer to read into
1574  * @param pStart pointer to the end of the last token. Feed with NULL on start.
1575  * @param separator tokenizer 
1576  * @returns -1 if not found, else length of token.
1577  */
1578 int StrBufExtract_NextToken(StrBuf *dest, const StrBuf *Source, const char **pStart, char separator)
1579 {
1580         const char *s;          /* source */
1581         const char *EndBuffer;  /* end stop of source buffer */
1582         int current_token = 0;  /* token currently being processed */
1583         int len = 0;            /* running total length of extracted string */
1584
1585         if ((Source          == NULL) || 
1586             (Source->BufUsed == 0)      ) 
1587         {
1588                 *pStart = StrBufNOTNULL;
1589                 if (dest != NULL)
1590                         FlushStrBuf(dest);
1591                 return -1;
1592         }
1593          
1594         EndBuffer = Source->buf + Source->BufUsed;
1595
1596         if (dest != NULL) 
1597         {
1598                 dest->buf[0] = '\0';
1599                 dest->BufUsed = 0;
1600         }
1601         else
1602         {
1603                 *pStart = EndBuffer + 1;
1604                 return -1;
1605         }
1606
1607         if (*pStart == NULL)
1608         {
1609                 *pStart = Source->buf; /* we're starting to examine this buffer. */
1610         }
1611         else if ((*pStart < Source->buf) || 
1612                  (*pStart > EndBuffer  )   ) 
1613         {
1614                 return -1; /* no more tokens to find. */
1615         }
1616
1617         s = *pStart;
1618         /* start to find the next token */
1619         while ((s <= EndBuffer)      && 
1620                (current_token == 0) ) 
1621         {
1622                 if (*s == separator) 
1623                 {
1624                         /* we found the next token */
1625                         ++current_token;
1626                 }
1627
1628                 if (len >= dest->BufSize) 
1629                 {
1630                         /* our Dest-buffer isn't big enough, increase it. */
1631                         dest->BufUsed = len;
1632
1633                         if (IncreaseBuf(dest, 1, -1) < 0) {
1634                                 /* WHUT? no more mem? bail out. */
1635                                 s = EndBuffer;
1636                                 dest->BufUsed --;
1637                                 break;
1638                         }
1639                 }
1640
1641                 if ( (current_token == 0 ) &&   /* are we in our target token? */
1642                      (!IsEmptyStr(s)     ) &&
1643                      (separator     != *s)    ) /* don't copy the token itself */
1644                 {
1645                         dest->buf[len] = *s;    /* Copy the payload */
1646                         ++len;                  /* remember the bigger size. */
1647                 }
1648
1649                 ++s;
1650         }
1651
1652         /* did we reach the end? */
1653         if ((s > EndBuffer)) {
1654                 EndBuffer = StrBufNOTNULL;
1655                 *pStart = EndBuffer;
1656         }
1657         else {
1658                 *pStart = s;  /* remember the position for the next run */
1659         }
1660
1661         /* sanitize our extracted token */
1662         dest->buf[len] = '\0';
1663         dest->BufUsed  = len;
1664
1665         return (len);
1666 }
1667
1668
1669 /**
1670  * @ingroup StrBuf_NextTokenizer
1671  * @brief a string tokenizer
1672  * @param Source StringBuffer to read from
1673  * @param pStart pointer to the end of the last token. Feed with NULL.
1674  * @param separator tokenizer character
1675  * @param nTokens number of tokens to fastforward over
1676  * @returns -1 if not found, else length of token.
1677  */
1678 int StrBufSkip_NTokenS(const StrBuf *Source, const char **pStart, char separator, int nTokens)
1679 {
1680         const char *s, *EndBuffer;      //* source * /
1681         int len = 0;                    //* running total length of extracted string * /
1682         int current_token = 0;          //* token currently being processed * /
1683
1684         if ((Source == NULL) || 
1685             (Source->BufUsed ==0)) {
1686                 return(-1);
1687         }
1688         if (nTokens == 0)
1689                 return Source->BufUsed;
1690
1691         if (*pStart == NULL)
1692                 *pStart = Source->buf;
1693
1694         EndBuffer = Source->buf + Source->BufUsed;
1695
1696         if ((*pStart < Source->buf) || 
1697             (*pStart >  EndBuffer)) {
1698                 return (-1);
1699         }
1700
1701
1702         s = *pStart;
1703
1704         //cit_backtrace();
1705         //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
1706
1707         while ((s < EndBuffer) && !IsEmptyStr(s)) {
1708                 if (*s == separator) {
1709                         ++current_token;
1710                 }
1711                 if (current_token >= nTokens) {
1712                         break;
1713                 }
1714                 ++s;
1715         }
1716         *pStart = s;
1717         (*pStart) ++;
1718
1719         return(len);
1720 }
1721
1722 /**
1723  * @ingroup StrBuf_NextTokenizer
1724  * @brief a string tokenizer to fetch an integer
1725  * @param Source StringBuffer to read from
1726  * @param pStart Cursor on the tokenstring
1727  * @param separator tokenizer character
1728  * @returns 0 if not found, else integer representation of the token
1729  */
1730 int StrBufExtractNext_int(const StrBuf* Source, const char **pStart, char separator)
1731 {
1732         StrBuf tmp;
1733         char buf[64];
1734         
1735         tmp.buf = buf;
1736         buf[0] = '\0';
1737         tmp.BufSize = 64;
1738         tmp.BufUsed = 0;
1739         tmp.ConstBuf = 1;
1740         if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0)
1741                 return(atoi(buf));
1742         else
1743                 return 0;
1744 }
1745
1746 /**
1747  * @ingroup StrBuf_NextTokenizer
1748  * @brief a string tokenizer to fetch a long integer
1749  * @param Source StringBuffer to read from
1750  * @param pStart Cursor on the tokenstring
1751  * @param separator tokenizer character
1752  * @returns 0 if not found, else long integer representation of the token
1753  */
1754 long StrBufExtractNext_long(const StrBuf* Source, const char **pStart, char separator)
1755 {
1756         StrBuf tmp;
1757         char buf[64];
1758         
1759         tmp.buf = buf;
1760         buf[0] = '\0';
1761         tmp.BufSize = 64;
1762         tmp.BufUsed = 0;
1763         tmp.ConstBuf = 1;
1764         if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0)
1765                 return(atoi(buf));
1766         else
1767                 return 0;
1768 }
1769
1770
1771 /**
1772  * @ingroup StrBuf_NextTokenizer
1773  * @brief a string tokenizer to fetch an unsigned long
1774  * @param Source StringBuffer to read from
1775  * @param pStart Cursor on the tokenstring
1776  * @param separator tokenizer character
1777  * @returns 0 if not found, else unsigned long representation of the token
1778  */
1779 unsigned long StrBufExtractNext_unsigned_long(const StrBuf* Source, const char **pStart, char separator)
1780 {
1781         StrBuf tmp;
1782         char buf[64];
1783         char *pnum;
1784         
1785         tmp.buf = buf;
1786         buf[0] = '\0';
1787         tmp.BufSize = 64;
1788         tmp.BufUsed = 0;
1789         tmp.ConstBuf = 1;
1790         if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0) {
1791                 pnum = &buf[0];
1792                 if (*pnum == '-')
1793                         pnum ++;
1794                 return (unsigned long) atol(pnum);
1795         }
1796         else 
1797                 return 0;
1798 }
1799
1800
1801
1802
1803
1804 /*******************************************************************************
1805  *                             Escape Appending                                *
1806  *******************************************************************************/
1807
1808 /** 
1809  * @ingroup StrBuf_DeEnCoder
1810  * @brief Escape a string for feeding out as a URL while appending it to a Buffer
1811  * @param OutBuf the output buffer
1812  * @param In Buffer to encode
1813  * @param PlainIn way in from plain old c strings
1814  */
1815 void StrBufUrlescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
1816 {
1817         const char *pch, *pche;
1818         char *pt, *pte;
1819         int len;
1820         
1821         if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1822                 return;
1823         if (PlainIn != NULL) {
1824                 len = strlen(PlainIn);
1825                 pch = PlainIn;
1826                 pche = pch + len;
1827         }
1828         else {
1829                 pch = In->buf;
1830                 pche = pch + In->BufUsed;
1831                 len = In->BufUsed;
1832         }
1833
1834         if (len == 0) 
1835                 return;
1836
1837         pt = OutBuf->buf + OutBuf->BufUsed;
1838         pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1839
1840         while (pch < pche) {
1841                 if (pt >= pte) {
1842                         IncreaseBuf(OutBuf, 1, -1);
1843                         pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1844                         pt = OutBuf->buf + OutBuf->BufUsed;
1845                 }
1846
1847                 if((*pch >= 'a' && *pch <= 'z') ||
1848                    (*pch >= '@' && *pch <= 'Z') || /* @ A-Z */
1849                    (*pch >= '0' && *pch <= ':') || /* 0-9 : */
1850                    (*pch == '!') || (*pch == '_') || 
1851                    (*pch == ',') || (*pch == '.'))
1852                 {
1853                         *(pt++) = *(pch++);
1854                         OutBuf->BufUsed++;
1855                 }                       
1856                 else {
1857                         *pt = '%';
1858                         *(pt + 1) = HexList[(unsigned char)*pch][0];
1859                         *(pt + 2) = HexList[(unsigned char)*pch][1];
1860                         pt += 3;
1861                         OutBuf->BufUsed += 3;
1862                         pch ++;
1863                 }
1864         }
1865         *pt = '\0';
1866 }
1867
1868 /** 
1869  * @ingroup StrBuf_DeEnCoder
1870  * @brief Escape a string for feeding out as a the username/password part of an URL while appending it to a Buffer
1871  * @param OutBuf the output buffer
1872  * @param In Buffer to encode
1873  * @param PlainIn way in from plain old c strings
1874  */
1875 void StrBufUrlescUPAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
1876 {
1877         const char *pch, *pche;
1878         char *pt, *pte;
1879         int len;
1880         
1881         if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1882                 return;
1883         if (PlainIn != NULL) {
1884                 len = strlen(PlainIn);
1885                 pch = PlainIn;
1886                 pche = pch + len;
1887         }
1888         else {
1889                 pch = In->buf;
1890                 pche = pch + In->BufUsed;
1891                 len = In->BufUsed;
1892         }
1893
1894         if (len == 0) 
1895                 return;
1896
1897         pt = OutBuf->buf + OutBuf->BufUsed;
1898         pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1899
1900         while (pch < pche) {
1901                 if (pt >= pte) {
1902                         IncreaseBuf(OutBuf, 1, -1);
1903                         pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1904                         pt = OutBuf->buf + OutBuf->BufUsed;
1905                 }
1906
1907                 if((*pch >= 'a' && *pch <= 'z') ||
1908                    (*pch >= 'A' && *pch <= 'Z') || /* A-Z */
1909                    (*pch >= '0' && *pch <= ':') || /* 0-9 : */
1910                    (*pch == '!') || (*pch == '_') || 
1911                    (*pch == ',') || (*pch == '.'))
1912                 {
1913                         *(pt++) = *(pch++);
1914                         OutBuf->BufUsed++;
1915                 }                       
1916                 else {
1917                         *pt = '%';
1918                         *(pt + 1) = HexList[(unsigned char)*pch][0];
1919                         *(pt + 2) = HexList[(unsigned char)*pch][1];
1920                         pt += 3;
1921                         OutBuf->BufUsed += 3;
1922                         pch ++;
1923                 }
1924         }
1925         *pt = '\0';
1926 }
1927
1928 /** 
1929  * @ingroup StrBuf_DeEnCoder
1930  * @brief append a string with characters having a special meaning in xml encoded to the buffer
1931  * @param OutBuf the output buffer
1932  * @param In Buffer to encode
1933  * @param PlainIn way in from plain old c strings
1934  * @param PlainInLen way in from plain old c strings; maybe you've got binary data or know the length?
1935  * @param OverrideLowChars should chars < 0x20 be replaced by _ or escaped as xml entity?
1936  */
1937 void StrBufXMLEscAppend(StrBuf *OutBuf,
1938                         const StrBuf *In,
1939                         const char *PlainIn,
1940                         long PlainInLen,
1941                         int OverrideLowChars)
1942 {
1943         const char *pch, *pche;
1944         char *pt, *pte;
1945         int IsUtf8Sequence;
1946         int len;
1947
1948         if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1949                 return;
1950         if (PlainIn != NULL) {
1951                 if (PlainInLen < 0)
1952                         len = strlen((const char*)PlainIn);
1953                 else
1954                         len = PlainInLen;
1955                 pch = PlainIn;
1956                 pche = pch + len;
1957         }
1958         else {
1959                 pch = (const char*)In->buf;
1960                 pche = pch + In->BufUsed;
1961                 len = In->BufUsed;
1962         }
1963
1964         if (len == 0)
1965                 return;
1966
1967         pt = OutBuf->buf + OutBuf->BufUsed;
1968         /**< we max append 6 chars at once plus the \0 */
1969         pte = OutBuf->buf + OutBuf->BufSize - 6;
1970
1971         while (pch < pche) {
1972                 if (pt >= pte) {
1973                         OutBuf->BufUsed = pt - OutBuf->buf;
1974                         IncreaseBuf(OutBuf, 1, -1);
1975                         pte = OutBuf->buf + OutBuf->BufSize - 6;
1976                         /**< we max append 3 chars at once plus the \0 */
1977
1978                         pt = OutBuf->buf + OutBuf->BufUsed;
1979                 }
1980
1981                 if (*pch == '<') {
1982                         memcpy(pt, HKEY("&lt;"));
1983                         pt += 4;
1984                         pch ++;
1985                 }
1986                 else if (*pch == '>') {
1987                         memcpy(pt, HKEY("&gt;"));
1988                         pt += 4;
1989                         pch ++;
1990                 }
1991                 else if (*pch == '&') {
1992                         memcpy(pt, HKEY("&amp;"));
1993                         pt += 5;
1994                         pch++;
1995                 }
1996                 else if ((*pch >= 0x20) && (*pch <= 0x7F)) {
1997                         *pt = *pch;
1998                         pt++; pch++;
1999                 }
2000                 else if (*pch < 0x20) {
2001                         /* we probably shouldn't be doing this */
2002                         if (OverrideLowChars)
2003                         {
2004                                 *pt = '_';
2005                                 pt ++;
2006                                 pch ++;
2007                         }
2008                         else
2009                         {
2010                                 *pt = '&';
2011                                 pt++;
2012                                 *pt = HexList[*(unsigned char*)pch][0];
2013                                 pt ++;
2014                                 *pt = HexList[*(unsigned char*)pch][1];
2015                                 pt ++; pch ++;
2016                                 *pt = '&';
2017                                 pt++;
2018                                 pch ++;
2019                         }
2020                 }
2021                 else {
2022                         IsUtf8Sequence =  Ctdl_GetUtf8SequenceLength(pch, pche);
2023                         if (IsUtf8Sequence)
2024                         {
2025                                 while (IsUtf8Sequence > 0){
2026                                         *pt = *pch;
2027                                         pt ++;
2028                                         pch ++;
2029                                         --IsUtf8Sequence;
2030                                 }
2031                         }
2032                         else
2033                         {
2034                                 *pt = '&';
2035                                 pt++;
2036                                 *pt = HexList[*(unsigned char*)pch][0];
2037                                 pt ++;
2038                                 *pt = HexList[*(unsigned char*)pch][1];
2039                                 pt ++; pch ++;
2040                                 *pt = '&';
2041                                 pt++;
2042                                 pch ++;
2043                         }
2044                 }
2045         }
2046         *pt = '\0';
2047         OutBuf->BufUsed = pt - OutBuf->buf;
2048 }
2049
2050
2051 /** 
2052  * @ingroup StrBuf_DeEnCoder
2053  * @brief append a string in hex encoding to the buffer
2054  * @param OutBuf the output buffer
2055  * @param In Buffer to encode
2056  * @param PlainIn way in from plain old c strings
2057  * @param PlainInLen way in from plain old c strings; maybe you've got binary data or know the length?
2058  */
2059 void StrBufHexEscAppend(StrBuf *OutBuf, const StrBuf *In, const unsigned char *PlainIn, long PlainInLen)
2060 {
2061         const unsigned char *pch, *pche;
2062         char *pt, *pte;
2063         int len;
2064         
2065         if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
2066                 return;
2067         if (PlainIn != NULL) {
2068                 if (PlainInLen < 0)
2069                         len = strlen((const char*)PlainIn);
2070                 else
2071                         len = PlainInLen;
2072                 pch = PlainIn;
2073                 pche = pch + len;
2074         }
2075         else {
2076                 pch = (const unsigned char*)In->buf;
2077                 pche = pch + In->BufUsed;
2078                 len = In->BufUsed;
2079         }
2080
2081         if (len == 0) 
2082                 return;
2083
2084         pt = OutBuf->buf + OutBuf->BufUsed;
2085         pte = OutBuf->buf + OutBuf->BufSize - 3; /**< we max append 3 chars at once plus the \0 */
2086
2087         while (pch < pche) {
2088                 if (pt >= pte) {
2089                         IncreaseBuf(OutBuf, 1, -1);
2090                         pte = OutBuf->buf + OutBuf->BufSize - 3; /**< we max append 3 chars at once plus the \0 */
2091                         pt = OutBuf->buf + OutBuf->BufUsed;
2092                 }
2093
2094                 *pt = HexList[*pch][0];
2095                 pt ++;
2096                 *pt = HexList[*pch][1];
2097                 pt ++; pch ++; OutBuf->BufUsed += 2;
2098         }
2099         *pt = '\0';
2100 }
2101
2102 void StrBufBase64Append(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn, long PlainInLen, int linebreaks)
2103 {
2104         const char *pch;
2105         char *pt;
2106         int len;
2107         long ExpectLen;
2108         
2109         if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
2110                 return;
2111         if (PlainIn != NULL) {
2112                 if (PlainInLen < 0)
2113                         len = strlen(PlainIn);
2114                 else
2115                         len = PlainInLen;
2116                 pch = PlainIn;
2117         }
2118         else {
2119                 pch = In->buf;
2120                 len = In->BufUsed;
2121         }
2122
2123         if (len == 0) 
2124                 return;
2125
2126         ExpectLen = ((len * 134) / 100) + OutBuf->BufUsed;
2127
2128         if (ExpectLen > OutBuf->BufSize)
2129                 if (IncreaseBuf(OutBuf, 1, ExpectLen) < ExpectLen)
2130                         return;
2131
2132         pt = OutBuf->buf + OutBuf->BufUsed;
2133
2134         len = CtdlEncodeBase64(pt, pch, len, linebreaks);
2135
2136         pt += len;
2137         OutBuf->BufUsed += len;
2138         *pt = '\0';
2139 }
2140
2141 /** 
2142  * @ingroup StrBuf_DeEnCoder
2143  * @brief append a string in hex encoding to the buffer
2144  * @param OutBuf the output buffer
2145  * @param In Buffer to encode
2146  * @param PlainIn way in from plain old c strings
2147  */
2148 void StrBufHexescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
2149 {
2150         StrBufHexEscAppend(OutBuf, In, (const unsigned char*) PlainIn, -1);
2151 }
2152
2153 /**
2154  * @ingroup StrBuf_DeEnCoder
2155  * @brief Append a string, escaping characters which have meaning in HTML.  
2156  *
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  * @param nbsp          If nonzero, spaces are converted to non-breaking spaces.
2161  * @param nolinebreaks  if set to 1, linebreaks are removed from the string.
2162  *                      if set to 2, linebreaks are replaced by &ltbr/&gt
2163  */
2164 long StrEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
2165 {
2166         const char *aptr, *eiptr;
2167         char *bptr, *eptr;
2168         long len;
2169
2170         if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2171                 return -1;
2172
2173         if (PlainIn != NULL) {
2174                 aptr = PlainIn;
2175                 len = strlen(PlainIn);
2176                 eiptr = aptr + len;
2177         }
2178         else {
2179                 aptr = Source->buf;
2180                 eiptr = aptr + Source->BufUsed;
2181                 len = Source->BufUsed;
2182         }
2183
2184         if (len == 0) 
2185                 return -1;
2186
2187         bptr = Target->buf + Target->BufUsed;
2188         eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in...  */
2189
2190         while (aptr < eiptr){
2191                 if(bptr >= eptr) {
2192                         IncreaseBuf(Target, 1, -1);
2193                         eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in...  */
2194                         bptr = Target->buf + Target->BufUsed;
2195                 }
2196                 if (*aptr == '<') {
2197                         memcpy(bptr, "&lt;", 4);
2198                         bptr += 4;
2199                         Target->BufUsed += 4;
2200                 }
2201                 else if (*aptr == '>') {
2202                         memcpy(bptr, "&gt;", 4);
2203                         bptr += 4;
2204                         Target->BufUsed += 4;
2205                 }
2206                 else if (*aptr == '&') {
2207                         memcpy(bptr, "&amp;", 5);
2208                         bptr += 5;
2209                         Target->BufUsed += 5;
2210                 }
2211                 else if (*aptr == '"') {
2212                         memcpy(bptr, "&quot;", 6);
2213                         bptr += 6;
2214                         Target->BufUsed += 6;
2215                 }
2216                 else if (*aptr == '\'') {
2217                         memcpy(bptr, "&#39;", 5);
2218                         bptr += 5;
2219                         Target->BufUsed += 5;
2220                 }
2221                 else if (*aptr == LB) {
2222                         *bptr = '<';
2223                         bptr ++;
2224                         Target->BufUsed ++;
2225                 }
2226                 else if (*aptr == RB) {
2227                         *bptr = '>';
2228                         bptr ++;
2229                         Target->BufUsed ++;
2230                 }
2231                 else if (*aptr == QU) {
2232                         *bptr ='"';
2233                         bptr ++;
2234                         Target->BufUsed ++;
2235                 }
2236                 else if ((*aptr == 32) && (nbsp == 1)) {
2237                         memcpy(bptr, "&nbsp;", 6);
2238                         bptr += 6;
2239                         Target->BufUsed += 6;
2240                 }
2241                 else if ((*aptr == '\n') && (nolinebreaks == 1)) {
2242                         *bptr='\0';     /* nothing */
2243                 }
2244                 else if ((*aptr == '\n') && (nolinebreaks == 2)) {
2245                         memcpy(bptr, "&lt;br/&gt;", 11);
2246                         bptr += 11;
2247                         Target->BufUsed += 11;
2248                 }
2249
2250
2251                 else if ((*aptr == '\r') && (nolinebreaks != 0)) {
2252                         *bptr='\0';     /* nothing */
2253                 }
2254                 else{
2255                         *bptr = *aptr;
2256                         bptr++;
2257                         Target->BufUsed ++;
2258                 }
2259                 aptr ++;
2260         }
2261         *bptr = '\0';
2262         if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
2263                 return -1;
2264         return Target->BufUsed;
2265 }
2266
2267 /**
2268  * @ingroup StrBuf_DeEnCoder
2269  * @brief Append a string, escaping characters which have meaning in HTML.  
2270  * Converts linebreaks into blanks; escapes single quotes
2271  * @param Target        target buffer
2272  * @param Source        source buffer; set to NULL if you just have a C-String
2273  * @param PlainIn       Plain-C string to append; set to NULL if unused
2274  */
2275 void StrMsgEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2276 {
2277         const char *aptr, *eiptr;
2278         char *tptr, *eptr;
2279         long len;
2280
2281         if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2282                 return ;
2283
2284         if (PlainIn != NULL) {
2285                 aptr = PlainIn;
2286                 len = strlen(PlainIn);
2287                 eiptr = aptr + len;
2288         }
2289         else {
2290                 aptr = Source->buf;
2291                 eiptr = aptr + Source->BufUsed;
2292                 len = Source->BufUsed;
2293         }
2294
2295         if (len == 0) 
2296                 return;
2297
2298         eptr = Target->buf + Target->BufSize - 8; 
2299         tptr = Target->buf + Target->BufUsed;
2300         
2301         while (aptr < eiptr){
2302                 if(tptr >= eptr) {
2303                         IncreaseBuf(Target, 1, -1);
2304                         eptr = Target->buf + Target->BufSize - 8; 
2305                         tptr = Target->buf + Target->BufUsed;
2306                 }
2307                
2308                 if (*aptr == '\n') {
2309                         *tptr = ' ';
2310                         Target->BufUsed++;
2311                 }
2312                 else if (*aptr == '\r') {
2313                         *tptr = ' ';
2314                         Target->BufUsed++;
2315                 }
2316                 else if (*aptr == '\'') {
2317                         *(tptr++) = '&';
2318                         *(tptr++) = '#';
2319                         *(tptr++) = '3';
2320                         *(tptr++) = '9';
2321                         *tptr = ';';
2322                         Target->BufUsed += 5;
2323                 } else {
2324                         *tptr = *aptr;
2325                         Target->BufUsed++;
2326                 }
2327                 tptr++; aptr++;
2328         }
2329         *tptr = '\0';
2330 }
2331
2332
2333
2334 /**
2335  * @ingroup StrBuf_DeEnCoder
2336  * @brief Append a string, escaping characters which have meaning in ICAL.  
2337  * [\n,] 
2338  * @param Target        target buffer
2339  * @param Source        source buffer; set to NULL if you just have a C-String
2340  * @param PlainIn       Plain-C string to append; set to NULL if unused
2341  */
2342 void StrIcalEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2343 {
2344         const char *aptr, *eiptr;
2345         char *tptr, *eptr;
2346         long len;
2347
2348         if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2349                 return ;
2350
2351         if (PlainIn != NULL) {
2352                 aptr = PlainIn;
2353                 len = strlen(PlainIn);
2354                 eiptr = aptr + len;
2355         }
2356         else {
2357                 aptr = Source->buf;
2358                 eiptr = aptr + Source->BufUsed;
2359                 len = Source->BufUsed;
2360         }
2361
2362         if (len == 0) 
2363                 return;
2364
2365         eptr = Target->buf + Target->BufSize - 8; 
2366         tptr = Target->buf + Target->BufUsed;
2367         
2368         while (aptr < eiptr){
2369                 if(tptr + 3 >= eptr) {
2370                         IncreaseBuf(Target, 1, -1);
2371                         eptr = Target->buf + Target->BufSize - 8; 
2372                         tptr = Target->buf + Target->BufUsed;
2373                 }
2374                
2375                 if (*aptr == '\n') {
2376                         *tptr = '\\';
2377                         Target->BufUsed++;
2378                         tptr++;
2379                         *tptr = 'n';
2380                         Target->BufUsed++;
2381                 }
2382                 else if (*aptr == '\r') {
2383                         *tptr = '\\';
2384                         Target->BufUsed++;
2385                         tptr++;
2386                         *tptr = 'r';
2387                         Target->BufUsed++;
2388                 }
2389                 else if (*aptr == ',') {
2390                         *tptr = '\\';
2391                         Target->BufUsed++;
2392                         tptr++;
2393                         *tptr = ',';
2394                         Target->BufUsed++;
2395                 } else {
2396                         *tptr = *aptr;
2397                         Target->BufUsed++;
2398                 }
2399                 tptr++; aptr++;
2400         }
2401         *tptr = '\0';
2402 }
2403
2404 /**
2405  * @ingroup StrBuf_DeEnCoder
2406  * @brief Append a string, escaping characters which have meaning in JavaScript strings .  
2407  *
2408  * @param Target        target buffer
2409  * @param Source        source buffer; set to NULL if you just have a C-String
2410  * @param PlainIn       Plain-C string to append; set to NULL if unused
2411  * @returns size of result or -1
2412  */
2413 long StrECMAEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2414 {
2415         const char *aptr, *eiptr;
2416         char *bptr, *eptr;
2417         long len;
2418         int IsUtf8Sequence;
2419
2420         if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2421                 return -1;
2422
2423         if (PlainIn != NULL) {
2424                 aptr = PlainIn;
2425                 len = strlen(PlainIn);
2426                 eiptr = aptr + len;
2427         }
2428         else {
2429                 aptr = Source->buf;
2430                 eiptr = aptr + Source->BufUsed;
2431                 len = Source->BufUsed;
2432         }
2433
2434         if (len == 0) 
2435                 return -1;
2436
2437         bptr = Target->buf + Target->BufUsed;
2438         eptr = Target->buf + Target->BufSize - 7; /* our biggest unit to put in...  */
2439
2440         while (aptr < eiptr){
2441                 if(bptr >= eptr) {
2442                         IncreaseBuf(Target, 1, -1);
2443                         eptr = Target->buf + Target->BufSize - 7; /* our biggest unit to put in...  */
2444                         bptr = Target->buf + Target->BufUsed;
2445                 }
2446                 switch (*aptr) {
2447                 case '\n':
2448                         memcpy(bptr, HKEY("\\n"));
2449                         bptr += 2;
2450                         Target->BufUsed += 2;                           
2451                         break;
2452                 case '\r':
2453                         memcpy(bptr, HKEY("\\r"));
2454                         bptr += 2;
2455                         Target->BufUsed += 2;
2456                         break;
2457                 case '"':
2458                         *bptr = '\\';
2459                         bptr ++;
2460                         *bptr = '"';
2461                         bptr ++;
2462                         Target->BufUsed += 2;
2463                         break;
2464                 case '\\':
2465                         if ((*(aptr + 1) == 'u') &&
2466                             isxdigit(*(aptr + 2)) &&
2467                             isxdigit(*(aptr + 3)) &&
2468                             isxdigit(*(aptr + 4)) &&
2469                             isxdigit(*(aptr + 5)))
2470                         { /* oh, a unicode escaper. let it pass through. */
2471                                 memcpy(bptr, aptr, 6);
2472                                 aptr += 5;
2473                                 bptr +=6;
2474                                 Target->BufUsed += 6;
2475                         }
2476                         else 
2477                         {
2478                                 *bptr = '\\';
2479                                 bptr ++;
2480                                 *bptr = '\\';
2481                                 bptr ++;
2482                                 Target->BufUsed += 2;
2483                         }
2484                         break;
2485                 case '\b':
2486                         *bptr = '\\';
2487                         bptr ++;
2488                         *bptr = 'b';
2489                         bptr ++;
2490                         Target->BufUsed += 2;
2491                         break;
2492                 case '\f':
2493                         *bptr = '\\';
2494                         bptr ++;
2495                         *bptr = 'f';
2496                         bptr ++;
2497                         Target->BufUsed += 2;
2498                         break;
2499                 case '\t':
2500                         *bptr = '\\';
2501                         bptr ++;
2502                         *bptr = 't';
2503                         bptr ++;
2504                         Target->BufUsed += 2;
2505                         break;
2506                 default:
2507                         IsUtf8Sequence =  Ctdl_GetUtf8SequenceLength(aptr, eiptr);
2508                         while (IsUtf8Sequence > 0){
2509                                 *bptr = *aptr;
2510                                 Target->BufUsed ++;
2511                                 if (--IsUtf8Sequence)
2512                                         aptr++;
2513                                 bptr++;
2514                         }
2515                 }
2516                 aptr ++;
2517         }
2518         *bptr = '\0';
2519         if ((bptr == eptr - 1 ) && !IsEmptyStr(aptr) )
2520                 return -1;
2521         return Target->BufUsed;
2522 }
2523
2524 /**
2525  * @ingroup StrBuf_DeEnCoder
2526  * @brief Append a string, escaping characters which have meaning in HTML + json.  
2527  *
2528  * @param Target        target buffer
2529  * @param Source        source buffer; set to NULL if you just have a C-String
2530  * @param PlainIn       Plain-C string to append; set to NULL if unused
2531  * @param nbsp          If nonzero, spaces are converted to non-breaking spaces.
2532  * @param nolinebreaks  if set to 1, linebreaks are removed from the string.
2533  *                      if set to 2, linebreaks are replaced by &ltbr/&gt
2534  */
2535 long StrHtmlEcmaEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
2536 {
2537         const char *aptr, *eiptr;
2538         char *bptr, *eptr;
2539         long len;
2540         int IsUtf8Sequence = 0;
2541
2542         if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2543                 return -1;
2544
2545         if (PlainIn != NULL) {
2546                 aptr = PlainIn;
2547                 len = strlen(PlainIn);
2548                 eiptr = aptr + len;
2549         }
2550         else {
2551                 aptr = Source->buf;
2552                 eiptr = aptr + Source->BufUsed;
2553                 len = Source->BufUsed;
2554         }
2555
2556         if (len == 0) 
2557                 return -1;
2558
2559         bptr = Target->buf + Target->BufUsed;
2560         eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in...  */
2561
2562         while (aptr < eiptr){
2563                 if(bptr >= eptr) {
2564                         IncreaseBuf(Target, 1, -1);
2565                         eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in...  */
2566                         bptr = Target->buf + Target->BufUsed;
2567                 }
2568                 switch (*aptr) {
2569                 case '<':
2570                         memcpy(bptr, HKEY("&lt;"));
2571                         bptr += 4;
2572                         Target->BufUsed += 4;
2573                         break;
2574                 case '>':
2575                         memcpy(bptr, HKEY("&gt;"));
2576                         bptr += 4;
2577                         Target->BufUsed += 4;
2578                         break;
2579                 case '&':
2580                         memcpy(bptr, HKEY("&amp;"));
2581                         bptr += 5;
2582                         Target->BufUsed += 5;
2583                         break;
2584                 case LB:
2585                         *bptr = '<';
2586                         bptr ++;
2587                         Target->BufUsed ++;
2588                         break;
2589                 case RB:
2590                         *bptr = '>';
2591                         bptr ++;
2592                         Target->BufUsed ++;
2593                         break;
2594                 case '\n':
2595                         switch (nolinebreaks) {
2596                         case 1:
2597                                 *bptr='\0';     /* nothing */
2598                                 break;
2599                         case 2:
2600                                 memcpy(bptr, HKEY("&lt;br/&gt;"));
2601                                 bptr += 11;
2602                                 Target->BufUsed += 11;
2603                                 break;
2604                         default:
2605                                 memcpy(bptr, HKEY("\\n"));
2606                                 bptr += 2;
2607                                 Target->BufUsed += 2;                           
2608                         }
2609                         break;
2610                 case '\r':
2611                         switch (nolinebreaks) {
2612                         case 1:
2613                         case 2:
2614                                 *bptr='\0';     /* nothing */
2615                                 break;
2616                         default:
2617                                 memcpy(bptr, HKEY("\\r"));
2618                                 bptr += 2;
2619                                 Target->BufUsed += 2;
2620                                 break;
2621                         }
2622                         break;
2623                 case '"':
2624                 case QU:
2625                         *bptr = '\\';
2626                         bptr ++;
2627                         *bptr = '"';
2628                         bptr ++;
2629                         Target->BufUsed += 2;
2630                         break;
2631                 case '\\':
2632                         if ((*(aptr + 1) == 'u') &&
2633                             isxdigit(*(aptr + 2)) &&
2634                             isxdigit(*(aptr + 3)) &&
2635                             isxdigit(*(aptr + 4)) &&
2636                             isxdigit(*(aptr + 5)))
2637                         { /* oh, a unicode escaper. let it pass through. */
2638                                 memcpy(bptr, aptr, 6);
2639                                 aptr += 5;
2640                                 bptr +=6;
2641                                 Target->BufUsed += 6;
2642                         }
2643                         else 
2644                         {
2645                                 *bptr = '\\';
2646                                 bptr ++;
2647                                 *bptr = '\\';
2648                                 bptr ++;
2649                                 Target->BufUsed += 2;
2650                         }
2651                         break;
2652                 case '\b':
2653                         *bptr = '\\';
2654                         bptr ++;
2655                         *bptr = 'b';
2656                         bptr ++;
2657                         Target->BufUsed += 2;
2658                         break;
2659                 case '\f':
2660                         *bptr = '\\';
2661                         bptr ++;
2662                         *bptr = 'f';
2663                         bptr ++;
2664                         Target->BufUsed += 2;
2665                         break;
2666                 case '\t':
2667                         *bptr = '\\';
2668                         bptr ++;
2669                         *bptr = 't';
2670                         bptr ++;
2671                         Target->BufUsed += 2;
2672                         break;
2673                 case  32:
2674                         if (nbsp == 1) {
2675                                 memcpy(bptr, HKEY("&nbsp;"));
2676                                 bptr += 6;
2677                                 Target->BufUsed += 6;
2678                                 break;
2679                         }
2680                 default:
2681                         IsUtf8Sequence =  Ctdl_GetUtf8SequenceLength(aptr, eiptr);
2682                         while (IsUtf8Sequence > 0){
2683                                 *bptr = *aptr;
2684                                 Target->BufUsed ++;
2685                                 if (--IsUtf8Sequence)
2686                                         aptr++;
2687                                 bptr++;
2688                         }
2689                 }
2690                 aptr ++;
2691         }
2692         *bptr = '\0';
2693         if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
2694                 return -1;
2695         return Target->BufUsed;
2696 }
2697
2698
2699 /**
2700  * @ingroup StrBuf_DeEnCoder
2701  * @brief replace all non-Ascii characters by another
2702  * @param Buf buffer to inspect
2703  * @param repl charater to stamp over non ascii chars
2704  */
2705 void StrBufAsciify(StrBuf *Buf, const char repl)
2706 {
2707         long offset;
2708
2709         for (offset = 0; offset < Buf->BufUsed; offset ++)
2710                 if (!isascii(Buf->buf[offset]))
2711                         Buf->buf[offset] = repl;
2712         
2713 }
2714
2715 /**
2716  * @ingroup StrBuf_DeEnCoder
2717  * @brief unhide special chars hidden to the HTML escaper
2718  * @param target buffer to put the unescaped string in
2719  * @param source buffer to unescape
2720  */
2721 void StrBufEUid_unescapize(StrBuf *target, const StrBuf *source) 
2722 {
2723         int a, b, len;
2724         char hex[3];
2725
2726         if ((source == NULL) || (target == NULL) || (target->buf == NULL))
2727         {
2728                 return;
2729         }
2730
2731         if (target != NULL)
2732                 FlushStrBuf(target);
2733
2734         len = source->BufUsed;
2735         for (a = 0; a < len; ++a) {
2736                 if (target->BufUsed >= target->BufSize)
2737                         IncreaseBuf(target, 1, -1);
2738
2739                 if (source->buf[a] == '=') {
2740                         hex[0] = source->buf[a + 1];
2741                         hex[1] = source->buf[a + 2];
2742                         hex[2] = 0;
2743                         b = 0;
2744                         sscanf(hex, "%02x", &b);
2745                         target->buf[target->BufUsed] = b;
2746                         target->buf[++target->BufUsed] = 0;
2747                         a += 2;
2748                 }
2749                 else {
2750                         target->buf[target->BufUsed] = source->buf[a];
2751                         target->buf[++target->BufUsed] = 0;
2752                 }
2753         }
2754 }
2755
2756
2757 /**
2758  * @ingroup StrBuf_DeEnCoder
2759  * @brief hide special chars from the HTML escapers and friends
2760  * @param target buffer to put the escaped string in
2761  * @param source buffer to escape
2762  */
2763 void StrBufEUid_escapize(StrBuf *target, const StrBuf *source) 
2764 {
2765         int i, len;
2766
2767         if (target != NULL)
2768                 FlushStrBuf(target);
2769
2770         if ((source == NULL) || (target == NULL) || (target->buf == NULL))
2771         {
2772                 return;
2773         }
2774
2775         len = source->BufUsed;
2776         for (i=0; i<len; ++i) {
2777                 if (target->BufUsed + 4 >= target->BufSize)
2778                         IncreaseBuf(target, 1, -1);
2779                 if ( (isalnum(source->buf[i])) || 
2780                      (source->buf[i]=='-') || 
2781                      (source->buf[i]=='_') ) {
2782                         target->buf[target->BufUsed++] = source->buf[i];
2783                 }
2784                 else {
2785                         sprintf(&target->buf[target->BufUsed], 
2786                                 "=%02X", 
2787                                 (0xFF &source->buf[i]));
2788                         target->BufUsed += 3;
2789                 }
2790         }
2791         target->buf[target->BufUsed + 1] = '\0';
2792 }
2793
2794
2795 /*******************************************************************************
2796  *                      Quoted Printable de/encoding                           *
2797  *******************************************************************************/
2798
2799 /**
2800  * @ingroup StrBuf_DeEnCoder
2801  * @brief decode a buffer from base 64 encoding; destroys original
2802  * @param Buf Buffor to transform
2803  */
2804 int StrBufDecodeBase64(StrBuf *Buf)
2805 {
2806         char *xferbuf;
2807         size_t siz;
2808
2809         if (Buf == NULL)
2810                 return -1;
2811
2812         xferbuf = (char*) malloc(Buf->BufSize);
2813         if (xferbuf == NULL)
2814                 return -1;
2815
2816         *xferbuf = '\0';
2817         siz = CtdlDecodeBase64(xferbuf,
2818                                Buf->buf,
2819                                Buf->BufUsed);
2820         free(Buf->buf);
2821         Buf->buf = xferbuf;
2822         Buf->BufUsed = siz;
2823         return siz;
2824 }
2825
2826 /**
2827  * @ingroup StrBuf_DeEnCoder
2828  * @brief decode a buffer from base 64 encoding; destroys original
2829  * @param Buf Buffor to transform
2830  */
2831 int StrBufDecodeHex(StrBuf *Buf)
2832 {
2833         unsigned int ch;
2834         char *pch, *pche, *pchi;
2835
2836         if (Buf == NULL) return -1;
2837
2838         pch = pchi = Buf->buf;
2839         pche = pch + Buf->BufUsed;
2840
2841         while (pchi < pche){
2842                 ch = decode_hex(pchi);
2843                 *pch = ch;
2844                 pch ++;
2845                 pchi += 2;
2846         }
2847
2848         *pch = '\0';
2849         Buf->BufUsed = pch - Buf->buf;
2850         return Buf->BufUsed;
2851 }
2852
2853 /**
2854  * @ingroup StrBuf_DeEnCoder
2855  * @brief replace all chars >0x20 && < 0x7F with Mute
2856  * @param Mute char to put over invalid chars
2857  * @param Buf Buffor to transform
2858  */
2859 int StrBufSanitizeAscii(StrBuf *Buf, const char Mute)
2860 {
2861         unsigned char *pch;
2862
2863         if (Buf == NULL) return -1;
2864         pch = (unsigned char *)Buf->buf;
2865         while (pch < (unsigned char *)Buf->buf + Buf->BufUsed) {
2866                 if ((*pch < 0x20) || (*pch > 0x7F))
2867                         *pch = Mute;
2868                 pch ++;
2869         }
2870         return Buf->BufUsed;
2871 }
2872
2873
2874 /**
2875  * @ingroup StrBuf_DeEnCoder
2876  * @brief remove escaped strings from i.e. the url string (like %20 for blanks)
2877  * @param Buf Buffer to translate
2878  * @param StripBlanks Reduce several blanks to one?
2879  */
2880 long StrBufUnescape(StrBuf *Buf, int StripBlanks)
2881 {
2882         int a, b;
2883         char hex[3];
2884         long len;
2885
2886         if (Buf == NULL)
2887                 return -1;
2888
2889         while ((Buf->BufUsed > 0) && (isspace(Buf->buf[Buf->BufUsed - 1]))){
2890                 Buf->buf[Buf->BufUsed - 1] = '\0';
2891                 Buf->BufUsed --;
2892         }
2893
2894         a = 0; 
2895         while (a < Buf->BufUsed) {
2896                 if (Buf->buf[a] == '+')
2897                         Buf->buf[a] = ' ';
2898                 else if (Buf->buf[a] == '%') {
2899                         /* don't let % chars through, rather truncate the input. */
2900                         if (a + 2 > Buf->BufUsed) {
2901                                 Buf->buf[a] = '\0';
2902                                 Buf->BufUsed = a;
2903                         }
2904                         else {                  
2905                                 hex[0] = Buf->buf[a + 1];
2906                                 hex[1] = Buf->buf[a + 2];
2907                                 hex[2] = 0;
2908                                 b = 0;
2909                                 sscanf(hex, "%02x", &b);
2910                                 Buf->buf[a] = (char) b;
2911                                 len = Buf->BufUsed - a - 2;
2912                                 if (len > 0)
2913                                         memmove(&Buf->buf[a + 1], &Buf->buf[a + 3], len);
2914                         
2915                                 Buf->BufUsed -=2;
2916                         }
2917                 }
2918                 a++;
2919         }
2920         return a;
2921 }
2922
2923
2924 /**
2925  * @ingroup StrBuf_DeEnCoder
2926  * @brief       RFC2047-encode a header field if necessary.
2927  *              If no non-ASCII characters are found, the string
2928  *              will be copied verbatim without encoding.
2929  *
2930  * @param       target          Target buffer.
2931  * @param       source          Source string to be encoded.
2932  * @returns     encoded length; -1 if non success.
2933  */
2934 int StrBufRFC2047encode(StrBuf **target, const StrBuf *source)
2935 {
2936         const char headerStr[] = "=?UTF-8?Q?";
2937         int need_to_encode = 0;
2938         int i = 0;
2939         unsigned char ch;
2940
2941         if ((source == NULL) || 
2942             (target == NULL))
2943             return -1;
2944
2945         while ((i < source->BufUsed) &&
2946                (!IsEmptyStr (&source->buf[i])) &&
2947                (need_to_encode == 0)) {
2948                 if (((unsigned char) source->buf[i] < 32) || 
2949                     ((unsigned char) source->buf[i] > 126)) {
2950                         need_to_encode = 1;
2951                 }
2952                 i++;
2953         }
2954
2955         if (!need_to_encode) {
2956                 if (*target == NULL) {
2957                         *target = NewStrBufPlain(source->buf, source->BufUsed);
2958                 }
2959                 else {
2960                         FlushStrBuf(*target);
2961                         StrBufAppendBuf(*target, source, 0);
2962                 }
2963                 if (*target != 0)
2964                         return (*target)->BufUsed;
2965                 else
2966                         return 0;
2967         }
2968         if (*target == NULL)
2969                 *target = NewStrBufPlain(NULL, sizeof(headerStr) + source->BufUsed * 2);
2970         else if (sizeof(headerStr) + source->BufUsed >= (*target)->BufSize)
2971                 IncreaseBuf(*target, sizeof(headerStr) + source->BufUsed, 0);
2972         memcpy ((*target)->buf, headerStr, sizeof(headerStr) - 1);
2973         (*target)->BufUsed = sizeof(headerStr) - 1;
2974         for (i=0; (i < source->BufUsed); ++i) {
2975                 if ((*target)->BufUsed + 4 >= (*target)->BufSize)
2976                         IncreaseBuf(*target, 1, 0);
2977                 ch = (unsigned char) source->buf[i];
2978                 if ((ch  <  32) || 
2979                     (ch  > 126) || 
2980                     (ch == '=') ||
2981                     (ch == '?') ||
2982                     (ch == '_') ||
2983                     (ch == '[') ||
2984                     (ch == ']')   )
2985                 {
2986                         sprintf(&(*target)->buf[(*target)->BufUsed], "=%02X", ch);
2987                         (*target)->BufUsed += 3;
2988                 }
2989                 else {
2990                         if (ch == ' ')
2991                                 (*target)->buf[(*target)->BufUsed] = '_';
2992                         else
2993                                 (*target)->buf[(*target)->BufUsed] = ch;
2994                         (*target)->BufUsed++;
2995                 }
2996         }
2997         
2998         if ((*target)->BufUsed + 4 >= (*target)->BufSize)
2999                 IncreaseBuf(*target, 1, 0);
3000
3001         (*target)->buf[(*target)->BufUsed++] = '?';
3002         (*target)->buf[(*target)->BufUsed++] = '=';
3003         (*target)->buf[(*target)->BufUsed] = '\0';
3004         return (*target)->BufUsed;;
3005 }
3006
3007 /**
3008  * @ingroup StrBuf_DeEnCoder
3009  * @brief       Quoted-Printable encode a message; make it < 80 columns width.
3010  * @param       source          Source string to be encoded.
3011  * @returns     buffer with encoded message.
3012  */
3013 StrBuf *StrBufRFC2047encodeMessage(const StrBuf *EncodeMe)
3014 {
3015         StrBuf *OutBuf;
3016         char *Optr, *OEptr;
3017         const char *ptr, *eptr;
3018         unsigned char ch;
3019         int LinePos;
3020
3021         OutBuf = NewStrBufPlain(NULL, StrLength(EncodeMe) * 4);
3022         Optr = OutBuf->buf;
3023         OEptr = OutBuf->buf + OutBuf->BufSize;
3024         ptr = EncodeMe->buf;
3025         eptr = EncodeMe->buf + EncodeMe->BufUsed;
3026         LinePos = 0;
3027
3028         while (ptr < eptr)
3029         {
3030                 if (Optr + 4 >= OEptr)
3031                 {
3032                         long Offset;
3033                         Offset = Optr - OutBuf->buf;
3034                         OutBuf->BufUsed = Optr - OutBuf->buf;
3035                         IncreaseBuf(OutBuf, 1, 0);
3036                         Optr = OutBuf->buf + Offset;
3037                         OEptr = OutBuf->buf + OutBuf->BufSize;
3038                 }
3039                 if (*ptr == '\r')
3040                 {
3041                         /* ignore carriage returns */
3042                         ptr ++;
3043                 }
3044                 else if (*ptr == '\n') {
3045                         /* hard line break */
3046                         memcpy(Optr, HKEY("=0A"));
3047                         Optr += 3;
3048                         LinePos += 3;
3049                         ptr ++;
3050                 }
3051                 else if (( (*ptr >= 32) && (*ptr <= 60) ) ||
3052                          ( (*ptr >= 62) && (*ptr <= 126) ))
3053                 {
3054                         *Optr = *ptr;
3055                         Optr ++;
3056                         ptr ++;
3057                         LinePos ++;
3058                 }
3059                 else {
3060                         ch = *ptr;
3061                         *Optr = '=';
3062                         Optr ++;
3063                         *Optr = HexList[ch][0];
3064                         Optr ++;
3065                         *Optr = HexList[ch][1];
3066                         Optr ++;
3067                         LinePos += 3;
3068                         ptr ++;
3069                 }
3070
3071                 if (LinePos > 72) {
3072                         /* soft line break */
3073                         if (isspace(*(Optr - 1))) {
3074                                 ch = *(Optr - 1);
3075                                 Optr --;
3076                                 *Optr = '=';
3077                                 Optr ++;
3078                                 *Optr = HexList[ch][0];
3079                                 Optr ++;
3080                                 *Optr = HexList[ch][1];
3081                                 Optr ++;
3082                                 LinePos += 3;
3083                         }
3084                         *Optr = '=';
3085                         Optr ++;
3086                         *Optr = '\n';
3087                         Optr ++;
3088                         LinePos = 0;
3089                 }
3090         }
3091         *Optr = '\0';
3092         OutBuf->BufUsed = Optr - OutBuf->buf;
3093
3094         return OutBuf;
3095 }
3096
3097
3098 static void AddRecipient(StrBuf *Target, 
3099                          StrBuf *UserName, 
3100                          StrBuf *EmailAddress, 
3101                          StrBuf *EncBuf)
3102 {
3103         int QuoteMe = 0;
3104
3105         if (StrLength(Target) > 0) StrBufAppendBufPlain(Target, HKEY(", "), 0);
3106         if (strchr(ChrPtr(UserName), ',') != NULL) QuoteMe = 1;
3107
3108         if (QuoteMe)  StrBufAppendBufPlain(Target, HKEY("\""), 0);
3109         StrBufRFC2047encode(&EncBuf, UserName);
3110         StrBufAppendBuf(Target, EncBuf, 0);
3111         if (QuoteMe)  StrBufAppendBufPlain(Target, HKEY("\" "), 0);
3112         else          StrBufAppendBufPlain(Target, HKEY(" "), 0);
3113
3114         if (StrLength(EmailAddress) > 0){
3115                 StrBufAppendBufPlain(Target, HKEY("<"), 0);
3116                 StrBufAppendBuf(Target, EmailAddress, 0); /* TODO: what about IDN???? */
3117                 StrBufAppendBufPlain(Target, HKEY(">"), 0);
3118         }
3119 }
3120
3121
3122 /**
3123  * \brief QP encode parts of an email TO/CC/BCC vector, and strip/filter invalid parts
3124  * \param Recp Source list of email recipients
3125  * \param UserName Temporary buffer for internal use; Please provide valid buffer.
3126  * \param EmailAddress Temporary buffer for internal use; Please provide valid buffer.
3127  * \param EncBuf Temporary buffer for internal use; Please provide valid buffer.
3128  * \returns encoded & sanitized buffer with the contents of Recp; Caller owns this memory.
3129  */
3130 StrBuf *StrBufSanitizeEmailRecipientVector(const StrBuf *Recp, 
3131                                            StrBuf *UserName, 
3132                                            StrBuf *EmailAddress,
3133                                            StrBuf *EncBuf)
3134 {
3135         StrBuf *Target;
3136         const char *pch, *pche;
3137         const char *UserStart, *UserEnd, *EmailStart, *EmailEnd, *At;
3138
3139         if ((Recp == NULL) || (StrLength(Recp) == 0))
3140                 return NULL;
3141
3142         pch = ChrPtr(Recp);
3143         pche = pch + StrLength(Recp);
3144
3145         if (!CheckEncode(pch, -1, pche))
3146                 return NewStrBufDup(Recp);
3147
3148         Target = NewStrBufPlain(NULL, StrLength(Recp));
3149
3150         while ((pch != NULL) && (pch < pche))
3151         {
3152                 while (isspace(*pch)) pch++;
3153                 UserEnd = EmailStart = EmailEnd = NULL;
3154                 
3155                 if ((*pch == '"') || (*pch == '\'')) {
3156                         UserStart = pch + 1;
3157                         
3158                         UserEnd = strchr(UserStart, *pch);
3159                         if (UserEnd == NULL) 
3160                                 break; ///TODO: Userfeedback??
3161                         EmailStart = UserEnd + 1;
3162                         while (isspace(*EmailStart))
3163                                 EmailStart++;
3164                         if (UserEnd == UserStart) {
3165                                 UserStart = UserEnd = NULL;
3166                         }
3167                         
3168                         if (*EmailStart == '<') {
3169                                 EmailStart++;
3170                                 EmailEnd = strchr(EmailStart, '>');
3171                                 if (EmailEnd == NULL)
3172                                         EmailEnd = strchr(EmailStart, ',');
3173                                 
3174                         }
3175                         else {
3176                                 EmailEnd = strchr(EmailStart, ',');
3177                         }
3178                         if (EmailEnd == NULL)
3179                                 EmailEnd = pche;
3180                         pch = EmailEnd + 1;
3181                 }
3182                 else {
3183                         int gt = 0;
3184                         UserStart = pch;
3185                         EmailEnd = strchr(UserStart, ',');
3186                         if (EmailEnd == NULL) {
3187                                 EmailEnd = strchr(pch, '>');
3188                                 pch = NULL;
3189                                 if (EmailEnd != NULL) {
3190                                         gt = 1;
3191                                 }
3192                                 else {
3193                                         EmailEnd = pche;
3194                                 }
3195                         }
3196                         else {
3197
3198                                 pch = EmailEnd + 1;
3199                                 while ((EmailEnd > UserStart) && !gt &&
3200                                        ((*EmailEnd == ',') ||
3201                                         (*EmailEnd == '>') ||
3202                                         (isspace(*EmailEnd))))
3203                                 {
3204                                         if (*EmailEnd == '>')
3205                                                 gt = 1;
3206                                         else 
3207                                                 EmailEnd--;
3208                                 }
3209                                 if (EmailEnd == UserStart)
3210                                         break;
3211                         }
3212                         if (gt) {
3213                                 EmailStart = strchr(UserStart, '<');
3214                                 if ((EmailStart == NULL) || (EmailStart > EmailEnd))
3215                                         break;
3216                                 UserEnd = EmailStart;
3217
3218                                 while ((UserEnd > UserStart) && 
3219                                        isspace (*(UserEnd - 1)))
3220                                         UserEnd --;
3221                                 EmailStart ++;
3222                                 if (UserStart >= UserEnd)
3223                                         UserStart = UserEnd = NULL;
3224                         }
3225                         else { /* this is a local recipient... no domain, just a realname */
3226                                 EmailStart = UserStart;
3227                                 At = strchr(EmailStart, '@');
3228                                 if (At == NULL) {
3229                                         UserEnd = EmailEnd;
3230                                         EmailEnd = NULL;
3231                                 }
3232                                 else {
3233                                         EmailStart = UserStart;
3234                                         UserStart = NULL;
3235                                 }
3236                         }
3237                 }
3238
3239                 if ((UserStart != NULL) && (UserEnd != NULL))
3240                         StrBufPlain(UserName, UserStart, UserEnd - UserStart);
3241                 else if ((UserStart != NULL) && (UserEnd == NULL))
3242                         StrBufPlain(UserName, UserStart, UserEnd - UserStart);
3243                 else
3244                         FlushStrBuf(UserName);
3245
3246                 if ((EmailStart != NULL) && (EmailEnd != NULL))
3247                         StrBufPlain(EmailAddress, EmailStart, EmailEnd - EmailStart);
3248                 else if ((EmailStart != NULL) && (EmailEnd == NULL))
3249                         StrBufPlain(EmailAddress, EmailStart, EmailEnd - pche);
3250                 else 
3251                         FlushStrBuf(EmailAddress);
3252
3253                 AddRecipient(Target, UserName, EmailAddress, EncBuf);
3254
3255                 if (pch == NULL)
3256                         break;
3257                 
3258                 if ((pch != NULL) && (*pch == ','))
3259                         pch ++;
3260                 if (pch != NULL) while (isspace(*pch))
3261                         pch ++;
3262         }
3263         return Target;
3264 }
3265
3266
3267 /**
3268  * @ingroup StrBuf
3269  * @brief replaces all occurances of 'search' by 'replace'
3270  * @param buf Buffer to modify
3271  * @param search character to search
3272  * @param replace character to replace search by
3273  */
3274 void StrBufReplaceChars(StrBuf *buf, char search, char replace)
3275 {
3276         long i;
3277         if (buf == NULL)
3278                 return;
3279         for (i=0; i<buf->BufUsed; i++)
3280                 if (buf->buf[i] == search)
3281                         buf->buf[i] = replace;
3282
3283 }
3284
3285 /**
3286  * @ingroup StrBuf
3287  * @brief removes all \\r s from the string, or replaces them with \n if its not a combination of both.
3288  * @param buf Buffer to modify
3289  */
3290 void StrBufToUnixLF(StrBuf *buf)
3291 {
3292         char *pche, *pchS, *pchT;
3293         if (buf == NULL)
3294                 return;
3295
3296         pche = buf->buf + buf->BufUsed;
3297         pchS = pchT = buf->buf;
3298         while (pchS < pche)
3299         {
3300                 if (*pchS == '\r')
3301                 {
3302                         pchS ++;
3303                         if (*pchS != '\n') {
3304                                 *pchT = '\n';
3305                                 pchT++;
3306                         }
3307                 }
3308                 *pchT = *pchS;
3309                 pchT++; pchS++;
3310         }
3311         *pchT = '\0';
3312         buf->BufUsed = pchT - buf->buf;
3313 }
3314
3315
3316 /*******************************************************************************
3317  *                 Iconv Wrapper; RFC822 de/encoding                           *
3318  *******************************************************************************/
3319
3320 /**
3321  * @ingroup StrBuf_DeEnCoder
3322  * @brief Wrapper around iconv_open()
3323  * Our version adds aliases for non-standard Microsoft charsets
3324  * such as 'MS950', aliasing them to names like 'CP950'
3325  *
3326  * @param tocode        Target encoding
3327  * @param fromcode      Source encoding
3328  * @param pic           anonimized pointer to iconv struct
3329  */
3330 void  ctdl_iconv_open(const char *tocode, const char *fromcode, void *pic)
3331 {
3332 #ifdef HAVE_ICONV
3333         iconv_t ic = (iconv_t)(-1) ;
3334         ic = iconv_open(tocode, fromcode);
3335         if (ic == (iconv_t)(-1) ) {
3336                 char alias_fromcode[64];
3337                 if ( (strlen(fromcode) == 5) && (!strncasecmp(fromcode, "MS", 2)) ) {
3338                         safestrncpy(alias_fromcode, fromcode, sizeof alias_fromcode);
3339                         alias_fromcode[0] = 'C';
3340                         alias_fromcode[1] = 'P';
3341                         ic = iconv_open(tocode, alias_fromcode);
3342                 }
3343         }
3344         *(iconv_t *)pic = ic;
3345 #endif
3346 }
3347
3348
3349 /**
3350  * @ingroup StrBuf_DeEnCoder
3351  * @brief find one chunk of a RFC822 encoded string
3352  * @param Buffer where to search
3353  * @param bptr where to start searching
3354  * @returns found position, NULL if none.
3355  */
3356 static inline const char *FindNextEnd (const StrBuf *Buf, const char *bptr)
3357 {
3358         const char * end;
3359         /* Find the next ?Q? */
3360         if (Buf->BufUsed - (bptr - Buf->buf)  < 6)
3361                 return NULL;
3362
3363         end = strchr(bptr + 2, '?');
3364
3365         if (end == NULL)
3366                 return NULL;
3367
3368         if ((Buf->BufUsed - (end - Buf->buf) > 3) &&
3369             (((*(end + 1) == 'B') || (*(end + 1) == 'Q')) ||
3370              ((*(end + 1) == 'b') || (*(end + 1) == 'q'))) && 
3371             (*(end + 2) == '?')) {
3372                 /* skip on to the end of the cluster, the next ?= */
3373                 end = strstr(end + 3, "?=");
3374         }
3375         else
3376                 /* sort of half valid encoding, try to find an end. */
3377                 end = strstr(bptr, "?=");
3378         return end;
3379 }
3380
3381
3382
3383 /**
3384  * @ingroup StrBuf_DeEnCoder
3385  * @brief convert one buffer according to the preselected iconv pointer PIC
3386  * @param ConvertBuf buffer we need to translate
3387  * @param TmpBuf To share a workbuffer over several iterations. prepare to have it filled with useless stuff afterwards.
3388  * @param pic Pointer to the iconv-session Object
3389  */
3390 void StrBufConvert(StrBuf *ConvertBuf, StrBuf *TmpBuf, void *pic)
3391 {
3392 #ifdef HAVE_ICONV
3393         long trycount = 0;
3394         size_t siz;
3395         iconv_t ic;
3396         char *ibuf;                     /**< Buffer of characters to be converted */
3397         char *obuf;                     /**< Buffer for converted characters */
3398         size_t ibuflen;                 /**< Length of input buffer */
3399         size_t obuflen;                 /**< Length of output buffer */
3400
3401
3402         if ((ConvertBuf == NULL) || (TmpBuf == NULL))
3403                 return;
3404
3405         /* since we're converting to utf-8, one glyph may take up to 6 bytes */
3406         if (ConvertBuf->BufUsed * 6 >= TmpBuf->BufSize)
3407                 IncreaseBuf(TmpBuf, 0, ConvertBuf->BufUsed * 6);
3408 TRYAGAIN:
3409         ic = *(iconv_t*)pic;
3410         ibuf = ConvertBuf->buf;
3411         ibuflen = ConvertBuf->BufUsed;
3412         obuf = TmpBuf->buf;
3413         obuflen = TmpBuf->BufSize;
3414         
3415         siz = iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
3416
3417         if (siz < 0) {
3418                 if (errno == E2BIG) {
3419                         trycount ++;                    
3420                         IncreaseBuf(TmpBuf, 0, 0);
3421                         if (trycount < 5) 
3422                                 goto TRYAGAIN;
3423
3424                 }
3425                 else if (errno == EILSEQ){ 
3426                         /* hm, invalid utf8 sequence... what to do now? */
3427                         /* An invalid multibyte sequence has been encountered in the input */
3428                 }
3429                 else if (errno == EINVAL) {
3430                         /* An incomplete multibyte sequence has been encountered in the input. */
3431                 }
3432
3433                 FlushStrBuf(TmpBuf);
3434         }
3435         else {
3436                 TmpBuf->BufUsed = TmpBuf->BufSize - obuflen;
3437                 TmpBuf->buf[TmpBuf->BufUsed] = '\0';
3438                 
3439                 /* little card game: wheres the red lady? */
3440                 SwapBuffers(ConvertBuf, TmpBuf);
3441                 FlushStrBuf(TmpBuf);
3442         }
3443 #endif
3444 }
3445
3446
3447 /**
3448  * @ingroup StrBuf_DeEnCoder
3449  * @brief catches one RFC822 encoded segment, and decodes it.
3450  * @param Target buffer to fill with result
3451  * @param DecodeMe buffer with stuff to process
3452  * @param SegmentStart points to our current segment in DecodeMe
3453  * @param SegmentEnd Points to the end of our current segment in DecodeMe
3454  * @param ConvertBuf Workbuffer shared between several iterations. Random content; needs to be valid
3455  * @param ConvertBuf2 Workbuffer shared between several iterations. Random content; needs to be valid
3456  * @param FoundCharset Characterset to default decoding to; if we find another we will overwrite it.
3457  */
3458 inline static void DecodeSegment(StrBuf *Target, 
3459                                  const StrBuf *DecodeMe, 
3460                                  const char *SegmentStart, 
3461                                  const char *SegmentEnd, 
3462                                  StrBuf *ConvertBuf,
3463                                  StrBuf *ConvertBuf2, 
3464                                  StrBuf *FoundCharset)
3465 {
3466         StrBuf StaticBuf;
3467         char charset[128];
3468         char encoding[16];
3469 #ifdef HAVE_ICONV
3470         iconv_t ic = (iconv_t)(-1);
3471 #else
3472         void *ic = NULL;
3473 #endif
3474         /* Now we handle foreign character sets properly encoded
3475          * in RFC2047 format.
3476          */
3477         StaticBuf.buf = (char*) SegmentStart; /*< it will just be read there... */
3478         StaticBuf.BufUsed = SegmentEnd - SegmentStart;
3479         StaticBuf.BufSize = DecodeMe->BufSize - (SegmentStart - DecodeMe->buf);
3480         extract_token(charset, SegmentStart, 1, '?', sizeof charset);
3481         if (FoundCharset != NULL) {
3482                 FlushStrBuf(FoundCharset);
3483                 StrBufAppendBufPlain(FoundCharset, charset, -1, 0);
3484         }
3485         extract_token(encoding, SegmentStart, 2, '?', sizeof encoding);
3486         StrBufExtract_token(ConvertBuf, &StaticBuf, 3, '?');
3487         
3488         *encoding = toupper(*encoding);
3489         if (*encoding == 'B') { /**< base64 */
3490                 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3491                         IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3492                 ConvertBuf2->BufUsed = CtdlDecodeBase64(ConvertBuf2->buf, 
3493                                                         ConvertBuf->buf, 
3494                                                         ConvertBuf->BufUsed);
3495         }
3496         else if (*encoding == 'Q') {    /**< quoted-printable */
3497                 long pos;
3498                 
3499                 pos = 0;
3500                 while (pos < ConvertBuf->BufUsed)
3501                 {
3502                         if (ConvertBuf->buf[pos] == '_') 
3503                                 ConvertBuf->buf[pos] = ' ';
3504                         pos++;
3505                 }
3506                 
3507                 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3508                         IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3509
3510                 ConvertBuf2->BufUsed = CtdlDecodeQuotedPrintable(
3511                         ConvertBuf2->buf, 
3512                         ConvertBuf->buf,
3513                         ConvertBuf->BufUsed);
3514         }
3515         else {
3516                 StrBufAppendBuf(ConvertBuf2, ConvertBuf, 0);
3517         }
3518 #ifdef HAVE_ICONV
3519         ctdl_iconv_open("UTF-8", charset, &ic);
3520         if (ic != (iconv_t)(-1) ) {             
3521 #endif
3522                 StrBufConvert(ConvertBuf2, ConvertBuf, &ic);
3523                 StrBufAppendBuf(Target, ConvertBuf2, 0);
3524 #ifdef HAVE_ICONV
3525                 iconv_close(ic);
3526         }
3527         else {
3528                 StrBufAppendBufPlain(Target, HKEY("(unreadable)"), 0);
3529         }
3530 #endif
3531 }
3532
3533 /**
3534  * @ingroup StrBuf_DeEnCoder
3535  * @brief Handle subjects with RFC2047 encoding such as: [deprecated old syntax!]
3536  * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3537  * @param Target where to put the decoded string to 
3538  * @param DecodeMe buffer with encoded string
3539  * @param DefaultCharset if we don't find one, which should we use?
3540  * @param FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string, 
3541  *        put it here for later use where no string might be known.
3542  */
3543 void StrBuf_RFC822_to_Utf8(StrBuf *Target, const StrBuf *DecodeMe, const StrBuf* DefaultCharset, StrBuf *FoundCharset)
3544 {
3545         StrBuf *ConvertBuf;
3546         StrBuf *ConvertBuf2;
3547         ConvertBuf = NewStrBufPlain(NULL, StrLength(DecodeMe));
3548         ConvertBuf2 = NewStrBufPlain(NULL, StrLength(DecodeMe));
3549         
3550         StrBuf_RFC822_2_Utf8(Target, 
3551                              DecodeMe, 
3552                              DefaultCharset, 
3553                              FoundCharset, 
3554                              ConvertBuf, 
3555                              ConvertBuf2);
3556         FreeStrBuf(&ConvertBuf);
3557         FreeStrBuf(&ConvertBuf2);
3558 }
3559
3560 /**
3561  * @ingroup StrBuf_DeEnCoder
3562  * @brief Handle subjects with RFC2047 encoding such as:
3563  * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3564  * @param Target where to put the decoded string to 
3565  * @param DecodeMe buffer with encoded string
3566  * @param DefaultCharset if we don't find one, which should we use?
3567  * @param FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string, 
3568  *        put it here for later use where no string might be known.
3569  * @param ConvertBuf workbuffer. feed in, you shouldn't care about its content.
3570  * @param ConvertBuf2 workbuffer. feed in, you shouldn't care about its content.
3571  */
3572 void StrBuf_RFC822_2_Utf8(StrBuf *Target, 
3573                           const StrBuf *DecodeMe, 
3574                           const StrBuf* DefaultCharset, 
3575                           StrBuf *FoundCharset, 
3576                           StrBuf *ConvertBuf, 
3577                           StrBuf *ConvertBuf2)
3578 {
3579         StrBuf *DecodedInvalidBuf = NULL;
3580         const StrBuf *DecodeMee = DecodeMe;
3581         const char *start, *end, *next, *nextend, *ptr = NULL;
3582 #ifdef HAVE_ICONV
3583         iconv_t ic = (iconv_t)(-1) ;
3584 #endif
3585         const char *eptr;
3586         int passes = 0;
3587         int i;
3588         int illegal_non_rfc2047_encoding = 0;
3589
3590
3591         if (DecodeMe == NULL)
3592                 return;
3593         /* Sometimes, badly formed messages contain strings which were simply
3594          *  written out directly in some foreign character set instead of
3595          *  using RFC2047 encoding.  This is illegal but we will attempt to
3596          *  handle it anyway by converting from a user-specified default
3597          *  charset to UTF-8 if we see any nonprintable characters.
3598          */
3599         
3600         for (i=0; i<DecodeMe->BufUsed; ++i) {
3601                 if ((DecodeMe->buf[i] < 32) || (DecodeMe->buf[i] > 126)) {
3602                         illegal_non_rfc2047_encoding = 1;
3603                         break;
3604                 }
3605         }
3606
3607         if ((illegal_non_rfc2047_encoding) &&
3608             (strcasecmp(ChrPtr(DefaultCharset), "UTF-8")) && 
3609             (strcasecmp(ChrPtr(DefaultCharset), "us-ascii")) )
3610         {
3611 #ifdef HAVE_ICONV
3612                 ctdl_iconv_open("UTF-8", ChrPtr(DefaultCharset), &ic);
3613                 if (ic != (iconv_t)(-1) ) {
3614                         DecodedInvalidBuf = NewStrBufDup(DecodeMe);
3615                         StrBufConvert(DecodedInvalidBuf, ConvertBuf, &ic);///TODO: don't void const?
3616                         DecodeMee = DecodedInvalidBuf;
3617                         iconv_close(ic);
3618                 }
3619 #endif
3620         }
3621
3622         /* pre evaluate the first pair */
3623         end = NULL;
3624         start = strstr(DecodeMee->buf, "=?");
3625         eptr = DecodeMee->buf + DecodeMee->BufUsed;
3626         if (start != NULL) 
3627                 end = FindNextEnd (DecodeMee, start + 2);
3628         else {
3629                 StrBufAppendBuf(Target, DecodeMee, 0);
3630                 FreeStrBuf(&DecodedInvalidBuf);
3631                 return;
3632         }
3633
3634
3635         if (start != DecodeMee->buf) {
3636                 long nFront;
3637                 
3638                 nFront = start - DecodeMee->buf;
3639                 StrBufAppendBufPlain(Target, DecodeMee->buf, nFront, 0);
3640         }
3641         /*
3642          * Since spammers will go to all sorts of absurd lengths to get their
3643          * messages through, there are LOTS of corrupt headers out there.
3644          * So, prevent a really badly formed RFC2047 header from throwing
3645          * this function into an infinite loop.
3646          */
3647         while ((start != NULL) && 
3648                (end != NULL) && 
3649                (start < eptr) && 
3650                (end < eptr) && 
3651                (passes < 20))
3652         {
3653                 passes++;
3654                 DecodeSegment(Target, 
3655                               DecodeMee, 
3656                               start, 
3657                               end, 
3658                               ConvertBuf,
3659                               ConvertBuf2,
3660                               FoundCharset);
3661                 
3662                 next = strstr(end, "=?");
3663                 nextend = NULL;
3664                 if ((next != NULL) && 
3665                     (next < eptr))
3666                         nextend = FindNextEnd(DecodeMee, next);
3667                 if (nextend == NULL)
3668                         next = NULL;
3669
3670                 /* did we find two partitions */
3671                 if ((next != NULL) && 
3672                     ((next - end) > 2))
3673                 {
3674                         ptr = end + 2;
3675                         while ((ptr < next) && 
3676                                (isspace(*ptr) ||
3677                                 (*ptr == '\r') ||
3678                                 (*ptr == '\n') || 
3679                                 (*ptr == '\t')))
3680                                 ptr ++;
3681                         /* 
3682                          * did we find a gab just filled with blanks?
3683                          * if not, copy its stuff over.
3684                          */
3685                         if (ptr != next)
3686                         {
3687                                 StrBufAppendBufPlain(Target, 
3688                                                      end + 2, 
3689                                                      next - end - 2,
3690                                                      0);
3691                         }
3692                 }
3693                 /* our next-pair is our new first pair now. */
3694                 ptr = end + 2;
3695                 start = next;
3696                 end = nextend;
3697         }
3698         end = ptr;
3699         nextend = DecodeMee->buf + DecodeMee->BufUsed;
3700         if ((end != NULL) && (end < nextend)) {
3701                 ptr = end;
3702                 while ( (ptr < nextend) &&
3703                         (isspace(*ptr) ||
3704                          (*ptr == '\r') ||
3705                          (*ptr == '\n') || 
3706                          (*ptr == '\t')))
3707                         ptr ++;
3708                 if (ptr < nextend)
3709                         StrBufAppendBufPlain(Target, end, nextend - end, 0);
3710         }
3711         FreeStrBuf(&DecodedInvalidBuf);
3712 }
3713
3714 /*******************************************************************************
3715  *                   Manipulating UTF-8 Strings                                *
3716  *******************************************************************************/
3717
3718 /**
3719  * @ingroup StrBuf
3720  * @brief evaluate the length of an utf8 special character sequence
3721  * @param Char the character to examine
3722  * @returns width of utf8 chars in bytes; if the sequence is broken 0 is returned; 1 if its simply ASCII.
3723  */
3724 static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE)
3725 {
3726         int n = 0;
3727         unsigned char test = (1<<7);
3728
3729         if ((*CharS & 0xC0) != 0xC0) 
3730                 return 1;
3731
3732         while ((n < 8) && 
3733                ((test & ((unsigned char)*CharS)) != 0)) 
3734         {
3735                 test = test >> 1;
3736                 n ++;
3737         }
3738         if ((n > 6) || ((CharE - CharS) < n))
3739                 n = 0;
3740         return n;
3741 }
3742
3743 /**
3744  * @ingroup StrBuf
3745  * @brief detect whether this char starts an utf-8 encoded char
3746  * @param Char character to inspect
3747  * @returns yes or no
3748  */
3749 static inline int Ctdl_IsUtf8SequenceStart(const char Char)
3750 {
3751 /** 11??.???? indicates an UTF8 Sequence. */
3752         return ((Char & 0xC0) == 0xC0);
3753 }
3754
3755 /**
3756  * @ingroup StrBuf
3757  * @brief measure the number of glyphs in an UTF8 string...
3758  * @param Buf string to measure
3759  * @returns the number of glyphs in Buf
3760  */
3761 long StrBuf_Utf8StrLen(StrBuf *Buf)
3762 {
3763         int n = 0;
3764         int m = 0;
3765         char *aptr, *eptr;
3766
3767         if ((Buf == NULL) || (Buf->BufUsed == 0))
3768                 return 0;
3769         aptr = Buf->buf;
3770         eptr = Buf->buf + Buf->BufUsed;
3771         while ((aptr < eptr) && (*aptr != '\0')) {
3772                 if (Ctdl_IsUtf8SequenceStart(*aptr)){
3773                         m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
3774                         while ((aptr < eptr) && (*aptr++ != '\0')&& (m-- > 0) );
3775                         n ++;
3776                 }
3777                 else {
3778                         n++;
3779                         aptr++;
3780                 }
3781         }
3782         return n;
3783 }
3784
3785 /**
3786  * @ingroup StrBuf
3787  * @brief cuts a string after maxlen glyphs
3788  * @param Buf string to cut to maxlen glyphs
3789  * @param maxlen how long may the string become?
3790  * @returns current length of the string
3791  */
3792 long StrBuf_Utf8StrCut(StrBuf *Buf, int maxlen)
3793 {
3794         char *aptr, *eptr;
3795         int n = 0, m = 0;
3796
3797         aptr = Buf->buf;
3798         eptr = Buf->buf + Buf->BufUsed;
3799         while ((aptr < eptr) && (*aptr != '\0')) {
3800                 if (Ctdl_IsUtf8SequenceStart(*aptr)){
3801                         m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
3802                         while ((*aptr++ != '\0') && (m-- > 0));
3803                         n ++;
3804                 }
3805                 else {
3806                         n++;
3807                         aptr++;
3808                 }
3809                 if (n > maxlen) {
3810                         *aptr = '\0';
3811                         Buf->BufUsed = aptr - Buf->buf;
3812                         return Buf->BufUsed;
3813                 }                       
3814         }
3815         return Buf->BufUsed;
3816
3817 }
3818
3819
3820
3821
3822
3823 /*******************************************************************************
3824  *                               wrapping ZLib                                 *
3825  *******************************************************************************/
3826
3827 #ifdef HAVE_ZLIB
3828 #define DEF_MEM_LEVEL 8 /*< memlevel??? */
3829 #define OS_CODE 0x03    /*< unix */
3830
3831 /**
3832  * @ingroup StrBuf_DeEnCoder
3833  * @brief uses the same calling syntax as compress2(), but it
3834  *   creates a stream compatible with HTTP "Content-encoding: gzip"
3835  * @param dest compressed buffer
3836  * @param destLen length of the compresed data 
3837  * @param source source to encode
3838  * @param sourceLen length of source to encode 
3839  * @param level compression level
3840  */
3841 int ZEXPORT compress_gzip(Bytef * dest,
3842                           size_t * destLen,
3843                           const Bytef * source,
3844                           uLong sourceLen,     
3845                           int level)
3846 {
3847         const int gz_magic[2] = { 0x1f, 0x8b }; /* gzip magic header */
3848
3849         /* write gzip header */
3850         snprintf((char *) dest, *destLen, 
3851                  "%c%c%c%c%c%c%c%c%c%c",
3852                  gz_magic[0], gz_magic[1], Z_DEFLATED,
3853                  0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /* xflags */ ,
3854                  OS_CODE);
3855
3856         /* normal deflate */
3857         z_stream stream;
3858         int err;
3859         stream.next_in = (Bytef *) source;
3860         stream.avail_in = (uInt) sourceLen;
3861         stream.next_out = dest + 10L;   // after header
3862         stream.avail_out = (uInt) * destLen;
3863         if ((uLong) stream.avail_out != *destLen)
3864                 return Z_BUF_ERROR;
3865
3866         stream.zalloc = (alloc_func) 0;
3867         stream.zfree = (free_func) 0;
3868         stream.opaque = (voidpf) 0;
3869
3870         err = deflateInit2(&stream, level, Z_DEFLATED, -MAX_WBITS,
3871                            DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
3872         if (err != Z_OK)
3873                 return err;
3874
3875         err = deflate(&stream, Z_FINISH);
3876         if (err != Z_STREAM_END) {
3877                 deflateEnd(&stream);
3878                 return err == Z_OK ? Z_BUF_ERROR : err;
3879         }
3880         *destLen = stream.total_out + 10L;
3881
3882         /* write CRC and Length */
3883         uLong crc = crc32(0L, source, sourceLen);
3884         int n;
3885         for (n = 0; n < 4; ++n, ++*destLen) {
3886                 dest[*destLen] = (int) (crc & 0xff);
3887                 crc >>= 8;
3888         }
3889         uLong len = stream.total_in;
3890         for (n = 0; n < 4; ++n, ++*destLen) {
3891                 dest[*destLen] = (int) (len & 0xff);
3892                 len >>= 8;
3893         }
3894         err = deflateEnd(&stream);
3895         return err;
3896 }
3897 #endif
3898
3899
3900 /**
3901  * @ingroup StrBuf_DeEnCoder
3902  * @brief compress the buffer with gzip
3903  * Attention! If you feed this a Const String, you must maintain the uncompressed buffer yourself!
3904  * @param Buf buffer whose content is to be gzipped
3905  */
3906 int CompressBuffer(StrBuf *Buf)
3907 {
3908 #ifdef HAVE_ZLIB
3909         char *compressed_data = NULL;
3910         size_t compressed_len, bufsize;
3911         int i = 0;
3912
3913         bufsize = compressed_len = Buf->BufUsed +  (Buf->BufUsed / 100) + 100;
3914         compressed_data = malloc(compressed_len);
3915         
3916         if (compressed_data == NULL)
3917                 return -1;
3918         /* Flush some space after the used payload so valgrind shuts up... */
3919         while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
3920                 Buf->buf[Buf->BufUsed + i++] = '\0';
3921         if (compress_gzip((Bytef *) compressed_data,
3922                           &compressed_len,
3923                           (Bytef *) Buf->buf,
3924                           (uLongf) Buf->BufUsed, Z_BEST_SPEED) == Z_OK) {
3925                 if (!Buf->ConstBuf)
3926                         free(Buf->buf);
3927                 Buf->buf = compressed_data;
3928                 Buf->BufUsed = compressed_len;
3929                 Buf->BufSize = bufsize;
3930                 /* Flush some space after the used payload so valgrind shuts up... */
3931                 i = 0;
3932                 while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
3933                         Buf->buf[Buf->BufUsed + i++] = '\0';
3934                 return 1;
3935         } else {
3936                 free(compressed_data);
3937         }
3938 #endif  /* HAVE_ZLIB */
3939         return 0;
3940 }
3941
3942 /*******************************************************************************
3943  *           File I/O; Callbacks to libevent                                   *
3944  *******************************************************************************/
3945
3946 long StrBuf_read_one_chunk_callback (int fd, short event, IOBuffer *FB)
3947 {
3948         long bufremain = 0;