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