war on BSD style curly braces
[citadel.git] / libcitadel / lib / stringbuf.c
1 /*
2  * Copyright (c) 1987-2018 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 #include "b64/cencode.h"
36 #include "b64/cdecode.h"
37
38 #ifdef HAVE_ICONV
39 #include <iconv.h>
40 #endif
41
42 #ifdef HAVE_BACKTRACE
43 #include <execinfo.h>
44 #endif
45
46 #ifdef UNDEF_MEMCPY
47 #undef memcpy
48 #endif
49
50 #ifdef HAVE_ZLIB
51 #include <zlib.h>
52 int ZEXPORT compress_gzip(Bytef * dest, size_t * destLen,
53                           const Bytef * source, uLong sourceLen, int level);
54 #endif
55 int BaseStrBufSize = 64;
56 int EnableSplice = 0;
57 int ZLibCompressionRatio = -1; /* defaults to 6 */
58 #ifdef HAVE_ZLIB
59 #define DEF_MEM_LEVEL 8 /*< memlevel??? */
60 #define OS_CODE 0x03    /*< unix */
61 const int gz_magic[2] = { 0x1f, 0x8b }; /* gzip magic header */
62 #endif
63
64 const char *StrBufNOTNULL = ((char*) NULL) - 1;
65
66 const char HexList[256][3] = {
67         "00","01","02","03","04","05","06","07","08","09","0A","0B","0C","0D","0E","0F",
68         "10","11","12","13","14","15","16","17","18","19","1A","1B","1C","1D","1E","1F",
69         "20","21","22","23","24","25","26","27","28","29","2A","2B","2C","2D","2E","2F",
70         "30","31","32","33","34","35","36","37","38","39","3A","3B","3C","3D","3E","3F",
71         "40","41","42","43","44","45","46","47","48","49","4A","4B","4C","4D","4E","4F",
72         "50","51","52","53","54","55","56","57","58","59","5A","5B","5C","5D","5E","5F",
73         "60","61","62","63","64","65","66","67","68","69","6A","6B","6C","6D","6E","6F",
74         "70","71","72","73","74","75","76","77","78","79","7A","7B","7C","7D","7E","7F",
75         "80","81","82","83","84","85","86","87","88","89","8A","8B","8C","8D","8E","8F",
76         "90","91","92","93","94","95","96","97","98","99","9A","9B","9C","9D","9E","9F",
77         "A0","A1","A2","A3","A4","A5","A6","A7","A8","A9","AA","AB","AC","AD","AE","AF",
78         "B0","B1","B2","B3","B4","B5","B6","B7","B8","B9","BA","BB","BC","BD","BE","BF",
79         "C0","C1","C2","C3","C4","C5","C6","C7","C8","C9","CA","CB","CC","CD","CE","CF",
80         "D0","D1","D2","D3","D4","D5","D6","D7","D8","D9","DA","DB","DC","DD","DE","DF",
81         "E0","E1","E2","E3","E4","E5","E6","E7","E8","E9","EA","EB","EC","ED","EE","EF",
82         "F0","F1","F2","F3","F4","F5","F6","F7","F8","F9","FA","FB","FC","FD","FE","FF"};
83
84 /**
85  * @defgroup StrBuf Stringbuffer, A class for manipulating strings with dynamic buffers
86  * StrBuf is a versatile class, aiding the handling of dynamic strings
87  *  * reduce de/reallocations
88  *  * reduce the need to remeasure it
89  *  * reduce scanning over the string (in @ref StrBuf_NextTokenizer "Tokenizers")
90  *  * allow asyncroneous IO for line and Blob based operations
91  *  * reduce the use of memove in those
92  *  * Quick filling in several operations with append functions
93  */
94
95 /**
96  * @defgroup StrBuf_DeConstructors Create/Destroy StrBufs
97  * @ingroup StrBuf
98  */
99
100 /**
101  * @defgroup StrBuf_Cast Cast operators to interact with char* based code
102  * @ingroup StrBuf
103  * use these operators to interfere with code demanding char*; 
104  * if you need to own the content, smash me. Avoid, since we loose the length information.
105  */
106
107 /**
108  * @defgroup StrBuf_Filler Create/Replace/Append Content into a StrBuf
109  * @ingroup StrBuf
110  * operations to get your Strings into a StrBuf, manipulating them, or appending
111  */
112 /**
113  * @defgroup StrBuf_NextTokenizer Fast tokenizer to pull tokens in sequence 
114  * @ingroup StrBuf
115  * Quick tokenizer; demands of the user to pull its tokens in sequence
116  */
117
118 /**
119  * @defgroup StrBuf_Tokenizer tokenizer Functions; Slow ones.
120  * @ingroup StrBuf
121  * versatile tokenizer; random access to tokens, but slower; Prefer the @ref StrBuf_NextTokenizer "Next Tokenizer"
122  */
123
124 /**
125  * @defgroup StrBuf_BufferedIO Buffered IO with Asynchroneous reads and no unneeded memmoves (the fast ones)
126  * @ingroup StrBuf
127  * File IO to fill StrBufs; Works with work-buffer shared across several calls;
128  * External Cursor to maintain the current read position inside of the buffer
129  * the non-fast ones will use memove to keep the start of the buffer the read buffer (which is slower) 
130  */
131
132 /**
133  * @defgroup StrBuf_IO FileIO; Prefer @ref StrBuf_BufferedIO
134  * @ingroup StrBuf
135  * Slow I/O; avoid.
136  */
137
138 /**
139  * @defgroup StrBuf_DeEnCoder functions to translate the contents of a buffer
140  * @ingroup StrBuf
141  * these functions translate the content of a buffer into another representation;
142  * some are combined Fillers and encoders
143  */
144
145 /**
146  * Private Structure for the Stringbuffer
147  */
148 struct StrBuf {
149         char *buf;         /**< the pointer to the dynamic buffer */
150         long BufSize;      /**< how many spcae do we optain */
151         long BufUsed;      /**< StNumber of Chars used excluding the trailing \\0 */
152         int ConstBuf;      /**< are we just a wrapper arround a static buffer and musn't we be changed? */
153 #ifdef SIZE_DEBUG
154         long nIncreases;   /**< for profiling; cound how many times we needed more */
155         char bt [SIZ];     /**< Stacktrace of last increase */
156         char bt_lastinc [SIZ]; /**< How much did we increase last time? */
157 #endif
158 };
159
160
161 static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE);
162 static inline int Ctdl_IsUtf8SequenceStart(const char Char);
163
164 #ifdef SIZE_DEBUG
165 #ifdef HAVE_BACKTRACE
166 static void StrBufBacktrace(StrBuf *Buf, int which)
167 {
168         int n;
169         char *pstart, *pch;
170         void *stack_frames[50];
171         size_t size, i;
172         char **strings;
173
174         if (which)
175                 pstart = pch = Buf->bt;
176         else
177                 pstart = pch = Buf->bt_lastinc;
178         size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
179         strings = backtrace_symbols(stack_frames, size);
180         for (i = 0; i < size; i++) {
181                 if (strings != NULL)
182                         n = snprintf(pch, SIZ - (pch - pstart), "%s\\n", strings[i]);
183                 else
184                         n = snprintf(pch, SIZ - (pch - pstart), "%p\\n", stack_frames[i]);
185                 pch += n;
186         }
187         free(strings);
188
189
190 }
191 #endif
192
193 void dbg_FreeStrBuf(StrBuf *FreeMe, char *FromWhere)
194 {
195         if (hFreeDbglog == -1){
196                 pid_t pid = getpid();
197                 char path [SIZ];
198                 snprintf(path, SIZ, "/tmp/libcitadel_strbuf_realloc.log.%d", pid);
199                 hFreeDbglog = open(path, O_APPEND|O_CREAT|O_WRONLY);
200         }
201         if ((*FreeMe)->nIncreases > 0)
202         {
203                 char buf[SIZ * 3];
204                 long n;
205                 n = snprintf(buf, SIZ * 3, "%c+|%ld|%ld|%ld|%s|%s|\n",
206                              FromWhere,
207                              (*FreeMe)->nIncreases,
208                              (*FreeMe)->BufUsed,
209                              (*FreeMe)->BufSize,
210                              (*FreeMe)->bt,
211                              (*FreeMe)->bt_lastinc);
212                 n = write(hFreeDbglog, buf, n);
213         }
214         else
215         {
216                 char buf[128];
217                 long n;
218                 n = snprintf(buf, 128, "%c_|0|%ld%ld|\n",
219                              FromWhere,
220                              (*FreeMe)->BufUsed,
221                              (*FreeMe)->BufSize);
222                 n = write(hFreeDbglog, buf, n);
223         }
224 }
225
226 void dbg_IncreaseBuf(StrBuf *IncMe)
227 {
228         Buf->nIncreases++;
229 #ifdef HAVE_BACKTRACE
230         StrBufBacktrace(Buf, 1);
231 #endif
232 }
233
234 void dbg_Init(StrBuf *Buf)
235 {
236         Buf->nIncreases = 0;
237         Buf->bt[0] = '\0';
238         Buf->bt_lastinc[0] = '\0';
239 #ifdef HAVE_BACKTRACE
240         StrBufBacktrace(Buf, 0);
241 #endif
242 }
243
244 #else
245 /* void it... */
246 #define dbg_FreeStrBuf(a, b)
247 #define dbg_IncreaseBuf(a)
248 #define dbg_Init(a)
249
250 #endif
251
252 /**
253  * @ingroup StrBuf
254  * @brief swaps the contents of two StrBufs
255  * this is to be used to have cheap switched between a work-buffer and a target buffer 
256  * @param A First one
257  * @param B second one
258  */
259 static inline void iSwapBuffers(StrBuf *A, StrBuf *B)
260 {
261         StrBuf C;
262
263         memcpy(&C, A, sizeof(*A));
264         memcpy(A, B, sizeof(*B));
265         memcpy(B, &C, sizeof(C));
266
267 }
268
269 void SwapBuffers(StrBuf *A, StrBuf *B)
270 {
271         iSwapBuffers(A, B);
272 }
273
274
275 /** 
276  * @ingroup StrBuf_Cast
277  * @brief Cast operator to Plain String 
278  * @note if the buffer is altered by StrBuf operations, this pointer may become 
279  *  invalid. So don't lean on it after altering the buffer!
280  *  Since this operation is considered cheap, rather call it often than risking
281  *  your pointer to become invalid!
282  * @param Str the string we want to get the c-string representation for
283  * @returns the Pointer to the Content. Don't mess with it!
284  */
285 inline const char *ChrPtr(const StrBuf *Str)
286 {
287         if (Str == NULL)
288                 return "";
289         return Str->buf;
290 }
291
292 /**
293  * @ingroup StrBuf_Cast
294  * @brief since we know strlen()'s result, provide it here.
295  * @param Str the string to return the length to
296  * @returns contentlength of the buffer
297  */
298 inline int StrLength(const StrBuf *Str)
299 {
300         return (Str != NULL) ? Str->BufUsed : 0;
301 }
302
303 /**
304  * @ingroup StrBuf_DeConstructors
305  * @brief local utility function to resize the buffer
306  * @param Buf the buffer whichs storage we should increase
307  * @param KeepOriginal should we copy the original buffer or just start over with a new one
308  * @param DestSize what should fit in after?
309  */
310 static int IncreaseBuf(StrBuf *Buf, int KeepOriginal, int DestSize)
311 {
312         char *NewBuf;
313         size_t NewSize = Buf->BufSize * 2;
314
315         if (Buf->ConstBuf)
316                 return -1;
317                 
318         if (DestSize > 0)
319                 while ((NewSize <= DestSize) && (NewSize != 0))
320                         NewSize *= 2;
321
322         if (NewSize == 0)
323                 return -1;
324
325         NewBuf= (char*) malloc(NewSize);
326         if (NewBuf == NULL)
327                 return -1;
328
329         if (KeepOriginal && (Buf->BufUsed > 0))
330         {
331                 memcpy(NewBuf, Buf->buf, Buf->BufUsed);
332         }
333         else
334         {
335                 NewBuf[0] = '\0';
336                 Buf->BufUsed = 0;
337         }
338         free (Buf->buf);
339         Buf->buf = NewBuf;
340         Buf->BufSize = NewSize;
341
342         dbg_IncreaseBuf(Buf);
343
344         return Buf->BufSize;
345 }
346
347 /**
348  * @ingroup StrBuf_DeConstructors
349  * @brief shrink / increase an _EMPTY_ buffer to NewSize. Buffercontent is thoroughly ignored and flushed.
350  * @param Buf Buffer to shrink (has to be empty)
351  * @param ThreshHold if the buffer is bigger then this, its readjusted
352  * @param NewSize if we Shrink it, how big are we going to be afterwards?
353  */
354 void ReAdjustEmptyBuf(StrBuf *Buf, long ThreshHold, long NewSize)
355 {
356         if ((Buf != NULL) && 
357             (Buf->BufUsed == 0) &&
358             (Buf->BufSize < ThreshHold)) {
359                 free(Buf->buf);
360                 Buf->buf = (char*) malloc(NewSize);
361                 Buf->BufUsed = 0;
362                 Buf->BufSize = NewSize;
363         }
364 }
365
366 /**
367  * @ingroup StrBuf_DeConstructors
368  * @brief shrink long term buffers to their real size so they don't waste memory
369  * @param Buf buffer to shrink
370  * @param Force if not set, will just executed if the buffer is much to big; set for lifetime strings
371  * @returns physical size of the buffer
372  */
373 long StrBufShrinkToFit(StrBuf *Buf, int Force)
374 {
375         if (Buf == NULL)
376                 return -1;
377         if (Force || 
378             (Buf->BufUsed + (Buf->BufUsed / 3) > Buf->BufSize))
379         {
380                 char *TmpBuf;
381
382                 TmpBuf = (char*) malloc(Buf->BufUsed + 1);
383                 if (TmpBuf == NULL)
384                         return -1;
385
386                 memcpy (TmpBuf, Buf->buf, Buf->BufUsed + 1);
387                 Buf->BufSize = Buf->BufUsed + 1;
388                 free(Buf->buf);
389                 Buf->buf = TmpBuf;
390         }
391         return Buf->BufUsed;
392 }
393
394 /**
395  * @ingroup StrBuf_DeConstructors
396  * @brief Allocate a new buffer with default buffer size
397  * @returns the new stringbuffer
398  */
399 StrBuf* NewStrBuf(void)
400 {
401         StrBuf *NewBuf;
402
403         NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
404         if (NewBuf == NULL)
405                 return NULL;
406
407         NewBuf->buf = (char*) malloc(BaseStrBufSize);
408         if (NewBuf->buf == NULL)
409         {
410                 free(NewBuf);
411                 return NULL;
412         }
413         NewBuf->buf[0] = '\0';
414         NewBuf->BufSize = BaseStrBufSize;
415         NewBuf->BufUsed = 0;
416         NewBuf->ConstBuf = 0;
417
418         dbg_Init (NewBuf);
419
420         return NewBuf;
421 }
422
423 /** 
424  * @ingroup StrBuf_DeConstructors
425  * @brief Copy Constructor; returns a duplicate of CopyMe
426  * @param CopyMe Buffer to faxmilate
427  * @returns the new stringbuffer
428  */
429 StrBuf* NewStrBufDup(const StrBuf *CopyMe)
430 {
431         StrBuf *NewBuf;
432         
433         if (CopyMe == NULL)
434                 return NewStrBuf();
435
436         NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
437         if (NewBuf == NULL)
438                 return NULL;
439
440         NewBuf->buf = (char*) malloc(CopyMe->BufSize);
441         if (NewBuf->buf == NULL)
442         {
443                 free(NewBuf);
444                 return NULL;
445         }
446
447         memcpy(NewBuf->buf, CopyMe->buf, CopyMe->BufUsed + 1);
448         NewBuf->BufUsed = CopyMe->BufUsed;
449         NewBuf->BufSize = CopyMe->BufSize;
450         NewBuf->ConstBuf = 0;
451
452         dbg_Init(NewBuf);
453
454         return NewBuf;
455 }
456
457 /** 
458  * @ingroup StrBuf_DeConstructors
459  * @brief Copy Constructor; CreateRelpaceMe will contain CopyFlushMe afterwards.
460  * @param NoMe if non-NULL, we will use that buffer as value; KeepOriginal will abused as len.
461  * @param CopyFlushMe Buffer to faxmilate if KeepOriginal, or to move into CreateRelpaceMe if !KeepOriginal.
462  * @param CreateRelpaceMe If NULL, will be created, else Flushed and filled CopyFlushMe 
463  * @param KeepOriginal should CopyFlushMe remain intact? or may we Steal its buffer?
464  * @returns the new stringbuffer
465  */
466 void NewStrBufDupAppendFlush(StrBuf **CreateRelpaceMe, StrBuf *CopyFlushMe, const char *NoMe, int KeepOriginal)
467 {
468         StrBuf *NewBuf;
469         
470         if (CreateRelpaceMe == NULL)
471                 return;
472
473         if (NoMe != NULL)
474         {
475                 if (*CreateRelpaceMe != NULL)
476                         StrBufPlain(*CreateRelpaceMe, NoMe, KeepOriginal);
477                 else 
478                         *CreateRelpaceMe = NewStrBufPlain(NoMe, KeepOriginal);
479                 return;
480         }
481
482         if (CopyFlushMe == NULL)
483         {
484                 if (*CreateRelpaceMe != NULL)
485                         FlushStrBuf(*CreateRelpaceMe);
486                 else 
487                         *CreateRelpaceMe = NewStrBuf();
488                 return;
489         }
490
491         /* 
492          * Randomly Chosen: bigger than 64 chars is cheaper to swap the buffers instead of copying.
493          * else *CreateRelpaceMe may use more memory than needed in a longer term, CopyFlushMe might
494          * be a big IO-Buffer...
495          */
496         if (KeepOriginal || (StrLength(CopyFlushMe) < 256))
497         {
498                 if (*CreateRelpaceMe == NULL)
499                 {
500                         *CreateRelpaceMe = NewBuf = NewStrBufPlain(NULL, CopyFlushMe->BufUsed);
501                         dbg_Init(NewBuf);
502                 }
503                 else 
504                 {
505                         NewBuf = *CreateRelpaceMe;
506                         FlushStrBuf(NewBuf);
507                 }
508                 StrBufAppendBuf(NewBuf, CopyFlushMe, 0);
509         }
510         else
511         {
512                 if (*CreateRelpaceMe == NULL)
513                 {
514                         *CreateRelpaceMe = NewBuf = NewStrBufPlain(NULL, CopyFlushMe->BufUsed);
515                         dbg_Init(NewBuf);
516                 }
517                 else 
518                         NewBuf = *CreateRelpaceMe;
519                 iSwapBuffers (NewBuf, CopyFlushMe);
520         }
521         if (!KeepOriginal)
522                 FlushStrBuf(CopyFlushMe);
523         return;
524 }
525
526 /**
527  * @ingroup StrBuf_DeConstructors
528  * @brief create a new Buffer using an existing c-string
529  * this function should also be used if you want to pre-suggest
530  * the buffer size to allocate in conjunction with ptr == NULL
531  * @param ptr the c-string to copy; may be NULL to create a blank instance
532  * @param nChars How many chars should we copy; -1 if we should measure the length ourselves
533  * @returns the new stringbuffer
534  */
535 StrBuf* NewStrBufPlain(const char* ptr, int nChars)
536 {
537         StrBuf *NewBuf;
538         size_t Siz = BaseStrBufSize;
539         size_t CopySize;
540
541         NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
542         if (NewBuf == NULL)
543                 return NULL;
544
545         if (nChars < 0)
546                 CopySize = strlen((ptr != NULL)?ptr:"");
547         else
548                 CopySize = nChars;
549
550         while ((Siz <= CopySize) && (Siz != 0))
551                 Siz *= 2;
552
553         if (Siz == 0)
554         {
555                 free(NewBuf);
556                 return NULL;
557         }
558
559         NewBuf->buf = (char*) malloc(Siz);
560         if (NewBuf->buf == NULL)
561         {
562                 free(NewBuf);
563                 return NULL;
564         }
565         NewBuf->BufSize = Siz;
566         if (ptr != NULL) {
567                 memcpy(NewBuf->buf, ptr, CopySize);
568                 NewBuf->buf[CopySize] = '\0';
569                 NewBuf->BufUsed = CopySize;
570         }
571         else {
572                 NewBuf->buf[0] = '\0';
573                 NewBuf->BufUsed = 0;
574         }
575         NewBuf->ConstBuf = 0;
576
577         dbg_Init(NewBuf);
578
579         return NewBuf;
580 }
581
582 /**
583  * @ingroup StrBuf_DeConstructors
584  * @brief Set an existing buffer from a c-string
585  * @param Buf buffer to load
586  * @param ptr c-string to put into 
587  * @param nChars set to -1 if we should work 0-terminated
588  * @returns the new length of the string
589  */
590 int StrBufPlain(StrBuf *Buf, const char* ptr, int nChars)
591 {
592         size_t Siz;
593         size_t CopySize;
594
595         if (Buf == NULL)
596                 return -1;
597         if (ptr == NULL) {
598                 FlushStrBuf(Buf);
599                 return -1;
600         }
601
602         Siz = Buf->BufSize;
603
604         if (nChars < 0)
605                 CopySize = strlen(ptr);
606         else
607                 CopySize = nChars;
608
609         while ((Siz <= CopySize) && (Siz != 0))
610                 Siz *= 2;
611
612         if (Siz == 0) {
613                 FlushStrBuf(Buf);
614                 return -1;
615         }
616
617         if (Siz != Buf->BufSize)
618                 IncreaseBuf(Buf, 0, Siz);
619         memcpy(Buf->buf, ptr, CopySize);
620         Buf->buf[CopySize] = '\0';
621         Buf->BufUsed = CopySize;
622         Buf->ConstBuf = 0;
623         return CopySize;
624 }
625
626
627 /**
628  * @ingroup StrBuf_DeConstructors
629  * @brief use strbuf as wrapper for a string constant for easy handling
630  * @param StringConstant a string to wrap
631  * @param SizeOfStrConstant should be sizeof(StringConstant)-1
632  */
633 StrBuf* _NewConstStrBuf(const char* StringConstant, size_t SizeOfStrConstant)
634 {
635         StrBuf *NewBuf;
636
637         NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
638         if (NewBuf == NULL)
639                 return NULL;
640         NewBuf->buf = (char*) StringConstant;
641         NewBuf->BufSize = SizeOfStrConstant;
642         NewBuf->BufUsed = SizeOfStrConstant;
643         NewBuf->ConstBuf = 1;
644
645         dbg_Init(NewBuf);
646
647         return NewBuf;
648 }
649
650
651 /**
652  * @ingroup StrBuf_DeConstructors
653  * @brief flush the content of a Buf; keep its struct
654  * @param buf Buffer to flush
655  */
656 int FlushStrBuf(StrBuf *buf)
657 {
658         if ((buf == NULL) || (buf->buf == NULL))
659                 return -1;
660         if (buf->ConstBuf)
661                 return -1;       
662         buf->buf[0] ='\0';
663         buf->BufUsed = 0;
664         return 0;
665 }
666
667 /**
668  * @ingroup StrBuf_DeConstructors
669  * @brief wipe the content of a Buf thoroughly (overwrite it -> expensive); keep its struct
670  * @param buf Buffer to wipe
671  */
672 int FLUSHStrBuf(StrBuf *buf)
673 {
674         if (buf == NULL)
675                 return -1;
676         if (buf->ConstBuf)
677                 return -1;
678         if (buf->BufUsed > 0) {
679                 memset(buf->buf, 0, buf->BufUsed);
680                 buf->BufUsed = 0;
681         }
682         return 0;
683 }
684
685 #ifdef SIZE_DEBUG
686 int hFreeDbglog = -1;
687 #endif
688 /**
689  * @ingroup StrBuf_DeConstructors
690  * @brief Release a Buffer
691  * Its a double pointer, so it can NULL your pointer
692  * so fancy SIG11 appear instead of random results
693  * @param FreeMe Pointer Pointer to the buffer to free
694  */
695 void FreeStrBuf (StrBuf **FreeMe)
696 {
697         if (*FreeMe == NULL)
698                 return;
699
700         dbg_FreeStrBuf(FreeMe, 'F');
701
702         if (!(*FreeMe)->ConstBuf) 
703                 free((*FreeMe)->buf);
704         free(*FreeMe);
705         *FreeMe = NULL;
706 }
707
708 /**
709  * @ingroup StrBuf_DeConstructors
710  * @brief flatten a Buffer to the Char * we return 
711  * Its a double pointer, so it can NULL your pointer
712  * so fancy SIG11 appear instead of random results
713  * The Callee then owns the buffer and is responsible for freeing it.
714  * @param SmashMe Pointer Pointer to the buffer to release Buf from and free
715  * @returns the pointer of the buffer; Callee owns the memory thereafter.
716  */
717 char *SmashStrBuf (StrBuf **SmashMe)
718 {
719         char *Ret;
720
721         if ((SmashMe == NULL) || (*SmashMe == NULL))
722                 return NULL;
723         
724         dbg_FreeStrBuf(SmashMe, 'S');
725
726         Ret = (*SmashMe)->buf;
727         free(*SmashMe);
728         *SmashMe = NULL;
729         return Ret;
730 }
731
732 /**
733  * @ingroup StrBuf_DeConstructors
734  * @brief Release the buffer
735  * If you want put your StrBuf into a Hash, use this as Destructor.
736  * @param VFreeMe untyped pointer to a StrBuf. be shure to do the right thing [TM]
737  */
738 void HFreeStrBuf (void *VFreeMe)
739 {
740         StrBuf *FreeMe = (StrBuf*)VFreeMe;
741         if (FreeMe == NULL)
742                 return;
743
744         dbg_FreeStrBuf(SmashMe, 'H');
745
746         if (!FreeMe->ConstBuf) 
747                 free(FreeMe->buf);
748         free(FreeMe);
749 }
750
751
752 /*******************************************************************************
753  *                      Simple string transformations                          *
754  *******************************************************************************/
755
756 /**
757  * @ingroup StrBuf
758  * @brief Wrapper around atol
759  */
760 long StrTol(const StrBuf *Buf)
761 {
762         if (Buf == NULL)
763                 return 0;
764         if(Buf->BufUsed > 0)
765                 return atol(Buf->buf);
766         else
767                 return 0;
768 }
769
770 /**
771  * @ingroup StrBuf
772  * @brief Wrapper around atoi
773  */
774 int StrToi(const StrBuf *Buf)
775 {
776         if (Buf == NULL)
777                 return 0;
778         if (Buf->BufUsed > 0)
779                 return atoi(Buf->buf);
780         else
781                 return 0;
782 }
783
784 /**
785  * @ingroup StrBuf
786  * @brief Checks to see if the string is a pure number 
787  * @param Buf The buffer to inspect
788  * @returns 1 if its a pure number, 0, if not.
789  */
790 int StrBufIsNumber(const StrBuf *Buf) {
791         char * pEnd;
792         if ((Buf == NULL) || (Buf->BufUsed == 0)) {
793                 return 0;
794         }
795         strtoll(Buf->buf, &pEnd, 10);
796         if (pEnd == Buf->buf)
797                 return 0;
798         if ((pEnd != NULL) && (pEnd == Buf->buf + Buf->BufUsed))
799                 return 1;
800         if (Buf->buf == pEnd)
801                 return 0;
802         return 0;
803
804
805 /**
806  * @ingroup StrBuf_Filler
807  * @brief modifies a Single char of the Buf
808  * You can point to it via char* or a zero-based integer
809  * @param Buf The buffer to manipulate
810  * @param ptr char* to zero; use NULL if unused
811  * @param nThChar zero based pointer into the string; use -1 if unused
812  * @param PeekValue The Character to place into the position
813  */
814 long StrBufPeek(StrBuf *Buf, const char* ptr, long nThChar, char PeekValue)
815 {
816         if (Buf == NULL)
817                 return -1;
818         if (ptr != NULL)
819                 nThChar = ptr - Buf->buf;
820         if ((nThChar < 0) || (nThChar > Buf->BufUsed))
821                 return -1;
822         Buf->buf[nThChar] = PeekValue;
823         return nThChar;
824 }
825
826 /**
827  * @ingroup StrBuf_Filler
828  * @brief modifies a range of chars of the Buf
829  * You can point to it via char* or a zero-based integer
830  * @param Buf The buffer to manipulate
831  * @param ptr char* to zero; use NULL if unused
832  * @param nThChar zero based pointer into the string; use -1 if unused
833  * @param nChars how many chars are to be flushed?
834  * @param PookValue The Character to place into that area
835  */
836 long StrBufPook(StrBuf *Buf, const char* ptr, long nThChar, long nChars, char PookValue)
837 {
838         if (Buf == NULL)
839                 return -1;
840         if (ptr != NULL)
841                 nThChar = ptr - Buf->buf;
842         if ((nThChar < 0) || (nThChar > Buf->BufUsed))
843                 return -1;
844         if (nThChar + nChars > Buf->BufUsed)
845                 nChars =  Buf->BufUsed - nThChar;
846
847         memset(Buf->buf + nThChar, PookValue, nChars);
848         /* just to be shure... */
849         Buf->buf[Buf->BufUsed] = 0;
850         return nChars;
851 }
852
853 /**
854  * @ingroup StrBuf_Filler
855  * @brief Append a StringBuffer to the buffer
856  * @param Buf Buffer to modify
857  * @param AppendBuf Buffer to copy at the end of our buffer
858  * @param Offset Should we start copying from an offset?
859  */
860 void StrBufAppendBuf(StrBuf *Buf, const StrBuf *AppendBuf, unsigned long Offset)
861 {
862         if ((AppendBuf == NULL) || (AppendBuf->buf == NULL) ||
863             (Buf == NULL) || (Buf->buf == NULL))
864                 return;
865
866         if (Buf->BufSize - Offset < AppendBuf->BufUsed + Buf->BufUsed + 1)
867                 IncreaseBuf(Buf, 
868                             (Buf->BufUsed > 0), 
869                             AppendBuf->BufUsed + Buf->BufUsed);
870
871         memcpy(Buf->buf + Buf->BufUsed, 
872                AppendBuf->buf + Offset, 
873                AppendBuf->BufUsed - Offset);
874         Buf->BufUsed += AppendBuf->BufUsed - Offset;
875         Buf->buf[Buf->BufUsed] = '\0';
876 }
877
878
879 /**
880  * @ingroup StrBuf_Filler
881  * @brief Append a C-String to the buffer
882  * @param Buf Buffer to modify
883  * @param AppendBuf Buffer to copy at the end of our buffer
884  * @param AppendSize number of bytes to copy; set to -1 if we should count it in advance
885  * @param Offset Should we start copying from an offset?
886  */
887 void StrBufAppendBufPlain(StrBuf *Buf, const char *AppendBuf, long AppendSize, unsigned long Offset)
888 {
889         long aps;
890         long BufSizeRequired;
891
892         if ((AppendBuf == NULL) || (Buf == NULL))
893                 return;
894
895         if (AppendSize < 0 )
896                 aps = strlen(AppendBuf + Offset);
897         else
898                 aps = AppendSize - Offset;
899
900         BufSizeRequired = Buf->BufUsed + aps + 1;
901         if (Buf->BufSize <= BufSizeRequired)
902                 IncreaseBuf(Buf, (Buf->BufUsed > 0), BufSizeRequired);
903
904         memcpy(Buf->buf + Buf->BufUsed, 
905                AppendBuf + Offset, 
906                aps);
907         Buf->BufUsed += aps;
908         Buf->buf[Buf->BufUsed] = '\0';
909 }
910
911 /**
912  * @ingroup StrBuf_Filler
913  * @brief sprintf like function appending the formated string to the buffer
914  * vsnprintf version to wrap into own calls
915  * @param Buf Buffer to extend by format and Params
916  * @param format printf alike format to add
917  * @param ap va_list containing the items for format
918  */
919 void StrBufVAppendPrintf(StrBuf *Buf, const char *format, va_list ap)
920 {
921         va_list apl;
922         size_t BufSize;
923         size_t nWritten;
924         size_t Offset;
925         size_t newused;
926
927         if ((Buf == NULL)  || (format == NULL))
928                 return;
929
930         BufSize = Buf->BufSize;
931         nWritten = Buf->BufSize + 1;
932         Offset = Buf->BufUsed;
933         newused = Offset + nWritten;
934         
935         while (newused >= BufSize) {
936                 va_copy(apl, ap);
937                 nWritten = vsnprintf(Buf->buf + Offset, 
938                                      Buf->BufSize - Offset, 
939                                      format, apl);
940                 va_end(apl);
941                 newused = Offset + nWritten;
942                 if (newused >= Buf->BufSize) {
943                         if (IncreaseBuf(Buf, 1, newused) == -1)
944                                 return; /* TODO: error handling? */
945                         newused = Buf->BufSize + 1;
946                 }
947                 else {
948                         Buf->BufUsed = Offset + nWritten;
949                         BufSize = Buf->BufSize;
950                 }
951
952         }
953 }
954
955 /**
956  * @ingroup StrBuf_Filler
957  * @brief sprintf like function appending the formated string to the buffer
958  * @param Buf Buffer to extend by format and Params
959  * @param format printf alike format to add
960  */
961 void StrBufAppendPrintf(StrBuf *Buf, const char *format, ...)
962 {
963         size_t BufSize;
964         size_t nWritten;
965         size_t Offset;
966         size_t newused;
967         va_list arg_ptr;
968         
969         if ((Buf == NULL)  || (format == NULL))
970                 return;
971
972         BufSize = Buf->BufSize;
973         nWritten = Buf->BufSize + 1;
974         Offset = Buf->BufUsed;
975         newused = Offset + nWritten;
976
977         while (newused >= BufSize) {
978                 va_start(arg_ptr, format);
979                 nWritten = vsnprintf(Buf->buf + Buf->BufUsed, 
980                                      Buf->BufSize - Buf->BufUsed, 
981                                      format, arg_ptr);
982                 va_end(arg_ptr);
983                 newused = Buf->BufUsed + nWritten;
984                 if (newused >= Buf->BufSize) {
985                         if (IncreaseBuf(Buf, 1, newused) == -1)
986                                 return; /* TODO: error handling? */
987                         newused = Buf->BufSize + 1;
988                 }
989                 else {
990                         Buf->BufUsed += nWritten;
991                         BufSize = Buf->BufSize;
992                 }
993
994         }
995 }
996
997 /**
998  * @ingroup StrBuf_Filler
999  * @brief sprintf like function putting the formated string into the buffer
1000  * @param Buf Buffer to extend by format and Parameters
1001  * @param format printf alike format to add
1002  */
1003 void StrBufPrintf(StrBuf *Buf, const char *format, ...)
1004 {
1005         size_t nWritten;
1006         va_list arg_ptr;
1007         
1008         if ((Buf == NULL)  || (format == NULL))
1009                 return;
1010
1011         nWritten = Buf->BufSize + 1;
1012         while (nWritten >= Buf->BufSize) {
1013                 va_start(arg_ptr, format);
1014                 nWritten = vsnprintf(Buf->buf, Buf->BufSize, format, arg_ptr);
1015                 va_end(arg_ptr);
1016                 if (nWritten >= Buf->BufSize) {
1017                         if (IncreaseBuf(Buf, 0, 0) == -1)
1018                                 return; /* TODO: error handling? */
1019                         nWritten = Buf->BufSize + 1;
1020                         continue;
1021                 }
1022                 Buf->BufUsed = nWritten ;
1023         }
1024 }
1025
1026 /**
1027  * @ingroup StrBuf_Filler
1028  * @brief Callback for cURL to append the webserver reply to a buffer
1029  * @param ptr pre-defined by the cURL API; see man 3 curl for mre info
1030  * @param size pre-defined by the cURL API; see man 3 curl for mre info
1031  * @param nmemb pre-defined by the cURL API; see man 3 curl for mre info
1032  * @param stream pre-defined by the cURL API; see man 3 curl for mre info
1033  */
1034 size_t CurlFillStrBuf_callback(void *ptr, size_t size, size_t nmemb, void *stream)
1035 {
1036
1037         StrBuf *Target;
1038
1039         Target = stream;
1040         if (ptr == NULL)
1041                 return 0;
1042
1043         StrBufAppendBufPlain(Target, ptr, size * nmemb, 0);
1044         return size * nmemb;
1045 }
1046
1047
1048 /**
1049  * @ingroup StrBuf
1050  * @brief extracts a substring from Source into dest
1051  * @param dest buffer to place substring into
1052  * @param Source string to copy substring from
1053  * @param Offset chars to skip from start
1054  * @param nChars number of chars to copy
1055  * @returns the number of chars copied; may be different from nChars due to the size of Source
1056  */
1057 int StrBufSub(StrBuf *dest, const StrBuf *Source, unsigned long Offset, size_t nChars)
1058 {
1059         size_t NCharsRemain;
1060         if (Offset > Source->BufUsed)
1061         {
1062                 if (dest != NULL)
1063                         FlushStrBuf(dest);
1064                 return 0;
1065         }
1066         if (Offset + nChars < Source->BufUsed)
1067         {
1068                 if ((nChars >= dest->BufSize) && 
1069                     (IncreaseBuf(dest, 0, nChars + 1) == -1))
1070                         return 0;
1071                 memcpy(dest->buf, Source->buf + Offset, nChars);
1072                 dest->BufUsed = nChars;
1073                 dest->buf[dest->BufUsed] = '\0';
1074                 return nChars;
1075         }
1076         NCharsRemain = Source->BufUsed - Offset;
1077         if ((NCharsRemain  >= dest->BufSize) && 
1078             (IncreaseBuf(dest, 0, NCharsRemain + 1) == -1))
1079                 return 0;
1080         memcpy(dest->buf, Source->buf + Offset, NCharsRemain);
1081         dest->BufUsed = NCharsRemain;
1082         dest->buf[dest->BufUsed] = '\0';
1083         return NCharsRemain;
1084 }
1085
1086 /**
1087  * @ingroup StrBuf
1088  * @brief Cut nChars from the start of the string
1089  * @param Buf Buffer to modify
1090  * @param nChars how many chars should be skipped?
1091  */
1092 void StrBufCutLeft(StrBuf *Buf, int nChars)
1093 {
1094         if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1095         if (nChars >= Buf->BufUsed) {
1096                 FlushStrBuf(Buf);
1097                 return;
1098         }
1099         memmove(Buf->buf, Buf->buf + nChars, Buf->BufUsed - nChars);
1100         Buf->BufUsed -= nChars;
1101         Buf->buf[Buf->BufUsed] = '\0';
1102 }
1103
1104 /**
1105  * @ingroup StrBuf
1106  * @brief Cut the trailing n Chars from the string
1107  * @param Buf Buffer to modify
1108  * @param nChars how many chars should be trunkated?
1109  */
1110 void StrBufCutRight(StrBuf *Buf, int nChars)
1111 {
1112         if ((Buf == NULL) || (Buf->BufUsed == 0) || (Buf->buf == NULL))
1113                 return;
1114
1115         if (nChars >= Buf->BufUsed) {
1116                 FlushStrBuf(Buf);
1117                 return;
1118         }
1119         Buf->BufUsed -= nChars;
1120         Buf->buf[Buf->BufUsed] = '\0';
1121 }
1122
1123 /**
1124  * @ingroup StrBuf
1125  * @brief Cut the string after n Chars
1126  * @param Buf Buffer to modify
1127  * @param AfternChars after how many chars should we trunkate the string?
1128  * @param At if non-null and points inside of our string, cut it there.
1129  */
1130 void StrBufCutAt(StrBuf *Buf, int AfternChars, const char *At)
1131 {
1132         if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1133         if (At != NULL){
1134                 AfternChars = At - Buf->buf;
1135         }
1136
1137         if ((AfternChars < 0) || (AfternChars >= Buf->BufUsed))
1138                 return;
1139         Buf->BufUsed = AfternChars;
1140         Buf->buf[Buf->BufUsed] = '\0';
1141 }
1142
1143
1144 /**
1145  * @ingroup StrBuf
1146  * @brief Strip leading and trailing spaces from a string; with premeasured and adjusted length.
1147  * @param Buf the string to modify
1148  */
1149 void StrBufTrim(StrBuf *Buf)
1150 {
1151         int delta = 0;
1152         if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1153
1154         while ((Buf->BufUsed > 0) &&
1155                isspace(Buf->buf[Buf->BufUsed - 1]))
1156         {
1157                 Buf->BufUsed --;
1158         }
1159         Buf->buf[Buf->BufUsed] = '\0';
1160
1161         if (Buf->BufUsed == 0) return;
1162
1163         while ((Buf->BufUsed > delta) && (isspace(Buf->buf[delta]))){
1164                 delta ++;
1165         }
1166         if (delta > 0) StrBufCutLeft(Buf, delta);
1167 }
1168 /**
1169  * @ingroup StrBuf
1170  * @brief changes all spaces in the string  (tab, linefeed...) to Blank (0x20)
1171  * @param Buf the string to modify
1172  */
1173 void StrBufSpaceToBlank(StrBuf *Buf)
1174 {
1175         char *pche, *pch;
1176
1177         if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1178
1179         pch = Buf->buf;
1180         pche = pch + Buf->BufUsed;
1181         while (pch < pche) 
1182         {
1183                 if (isspace(*pch))
1184                         *pch = ' ';
1185                 pch ++;
1186         }
1187 }
1188
1189 void StrBufStripAllBut(StrBuf *Buf, char leftboundary, char rightboundary)
1190 {
1191         const char *pLeft;
1192         const char *pRight;
1193
1194         if ((Buf == NULL) || (Buf->buf == NULL)) {
1195                 return;
1196         }
1197
1198         pRight = strchr(Buf->buf, rightboundary);
1199         if (pRight != NULL) {
1200                 StrBufCutAt(Buf, 0, pRight);
1201         }
1202
1203         pLeft = strrchr(ChrPtr(Buf), leftboundary);
1204         if (pLeft != NULL) {
1205                 StrBufCutLeft(Buf, pLeft - Buf->buf + 1);
1206         }
1207 }
1208
1209
1210 /**
1211  * @ingroup StrBuf_Filler
1212  * @brief uppercase the contents of a buffer
1213  * @param Buf the buffer to translate
1214  */
1215 void StrBufUpCase(StrBuf *Buf) 
1216 {
1217         char *pch, *pche;
1218
1219         if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1220
1221         pch = Buf->buf;
1222         pche = pch + Buf->BufUsed;
1223         while (pch < pche) {
1224                 *pch = toupper(*pch);
1225                 pch ++;
1226         }
1227 }
1228
1229
1230 /**
1231  * @ingroup StrBuf_Filler
1232  * @brief lowercase the contents of a buffer
1233  * @param Buf the buffer to translate
1234  */
1235 void StrBufLowerCase(StrBuf *Buf) 
1236 {
1237         char *pch, *pche;
1238
1239         if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1240
1241         pch = Buf->buf;
1242         pche = pch + Buf->BufUsed;
1243         while (pch < pche) {
1244                 *pch = tolower(*pch);
1245                 pch ++;
1246         }
1247 }
1248
1249
1250 /*******************************************************************************
1251  *           a tokenizer that kills, maims, and destroys                       *
1252  *******************************************************************************/
1253
1254 /**
1255  * @ingroup StrBuf_Tokenizer
1256  * @brief Replace a token at a given place with a given length by another token with given length
1257  * @param Buf String where to work on
1258  * @param where where inside of the Buf is the search-token
1259  * @param HowLong How long is the token to be replaced
1260  * @param Repl Token to insert at 'where'
1261  * @param ReplLen Length of repl
1262  * @returns -1 if fail else length of resulting Buf
1263  */
1264 int StrBufReplaceToken(StrBuf *Buf, long where, long HowLong, 
1265                        const char *Repl, long ReplLen)
1266 {
1267
1268         if ((Buf == NULL) || 
1269             (where > Buf->BufUsed) ||
1270             (where + HowLong > Buf->BufUsed))
1271                 return -1;
1272
1273         if (where + ReplLen - HowLong > Buf->BufSize)
1274                 if (IncreaseBuf(Buf, 1, Buf->BufUsed + ReplLen) < 0)
1275                         return -1;
1276
1277         memmove(Buf->buf + where + ReplLen, 
1278                 Buf->buf + where + HowLong,
1279                 Buf->BufUsed - where - HowLong);
1280                                                 
1281         memcpy(Buf->buf + where, 
1282                Repl, ReplLen);
1283
1284         Buf->BufUsed += ReplLen - HowLong;
1285
1286         return Buf->BufUsed;
1287 }
1288
1289 /**
1290  * @ingroup StrBuf_Tokenizer
1291  * @brief Counts the numbmer of tokens in a buffer
1292  * @param source String to count tokens in
1293  * @param tok    Tokenizer char to count
1294  * @returns numbers of tokenizer chars found
1295  */
1296 int StrBufNum_tokens(const StrBuf *source, char tok)
1297 {
1298         char *pch, *pche;
1299         long NTokens;
1300         if ((source == NULL) || (source->BufUsed == 0))
1301                 return 0;
1302         if ((source->BufUsed == 1) && (*source->buf == tok))
1303                 return 2;
1304         NTokens = 1;
1305         pch = source->buf;
1306         pche = pch + source->BufUsed;
1307         while (pch < pche)
1308         {
1309                 if (*pch == tok)
1310                         NTokens ++;
1311                 pch ++;
1312         }
1313         return NTokens;
1314 }
1315
1316 /**
1317  * @ingroup StrBuf_Tokenizer
1318  * @brief a string tokenizer
1319  * @param Source StringBuffer to read into
1320  * @param parmnum n'th Parameter to remove
1321  * @param separator tokenizer character
1322  * @returns -1 if not found, else length of token.
1323  */
1324 int StrBufRemove_token(StrBuf *Source, int parmnum, char separator)
1325 {
1326         int ReducedBy;
1327         char *d, *s, *end;              /* dest, source */
1328         int count = 0;
1329
1330         /* Find desired @parameter */
1331         end = Source->buf + Source->BufUsed;
1332         d = Source->buf;
1333         while ((d <= end) && 
1334                (count < parmnum))
1335         {
1336                 /* End of string, bail! */
1337                 if (!*d) {
1338                         d = NULL;
1339                         break;
1340                 }
1341                 if (*d == separator) {
1342                         count++;
1343                 }
1344                 d++;
1345         }
1346         if ((d == NULL) || (d >= end))
1347                 return 0;               /* @Parameter not found */
1348
1349         /* Find next @parameter */
1350         s = d;
1351         while ((s <= end) && 
1352                (*s && *s != separator))
1353         {
1354                 s++;
1355         }
1356         if (*s == separator)
1357                 s++;
1358         ReducedBy = d - s;
1359
1360         /* Hack and slash */
1361         if (s >= end) {
1362                 return 0;
1363         }
1364         else if (*s) {
1365                 memmove(d, s, Source->BufUsed - (s - Source->buf));
1366                 Source->BufUsed += ReducedBy;
1367                 Source->buf[Source->BufUsed] = '\0';
1368         }
1369         else if (d == Source->buf) {
1370                 *d = 0;
1371                 Source->BufUsed = 0;
1372         }
1373         else {
1374                 *--d = '\0';
1375                 Source->BufUsed += ReducedBy;
1376         }
1377         /*
1378         while (*s) {
1379                 *d++ = *s++;
1380         }
1381         *d = 0;
1382         */
1383         return ReducedBy;
1384 }
1385
1386 int StrBufExtract_tokenFromStr(StrBuf *dest, const char *Source, long SourceLen, int parmnum, char separator)
1387 {
1388         const StrBuf Temp = {
1389                 (char*)Source,
1390                 SourceLen,
1391                 SourceLen,
1392                 1
1393 #ifdef SIZE_DEBUG
1394                 ,
1395                 0,
1396                 "",
1397                 ""
1398 #endif
1399         };
1400
1401         return StrBufExtract_token(dest, &Temp, parmnum, separator);
1402 }
1403
1404 /**
1405  * @ingroup StrBuf_Tokenizer
1406  * @brief a string tokenizer
1407  * @param dest Destination StringBuffer
1408  * @param Source StringBuffer to read into
1409  * @param parmnum n'th Parameter to extract
1410  * @param separator tokenizer character
1411  * @returns -1 if not found, else length of token.
1412  */
1413 int StrBufExtract_token(StrBuf *dest, const StrBuf *Source, int parmnum, char separator)
1414 {
1415         const char *s, *e;              //* source * /
1416         int len = 0;                    //* running total length of extracted string * /
1417         int current_token = 0;          //* token currently being processed * /
1418          
1419         if (dest != NULL) {
1420                 dest->buf[0] = '\0';
1421                 dest->BufUsed = 0;
1422         }
1423         else
1424                 return(-1);
1425
1426         if ((Source == NULL) || (Source->BufUsed ==0)) {
1427                 return(-1);
1428         }
1429         s = Source->buf;
1430         e = s + Source->BufUsed;
1431
1432         //cit_backtrace();
1433         //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
1434
1435         while ((s < e) && !IsEmptyStr(s)) {
1436                 if (*s == separator) {
1437                         ++current_token;
1438                 }
1439                 if (len >= dest->BufSize) {
1440                         dest->BufUsed = len;
1441                         if (IncreaseBuf(dest, 1, -1) < 0) {
1442                                 dest->BufUsed --;
1443                                 break;
1444                         }
1445                 }
1446                 if ( (current_token == parmnum) && 
1447                      (*s != separator)) {
1448                         dest->buf[len] = *s;
1449                         ++len;
1450                 }
1451                 else if (current_token > parmnum) {
1452                         break;
1453                 }
1454                 ++s;
1455         }
1456         
1457         dest->buf[len] = '\0';
1458         dest->BufUsed = len;
1459                 
1460         if (current_token < parmnum) {
1461                 //lprintf (CTDL_DEBUG,"test <!: %s\n", dest);
1462                 return(-1);
1463         }
1464         //lprintf (CTDL_DEBUG,"test <: %d; %s\n", len, dest);
1465         return(len);
1466 }
1467
1468
1469
1470
1471
1472 /**
1473  * @ingroup StrBuf_Tokenizer
1474  * @brief a string tokenizer to fetch an integer
1475  * @param Source String containing tokens
1476  * @param parmnum n'th Parameter to extract
1477  * @param separator tokenizer character
1478  * @returns 0 if not found, else integer representation of the token
1479  */
1480 int StrBufExtract_int(const StrBuf* Source, int parmnum, char separator)
1481 {
1482         StrBuf tmp;
1483         char buf[64];
1484         
1485         tmp.buf = buf;
1486         buf[0] = '\0';
1487         tmp.BufSize = 64;
1488         tmp.BufUsed = 0;
1489         tmp.ConstBuf = 1;
1490         if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
1491                 return(atoi(buf));
1492         else
1493                 return 0;
1494 }
1495
1496 /**
1497  * @ingroup StrBuf_Tokenizer
1498  * @brief a string tokenizer to fetch a long integer
1499  * @param Source String containing tokens
1500  * @param parmnum n'th Parameter to extract
1501  * @param separator tokenizer character
1502  * @returns 0 if not found, else long integer representation of the token
1503  */
1504 long StrBufExtract_long(const StrBuf* Source, int parmnum, char separator)
1505 {
1506         StrBuf tmp;
1507         char buf[64];
1508         
1509         tmp.buf = buf;
1510         buf[0] = '\0';
1511         tmp.BufSize = 64;
1512         tmp.BufUsed = 0;
1513         tmp.ConstBuf = 1;
1514         if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
1515                 return(atoi(buf));
1516         else
1517                 return 0;
1518 }
1519
1520
1521 /**
1522  * @ingroup StrBuf_Tokenizer
1523  * @brief a string tokenizer to fetch an unsigned long
1524  * @param Source String containing tokens
1525  * @param parmnum n'th Parameter to extract
1526  * @param separator tokenizer character
1527  * @returns 0 if not found, else unsigned long representation of the token
1528  */
1529 unsigned long StrBufExtract_unsigned_long(const StrBuf* Source, int parmnum, char separator)
1530 {
1531         StrBuf tmp;
1532         char buf[64];
1533         char *pnum;
1534         
1535         tmp.buf = buf;
1536         buf[0] = '\0';
1537         tmp.BufSize = 64;
1538         tmp.BufUsed = 0;
1539         tmp.ConstBuf = 1;
1540         if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0) {
1541                 pnum = &buf[0];
1542                 if (*pnum == '-')
1543                         pnum ++;
1544                 return (unsigned long) atol(pnum);
1545         }
1546         else 
1547                 return 0;
1548 }
1549
1550
1551
1552 /**
1553  * @ingroup StrBuf_NextTokenizer
1554  * @brief a string tokenizer; Bounds checker
1555  *  function to make shure whether StrBufExtract_NextToken and friends have reached the end of the string.
1556  * @param Source our tokenbuffer
1557  * @param pStart the token iterator pointer to inspect
1558  * @returns whether the revolving pointer is inside of the search range
1559  */
1560 int StrBufHaveNextToken(const StrBuf *Source, const char **pStart)
1561 {
1562         if ((Source == NULL) || 
1563             (*pStart == StrBufNOTNULL) ||
1564             (Source->BufUsed == 0))
1565         {
1566                 return 0;
1567         }
1568         if (*pStart == NULL)
1569         {
1570                 return 1;
1571         }
1572         else if (*pStart > Source->buf + Source->BufUsed)
1573         {
1574                 return 0;
1575         }
1576         else if (*pStart <= Source->buf)
1577         {
1578                 return 0;
1579         }
1580
1581         return 1;
1582 }
1583
1584 /**
1585  * @ingroup StrBuf_NextTokenizer
1586  * @brief a string tokenizer
1587  * @param dest Destination StringBuffer
1588  * @param Source StringBuffer to read into
1589  * @param pStart pointer to the end of the last token. Feed with NULL on start.
1590  * @param separator tokenizer 
1591  * @returns -1 if not found, else length of token.
1592  */
1593 int StrBufExtract_NextToken(StrBuf *dest, const StrBuf *Source, const char **pStart, char separator)
1594 {
1595         const char *s;          /* source */
1596         const char *EndBuffer;  /* end stop of source buffer */
1597         int current_token = 0;  /* token currently being processed */
1598         int len = 0;            /* running total length of extracted string */
1599
1600         if ((Source          == NULL) || 
1601             (Source->BufUsed == 0)      ) 
1602         {
1603                 *pStart = StrBufNOTNULL;
1604                 if (dest != NULL)
1605                         FlushStrBuf(dest);
1606                 return -1;
1607         }
1608          
1609         EndBuffer = Source->buf + Source->BufUsed;
1610
1611         if (dest != NULL) 
1612         {
1613                 dest->buf[0] = '\0';
1614                 dest->BufUsed = 0;
1615         }
1616         else
1617         {
1618                 *pStart = EndBuffer + 1;
1619                 return -1;
1620         }
1621
1622         if (*pStart == NULL)
1623         {
1624                 *pStart = Source->buf; /* we're starting to examine this buffer. */
1625         }
1626         else if ((*pStart < Source->buf) || 
1627                  (*pStart > EndBuffer  )   ) 
1628         {
1629                 return -1; /* no more tokens to find. */
1630         }
1631
1632         s = *pStart;
1633         /* start to find the next token */
1634         while ((s <= EndBuffer)      && 
1635                (current_token == 0) ) 
1636         {
1637                 if (*s == separator) 
1638                 {
1639                         /* we found the next token */
1640                         ++current_token;
1641                 }
1642
1643                 if (len >= dest->BufSize) 
1644                 {
1645                         /* our Dest-buffer isn't big enough, increase it. */
1646                         dest->BufUsed = len;
1647
1648                         if (IncreaseBuf(dest, 1, -1) < 0) {
1649                                 /* WHUT? no more mem? bail out. */
1650                                 s = EndBuffer;
1651                                 dest->BufUsed --;
1652                                 break;
1653                         }
1654                 }
1655
1656                 if ( (current_token == 0 ) &&   /* are we in our target token? */
1657                      (!IsEmptyStr(s)     ) &&
1658                      (separator     != *s)    ) /* don't copy the token itself */
1659                 {
1660                         dest->buf[len] = *s;    /* Copy the payload */
1661                         ++len;                  /* remember the bigger size. */
1662                 }
1663
1664                 ++s;
1665         }
1666
1667         /* did we reach the end? */
1668         if ((s > EndBuffer)) {
1669                 EndBuffer = StrBufNOTNULL;
1670                 *pStart = EndBuffer;
1671         }
1672         else {
1673                 *pStart = s;  /* remember the position for the next run */
1674         }
1675
1676         /* sanitize our extracted token */
1677         dest->buf[len] = '\0';
1678         dest->BufUsed  = len;
1679
1680         return (len);
1681 }
1682
1683
1684 /**
1685  * @ingroup StrBuf_NextTokenizer
1686  * @brief a string tokenizer
1687  * @param Source StringBuffer to read from
1688  * @param pStart pointer to the end of the last token. Feed with NULL.
1689  * @param separator tokenizer character
1690  * @param nTokens number of tokens to fastforward over
1691  * @returns -1 if not found, else length of token.
1692  */
1693 int StrBufSkip_NTokenS(const StrBuf *Source, const char **pStart, char separator, int nTokens)
1694 {
1695         const char *s, *EndBuffer;      //* source * /
1696         int len = 0;                    //* running total length of extracted string * /
1697         int current_token = 0;          //* token currently being processed * /
1698
1699         if ((Source == NULL) || 
1700             (Source->BufUsed ==0)) {
1701                 return(-1);
1702         }
1703         if (nTokens == 0)
1704                 return Source->BufUsed;
1705
1706         if (*pStart == NULL)
1707                 *pStart = Source->buf;
1708
1709         EndBuffer = Source->buf + Source->BufUsed;
1710
1711         if ((*pStart < Source->buf) || 
1712             (*pStart >  EndBuffer)) {
1713                 return (-1);
1714         }
1715
1716
1717         s = *pStart;
1718
1719         //cit_backtrace();
1720         //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
1721
1722         while ((s < EndBuffer) && !IsEmptyStr(s)) {
1723                 if (*s == separator) {
1724                         ++current_token;
1725                 }
1726                 if (current_token >= nTokens) {
1727                         break;
1728                 }
1729                 ++s;
1730         }
1731         *pStart = s;
1732         (*pStart) ++;
1733
1734         return(len);
1735 }
1736
1737 /**
1738  * @ingroup StrBuf_NextTokenizer
1739  * @brief a string tokenizer to fetch an integer
1740  * @param Source StringBuffer to read from
1741  * @param pStart Cursor on the tokenstring
1742  * @param separator tokenizer character
1743  * @returns 0 if not found, else integer representation of the token
1744  */
1745 int StrBufExtractNext_int(const StrBuf* Source, const char **pStart, char separator)
1746 {
1747         StrBuf tmp;
1748         char buf[64];
1749         
1750         tmp.buf = buf;
1751         buf[0] = '\0';
1752         tmp.BufSize = 64;
1753         tmp.BufUsed = 0;
1754         tmp.ConstBuf = 1;
1755         if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0)
1756                 return(atoi(buf));
1757         else
1758                 return 0;
1759 }
1760
1761 /**
1762  * @ingroup StrBuf_NextTokenizer
1763  * @brief a string tokenizer to fetch a long integer
1764  * @param Source StringBuffer to read from
1765  * @param pStart Cursor on the tokenstring
1766  * @param separator tokenizer character
1767  * @returns 0 if not found, else long integer representation of the token
1768  */
1769 long StrBufExtractNext_long(const StrBuf* Source, const char **pStart, char separator)
1770 {
1771         StrBuf tmp;
1772         char buf[64];
1773         
1774         tmp.buf = buf;
1775         buf[0] = '\0';
1776         tmp.BufSize = 64;
1777         tmp.BufUsed = 0;
1778         tmp.ConstBuf = 1;
1779         if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0)
1780                 return(atoi(buf));
1781         else
1782                 return 0;
1783 }
1784
1785
1786 /**
1787  * @ingroup StrBuf_NextTokenizer
1788  * @brief a string tokenizer to fetch an unsigned long
1789  * @param Source StringBuffer to read from
1790  * @param pStart Cursor on the tokenstring
1791  * @param separator tokenizer character
1792  * @returns 0 if not found, else unsigned long representation of the token
1793  */
1794 unsigned long StrBufExtractNext_unsigned_long(const StrBuf* Source, const char **pStart, char separator)
1795 {
1796         StrBuf tmp;
1797         char buf[64];
1798         char *pnum;
1799         
1800         tmp.buf = buf;
1801         buf[0] = '\0';
1802         tmp.BufSize = 64;
1803         tmp.BufUsed = 0;
1804         tmp.ConstBuf = 1;
1805         if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0) {
1806                 pnum = &buf[0];
1807                 if (*pnum == '-')
1808                         pnum ++;
1809                 return (unsigned long) atol(pnum);
1810         }
1811         else 
1812                 return 0;
1813 }
1814
1815
1816
1817
1818
1819 /*******************************************************************************
1820  *                             Escape Appending                                *
1821  *******************************************************************************/
1822
1823 /** 
1824  * @ingroup StrBuf_DeEnCoder
1825  * @brief Escape a string for feeding out as a URL while appending it to a Buffer
1826  * @param OutBuf the output buffer
1827  * @param In Buffer to encode
1828  * @param PlainIn way in from plain old c strings
1829  */
1830 void StrBufUrlescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
1831 {
1832         const char *pch, *pche;
1833         char *pt, *pte;
1834         int len;
1835         
1836         if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1837                 return;
1838         if (PlainIn != NULL) {
1839                 len = strlen(PlainIn);
1840                 pch = PlainIn;
1841                 pche = pch + len;
1842         }
1843         else {
1844                 pch = In->buf;
1845                 pche = pch + In->BufUsed;
1846                 len = In->BufUsed;
1847         }
1848
1849         if (len == 0) 
1850                 return;
1851
1852         pt = OutBuf->buf + OutBuf->BufUsed;
1853         pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1854
1855         while (pch < pche) {
1856                 if (pt >= pte) {
1857                         IncreaseBuf(OutBuf, 1, -1);
1858                         pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1859                         pt = OutBuf->buf + OutBuf->BufUsed;
1860                 }
1861
1862                 if((*pch >= 'a' && *pch <= 'z') ||
1863                    (*pch >= '@' && *pch <= 'Z') || /* @ A-Z */
1864                    (*pch >= '0' && *pch <= ':') || /* 0-9 : */
1865                    (*pch == '!') || (*pch == '_') || 
1866                    (*pch == ',') || (*pch == '.'))
1867                 {
1868                         *(pt++) = *(pch++);
1869                         OutBuf->BufUsed++;
1870                 }                       
1871                 else {
1872                         *pt = '%';
1873                         *(pt + 1) = HexList[(unsigned char)*pch][0];
1874                         *(pt + 2) = HexList[(unsigned char)*pch][1];
1875                         pt += 3;
1876                         OutBuf->BufUsed += 3;
1877                         pch ++;
1878                 }
1879         }
1880         *pt = '\0';
1881 }
1882
1883 /** 
1884  * @ingroup StrBuf_DeEnCoder
1885  * @brief Escape a string for feeding out as a the username/password part of an URL while appending it to a Buffer
1886  * @param OutBuf the output buffer
1887  * @param In Buffer to encode
1888  * @param PlainIn way in from plain old c strings
1889  */
1890 void StrBufUrlescUPAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
1891 {
1892         const char *pch, *pche;
1893         char *pt, *pte;
1894         int len;
1895         
1896         if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1897                 return;
1898         if (PlainIn != NULL) {
1899                 len = strlen(PlainIn);
1900                 pch = PlainIn;
1901                 pche = pch + len;
1902         }
1903         else {
1904                 pch = In->buf;
1905                 pche = pch + In->BufUsed;
1906                 len = In->BufUsed;
1907         }
1908
1909         if (len == 0) 
1910                 return;
1911
1912         pt = OutBuf->buf + OutBuf->BufUsed;
1913         pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1914
1915         while (pch < pche) {
1916                 if (pt >= pte) {
1917                         IncreaseBuf(OutBuf, 1, -1);
1918                         pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1919                         pt = OutBuf->buf + OutBuf->BufUsed;
1920                 }
1921
1922                 if((*pch >= 'a' && *pch <= 'z') ||
1923                    (*pch >= 'A' && *pch <= 'Z') || /* A-Z */
1924                    (*pch >= '0' && *pch <= ':') || /* 0-9 : */
1925                    (*pch == '!') || (*pch == '_') || 
1926                    (*pch == ',') || (*pch == '.'))
1927                 {
1928                         *(pt++) = *(pch++);
1929                         OutBuf->BufUsed++;
1930                 }                       
1931                 else {
1932                         *pt = '%';
1933                         *(pt + 1) = HexList[(unsigned char)*pch][0];
1934                         *(pt + 2) = HexList[(unsigned char)*pch][1];
1935                         pt += 3;
1936                         OutBuf->BufUsed += 3;
1937                         pch ++;
1938                 }
1939         }
1940         *pt = '\0';
1941 }
1942
1943 /** 
1944  * @ingroup StrBuf_DeEnCoder
1945  * @brief append a string with characters having a special meaning in xml encoded to the buffer
1946  * @param OutBuf the output buffer
1947  * @param In Buffer to encode
1948  * @param PlainIn way in from plain old c strings
1949  * @param PlainInLen way in from plain old c strings; maybe you've got binary data or know the length?
1950  * @param OverrideLowChars should chars < 0x20 be replaced by _ or escaped as xml entity?
1951  */
1952 void StrBufXMLEscAppend(StrBuf *OutBuf,
1953                         const StrBuf *In,
1954                         const char *PlainIn,
1955                         long PlainInLen,
1956                         int OverrideLowChars)
1957 {
1958         const char *pch, *pche;
1959         char *pt, *pte;
1960         int IsUtf8Sequence;
1961         int len;
1962
1963         if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1964                 return;
1965         if (PlainIn != NULL) {
1966                 if (PlainInLen < 0)
1967                         len = strlen((const char*)PlainIn);
1968                 else
1969                         len = PlainInLen;
1970                 pch = PlainIn;
1971                 pche = pch + len;
1972         }
1973         else {
1974                 pch = (const char*)In->buf;
1975                 pche = pch + In->BufUsed;
1976                 len = In->BufUsed;
1977         }
1978
1979         if (len == 0)
1980                 return;
1981
1982         pt = OutBuf->buf + OutBuf->BufUsed;
1983         /**< we max append 6 chars at once plus the \0 */
1984         pte = OutBuf->buf + OutBuf->BufSize - 6;
1985
1986         while (pch < pche) {
1987                 if (pt >= pte) {
1988                         OutBuf->BufUsed = pt - OutBuf->buf;
1989                         IncreaseBuf(OutBuf, 1, -1);
1990                         pte = OutBuf->buf + OutBuf->BufSize - 6;
1991                         /**< we max append 3 chars at once plus the \0 */
1992
1993                         pt = OutBuf->buf + OutBuf->BufUsed;
1994                 }
1995
1996                 if (*pch == '<') {
1997                         memcpy(pt, HKEY("&lt;"));
1998                         pt += 4;
1999                         pch ++;
2000                 }
2001                 else if (*pch == '>') {
2002                         memcpy(pt, HKEY("&gt;"));
2003                         pt += 4;
2004                         pch ++;
2005                 }
2006                 else if (*pch == '&') {
2007                         memcpy(pt, HKEY("&amp;"));
2008                         pt += 5;
2009                         pch++;
2010                 }
2011                 else if ((*pch >= 0x20) && (*pch <= 0x7F)) {
2012                         *pt = *pch;
2013                         pt++; pch++;
2014                 }
2015                 else {
2016                         IsUtf8Sequence =  Ctdl_GetUtf8SequenceLength(pch, pche);
2017                         if (IsUtf8Sequence)
2018                         {
2019                                 while ((IsUtf8Sequence > 0) && 
2020                                        (pch < pche))
2021                                 {
2022                                         *pt = *pch;
2023                                         pt ++;
2024                                         pch ++;
2025                                         --IsUtf8Sequence;
2026                                 }
2027                         }
2028                         else
2029                         {
2030                                 *pt = '&';
2031                                 pt++;
2032                                 *pt = HexList[*(unsigned char*)pch][0];
2033                                 pt ++;
2034                                 *pt = HexList[*(unsigned char*)pch][1];
2035                                 pt ++; pch ++;
2036                                 *pt = '&';
2037                                 pt++;
2038                                 pch ++;
2039                         }
2040                 }
2041         }
2042         *pt = '\0';
2043         OutBuf->BufUsed = pt - OutBuf->buf;
2044 }
2045
2046
2047 /** 
2048  * @ingroup StrBuf_DeEnCoder
2049  * @brief append a string in hex encoding to the buffer
2050  * @param OutBuf the output buffer
2051  * @param In Buffer to encode
2052  * @param PlainIn way in from plain old c strings
2053  * @param PlainInLen way in from plain old c strings; maybe you've got binary data or know the length?
2054  */
2055 void StrBufHexEscAppend(StrBuf *OutBuf, const StrBuf *In, const unsigned char *PlainIn, long PlainInLen)
2056 {
2057         const unsigned char *pch, *pche;
2058         char *pt, *pte;
2059         int len;
2060         
2061         if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
2062                 return;
2063         if (PlainIn != NULL) {
2064                 if (PlainInLen < 0)
2065                         len = strlen((const char*)PlainIn);
2066                 else
2067                         len = PlainInLen;
2068                 pch = PlainIn;
2069                 pche = pch + len;
2070         }
2071         else {
2072                 pch = (const unsigned char*)In->buf;
2073                 pche = pch + In->BufUsed;
2074                 len = In->BufUsed;
2075         }
2076
2077         if (len == 0) 
2078                 return;
2079
2080         pt = OutBuf->buf + OutBuf->BufUsed;
2081         pte = OutBuf->buf + OutBuf->BufSize - 3; /**< we max append 3 chars at once plus the \0 */
2082
2083         while (pch < pche) {
2084                 if (pt >= pte) {
2085                         IncreaseBuf(OutBuf, 1, -1);
2086                         pte = OutBuf->buf + OutBuf->BufSize - 3; /**< we max append 3 chars at once plus the \0 */
2087                         pt = OutBuf->buf + OutBuf->BufUsed;
2088                 }
2089
2090                 *pt = HexList[*pch][0];
2091                 pt ++;
2092                 *pt = HexList[*pch][1];
2093                 pt ++; pch ++; OutBuf->BufUsed += 2;
2094         }
2095         *pt = '\0';
2096 }
2097
2098 void StrBufBase64Append(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn, long PlainInLen, int linebreaks)
2099 {
2100         const char *pch;
2101         char *pt;
2102         int len;
2103         long ExpectLen;
2104         
2105         if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
2106                 return;
2107         if (PlainIn != NULL) {
2108                 if (PlainInLen < 0)
2109                         len = strlen(PlainIn);
2110                 else
2111                         len = PlainInLen;
2112                 pch = PlainIn;
2113         }
2114         else {
2115                 pch = In->buf;
2116                 len = In->BufUsed;
2117         }
2118
2119         if (len == 0) 
2120                 return;
2121
2122         ExpectLen = ((len * 134) / 100) + OutBuf->BufUsed;
2123
2124         if (ExpectLen > OutBuf->BufSize)
2125                 if (IncreaseBuf(OutBuf, 1, ExpectLen) < ExpectLen)
2126                         return;
2127
2128         pt = OutBuf->buf + OutBuf->BufUsed;
2129
2130         len = CtdlEncodeBase64(pt, pch, len, linebreaks);
2131
2132         pt += len;
2133         OutBuf->BufUsed += len;
2134         *pt = '\0';
2135 }
2136
2137 /** 
2138  * @ingroup StrBuf_DeEnCoder
2139  * @brief append a string in hex encoding to the buffer
2140  * @param OutBuf the output buffer
2141  * @param In Buffer to encode
2142  * @param PlainIn way in from plain old c strings
2143  */
2144 void StrBufHexescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
2145 {
2146         StrBufHexEscAppend(OutBuf, In, (const unsigned char*) PlainIn, -1);
2147 }
2148
2149 /**
2150  * @ingroup StrBuf_DeEnCoder
2151  * @brief Append a string, escaping characters which have meaning in HTML.  
2152  *
2153  * @param Target        target buffer
2154  * @param Source        source buffer; set to NULL if you just have a C-String
2155  * @param PlainIn       Plain-C string to append; set to NULL if unused
2156  * @param nbsp          If nonzero, spaces are converted to non-breaking spaces.
2157  * @param nolinebreaks  if set to 1, linebreaks are removed from the string.
2158  *                      if set to 2, linebreaks are replaced by &ltbr/&gt
2159  */
2160 long StrEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
2161 {
2162         const char *aptr, *eiptr;
2163         char *bptr, *eptr;
2164         long len;
2165
2166         if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2167                 return -1;
2168
2169         if (PlainIn != NULL) {
2170                 aptr = PlainIn;
2171                 len = strlen(PlainIn);
2172                 eiptr = aptr + len;
2173         }
2174         else {
2175                 aptr = Source->buf;
2176                 eiptr = aptr + Source->BufUsed;
2177                 len = Source->BufUsed;
2178         }
2179
2180         if (len == 0) 
2181                 return -1;
2182
2183         bptr = Target->buf + Target->BufUsed;
2184         eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in...  */
2185
2186         while (aptr < eiptr){
2187                 if(bptr >= eptr) {
2188                         IncreaseBuf(Target, 1, -1);
2189                         eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in...  */
2190                         bptr = Target->buf + Target->BufUsed;
2191                 }
2192                 if (*aptr == '<') {
2193                         memcpy(bptr, "&lt;", 4);
2194                         bptr += 4;
2195                         Target->BufUsed += 4;
2196                 }
2197                 else if (*aptr == '>') {
2198                         memcpy(bptr, "&gt;", 4);
2199                         bptr += 4;
2200                         Target->BufUsed += 4;
2201                 }
2202                 else if (*aptr == '&') {
2203                         memcpy(bptr, "&amp;", 5);
2204                         bptr += 5;
2205                         Target->BufUsed += 5;
2206                 }
2207                 else if (*aptr == '"') {
2208                         memcpy(bptr, "&quot;", 6);
2209                         bptr += 6;
2210                         Target->BufUsed += 6;
2211                 }
2212                 else if (*aptr == '\'') {
2213                         memcpy(bptr, "&#39;", 5);
2214                         bptr += 5;
2215                         Target->BufUsed += 5;
2216                 }
2217                 else if (*aptr == LB) {
2218                         *bptr = '<';
2219                         bptr ++;
2220                         Target->BufUsed ++;
2221                 }
2222                 else if (*aptr == RB) {
2223                         *bptr = '>';
2224                         bptr ++;
2225                         Target->BufUsed ++;
2226                 }
2227                 else if (*aptr == QU) {
2228                         *bptr ='"';
2229                         bptr ++;
2230                         Target->BufUsed ++;
2231                 }
2232                 else if ((*aptr == 32) && (nbsp == 1)) {
2233                         memcpy(bptr, "&nbsp;", 6);
2234                         bptr += 6;
2235                         Target->BufUsed += 6;
2236                 }
2237                 else if ((*aptr == '\n') && (nolinebreaks == 1)) {
2238                         *bptr='\0';     /* nothing */
2239                 }
2240                 else if ((*aptr == '\n') && (nolinebreaks == 2)) {
2241                         memcpy(bptr, "&lt;br/&gt;", 11);
2242                         bptr += 11;
2243                         Target->BufUsed += 11;
2244                 }
2245
2246
2247                 else if ((*aptr == '\r') && (nolinebreaks != 0)) {
2248                         *bptr='\0';     /* nothing */
2249                 }
2250                 else{
2251                         *bptr = *aptr;
2252                         bptr++;
2253                         Target->BufUsed ++;
2254                 }
2255                 aptr ++;
2256         }
2257         *bptr = '\0';
2258         if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
2259                 return -1;
2260         return Target->BufUsed;
2261 }
2262
2263 /**
2264  * @ingroup StrBuf_DeEnCoder
2265  * @brief Append a string, escaping characters which have meaning in HTML.  
2266  * Converts linebreaks into blanks; escapes single quotes
2267  * @param Target        target buffer
2268  * @param Source        source buffer; set to NULL if you just have a C-String
2269  * @param PlainIn       Plain-C string to append; set to NULL if unused
2270  */
2271 void StrMsgEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2272 {
2273         const char *aptr, *eiptr;
2274         char *tptr, *eptr;
2275         long len;
2276
2277         if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2278                 return ;
2279
2280         if (PlainIn != NULL) {
2281                 aptr = PlainIn;
2282                 len = strlen(PlainIn);
2283                 eiptr = aptr + len;
2284         }
2285         else {
2286                 aptr = Source->buf;
2287                 eiptr = aptr + Source->BufUsed;
2288                 len = Source->BufUsed;
2289         }
2290
2291         if (len == 0) 
2292                 return;
2293
2294         eptr = Target->buf + Target->BufSize - 8; 
2295         tptr = Target->buf + Target->BufUsed;
2296         
2297         while (aptr < eiptr){
2298                 if(tptr >= eptr) {
2299                         IncreaseBuf(Target, 1, -1);
2300                         eptr = Target->buf + Target->BufSize - 8; 
2301                         tptr = Target->buf + Target->BufUsed;
2302                 }
2303                
2304                 if (*aptr == '\n') {
2305                         *tptr = ' ';
2306                         Target->BufUsed++;
2307                 }
2308                 else if (*aptr == '\r') {
2309                         *tptr = ' ';
2310                         Target->BufUsed++;
2311                 }
2312                 else if (*aptr == '\'') {
2313                         *(tptr++) = '&';
2314                         *(tptr++) = '#';
2315                         *(tptr++) = '3';
2316                         *(tptr++) = '9';
2317                         *tptr = ';';
2318                         Target->BufUsed += 5;
2319                 } else {
2320                         *tptr = *aptr;
2321                         Target->BufUsed++;
2322                 }
2323                 tptr++; aptr++;
2324         }
2325         *tptr = '\0';
2326 }
2327
2328
2329
2330 /**
2331  * @ingroup StrBuf_DeEnCoder
2332  * @brief Append a string, escaping characters which have meaning in ICAL.  
2333  * [\n,] 
2334  * @param Target        target buffer
2335  * @param Source        source buffer; set to NULL if you just have a C-String
2336  * @param PlainIn       Plain-C string to append; set to NULL if unused
2337  */
2338 void StrIcalEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2339 {
2340         const char *aptr, *eiptr;
2341         char *tptr, *eptr;
2342         long len;
2343
2344         if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2345                 return ;
2346
2347         if (PlainIn != NULL) {
2348                 aptr = PlainIn;
2349                 len = strlen(PlainIn);
2350                 eiptr = aptr + len;
2351         }
2352         else {
2353                 aptr = Source->buf;
2354                 eiptr = aptr + Source->BufUsed;
2355                 len = Source->BufUsed;
2356         }
2357
2358         if (len == 0) 
2359                 return;
2360
2361         eptr = Target->buf + Target->BufSize - 8; 
2362         tptr = Target->buf + Target->BufUsed;
2363         
2364         while (aptr < eiptr){
2365                 if(tptr + 3 >= eptr) {
2366                         IncreaseBuf(Target, 1, -1);
2367                         eptr = Target->buf + Target->BufSize - 8; 
2368                         tptr = Target->buf + Target->BufUsed;
2369                 }
2370                
2371                 if (*aptr == '\n') {
2372                         *tptr = '\\';
2373                         Target->BufUsed++;
2374                         tptr++;
2375                         *tptr = 'n';
2376                         Target->BufUsed++;
2377                 }
2378                 else if (*aptr == '\r') {
2379                         *tptr = '\\';
2380                         Target->BufUsed++;
2381                         tptr++;
2382                         *tptr = 'r';
2383                         Target->BufUsed++;
2384                 }
2385                 else if (*aptr == ',') {
2386                         *tptr = '\\';
2387                         Target->BufUsed++;
2388                         tptr++;
2389                         *tptr = ',';
2390                         Target->BufUsed++;
2391                 } else {
2392                         *tptr = *aptr;
2393                         Target->BufUsed++;
2394                 }
2395                 tptr++; aptr++;
2396         }
2397         *tptr = '\0';
2398 }
2399
2400 /**
2401  * @ingroup StrBuf_DeEnCoder
2402  * @brief Append a string, escaping characters which have meaning in JavaScript strings .  
2403  *
2404  * @param Target        target buffer
2405  * @param Source        source buffer; set to NULL if you just have a C-String
2406  * @param PlainIn       Plain-C string to append; set to NULL if unused
2407  * @returns size of result or -1
2408  */
2409 long StrECMAEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2410 {
2411         const char *aptr, *eiptr;
2412         char *bptr, *eptr;
2413         long len;
2414         int IsUtf8Sequence;
2415
2416         if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2417                 return -1;
2418
2419         if (PlainIn != NULL) {
2420                 aptr = PlainIn;
2421                 len = strlen(PlainIn);
2422                 eiptr = aptr + len;
2423         }
2424         else {
2425                 aptr = Source->buf;
2426                 eiptr = aptr + Source->BufUsed;
2427                 len = Source->BufUsed;
2428         }
2429
2430         if (len == 0) 
2431                 return -1;
2432
2433         bptr = Target->buf + Target->BufUsed;
2434         eptr = Target->buf + Target->BufSize - 7; /* our biggest unit to put in...  */
2435
2436         while (aptr < eiptr){
2437                 if(bptr >= eptr) {
2438                         IncreaseBuf(Target, 1, -1);
2439                         eptr = Target->buf + Target->BufSize - 7; /* our biggest unit to put in...  */
2440                         bptr = Target->buf + Target->BufUsed;
2441                 }
2442                 switch (*aptr) {
2443                 case '\n':
2444                         memcpy(bptr, HKEY("\\n"));
2445                         bptr += 2;
2446                         Target->BufUsed += 2;                           
2447                         break;
2448                 case '\r':
2449                         memcpy(bptr, HKEY("\\r"));
2450                         bptr += 2;
2451                         Target->BufUsed += 2;
2452                         break;
2453                 case '"':
2454                         *bptr = '\\';
2455                         bptr ++;
2456                         *bptr = '"';
2457                         bptr ++;
2458                         Target->BufUsed += 2;
2459                         break;
2460                 case '\\':
2461                         if ((*(aptr + 1) == 'u') &&
2462                             isxdigit(*(aptr + 2)) &&
2463                             isxdigit(*(aptr + 3)) &&
2464                             isxdigit(*(aptr + 4)) &&
2465                             isxdigit(*(aptr + 5)))
2466                         { /* oh, a unicode escaper. let it pass through. */
2467                                 memcpy(bptr, aptr, 6);
2468                                 aptr += 5;
2469                                 bptr +=6;
2470                                 Target->BufUsed += 6;
2471                         }
2472                         else 
2473                         {
2474                                 *bptr = '\\';
2475                                 bptr ++;
2476                                 *bptr = '\\';
2477                                 bptr ++;
2478                                 Target->BufUsed += 2;
2479                         }
2480                         break;
2481                 case '\b':
2482                         *bptr = '\\';
2483                         bptr ++;
2484                         *bptr = 'b';
2485                         bptr ++;
2486                         Target->BufUsed += 2;
2487                         break;
2488                 case '\f':
2489                         *bptr = '\\';
2490                         bptr ++;
2491                         *bptr = 'f';
2492                         bptr ++;
2493                         Target->BufUsed += 2;
2494                         break;
2495                 case '\t':
2496                         *bptr = '\\';
2497                         bptr ++;
2498                         *bptr = 't';
2499                         bptr ++;
2500                         Target->BufUsed += 2;
2501                         break;
2502                 default:
2503                         IsUtf8Sequence =  Ctdl_GetUtf8SequenceLength(aptr, eiptr);
2504                         while (IsUtf8Sequence > 0){
2505                                 *bptr = *aptr;
2506                                 Target->BufUsed ++;
2507                                 if (--IsUtf8Sequence)
2508                                         aptr++;
2509                                 bptr++;
2510                         }
2511                 }
2512                 aptr ++;
2513         }
2514         *bptr = '\0';
2515         if ((bptr == eptr - 1 ) && !IsEmptyStr(aptr) )
2516                 return -1;
2517         return Target->BufUsed;
2518 }
2519
2520 /**
2521  * @ingroup StrBuf_DeEnCoder
2522  * @brief Append a string, escaping characters which have meaning in HTML + json.  
2523  *
2524  * @param Target        target buffer
2525  * @param Source        source buffer; set to NULL if you just have a C-String
2526  * @param PlainIn       Plain-C string to append; set to NULL if unused
2527  * @param nbsp          If nonzero, spaces are converted to non-breaking spaces.
2528  * @param nolinebreaks  if set to 1, linebreaks are removed from the string.
2529  *                      if set to 2, linebreaks are replaced by &ltbr/&gt
2530  */
2531 long StrHtmlEcmaEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
2532 {
2533         const char *aptr, *eiptr;
2534         char *bptr, *eptr;
2535         long len;
2536         int IsUtf8Sequence = 0;
2537
2538         if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2539                 return -1;
2540
2541         if (PlainIn != NULL) {
2542                 aptr = PlainIn;
2543                 len = strlen(PlainIn);
2544                 eiptr = aptr + len;
2545         }
2546         else {
2547                 aptr = Source->buf;
2548                 eiptr = aptr + Source->BufUsed;
2549                 len = Source->BufUsed;
2550         }
2551
2552         if (len == 0) 
2553                 return -1;
2554
2555         bptr = Target->buf + Target->BufUsed;
2556         eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in...  */
2557
2558         while (aptr < eiptr){
2559                 if(bptr >= eptr) {
2560                         IncreaseBuf(Target, 1, -1);
2561                         eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in...  */
2562                         bptr = Target->buf + Target->BufUsed;
2563                 }
2564                 switch (*aptr) {
2565                 case '<':
2566                         memcpy(bptr, HKEY("&lt;"));
2567                         bptr += 4;
2568                         Target->BufUsed += 4;
2569                         break;
2570                 case '>':
2571                         memcpy(bptr, HKEY("&gt;"));
2572                         bptr += 4;
2573                         Target->BufUsed += 4;
2574                         break;
2575                 case '&':
2576                         memcpy(bptr, HKEY("&amp;"));
2577                         bptr += 5;
2578                         Target->BufUsed += 5;
2579                         break;
2580                 case LB:
2581                         *bptr = '<';
2582                         bptr ++;
2583                         Target->BufUsed ++;
2584                         break;
2585                 case RB:
2586                         *bptr = '>';
2587                         bptr ++;
2588                         Target->BufUsed ++;
2589                         break;
2590                 case '\n':
2591                         switch (nolinebreaks) {
2592                         case 1:
2593                                 *bptr='\0';     /* nothing */
2594                                 break;
2595                         case 2:
2596                                 memcpy(bptr, HKEY("&lt;br/&gt;"));
2597                                 bptr += 11;
2598                                 Target->BufUsed += 11;
2599                                 break;
2600                         default:
2601                                 memcpy(bptr, HKEY("\\n"));
2602                                 bptr += 2;
2603                                 Target->BufUsed += 2;                           
2604                         }
2605                         break;
2606                 case '\r':
2607                         switch (nolinebreaks) {
2608                         case 1:
2609                         case 2:
2610                                 *bptr='\0';     /* nothing */
2611                                 break;
2612                         default:
2613                                 memcpy(bptr, HKEY("\\r"));
2614                                 bptr += 2;
2615                                 Target->BufUsed += 2;
2616                                 break;
2617                         }
2618                         break;
2619                 case '"':
2620                 case QU:
2621                         *bptr = '\\';
2622                         bptr ++;
2623                         *bptr = '"';
2624                         bptr ++;
2625                         Target->BufUsed += 2;
2626                         break;
2627                 case '\\':
2628                         if ((*(aptr + 1) == 'u') &&
2629                             isxdigit(*(aptr + 2)) &&
2630                             isxdigit(*(aptr + 3)) &&
2631                             isxdigit(*(aptr + 4)) &&
2632                             isxdigit(*(aptr + 5)))
2633                         { /* oh, a unicode escaper. let it pass through. */
2634                                 memcpy(bptr, aptr, 6);
2635                                 aptr += 5;
2636                                 bptr +=6;
2637                                 Target->BufUsed += 6;
2638                         }
2639                         else 
2640                         {
2641                                 *bptr = '\\';
2642                                 bptr ++;
2643                                 *bptr = '\\';
2644                                 bptr ++;
2645                                 Target->BufUsed += 2;
2646                         }
2647                         break;
2648                 case '\b':
2649                         *bptr = '\\';
2650                         bptr ++;
2651                         *bptr = 'b';
2652                         bptr ++;
2653                         Target->BufUsed += 2;
2654                         break;
2655                 case '\f':
2656                         *bptr = '\\';
2657                         bptr ++;
2658                         *bptr = 'f';
2659                         bptr ++;
2660                         Target->BufUsed += 2;
2661                         break;
2662                 case '\t':
2663                         *bptr = '\\';
2664                         bptr ++;
2665                         *bptr = 't';
2666                         bptr ++;
2667                         Target->BufUsed += 2;
2668                         break;
2669                 case  32:
2670                         if (nbsp == 1) {
2671                                 memcpy(bptr, HKEY("&nbsp;"));
2672                                 bptr += 6;
2673                                 Target->BufUsed += 6;
2674                                 break;
2675                         }
2676                 default:
2677                         IsUtf8Sequence =  Ctdl_GetUtf8SequenceLength(aptr, eiptr);
2678                         while (IsUtf8Sequence > 0){
2679                                 *bptr = *aptr;
2680                                 Target->BufUsed ++;
2681                                 if (--IsUtf8Sequence)
2682                                         aptr++;
2683                                 bptr++;
2684                         }
2685                 }
2686                 aptr ++;
2687         }
2688         *bptr = '\0';
2689         if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
2690                 return -1;
2691         return Target->BufUsed;
2692 }
2693
2694
2695 /**
2696  * @ingroup StrBuf_DeEnCoder
2697  * @brief replace all non-Ascii characters by another
2698  * @param Buf buffer to inspect
2699  * @param repl charater to stamp over non ascii chars
2700  */
2701 void StrBufAsciify(StrBuf *Buf, const char repl)
2702 {
2703         long offset;
2704
2705         for (offset = 0; offset < Buf->BufUsed; offset ++)
2706                 if (!isascii(Buf->buf[offset]))
2707                         Buf->buf[offset] = repl;
2708         
2709 }
2710
2711 /**
2712  * @ingroup StrBuf_DeEnCoder
2713  * @brief unhide special chars hidden to the HTML escaper
2714  * @param target buffer to put the unescaped string in
2715  * @param source buffer to unescape
2716  */
2717 void StrBufEUid_unescapize(StrBuf *target, const StrBuf *source) 
2718 {
2719         int a, b, len;
2720         char hex[3];
2721
2722         if ((source == NULL) || (target == NULL) || (target->buf == NULL))
2723         {
2724                 return;
2725         }
2726
2727         if (target != NULL)
2728                 FlushStrBuf(target);
2729
2730         len = source->BufUsed;
2731         for (a = 0; a < len; ++a) {
2732                 if (target->BufUsed >= target->BufSize)
2733                         IncreaseBuf(target, 1, -1);
2734
2735                 if (source->buf[a] == '=') {
2736                         hex[0] = source->buf[a + 1];
2737                         hex[1] = source->buf[a + 2];
2738                         hex[2] = 0;
2739                         b = 0;
2740                         sscanf(hex, "%02x", &b);
2741                         target->buf[target->BufUsed] = b;
2742                         target->buf[++target->BufUsed] = 0;
2743                         a += 2;
2744                 }
2745                 else {
2746                         target->buf[target->BufUsed] = source->buf[a];
2747                         target->buf[++target->BufUsed] = 0;
2748                 }
2749         }
2750 }
2751
2752
2753 /**
2754  * @ingroup StrBuf_DeEnCoder
2755  * @brief hide special chars from the HTML escapers and friends
2756  * @param target buffer to put the escaped string in
2757  * @param source buffer to escape
2758  */
2759 void StrBufEUid_escapize(StrBuf *target, const StrBuf *source) 
2760 {
2761         int i, len;
2762
2763         if (target != NULL)
2764                 FlushStrBuf(target);
2765
2766         if ((source == NULL) || (target == NULL) || (target->buf == NULL))
2767         {
2768                 return;
2769         }
2770
2771         len = source->BufUsed;
2772         for (i=0; i<len; ++i) {
2773                 if (target->BufUsed + 4 >= target->BufSize)
2774                         IncreaseBuf(target, 1, -1);
2775                 if ( (isalnum(source->buf[i])) || 
2776                      (source->buf[i]=='-') || 
2777                      (source->buf[i]=='_') ) {
2778                         target->buf[target->BufUsed++] = source->buf[i];
2779                 }
2780                 else {
2781                         sprintf(&target->buf[target->BufUsed], 
2782                                 "=%02X", 
2783                                 (0xFF &source->buf[i]));
2784                         target->BufUsed += 3;
2785                 }
2786         }
2787         target->buf[target->BufUsed + 1] = '\0';
2788 }
2789
2790
2791 /*******************************************************************************
2792  *                      Quoted Printable de/encoding                           *
2793  *******************************************************************************/
2794
2795 /**
2796  * @ingroup StrBuf_DeEnCoder
2797  * @brief decode a buffer from base 64 encoding; destroys original
2798  * @param Buf Buffor to transform
2799  */
2800 int StrBufDecodeBase64(StrBuf *Buf)
2801 {
2802         char *xferbuf;
2803         size_t siz;
2804
2805         if (Buf == NULL)
2806                 return -1;
2807
2808         xferbuf = (char*) malloc(Buf->BufSize);
2809         if (xferbuf == NULL)
2810                 return -1;
2811
2812         *xferbuf = '\0';
2813         siz = CtdlDecodeBase64(xferbuf,
2814                                Buf->buf,
2815                                Buf->BufUsed);
2816         free(Buf->buf);
2817         Buf->buf = xferbuf;
2818         Buf->BufUsed = siz;
2819
2820         Buf->buf[Buf->BufUsed] = '\0';
2821         return siz;
2822 }
2823
2824 /**
2825  * @ingroup StrBuf_DeEnCoder
2826  * @brief decode a buffer from base 64 encoding; expects targetbuffer
2827  * @param BufIn Buffor to transform
2828  * @param BufOut Buffer to put result into
2829  */
2830 int StrBufDecodeBase64To(const StrBuf *BufIn, StrBuf *BufOut)
2831 {
2832         if ((BufIn == NULL) || (BufOut == NULL))
2833                 return -1;
2834
2835         if (BufOut->BufSize < BufIn->BufUsed)
2836                 IncreaseBuf(BufOut, 0, BufIn->BufUsed);
2837
2838         BufOut->BufUsed = CtdlDecodeBase64(BufOut->buf,
2839                                            BufIn->buf,
2840                                            BufIn->BufUsed);
2841         return BufOut->BufUsed;
2842 }
2843
2844 typedef struct __z_enc_stream {
2845         StrBuf OutBuf;
2846         z_stream zstream;
2847 } z_enc_stream;
2848
2849 vStreamT *StrBufNewStreamContext(eStreamType type, const char **Err)
2850 {
2851         base64_decodestate *state;;
2852         *Err = NULL;
2853
2854         switch (type)
2855         {
2856         case eBase64Decode:
2857         case eBase64Encode:
2858                 state = (base64_decodestate*) malloc(sizeof(base64_decodestate));
2859                 base64_init_decodestate(state);
2860                 return (vStreamT*) state;
2861                 break;
2862         case eZLibDecode:
2863         {
2864
2865                 z_enc_stream *stream;
2866                 int err;
2867
2868                 stream = (z_enc_stream *) malloc(sizeof(z_enc_stream));
2869                 memset(stream, 0, sizeof(z_enc_stream));
2870                 stream->OutBuf.BufSize = 4*SIZ; /// TODO 64
2871                 stream->OutBuf.buf = (char*)malloc(stream->OutBuf.BufSize);
2872
2873                 err = inflateInit(&stream->zstream);
2874
2875                 if (err != Z_OK) {
2876                         StrBufDestroyStreamContext(type, (vStreamT**) &stream, Err);
2877                         *Err = zError(err);
2878                         return NULL;
2879                 }
2880                 return (vStreamT*) stream;
2881
2882         }
2883         case eZLibEncode:
2884         {
2885                 z_enc_stream *stream;
2886                 int err;
2887
2888                 stream = (z_enc_stream *) malloc(sizeof(z_enc_stream));
2889                 memset(stream, 0, sizeof(z_enc_stream));
2890                 stream->OutBuf.BufSize = 4*SIZ; /// todo 64
2891                 stream->OutBuf.buf = (char*)malloc(stream->OutBuf.BufSize);
2892                 /* write gzip header */
2893                 stream->OutBuf.BufUsed = snprintf
2894                         (stream->OutBuf.buf,
2895                          stream->OutBuf.BufSize, 
2896                          "%c%c%c%c%c%c%c%c%c%c",
2897                          gz_magic[0], gz_magic[1], Z_DEFLATED,
2898                          0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /* xflags */ ,
2899                          OS_CODE);
2900
2901                 err = deflateInit2(&stream->zstream,
2902                                    ZLibCompressionRatio,
2903                                    Z_DEFLATED,
2904                                    -MAX_WBITS,
2905                                    DEF_MEM_LEVEL,
2906                                    Z_DEFAULT_STRATEGY);
2907                 if (err != Z_OK) {
2908                         StrBufDestroyStreamContext(type, (vStreamT**) &stream, Err);
2909                         *Err = zError(err);
2910                         return NULL;
2911                 }
2912                 return (vStreamT*) stream;
2913         }
2914         case eEmtyCodec:
2915                 /// TODO
2916
2917                 break;
2918         }
2919         return NULL;
2920 }
2921
2922 int StrBufDestroyStreamContext(eStreamType type, vStreamT **vStream, const char **Err)
2923 {
2924         int err;
2925         int rc = 0;
2926         *Err = NULL;
2927         
2928         if ((vStream == NULL) || (*vStream==NULL)) {
2929                 *Err = strerror(EINVAL);
2930                 return EINVAL;
2931         }
2932         switch (type)
2933         {
2934         case eBase64Encode:
2935         case eBase64Decode:
2936                 free(*vStream);
2937                 *vStream = NULL;
2938                 break;
2939         case eZLibDecode:
2940         {
2941                 z_enc_stream *stream = (z_enc_stream *)*vStream;
2942                 (void)inflateEnd(&stream->zstream);
2943                 free(stream->OutBuf.buf);
2944                 free(stream);
2945         }
2946         case eZLibEncode:
2947         {
2948                 z_enc_stream *stream = (z_enc_stream *)*vStream;
2949                 err = deflateEnd(&stream->zstream);
2950                 if (err != Z_OK) {
2951                         *Err = zError(err);
2952                         rc = -1;
2953                 }
2954                 free(stream->OutBuf.buf);
2955                 free(stream);
2956                 *vStream = NULL;
2957                 break;
2958         }
2959         case eEmtyCodec: 
2960                 break; /// TODO
2961         }
2962         return rc;
2963 }
2964
2965 int StrBufStreamTranscode(eStreamType type, IOBuffer *Target, IOBuffer *In, const char* pIn, long pInLen, vStreamT *vStream, int LastChunk, const char **Err)
2966 {
2967         int rc = 0;
2968         switch (type)
2969         {
2970         case eBase64Encode:
2971         {
2972                 /// TODO
2973 /*
2974                 // base64_decodestate *state = (base64_decodestate*)vStream;
2975                 long ExpectLen;
2976                 
2977                 if (In != NULL)
2978                 {
2979                         pIn = In->buf;
2980                         pInLen = In->BufUsed;
2981                 }
2982                 if ((In == NULL) || (vStream == NULL))
2983                         return;
2984                 
2985                 ExpectLen = (pInLen / 4) * 3;
2986
2987                 if (Target->BufSize - Target->BufUsed < ExpectLen)
2988                 {
2989                         IncreaseBuf(Target, 1, Target->BufUsed + ExpectLen + 1);
2990                 }
2991
2992                 ////    ExpectLen = base64_encode_block(pIn, pInLen, Target->buf + Target->BufUsed, state);
2993                 Target->BufUsed += ExpectLen;
2994                 Target->buf[Target->BufUsed] = '\0';
2995 */
2996         }
2997         break;
2998         case eBase64Decode:
2999         {
3000 /*
3001                 base64_decodestate *state = (base64_decodestate *)vStream;
3002                 long ExpectLen;
3003                 
3004                 if (In != NULL)
3005                 {
3006                         pIn = In->buf;
3007                         pInLen = In->BufUsed;
3008                 }
3009                 if ((pIn == NULL) || (vStream == NULL))
3010                         return;
3011                 
3012                 ExpectLen = (pInLen / 4) * 3;
3013
3014                 if (Target->BufSize - Target->BufUsed < ExpectLen)
3015                 {
3016                         IncreaseBuf(Target, 1, Target->BufUsed + ExpectLen + 1);
3017                 }
3018
3019                 ExpectLen = base64_decode_block(pIn, pInLen, Target->buf + Target->BufUsed, state);
3020                 Target->BufUsed += ExpectLen;
3021                 Target->buf[Target->BufUsed] = '\0';
3022 */
3023         }
3024         break;
3025         case eZLibEncode:
3026         {
3027                 z_enc_stream *stream = (z_enc_stream *)vStream;
3028                 int org_outbuf_len = stream->OutBuf.BufUsed;
3029                 int err;
3030                 unsigned int chunkavail;
3031
3032                 if (In->ReadWritePointer != NULL)
3033                 {
3034                         stream->zstream.next_in = (Bytef *) In->ReadWritePointer;
3035                         stream->zstream.avail_in = (uInt) In->Buf->BufUsed - 
3036                                 (In->ReadWritePointer - In->Buf->buf);
3037                 }
3038                 else
3039                 {
3040                         stream->zstream.next_in = (Bytef *) In->Buf->buf;
3041                         stream->zstream.avail_in = (uInt) In->Buf->BufUsed;
3042                 }
3043                 
3044                 stream->zstream.next_out = (unsigned char*)stream->OutBuf.buf + stream->OutBuf.BufUsed;
3045                 stream->zstream.avail_out = chunkavail = (uInt) stream->OutBuf.BufSize - stream->OutBuf.BufUsed;
3046
3047                 err = deflate(&stream->zstream,  (LastChunk) ? Z_FINISH : Z_NO_FLUSH);
3048
3049                 stream->OutBuf.BufUsed += (chunkavail - stream->zstream.avail_out);
3050
3051                 if (Target && 
3052                     (LastChunk ||
3053                      (stream->OutBuf.BufUsed != org_outbuf_len)
3054                             ))
3055                 {
3056                         iSwapBuffers(Target->Buf, &stream->OutBuf);
3057                 }
3058
3059                 if (stream->zstream.avail_in == 0)
3060                 {
3061                         FlushStrBuf(In->Buf);
3062                         In->ReadWritePointer = NULL;
3063                 }
3064                 else
3065                 {
3066                         if (stream->zstream.avail_in < 64)
3067                         {
3068                                 memmove(In->Buf->buf,
3069                                         In->Buf->buf + In->Buf->BufUsed - stream->zstream.avail_in,
3070                                         stream->zstream.avail_in);
3071
3072                                 In->Buf->BufUsed = stream->zstream.avail_in;
3073                                 In->Buf->buf[In->Buf->BufUsed] = '\0';
3074                         }
3075                         else
3076                         {
3077                                 
3078                                 In->ReadWritePointer = In->Buf->buf + 
3079                                         (In->Buf->BufUsed - stream->zstream.avail_in);
3080                         }
3081                 }
3082                 rc = (LastChunk && (err != Z_FINISH));
3083                 if (!rc && (err != Z_OK)) {
3084                         *Err = zError(err);
3085                 }
3086                 
3087         }
3088         break;
3089         case eZLibDecode: {
3090                 z_enc_stream *stream = (z_enc_stream *)vStream;
3091                 int org_outbuf_len = stream->zstream.total_out;
3092                 int err;
3093
3094                 if ((stream->zstream.avail_out != 0) && (stream->zstream.next_in != NULL)) {
3095                         if (In->ReadWritePointer != NULL)
3096                         {
3097                                 stream->zstream.next_in = (Bytef *) In->ReadWritePointer;
3098                                 stream->zstream.avail_in = (uInt) In->Buf->BufUsed - 
3099                                         (In->ReadWritePointer - In->Buf->buf);
3100                         }
3101                         else
3102                         {
3103                                 stream->zstream.next_in = (Bytef *) In->Buf->buf;
3104                                 stream->zstream.avail_in = (uInt) In->Buf->BufUsed;
3105                         }
3106                 }
3107
3108                 stream->zstream.next_out = (unsigned char*)stream->OutBuf.buf + stream->OutBuf.BufUsed;
3109                 stream->zstream.avail_out = (uInt) stream->OutBuf.BufSize - stream->OutBuf.BufUsed;
3110
3111                 err = inflate(&stream->zstream, Z_NO_FLUSH);
3112
3113                 ///assert(ret != Z_STREAM_ERROR);  /* state not clobbered * /
3114                 switch (err) {
3115                 case Z_NEED_DICT:
3116                         err = Z_DATA_ERROR;     /* and fall through */
3117
3118                 case Z_DATA_ERROR:
3119                         *Err = zError(err);
3120                 case Z_MEM_ERROR:
3121                         (void)inflateEnd(&stream->zstream);
3122                         return err;
3123                 }
3124
3125                 stream->OutBuf.BufUsed += stream->zstream.total_out + org_outbuf_len;
3126
3127                 if (Target) iSwapBuffers(Target->Buf, &stream->OutBuf);
3128
3129                 if (stream->zstream.avail_in == 0)
3130                 {
3131                         FlushStrBuf(In->Buf);
3132                         In->ReadWritePointer = NULL;
3133                 }
3134                 else
3135                 {
3136                         if (stream->zstream.avail_in < 64)
3137                         {
3138                                 memmove(In->Buf->buf,
3139                                         In->Buf->buf + In->Buf->BufUsed - stream->zstream.avail_in,
3140                                         stream->zstream.avail_in);
3141
3142                                 In->Buf->BufUsed = stream->zstream.avail_in;
3143                                 In->Buf->buf[In->Buf->BufUsed] = '\0';
3144                         }
3145                         else
3146                         {
3147                                 
3148                                 In->ReadWritePointer = In->Buf->buf + 
3149                                         (In->Buf->BufUsed - stream->zstream.avail_in);
3150                         }
3151                 }
3152         }
3153                 break;
3154         case eEmtyCodec: {
3155
3156         }
3157                 break; /// TODO
3158         }
3159         return rc;
3160 }
3161
3162 /**
3163  * @ingroup StrBuf_DeEnCoder
3164  * @brief decode a buffer from base 64 encoding; destroys original
3165  * @param Buf Buffor to transform
3166  */
3167 int StrBufDecodeHex(StrBuf *Buf)
3168 {
3169         unsigned int ch;
3170         char *pch, *pche, *pchi;
3171
3172         if (Buf == NULL) return -1;
3173
3174         pch = pchi = Buf->buf;
3175         pche = pch + Buf->BufUsed;
3176
3177         while (pchi < pche){
3178                 ch = decode_hex(pchi);
3179                 *pch = ch;
3180                 pch ++;
3181                 pchi += 2;
3182         }
3183
3184         *pch = '\0';
3185         Buf->BufUsed = pch - Buf->buf;
3186         return Buf->BufUsed;
3187 }
3188
3189 /**
3190  * @ingroup StrBuf_DeEnCoder
3191  * @brief replace all chars >0x20 && < 0x7F with Mute
3192  * @param Mute char to put over invalid chars
3193  * @param Buf Buffor to transform
3194  */
3195 int StrBufSanitizeAscii(StrBuf *Buf, const char Mute)
3196 {
3197         unsigned char *pch;
3198
3199         if (Buf == NULL) return -1;
3200         pch = (unsigned char *)Buf->buf;
3201         while (pch < (unsigned char *)Buf->buf + Buf->BufUsed) {
3202                 if ((*pch < 0x20) || (*pch > 0x7F))
3203                         *pch = Mute;
3204                 pch ++;
3205         }
3206         return Buf->BufUsed;
3207 }
3208
3209
3210 /**
3211  * @ingroup StrBuf_DeEnCoder
3212  * @brief remove escaped strings from i.e. the url string (like %20 for blanks)
3213  * @param Buf Buffer to translate
3214  * @param StripBlanks Reduce several blanks to one?
3215  */
3216 long StrBufUnescape(StrBuf *Buf, int StripBlanks)
3217 {
3218         int a, b;
3219         char hex[3];
3220         long len;
3221
3222         if (Buf == NULL)
3223                 return -1;
3224
3225         while ((Buf->BufUsed > 0) && (isspace(Buf->buf[Buf->BufUsed - 1]))){
3226                 Buf->buf[Buf->BufUsed - 1] = '\0';
3227                 Buf->BufUsed --;
3228         }
3229
3230         a = 0; 
3231         while (a < Buf->BufUsed) {
3232                 if (Buf->buf[a] == '+')
3233                         Buf->buf[a] = ' ';
3234                 else if (Buf->buf[a] == '%') {
3235                         /* don't let % chars through, rather truncate the input. */
3236                         if (a + 2 > Buf->BufUsed) {
3237                                 Buf->buf[a] = '\0';
3238                                 Buf->BufUsed = a;
3239                         }
3240                         else {                  
3241                                 hex[0] = Buf->buf[a + 1];
3242                                 hex[1] = Buf->buf[a + 2];
3243                                 hex[2] = 0;
3244                                 b = 0;
3245                                 sscanf(hex, "%02x", &b);
3246                                 Buf->buf[a] = (char) b;
3247                                 len = Buf->BufUsed - a - 2;
3248                                 if (len > 0)
3249                                         memmove(&Buf->buf[a + 1], &Buf->buf[a + 3], len);
3250                         
3251                                 Buf->BufUsed -=2;
3252                         }
3253                 }
3254                 a++;
3255         }
3256         return a;
3257 }
3258
3259
3260 /**
3261  * @ingroup StrBuf_DeEnCoder
3262  * @brief       RFC2047-encode a header field if necessary.
3263  *              If no non-ASCII characters are found, the string
3264  *              will be copied verbatim without encoding.
3265  *
3266  * @param       target          Target buffer.
3267  * @param       source          Source string to be encoded.
3268  * @returns     encoded length; -1 if non success.
3269  */
3270 int StrBufRFC2047encode(StrBuf **target, const StrBuf *source)
3271 {
3272         const char headerStr[] = "=?UTF-8?Q?";
3273         int need_to_encode = 0;
3274         int i = 0;
3275         unsigned char ch;
3276
3277         if ((source == NULL) || 
3278             (target == NULL))
3279             return -1;
3280
3281         while ((i < source->BufUsed) &&
3282                (!IsEmptyStr (&source->buf[i])) &&
3283                (need_to_encode == 0)) {
3284                 if (((unsigned char) source->buf[i] < 32) || 
3285                     ((unsigned char) source->buf[i] > 126)) {
3286                         need_to_encode = 1;
3287                 }
3288                 i++;
3289         }
3290
3291         if (!need_to_encode) {
3292                 if (*target == NULL) {
3293                         *target = NewStrBufPlain(source->buf, source->BufUsed);
3294                 }
3295                 else {
3296                         FlushStrBuf(*target);
3297                         StrBufAppendBuf(*target, source, 0);
3298                 }
3299                 if (*target != 0)
3300                         return (*target)->BufUsed;
3301                 else
3302                         return 0;
3303         }
3304         if (*target == NULL)
3305                 *target = NewStrBufPlain(NULL, sizeof(headerStr) + source->BufUsed * 2);
3306         else if (sizeof(headerStr) + source->BufUsed >= (*target)->BufSize)
3307                 IncreaseBuf(*target, sizeof(headerStr) + source->BufUsed, 0);
3308         memcpy ((*target)->buf, headerStr, sizeof(headerStr) - 1);
3309         (*target)->BufUsed = sizeof(headerStr) - 1;
3310         for (i=0; (i < source->BufUsed); ++i) {
3311                 if ((*target)->BufUsed + 4 >= (*target)->BufSize)
3312                         IncreaseBuf(*target, 1, 0);
3313                 ch = (unsigned char) source->buf[i];
3314                 if ((ch  <  32) || 
3315                     (ch  > 126) || 
3316                     (ch == '=') ||
3317                     (ch == '?') ||
3318                     (ch == '_') ||
3319                     (ch == '[') ||
3320                     (ch == ']')   )
3321                 {
3322                         sprintf(&(*target)->buf[(*target)->BufUsed], "=%02X", ch);
3323                         (*target)->BufUsed += 3;
3324                 }
3325                 else {
3326                         if (ch == ' ')
3327                                 (*target)->buf[(*target)->BufUsed] = '_';
3328                         else
3329                                 (*target)->buf[(*target)->BufUsed] = ch;
3330                         (*target)->BufUsed++;
3331                 }
3332         }
3333         
3334         if ((*target)->BufUsed + 4 >= (*target)->BufSize)
3335                 IncreaseBuf(*target, 1, 0);
3336
3337         (*target)->buf[(*target)->BufUsed++] = '?';
3338         (*target)->buf[(*target)->BufUsed++] = '=';
3339         (*target)->buf[(*target)->BufUsed] = '\0';
3340         return (*target)->BufUsed;;
3341 }
3342
3343 /**
3344  * @ingroup StrBuf_DeEnCoder
3345  * @brief       Quoted-Printable encode a message; make it < 80 columns width.
3346  * @param       source          Source string to be encoded.
3347  * @returns     buffer with encoded message.
3348  */
3349 StrBuf *StrBufRFC2047encodeMessage(const StrBuf *EncodeMe)
3350 {
3351         StrBuf *OutBuf;
3352         char *Optr, *OEptr;
3353         const char *ptr, *eptr;
3354         unsigned char ch;
3355         int LinePos;
3356
3357         OutBuf = NewStrBufPlain(NULL, StrLength(EncodeMe) * 4);
3358         Optr = OutBuf->buf;
3359         OEptr = OutBuf->buf + OutBuf->BufSize;
3360         ptr = EncodeMe->buf;
3361         eptr = EncodeMe->buf + EncodeMe->BufUsed;
3362         LinePos = 0;
3363
3364         while (ptr < eptr)
3365         {
3366                 if (Optr + 4 >= OEptr)
3367                 {
3368                         long Offset;
3369                         Offset = Optr - OutBuf->buf;
3370                         OutBuf->BufUsed = Optr - OutBuf->buf;
3371                         IncreaseBuf(OutBuf, 1, 0);
3372                         Optr = OutBuf->buf + Offset;
3373                         OEptr = OutBuf->buf + OutBuf->BufSize;
3374                 }
3375                 if (*ptr == '\r')
3376                 {
3377                         /* ignore carriage returns */
3378                         ptr ++;
3379                 }
3380                 else if (*ptr == '\n') {
3381                         /* hard line break */
3382                         memcpy(Optr, HKEY("=0A"));
3383                         Optr += 3;
3384                         LinePos += 3;
3385                         ptr ++;
3386                 }
3387                 else if (( (*ptr >= 32) && (*ptr <= 60) ) ||
3388                          ( (*ptr >= 62) && (*ptr <= 126) ))
3389                 {
3390                         *Optr = *ptr;
3391                         Optr ++;
3392                         ptr ++;
3393                         LinePos ++;
3394                 }
3395                 else {
3396                         ch = *ptr;
3397                         *Optr = '=';
3398                         Optr ++;
3399                         *Optr = HexList[ch][0];
3400                         Optr ++;
3401                         *Optr = HexList[ch][1];
3402                         Optr ++;
3403                         LinePos += 3;
3404                         ptr ++;
3405                 }
3406
3407                 if (LinePos > 72) {
3408                         /* soft line break */
3409                         if (isspace(*(Optr - 1))) {
3410                                 ch = *(Optr - 1);
3411                                 Optr --;
3412                                 *Optr = '=';
3413                                 Optr ++;
3414                                 *Optr = HexList[ch][0];
3415                                 Optr ++;
3416                                 *Optr = HexList[ch][1];
3417                                 Optr ++;
3418                                 LinePos += 3;
3419                         }
3420                         *Optr = '=';
3421                         Optr ++;
3422                         *Optr = '\n';
3423                         Optr ++;
3424                         LinePos = 0;
3425                 }
3426         }
3427         *Optr = '\0';
3428         OutBuf->BufUsed = Optr - OutBuf->buf;
3429
3430         return OutBuf;
3431 }
3432
3433
3434 static void AddRecipient(StrBuf *Target, 
3435                          StrBuf *UserName, 
3436                          StrBuf *EmailAddress, 
3437                          StrBuf *EncBuf)
3438 {
3439         int QuoteMe = 0;
3440
3441         if (StrLength(Target) > 0) StrBufAppendBufPlain(Target, HKEY(", "), 0);
3442         if (strchr(ChrPtr(UserName), ',') != NULL) QuoteMe = 1;
3443
3444         if (QuoteMe)  StrBufAppendBufPlain(Target, HKEY("\""), 0);
3445         StrBufRFC2047encode(&EncBuf, UserName);
3446         StrBufAppendBuf(Target, EncBuf, 0);
3447         if (QuoteMe)  StrBufAppendBufPlain(Target, HKEY("\" "), 0);
3448         else          StrBufAppendBufPlain(Target, HKEY(" "), 0);
3449
3450         if (StrLength(EmailAddress) > 0){
3451                 StrBufAppendBufPlain(Target, HKEY("<"), 0);
3452                 StrBufAppendBuf(Target, EmailAddress, 0); /* TODO: what about IDN???? */
3453                 StrBufAppendBufPlain(Target, HKEY(">"), 0);
3454         }
3455 }
3456
3457
3458 /**
3459  * \brief QP encode parts of an email TO/CC/BCC vector, and strip/filter invalid parts
3460  * \param Recp Source list of email recipients
3461  * \param UserName Temporary buffer for internal use; Please provide valid buffer.
3462  * \param EmailAddress Temporary buffer for internal use; Please provide valid buffer.
3463  * \param EncBuf Temporary buffer for internal use; Please provide valid buffer.
3464  * \returns encoded & sanitized buffer with the contents of Recp; Caller owns this memory.
3465  */
3466 StrBuf *StrBufSanitizeEmailRecipientVector(const StrBuf *Recp, 
3467                                            StrBuf *UserName, 
3468                                            StrBuf *EmailAddress,
3469                                            StrBuf *EncBuf)
3470 {
3471         StrBuf *Target;
3472         const char *pch, *pche;
3473         const char *UserStart, *UserEnd, *EmailStart, *EmailEnd, *At;
3474
3475         if ((Recp == NULL) || (StrLength(Recp) == 0))
3476                 return NULL;
3477
3478         pch = ChrPtr(Recp);
3479         pche = pch + StrLength(Recp);
3480
3481         if (!CheckEncode(pch, -1, pche))
3482                 return NewStrBufDup(Recp);
3483
3484         Target = NewStrBufPlain(NULL, StrLength(Recp));
3485
3486         while ((pch != NULL) && (pch < pche))
3487         {
3488                 while (isspace(*pch)) pch++;
3489                 UserEnd = EmailStart = EmailEnd = NULL;
3490                 
3491                 if ((*pch == '"') || (*pch == '\'')) {
3492                         UserStart = pch + 1;
3493                         
3494                         UserEnd = strchr(UserStart, *pch);
3495                         if (UserEnd == NULL) 
3496                                 break; ///TODO: Userfeedback??
3497                         EmailStart = UserEnd + 1;
3498                         while (isspace(*EmailStart))
3499                                 EmailStart++;
3500                         if (UserEnd == UserStart) {
3501                                 UserStart = UserEnd = NULL;
3502                         }
3503                         
3504                         if (*EmailStart == '<') {
3505                                 EmailStart++;
3506                                 EmailEnd = strchr(EmailStart, '>');
3507                                 if (EmailEnd == NULL)
3508                                         EmailEnd = strchr(EmailStart, ',');
3509                                 
3510                         }
3511                         else {
3512                                 EmailEnd = strchr(EmailStart, ',');
3513                         }
3514                         if (EmailEnd == NULL)
3515                                 EmailEnd = pche;
3516                         pch = EmailEnd + 1;
3517                 }
3518                 else {
3519                         int gt = 0;
3520                         UserStart = pch;
3521                         EmailEnd = strchr(UserStart, ',');
3522                         if (EmailEnd == NULL) {
3523                                 EmailEnd = strchr(pch, '>');
3524                                 pch = NULL;
3525                                 if (EmailEnd != NULL) {
3526                                         gt = 1;
3527                                 }
3528                                 else {
3529                                         EmailEnd = pche;
3530                                 }
3531                         }
3532                         else {
3533
3534                                 pch = EmailEnd + 1;
3535                                 while ((EmailEnd > UserStart) && !gt &&
3536                                        ((*EmailEnd == ',') ||
3537                                         (*EmailEnd == '>') ||
3538                                         (isspace(*EmailEnd))))
3539                                 {
3540                                         if (*EmailEnd == '>')
3541                                                 gt = 1;
3542                                         else 
3543                                                 EmailEnd--;
3544                                 }
3545                                 if (EmailEnd == UserStart)
3546                                         break;
3547                         }
3548                         if (gt) {
3549                                 EmailStart = strchr(UserStart, '<');
3550                                 if ((EmailStart == NULL) || (EmailStart > EmailEnd))
3551                                         break;
3552                                 UserEnd = EmailStart;
3553
3554                                 while ((UserEnd > UserStart) && 
3555                                        isspace (*(UserEnd - 1)))
3556                                         UserEnd --;
3557                                 EmailStart ++;
3558                                 if (UserStart >= UserEnd)
3559                                         UserStart = UserEnd = NULL;
3560                         }
3561                         else { /* this is a local recipient... no domain, just a realname */
3562                                 EmailStart = UserStart;
3563                                 At = strchr(EmailStart, '@');
3564                                 if (At == NULL) {
3565                                         UserEnd = EmailEnd;
3566                                         EmailEnd = NULL;
3567                                 }
3568                                 else {
3569                                         EmailStart = UserStart;
3570                                         UserStart = NULL;
3571                                 }
3572                         }
3573                 }
3574
3575                 if ((UserStart != NULL) && (UserEnd != NULL))
3576                         StrBufPlain(UserName, UserStart, UserEnd - UserStart);
3577                 else if ((UserStart != NULL) && (UserEnd == NULL))
3578                         StrBufPlain(UserName, UserStart, UserEnd - UserStart);
3579                 else
3580                         FlushStrBuf(UserName);
3581
3582                 if ((EmailStart != NULL) && (EmailEnd != NULL))
3583                         StrBufPlain(EmailAddress, EmailStart, EmailEnd - EmailStart);
3584                 else if ((EmailStart != NULL) && (EmailEnd == NULL))
3585                         StrBufPlain(EmailAddress, EmailStart, EmailEnd - pche);
3586                 else 
3587                         FlushStrBuf(EmailAddress);
3588
3589                 AddRecipient(Target, UserName, EmailAddress, EncBuf);
3590
3591                 if (pch == NULL)
3592                         break;
3593                 
3594                 if ((pch != NULL) && (*pch == ','))
3595                         pch ++;
3596                 if (pch != NULL) while (isspace(*pch))
3597                         pch ++;
3598         }
3599         return Target;
3600 }
3601
3602
3603 /**
3604  * @ingroup StrBuf
3605  * @brief replaces all occurances of 'search' by 'replace'
3606  * @param buf Buffer to modify
3607  * @param search character to search
3608  * @param replace character to replace search by
3609  */
3610 void StrBufReplaceChars(StrBuf *buf, char search, char replace)
3611 {
3612         long i;
3613         if (buf == NULL)
3614                 return;
3615         for (i=0; i<buf->BufUsed; i++)
3616                 if (buf->buf[i] == search)
3617                         buf->buf[i] = replace;
3618
3619 }
3620
3621 /**
3622  * @ingroup StrBuf
3623  * @brief removes all \\r s from the string, or replaces them with \n if its not a combination of both.
3624  * @param buf Buffer to modify
3625  */
3626 void StrBufToUnixLF(StrBuf *buf)
3627 {
3628         char *pche, *pchS, *pchT;
3629         if (buf == NULL)
3630                 return;
3631
3632         pche = buf->buf + buf->BufUsed;
3633         pchS = pchT = buf->buf;
3634         while (pchS < pche)
3635         {
3636                 if (*pchS == '\r')
3637                 {
3638                         pchS ++;
3639                         if (*pchS != '\n') {
3640                                 *pchT = '\n';
3641                                 pchT++;
3642                         }
3643                 }
3644                 *pchT = *pchS;
3645                 pchT++; pchS++;
3646         }
3647         *pchT = '\0';
3648         buf->BufUsed = pchT - buf->buf;
3649 }
3650
3651
3652 /*******************************************************************************
3653  *                 Iconv Wrapper; RFC822 de/encoding                           *
3654  *******************************************************************************/
3655
3656 /**
3657  * @ingroup StrBuf_DeEnCoder
3658  * @brief Wrapper around iconv_open()
3659  * Our version adds aliases for non-standard Microsoft charsets
3660  * such as 'MS950', aliasing them to names like 'CP950'
3661  *
3662  * @param tocode        Target encoding
3663  * @param fromcode      Source encoding
3664  * @param pic           anonimized pointer to iconv struct
3665  */
3666 void  ctdl_iconv_open(const char *tocode, const char *fromcode, void *pic)
3667 {
3668 #ifdef HAVE_ICONV
3669         iconv_t ic = (iconv_t)(-1) ;
3670         ic = iconv_open(tocode, fromcode);
3671         if (ic == (iconv_t)(-1) ) {
3672                 char alias_fromcode[64];
3673                 if ( (strlen(fromcode) == 5) && (!strncasecmp(fromcode, "MS", 2)) ) {
3674                         safestrncpy(alias_fromcode, fromcode, sizeof alias_fromcode);
3675                         alias_fromcode[0] = 'C';
3676                         alias_fromcode[1] = 'P';
3677                         ic = iconv_open(tocode, alias_fromcode);
3678                 }
3679         }
3680         *(iconv_t *)pic = ic;
3681 #endif
3682 }
3683
3684
3685 /**
3686  * @ingroup StrBuf_DeEnCoder
3687  * @brief find one chunk of a RFC822 encoded string
3688  * @param Buffer where to search
3689  * @param bptr where to start searching
3690  * @returns found position, NULL if none.
3691  */
3692 static inline const char *FindNextEnd (const StrBuf *Buf, const char *bptr)
3693 {
3694         const char * end;
3695         /* Find the next ?Q? */
3696         if (Buf->BufUsed - (bptr - Buf->buf)  < 6)
3697                 return NULL;
3698
3699         end = strchr(bptr + 2, '?');
3700
3701         if (end == NULL)
3702                 return NULL;
3703
3704         if ((Buf->BufUsed - (end - Buf->buf) > 3) &&
3705             (((*(end + 1) == 'B') || (*(end + 1) == 'Q')) ||
3706              ((*(end + 1) == 'b') || (*(end + 1) == 'q'))) && 
3707             (*(end + 2) == '?')) {
3708                 /* skip on to the end of the cluster, the next ?= */
3709                 end = strstr(end + 3, "?=");
3710         }
3711         else
3712                 /* sort of half valid encoding, try to find an end. */
3713                 end = strstr(bptr, "?=");
3714         return end;
3715 }
3716
3717
3718
3719 /**
3720  * @ingroup StrBuf_DeEnCoder
3721  * @brief convert one buffer according to the preselected iconv pointer PIC
3722  * @param ConvertBuf buffer we need to translate
3723  * @param TmpBuf To share a workbuffer over several iterations. prepare to have it filled with useless stuff afterwards.
3724  * @param pic Pointer to the iconv-session Object
3725  */
3726 void StrBufConvert(StrBuf *ConvertBuf, StrBuf *TmpBuf, void *pic)
3727 {
3728 #ifdef HAVE_ICONV
3729         long trycount = 0;
3730         size_t siz;
3731         iconv_t ic;
3732         char *ibuf;                     /**< Buffer of characters to be converted */
3733         char *obuf;                     /**< Buffer for converted characters */
3734         size_t ibuflen;                 /**< Length of input buffer */
3735         size_t obuflen;                 /**< Length of output buffer */
3736
3737
3738         if ((ConvertBuf == NULL) || (TmpBuf == NULL))
3739                 return;
3740
3741         /* since we're converting to utf-8, one glyph may take up to 6 bytes */
3742         if (ConvertBuf->BufUsed * 6 >= TmpBuf->BufSize)
3743                 IncreaseBuf(TmpBuf, 0, ConvertBuf->BufUsed * 6);
3744 TRYAGAIN:
3745         ic = *(iconv_t*)pic;
3746         ibuf = ConvertBuf->buf;
3747         ibuflen = ConvertBuf->BufUsed;
3748         obuf = TmpBuf->buf;
3749         obuflen = TmpBuf->BufSize;
3750         
3751         siz = iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
3752
3753         if (siz < 0) {
3754                 if (errno == E2BIG) {
3755                         trycount ++;                    
3756                         IncreaseBuf(TmpBuf, 0, 0);
3757                         if (trycount < 5) 
3758                                 goto TRYAGAIN;
3759
3760                 }
3761                 else if (errno == EILSEQ){ 
3762                         /* hm, invalid utf8 sequence... what to do now? */
3763                         /* An invalid multibyte sequence has been encountered in the input */
3764                 }
3765                 else if (errno == EINVAL) {
3766                         /* An incomplete multibyte sequence has been encountered in the input. */
3767                 }
3768
3769                 FlushStrBuf(TmpBuf);
3770         }
3771         else {
3772                 TmpBuf->BufUsed = TmpBuf->BufSize - obuflen;
3773                 TmpBuf->buf[TmpBuf->BufUsed] = '\0';
3774                 
3775                 /* little card game: wheres the red lady? */
3776                 iSwapBuffers(ConvertBuf, TmpBuf);
3777                 FlushStrBuf(TmpBuf);
3778         }
3779 #endif
3780 }
3781
3782
3783 /**
3784  * @ingroup StrBuf_DeEnCoder
3785  * @brief catches one RFC822 encoded segment, and decodes it.
3786  * @param Target buffer to fill with result
3787  * @param DecodeMe buffer with stuff to process
3788  * @param SegmentStart points to our current segment in DecodeMe
3789  * @param SegmentEnd Points to the end of our current segment in DecodeMe
3790  * @param ConvertBuf Workbuffer shared between several iterations. Random content; needs to be valid
3791  * @param ConvertBuf2 Workbuffer shared between several iterations. Random content; needs to be valid
3792  * @param FoundCharset Characterset to default decoding to; if we find another we will overwrite it.
3793  */
3794 inline static void DecodeSegment(StrBuf *Target, 
3795                                  const StrBuf *DecodeMe, 
3796                                  const char *SegmentStart, 
3797                                  const char *SegmentEnd, 
3798                                  StrBuf *ConvertBuf,
3799                                  StrBuf *ConvertBuf2, 
3800                                  StrBuf *FoundCharset)
3801 {
3802         StrBuf StaticBuf;
3803         char charset[128];
3804         char encoding[16];
3805 #ifdef HAVE_ICONV
3806         iconv_t ic = (iconv_t)(-1);
3807 #else
3808         void *ic = NULL;
3809 #endif
3810         /* Now we handle foreign character sets properly encoded
3811          * in RFC2047 format.
3812          */
3813         StaticBuf.buf = (char*) SegmentStart; /*< it will just be read there... */
3814         StaticBuf.BufUsed = SegmentEnd - SegmentStart;
3815         StaticBuf.BufSize = DecodeMe->BufSize - (SegmentStart - DecodeMe->buf);
3816         extract_token(charset, SegmentStart, 1, '?', sizeof charset);
3817         if (FoundCharset != NULL) {
3818                 FlushStrBuf(FoundCharset);
3819                 StrBufAppendBufPlain(FoundCharset, charset, -1, 0);
3820         }
3821         extract_token(encoding, SegmentStart, 2, '?', sizeof encoding);
3822         StrBufExtract_token(ConvertBuf, &StaticBuf, 3, '?');
3823         
3824         *encoding = toupper(*encoding);
3825         if (*encoding == 'B') { /**< base64 */
3826                 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3827                         IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3828                 ConvertBuf2->BufUsed = CtdlDecodeBase64(ConvertBuf2->buf, 
3829                                                         ConvertBuf->buf, 
3830                                                         ConvertBuf->BufUsed);
3831         }
3832         else if (*encoding == 'Q') {    /**< quoted-printable */
3833                 long pos;
3834                 
3835                 pos = 0;
3836                 while (pos < ConvertBuf->BufUsed)
3837                 {
3838                         if (ConvertBuf->buf[pos] == '_') 
3839                                 ConvertBuf->buf[pos] = ' ';
3840                         pos++;
3841                 }
3842                 
3843                 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3844                         IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3845
3846                 ConvertBuf2->BufUsed = CtdlDecodeQuotedPrintable(
3847                         ConvertBuf2->buf, 
3848                         ConvertBuf->buf,
3849                         ConvertBuf->BufUsed);
3850         }
3851         else {
3852                 StrBufAppendBuf(ConvertBuf2, ConvertBuf, 0);
3853         }
3854 #ifdef HAVE_ICONV
3855         ctdl_iconv_open("UTF-8", charset, &ic);
3856         if (ic != (iconv_t)(-1) ) {             
3857 #endif
3858                 StrBufConvert(ConvertBuf2, ConvertBuf, &ic);
3859                 StrBufAppendBuf(Target, ConvertBuf2, 0);
3860 #ifdef HAVE_ICONV
3861                 iconv_close(ic);
3862         }
3863         else {
3864                 StrBufAppendBufPlain(Target, HKEY("(unreadable)"), 0);
3865         }
3866 #endif
3867 }
3868
3869 /**
3870  * @ingroup StrBuf_DeEnCoder
3871  * @brief Handle subjects with RFC2047 encoding such as: [deprecated old syntax!]
3872  * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3873  * @param Target where to put the decoded string to 
3874  * @param DecodeMe buffer with encoded string
3875  * @param DefaultCharset if we don't find one, which should we use?
3876  * @param FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string, 
3877  *        put it here for later use where no string might be known.
3878  */
3879 void StrBuf_RFC822_to_Utf8(StrBuf *Target, const StrBuf *DecodeMe, const StrBuf* DefaultCharset, StrBuf *FoundCharset)
3880 {
3881         StrBuf *ConvertBuf;
3882         StrBuf *ConvertBuf2;
3883         ConvertBuf = NewStrBufPlain(NULL, StrLength(DecodeMe));
3884         ConvertBuf2 = NewStrBufPlain(NULL, StrLength(DecodeMe));
3885         
3886         StrBuf_RFC822_2_Utf8(Target, 
3887                              DecodeMe, 
3888                              DefaultCharset, 
3889                              FoundCharset, 
3890                              ConvertBuf, 
3891                              ConvertBuf2);
3892         FreeStrBuf(&ConvertBuf);
3893         FreeStrBuf(&ConvertBuf2);
3894 }
3895
3896 /**
3897  * @ingroup StrBuf_DeEnCoder
3898  * @brief Handle subjects with RFC2047 encoding such as:
3899  * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3900  * @param Target where to put the decoded string to 
3901  * @param DecodeMe buffer with encoded string
3902  * @param DefaultCharset if we don't find one, which should we use?
3903  * @param FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string, 
3904  *        put it here for later use where no string might be known.
3905  * @param ConvertBuf workbuffer. feed in, you shouldn't care about its content.
3906  * @param ConvertBuf2 workbuffer. feed in, you shouldn't care about its content.
3907  */
3908 void StrBuf_RFC822_2_Utf8(StrBuf *Target, 
3909                           const StrBuf *DecodeMe, 
3910                           const StrBuf* DefaultCharset, 
3911                           StrBuf *FoundCharset, 
3912                           StrBuf *ConvertBuf, 
3913                           StrBuf *ConvertBuf2)
3914 {
3915         StrBuf *DecodedInvalidBuf = NULL;
3916         const StrBuf *DecodeMee = DecodeMe;
3917         const char *start, *end, *next, *nextend, *ptr = NULL;
3918 #ifdef HAVE_ICONV
3919         iconv_t ic = (iconv_t)(-1) ;
3920 #endif
3921         const char *eptr;
3922         int passes = 0;
3923         int i;
3924         int illegal_non_rfc2047_encoding = 0;
3925
3926
3927         if (DecodeMe == NULL)
3928                 return;
3929         /* Sometimes, badly formed messages contain strings which were simply
3930          *  written out directly in some foreign character set instead of
3931          *  using RFC2047 encoding.  This is illegal but we will attempt to
3932          *  handle it anyway by converting from a user-specified default
3933          *  charset to UTF-8 if we see any nonprintable characters.
3934          */
3935         
3936         for (i=0; i<DecodeMe->BufUsed; ++i) {
3937                 if ((DecodeMe->buf[i] < 32) || (DecodeMe->buf[i] > 126)) {
3938                         illegal_non_rfc2047_encoding = 1;
3939                         break;
3940                 }
3941         }
3942
3943         if ((illegal_non_rfc2047_encoding) &&
3944             (strcasecmp(ChrPtr(DefaultCharset), "UTF-8")) && 
3945             (strcasecmp(ChrPtr(DefaultCharset), "us-ascii")) )
3946         {
3947 #ifdef HAVE_ICONV
3948                 ctdl_iconv_open("UTF-8", ChrPtr(DefaultCharset), &ic);
3949                 if (ic != (iconv_t)(-1) ) {
3950                         DecodedInvalidBuf = NewStrBufDup(DecodeMe);
3951                         StrBufConvert(DecodedInvalidBuf, ConvertBuf, &ic);///TODO: don't void const?
3952                         DecodeMee = DecodedInvalidBuf;
3953                         iconv_close(ic);
3954                 }
3955 #endif
3956         }
3957
3958         /* pre evaluate the first pair */
3959         end = NULL;
3960         start = strstr(DecodeMee->buf, "=?");
3961         eptr = DecodeMee->buf + DecodeMee->BufUsed;
3962         if (start != NULL) 
3963                 end = FindNextEnd (DecodeMee, start + 2);
3964         else {
3965                 StrBufAppendBuf(Target, DecodeMee, 0);
3966                 FreeStrBuf(&DecodedInvalidBuf);
3967                 return;
3968         }
3969
3970
3971         if (start != DecodeMee->buf) {
3972                 long nFront;
3973                 
3974                 nFront = start - DecodeMee->buf;
3975                 StrBufAppendBufPlain(Target, DecodeMee->buf, nFront, 0);
3976         }
3977         /*
3978          * Since spammers will go to all sorts of absurd lengths to get their
3979          * messages through, there are LOTS of corrupt headers out there.
3980          * So, prevent a really badly formed RFC2047 header from throwing
3981          * this function into an infinite loop.
3982          */
3983         while ((start != NULL) && 
3984                (end != NULL) && 
3985                (start < eptr) && 
3986                (end < eptr) && 
3987                (passes < 20))
3988         {
3989                 passes++;
3990                 DecodeSegment(Target, 
3991                               DecodeMee, 
3992                               start, 
3993                               end, 
3994                               ConvertBuf,
3995                               ConvertBuf2,
3996                               FoundCharset);
3997                 
3998                 next = strstr(end, "=?");
3999                 nextend = NULL;
4000                 if ((next != NULL) && 
4001                     (next < eptr))
4002                         nextend = FindNextEnd(DecodeMee, next);
4003                 if (nextend == NULL)
4004                         next = NULL;
4005
4006                 /* did we find two partitions */
4007                 if ((next != NULL) && 
4008                     ((next - end) > 2))
4009                 {
4010                         ptr = end + 2;
4011                         while ((ptr < next) && 
4012                                (isspace(*ptr) ||
4013                                 (*ptr == '\r') ||
4014                                 (*ptr == '\n') || 
4015                                 (*ptr == '\t')))
4016                                 ptr ++;
4017                         /* 
4018                          * did we find a gab just filled with blanks?
4019                          * if not, copy its stuff over.
4020                          */
4021                         if (ptr != next)
4022                         {
4023                                 StrBufAppendBufPlain(Target, 
4024                                                      end + 2, 
4025                                                      next - end - 2,
4026                                                      0);
4027                         }
4028                 }
4029                 /* our next-pair is our new first pair now. */
4030                 ptr = end + 2;
4031                 start = next;
4032                 end = nextend;
4033         }
4034         end = ptr;
4035         nextend = DecodeMee->buf + DecodeMee->BufUsed;
4036         if ((end != NULL) && (end < nextend)) {
4037                 ptr = end;
4038                 while ( (ptr < nextend) &&
4039                         (isspace(*ptr) ||
4040                          (*ptr == '\r') ||
4041                          (*ptr == '\n') || 
4042                          (*ptr == '\t')))
4043                         ptr ++;
4044                 if (ptr < nextend)
4045                         StrBufAppendBufPlain(Target, end, nextend - end, 0);
4046         }
4047         FreeStrBuf(&DecodedInvalidBuf);
4048 }
4049
4050 /*******************************************************************************
4051  *                   Manipulating UTF-8 Strings                                *
4052  *******************************************************************************/
4053
4054 /**
4055  * @ingroup StrBuf
4056  * @brief evaluate the length of an utf8 special character sequence
4057  * @param Char the character to examine
4058  * @returns width of utf8 chars in bytes; if the sequence is broken 0 is returned; 1 if its simply ASCII.
4059  */
4060 static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE)
4061 {
4062         int n = 0;
4063         unsigned char test = (1<<7);
4064
4065         if ((*CharS & 0xC0) != 0xC0) 
4066                 return 1;
4067
4068         while ((n < 8) && 
4069                ((test & ((unsigned char)*CharS)) != 0)) 
4070         {
4071                 test = test >> 1;
4072                 n ++;
4073         }
4074         if ((n > 6) || ((CharE - CharS) < n))
4075                 n = 0;
4076         return n;
4077 }
4078
4079 /**
4080  * @ingroup StrBuf
4081  * @brief detect whether this char starts an utf-8 encoded char
4082  * @param Char character to inspect
4083  * @returns yes or no
4084  */
4085 static inline int Ctdl_IsUtf8SequenceStart(const char Char)
4086 {
4087 /** 11??.???? indicates an UTF8 Sequence. */
4088         return ((Char & 0xC0) == 0xC0);
4089 }
4090
4091 /**
4092  * @ingroup StrBuf
4093  * @brief measure the number of glyphs in an UTF8 string...
4094  * @param Buf string to measure
4095  * @returns the number of glyphs in Buf
4096  */
4097 long StrBuf_Utf8StrLen(StrBuf *Buf)
4098 {
4099         int n = 0;
4100         int m = 0;
4101         char *aptr, *eptr;
4102
4103         if ((Buf == NULL) || (Buf->BufUsed == 0))
4104                 return 0;
4105         aptr = Buf->buf;
4106         eptr = Buf->buf + Buf->BufUsed;
4107         while ((aptr < eptr) && (*aptr != '\0')) {
4108                 if (Ctdl_IsUtf8SequenceStart(*aptr)){
4109                         m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
4110                         while ((aptr < eptr) && (*aptr++ != '\0')&& (m-- > 0) );
4111                         n ++;
4112                 }
4113                 else {
4114                         n++;
4115                         aptr++;
4116                 }
4117         }
4118         return n;
4119 }
4120
4121 /**
4122  * @ingroup StrBuf
4123  * @brief cuts a string after maxlen glyphs
4124  * @param Buf string to cut to maxlen glyphs
4125  * @param maxlen how long may the string become?
4126  * @returns current length of the string
4127  */
4128 long StrBuf_Utf8StrCut(StrBuf *Buf, int maxlen)
4129 {
4130         char *aptr, *eptr;
4131         int n = 0, m = 0;
4132
4133         aptr = Buf->buf;
4134         eptr = Buf->buf + Buf->BufUsed;
4135         while ((aptr < eptr) && (*aptr != '\0')) {
4136                 if (Ctdl_IsUtf8SequenceStart(*aptr)){
4137                         m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
4138                         while ((*aptr++ != '\0') && (m-- > 0));
4139                         n ++;
4140                 }
4141                 else {
4142                         n++;
4143                         aptr++;
4144                 }
4145                 if (n >= maxlen) {
4146                         *aptr = '\0';
4147                         Buf->BufUsed = aptr - Buf->buf;
4148                         return Buf->BufUsed;
4149                 }
4150         }
4151         return Buf->BufUsed;
4152
4153 }
4154
4155
4156
4157
4158
4159 /*******************************************************************************
4160  *                               wrapping ZLib                                 *
4161  *******************************************************************************/
4162
4163 /**
4164  * @ingroup StrBuf_DeEnCoder
4165  * @brief uses the same calling syntax as compress2(), but it
4166  *   creates a stream compatible with HTTP "Content-encoding: gzip"
4167  * @param dest compressed buffer
4168  * @param destLen length of the compresed data 
4169  * @param source source to encode
4170  * @param sourceLen length of source to encode 
4171  * @param level compression level
4172  */
4173 #ifdef HAVE_ZLIB
4174 int ZEXPORT compress_gzip(Bytef * dest,
4175                           size_t * destLen,
4176                           const Bytef * source,
4177                           uLong sourceLen,     
4178                           int level)
4179 {
4180         /* write gzip header */
4181         snprintf((char *) dest, *destLen, 
4182                  "%c%c%c%c%c%c%c%c%c%c",
4183                  gz_magic[0], gz_magic[1], Z_DEFLATED,
4184                  0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /* xflags */ ,
4185                  OS_CODE);
4186
4187         /* normal deflate */
4188         z_stream stream;
4189         int err;
4190         stream.next_in = (Bytef *) source;
4191         stream.avail_in = (uInt) sourceLen;
4192         stream.next_out = dest + 10L;   // after header
4193         stream.avail_out = (uInt) * destLen;
4194         if ((uLong) stream.avail_out != *destLen)
4195                 return Z_BUF_ERROR;
4196
4197         stream.zalloc = (alloc_func) 0;
4198         stream.zfree = (free_func) 0;
4199         stream.opaque = (voidpf) 0;
4200
4201         err = deflateInit2(&stream, level, Z_DEFLATED, -MAX_WBITS,
4202                            DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
4203         if (err != Z_OK)
4204                 return err;
4205
4206         err = deflate(&stream, Z_FINISH);
4207         if (err != Z_STREAM_END) {
4208                 deflateEnd(&stream);
4209                 return err == Z_OK ? Z_BUF_ERROR : err;
4210         }
4211         *destLen = stream.total_out + 10L;
4212
4213         /* write CRC and Length */
4214         uLong crc = crc32(0L, source, sourceLen);
4215         int n;
4216         for (n = 0; n < 4; ++n, ++*destLen) {
4217                 dest[*destLen] = (int) (crc & 0xff);
4218                 crc >>= 8;
4219         }
4220         uLong len = stream.total_in;
4221         for (n = 0; n < 4; ++n, ++*destLen) {
4222                 dest[*destLen] = (int) (len & 0xff);
4223                 len >>= 8;
4224         }
4225         err = deflateEnd(&stream);
4226         return err;
4227 }
4228 #endif
4229
4230
4231 /**
4232  * @ingroup StrBuf_DeEnCoder
4233  * @brief compress the buffer with gzip
4234  * Attention! If you feed this a Const String, you must maintain the uncompressed buffer yourself!
4235  * @param Buf buffer whose content is to be gzipped
4236  */
4237 int CompressBuffer(StrBuf *Buf)
4238 {
4239 #ifdef HAVE_ZLIB
4240         char *compressed_data = NULL;
4241         size_t compressed_len, bufsize;
4242         int i = 0;
4243
4244         bufsize = compressed_len = Buf->BufUsed +  (Buf->BufUsed / 100) + 100;
4245         compressed_data = malloc(compressed_len);
4246         
4247         if (compressed_data == NULL)
4248                 return -1;
4249         /* Flush some space after the used payload so valgrind shuts up... */
4250         while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
4251                 Buf->buf[Buf->BufUsed + i++] = '\0';
4252         if (compress_gzip((Bytef *) compressed_data,
4253                           &compressed_len,
4254                           (Bytef *) Buf->buf,
4255                           (uLongf) Buf->BufUsed, Z_BEST_SPEED) == Z_OK) {
4256                 if (!Buf->ConstBuf)
4257                         free(Buf->buf);
4258                 Buf->buf = compressed_data;
4259                 Buf->BufUsed = compressed_len;
4260                 Buf->BufSize = bufsize;
4261                 /* Flush some space after the used payload so valgrind shuts up... */
4262                 i = 0;
4263                 while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
4264                         Buf->buf[Buf->BufUsed + i++] = '\0';
4265                 return 1;
4266         } else {
4267                 free(compressed_data);
4268         }
4269 #endif  /* HAVE_ZLIB */
4270         return 0;
4271 }
4272
4273 /*******************************************************************************
4274  *           File I/O; Callbacks to libevent                                   *
4275  *******************************************************************************/
4276
4277 long StrBuf_read_one_chunk_callback (int fd, short event, IOBuffer *FB)
4278 {
4279         long bufremain = 0;
4280         int n;
4281         
4282         if ((FB == NULL) || (FB->Buf == NULL))
4283                 return -1;
4284
4285         /*
4286          * check whether the read pointer is somewhere in a range 
4287          * where a cut left is inexpensive
4288          */
4289
4290         if (FB->ReadWritePointer != NULL)
4291         {
4292                 long already_read;
4293                 
4294                 already_read = FB->ReadWritePointer - FB->Buf->buf;
4295                 bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
4296
4297                 if (already_read != 0) {
4298                         long unread;
4299                         
4300                         unread = FB->Buf->BufUsed - already_read;
4301
4302                         /* else nothing to compact... */
4303                         if (unread == 0) {
4304                                 FB->ReadWritePointer = FB->Buf->buf;
4305                                 bufremain = FB->Buf->BufSize;                   
4306                         }
4307                         else if ((unread < 64) || 
4308                                  (bufremain < already_read))
4309                         {
4310                                 /* 
4311                                  * if its just a tiny bit remaining, or we run out of space... 
4312                                  * lets tidy up.
4313                                  */
4314                                 FB->Buf->BufUsed = unread;
4315                                 if (unread < already_read)
4316                                         memcpy(FB->Buf->buf, FB->ReadWritePointer, unread);
4317                                 else
4318                                         memmove(FB->Buf->buf, FB->ReadWritePointer, unread);
4319                                 FB->ReadWritePointer = FB->Buf->buf;
4320                                 bufremain = FB->Buf->BufSize - unread - 1;
4321                         }
4322                         else if (bufremain < (FB->Buf->BufSize / 10))
4323                         {
4324                                 /* get a bigger buffer */ 
4325
4326                                 IncreaseBuf(FB->Buf, 0, FB->Buf->BufUsed + 1);
4327
4328                                 FB->ReadWritePointer = FB->Buf->buf + unread;
4329
4330                                 bufremain = FB->Buf->BufSize - unread - 1;
4331 /*TODO: special increase function that won't copy the already read! */
4332                         }
4333                 }
4334                 else if (bufremain < 10) {
4335                         IncreaseBuf(FB->Buf, 1, FB->Buf->BufUsed + 10);
4336                         
4337                         FB->ReadWritePointer = FB->Buf->buf;
4338                         
4339                         bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
4340                 }
4341                 
4342         }
4343         else {
4344                 FB->ReadWritePointer = FB->Buf->buf;
4345                 bufremain = FB->Buf->BufSize - 1;
4346         }
4347
4348         n = read(fd, FB->Buf->buf + FB->Buf->BufUsed, bufremain);
4349
4350         if (n > 0) {
4351                 FB->Buf->BufUsed += n;
4352                 FB->Buf->buf[FB->Buf->BufUsed] = '\0';
4353         }
4354         return n;
4355 }
4356
4357 int StrBuf_write_one_chunk_callback(int fd, short event, IOBuffer *FB)
4358 {
4359         long WriteRemain;
4360         int n;
4361
4362         if ((FB == NULL) || (FB->Buf == NULL))
4363                 return -1;
4364
4365         if (FB->ReadWritePointer != NULL)
4366         {
4367                 WriteRemain = FB->Buf->BufUsed - 
4368                         (FB->ReadWritePointer - 
4369                          FB->Buf->buf);
4370         }
4371         else {
4372                 FB->ReadWritePointer = FB->Buf->buf;
4373                 WriteRemain = FB->Buf->BufUsed;
4374         }
4375
4376         n = write(fd, FB->ReadWritePointer, WriteRemain);
4377         if (n > 0) {
4378                 FB->ReadWritePointer += n;
4379
4380                 if (FB->ReadWritePointer == 
4381                     FB->Buf->buf + FB->Buf->BufUsed)
4382                 {
4383                         FlushStrBuf(FB->Buf);
4384                         FB->ReadWritePointer = NULL;
4385                         return 0;
4386                 }
4387         // check whether we've got something to write
4388         // get the maximum chunk plus the pointer we can send
4389         // write whats there
4390         // if not all was sent, remember the send pointer for the next time
4391                 return FB->ReadWritePointer - FB->Buf->buf + FB->Buf->BufUsed;
4392         }
4393         return n;
4394 }
4395
4396 /**
4397  * @ingroup StrBuf_IO
4398  * @brief extract a "next line" from Buf; Ptr to persist across several iterations
4399  * @param LineBuf your line will be copied here.
4400  * @param FB BLOB with lines of text...
4401  * @param Ptr moved arround to keep the next-line across several iterations
4402  *        has to be &NULL on start; will be &NotNULL on end of buffer
4403  * @returns size of copied buffer
4404  */
4405 eReadState StrBufChunkSipLine(StrBuf *LineBuf, IOBuffer *FB)
4406 {
4407         const char *aptr, *ptr, *eptr;
4408         char *optr, *xptr;
4409
4410         if ((FB == NULL) || (LineBuf == NULL) || (LineBuf->buf == NULL))
4411                 return eReadFail;
4412         
4413
4414         if ((FB->Buf == NULL) || (FB->ReadWritePointer == StrBufNOTNULL)) {
4415                 FB->ReadWritePointer = StrBufNOTNULL;
4416                 return eReadFail;
4417         }
4418
4419         FlushStrBuf(LineBuf);
4420         if (FB->ReadWritePointer == NULL)
4421                 ptr = aptr = FB->Buf->buf;
4422         else
4423                 ptr = aptr = FB->ReadWritePointer;
4424
4425         optr = LineBuf->buf;
4426         eptr = FB->Buf->buf + FB->Buf->BufUsed;
4427         xptr = LineBuf->buf + LineBuf->BufSize - 1;
4428
4429         while ((ptr <= eptr) && 
4430                (*ptr != '\n') &&
4431                (*ptr != '\r') )
4432         {
4433                 *optr = *ptr;
4434                 optr++; ptr++;
4435                 if (optr == xptr) {
4436                         LineBuf->BufUsed = optr - LineBuf->buf;
4437                         IncreaseBuf(LineBuf,  1, LineBuf->BufUsed + 1);
4438                         optr = LineBuf->buf + LineBuf->BufUsed;
4439                         xptr = LineBuf->buf + LineBuf->BufSize - 1;
4440                 }
4441         }
4442
4443         if (ptr >= eptr) {
4444                 if (optr > LineBuf->buf)
4445                         optr --;
4446                 if ((*(ptr - 1) != '\r') && (*(ptr - 1) != '\n')) {
4447                         LineBuf->BufUsed = optr - LineBuf->buf;
4448                         *optr = '\0';
4449                         if ((FB->ReadWritePointer != NULL) && 
4450                             (FB->ReadWritePointer != FB->Buf->buf))
4451                         {
4452                                 /* Ok, the client application read all the data 
4453                                    it was interested in so far. Since there is more to read, 
4454                                    we now shrink the buffer, and move the rest over.
4455                                 */
4456                                 StrBufCutLeft(FB->Buf, 
4457                                               FB->ReadWritePointer - FB->Buf->buf);
4458                                 FB->ReadWritePointer = FB->Buf->buf;
4459                         }
4460                         return eMustReadMore;
4461                 }
4462         }
4463         LineBuf->BufUsed = optr - LineBuf->buf;
4464         *optr = '\0';       
4465         if ((ptr <= eptr) && (*ptr == '\r'))
4466                 ptr ++;
4467         if ((ptr <= eptr) && (*ptr == '\n'))
4468                 ptr ++;
4469         
4470         if (ptr < eptr) {
4471                 FB->ReadWritePointer = ptr;
4472         }
4473         else {
4474                 FlushStrBuf(FB->Buf);
4475                 FB->ReadWritePointer = NULL;
4476         }
4477
4478         return eReadSuccess;
4479 }
4480
4481 /**
4482  * @ingroup StrBuf_CHUNKED_IO
4483  * @brief check whether the chunk-buffer has more data waiting or not.
4484  * @param FB Chunk-Buffer to inspect
4485  */
4486 eReadState StrBufCheckBuffer(IOBuffer *FB)
4487 {
4488         if (FB == NULL)
4489                 return eReadFail;
4490         if (FB->Buf->BufUsed == 0)
4491                 return eReadSuccess;
4492         if (FB->ReadWritePointer == NULL)
4493                 return eBufferNotEmpty;
4494         if (FB->Buf->buf + FB->Buf->BufUsed > FB->ReadWritePointer)
4495                 return eBufferNotEmpty;
4496         return eReadSuccess;
4497 }
4498
4499 long IOBufferStrLength(IOBuffer *FB)
4500 {
4501         if ((FB == NULL) || (FB->Buf == NULL))
4502                 return 0;
4503         if (FB->ReadWritePointer == NULL)
4504                 return StrLength(FB->Buf);
4505         
4506         return StrLength(FB->Buf) - (FB->ReadWritePointer - FB->Buf->buf);
4507 }
4508
4509
4510 /*******************************************************************************
4511  *           File I/O; Prefer buffered read since its faster!                  *
4512  *******************************************************************************/
4513
4514 /**
4515  * @ingroup StrBuf_IO
4516  * @brief Read a line from socket
4517  * flushes and closes the FD on error
4518  * @param buf the buffer to get the input to
4519  * @param fd pointer to the filedescriptor to read
4520  * @param append Append to an existing string or replace?
4521  * @param Error strerror() on error 
4522  * @returns numbers of chars read
4523  */
4524 int StrBufTCP_read_line(StrBuf *buf, int *fd, int append, const char **Error)
4525 {
4526         int len, rlen, slen;
4527
4528         if ((buf == NULL) || (buf->buf == NULL)) {
4529                 *Error = strerror(EINVAL);
4530                 return -1;
4531         }
4532
4533         if (!append)
4534                 FlushStrBuf(buf);
4535
4536         slen = len = buf->BufUsed;
4537         while (1) {
4538                 rlen = read(*fd, &buf->buf[len], 1);
4539                 if (rlen < 1) {
4540                         *Error = strerror(errno);
4541                         
4542                         close(*fd);
4543                         *fd = -1;
4544                         
4545                         return -1;
4546                 }
4547                 if (buf->buf[len] == '\n')
4548                         break;
4549                 if (buf->buf[len] != '\r')
4550                         len ++;
4551                 if (len + 2 >= buf->BufSize) {
4552                         buf->BufUsed = len;
4553                         buf->buf[len+1] = '\0';
4554                         IncreaseBuf(buf, 1, -1);
4555                 }
4556         }
4557         buf->BufUsed = len;
4558         buf->buf[len] = '\0';
4559         return len - slen;
4560 }
4561
4562
4563 /**
4564  * @ingroup StrBuf_BufferedIO
4565  * @brief Read a line from socket
4566  * flushes and closes the FD on error
4567  * @param Line the line to read from the fd / I/O Buffer
4568  * @param buf the buffer to get the input to
4569  * @param fd pointer to the filedescriptor to read
4570  * @param timeout number of successless selects until we bail out
4571  * @param selectresolution how long to wait on each select
4572  * @param Error strerror() on error 
4573  * @returns numbers of chars read
4574  */
4575 int StrBufTCP_read_buffered_line(StrBuf *Line, 
4576                                  StrBuf *buf, 
4577                                  int *fd, 
4578                                  int timeout, 
4579                                  int selectresolution, 
4580                                  const char **Error)
4581 {
4582         int len, rlen;
4583         int nSuccessLess = 0;
4584         fd_set rfds;
4585         char *pch = NULL;
4586         int fdflags;
4587         int IsNonBlock;
4588         struct timeval tv;
4589
4590         if (buf->BufUsed > 0) {
4591                 pch = strchr(buf->buf, '\n');
4592                 if (pch != NULL) {
4593                         rlen = 0;
4594                         len = pch - buf->buf;
4595                         if (len > 0 && (*(pch - 1) == '\r') )
4596                                 rlen ++;
4597                         StrBufSub(Line, buf, 0, len - rlen);
4598                         StrBufCutLeft(buf, len + 1);
4599                         return len - rlen;
4600                 }
4601         }
4602         
4603         if (buf->BufSize - buf->BufUsed < 10)
4604                 IncreaseBuf(buf, 1, -1);
4605
4606         fdflags = fcntl(*fd, F_GETFL);
4607         IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4608
4609         while ((nSuccessLess < timeout) && (pch == NULL)) {
4610                 if (IsNonBlock){
4611                         tv.tv_sec = selectresolution;
4612                         tv.tv_usec = 0;
4613                         
4614                         FD_ZERO(&rfds);
4615                         FD_SET(*fd, &rfds);
4616                         if (select(*fd + 1, NULL, &rfds, NULL, &tv) == -1) {
4617                                 *Error = strerror(errno);
4618                                 close (*fd);
4619                                 *fd = -1;
4620                                 return -1;
4621                         }
4622                 }
4623                 if (IsNonBlock && !  FD_ISSET(*fd, &rfds)) {
4624                         nSuccessLess ++;
4625                         continue;
4626                 }
4627                 rlen = read(*fd, 
4628                             &buf->buf[buf->BufUsed], 
4629                             buf->BufSize - buf->BufUsed - 1);
4630                 if (rlen < 1) {
4631                         *Error = strerror(errno);
4632                         close(*fd);
4633                         *fd = -1;
4634                         return -1;
4635                 }
4636                 else if (rlen > 0) {
4637                         nSuccessLess = 0;
4638                         buf->BufUsed += rlen;
4639                         buf->buf[buf->BufUsed] = '\0';
4640                         pch = strchr(buf->buf, '\n');
4641                         if ((pch == NULL) &&
4642                             (buf->BufUsed + 10 > buf->BufSize) &&
4643                             (IncreaseBuf(buf, 1, -1) == -1))
4644                                 return -1;
4645                         continue;
4646                 }
4647                 
4648         }
4649         if (pch != NULL) {
4650                 rlen = 0;
4651                 len = pch - buf->buf;
4652                 if (len > 0 && (*(pch - 1) == '\r') )
4653                         rlen ++;
4654                 StrBufSub(Line, buf, 0, len - rlen);
4655                 StrBufCutLeft(buf, len + 1);
4656                 return len - rlen;
4657         }
4658         return -1;
4659
4660 }
4661
4662 static const char *ErrRBLF_PreConditionFailed="StrBufTCP_read_buffered_line_fast: Wrong arguments or invalid Filedescriptor";
4663 static const char *ErrRBLF_SelectFailed="StrBufTCP_read_buffered_line_fast: Select failed without reason";
4664 static const char *ErrRBLF_NotEnoughSentFromServer="StrBufTCP_read_buffered_line_fast: No complete line was sent from peer";
4665 /**
4666  * @ingroup StrBuf_BufferedIO
4667  * @brief Read a line from socket
4668  * flushes and closes the FD on error
4669  * @param Line where to append our Line read from the fd / I/O Buffer; 
4670  * @param IOBuf the buffer to get the input to; lifetime pair to FD
4671  * @param Pos pointer to the current read position, should be NULL initialized on opening the FD it belongs to.!
4672  * @param fd pointer to the filedescriptor to read
4673  * @param timeout number of successless selects until we bail out
4674  * @param selectresolution how long to wait on each select
4675  * @param Error strerror() on error 
4676  * @returns numbers of chars read or -1 in case of error. "\n" will become 0
4677  */
4678 int StrBufTCP_read_buffered_line_fast(StrBuf *Line, 
4679                                       StrBuf *IOBuf, 
4680                                       const char **Pos,
4681                                       int *fd, 
4682                                       int timeout, 
4683                                       int selectresolution, 
4684                                       const char **Error)
4685 {
4686         const char *pche = NULL;
4687         const char *pos = NULL;
4688         const char *pLF;
4689         int len, rlen, retlen;
4690         int nSuccessLess = 0;
4691         fd_set rfds;
4692         const char *pch = NULL;
4693         int fdflags;
4694         int IsNonBlock;
4695         struct timeval tv;
4696         
4697         retlen = 0;
4698         if ((Line == NULL) ||
4699             (Pos == NULL) ||
4700             (IOBuf == NULL) ||
4701             (*fd == -1))
4702         {
4703                 if (Pos != NULL)
4704                         *Pos = NULL;
4705                 *Error = ErrRBLF_PreConditionFailed;
4706                 return -1;
4707         }
4708
4709         pos = *Pos;
4710         if ((IOBuf->BufUsed > 0) && 
4711             (pos != NULL) && 
4712             (pos < IOBuf->buf + IOBuf->BufUsed)) 
4713         {
4714                 char *pcht;
4715
4716                 pche = IOBuf->buf + IOBuf->BufUsed;
4717                 pch = pos;
4718                 pcht = Line->buf;
4719
4720                 while ((pch < pche) && (*pch != '\n'))
4721                 {
4722                         if (Line->BufUsed + 10 > Line->BufSize)
4723                         {
4724                                 long apos;
4725                                 apos = pcht - Line->buf;
4726                                 *pcht = '\0';
4727                                 IncreaseBuf(Line, 1, -1);
4728                                 pcht = Line->buf + apos;
4729                         }
4730                         *pcht++ = *pch++;
4731                         Line->BufUsed++;
4732                         retlen++;
4733                 }
4734
4735                 len = pch - pos;
4736                 if (len > 0 && (*(pch - 1) == '\r') )
4737                 {
4738                         retlen--;
4739                         len --;
4740                         pcht --;
4741                         Line->BufUsed --;
4742                 }
4743                 *pcht = '\0';
4744
4745                 if ((pch >= pche) || (*pch == '\0'))
4746                 {
4747                         FlushStrBuf(IOBuf);
4748                         *Pos = NULL;
4749                         pch = NULL;
4750                         pos = 0;
4751                 }
4752
4753                 if ((pch != NULL) && 
4754                     (pch <= pche)) 
4755                 {
4756                         if (pch + 1 >= pche) {
4757                                 *Pos = NULL;
4758                                 FlushStrBuf(IOBuf);
4759                         }
4760                         else
4761                                 *Pos = pch + 1;
4762                         
4763                         return retlen;
4764                 }
4765                 else 
4766                         FlushStrBuf(IOBuf);
4767         }
4768
4769         /* If we come here, Pos is Unset since we read everything into Line, and now go for more. */
4770         
4771         if (IOBuf->BufSize - IOBuf->BufUsed < 10)
4772                 IncreaseBuf(IOBuf, 1, -1);
4773
4774         fdflags = fcntl(*fd, F_GETFL);
4775         IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4776
4777         pLF = NULL;
4778         while ((nSuccessLess < timeout) && 
4779                (pLF == NULL) &&
4780                (*fd != -1)) {
4781                 if (IsNonBlock)
4782                 {
4783                         tv.tv_sec = 1;
4784                         tv.tv_usec = 0;
4785                 
4786                         FD_ZERO(&rfds);
4787                         FD_SET(*fd, &rfds);
4788                         if (select((*fd) + 1, &rfds, NULL, NULL, &tv) == -1) {
4789                                 *Error = strerror(errno);
4790                                 close (*fd);
4791                                 *fd = -1;
4792                                 if (*Error == NULL)
4793                                         *Error = ErrRBLF_SelectFailed;
4794                                 return -1;
4795                         }
4796                         if (! FD_ISSET(*fd, &rfds) != 0) {
4797                                 nSuccessLess ++;
4798                                 continue;
4799                         }
4800                 }
4801                 rlen = read(*fd, 
4802                             &IOBuf->buf[IOBuf->BufUsed], 
4803                             IOBuf->BufSize - IOBuf->BufUsed - 1);
4804                 if (rlen < 1) {
4805                         *Error = strerror(errno);
4806                         close(*fd);
4807                         *fd = -1;
4808                         return -1;
4809                 }
4810                 else if (rlen > 0) {
4811                         nSuccessLess = 0;
4812                         pLF = IOBuf->buf + IOBuf->BufUsed;
4813                         IOBuf->BufUsed += rlen;
4814                         IOBuf->buf[IOBuf->BufUsed] = '\0';
4815                         
4816                         pche = IOBuf->buf + IOBuf->BufUsed;
4817                         
4818                         while ((pLF < pche) && (*pLF != '\n'))
4819                                 pLF ++;
4820                         if ((pLF >= pche) || (*pLF == '\0'))
4821                                 pLF = NULL;
4822
4823                         if (IOBuf->BufUsed + 10 > IOBuf->BufSize)
4824                         {
4825                                 long apos = 0;
4826
4827                                 if (pLF != NULL) apos = pLF - IOBuf->buf;
4828                                 IncreaseBuf(IOBuf, 1, -1);      
4829                                 if (pLF != NULL) pLF = IOBuf->buf + apos;
4830                         }
4831
4832                         continue;
4833                 }
4834                 else
4835                 {
4836                         nSuccessLess++;
4837                 }
4838         }
4839         *Pos = NULL;
4840         if (pLF != NULL) {
4841                 pos = IOBuf->buf;
4842                 len = pLF - pos;
4843                 if (len > 0 && (*(pLF - 1) == '\r') )
4844                         len --;
4845                 StrBufAppendBufPlain(Line, ChrPtr(IOBuf), len, 0);
4846                 if (pLF + 1 >= IOBuf->buf + IOBuf->BufUsed)
4847                 {
4848                         FlushStrBuf(IOBuf);
4849                 }
4850                 else 
4851                         *Pos = pLF + 1;
4852                 return retlen + len;
4853         }
4854         *Error = ErrRBLF_NotEnoughSentFromServer;
4855         return -1;
4856
4857 }
4858
4859 static const char *ErrRBLF_BLOBPreConditionFailed="StrBufReadBLOB: Wrong arguments or invalid Filedescriptor";
4860 /**
4861  * @ingroup StrBuf_IO
4862  * @brief Input binary data from socket
4863  * flushes and closes the FD on error
4864  * @param Buf the buffer to get the input to
4865  * @param fd pointer to the filedescriptor to read
4866  * @param append Append to an existing string or replace?
4867  * @param nBytes the maximal number of bytes to read
4868  * @param Error strerror() on error 
4869  * @returns numbers of chars read
4870  */
4871 int StrBufReadBLOB(StrBuf *Buf, int *fd, int append, long nBytes, const char **Error)
4872 {
4873         int fdflags;
4874         int rlen;
4875         int nSuccessLess;
4876         int nRead = 0;
4877         char *ptr;
4878         int IsNonBlock;
4879         struct timeval tv;
4880         fd_set rfds;
4881
4882         if ((Buf == NULL) || (Buf->buf == NULL) || (*fd == -1))
4883         {
4884                 *Error = ErrRBLF_BLOBPreConditionFailed;
4885                 return -1;
4886         }
4887         if (!append)
4888                 FlushStrBuf(Buf);
4889         if (Buf->BufUsed + nBytes >= Buf->BufSize)
4890                 IncreaseBuf(Buf, 1, Buf->BufUsed + nBytes);
4891
4892         ptr = Buf->buf + Buf->BufUsed;
4893
4894         fdflags = fcntl(*fd, F_GETFL);
4895         IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4896         nSuccessLess = 0;
4897         while ((nRead < nBytes) && 
4898                (*fd != -1)) 
4899         {
4900                 if (IsNonBlock)
4901                 {
4902                         tv.tv_sec = 1;
4903                         tv.tv_usec = 0;
4904                 
4905                         FD_ZERO(&rfds);
4906                         FD_SET(*fd, &rfds);
4907                         if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
4908                                 *Error = strerror(errno);
4909                                 close (*fd);
4910                                 *fd = -1;
4911                                 if (*Error == NULL)
4912                                         *Error = ErrRBLF_SelectFailed;
4913                                 return -1;
4914                         }
4915                         if (! FD_ISSET(*fd, &rfds) != 0) {
4916                                 nSuccessLess ++;
4917                                 continue;
4918                         }
4919                 }
4920
4921                 if ((rlen = read(*fd, 
4922                                  ptr,
4923                                  nBytes - nRead)) == -1) {
4924                         close(*fd);
4925                         *fd = -1;
4926                         *Error = strerror(errno);
4927                         return rlen;
4928                 }
4929                 nRead += rlen;
4930                 ptr += rlen;
4931                 Buf->BufUsed += rlen;
4932         }
4933         Buf->buf[Buf->BufUsed] = '\0';
4934         return nRead;
4935 }
4936
4937 const char *ErrRBB_BLOBFPreConditionFailed = "StrBufReadBLOBBuffered: to many selects; aborting.";
4938 const char *ErrRBB_too_many_selects        = "StrBufReadBLOBBuffered: to many selects; aborting.";
4939 /**
4940  * @ingroup StrBuf_BufferedIO
4941  * @brief Input binary data from socket
4942  * flushes and closes the FD on error
4943  * @param Blob put binary thing here
4944  * @param IOBuf the buffer to get the input to
4945  * @param Pos offset inside of IOBuf
4946  * @param fd pointer to the filedescriptor to read
4947  * @param append Append to an existing string or replace?
4948  * @param nBytes the maximal number of bytes to read
4949  * @param check whether we should search for '000\n' terminators in case of timeouts
4950  * @param Error strerror() on error 
4951  * @returns numbers of chars read
4952  */
4953 int StrBufReadBLOBBuffered(StrBuf *Blob, 
4954                            StrBuf *IOBuf, 
4955                            const char **Pos,
4956                            int *fd, 
4957                            int append, 
4958                            long nBytes, 
4959                            int check, 
4960                            const char **Error)
4961 {
4962         const char *pos;
4963         int fdflags;
4964         int rlen = 0;
4965         int nRead = 0;
4966         int nAlreadyRead = 0;
4967         int IsNonBlock;
4968         char *ptr;
4969         fd_set rfds;
4970         struct timeval tv;
4971         int nSuccessLess = 0;
4972         int MaxTries;
4973
4974         if ((Blob == NULL)  ||
4975             (*fd == -1)     ||
4976             (IOBuf == NULL) ||
4977             (Pos == NULL))
4978         {
4979                 if (Pos != NULL)
4980                         *Pos = NULL;
4981                 *Error = ErrRBB_BLOBFPreConditionFailed;
4982                 return -1;
4983         }
4984
4985         if (!append)
4986                 FlushStrBuf(Blob);
4987         if (Blob->BufUsed + nBytes >= Blob->BufSize) 
4988                 IncreaseBuf(Blob, append, Blob->BufUsed + nBytes);
4989         
4990         pos = *Pos;
4991
4992         if (pos != NULL)
4993                 rlen = pos - IOBuf->buf;
4994         rlen = IOBuf->BufUsed - rlen;
4995
4996
4997         if ((IOBuf->BufUsed > 0) && 
4998             (pos != NULL) && 
4999             (pos < IOBuf->buf + IOBuf->BufUsed)) 
5000         {
5001                 if (rlen < nBytes) {
5002                         memcpy(Blob->buf + Blob->BufUsed, pos, rlen);
5003                         Blob->BufUsed += rlen;
5004                         Blob->buf[Blob->BufUsed] = '\0';
5005                         nAlreadyRead = nRead = rlen;
5006                         *Pos = NULL; 
5007                 }
5008                 if (rlen >= nBytes) {
5009                         memcpy(Blob->buf + Blob->BufUsed, pos, nBytes);
5010                         Blob->BufUsed += nBytes;
5011                         Blob->buf[Blob->BufUsed] = '\0';
5012                         if (rlen == nBytes) {
5013                                 *Pos = NULL; 
5014                                 FlushStrBuf(IOBuf);
5015                         }
5016                         else 
5017                                 *Pos += nBytes;
5018                         return nBytes;
5019                 }
5020         }
5021
5022         FlushStrBuf(IOBuf);
5023         *Pos = NULL;
5024         if (IOBuf->BufSize < nBytes - nRead)
5025                 IncreaseBuf(IOBuf, 0, nBytes - nRead);
5026         ptr = IOBuf->buf;
5027
5028         fdflags = fcntl(*fd, F_GETFL);
5029         IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
5030         if (IsNonBlock)
5031                 MaxTries =   1000;
5032         else
5033                 MaxTries = 100000;
5034
5035         nBytes -= nRead;
5036         nRead = 0;
5037         while ((nSuccessLess < MaxTries) && 
5038                (nRead < nBytes) &&
5039                (*fd != -1)) {
5040                 if (IsNonBlock)
5041                 {
5042                         tv.tv_sec = 1;
5043                         tv.tv_usec = 0;
5044                 
5045                         FD_ZERO(&rfds);
5046                         FD_SET(*fd, &rfds);
5047                         if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
5048                                 *Error = strerror(errno);
5049                                 close (*fd);
5050                                 *fd = -1;
5051                                 if (*Error == NULL)
5052                                         *Error = ErrRBLF_SelectFailed;
5053                                 return -1;
5054                         }
5055                         if (! FD_ISSET(*fd, &rfds) != 0) {
5056                                 nSuccessLess ++;
5057                                 continue;
5058                         }
5059                 }
5060                 rlen = read(*fd, 
5061                             ptr,
5062                             IOBuf->BufSize - (ptr - IOBuf->buf));
5063                 if (rlen == -1) {
5064                         close(*fd);
5065                         *fd = -1;
5066                         *Error = strerror(errno);
5067                         return rlen;
5068                 }
5069                 else if (rlen == 0){
5070                         if ((check == NNN_TERM) && 
5071                             (nRead > 5) &&
5072                             (strncmp(IOBuf->buf + IOBuf->BufUsed - 5, "\n000\n", 5) == 0)) 
5073                         {
5074                                 StrBufPlain(Blob, HKEY("\n000\n"));
5075                                 StrBufCutRight(Blob, 5);
5076                                 return Blob->BufUsed;
5077                         }
5078                         else if (!IsNonBlock) 
5079                                 nSuccessLess ++;
5080                         else if (nSuccessLess > MaxTries) {
5081                                 FlushStrBuf(IOBuf);
5082                                 *Error = ErrRBB_too_many_selects;
5083                                 return -1;
5084                         }
5085                 }
5086                 else if (rlen > 0) {
5087                         nSuccessLess = 0;
5088                         nRead += rlen;
5089                         ptr += rlen;
5090                         IOBuf->BufUsed += rlen;
5091                 }
5092         }
5093         if (nSuccessLess >= MaxTries) {
5094                 FlushStrBuf(IOBuf);
5095                 *Error = ErrRBB_too_many_selects;
5096                 return -1;
5097         }
5098
5099         if (nRead > nBytes) {
5100                 *Pos = IOBuf->buf + nBytes;
5101         }
5102         Blob->buf[Blob->BufUsed] = '\0';
5103         StrBufAppendBufPlain(Blob, IOBuf->buf, nBytes, 0);
5104         if (*Pos == NULL) {
5105                 FlushStrBuf(IOBuf);
5106         }
5107         return nRead + nAlreadyRead;
5108 }
5109
5110 /**
5111  * @ingroup StrBuf_IO
5112  * @brief extract a "next line" from Buf; Ptr to persist across several iterations
5113  * @param LineBuf your line will be copied here.
5114  * @param Buf BLOB with lines of text...
5115  * @param Ptr moved arround to keep the next-line across several iterations
5116  *        has to be &NULL on start; will be &NotNULL on end of buffer
5117  * @returns size of remaining buffer
5118  */
5119 int StrBufSipLine(StrBuf *LineBuf, const StrBuf *Buf, const char **Ptr)
5120 {
5121         const char *aptr, *ptr, *eptr;
5122         char *optr, *xptr;
5123
5124         if ((Buf == NULL) ||
5125             (*Ptr == StrBufNOTNULL) ||
5126             (LineBuf == NULL)||
5127             (LineBuf->buf == NULL))
5128         {
5129                 *Ptr = StrBufNOTNULL;
5130                 return 0;
5131         }
5132
5133         FlushStrBuf(LineBuf);
5134         if (*Ptr==NULL)
5135                 ptr = aptr = Buf->buf;
5136         else
5137                 ptr = aptr = *Ptr;
5138
5139         optr = LineBuf->buf;
5140         eptr = Buf->buf + Buf->BufUsed;
5141         xptr = LineBuf->buf + LineBuf->BufSize - 1;
5142
5143         while ((ptr <= eptr) && 
5144                (*ptr != '\n') &&
5145                (*ptr != '\r') )
5146         {
5147                 *optr = *ptr;
5148                 optr++; ptr++;
5149                 if (optr == xptr) {
5150                         LineBuf->BufUsed = optr - LineBuf->buf;
5151                         IncreaseBuf(LineBuf,  1, LineBuf->BufUsed + 1);
5152                         optr = LineBuf->buf + LineBuf->BufUsed;
5153                         xptr = LineBuf->buf + LineBuf->BufSize - 1;
5154                 }
5155         }
5156
5157         if ((ptr >= eptr) && (optr > LineBuf->buf))
5158                 optr --;
5159         LineBuf->BufUsed = optr - LineBuf->buf;
5160         *optr = '\0';       
5161         if ((ptr <= eptr) && (*ptr == '\r'))
5162                 ptr ++;
5163         if ((ptr <= eptr) && (*ptr == '\n'))
5164                 ptr ++;
5165         
5166         if (ptr < eptr) {
5167                 *Ptr = ptr;
5168         }
5169         else {
5170                 *Ptr = StrBufNOTNULL;
5171         }
5172
5173         return Buf->BufUsed - (ptr - Buf->buf);
5174 }
5175
5176
5177 /**
5178  * @ingroup StrBuf_IO
5179  * @brief removes double slashes from pathnames
5180  * @param Dir directory string to filter
5181  * @param RemoveTrailingSlash allows / disallows trailing slashes
5182  */
5183 void StrBufStripSlashes(StrBuf *Dir, int RemoveTrailingSlash)
5184 {
5185         char *a, *b;
5186
5187         a = b = Dir->buf;
5188
5189         while (!IsEmptyStr(a)) {
5190                 if (*a == '/') {
5191                         while (*a == '/')
5192                                 a++;
5193                         *b = '/';
5194                         b++;
5195                 }
5196                 else {
5197                         *b = *a;
5198                         b++; a++;
5199                 }
5200         }
5201         if ((RemoveTrailingSlash) &&
5202             (b > Dir->buf) && 
5203             (*(b - 1) == '/')){
5204                 b--;
5205         }
5206         *b = '\0';
5207         Dir->BufUsed = b - Dir->buf;
5208 }
5209
5210
5211 /*
5212  * Decode a quoted-printable encoded StrBuf buffer "in place"
5213  * This is possible because the decoded will always be shorter than the encoded
5214  * so we don't have to worry about the buffer being to small.
5215  */
5216 void StrBufDecodeQP(StrBuf *Buf)
5217 {
5218         if (!Buf) {                             // sanity check #1
5219                 return;
5220         }
5221
5222         int source_len = StrLength(Buf);
5223         if (source_len < 1) {                   // sanity check #2
5224                 return;
5225         }
5226
5227         int spos = 0;                           // source position
5228         int tpos = 0;                           // target position
5229
5230         while (spos < source_len) {
5231                 if (!strncmp(&Buf->buf[spos], "=\r\n", 3)) {
5232                         spos += 3;
5233                 }
5234                 else if (!strncmp(&Buf->buf[spos], "=\n", 2)) {
5235                         spos += 2;
5236                 }
5237                 else if (Buf->buf[spos] == '=') {
5238                         ++spos;
5239                         int ch;
5240                         sscanf(&Buf->buf[spos], "%02x", &ch);
5241                         Buf->buf[tpos++] = ch;
5242                         spos +=2;
5243                 }
5244                 else {
5245                         Buf->buf[tpos++] = Buf->buf[spos++];
5246                 }
5247         }
5248
5249         Buf->buf[tpos] = 0;
5250         Buf->BufUsed = tpos;
5251 }