work on decompressing
[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                         return NULL;/// tODO cleanup
2889                 return stream;
2890
2891         }
2892         case eZLibEncode:
2893         {
2894                 z_enc_stream *stream;
2895                 int err;
2896
2897                 stream = (z_enc_stream *) malloc(sizeof(z_enc_stream));
2898                 memset(stream, 0, sizeof(z_enc_stream));
2899                 stream->OutBuf.BufSize = 4*SIZ; /// todo 64
2900                 stream->OutBuf.buf = (char*)malloc(stream->OutBuf.BufSize);
2901                 /* write gzip header * /
2902                 stream->OutBuf.BufUsed = snprintf
2903                         (stream->OutBuf.buf,
2904                          stream->OutBuf.BufSize, 
2905                          "%c%c%c%c%c%c%c%c%c%c",
2906                          gz_magic[0], gz_magic[1], Z_DEFLATED,
2907                          0 /*flags * / , 0, 0, 0, 0 /*time * / , 0 /* xflags * / ,
2908                          OS_CODE);
2909 /*
2910                 err = deflateInit2(&stream->zstream,
2911                                    ZLibCompressionRatio,
2912                                    Z_DEFLATED,
2913                                    -MAX_WBITS,
2914                                    DEF_MEM_LEVEL,
2915                                    Z_DEFAULT_STRATEGY);
2916 */
2917                 err = deflateInit(&stream->zstream,
2918                                   ZLibCompressionRatio);
2919
2920                 if (err != Z_OK)
2921                         return NULL;/// tODO cleanup
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         switch (type)
2935         {
2936         case eBase64Encode:
2937         case eBase64Decode:
2938                 free(*vStream);
2939                 *vStream = NULL;
2940                 break;
2941         case eZLibDecode:
2942         {
2943                 z_enc_stream *stream = (z_enc_stream *)*vStream;
2944                 (void)inflateEnd(&stream->zstream);
2945                 free(stream->OutBuf.buf);
2946                 free(stream);
2947         }
2948         case eZLibEncode:
2949         {
2950                 z_enc_stream *stream = (z_enc_stream *)*vStream;
2951                 free(stream->OutBuf.buf);
2952                 free(stream);
2953 // todo more?
2954                 *vStream = NULL;
2955                 break;
2956         }
2957         case eEmtyCodec: 
2958                 break; /// TODO
2959         }
2960 }
2961
2962 void StrBufStreamTranscode(eStreamType type, IOBuffer *Target, IOBuffer *In, const char* pIn, long pInLen, void *vStream, int LastChunk)
2963 {
2964
2965         switch (type)
2966         {
2967         case eBase64Encode:
2968         {
2969                 /// TODO
2970 /*
2971                 // base64_decodestate *state = (base64_decodestate*)vStream;
2972                 long ExpectLen;
2973                 
2974                 if (In != NULL)
2975                 {
2976                         pIn = In->buf;
2977                         pInLen = In->BufUsed;
2978                 }
2979                 if ((In == NULL) || (vStream == NULL))
2980                         return;
2981                 
2982                 ExpectLen = (pInLen / 4) * 3;
2983
2984                 if (Target->BufSize - Target->BufUsed < ExpectLen)
2985                 {
2986                         IncreaseBuf(Target, 1, Target->BufUsed + ExpectLen + 1);
2987                 }
2988
2989                 ////    ExpectLen = base64_encode_block(pIn, pInLen, Target->buf + Target->BufUsed, state);
2990                 Target->BufUsed += ExpectLen;
2991                 Target->buf[Target->BufUsed] = '\0';
2992 */
2993         }
2994         break;
2995         case eBase64Decode:
2996         {
2997 /*
2998                 base64_decodestate *state = (base64_decodestate *)vStream;
2999                 long ExpectLen;
3000                 
3001                 if (In != NULL)
3002                 {
3003                         pIn = In->buf;
3004                         pInLen = In->BufUsed;
3005                 }
3006                 if ((pIn == NULL) || (vStream == NULL))
3007                         return;
3008                 
3009                 ExpectLen = (pInLen / 4) * 3;
3010
3011                 if (Target->BufSize - Target->BufUsed < ExpectLen)
3012                 {
3013                         IncreaseBuf(Target, 1, Target->BufUsed + ExpectLen + 1);
3014                 }
3015
3016                 ExpectLen = base64_decode_block(pIn, pInLen, Target->buf + Target->BufUsed, state);
3017                 Target->BufUsed += ExpectLen;
3018                 Target->buf[Target->BufUsed] = '\0';
3019 */
3020         }
3021         break;
3022         case eZLibEncode:
3023         {
3024                 z_enc_stream *stream = (z_enc_stream *)vStream;
3025                 int org_outbuf_len = stream->zstream.total_out;
3026                 int err;
3027                 unsigned int chunkavail;
3028
3029                 if (In->ReadWritePointer != NULL)
3030                 {
3031                         stream->zstream.next_in = (Bytef *) In->ReadWritePointer;
3032                         stream->zstream.avail_in = (uInt) In->Buf->BufUsed - 
3033                                 (In->ReadWritePointer - In->Buf->buf);
3034                 }
3035                 else
3036                 {
3037                         stream->zstream.next_in = (Bytef *) In->Buf->buf;
3038                         stream->zstream.avail_in = (uInt) In->Buf->BufUsed;
3039                 }
3040                 
3041                 stream->zstream.next_out = (unsigned char*)stream->OutBuf.buf + stream->OutBuf.BufUsed;
3042                 stream->zstream.avail_out = chunkavail = (uInt) stream->OutBuf.BufSize - stream->OutBuf.BufUsed;
3043
3044                 err = deflate(&stream->zstream,  (LastChunk) ? Z_FINISH : Z_NO_FLUSH);
3045
3046                 stream->OutBuf.BufUsed += (chunkavail - stream->zstream.avail_out);
3047                 /// todo + org_outbuf_len;
3048
3049                 if (Target) SwapBuffers(Target->Buf, &stream->OutBuf);
3050
3051                 if (stream->zstream.avail_in == 0)
3052                 {
3053                         FlushStrBuf(In->Buf);
3054                         In->ReadWritePointer = NULL;
3055                 }
3056                 else
3057                 {
3058                         if (stream->zstream.avail_in < 64)
3059                         {
3060                                 memmove(In->Buf->buf,
3061                                         In->Buf->buf + In->Buf->BufUsed - stream->zstream.avail_in,
3062                                         stream->zstream.avail_in);
3063
3064                                 In->Buf->BufUsed = stream->zstream.avail_in;
3065                                 In->Buf->buf[In->Buf->BufUsed] = '\0';
3066                         }
3067                         else
3068                         {
3069                                 
3070                                 In->ReadWritePointer = In->Buf->buf + 
3071                                         (In->Buf->BufUsed - stream->zstream.avail_in);
3072                         }
3073                 }
3074
3075         }
3076         break;
3077         case eZLibDecode: {
3078                 z_enc_stream *stream = (z_enc_stream *)vStream;
3079                 int org_outbuf_len = stream->zstream.total_out;
3080                 int err;
3081
3082                 if ((stream->zstream.avail_out != 0) && (stream->zstream.next_in != NULL)) {
3083                         if (In->ReadWritePointer != NULL)
3084                         {
3085                                 stream->zstream.next_in = (Bytef *) In->ReadWritePointer;
3086                                 stream->zstream.avail_in = (uInt) In->Buf->BufUsed - 
3087                                         (In->ReadWritePointer - In->Buf->buf);
3088                         }
3089                         else
3090                         {
3091                                 stream->zstream.next_in = (Bytef *) In->Buf->buf;
3092                                 stream->zstream.avail_in = (uInt) In->Buf->BufUsed;
3093                         }
3094                 }
3095
3096                 stream->zstream.next_out = (unsigned char*)stream->OutBuf.buf + stream->OutBuf.BufUsed;
3097                 stream->zstream.avail_out = (uInt) stream->OutBuf.BufSize - stream->OutBuf.BufUsed;
3098
3099                 err = inflate(&stream->zstream, Z_NO_FLUSH);
3100
3101                 ///assert(ret != Z_STREAM_ERROR);  /* state not clobbered * /
3102                 switch (err) {
3103                 case Z_NEED_DICT:
3104                         err = Z_DATA_ERROR;     /* and fall through */
3105
3106                 case Z_DATA_ERROR:
3107                         fprintf(stderr, "sanoteuh\n");
3108                 case Z_MEM_ERROR:
3109                         (void)inflateEnd(&stream->zstream);
3110                         return err;
3111                 }
3112
3113                 stream->OutBuf.BufUsed += stream->zstream.total_out + org_outbuf_len;
3114
3115                 if (Target) SwapBuffers(Target->Buf, &stream->OutBuf);
3116
3117                 if (stream->zstream.avail_in == 0)
3118                 {
3119                         FlushStrBuf(In->Buf);
3120                         In->ReadWritePointer = NULL;
3121                 }
3122                 else
3123                 {
3124                         if (stream->zstream.avail_in < 64)
3125                         {
3126                                 memmove(In->Buf->buf,
3127                                         In->Buf->buf + In->Buf->BufUsed - stream->zstream.avail_in,
3128                                         stream->zstream.avail_in);
3129
3130                                 In->Buf->BufUsed = stream->zstream.avail_in;
3131                                 In->Buf->buf[In->Buf->BufUsed] = '\0';
3132                         }
3133                         else
3134                         {
3135                                 
3136                                 In->ReadWritePointer = In->Buf->buf + 
3137                                         (In->Buf->BufUsed - stream->zstream.avail_in);
3138                         }
3139                 }
3140         }
3141                 break;
3142         case eEmtyCodec: {
3143
3144         }
3145                 break; /// TODO
3146         }
3147 }
3148
3149 /**
3150  * @ingroup StrBuf_DeEnCoder
3151  * @brief decode a buffer from base 64 encoding; destroys original
3152  * @param Buf Buffor to transform
3153  */
3154 int StrBufDecodeHex(StrBuf *Buf)
3155 {
3156         unsigned int ch;
3157         char *pch, *pche, *pchi;
3158
3159         if (Buf == NULL) return -1;
3160
3161         pch = pchi = Buf->buf;
3162         pche = pch + Buf->BufUsed;
3163
3164         while (pchi < pche){
3165                 ch = decode_hex(pchi);
3166                 *pch = ch;
3167                 pch ++;
3168                 pchi += 2;
3169         }
3170
3171         *pch = '\0';
3172         Buf->BufUsed = pch - Buf->buf;
3173         return Buf->BufUsed;
3174 }
3175
3176 /**
3177  * @ingroup StrBuf_DeEnCoder
3178  * @brief replace all chars >0x20 && < 0x7F with Mute
3179  * @param Mute char to put over invalid chars
3180  * @param Buf Buffor to transform
3181  */
3182 int StrBufSanitizeAscii(StrBuf *Buf, const char Mute)
3183 {
3184         unsigned char *pch;
3185
3186         if (Buf == NULL) return -1;
3187         pch = (unsigned char *)Buf->buf;
3188         while (pch < (unsigned char *)Buf->buf + Buf->BufUsed) {
3189                 if ((*pch < 0x20) || (*pch > 0x7F))
3190                         *pch = Mute;
3191                 pch ++;
3192         }
3193         return Buf->BufUsed;
3194 }
3195
3196
3197 /**
3198  * @ingroup StrBuf_DeEnCoder
3199  * @brief remove escaped strings from i.e. the url string (like %20 for blanks)
3200  * @param Buf Buffer to translate
3201  * @param StripBlanks Reduce several blanks to one?
3202  */
3203 long StrBufUnescape(StrBuf *Buf, int StripBlanks)
3204 {
3205         int a, b;
3206         char hex[3];
3207         long len;
3208
3209         if (Buf == NULL)
3210                 return -1;
3211
3212         while ((Buf->BufUsed > 0) && (isspace(Buf->buf[Buf->BufUsed - 1]))){
3213                 Buf->buf[Buf->BufUsed - 1] = '\0';
3214                 Buf->BufUsed --;
3215         }
3216
3217         a = 0; 
3218         while (a < Buf->BufUsed) {
3219                 if (Buf->buf[a] == '+')
3220                         Buf->buf[a] = ' ';
3221                 else if (Buf->buf[a] == '%') {
3222                         /* don't let % chars through, rather truncate the input. */
3223                         if (a + 2 > Buf->BufUsed) {
3224                                 Buf->buf[a] = '\0';
3225                                 Buf->BufUsed = a;
3226                         }
3227                         else {                  
3228                                 hex[0] = Buf->buf[a + 1];
3229                                 hex[1] = Buf->buf[a + 2];
3230                                 hex[2] = 0;
3231                                 b = 0;
3232                                 sscanf(hex, "%02x", &b);
3233                                 Buf->buf[a] = (char) b;
3234                                 len = Buf->BufUsed - a - 2;
3235                                 if (len > 0)
3236                                         memmove(&Buf->buf[a + 1], &Buf->buf[a + 3], len);
3237                         
3238                                 Buf->BufUsed -=2;
3239                         }
3240                 }
3241                 a++;
3242         }
3243         return a;
3244 }
3245
3246
3247 /**
3248  * @ingroup StrBuf_DeEnCoder
3249  * @brief       RFC2047-encode a header field if necessary.
3250  *              If no non-ASCII characters are found, the string
3251  *              will be copied verbatim without encoding.
3252  *
3253  * @param       target          Target buffer.
3254  * @param       source          Source string to be encoded.
3255  * @returns     encoded length; -1 if non success.
3256  */
3257 int StrBufRFC2047encode(StrBuf **target, const StrBuf *source)
3258 {
3259         const char headerStr[] = "=?UTF-8?Q?";
3260         int need_to_encode = 0;
3261         int i = 0;
3262         unsigned char ch;
3263
3264         if ((source == NULL) || 
3265             (target == NULL))
3266             return -1;
3267
3268         while ((i < source->BufUsed) &&
3269                (!IsEmptyStr (&source->buf[i])) &&
3270                (need_to_encode == 0)) {
3271                 if (((unsigned char) source->buf[i] < 32) || 
3272                     ((unsigned char) source->buf[i] > 126)) {
3273                         need_to_encode = 1;
3274                 }
3275                 i++;
3276         }
3277
3278         if (!need_to_encode) {
3279                 if (*target == NULL) {
3280                         *target = NewStrBufPlain(source->buf, source->BufUsed);
3281                 }
3282                 else {
3283                         FlushStrBuf(*target);
3284                         StrBufAppendBuf(*target, source, 0);
3285                 }
3286                 if (*target != 0)
3287                         return (*target)->BufUsed;
3288                 else
3289                         return 0;
3290         }
3291         if (*target == NULL)
3292                 *target = NewStrBufPlain(NULL, sizeof(headerStr) + source->BufUsed * 2);
3293         else if (sizeof(headerStr) + source->BufUsed >= (*target)->BufSize)
3294                 IncreaseBuf(*target, sizeof(headerStr) + source->BufUsed, 0);
3295         memcpy ((*target)->buf, headerStr, sizeof(headerStr) - 1);
3296         (*target)->BufUsed = sizeof(headerStr) - 1;
3297         for (i=0; (i < source->BufUsed); ++i) {
3298                 if ((*target)->BufUsed + 4 >= (*target)->BufSize)
3299                         IncreaseBuf(*target, 1, 0);
3300                 ch = (unsigned char) source->buf[i];
3301                 if ((ch  <  32) || 
3302                     (ch  > 126) || 
3303                     (ch == '=') ||
3304                     (ch == '?') ||
3305                     (ch == '_') ||
3306                     (ch == '[') ||
3307                     (ch == ']')   )
3308                 {
3309                         sprintf(&(*target)->buf[(*target)->BufUsed], "=%02X", ch);
3310                         (*target)->BufUsed += 3;
3311                 }
3312                 else {
3313                         if (ch == ' ')
3314                                 (*target)->buf[(*target)->BufUsed] = '_';
3315                         else
3316                                 (*target)->buf[(*target)->BufUsed] = ch;
3317                         (*target)->BufUsed++;
3318                 }
3319         }
3320         
3321         if ((*target)->BufUsed + 4 >= (*target)->BufSize)
3322                 IncreaseBuf(*target, 1, 0);
3323
3324         (*target)->buf[(*target)->BufUsed++] = '?';
3325         (*target)->buf[(*target)->BufUsed++] = '=';
3326         (*target)->buf[(*target)->BufUsed] = '\0';
3327         return (*target)->BufUsed;;
3328 }
3329
3330 /**
3331  * @ingroup StrBuf_DeEnCoder
3332  * @brief       Quoted-Printable encode a message; make it < 80 columns width.
3333  * @param       source          Source string to be encoded.
3334  * @returns     buffer with encoded message.
3335  */
3336 StrBuf *StrBufRFC2047encodeMessage(const StrBuf *EncodeMe)
3337 {
3338         StrBuf *OutBuf;
3339         char *Optr, *OEptr;
3340         const char *ptr, *eptr;
3341         unsigned char ch;
3342         int LinePos;
3343
3344         OutBuf = NewStrBufPlain(NULL, StrLength(EncodeMe) * 4);
3345         Optr = OutBuf->buf;
3346         OEptr = OutBuf->buf + OutBuf->BufSize;
3347         ptr = EncodeMe->buf;
3348         eptr = EncodeMe->buf + EncodeMe->BufUsed;
3349         LinePos = 0;
3350
3351         while (ptr < eptr)
3352         {
3353                 if (Optr + 4 >= OEptr)
3354                 {
3355                         long Offset;
3356                         Offset = Optr - OutBuf->buf;
3357                         OutBuf->BufUsed = Optr - OutBuf->buf;
3358                         IncreaseBuf(OutBuf, 1, 0);
3359                         Optr = OutBuf->buf + Offset;
3360                         OEptr = OutBuf->buf + OutBuf->BufSize;
3361                 }
3362                 if (*ptr == '\r')
3363                 {
3364                         /* ignore carriage returns */
3365                         ptr ++;
3366                 }
3367                 else if (*ptr == '\n') {
3368                         /* hard line break */
3369                         memcpy(Optr, HKEY("=0A"));
3370                         Optr += 3;
3371                         LinePos += 3;
3372                         ptr ++;
3373                 }
3374                 else if (( (*ptr >= 32) && (*ptr <= 60) ) ||
3375                          ( (*ptr >= 62) && (*ptr <= 126) ))
3376                 {
3377                         *Optr = *ptr;
3378                         Optr ++;
3379                         ptr ++;
3380                         LinePos ++;
3381                 }
3382                 else {
3383                         ch = *ptr;
3384                         *Optr = '=';
3385                         Optr ++;
3386                         *Optr = HexList[ch][0];
3387                         Optr ++;
3388                         *Optr = HexList[ch][1];
3389                         Optr ++;
3390                         LinePos += 3;
3391                         ptr ++;
3392                 }
3393
3394                 if (LinePos > 72) {
3395                         /* soft line break */
3396                         if (isspace(*(Optr - 1))) {
3397                                 ch = *(Optr - 1);
3398                                 Optr --;
3399                                 *Optr = '=';
3400                                 Optr ++;
3401                                 *Optr = HexList[ch][0];
3402                                 Optr ++;
3403                                 *Optr = HexList[ch][1];
3404                                 Optr ++;
3405                                 LinePos += 3;
3406                         }
3407                         *Optr = '=';
3408                         Optr ++;
3409                         *Optr = '\n';
3410                         Optr ++;
3411                         LinePos = 0;
3412                 }
3413         }
3414         *Optr = '\0';
3415         OutBuf->BufUsed = Optr - OutBuf->buf;
3416
3417         return OutBuf;
3418 }
3419
3420
3421 static void AddRecipient(StrBuf *Target, 
3422                          StrBuf *UserName, 
3423                          StrBuf *EmailAddress, 
3424                          StrBuf *EncBuf)
3425 {
3426         int QuoteMe = 0;
3427
3428         if (StrLength(Target) > 0) StrBufAppendBufPlain(Target, HKEY(", "), 0);
3429         if (strchr(ChrPtr(UserName), ',') != NULL) QuoteMe = 1;
3430
3431         if (QuoteMe)  StrBufAppendBufPlain(Target, HKEY("\""), 0);
3432         StrBufRFC2047encode(&EncBuf, UserName);
3433         StrBufAppendBuf(Target, EncBuf, 0);
3434         if (QuoteMe)  StrBufAppendBufPlain(Target, HKEY("\" "), 0);
3435         else          StrBufAppendBufPlain(Target, HKEY(" "), 0);
3436
3437         if (StrLength(EmailAddress) > 0){
3438                 StrBufAppendBufPlain(Target, HKEY("<"), 0);
3439                 StrBufAppendBuf(Target, EmailAddress, 0); /* TODO: what about IDN???? */
3440                 StrBufAppendBufPlain(Target, HKEY(">"), 0);
3441         }
3442 }
3443
3444
3445 /**
3446  * \brief QP encode parts of an email TO/CC/BCC vector, and strip/filter invalid parts
3447  * \param Recp Source list of email recipients
3448  * \param UserName Temporary buffer for internal use; Please provide valid buffer.
3449  * \param EmailAddress Temporary buffer for internal use; Please provide valid buffer.
3450  * \param EncBuf Temporary buffer for internal use; Please provide valid buffer.
3451  * \returns encoded & sanitized buffer with the contents of Recp; Caller owns this memory.
3452  */
3453 StrBuf *StrBufSanitizeEmailRecipientVector(const StrBuf *Recp, 
3454                                            StrBuf *UserName, 
3455                                            StrBuf *EmailAddress,
3456                                            StrBuf *EncBuf)
3457 {
3458         StrBuf *Target;
3459         const char *pch, *pche;
3460         const char *UserStart, *UserEnd, *EmailStart, *EmailEnd, *At;
3461
3462         if ((Recp == NULL) || (StrLength(Recp) == 0))
3463                 return NULL;
3464
3465         pch = ChrPtr(Recp);
3466         pche = pch + StrLength(Recp);
3467
3468         if (!CheckEncode(pch, -1, pche))
3469                 return NewStrBufDup(Recp);
3470
3471         Target = NewStrBufPlain(NULL, StrLength(Recp));
3472
3473         while ((pch != NULL) && (pch < pche))
3474         {
3475                 while (isspace(*pch)) pch++;
3476                 UserEnd = EmailStart = EmailEnd = NULL;
3477                 
3478                 if ((*pch == '"') || (*pch == '\'')) {
3479                         UserStart = pch + 1;
3480                         
3481                         UserEnd = strchr(UserStart, *pch);
3482                         if (UserEnd == NULL) 
3483                                 break; ///TODO: Userfeedback??
3484                         EmailStart = UserEnd + 1;
3485                         while (isspace(*EmailStart))
3486                                 EmailStart++;
3487                         if (UserEnd == UserStart) {
3488                                 UserStart = UserEnd = NULL;
3489                         }
3490                         
3491                         if (*EmailStart == '<') {
3492                                 EmailStart++;
3493                                 EmailEnd = strchr(EmailStart, '>');
3494                                 if (EmailEnd == NULL)
3495                                         EmailEnd = strchr(EmailStart, ',');
3496                                 
3497                         }
3498                         else {
3499                                 EmailEnd = strchr(EmailStart, ',');
3500                         }
3501                         if (EmailEnd == NULL)
3502                                 EmailEnd = pche;
3503                         pch = EmailEnd + 1;
3504                 }
3505                 else {
3506                         int gt = 0;
3507                         UserStart = pch;
3508                         EmailEnd = strchr(UserStart, ',');
3509                         if (EmailEnd == NULL) {
3510                                 EmailEnd = strchr(pch, '>');
3511                                 pch = NULL;
3512                                 if (EmailEnd != NULL) {
3513                                         gt = 1;
3514                                 }
3515                                 else {
3516                                         EmailEnd = pche;
3517                                 }
3518                         }
3519                         else {
3520
3521                                 pch = EmailEnd + 1;
3522                                 while ((EmailEnd > UserStart) && !gt &&
3523                                        ((*EmailEnd == ',') ||
3524                                         (*EmailEnd == '>') ||
3525                                         (isspace(*EmailEnd))))
3526                                 {
3527                                         if (*EmailEnd == '>')
3528                                                 gt = 1;
3529                                         else 
3530                                                 EmailEnd--;
3531                                 }
3532                                 if (EmailEnd == UserStart)
3533                                         break;
3534                         }
3535                         if (gt) {
3536                                 EmailStart = strchr(UserStart, '<');
3537                                 if ((EmailStart == NULL) || (EmailStart > EmailEnd))
3538                                         break;
3539                                 UserEnd = EmailStart;
3540
3541                                 while ((UserEnd > UserStart) && 
3542                                        isspace (*(UserEnd - 1)))
3543                                         UserEnd --;
3544                                 EmailStart ++;
3545                                 if (UserStart >= UserEnd)
3546                                         UserStart = UserEnd = NULL;
3547                         }
3548                         else { /* this is a local recipient... no domain, just a realname */
3549                                 EmailStart = UserStart;
3550                                 At = strchr(EmailStart, '@');
3551                                 if (At == NULL) {
3552                                         UserEnd = EmailEnd;
3553                                         EmailEnd = NULL;
3554                                 }
3555                                 else {
3556                                         EmailStart = UserStart;
3557                                         UserStart = NULL;
3558                                 }
3559                         }
3560                 }
3561
3562                 if ((UserStart != NULL) && (UserEnd != NULL))
3563                         StrBufPlain(UserName, UserStart, UserEnd - UserStart);
3564                 else if ((UserStart != NULL) && (UserEnd == NULL))
3565                         StrBufPlain(UserName, UserStart, UserEnd - UserStart);
3566                 else
3567                         FlushStrBuf(UserName);
3568
3569                 if ((EmailStart != NULL) && (EmailEnd != NULL))
3570                         StrBufPlain(EmailAddress, EmailStart, EmailEnd - EmailStart);
3571                 else if ((EmailStart != NULL) && (EmailEnd == NULL))
3572                         StrBufPlain(EmailAddress, EmailStart, EmailEnd - pche);
3573                 else 
3574                         FlushStrBuf(EmailAddress);
3575
3576                 AddRecipient(Target, UserName, EmailAddress, EncBuf);
3577
3578                 if (pch == NULL)
3579                         break;
3580                 
3581                 if ((pch != NULL) && (*pch == ','))
3582                         pch ++;
3583                 if (pch != NULL) while (isspace(*pch))
3584                         pch ++;
3585         }
3586         return Target;
3587 }
3588
3589
3590 /**
3591  * @ingroup StrBuf
3592  * @brief replaces all occurances of 'search' by 'replace'
3593  * @param buf Buffer to modify
3594  * @param search character to search
3595  * @param replace character to replace search by
3596  */
3597 void StrBufReplaceChars(StrBuf *buf, char search, char replace)
3598 {
3599         long i;
3600         if (buf == NULL)
3601                 return;
3602         for (i=0; i<buf->BufUsed; i++)
3603                 if (buf->buf[i] == search)
3604                         buf->buf[i] = replace;
3605
3606 }
3607
3608 /**
3609  * @ingroup StrBuf
3610  * @brief removes all \\r s from the string, or replaces them with \n if its not a combination of both.
3611  * @param buf Buffer to modify
3612  */
3613 void StrBufToUnixLF(StrBuf *buf)
3614 {
3615         char *pche, *pchS, *pchT;
3616         if (buf == NULL)
3617                 return;
3618
3619         pche = buf->buf + buf->BufUsed;
3620         pchS = pchT = buf->buf;
3621         while (pchS < pche)
3622         {
3623                 if (*pchS == '\r')
3624                 {
3625                         pchS ++;
3626                         if (*pchS != '\n') {
3627                                 *pchT = '\n';
3628                                 pchT++;
3629                         }
3630                 }
3631                 *pchT = *pchS;
3632                 pchT++; pchS++;
3633         }
3634         *pchT = '\0';
3635         buf->BufUsed = pchT - buf->buf;
3636 }
3637
3638
3639 /*******************************************************************************
3640  *                 Iconv Wrapper; RFC822 de/encoding                           *
3641  *******************************************************************************/
3642
3643 /**
3644  * @ingroup StrBuf_DeEnCoder
3645  * @brief Wrapper around iconv_open()
3646  * Our version adds aliases for non-standard Microsoft charsets
3647  * such as 'MS950', aliasing them to names like 'CP950'
3648  *
3649  * @param tocode        Target encoding
3650  * @param fromcode      Source encoding
3651  * @param pic           anonimized pointer to iconv struct
3652  */
3653 void  ctdl_iconv_open(const char *tocode, const char *fromcode, void *pic)
3654 {
3655 #ifdef HAVE_ICONV
3656         iconv_t ic = (iconv_t)(-1) ;
3657         ic = iconv_open(tocode, fromcode);
3658         if (ic == (iconv_t)(-1) ) {
3659                 char alias_fromcode[64];
3660                 if ( (strlen(fromcode) == 5) && (!strncasecmp(fromcode, "MS", 2)) ) {
3661                         safestrncpy(alias_fromcode, fromcode, sizeof alias_fromcode);
3662                         alias_fromcode[0] = 'C';
3663                         alias_fromcode[1] = 'P';
3664                         ic = iconv_open(tocode, alias_fromcode);
3665                 }
3666         }
3667         *(iconv_t *)pic = ic;
3668 #endif
3669 }
3670
3671
3672 /**
3673  * @ingroup StrBuf_DeEnCoder
3674  * @brief find one chunk of a RFC822 encoded string
3675  * @param Buffer where to search
3676  * @param bptr where to start searching
3677  * @returns found position, NULL if none.
3678  */
3679 static inline const char *FindNextEnd (const StrBuf *Buf, const char *bptr)
3680 {
3681         const char * end;
3682         /* Find the next ?Q? */
3683         if (Buf->BufUsed - (bptr - Buf->buf)  < 6)
3684                 return NULL;
3685
3686         end = strchr(bptr + 2, '?');
3687
3688         if (end == NULL)
3689                 return NULL;
3690
3691         if ((Buf->BufUsed - (end - Buf->buf) > 3) &&
3692             (((*(end + 1) == 'B') || (*(end + 1) == 'Q')) ||
3693              ((*(end + 1) == 'b') || (*(end + 1) == 'q'))) && 
3694             (*(end + 2) == '?')) {
3695                 /* skip on to the end of the cluster, the next ?= */
3696                 end = strstr(end + 3, "?=");
3697         }
3698         else
3699                 /* sort of half valid encoding, try to find an end. */
3700                 end = strstr(bptr, "?=");
3701         return end;
3702 }
3703
3704
3705
3706 /**
3707  * @ingroup StrBuf_DeEnCoder
3708  * @brief convert one buffer according to the preselected iconv pointer PIC
3709  * @param ConvertBuf buffer we need to translate
3710  * @param TmpBuf To share a workbuffer over several iterations. prepare to have it filled with useless stuff afterwards.
3711  * @param pic Pointer to the iconv-session Object
3712  */
3713 void StrBufConvert(StrBuf *ConvertBuf, StrBuf *TmpBuf, void *pic)
3714 {
3715 #ifdef HAVE_ICONV
3716         long trycount = 0;
3717         size_t siz;
3718         iconv_t ic;
3719         char *ibuf;                     /**< Buffer of characters to be converted */
3720         char *obuf;                     /**< Buffer for converted characters */
3721         size_t ibuflen;                 /**< Length of input buffer */
3722         size_t obuflen;                 /**< Length of output buffer */
3723
3724
3725         if ((ConvertBuf == NULL) || (TmpBuf == NULL))
3726                 return;
3727
3728         /* since we're converting to utf-8, one glyph may take up to 6 bytes */
3729         if (ConvertBuf->BufUsed * 6 >= TmpBuf->BufSize)
3730                 IncreaseBuf(TmpBuf, 0, ConvertBuf->BufUsed * 6);
3731 TRYAGAIN:
3732         ic = *(iconv_t*)pic;
3733         ibuf = ConvertBuf->buf;
3734         ibuflen = ConvertBuf->BufUsed;
3735         obuf = TmpBuf->buf;
3736         obuflen = TmpBuf->BufSize;
3737         
3738         siz = iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
3739
3740         if (siz < 0) {
3741                 if (errno == E2BIG) {
3742                         trycount ++;                    
3743                         IncreaseBuf(TmpBuf, 0, 0);
3744                         if (trycount < 5) 
3745                                 goto TRYAGAIN;
3746
3747                 }
3748                 else if (errno == EILSEQ){ 
3749                         /* hm, invalid utf8 sequence... what to do now? */
3750                         /* An invalid multibyte sequence has been encountered in the input */
3751                 }
3752                 else if (errno == EINVAL) {
3753                         /* An incomplete multibyte sequence has been encountered in the input. */
3754                 }
3755
3756                 FlushStrBuf(TmpBuf);
3757         }
3758         else {
3759                 TmpBuf->BufUsed = TmpBuf->BufSize - obuflen;
3760                 TmpBuf->buf[TmpBuf->BufUsed] = '\0';
3761                 
3762                 /* little card game: wheres the red lady? */
3763                 SwapBuffers(ConvertBuf, TmpBuf);
3764                 FlushStrBuf(TmpBuf);
3765         }
3766 #endif
3767 }
3768
3769
3770 /**
3771  * @ingroup StrBuf_DeEnCoder
3772  * @brief catches one RFC822 encoded segment, and decodes it.
3773  * @param Target buffer to fill with result
3774  * @param DecodeMe buffer with stuff to process
3775  * @param SegmentStart points to our current segment in DecodeMe
3776  * @param SegmentEnd Points to the end of our current segment in DecodeMe
3777  * @param ConvertBuf Workbuffer shared between several iterations. Random content; needs to be valid
3778  * @param ConvertBuf2 Workbuffer shared between several iterations. Random content; needs to be valid
3779  * @param FoundCharset Characterset to default decoding to; if we find another we will overwrite it.
3780  */
3781 inline static void DecodeSegment(StrBuf *Target, 
3782                                  const StrBuf *DecodeMe, 
3783                                  const char *SegmentStart, 
3784                                  const char *SegmentEnd, 
3785                                  StrBuf *ConvertBuf,
3786                                  StrBuf *ConvertBuf2, 
3787                                  StrBuf *FoundCharset)
3788 {
3789         StrBuf StaticBuf;
3790         char charset[128];
3791         char encoding[16];
3792 #ifdef HAVE_ICONV
3793         iconv_t ic = (iconv_t)(-1);
3794 #else
3795         void *ic = NULL;
3796 #endif
3797         /* Now we handle foreign character sets properly encoded
3798          * in RFC2047 format.
3799          */
3800         StaticBuf.buf = (char*) SegmentStart; /*< it will just be read there... */
3801         StaticBuf.BufUsed = SegmentEnd - SegmentStart;
3802         StaticBuf.BufSize = DecodeMe->BufSize - (SegmentStart - DecodeMe->buf);
3803         extract_token(charset, SegmentStart, 1, '?', sizeof charset);
3804         if (FoundCharset != NULL) {
3805                 FlushStrBuf(FoundCharset);
3806                 StrBufAppendBufPlain(FoundCharset, charset, -1, 0);
3807         }
3808         extract_token(encoding, SegmentStart, 2, '?', sizeof encoding);
3809         StrBufExtract_token(ConvertBuf, &StaticBuf, 3, '?');
3810         
3811         *encoding = toupper(*encoding);
3812         if (*encoding == 'B') { /**< base64 */
3813                 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3814                         IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3815                 ConvertBuf2->BufUsed = CtdlDecodeBase64(ConvertBuf2->buf, 
3816                                                         ConvertBuf->buf, 
3817                                                         ConvertBuf->BufUsed);
3818         }
3819         else if (*encoding == 'Q') {    /**< quoted-printable */
3820                 long pos;
3821                 
3822                 pos = 0;
3823                 while (pos < ConvertBuf->BufUsed)
3824                 {
3825                         if (ConvertBuf->buf[pos] == '_') 
3826                                 ConvertBuf->buf[pos] = ' ';
3827                         pos++;
3828                 }
3829                 
3830                 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3831                         IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3832
3833                 ConvertBuf2->BufUsed = CtdlDecodeQuotedPrintable(
3834                         ConvertBuf2->buf, 
3835                         ConvertBuf->buf,
3836                         ConvertBuf->BufUsed);
3837         }
3838         else {
3839                 StrBufAppendBuf(ConvertBuf2, ConvertBuf, 0);
3840         }
3841 #ifdef HAVE_ICONV
3842         ctdl_iconv_open("UTF-8", charset, &ic);
3843         if (ic != (iconv_t)(-1) ) {             
3844 #endif
3845                 StrBufConvert(ConvertBuf2, ConvertBuf, &ic);
3846                 StrBufAppendBuf(Target, ConvertBuf2, 0);
3847 #ifdef HAVE_ICONV
3848                 iconv_close(ic);
3849         }
3850         else {
3851                 StrBufAppendBufPlain(Target, HKEY("(unreadable)"), 0);
3852         }
3853 #endif
3854 }
3855
3856 /**
3857  * @ingroup StrBuf_DeEnCoder
3858  * @brief Handle subjects with RFC2047 encoding such as: [deprecated old syntax!]
3859  * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3860  * @param Target where to put the decoded string to 
3861  * @param DecodeMe buffer with encoded string
3862  * @param DefaultCharset if we don't find one, which should we use?
3863  * @param FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string, 
3864  *        put it here for later use where no string might be known.
3865  */
3866 void StrBuf_RFC822_to_Utf8(StrBuf *Target, const StrBuf *DecodeMe, const StrBuf* DefaultCharset, StrBuf *FoundCharset)
3867 {
3868         StrBuf *ConvertBuf;
3869         StrBuf *ConvertBuf2;
3870         ConvertBuf = NewStrBufPlain(NULL, StrLength(DecodeMe));
3871         ConvertBuf2 = NewStrBufPlain(NULL, StrLength(DecodeMe));
3872         
3873         StrBuf_RFC822_2_Utf8(Target, 
3874                              DecodeMe, 
3875                              DefaultCharset, 
3876                              FoundCharset, 
3877                              ConvertBuf, 
3878                              ConvertBuf2);
3879         FreeStrBuf(&ConvertBuf);
3880         FreeStrBuf(&ConvertBuf2);
3881 }
3882
3883 /**
3884  * @ingroup StrBuf_DeEnCoder
3885  * @brief Handle subjects with RFC2047 encoding such as:
3886  * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3887  * @param Target where to put the decoded string to 
3888  * @param DecodeMe buffer with encoded string
3889  * @param DefaultCharset if we don't find one, which should we use?
3890  * @param FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string, 
3891  *        put it here for later use where no string might be known.
3892  * @param ConvertBuf workbuffer. feed in, you shouldn't care about its content.
3893  * @param ConvertBuf2 workbuffer. feed in, you shouldn't care about its content.
3894  */
3895 void StrBuf_RFC822_2_Utf8(StrBuf *Target, 
3896                           const StrBuf *DecodeMe, 
3897                           const StrBuf* DefaultCharset, 
3898                           StrBuf *FoundCharset, 
3899                           StrBuf *ConvertBuf, 
3900                           StrBuf *ConvertBuf2)
3901 {
3902         StrBuf *DecodedInvalidBuf = NULL;
3903         const StrBuf *DecodeMee = DecodeMe;
3904         const char *start, *end, *next, *nextend, *ptr = NULL;
3905 #ifdef HAVE_ICONV
3906         iconv_t ic = (iconv_t)(-1) ;
3907 #endif
3908         const char *eptr;
3909         int passes = 0;
3910         int i;
3911         int illegal_non_rfc2047_encoding = 0;
3912
3913
3914         if (DecodeMe == NULL)
3915                 return;
3916         /* Sometimes, badly formed messages contain strings which were simply
3917          *  written out directly in some foreign character set instead of
3918          *  using RFC2047 encoding.  This is illegal but we will attempt to
3919          *  handle it anyway by converting from a user-specified default
3920          *  charset to UTF-8 if we see any nonprintable characters.
3921          */
3922         
3923         for (i=0; i<DecodeMe->BufUsed; ++i) {
3924                 if ((DecodeMe->buf[i] < 32) || (DecodeMe->buf[i] > 126)) {
3925                         illegal_non_rfc2047_encoding = 1;
3926                         break;
3927                 }
3928         }
3929
3930         if ((illegal_non_rfc2047_encoding) &&
3931             (strcasecmp(ChrPtr(DefaultCharset), "UTF-8")) && 
3932             (strcasecmp(ChrPtr(DefaultCharset), "us-ascii")) )
3933         {
3934 #ifdef HAVE_ICONV
3935                 ctdl_iconv_open("UTF-8", ChrPtr(DefaultCharset), &ic);
3936                 if (ic != (iconv_t)(-1) ) {
3937                         DecodedInvalidBuf = NewStrBufDup(DecodeMe);
3938                         StrBufConvert(DecodedInvalidBuf, ConvertBuf, &ic);///TODO: don't void const?
3939                         DecodeMee = DecodedInvalidBuf;
3940                         iconv_close(ic);
3941                 }
3942 #endif
3943         }
3944
3945         /* pre evaluate the first pair */
3946         end = NULL;
3947         start = strstr(DecodeMee->buf, "=?");
3948         eptr = DecodeMee->buf + DecodeMee->BufUsed;
3949         if (start != NULL) 
3950                 end = FindNextEnd (DecodeMee, start + 2);
3951         else {
3952                 StrBufAppendBuf(Target, DecodeMee, 0);
3953                 FreeStrBuf(&DecodedInvalidBuf);
3954                 return;
3955         }
3956
3957
3958         if (start != DecodeMee->buf) {
3959                 long nFront;
3960                 
3961                 nFront = start - DecodeMee->buf;
3962                 StrBufAppendBufPlain(Target, DecodeMee->buf, nFront, 0);
3963         }
3964         /*
3965          * Since spammers will go to all sorts of absurd lengths to get their
3966          * messages through, there are LOTS of corrupt headers out there.
3967          * So, prevent a really badly formed RFC2047 header from throwing
3968          * this function into an infinite loop.
3969          */
3970         while ((start != NULL) && 
3971                (end != NULL) && 
3972                (start < eptr) && 
3973                (end < eptr) && 
3974                (passes < 20))
3975         {
3976                 passes++;
3977                 DecodeSegment(Target, 
3978                               DecodeMee, 
3979                               start, 
3980                               end, 
3981                               ConvertBuf,
3982                               ConvertBuf2,
3983                               FoundCharset);
3984                 
3985                 next = strstr(end, "=?");
3986                 nextend = NULL;
3987                 if ((next != NULL) && 
3988                     (next < eptr))
3989                         nextend = FindNextEnd(DecodeMee, next);
3990                 if (nextend == NULL)
3991                         next = NULL;
3992
3993                 /* did we find two partitions */
3994                 if ((next != NULL) && 
3995                     ((next - end) > 2))
3996                 {
3997                         ptr = end + 2;
3998                         while ((ptr < next) && 
3999                                (isspace(*ptr) ||
4000                                 (*ptr == '\r') ||
4001                                 (*ptr == '\n') || 
4002                                 (*ptr == '\t')))
4003                                 ptr ++;
4004                         /* 
4005                          * did we find a gab just filled with blanks?
4006                          * if not, copy its stuff over.
4007                          */
4008                         if (ptr != next)
4009                         {
4010                                 StrBufAppendBufPlain(Target, 
4011                                                      end + 2, 
4012                                                      next - end - 2,
4013                                                      0);
4014                         }
4015                 }
4016                 /* our next-pair is our new first pair now. */
4017                 ptr = end + 2;
4018                 start = next;
4019                 end = nextend;
4020         }
4021         end = ptr;
4022         nextend = DecodeMee->buf + DecodeMee->BufUsed;
4023         if ((end != NULL) && (end < nextend)) {
4024                 ptr = end;
4025                 while ( (ptr < nextend) &&
4026                         (isspace(*ptr) ||
4027                          (*ptr == '\r') ||
4028                          (*ptr == '\n') || 
4029                          (*ptr == '\t')))
4030                         ptr ++;
4031                 if (ptr < nextend)
4032                         StrBufAppendBufPlain(Target, end, nextend - end, 0);
4033         }
4034         FreeStrBuf(&DecodedInvalidBuf);
4035 }
4036
4037 /*******************************************************************************
4038  *                   Manipulating UTF-8 Strings                                *
4039  *******************************************************************************/
4040
4041 /**
4042  * @ingroup StrBuf
4043  * @brief evaluate the length of an utf8 special character sequence
4044  * @param Char the character to examine
4045  * @returns width of utf8 chars in bytes; if the sequence is broken 0 is returned; 1 if its simply ASCII.
4046  */
4047 static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE)
4048 {
4049         int n = 0;
4050         unsigned char test = (1<<7);
4051
4052         if ((*CharS & 0xC0) != 0xC0) 
4053                 return 1;
4054
4055         while ((n < 8) && 
4056                ((test & ((unsigned char)*CharS)) != 0)) 
4057         {
4058                 test = test >> 1;
4059                 n ++;
4060         }
4061         if ((n > 6) || ((CharE - CharS) < n))
4062                 n = 0;
4063         return n;
4064 }
4065
4066 /**
4067  * @ingroup StrBuf
4068  * @brief detect whether this char starts an utf-8 encoded char
4069  * @param Char character to inspect
4070  * @returns yes or no
4071  */
4072 static inline int Ctdl_IsUtf8SequenceStart(const char Char)
4073 {
4074 /** 11??.???? indicates an UTF8 Sequence. */
4075         return ((Char & 0xC0) == 0xC0);
4076 }
4077
4078 /**
4079  * @ingroup StrBuf
4080  * @brief measure the number of glyphs in an UTF8 string...
4081  * @param Buf string to measure
4082  * @returns the number of glyphs in Buf
4083  */
4084 long StrBuf_Utf8StrLen(StrBuf *Buf)
4085 {
4086         int n = 0;
4087         int m = 0;
4088         char *aptr, *eptr;
4089
4090         if ((Buf == NULL) || (Buf->BufUsed == 0))
4091                 return 0;
4092         aptr = Buf->buf;
4093         eptr = Buf->buf + Buf->BufUsed;
4094         while ((aptr < eptr) && (*aptr != '\0')) {
4095                 if (Ctdl_IsUtf8SequenceStart(*aptr)){
4096                         m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
4097                         while ((aptr < eptr) && (*aptr++ != '\0')&& (m-- > 0) );
4098                         n ++;
4099                 }
4100                 else {
4101                         n++;
4102                         aptr++;
4103                 }
4104         }
4105         return n;
4106 }
4107
4108 /**
4109  * @ingroup StrBuf
4110  * @brief cuts a string after maxlen glyphs
4111  * @param Buf string to cut to maxlen glyphs
4112  * @param maxlen how long may the string become?
4113  * @returns current length of the string
4114  */
4115 long StrBuf_Utf8StrCut(StrBuf *Buf, int maxlen)
4116 {
4117         char *aptr, *eptr;
4118         int n = 0, m = 0;
4119
4120         aptr = Buf->buf;
4121         eptr = Buf->buf + Buf->BufUsed;
4122         while ((aptr < eptr) && (*aptr != '\0')) {
4123                 if (Ctdl_IsUtf8SequenceStart(*aptr)){
4124                         m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
4125                         while ((*aptr++ != '\0') && (m-- > 0));
4126                         n ++;
4127                 }
4128                 else {
4129                         n++;
4130                         aptr++;
4131                 }
4132                 if (n > maxlen) {
4133                         *aptr = '\0';
4134                         Buf->BufUsed = aptr - Buf->buf;
4135                         return Buf->BufUsed;
4136                 }                       
4137         }
4138         return Buf->BufUsed;
4139
4140 }
4141
4142
4143
4144
4145
4146 /*******************************************************************************
4147  *                               wrapping ZLib                                 *
4148  *******************************************************************************/
4149
4150 /**
4151  * @ingroup StrBuf_DeEnCoder
4152  * @brief uses the same calling syntax as compress2(), but it
4153  *   creates a stream compatible with HTTP "Content-encoding: gzip"
4154  * @param dest compressed buffer
4155  * @param destLen length of the compresed data 
4156  * @param source source to encode
4157  * @param sourceLen length of source to encode 
4158  * @param level compression level
4159  */
4160 #ifdef HAVE_ZLIB
4161 int ZEXPORT compress_gzip(Bytef * dest,
4162                           size_t * destLen,
4163                           const Bytef * source,
4164                           uLong sourceLen,     
4165                           int level)
4166 {
4167         /* write gzip header */
4168         snprintf((char *) dest, *destLen, 
4169                  "%c%c%c%c%c%c%c%c%c%c",
4170                  gz_magic[0], gz_magic[1], Z_DEFLATED,
4171                  0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /* xflags */ ,
4172                  OS_CODE);
4173
4174         /* normal deflate */
4175         z_stream stream;
4176         int err;
4177         stream.next_in = (Bytef *) source;
4178         stream.avail_in = (uInt) sourceLen;
4179         stream.next_out = dest + 10L;   // after header
4180         stream.avail_out = (uInt) * destLen;
4181         if ((uLong) stream.avail_out != *destLen)
4182                 return Z_BUF_ERROR;
4183
4184         stream.zalloc = (alloc_func) 0;
4185         stream.zfree = (free_func) 0;
4186         stream.opaque = (voidpf) 0;
4187
4188         err = deflateInit2(&stream, level, Z_DEFLATED, -MAX_WBITS,
4189                            DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
4190         if (err != Z_OK)
4191                 return err;
4192
4193         err = deflate(&stream, Z_FINISH);
4194         if (err != Z_STREAM_END) {
4195                 deflateEnd(&stream);
4196                 return err == Z_OK ? Z_BUF_ERROR : err;
4197         }
4198         *destLen = stream.total_out + 10L;
4199
4200         /* write CRC and Length */
4201         uLong crc = crc32(0L, source, sourceLen);
4202         int n;
4203         for (n = 0; n < 4; ++n, ++*destLen) {
4204                 dest[*destLen] = (int) (crc & 0xff);
4205                 crc >>= 8;
4206         }
4207         uLong len = stream.total_in;
4208         for (n = 0; n < 4; ++n, ++*destLen) {
4209                 dest[*destLen] = (int) (len & 0xff);
4210                 len >>= 8;
4211         }
4212         err = deflateEnd(&stream);
4213         return err;
4214 }
4215 #endif
4216
4217
4218 /**
4219  * @ingroup StrBuf_DeEnCoder
4220  * @brief compress the buffer with gzip
4221  * Attention! If you feed this a Const String, you must maintain the uncompressed buffer yourself!
4222  * @param Buf buffer whose content is to be gzipped
4223  */
4224 int CompressBuffer(StrBuf *Buf)
4225 {
4226 #ifdef HAVE_ZLIB
4227         char *compressed_data = NULL;
4228         size_t compressed_len, bufsize;
4229         int i = 0;
4230
4231         bufsize = compressed_len = Buf->BufUsed +  (Buf->BufUsed / 100) + 100;
4232         compressed_data = malloc(compressed_len);
4233         
4234         if (compressed_data == NULL)
4235                 return -1;
4236         /* Flush some space after the used payload so valgrind shuts up... */
4237         while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
4238                 Buf->buf[Buf->BufUsed + i++] = '\0';
4239         if (compress_gzip((Bytef *) compressed_data,
4240                           &compressed_len,
4241                           (Bytef *) Buf->buf,
4242                           (uLongf) Buf->BufUsed, Z_BEST_SPEED) == Z_OK) {
4243                 if (!Buf->ConstBuf)
4244                         free(Buf->buf);
4245                 Buf->buf = compressed_data;
4246                 Buf->BufUsed = compressed_len;
4247                 Buf->BufSize = bufsize;
4248                 /* Flush some space after the used payload so valgrind shuts up... */
4249                 i = 0;
4250                 while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
4251                         Buf->buf[Buf->BufUsed + i++] = '\0';
4252                 return 1;
4253         } else {
4254                 free(compressed_data);
4255         }
4256 #endif  /* HAVE_ZLIB */
4257         return 0;
4258 }
4259
4260 /*******************************************************************************
4261  *           File I/O; Callbacks to libevent                                   *
4262  *******************************************************************************/
4263
4264 long StrBuf_read_one_chunk_callback (int fd, short event, IOBuffer *FB)
4265 {
4266         long bufremain = 0;
4267         int n;
4268         
4269         if ((FB == NULL) || (FB->Buf == NULL))
4270                 return -1;
4271
4272         /*
4273          * check whether the read pointer is somewhere in a range 
4274          * where a cut left is inexpensive
4275          */
4276
4277         if (FB->ReadWritePointer != NULL)
4278         {
4279                 long already_read;
4280                 
4281                 already_read = FB->ReadWritePointer - FB->Buf->buf;
4282                 bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
4283
4284                 if (already_read != 0) {
4285                         long unread;
4286                         
4287                         unread = FB->Buf->BufUsed - already_read;
4288
4289                         /* else nothing to compact... */
4290                         if (unread == 0) {
4291                                 FB->ReadWritePointer = FB->Buf->buf;
4292                                 bufremain = FB->Buf->BufSize;                   
4293                         }
4294                         else if ((unread < 64) || 
4295                                  (bufremain < already_read))
4296                         {
4297                                 /* 
4298                                  * if its just a tiny bit remaining, or we run out of space... 
4299                                  * lets tidy up.
4300                                  */
4301                                 FB->Buf->BufUsed = unread;
4302                                 if (unread < already_read)
4303                                         memcpy(FB->Buf->buf, FB->ReadWritePointer, unread);
4304                                 else
4305                                         memmove(FB->Buf->buf, FB->ReadWritePointer, unread);
4306                                 FB->ReadWritePointer = FB->Buf->buf;
4307                                 bufremain = FB->Buf->BufSize - unread - 1;
4308                         }
4309                         else if (bufremain < (FB->Buf->BufSize / 10))
4310                         {
4311                                 /* get a bigger buffer */ 
4312
4313                                 IncreaseBuf(FB->Buf, 0, FB->Buf->BufUsed + 1);
4314
4315                                 FB->ReadWritePointer = FB->Buf->buf + unread;
4316
4317                                 bufremain = FB->Buf->BufSize - unread - 1;
4318 /*TODO: special increase function that won't copy the already read! */
4319                         }
4320                 }
4321                 else if (bufremain < 10) {
4322                         IncreaseBuf(FB->Buf, 1, FB->Buf->BufUsed + 10);
4323                         
4324                         FB->ReadWritePointer = FB->Buf->buf;
4325                         
4326                         bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
4327                 }
4328                 
4329         }
4330         else {
4331                 FB->ReadWritePointer = FB->Buf->buf;
4332                 bufremain = FB->Buf->BufSize - 1;
4333         }
4334
4335         n = read(fd, FB->Buf->buf + FB->Buf->BufUsed, bufremain);
4336
4337         if (n > 0) {
4338                 FB->Buf->BufUsed += n;
4339                 FB->Buf->buf[FB->Buf->BufUsed] = '\0';
4340         }
4341         return n;
4342 }
4343
4344 int StrBuf_write_one_chunk_callback(int fd, short event, IOBuffer *FB)
4345 {
4346         long WriteRemain;
4347         int n;
4348
4349         if ((FB == NULL) || (FB->Buf == NULL))
4350                 return -1;
4351
4352         if (FB->ReadWritePointer != NULL)
4353         {
4354                 WriteRemain = FB->Buf->BufUsed - 
4355                         (FB->ReadWritePointer - 
4356                          FB->Buf->buf);
4357         }
4358         else {
4359                 FB->ReadWritePointer = FB->Buf->buf;
4360                 WriteRemain = FB->Buf->BufUsed;
4361         }
4362
4363         n = write(fd, FB->ReadWritePointer, WriteRemain);
4364         if (n > 0) {
4365                 FB->ReadWritePointer += n;
4366
4367                 if (FB->ReadWritePointer == 
4368                     FB->Buf->buf + FB->Buf->BufUsed)
4369                 {
4370                         FlushStrBuf(FB->Buf);
4371                         FB->ReadWritePointer = NULL;
4372                         return 0;
4373                 }
4374         // check whether we've got something to write
4375         // get the maximum chunk plus the pointer we can send
4376         // write whats there
4377         // if not all was sent, remember the send pointer for the next time
4378                 return FB->ReadWritePointer - FB->Buf->buf + FB->Buf->BufUsed;
4379         }
4380         return n;
4381 }
4382
4383 /**
4384  * @ingroup StrBuf_IO
4385  * @brief extract a "next line" from Buf; Ptr to persist across several iterations
4386  * @param LineBuf your line will be copied here.
4387  * @param FB BLOB with lines of text...
4388  * @param Ptr moved arround to keep the next-line across several iterations
4389  *        has to be &NULL on start; will be &NotNULL on end of buffer
4390  * @returns size of copied buffer
4391  */
4392 eReadState StrBufChunkSipLine(StrBuf *LineBuf, IOBuffer *FB)
4393 {
4394         const char *aptr, *ptr, *eptr;
4395         char *optr, *xptr;
4396
4397         if ((FB == NULL) || (LineBuf == NULL) || (LineBuf->buf == NULL))
4398                 return eReadFail;
4399         
4400
4401         if ((FB->Buf == NULL) || (FB->ReadWritePointer == StrBufNOTNULL)) {
4402                 FB->ReadWritePointer = StrBufNOTNULL;
4403                 return eReadFail;
4404         }
4405
4406         FlushStrBuf(LineBuf);
4407         if (FB->ReadWritePointer == NULL)
4408                 ptr = aptr = FB->Buf->buf;
4409         else
4410                 ptr = aptr = FB->ReadWritePointer;
4411
4412         optr = LineBuf->buf;
4413         eptr = FB->Buf->buf + FB->Buf->BufUsed;
4414         xptr = LineBuf->buf + LineBuf->BufSize - 1;
4415
4416         while ((ptr <= eptr) && 
4417                (*ptr != '\n') &&
4418                (*ptr != '\r') )
4419         {
4420                 *optr = *ptr;
4421                 optr++; ptr++;
4422                 if (optr == xptr) {
4423                         LineBuf->BufUsed = optr - LineBuf->buf;
4424                         IncreaseBuf(LineBuf,  1, LineBuf->BufUsed + 1);
4425                         optr = LineBuf->buf + LineBuf->BufUsed;
4426                         xptr = LineBuf->buf + LineBuf->BufSize - 1;
4427                 }
4428         }
4429
4430         if (ptr >= eptr) {
4431                 if (optr > LineBuf->buf)
4432                         optr --;
4433                 if ((*(ptr - 1) != '\r') && (*(ptr - 1) != '\n')) {
4434                         LineBuf->BufUsed = optr - LineBuf->buf;
4435                         *optr = '\0';
4436                         if ((FB->ReadWritePointer != NULL) && 
4437                             (FB->ReadWritePointer != FB->Buf->buf))
4438                         {
4439                                 /* Ok, the client application read all the data 
4440                                    it was interested in so far. Since there is more to read, 
4441                                    we now shrink the buffer, and move the rest over.
4442                                 */
4443                                 StrBufCutLeft(FB->Buf, 
4444                                               FB->ReadWritePointer - FB->Buf->buf);
4445                                 FB->ReadWritePointer = FB->Buf->buf;
4446                         }
4447                         return eMustReadMore;
4448                 }
4449         }
4450         LineBuf->BufUsed = optr - LineBuf->buf;
4451         *optr = '\0';       
4452         if ((ptr <= eptr) && (*ptr == '\r'))
4453                 ptr ++;
4454         if ((ptr <= eptr) && (*ptr == '\n'))
4455                 ptr ++;
4456         
4457         if (ptr < eptr) {
4458                 FB->ReadWritePointer = ptr;
4459         }
4460         else {
4461                 FlushStrBuf(FB->Buf);
4462                 FB->ReadWritePointer = NULL;
4463         }
4464
4465         return eReadSuccess;
4466 }
4467
4468 /**
4469  * @ingroup StrBuf_CHUNKED_IO
4470  * @brief check whether the chunk-buffer has more data waiting or not.
4471  * @param FB Chunk-Buffer to inspect
4472  */
4473 eReadState StrBufCheckBuffer(IOBuffer *FB)
4474 {
4475         if (FB == NULL)
4476                 return eReadFail;
4477         if (FB->Buf->BufUsed == 0)
4478                 return eReadSuccess;
4479         if (FB->ReadWritePointer == NULL)
4480                 return eBufferNotEmpty;
4481         if (FB->Buf->buf + FB->Buf->BufUsed > FB->ReadWritePointer)
4482                 return eBufferNotEmpty;
4483         return eReadSuccess;
4484 }
4485
4486 long IOBufferStrLength(IOBuffer *FB)
4487 {
4488         if ((FB == NULL) || (FB->Buf == NULL))
4489                 return 0;
4490         if (FB->ReadWritePointer == NULL)
4491                 return StrLength(FB->Buf);
4492         
4493         return StrLength(FB->Buf) - (FB->ReadWritePointer - FB->Buf->buf);
4494 }
4495
4496 inline static void FDIOBufferFlush(FDIOBuffer *FDB)
4497 {
4498         memset(FDB, 0, sizeof(FDIOBuffer));
4499         FDB->OtherFD = -1;
4500         FDB->SplicePipe[0] = -1;
4501         FDB->SplicePipe[1] = -1;
4502 }
4503
4504 void FDIOBufferInit(FDIOBuffer *FDB, IOBuffer *IO, int FD, long TotalSendSize)
4505 {
4506         FDIOBufferFlush(FDB);
4507
4508         FDB->TotalSendSize = TotalSendSize;
4509         if (TotalSendSize > 0)
4510                 FDB->ChunkSize = TotalSendSize;
4511         else
4512         {
4513                 TotalSendSize = SIZ * 10;
4514                 FDB->ChunkSize = TotalSendSize;
4515         }
4516         FDB->IOB = IO;
4517
4518 #ifdef LINUX_SPLICE
4519         if (EnableSplice)
4520                 pipe(FDB->SplicePipe);
4521         else
4522 #endif
4523                 FDB->ChunkBuffer = NewStrBufPlain(NULL, TotalSendSize+ 1);
4524
4525         FDB->OtherFD = FD;
4526 }
4527
4528 void FDIOBufferDelete(FDIOBuffer *FDB)
4529 {
4530 #ifdef LINUX_SPLICE
4531         if (EnableSplice)
4532         {
4533                 if (FDB->SplicePipe[0] > 0)
4534                         close(FDB->SplicePipe[0]);
4535                 if (FDB->SplicePipe[1] > 0)
4536                         close(FDB->SplicePipe[1]);
4537         }
4538         else
4539 #endif
4540                 FreeStrBuf(&FDB->ChunkBuffer);
4541         
4542         if (FDB->OtherFD > 0)
4543                 close(FDB->OtherFD);
4544         FDIOBufferFlush(FDB);
4545 }
4546
4547 int FileSendChunked(FDIOBuffer *FDB, const char **Err)
4548 {
4549         ssize_t sent, pipesize;
4550
4551         if (FDB->TotalSendSize > 0)
4552         {
4553 #ifdef LINUX_SPLICE
4554                 if (EnableSplice)
4555                 {
4556                         if (FDB->PipeSize == 0)
4557                         {
4558                                 pipesize = splice(FDB->OtherFD,
4559                                                   &FDB->TotalSentAlready, 
4560                                                   FDB->SplicePipe[1],
4561                                                   NULL, 
4562                                                   FDB->ChunkSendRemain, 
4563                                                   SPLICE_F_MOVE);
4564         
4565                                 if (pipesize == -1)
4566                                 {
4567                                         *Err = strerror(errno);
4568                                         return pipesize;
4569                                 }
4570                                 FDB->PipeSize = pipesize;
4571                         }
4572                         sent =  splice(FDB->SplicePipe[0],
4573                                        NULL, 
4574                                        FDB->IOB->fd,
4575                                        NULL, 
4576                                        FDB->PipeSize,
4577                                        SPLICE_F_MORE | SPLICE_F_MOVE | SPLICE_F_NONBLOCK);
4578                         if (sent == -1)
4579                         {
4580                                 *Err = strerror(errno);
4581                                 return sent;
4582                         }
4583                         FDB->PipeSize -= sent;
4584                         FDB->ChunkSendRemain -= sent;
4585                         return sent;
4586                 }
4587                 else
4588 #endif
4589                 {
4590                         char *pRead;
4591                         long nRead = 0;
4592
4593                         pRead = FDB->ChunkBuffer->buf;
4594                         while ((FDB->ChunkBuffer->BufUsed < FDB->TotalSendSize) && (nRead >= 0))
4595                         {
4596                                 nRead = read(FDB->OtherFD, pRead, FDB->TotalSendSize - FDB->ChunkBuffer->BufUsed);
4597                                 if (nRead > 0) {
4598                                         FDB->ChunkBuffer->BufUsed += nRead;
4599                                         FDB->ChunkBuffer->buf[FDB->ChunkBuffer->BufUsed] = '\0';
4600                                 }
4601                                 else if (nRead == 0) {}
4602                                 else return nRead;
4603                         }
4604
4605                         nRead = write(FDB->IOB->fd,
4606                                       FDB->ChunkBuffer->buf     + FDB->TotalSentAlready,
4607                                       FDB->ChunkBuffer->BufUsed - FDB->TotalSentAlready);
4608
4609                         if (nRead >= 0) {
4610                                 FDB->TotalSentAlready += nRead;
4611                                 FDB->ChunkSendRemain -= nRead;
4612                                 return FDB->ChunkSendRemain;
4613                         }
4614                         else {
4615                                 return nRead;
4616                         }
4617                 }
4618         }
4619         else
4620         {
4621 #ifdef LINUX_SPLICE
4622                 if (EnableSplice)
4623                 {
4624                         if (FDB->PipeSize == 0)
4625                         {
4626                                 pipesize = splice(FDB->OtherFD,
4627                                                   &FDB->TotalSentAlready, 
4628                                                   FDB->SplicePipe[1],
4629                                                   NULL, 
4630                                                   SIZ * 10, 
4631                                                   SPLICE_F_MOVE);
4632         
4633                                 if (pipesize == -1)
4634                                 {
4635                                         *Err = strerror(errno);
4636                                         return pipesize;
4637                                 }
4638                                 FDB->PipeSize = pipesize;
4639                                 if (pipesize == 0)
4640                                         return -1;
4641                         }
4642                         sent =  splice(FDB->SplicePipe[0],
4643                                        NULL, 
4644                                        FDB->IOB->fd,
4645                                        NULL, 
4646                                        FDB->PipeSize,
4647                                        SPLICE_F_MORE | SPLICE_F_MOVE | SPLICE_F_NONBLOCK);
4648                         if (sent == -1)
4649                         {
4650                                 *Err = strerror(errno);
4651                                 return sent;
4652                         }
4653                         FDB->PipeSize -= sent;
4654                         FDB->ChunkSendRemain -= sent;
4655                         return sent;
4656                 }
4657                 else
4658 #endif
4659                 {
4660                         char *pRead;
4661                         long nRead = 0;
4662
4663                         pRead = FDB->ChunkBuffer->buf;
4664                         while ((FDB->ChunkSendRemain == 0) && 
4665                                (FDB->ChunkBuffer->BufUsed < FDB->ChunkBuffer->BufSize) &&
4666                                (nRead >= 0))
4667                         {
4668                                 FDB->TotalSentAlready = 0;
4669                                 nRead = read(FDB->OtherFD, pRead, FDB->ChunkBuffer->BufSize - FDB->ChunkBuffer->BufUsed);
4670                                 if (nRead > 0) {
4671                                         FDB->ChunkBuffer->BufUsed += nRead;
4672                                         FDB->ChunkBuffer->buf[FDB->ChunkBuffer->BufUsed] = '\0';
4673                                         FDB->ChunkSendRemain += nRead;
4674                                 }
4675                                 else if (nRead == 0)
4676                                 {
4677                                         return -1;
4678                                 }
4679                                 else
4680                                 {
4681                                         *Err = strerror(errno);
4682                                         return nRead;
4683                                 }
4684                         }
4685
4686                         nRead = write(FDB->IOB->fd,
4687                                       FDB->ChunkBuffer->buf     + FDB->TotalSentAlready,
4688                                       FDB->ChunkBuffer->BufUsed - FDB->TotalSentAlready);
4689
4690                         if (nRead >= 0) {
4691                                 FDB->TotalSentAlready += nRead;
4692                                 FDB->ChunkSendRemain -= nRead;
4693                                 if (FDB->ChunkSendRemain == 0)
4694                                 {
4695                                         FDB->ChunkBuffer->BufUsed = 0;
4696                                         FDB->TotalSentAlready = 0;
4697                                 }
4698                                 return FDB->ChunkSendRemain;
4699                         }
4700                         else {
4701                                 return nRead;
4702                         }
4703                 }
4704         }
4705 }
4706
4707 int FileRecvChunked(FDIOBuffer *FDB, const char **Err)
4708 {
4709         ssize_t sent, pipesize;
4710
4711 #ifdef LINUX_SPLICE
4712         if (EnableSplice)
4713         {
4714                 if (FDB->PipeSize == 0)
4715                 {
4716                         pipesize = splice(FDB->IOB->fd,
4717                                           NULL, 
4718                                           FDB->SplicePipe[1],
4719                                           NULL, 
4720                                           FDB->ChunkSendRemain, 
4721                                           SPLICE_F_MORE | SPLICE_F_MOVE|SPLICE_F_NONBLOCK);
4722
4723                         if (pipesize == -1)
4724                         {
4725                                 *Err = strerror(errno);
4726                                 return pipesize;
4727                         }
4728                         FDB->PipeSize = pipesize;
4729                 }
4730         
4731                 sent = splice(FDB->SplicePipe[0],
4732                               NULL, 
4733                               FDB->OtherFD,
4734                               &FDB->TotalSentAlready, 
4735                               FDB->PipeSize,
4736                               SPLICE_F_MORE | SPLICE_F_MOVE);
4737
4738                 if (sent == -1)
4739                 {
4740                         *Err = strerror(errno);
4741                         return sent;
4742                 }
4743                 FDB->PipeSize -= sent;
4744                 FDB->ChunkSendRemain -= sent;
4745                 return sent;
4746         }
4747         else
4748 #endif
4749         {
4750                 sent = read(FDB->IOB->fd, FDB->ChunkBuffer->buf, FDB->ChunkSendRemain);
4751                 if (sent > 0) {
4752                         int nWritten = 0;
4753                         int rc; 
4754                 
4755                         FDB->ChunkBuffer->BufUsed = sent;
4756
4757                         while (nWritten < FDB->ChunkBuffer->BufUsed) {
4758                                 rc =  write(FDB->OtherFD, FDB->ChunkBuffer->buf + nWritten, FDB->ChunkBuffer->BufUsed - nWritten);
4759                                 if (rc < 0) {
4760                                         *Err = strerror(errno);
4761                                         return rc;
4762                                 }
4763                                 nWritten += rc;
4764
4765                         }
4766                         FDB->ChunkBuffer->BufUsed = 0;
4767                         FDB->TotalSentAlready += sent;
4768                         FDB->ChunkSendRemain -= sent;
4769                         return FDB->ChunkSendRemain;
4770                 }
4771                 else if (sent < 0) {
4772                         *Err = strerror(errno);
4773                         return sent;
4774                 }
4775                 return 0;
4776         }
4777 }
4778
4779 int FileMoveChunked(FDIOBuffer *FDB, const char **Err)
4780 {
4781         ssize_t sent, pipesize;
4782
4783 #ifdef LINUX_SPLICE
4784         if (EnableSplice)
4785         {
4786                 if (FDB->PipeSize == 0)
4787                 {
4788                         pipesize = splice(FDB->IOB->fd,
4789                                           &FDB->TotalReadAlready, 
4790                                           FDB->SplicePipe[1],
4791                                           NULL, 
4792                                           FDB->ChunkSendRemain, 
4793                                           SPLICE_F_MORE | SPLICE_F_MOVE|SPLICE_F_NONBLOCK);
4794                         
4795                         if (pipesize == -1)
4796                         {
4797                                 *Err = strerror(errno);
4798                                 return pipesize;
4799                         }
4800                         FDB->PipeSize = pipesize;
4801                 }
4802                 
4803                 sent = splice(FDB->SplicePipe[0],
4804                               NULL, 
4805                               FDB->OtherFD,
4806                               &FDB->TotalSentAlready, 
4807                               FDB->PipeSize,
4808                               SPLICE_F_MORE | SPLICE_F_MOVE);
4809                 
4810                 if (sent == -1)
4811                 {
4812                         *Err = strerror(errno);
4813                         return sent;
4814                 }
4815                 FDB->PipeSize -= sent;
4816                 FDB->ChunkSendRemain -= sent;
4817                 return sent;
4818         }
4819         else
4820 #endif  
4821         {
4822                 sent = read(FDB->IOB->fd, FDB->ChunkBuffer->buf, FDB->ChunkSendRemain);
4823                 if (sent > 0) {
4824                         int nWritten = 0;
4825                         int rc; 
4826                 
4827                         FDB->ChunkBuffer->BufUsed = sent;
4828
4829                         while (nWritten < FDB->ChunkBuffer->BufUsed) {
4830                                 rc =  write(FDB->OtherFD, FDB->ChunkBuffer->buf + nWritten, FDB->ChunkBuffer->BufUsed - nWritten);
4831                                 if (rc < 0) {
4832                                         *Err = strerror(errno);
4833                                         return rc;
4834                                 }
4835                                 nWritten += rc;
4836
4837                         }
4838                         FDB->ChunkBuffer->BufUsed = 0;
4839                         FDB->TotalSentAlready += sent;
4840                         FDB->ChunkSendRemain -= sent;
4841                         return FDB->ChunkSendRemain;
4842                 }
4843                 else if (sent < 0) {
4844                         *Err = strerror(errno);
4845                         return sent;
4846                 }
4847                 return 0;
4848         }
4849 }
4850
4851 eReadState WriteIOBAlreadyRead(FDIOBuffer *FDB, const char **Error)
4852 {
4853         int IsNonBlock;
4854         int fdflags;
4855         long rlen;
4856         long should_write;
4857         int nSuccessLess = 0;
4858         struct timeval tv;
4859         fd_set rfds;
4860
4861         fdflags = fcntl(FDB->OtherFD, F_GETFL);
4862         IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4863
4864         while ((FDB->IOB->ReadWritePointer - FDB->IOB->Buf->buf < FDB->IOB->Buf->BufUsed) &&
4865                (FDB->ChunkSendRemain > 0))
4866         {
4867                 if (IsNonBlock){
4868                         tv.tv_sec = 1; /* selectresolution; */
4869                         tv.tv_usec = 0;
4870                         
4871                         FD_ZERO(&rfds);
4872                         FD_SET(FDB->OtherFD, &rfds);
4873                         if (select(FDB->OtherFD + 1, NULL, &rfds, NULL, &tv) == -1) {
4874                                 *Error = strerror(errno);
4875                                 return eReadFail;
4876                         }
4877                 }
4878                 if (IsNonBlock && !  FD_ISSET(FDB->OtherFD, &rfds)) {
4879                         nSuccessLess ++;
4880                         continue;
4881                 }
4882
4883                 should_write = FDB->IOB->Buf->BufUsed - 
4884                         (FDB->IOB->ReadWritePointer - FDB->IOB->Buf->buf);
4885                 if (should_write > FDB->ChunkSendRemain)
4886                         should_write = FDB->ChunkSendRemain;
4887
4888                 rlen = write(FDB->OtherFD, 
4889                              FDB->IOB->ReadWritePointer, 
4890                              should_write);
4891                 if (rlen < 1) {
4892                         *Error = strerror(errno);
4893                                                 
4894                         return eReadFail;
4895                 }
4896                 FDB->TotalSentAlready += rlen;
4897                 FDB->IOB->ReadWritePointer += rlen;
4898                 FDB->ChunkSendRemain -= rlen;
4899         }
4900         if (FDB->IOB->ReadWritePointer >= FDB->IOB->Buf->buf + FDB->IOB->Buf->BufUsed)
4901         {
4902                 FlushStrBuf(FDB->IOB->Buf);
4903                 FDB->IOB->ReadWritePointer = NULL;
4904         }
4905
4906         if (FDB->ChunkSendRemain == 0)
4907                 return eReadSuccess;
4908         else 
4909                 return eMustReadMore;
4910 }
4911
4912 /*******************************************************************************
4913  *           File I/O; Prefer buffered read since its faster!                  *
4914  *******************************************************************************/
4915
4916 /**
4917  * @ingroup StrBuf_IO
4918  * @brief Read a line from socket
4919  * flushes and closes the FD on error
4920  * @param buf the buffer to get the input to
4921  * @param fd pointer to the filedescriptor to read
4922  * @param append Append to an existing string or replace?
4923  * @param Error strerror() on error 
4924  * @returns numbers of chars read
4925  */
4926 int StrBufTCP_read_line(StrBuf *buf, int *fd, int append, const char **Error)
4927 {
4928         int len, rlen, slen;
4929
4930         if ((buf == NULL) || (buf->buf == NULL)) {
4931                 *Error = strerror(EINVAL);
4932                 return -1;
4933         }
4934
4935         if (!append)
4936                 FlushStrBuf(buf);
4937
4938         slen = len = buf->BufUsed;
4939         while (1) {
4940                 rlen = read(*fd, &buf->buf[len], 1);
4941                 if (rlen < 1) {
4942                         *Error = strerror(errno);
4943                         
4944                         close(*fd);
4945                         *fd = -1;
4946                         
4947                         return -1;
4948                 }
4949                 if (buf->buf[len] == '\n')
4950                         break;
4951                 if (buf->buf[len] != '\r')
4952                         len ++;
4953                 if (len + 2 >= buf->BufSize) {
4954                         buf->BufUsed = len;
4955                         buf->buf[len+1] = '\0';
4956                         IncreaseBuf(buf, 1, -1);
4957                 }
4958         }
4959         buf->BufUsed = len;
4960         buf->buf[len] = '\0';
4961         return len - slen;
4962 }
4963
4964 /**
4965  * @ingroup StrBuf_BufferedIO
4966  * @brief Read a line from socket
4967  * flushes and closes the FD on error
4968  * @param Line the line to read from the fd / I/O Buffer
4969  * @param buf the buffer to get the input to
4970  * @param fd pointer to the filedescriptor to read
4971  * @param timeout number of successless selects until we bail out
4972  * @param selectresolution how long to wait on each select
4973  * @param Error strerror() on error 
4974  * @returns numbers of chars read
4975  */
4976 int StrBufTCP_read_buffered_line(StrBuf *Line, 
4977                                  StrBuf *buf, 
4978                                  int *fd, 
4979                                  int timeout, 
4980                                  int selectresolution, 
4981                                  const char **Error)
4982 {
4983         int len, rlen;
4984         int nSuccessLess = 0;
4985         fd_set rfds;
4986         char *pch = NULL;
4987         int fdflags;
4988         int IsNonBlock;
4989         struct timeval tv;
4990
4991         if (buf->BufUsed > 0) {
4992                 pch = strchr(buf->buf, '\n');
4993                 if (pch != NULL) {
4994                         rlen = 0;
4995                         len = pch - buf->buf;
4996                         if (len > 0 && (*(pch - 1) == '\r') )
4997                                 rlen ++;
4998                         StrBufSub(Line, buf, 0, len - rlen);
4999                         StrBufCutLeft(buf, len + 1);
5000                         return len - rlen;
5001                 }
5002         }
5003         
5004         if (buf->BufSize - buf->BufUsed < 10)
5005                 IncreaseBuf(buf, 1, -1);
5006
5007         fdflags = fcntl(*fd, F_GETFL);
5008         IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
5009
5010         while ((nSuccessLess < timeout) && (pch == NULL)) {
5011                 if (IsNonBlock){
5012                         tv.tv_sec = selectresolution;
5013                         tv.tv_usec = 0;
5014                         
5015                         FD_ZERO(&rfds);
5016                         FD_SET(*fd, &rfds);
5017                         if (select(*fd + 1, NULL, &rfds, NULL, &tv) == -1) {
5018                                 *Error = strerror(errno);
5019                                 close (*fd);
5020                                 *fd = -1;
5021                                 return -1;
5022                         }
5023                 }
5024                 if (IsNonBlock && !  FD_ISSET(*fd, &rfds)) {
5025                         nSuccessLess ++;
5026                         continue;
5027                 }
5028                 rlen = read(*fd, 
5029                             &buf->buf[buf->BufUsed], 
5030                             buf->BufSize - buf->BufUsed - 1);
5031                 if (rlen < 1) {
5032                         *Error = strerror(errno);
5033                         close(*fd);
5034                         *fd = -1;
5035                         return -1;
5036                 }
5037                 else if (rlen > 0) {
5038                         nSuccessLess = 0;
5039                         buf->BufUsed += rlen;
5040                         buf->buf[buf->BufUsed] = '\0';
5041                         pch = strchr(buf->buf, '\n');
5042                         if ((pch == NULL) &&
5043                             (buf->BufUsed + 10 > buf->BufSize) &&
5044                             (IncreaseBuf(buf, 1, -1) == -1))
5045                                 return -1;
5046                         continue;
5047                 }
5048                 
5049         }
5050         if (pch != NULL) {
5051                 rlen = 0;
5052                 len = pch - buf->buf;
5053                 if (len > 0 && (*(pch - 1) == '\r') )
5054                         rlen ++;
5055                 StrBufSub(Line, buf, 0, len - rlen);
5056                 StrBufCutLeft(buf, len + 1);
5057                 return len - rlen;
5058         }
5059         return -1;
5060
5061 }
5062
5063 static const char *ErrRBLF_PreConditionFailed="StrBufTCP_read_buffered_line_fast: Wrong arguments or invalid Filedescriptor";
5064 static const char *ErrRBLF_SelectFailed="StrBufTCP_read_buffered_line_fast: Select failed without reason";
5065 static const char *ErrRBLF_NotEnoughSentFromServer="StrBufTCP_read_buffered_line_fast: No complete line was sent from peer";
5066 /**
5067  * @ingroup StrBuf_BufferedIO
5068  * @brief Read a line from socket
5069  * flushes and closes the FD on error
5070  * @param Line where to append our Line read from the fd / I/O Buffer; 
5071  * @param IOBuf the buffer to get the input to; lifetime pair to FD
5072  * @param Pos pointer to the current read position, should be NULL initialized on opening the FD it belongs to.!
5073  * @param fd pointer to the filedescriptor to read
5074  * @param timeout number of successless selects until we bail out
5075  * @param selectresolution how long to wait on each select
5076  * @param Error strerror() on error 
5077  * @returns numbers of chars read or -1 in case of error. "\n" will become 0
5078  */
5079 int StrBufTCP_read_buffered_line_fast(StrBuf *Line, 
5080                                       StrBuf *IOBuf, 
5081                                       const char **Pos,
5082                                       int *fd, 
5083                                       int timeout, 
5084                                       int selectresolution, 
5085                                       const char **Error)
5086 {
5087         const char *pche = NULL;
5088         const char *pos = NULL;
5089         const char *pLF;
5090         int len, rlen, retlen;
5091         int nSuccessLess = 0;
5092         fd_set rfds;
5093         const char *pch = NULL;
5094         int fdflags;
5095         int IsNonBlock;
5096         struct timeval tv;
5097         
5098         retlen = 0;
5099         if ((Line == NULL) ||
5100             (Pos == NULL) ||
5101             (IOBuf == NULL) ||
5102             (*fd == -1))
5103         {
5104                 if (Pos != NULL)
5105                         *Pos = NULL;
5106                 *Error = ErrRBLF_PreConditionFailed;
5107                 return -1;
5108         }
5109
5110         pos = *Pos;
5111         if ((IOBuf->BufUsed > 0) && 
5112             (pos != NULL) && 
5113             (pos < IOBuf->buf + IOBuf->BufUsed)) 
5114         {
5115                 char *pcht;
5116
5117                 pche = IOBuf->buf + IOBuf->BufUsed;
5118                 pch = pos;
5119                 pcht = Line->buf;
5120
5121                 while ((pch < pche) && (*pch != '\n'))
5122                 {
5123                         if (Line->BufUsed + 10 > Line->BufSize)
5124                         {
5125                                 long apos;
5126                                 apos = pcht - Line->buf;
5127                                 *pcht = '\0';
5128                                 IncreaseBuf(Line, 1, -1);
5129                                 pcht = Line->buf + apos;
5130                         }
5131                         *pcht++ = *pch++;
5132                         Line->BufUsed++;
5133                         retlen++;
5134                 }
5135
5136                 len = pch - pos;
5137                 if (len > 0 && (*(pch - 1) == '\r') )
5138                 {
5139                         retlen--;
5140                         len --;
5141                         pcht --;
5142                         Line->BufUsed --;
5143                 }
5144                 *pcht = '\0';
5145
5146                 if ((pch >= pche) || (*pch == '\0'))
5147                 {
5148                         FlushStrBuf(IOBuf);
5149                         *Pos = NULL;
5150                         pch = NULL;
5151                         pos = 0;
5152                 }
5153
5154                 if ((pch != NULL) && 
5155                     (pch <= pche)) 
5156                 {
5157                         if (pch + 1 >= pche) {
5158                                 *Pos = NULL;
5159                                 FlushStrBuf(IOBuf);
5160                         }
5161                         else
5162                                 *Pos = pch + 1;
5163                         
5164                         return retlen;
5165                 }
5166                 else 
5167                         FlushStrBuf(IOBuf);
5168         }
5169
5170         /* If we come here, Pos is Unset since we read everything into Line, and now go for more. */
5171         
5172         if (IOBuf->BufSize - IOBuf->BufUsed < 10)
5173                 IncreaseBuf(IOBuf, 1, -1);
5174
5175         fdflags = fcntl(*fd, F_GETFL);
5176         IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
5177
5178         pLF = NULL;
5179         while ((nSuccessLess < timeout) && 
5180                (pLF == NULL) &&
5181                (*fd != -1)) {
5182                 if (IsNonBlock)
5183                 {
5184                         tv.tv_sec = 1;
5185                         tv.tv_usec = 0;
5186                 
5187                         FD_ZERO(&rfds);
5188                         FD_SET(*fd, &rfds);
5189                         if (select((*fd) + 1, &rfds, NULL, NULL, &tv) == -1) {
5190                                 *Error = strerror(errno);
5191                                 close (*fd);
5192                                 *fd = -1;
5193                                 if (*Error == NULL)
5194                                         *Error = ErrRBLF_SelectFailed;
5195                                 return -1;
5196                         }
5197                         if (! FD_ISSET(*fd, &rfds) != 0) {
5198                                 nSuccessLess ++;
5199                                 continue;
5200                         }
5201                 }
5202                 rlen = read(*fd, 
5203                             &IOBuf->buf[IOBuf->BufUsed], 
5204                             IOBuf->BufSize - IOBuf->BufUsed - 1);
5205                 if (rlen < 1) {
5206                         *Error = strerror(errno);
5207                         close(*fd);
5208                         *fd = -1;
5209                         return -1;
5210                 }
5211                 else if (rlen > 0) {
5212                         nSuccessLess = 0;
5213                         pLF = IOBuf->buf + IOBuf->BufUsed;
5214                         IOBuf->BufUsed += rlen;
5215                         IOBuf->buf[IOBuf->BufUsed] = '\0';
5216                         
5217                         pche = IOBuf->buf + IOBuf->BufUsed;
5218                         
5219                         while ((pLF < pche) && (*pLF != '\n'))
5220                                 pLF ++;
5221                         if ((pLF >= pche) || (*pLF == '\0'))
5222                                 pLF = NULL;
5223
5224                         if (IOBuf->BufUsed + 10 > IOBuf->BufSize)
5225                         {
5226                                 long apos = 0;
5227
5228                                 if (pLF != NULL) apos = pLF - IOBuf->buf;
5229                                 IncreaseBuf(IOBuf, 1, -1);      
5230                                 if (pLF != NULL) pLF = IOBuf->buf + apos;
5231                         }
5232
5233                         continue;
5234                 }
5235                 else
5236                 {
5237                         nSuccessLess++;
5238                 }
5239         }
5240         *Pos = NULL;
5241         if (pLF != NULL) {
5242                 pos = IOBuf->buf;
5243                 len = pLF - pos;
5244                 if (len > 0 && (*(pLF - 1) == '\r') )
5245                         len --;
5246                 StrBufAppendBufPlain(Line, ChrPtr(IOBuf), len, 0);
5247                 if (pLF + 1 >= IOBuf->buf + IOBuf->BufUsed)
5248                 {
5249                         FlushStrBuf(IOBuf);
5250                 }
5251                 else 
5252                         *Pos = pLF + 1;
5253                 return retlen + len;
5254         }
5255         *Error = ErrRBLF_NotEnoughSentFromServer;
5256         return -1;
5257
5258 }
5259
5260 static const char *ErrRBLF_BLOBPreConditionFailed="StrBufReadBLOB: Wrong arguments or invalid Filedescriptor";
5261 /**
5262  * @ingroup StrBuf_IO
5263  * @brief Input binary data from socket
5264  * flushes and closes the FD on error
5265  * @param Buf the buffer to get the input to
5266  * @param fd pointer to the filedescriptor to read
5267  * @param append Append to an existing string or replace?
5268  * @param nBytes the maximal number of bytes to read
5269  * @param Error strerror() on error 
5270  * @returns numbers of chars read
5271  */
5272 int StrBufReadBLOB(StrBuf *Buf, int *fd, int append, long nBytes, const char **Error)
5273 {
5274         int fdflags;
5275         int rlen;
5276         int nSuccessLess;
5277         int nRead = 0;
5278         char *ptr;
5279         int IsNonBlock;
5280         struct timeval tv;
5281         fd_set rfds;
5282
5283         if ((Buf == NULL) || (Buf->buf == NULL) || (*fd == -1))
5284         {
5285                 *Error = ErrRBLF_BLOBPreConditionFailed;
5286                 return -1;
5287         }
5288         if (!append)
5289                 FlushStrBuf(Buf);
5290         if (Buf->BufUsed + nBytes >= Buf->BufSize)
5291                 IncreaseBuf(Buf, 1, Buf->BufUsed + nBytes);
5292
5293         ptr = Buf->buf + Buf->BufUsed;
5294
5295         fdflags = fcntl(*fd, F_GETFL);
5296         IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
5297         nSuccessLess = 0;
5298         while ((nRead < nBytes) && 
5299                (*fd != -1)) 
5300         {
5301                 if (IsNonBlock)
5302                 {
5303                         tv.tv_sec = 1;
5304                         tv.tv_usec = 0;
5305                 
5306                         FD_ZERO(&rfds);
5307                         FD_SET(*fd, &rfds);
5308                         if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
5309                                 *Error = strerror(errno);
5310                                 close (*fd);
5311                                 *fd = -1;
5312                                 if (*Error == NULL)
5313                                         *Error = ErrRBLF_SelectFailed;
5314                                 return -1;
5315                         }
5316                         if (! FD_ISSET(*fd, &rfds) != 0) {
5317                                 nSuccessLess ++;
5318                                 continue;
5319                         }
5320                 }
5321
5322                 if ((rlen = read(*fd, 
5323                                  ptr,
5324                                  nBytes - nRead)) == -1) {
5325                         close(*fd);
5326                         *fd = -1;
5327                         *Error = strerror(errno);
5328                         return rlen;
5329                 }
5330                 nRead += rlen;
5331                 ptr += rlen;
5332                 Buf->BufUsed += rlen;
5333         }
5334         Buf->buf[Buf->BufUsed] = '\0';
5335         return nRead;
5336 }
5337
5338 const char *ErrRBB_BLOBFPreConditionFailed = "StrBufReadBLOBBuffered: to many selects; aborting.";
5339 const char *ErrRBB_too_many_selects        = "StrBufReadBLOBBuffered: to many selects; aborting.";
5340 /**
5341  * @ingroup StrBuf_BufferedIO
5342  * @brief Input binary data from socket
5343  * flushes and closes the FD on error
5344  * @param Blob put binary thing here
5345  * @param IOBuf the buffer to get the input to
5346  * @param Pos offset inside of IOBuf
5347  * @param fd pointer to the filedescriptor to read
5348  * @param append Append to an existing string or replace?
5349  * @param nBytes the maximal number of bytes to read
5350  * @param check whether we should search for '000\n' terminators in case of timeouts
5351  * @param Error strerror() on error 
5352  * @returns numbers of chars read
5353  */
5354 int StrBufReadBLOBBuffered(StrBuf *Blob, 
5355                            StrBuf *IOBuf, 
5356                            const char **Pos,
5357                            int *fd, 
5358                            int append, 
5359                            long nBytes, 
5360                            int check, 
5361                            const char **Error)
5362 {
5363         const char *pos;
5364         int fdflags;
5365         int rlen = 0;
5366         int nRead = 0;
5367         int nAlreadyRead = 0;
5368         int IsNonBlock;
5369         char *ptr;
5370         fd_set rfds;
5371         struct timeval tv;
5372         int nSuccessLess = 0;
5373         int MaxTries;
5374
5375         if ((Blob == NULL)  ||
5376             (*fd == -1)     ||
5377             (IOBuf == NULL) ||
5378             (Pos == NULL))
5379         {
5380                 if (Pos != NULL)
5381                         *Pos = NULL;
5382                 *Error = ErrRBB_BLOBFPreConditionFailed;
5383                 return -1;
5384         }
5385
5386         if (!append)
5387                 FlushStrBuf(Blob);
5388         if (Blob->BufUsed + nBytes >= Blob->BufSize) 
5389                 IncreaseBuf(Blob, append, Blob->BufUsed + nBytes);
5390         
5391         pos = *Pos;
5392
5393         if (pos != NULL)
5394                 rlen = pos - IOBuf->buf;
5395         rlen = IOBuf->BufUsed - rlen;
5396
5397
5398         if ((IOBuf->BufUsed > 0) && 
5399             (pos != NULL) && 
5400             (pos < IOBuf->buf + IOBuf->BufUsed)) 
5401         {
5402                 if (rlen < nBytes) {
5403                         memcpy(Blob->buf + Blob->BufUsed, pos, rlen);
5404                         Blob->BufUsed += rlen;
5405                         Blob->buf[Blob->BufUsed] = '\0';
5406                         nAlreadyRead = nRead = rlen;
5407                         *Pos = NULL; 
5408                 }
5409                 if (rlen >= nBytes) {
5410                         memcpy(Blob->buf + Blob->BufUsed, pos, nBytes);
5411                         Blob->BufUsed += nBytes;
5412                         Blob->buf[Blob->BufUsed] = '\0';
5413                         if (rlen == nBytes) {
5414                                 *Pos = NULL; 
5415                                 FlushStrBuf(IOBuf);
5416                         }
5417                         else 
5418                                 *Pos += nBytes;
5419                         return nBytes;
5420                 }
5421         }
5422
5423         FlushStrBuf(IOBuf);
5424         *Pos = NULL;
5425         if (IOBuf->BufSize < nBytes - nRead)
5426                 IncreaseBuf(IOBuf, 0, nBytes - nRead);
5427         ptr = IOBuf->buf;
5428
5429         fdflags = fcntl(*fd, F_GETFL);
5430         IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
5431         if (IsNonBlock)
5432                 MaxTries =   1000;
5433         else
5434                 MaxTries = 100000;
5435
5436         nBytes -= nRead;
5437         nRead = 0;
5438         while ((nSuccessLess < MaxTries) && 
5439                (nRead < nBytes) &&
5440                (*fd != -1)) {
5441                 if (IsNonBlock)
5442                 {
5443                         tv.tv_sec = 1;
5444                         tv.tv_usec = 0;
5445                 
5446                         FD_ZERO(&rfds);
5447                         FD_SET(*fd, &rfds);
5448                         if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
5449                                 *Error = strerror(errno);
5450                                 close (*fd);
5451                                 *fd = -1;
5452                                 if (*Error == NULL)
5453                                         *Error = ErrRBLF_SelectFailed;
5454                                 return -1;
5455                         }
5456                         if (! FD_ISSET(*fd, &rfds) != 0) {
5457                                 nSuccessLess ++;
5458                                 continue;
5459                         }
5460                 }
5461                 rlen = read(*fd, 
5462                             ptr,
5463                             IOBuf->BufSize - (ptr - IOBuf->buf));
5464                 if (rlen == -1) {
5465                         close(*fd);
5466                         *fd = -1;
5467                         *Error = strerror(errno);
5468                         return rlen;
5469                 }
5470                 else if (rlen == 0){
5471                         if ((check == NNN_TERM) && 
5472                             (nRead > 5) &&
5473                             (strncmp(IOBuf->buf + IOBuf->BufUsed - 5, "\n000\n", 5) == 0)) 
5474                         {
5475                                 StrBufPlain(Blob, HKEY("\n000\n"));
5476                                 StrBufCutRight(Blob, 5);
5477                                 return Blob->BufUsed;
5478                         }
5479                         else if (!IsNonBlock) 
5480                                 nSuccessLess ++;
5481                         else if (nSuccessLess > MaxTries) {
5482                                 FlushStrBuf(IOBuf);
5483                                 *Error = ErrRBB_too_many_selects;
5484                                 return -1;
5485                         }
5486                 }
5487                 else if (rlen > 0) {
5488                         nSuccessLess = 0;
5489                         nRead += rlen;
5490                         ptr += rlen;
5491                         IOBuf->BufUsed += rlen;
5492                 }
5493         }
5494         if (nSuccessLess >= MaxTries) {
5495                 FlushStrBuf(IOBuf);
5496                 *Error = ErrRBB_too_many_selects;
5497                 return -1;
5498         }
5499
5500         if (nRead > nBytes) {
5501                 *Pos = IOBuf->buf + nBytes;
5502         }
5503         Blob->buf[Blob->BufUsed] = '\0';
5504         StrBufAppendBufPlain(Blob, IOBuf->buf, nBytes, 0);
5505         if (*Pos == NULL) {
5506                 FlushStrBuf(IOBuf);
5507         }
5508         return nRead + nAlreadyRead;
5509 }
5510
5511 /**
5512  * @ingroup StrBuf_IO
5513  * @brief extract a "next line" from Buf; Ptr to persist across several iterations
5514  * @param LineBuf your line will be copied here.
5515  * @param Buf BLOB with lines of text...
5516  * @param Ptr moved arround to keep the next-line across several iterations
5517  *        has to be &NULL on start; will be &NotNULL on end of buffer
5518  * @returns size of remaining buffer
5519  */
5520 int StrBufSipLine(StrBuf *LineBuf, const StrBuf *Buf, const char **Ptr)
5521 {
5522         const char *aptr, *ptr, *eptr;
5523         char *optr, *xptr;
5524
5525         if ((Buf == NULL) ||
5526             (*Ptr == StrBufNOTNULL) ||
5527             (LineBuf == NULL)||
5528             (LineBuf->buf == NULL))
5529         {
5530                 *Ptr = StrBufNOTNULL;
5531                 return 0;
5532         }
5533
5534         FlushStrBuf(LineBuf);
5535         if (*Ptr==NULL)
5536                 ptr = aptr = Buf->buf;
5537         else
5538                 ptr = aptr = *Ptr;
5539
5540         optr = LineBuf->buf;
5541         eptr = Buf->buf + Buf->BufUsed;
5542         xptr = LineBuf->buf + LineBuf->BufSize - 1;
5543
5544         while ((ptr <= eptr) && 
5545                (*ptr != '\n') &&
5546                (*ptr != '\r') )
5547         {
5548                 *optr = *ptr;
5549                 optr++; ptr++;
5550                 if (optr == xptr) {
5551                         LineBuf->BufUsed = optr - LineBuf->buf;
5552                         IncreaseBuf(LineBuf,  1, LineBuf->BufUsed + 1);
5553                         optr = LineBuf->buf + LineBuf->BufUsed;
5554                         xptr = LineBuf->buf + LineBuf->BufSize - 1;
5555                 }
5556         }
5557
5558         if ((ptr >= eptr) && (optr > LineBuf->buf))
5559                 optr --;
5560         LineBuf->BufUsed = optr - LineBuf->buf;
5561         *optr = '\0';       
5562         if ((ptr <= eptr) && (*ptr == '\r'))
5563                 ptr ++;
5564         if ((ptr <= eptr) && (*ptr == '\n'))
5565                 ptr ++;
5566         
5567         if (ptr < eptr) {
5568                 *Ptr = ptr;
5569         }
5570         else {
5571                 *Ptr = StrBufNOTNULL;
5572         }
5573
5574         return Buf->BufUsed - (ptr - Buf->buf);
5575 }
5576
5577
5578 /**
5579  * @ingroup StrBuf_IO
5580  * @brief removes double slashes from pathnames
5581  * @param Dir directory string to filter
5582  * @param RemoveTrailingSlash allows / disallows trailing slashes
5583  */
5584 void StrBufStripSlashes(StrBuf *Dir, int RemoveTrailingSlash)
5585 {
5586         char *a, *b;
5587
5588         a = b = Dir->buf;
5589
5590         while (!IsEmptyStr(a)) {
5591                 if (*a == '/') {
5592                         while (*a == '/')
5593                                 a++;
5594                         *b = '/';
5595                         b++;
5596                 }
5597                 else {
5598                         *b = *a;
5599                         b++; a++;
5600                 }
5601         }
5602         if ((RemoveTrailingSlash) &&
5603             (b > Dir->buf) && 
5604             (*(b - 1) == '/')){
5605                 b--;
5606         }
5607         *b = '\0';
5608         Dir->BufUsed = b - Dir->buf;
5609 }
5610
5611