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