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