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