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