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