4cee2be32980d3133f41bdd9749a48ae850b375e
[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                 StrBufCutAt(Buf, 0, Buf->buf);
1181                 return;
1182         }
1183
1184         pRight = strchr(Buf->buf, rightboundary);
1185         if (pRight != NULL) {
1186                 StrBufCutAt(Buf, 0, pRight);
1187         }
1188         else {
1189                 StrBufCutAt(Buf, 0, Buf->buf);
1190                 return;
1191         }
1192
1193         pLeft = strrchr(ChrPtr(Buf), leftboundary);
1194         if (pLeft != NULL) {
1195                 StrBufCutLeft(Buf, pLeft - Buf->buf + 1);
1196         }
1197         else {
1198                 StrBufCutAt(Buf, 0, Buf->buf);
1199                 return;
1200         }
1201 }
1202
1203
1204 /**
1205  * @ingroup StrBuf_Filler
1206  * @brief uppercase the contents of a buffer
1207  * @param Buf the buffer to translate
1208  */
1209 void StrBufUpCase(StrBuf *Buf) 
1210 {
1211         char *pch, *pche;
1212
1213         if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1214
1215         pch = Buf->buf;
1216         pche = pch + Buf->BufUsed;
1217         while (pch < pche) {
1218                 *pch = toupper(*pch);
1219                 pch ++;
1220         }
1221 }
1222
1223
1224 /**
1225  * @ingroup StrBuf_Filler
1226  * @brief lowercase the contents of a buffer
1227  * @param Buf the buffer to translate
1228  */
1229 void StrBufLowerCase(StrBuf *Buf) 
1230 {
1231         char *pch, *pche;
1232
1233         if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1234
1235         pch = Buf->buf;
1236         pche = pch + Buf->BufUsed;
1237         while (pch < pche) {
1238                 *pch = tolower(*pch);
1239                 pch ++;
1240         }
1241 }
1242
1243
1244 /*******************************************************************************
1245  *           a tokenizer that kills, maims, and destroys                       *
1246  *******************************************************************************/
1247
1248 /**
1249  * @ingroup StrBuf_Tokenizer
1250  * @brief Replace a token at a given place with a given length by another token with given length
1251  * @param Buf String where to work on
1252  * @param where where inside of the Buf is the search-token
1253  * @param HowLong How long is the token to be replaced
1254  * @param Repl Token to insert at 'where'
1255  * @param ReplLen Length of repl
1256  * @returns -1 if fail else length of resulting Buf
1257  */
1258 int StrBufReplaceToken(StrBuf *Buf, long where, long HowLong, 
1259                        const char *Repl, long ReplLen)
1260 {
1261
1262         if ((Buf == NULL) || 
1263             (where > Buf->BufUsed) ||
1264             (where + HowLong > Buf->BufUsed))
1265                 return -1;
1266
1267         if (where + ReplLen - HowLong > Buf->BufSize)
1268                 if (IncreaseBuf(Buf, 1, Buf->BufUsed + ReplLen) < 0)
1269                         return -1;
1270
1271         memmove(Buf->buf + where + ReplLen, 
1272                 Buf->buf + where + HowLong,
1273                 Buf->BufUsed - where - HowLong);
1274                                                 
1275         memcpy(Buf->buf + where, 
1276                Repl, ReplLen);
1277
1278         Buf->BufUsed += ReplLen - HowLong;
1279
1280         return Buf->BufUsed;
1281 }
1282
1283 /**
1284  * @ingroup StrBuf_Tokenizer
1285  * @brief Counts the numbmer of tokens in a buffer
1286  * @param source String to count tokens in
1287  * @param tok    Tokenizer char to count
1288  * @returns numbers of tokenizer chars found
1289  */
1290 int StrBufNum_tokens(const StrBuf *source, char tok)
1291 {
1292         char *pch, *pche;
1293         long NTokens;
1294         if ((source == NULL) || (source->BufUsed == 0))
1295                 return 0;
1296         if ((source->BufUsed == 1) && (*source->buf == tok))
1297                 return 2;
1298         NTokens = 1;
1299         pch = source->buf;
1300         pche = pch + source->BufUsed;
1301         while (pch < pche)
1302         {
1303                 if (*pch == tok)
1304                         NTokens ++;
1305                 pch ++;
1306         }
1307         return NTokens;
1308 }
1309
1310 /**
1311  * @ingroup StrBuf_Tokenizer
1312  * @brief a string tokenizer
1313  * @param Source StringBuffer to read into
1314  * @param parmnum n'th Parameter to remove
1315  * @param separator tokenizer character
1316  * @returns -1 if not found, else length of token.
1317  */
1318 int StrBufRemove_token(StrBuf *Source, int parmnum, char separator)
1319 {
1320         int ReducedBy;
1321         char *d, *s, *end;              /* dest, source */
1322         int count = 0;
1323
1324         /* Find desired @parameter */
1325         end = Source->buf + Source->BufUsed;
1326         d = Source->buf;
1327         while ((d <= end) && 
1328                (count < parmnum))
1329         {
1330                 /* End of string, bail! */
1331                 if (!*d) {
1332                         d = NULL;
1333                         break;
1334                 }
1335                 if (*d == separator) {
1336                         count++;
1337                 }
1338                 d++;
1339         }
1340         if ((d == NULL) || (d >= end))
1341                 return 0;               /* @Parameter not found */
1342
1343         /* Find next @parameter */
1344         s = d;
1345         while ((s <= end) && 
1346                (*s && *s != separator))
1347         {
1348                 s++;
1349         }
1350         if (*s == separator)
1351                 s++;
1352         ReducedBy = d - s;
1353
1354         /* Hack and slash */
1355         if (s >= end) {
1356                 return 0;
1357         }
1358         else if (*s) {
1359                 memmove(d, s, Source->BufUsed - (s - Source->buf));
1360                 Source->BufUsed += ReducedBy;
1361                 Source->buf[Source->BufUsed] = '\0';
1362         }
1363         else if (d == Source->buf) {
1364                 *d = 0;
1365                 Source->BufUsed = 0;
1366         }
1367         else {
1368                 *--d = '\0';
1369                 Source->BufUsed += ReducedBy;
1370         }
1371         /*
1372         while (*s) {
1373                 *d++ = *s++;
1374         }
1375         *d = 0;
1376         */
1377         return ReducedBy;
1378 }
1379
1380 int StrBufExtract_tokenFromStr(StrBuf *dest, const char *Source, long SourceLen, int parmnum, char separator)
1381 {
1382         const StrBuf Temp = {
1383                 (char*)Source,
1384                 SourceLen,
1385                 SourceLen,
1386                 1
1387 #ifdef SIZE_DEBUG
1388                 ,
1389                 0,
1390                 "",
1391                 ""
1392 #endif
1393         };
1394
1395         return StrBufExtract_token(dest, &Temp, parmnum, separator);
1396 }
1397
1398 /**
1399  * @ingroup StrBuf_Tokenizer
1400  * @brief a string tokenizer
1401  * @param dest Destination StringBuffer
1402  * @param Source StringBuffer to read into
1403  * @param parmnum n'th Parameter to extract
1404  * @param separator tokenizer character
1405  * @returns -1 if not found, else length of token.
1406  */
1407 int StrBufExtract_token(StrBuf *dest, const StrBuf *Source, int parmnum, char separator)
1408 {
1409         const char *s, *e;              //* source * /
1410         int len = 0;                    //* running total length of extracted string * /
1411         int current_token = 0;          //* token currently being processed * /
1412          
1413         if (dest != NULL) {
1414                 dest->buf[0] = '\0';
1415                 dest->BufUsed = 0;
1416         }
1417         else
1418                 return(-1);
1419
1420         if ((Source == NULL) || (Source->BufUsed ==0)) {
1421                 return(-1);
1422         }
1423         s = Source->buf;
1424         e = s + Source->BufUsed;
1425
1426         //cit_backtrace();
1427         //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
1428
1429         while ((s < e) && !IsEmptyStr(s)) {
1430                 if (*s == separator) {
1431                         ++current_token;
1432                 }
1433                 if (len >= dest->BufSize) {
1434                         dest->BufUsed = len;
1435                         if (IncreaseBuf(dest, 1, -1) < 0) {
1436                                 dest->BufUsed --;
1437                                 break;
1438                         }
1439                 }
1440                 if ( (current_token == parmnum) && 
1441                      (*s != separator)) {
1442                         dest->buf[len] = *s;
1443                         ++len;
1444                 }
1445                 else if (current_token > parmnum) {
1446                         break;
1447                 }
1448                 ++s;
1449         }
1450         
1451         dest->buf[len] = '\0';
1452         dest->BufUsed = len;
1453                 
1454         if (current_token < parmnum) {
1455                 //lprintf (CTDL_DEBUG,"test <!: %s\n", dest);
1456                 return(-1);
1457         }
1458         //lprintf (CTDL_DEBUG,"test <: %d; %s\n", len, dest);
1459         return(len);
1460 }
1461
1462
1463
1464
1465
1466 /**
1467  * @ingroup StrBuf_Tokenizer
1468  * @brief a string tokenizer to fetch an integer
1469  * @param Source String containing tokens
1470  * @param parmnum n'th Parameter to extract
1471  * @param separator tokenizer character
1472  * @returns 0 if not found, else integer representation of the token
1473  */
1474 int StrBufExtract_int(const StrBuf* Source, int parmnum, char separator)
1475 {
1476         StrBuf tmp;
1477         char buf[64];
1478         
1479         tmp.buf = buf;
1480         buf[0] = '\0';
1481         tmp.BufSize = 64;
1482         tmp.BufUsed = 0;
1483         tmp.ConstBuf = 1;
1484         if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
1485                 return(atoi(buf));
1486         else
1487                 return 0;
1488 }
1489
1490 /**
1491  * @ingroup StrBuf_Tokenizer
1492  * @brief a string tokenizer to fetch a long integer
1493  * @param Source String containing tokens
1494  * @param parmnum n'th Parameter to extract
1495  * @param separator tokenizer character
1496  * @returns 0 if not found, else long integer representation of the token
1497  */
1498 long StrBufExtract_long(const StrBuf* Source, int parmnum, char separator)
1499 {
1500         StrBuf tmp;
1501         char buf[64];
1502         
1503         tmp.buf = buf;
1504         buf[0] = '\0';
1505         tmp.BufSize = 64;
1506         tmp.BufUsed = 0;
1507         tmp.ConstBuf = 1;
1508         if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
1509                 return(atoi(buf));
1510         else
1511                 return 0;
1512 }
1513
1514
1515 /**
1516  * @ingroup StrBuf_Tokenizer
1517  * @brief a string tokenizer to fetch an unsigned long
1518  * @param Source String containing tokens
1519  * @param parmnum n'th Parameter to extract
1520  * @param separator tokenizer character
1521  * @returns 0 if not found, else unsigned long representation of the token
1522  */
1523 unsigned long StrBufExtract_unsigned_long(const StrBuf* Source, int parmnum, char separator)
1524 {
1525         StrBuf tmp;
1526         char buf[64];
1527         char *pnum;
1528         
1529         tmp.buf = buf;
1530         buf[0] = '\0';
1531         tmp.BufSize = 64;
1532         tmp.BufUsed = 0;
1533         tmp.ConstBuf = 1;
1534         if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0) {
1535                 pnum = &buf[0];
1536                 if (*pnum == '-')
1537                         pnum ++;
1538                 return (unsigned long) atol(pnum);
1539         }
1540         else 
1541                 return 0;
1542 }
1543
1544
1545
1546 /**
1547  * @ingroup StrBuf_NextTokenizer
1548  * @brief a string tokenizer; Bounds checker
1549  *  function to make shure whether StrBufExtract_NextToken and friends have reached the end of the string.
1550  * @param Source our tokenbuffer
1551  * @param pStart the token iterator pointer to inspect
1552  * @returns whether the revolving pointer is inside of the search range
1553  */
1554 int StrBufHaveNextToken(const StrBuf *Source, const char **pStart)
1555 {
1556         if ((Source == NULL) || 
1557             (*pStart == StrBufNOTNULL) ||
1558             (Source->BufUsed == 0))
1559         {
1560                 return 0;
1561         }
1562         if (*pStart == NULL)
1563         {
1564                 return 1;
1565         }
1566         else if (*pStart > Source->buf + Source->BufUsed)
1567         {
1568                 return 0;
1569         }
1570         else if (*pStart <= Source->buf)
1571         {
1572                 return 0;
1573         }
1574
1575         return 1;
1576 }
1577
1578 /**
1579  * @ingroup StrBuf_NextTokenizer
1580  * @brief a string tokenizer
1581  * @param dest Destination StringBuffer
1582  * @param Source StringBuffer to read into
1583  * @param pStart pointer to the end of the last token. Feed with NULL on start.
1584  * @param separator tokenizer 
1585  * @returns -1 if not found, else length of token.
1586  */
1587 int StrBufExtract_NextToken(StrBuf *dest, const StrBuf *Source, const char **pStart, char separator)
1588 {
1589         const char *s;          /* source */
1590         const char *EndBuffer;  /* end stop of source buffer */
1591         int current_token = 0;  /* token currently being processed */
1592         int len = 0;            /* running total length of extracted string */
1593
1594         if ((Source          == NULL) || 
1595             (Source->BufUsed == 0)      ) 
1596         {
1597                 *pStart = StrBufNOTNULL;
1598                 if (dest != NULL)
1599                         FlushStrBuf(dest);
1600                 return -1;
1601         }
1602          
1603         EndBuffer = Source->buf + Source->BufUsed;
1604
1605         if (dest != NULL) 
1606         {
1607                 dest->buf[0] = '\0';
1608                 dest->BufUsed = 0;
1609         }
1610         else
1611         {
1612                 *pStart = EndBuffer + 1;
1613                 return -1;
1614         }
1615
1616         if (*pStart == NULL)
1617         {
1618                 *pStart = Source->buf; /* we're starting to examine this buffer. */
1619         }
1620         else if ((*pStart < Source->buf) || 
1621                  (*pStart > EndBuffer  )   ) 
1622         {
1623                 return -1; /* no more tokens to find. */
1624         }
1625
1626         s = *pStart;
1627         /* start to find the next token */
1628         while ((s <= EndBuffer)      && 
1629                (current_token == 0) ) 
1630         {
1631                 if (*s == separator) 
1632                 {
1633                         /* we found the next token */
1634                         ++current_token;
1635                 }
1636
1637                 if (len >= dest->BufSize) 
1638                 {
1639                         /* our Dest-buffer isn't big enough, increase it. */
1640                         dest->BufUsed = len;
1641
1642                         if (IncreaseBuf(dest, 1, -1) < 0) {
1643                                 /* WHUT? no more mem? bail out. */
1644                                 s = EndBuffer;
1645                                 dest->BufUsed --;
1646                                 break;
1647                         }
1648                 }
1649
1650                 if ( (current_token == 0 ) &&   /* are we in our target token? */
1651                      (!IsEmptyStr(s)     ) &&
1652                      (separator     != *s)    ) /* don't copy the token itself */
1653                 {
1654                         dest->buf[len] = *s;    /* Copy the payload */
1655                         ++len;                  /* remember the bigger size. */
1656                 }
1657
1658                 ++s;
1659         }
1660
1661         /* did we reach the end? */
1662         if ((s > EndBuffer)) {
1663                 EndBuffer = StrBufNOTNULL;
1664                 *pStart = EndBuffer;
1665         }
1666         else {
1667                 *pStart = s;  /* remember the position for the next run */
1668         }
1669
1670         /* sanitize our extracted token */
1671         dest->buf[len] = '\0';
1672         dest->BufUsed  = len;
1673
1674         return (len);
1675 }
1676
1677
1678 /**
1679  * @ingroup StrBuf_NextTokenizer
1680  * @brief a string tokenizer
1681  * @param Source StringBuffer to read from
1682  * @param pStart pointer to the end of the last token. Feed with NULL.
1683  * @param separator tokenizer character
1684  * @param nTokens number of tokens to fastforward over
1685  * @returns -1 if not found, else length of token.
1686  */
1687 int StrBufSkip_NTokenS(const StrBuf *Source, const char **pStart, char separator, int nTokens)
1688 {
1689         const char *s, *EndBuffer;      //* source * /
1690         int len = 0;                    //* running total length of extracted string * /
1691         int current_token = 0;          //* token currently being processed * /
1692
1693         if ((Source == NULL) || 
1694             (Source->BufUsed ==0)) {
1695                 return(-1);
1696         }
1697         if (nTokens == 0)
1698                 return Source->BufUsed;
1699
1700         if (*pStart == NULL)
1701                 *pStart = Source->buf;
1702
1703         EndBuffer = Source->buf + Source->BufUsed;
1704
1705         if ((*pStart < Source->buf) || 
1706             (*pStart >  EndBuffer)) {
1707                 return (-1);
1708         }
1709
1710
1711         s = *pStart;
1712
1713         //cit_backtrace();
1714         //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
1715
1716         while ((s < EndBuffer) && !IsEmptyStr(s)) {
1717                 if (*s == separator) {
1718                         ++current_token;
1719                 }
1720                 if (current_token >= nTokens) {
1721                         break;
1722                 }
1723                 ++s;
1724         }
1725         *pStart = s;
1726         (*pStart) ++;
1727
1728         return(len);
1729 }
1730
1731 /**
1732  * @ingroup StrBuf_NextTokenizer
1733  * @brief a string tokenizer to fetch an integer
1734  * @param Source StringBuffer to read from
1735  * @param pStart Cursor on the tokenstring
1736  * @param separator tokenizer character
1737  * @returns 0 if not found, else integer representation of the token
1738  */
1739 int StrBufExtractNext_int(const StrBuf* Source, const char **pStart, char separator)
1740 {
1741         StrBuf tmp;
1742         char buf[64];
1743         
1744         tmp.buf = buf;
1745         buf[0] = '\0';
1746         tmp.BufSize = 64;
1747         tmp.BufUsed = 0;
1748         tmp.ConstBuf = 1;
1749         if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0)
1750                 return(atoi(buf));
1751         else
1752                 return 0;
1753 }
1754
1755 /**
1756  * @ingroup StrBuf_NextTokenizer
1757  * @brief a string tokenizer to fetch a long integer
1758  * @param Source StringBuffer to read from
1759  * @param pStart Cursor on the tokenstring
1760  * @param separator tokenizer character
1761  * @returns 0 if not found, else long integer representation of the token
1762  */
1763 long StrBufExtractNext_long(const StrBuf* Source, const char **pStart, char separator)
1764 {
1765         StrBuf tmp;
1766         char buf[64];
1767         
1768         tmp.buf = buf;
1769         buf[0] = '\0';
1770         tmp.BufSize = 64;
1771         tmp.BufUsed = 0;
1772         tmp.ConstBuf = 1;
1773         if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0)
1774                 return(atoi(buf));
1775         else
1776                 return 0;
1777 }
1778
1779
1780 /**
1781  * @ingroup StrBuf_NextTokenizer
1782  * @brief a string tokenizer to fetch an unsigned long
1783  * @param Source StringBuffer to read from
1784  * @param pStart Cursor on the tokenstring
1785  * @param separator tokenizer character
1786  * @returns 0 if not found, else unsigned long representation of the token
1787  */
1788 unsigned long StrBufExtractNext_unsigned_long(const StrBuf* Source, const char **pStart, char separator)
1789 {
1790         StrBuf tmp;
1791         char buf[64];
1792         char *pnum;
1793         
1794         tmp.buf = buf;
1795         buf[0] = '\0';
1796         tmp.BufSize = 64;
1797         tmp.BufUsed = 0;
1798         tmp.ConstBuf = 1;
1799         if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0) {
1800                 pnum = &buf[0];
1801                 if (*pnum == '-')
1802                         pnum ++;
1803                 return (unsigned long) atol(pnum);
1804         }
1805         else 
1806                 return 0;
1807 }
1808
1809
1810
1811
1812
1813 /*******************************************************************************
1814  *                             Escape Appending                                *
1815  *******************************************************************************/
1816
1817 /** 
1818  * @ingroup StrBuf_DeEnCoder
1819  * @brief Escape a string for feeding out as a URL while appending it to a Buffer
1820  * @param OutBuf the output buffer
1821  * @param In Buffer to encode
1822  * @param PlainIn way in from plain old c strings
1823  */
1824 void StrBufUrlescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
1825 {
1826         const char *pch, *pche;
1827         char *pt, *pte;
1828         int len;
1829         
1830         if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1831                 return;
1832         if (PlainIn != NULL) {
1833                 len = strlen(PlainIn);
1834                 pch = PlainIn;
1835                 pche = pch + len;
1836         }
1837         else {
1838                 pch = In->buf;
1839                 pche = pch + In->BufUsed;
1840                 len = In->BufUsed;
1841         }
1842
1843         if (len == 0) 
1844                 return;
1845
1846         pt = OutBuf->buf + OutBuf->BufUsed;
1847         pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1848
1849         while (pch < pche) {
1850                 if (pt >= pte) {
1851                         IncreaseBuf(OutBuf, 1, -1);
1852                         pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1853                         pt = OutBuf->buf + OutBuf->BufUsed;
1854                 }
1855
1856                 if((*pch >= 'a' && *pch <= 'z') ||
1857                    (*pch >= '@' && *pch <= 'Z') || /* @ A-Z */
1858                    (*pch >= '0' && *pch <= ':') || /* 0-9 : */
1859                    (*pch == '!') || (*pch == '_') || 
1860                    (*pch == ',') || (*pch == '.'))
1861                 {
1862                         *(pt++) = *(pch++);
1863                         OutBuf->BufUsed++;
1864                 }                       
1865                 else {
1866                         *pt = '%';
1867                         *(pt + 1) = HexList[(unsigned char)*pch][0];
1868                         *(pt + 2) = HexList[(unsigned char)*pch][1];
1869                         pt += 3;
1870                         OutBuf->BufUsed += 3;
1871                         pch ++;
1872                 }
1873         }
1874         *pt = '\0';
1875 }
1876
1877 /** 
1878  * @ingroup StrBuf_DeEnCoder
1879  * @brief Escape a string for feeding out as a the username/password part of an URL while appending it to a Buffer
1880  * @param OutBuf the output buffer
1881  * @param In Buffer to encode
1882  * @param PlainIn way in from plain old c strings
1883  */
1884 void StrBufUrlescUPAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
1885 {
1886         const char *pch, *pche;
1887         char *pt, *pte;
1888         int len;
1889         
1890         if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1891                 return;
1892         if (PlainIn != NULL) {
1893                 len = strlen(PlainIn);
1894                 pch = PlainIn;
1895                 pche = pch + len;
1896         }
1897         else {
1898                 pch = In->buf;
1899                 pche = pch + In->BufUsed;
1900                 len = In->BufUsed;
1901         }
1902
1903         if (len == 0) 
1904                 return;
1905
1906         pt = OutBuf->buf + OutBuf->BufUsed;
1907         pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1908
1909         while (pch < pche) {
1910                 if (pt >= pte) {
1911                         IncreaseBuf(OutBuf, 1, -1);
1912                         pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1913                         pt = OutBuf->buf + OutBuf->BufUsed;
1914                 }
1915
1916                 if((*pch >= 'a' && *pch <= 'z') ||
1917                    (*pch >= 'A' && *pch <= 'Z') || /* A-Z */
1918                    (*pch >= '0' && *pch <= ':') || /* 0-9 : */
1919                    (*pch == '!') || (*pch == '_') || 
1920                    (*pch == ',') || (*pch == '.'))
1921                 {
1922                         *(pt++) = *(pch++);
1923                         OutBuf->BufUsed++;
1924                 }                       
1925                 else {
1926                         *pt = '%';
1927                         *(pt + 1) = HexList[(unsigned char)*pch][0];
1928                         *(pt + 2) = HexList[(unsigned char)*pch][1];
1929                         pt += 3;
1930                         OutBuf->BufUsed += 3;
1931                         pch ++;
1932                 }
1933         }
1934         *pt = '\0';
1935 }
1936
1937 /** 
1938  * @ingroup StrBuf_DeEnCoder
1939  * @brief append a string in hex encoding to the buffer
1940  * @param OutBuf the output buffer
1941  * @param In Buffer to encode
1942  * @param PlainIn way in from plain old c strings
1943  * @param PlainInLen way in from plain old c strings; maybe you've got binary data or know the length?
1944  */
1945 void StrBufHexEscAppend(StrBuf *OutBuf, const StrBuf *In, const unsigned char *PlainIn, long PlainInLen)
1946 {
1947         const unsigned char *pch, *pche;
1948         char *pt, *pte;
1949         int len;
1950         
1951         if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1952                 return;
1953         if (PlainIn != NULL) {
1954                 if (PlainInLen < 0)
1955                         len = strlen((const char*)PlainIn);
1956                 else
1957                         len = PlainInLen;
1958                 pch = PlainIn;
1959                 pche = pch + len;
1960         }
1961         else {
1962                 pch = (const unsigned char*)In->buf;
1963                 pche = pch + In->BufUsed;
1964                 len = In->BufUsed;
1965         }
1966
1967         if (len == 0) 
1968                 return;
1969
1970         pt = OutBuf->buf + OutBuf->BufUsed;
1971         pte = OutBuf->buf + OutBuf->BufSize - 3; /**< we max append 3 chars at once plus the \0 */
1972
1973         while (pch < pche) {
1974                 if (pt >= pte) {
1975                         IncreaseBuf(OutBuf, 1, -1);
1976                         pte = OutBuf->buf + OutBuf->BufSize - 3; /**< we max append 3 chars at once plus the \0 */
1977                         pt = OutBuf->buf + OutBuf->BufUsed;
1978                 }
1979
1980                 *pt = HexList[*pch][0];
1981                 pt ++;
1982                 *pt = HexList[*pch][1];
1983                 pt ++; pch ++; OutBuf->BufUsed += 2;
1984         }
1985         *pt = '\0';
1986 }
1987
1988 void StrBufBase64Append(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn, long PlainInLen, int linebreaks)
1989 {
1990         const char *pch;
1991         char *pt;
1992         int len;
1993         long ExpectLen;
1994         
1995         if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1996                 return;
1997         if (PlainIn != NULL) {
1998                 if (PlainInLen < 0)
1999                         len = strlen(PlainIn);
2000                 else
2001                         len = PlainInLen;
2002                 pch = PlainIn;
2003         }
2004         else {
2005                 pch = In->buf;
2006                 len = In->BufUsed;
2007         }
2008
2009         if (len == 0) 
2010                 return;
2011
2012         ExpectLen = ((len * 134) / 100) + OutBuf->BufUsed;
2013
2014         if (ExpectLen > OutBuf->BufSize)
2015                 if (IncreaseBuf(OutBuf, 1, ExpectLen) < ExpectLen)
2016                         return;
2017
2018         pt = OutBuf->buf + OutBuf->BufUsed;
2019
2020         len = CtdlEncodeBase64(pt, pch, len, linebreaks);
2021
2022         pt += len;
2023         OutBuf->BufUsed += len;
2024         *pt = '\0';
2025 }
2026
2027 /** 
2028  * @ingroup StrBuf_DeEnCoder
2029  * @brief append a string in hex encoding to the buffer
2030  * @param OutBuf the output buffer
2031  * @param In Buffer to encode
2032  * @param PlainIn way in from plain old c strings
2033  */
2034 void StrBufHexescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
2035 {
2036         StrBufHexEscAppend(OutBuf, In, (const unsigned char*) PlainIn, -1);
2037 }
2038
2039 /**
2040  * @ingroup StrBuf_DeEnCoder
2041  * @brief Append a string, escaping characters which have meaning in HTML.  
2042  *
2043  * @param Target        target buffer
2044  * @param Source        source buffer; set to NULL if you just have a C-String
2045  * @param PlainIn       Plain-C string to append; set to NULL if unused
2046  * @param nbsp          If nonzero, spaces are converted to non-breaking spaces.
2047  * @param nolinebreaks  if set to 1, linebreaks are removed from the string.
2048  *                      if set to 2, linebreaks are replaced by &ltbr/&gt
2049  */
2050 long StrEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
2051 {
2052         const char *aptr, *eiptr;
2053         char *bptr, *eptr;
2054         long len;
2055
2056         if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2057                 return -1;
2058
2059         if (PlainIn != NULL) {
2060                 aptr = PlainIn;
2061                 len = strlen(PlainIn);
2062                 eiptr = aptr + len;
2063         }
2064         else {
2065                 aptr = Source->buf;
2066                 eiptr = aptr + Source->BufUsed;
2067                 len = Source->BufUsed;
2068         }
2069
2070         if (len == 0) 
2071                 return -1;
2072
2073         bptr = Target->buf + Target->BufUsed;
2074         eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in...  */
2075
2076         while (aptr < eiptr){
2077                 if(bptr >= eptr) {
2078                         IncreaseBuf(Target, 1, -1);
2079                         eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in...  */
2080                         bptr = Target->buf + Target->BufUsed;
2081                 }
2082                 if (*aptr == '<') {
2083                         memcpy(bptr, "&lt;", 4);
2084                         bptr += 4;
2085                         Target->BufUsed += 4;
2086                 }
2087                 else if (*aptr == '>') {
2088                         memcpy(bptr, "&gt;", 4);
2089                         bptr += 4;
2090                         Target->BufUsed += 4;
2091                 }
2092                 else if (*aptr == '&') {
2093                         memcpy(bptr, "&amp;", 5);
2094                         bptr += 5;
2095                         Target->BufUsed += 5;
2096                 }
2097                 else if (*aptr == '"') {
2098                         memcpy(bptr, "&quot;", 6);
2099                         bptr += 6;
2100                         Target->BufUsed += 6;
2101                 }
2102                 else if (*aptr == '\'') {
2103                         memcpy(bptr, "&#39;", 5);
2104                         bptr += 5;
2105                         Target->BufUsed += 5;
2106                 }
2107                 else if (*aptr == LB) {
2108                         *bptr = '<';
2109                         bptr ++;
2110                         Target->BufUsed ++;
2111                 }
2112                 else if (*aptr == RB) {
2113                         *bptr = '>';
2114                         bptr ++;
2115                         Target->BufUsed ++;
2116                 }
2117                 else if (*aptr == QU) {
2118                         *bptr ='"';
2119                         bptr ++;
2120                         Target->BufUsed ++;
2121                 }
2122                 else if ((*aptr == 32) && (nbsp == 1)) {
2123                         memcpy(bptr, "&nbsp;", 6);
2124                         bptr += 6;
2125                         Target->BufUsed += 6;
2126                 }
2127                 else if ((*aptr == '\n') && (nolinebreaks == 1)) {
2128                         *bptr='\0';     /* nothing */
2129                 }
2130                 else if ((*aptr == '\n') && (nolinebreaks == 2)) {
2131                         memcpy(bptr, "&lt;br/&gt;", 11);
2132                         bptr += 11;
2133                         Target->BufUsed += 11;
2134                 }
2135
2136
2137                 else if ((*aptr == '\r') && (nolinebreaks != 0)) {
2138                         *bptr='\0';     /* nothing */
2139                 }
2140                 else{
2141                         *bptr = *aptr;
2142                         bptr++;
2143                         Target->BufUsed ++;
2144                 }
2145                 aptr ++;
2146         }
2147         *bptr = '\0';
2148         if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
2149                 return -1;
2150         return Target->BufUsed;
2151 }
2152
2153 /**
2154  * @ingroup StrBuf_DeEnCoder
2155  * @brief Append a string, escaping characters which have meaning in HTML.  
2156  * Converts linebreaks into blanks; escapes single quotes
2157  * @param Target        target buffer
2158  * @param Source        source buffer; set to NULL if you just have a C-String
2159  * @param PlainIn       Plain-C string to append; set to NULL if unused
2160  */
2161 void StrMsgEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2162 {
2163         const char *aptr, *eiptr;
2164         char *tptr, *eptr;
2165         long len;
2166
2167         if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2168                 return ;
2169
2170         if (PlainIn != NULL) {
2171                 aptr = PlainIn;
2172                 len = strlen(PlainIn);
2173                 eiptr = aptr + len;
2174         }
2175         else {
2176                 aptr = Source->buf;
2177                 eiptr = aptr + Source->BufUsed;
2178                 len = Source->BufUsed;
2179         }
2180
2181         if (len == 0) 
2182                 return;
2183
2184         eptr = Target->buf + Target->BufSize - 8; 
2185         tptr = Target->buf + Target->BufUsed;
2186         
2187         while (aptr < eiptr){
2188                 if(tptr >= eptr) {
2189                         IncreaseBuf(Target, 1, -1);
2190                         eptr = Target->buf + Target->BufSize - 8; 
2191                         tptr = Target->buf + Target->BufUsed;
2192                 }
2193                
2194                 if (*aptr == '\n') {
2195                         *tptr = ' ';
2196                         Target->BufUsed++;
2197                 }
2198                 else if (*aptr == '\r') {
2199                         *tptr = ' ';
2200                         Target->BufUsed++;
2201                 }
2202                 else if (*aptr == '\'') {
2203                         *(tptr++) = '&';
2204                         *(tptr++) = '#';
2205                         *(tptr++) = '3';
2206                         *(tptr++) = '9';
2207                         *tptr = ';';
2208                         Target->BufUsed += 5;
2209                 } else {
2210                         *tptr = *aptr;
2211                         Target->BufUsed++;
2212                 }
2213                 tptr++; aptr++;
2214         }
2215         *tptr = '\0';
2216 }
2217
2218
2219
2220 /**
2221  * @ingroup StrBuf_DeEnCoder
2222  * @brief Append a string, escaping characters which have meaning in ICAL.  
2223  * [\n,] 
2224  * @param Target        target buffer
2225  * @param Source        source buffer; set to NULL if you just have a C-String
2226  * @param PlainIn       Plain-C string to append; set to NULL if unused
2227  */
2228 void StrIcalEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2229 {
2230         const char *aptr, *eiptr;
2231         char *tptr, *eptr;
2232         long len;
2233
2234         if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2235                 return ;
2236
2237         if (PlainIn != NULL) {
2238                 aptr = PlainIn;
2239                 len = strlen(PlainIn);
2240                 eiptr = aptr + len;
2241         }
2242         else {
2243                 aptr = Source->buf;
2244                 eiptr = aptr + Source->BufUsed;
2245                 len = Source->BufUsed;
2246         }
2247
2248         if (len == 0) 
2249                 return;
2250
2251         eptr = Target->buf + Target->BufSize - 8; 
2252         tptr = Target->buf + Target->BufUsed;
2253         
2254         while (aptr < eiptr){
2255                 if(tptr + 3 >= eptr) {
2256                         IncreaseBuf(Target, 1, -1);
2257                         eptr = Target->buf + Target->BufSize - 8; 
2258                         tptr = Target->buf + Target->BufUsed;
2259                 }
2260                
2261                 if (*aptr == '\n') {
2262                         *tptr = '\\';
2263                         Target->BufUsed++;
2264                         tptr++;
2265                         *tptr = 'n';
2266                         Target->BufUsed++;
2267                 }
2268                 else if (*aptr == '\r') {
2269                         *tptr = '\\';
2270                         Target->BufUsed++;
2271                         tptr++;
2272                         *tptr = 'r';
2273                         Target->BufUsed++;
2274                 }
2275                 else if (*aptr == ',') {
2276                         *tptr = '\\';
2277                         Target->BufUsed++;
2278                         tptr++;
2279                         *tptr = ',';
2280                         Target->BufUsed++;
2281                 } else {
2282                         *tptr = *aptr;
2283                         Target->BufUsed++;
2284                 }
2285                 tptr++; aptr++;
2286         }
2287         *tptr = '\0';
2288 }
2289
2290 /**
2291  * @ingroup StrBuf_DeEnCoder
2292  * @brief Append a string, escaping characters which have meaning in JavaScript strings .  
2293  *
2294  * @param Target        target buffer
2295  * @param Source        source buffer; set to NULL if you just have a C-String
2296  * @param PlainIn       Plain-C string to append; set to NULL if unused
2297  * @returns size of result or -1
2298  */
2299 long StrECMAEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2300 {
2301         const char *aptr, *eiptr;
2302         char *bptr, *eptr;
2303         long len;
2304         int IsUtf8Sequence;
2305
2306         if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2307                 return -1;
2308
2309         if (PlainIn != NULL) {
2310                 aptr = PlainIn;
2311                 len = strlen(PlainIn);
2312                 eiptr = aptr + len;
2313         }
2314         else {
2315                 aptr = Source->buf;
2316                 eiptr = aptr + Source->BufUsed;
2317                 len = Source->BufUsed;
2318         }
2319
2320         if (len == 0) 
2321                 return -1;
2322
2323         bptr = Target->buf + Target->BufUsed;
2324         eptr = Target->buf + Target->BufSize - 7; /* our biggest unit to put in...  */
2325
2326         while (aptr < eiptr){
2327                 if(bptr >= eptr) {
2328                         IncreaseBuf(Target, 1, -1);
2329                         eptr = Target->buf + Target->BufSize - 7; /* our biggest unit to put in...  */
2330                         bptr = Target->buf + Target->BufUsed;
2331                 }
2332                 switch (*aptr) {
2333                 case '\n':
2334                         memcpy(bptr, HKEY("\\n"));
2335                         bptr += 2;
2336                         Target->BufUsed += 2;                           
2337                         break;
2338                 case '\r':
2339                         memcpy(bptr, HKEY("\\r"));
2340                         bptr += 2;
2341                         Target->BufUsed += 2;
2342                         break;
2343                 case '"':
2344                         *bptr = '\\';
2345                         bptr ++;
2346                         *bptr = '"';
2347                         bptr ++;
2348                         Target->BufUsed += 2;
2349                         break;
2350                 case '\\':
2351                         if ((*(aptr + 1) == 'u') &&
2352                             isxdigit(*(aptr + 2)) &&
2353                             isxdigit(*(aptr + 3)) &&
2354                             isxdigit(*(aptr + 4)) &&
2355                             isxdigit(*(aptr + 5)))
2356                         { /* oh, a unicode escaper. let it pass through. */
2357                                 memcpy(bptr, aptr, 6);
2358                                 aptr += 5;
2359                                 bptr +=6;
2360                                 Target->BufUsed += 6;
2361                         }
2362                         else 
2363                         {
2364                                 *bptr = '\\';
2365                                 bptr ++;
2366                                 *bptr = '\\';
2367                                 bptr ++;
2368                                 Target->BufUsed += 2;
2369                         }
2370                         break;
2371                 case '\b':
2372                         *bptr = '\\';
2373                         bptr ++;
2374                         *bptr = 'b';
2375                         bptr ++;
2376                         Target->BufUsed += 2;
2377                         break;
2378                 case '\f':
2379                         *bptr = '\\';
2380                         bptr ++;
2381                         *bptr = 'f';
2382                         bptr ++;
2383                         Target->BufUsed += 2;
2384                         break;
2385                 case '\t':
2386                         *bptr = '\\';
2387                         bptr ++;
2388                         *bptr = 't';
2389                         bptr ++;
2390                         Target->BufUsed += 2;
2391                         break;
2392                 default:
2393                         IsUtf8Sequence =  Ctdl_GetUtf8SequenceLength(aptr, eiptr);
2394                         while (IsUtf8Sequence > 0){
2395                                 *bptr = *aptr;
2396                                 Target->BufUsed ++;
2397                                 if (--IsUtf8Sequence)
2398                                         aptr++;
2399                                 bptr++;
2400                         }
2401                 }
2402                 aptr ++;
2403         }
2404         *bptr = '\0';
2405         if ((bptr == eptr - 1 ) && !IsEmptyStr(aptr) )
2406                 return -1;
2407         return Target->BufUsed;
2408 }
2409
2410 /**
2411  * @ingroup StrBuf_DeEnCoder
2412  * @brief Append a string, escaping characters which have meaning in HTML + json.  
2413  *
2414  * @param Target        target buffer
2415  * @param Source        source buffer; set to NULL if you just have a C-String
2416  * @param PlainIn       Plain-C string to append; set to NULL if unused
2417  * @param nbsp          If nonzero, spaces are converted to non-breaking spaces.
2418  * @param nolinebreaks  if set to 1, linebreaks are removed from the string.
2419  *                      if set to 2, linebreaks are replaced by &ltbr/&gt
2420  */
2421 long StrHtmlEcmaEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
2422 {
2423         const char *aptr, *eiptr;
2424         char *bptr, *eptr;
2425         long len;
2426         int IsUtf8Sequence = 0;
2427
2428         if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2429                 return -1;
2430
2431         if (PlainIn != NULL) {
2432                 aptr = PlainIn;
2433                 len = strlen(PlainIn);
2434                 eiptr = aptr + len;
2435         }
2436         else {
2437                 aptr = Source->buf;
2438                 eiptr = aptr + Source->BufUsed;
2439                 len = Source->BufUsed;
2440         }
2441
2442         if (len == 0) 
2443                 return -1;
2444
2445         bptr = Target->buf + Target->BufUsed;
2446         eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in...  */
2447
2448         while (aptr < eiptr){
2449                 if(bptr >= eptr) {
2450                         IncreaseBuf(Target, 1, -1);
2451                         eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in...  */
2452                         bptr = Target->buf + Target->BufUsed;
2453                 }
2454                 switch (*aptr) {
2455                 case '<':
2456                         memcpy(bptr, HKEY("&lt;"));
2457                         bptr += 4;
2458                         Target->BufUsed += 4;
2459                         break;
2460                 case '>':
2461                         memcpy(bptr, HKEY("&gt;"));
2462                         bptr += 4;
2463                         Target->BufUsed += 4;
2464                         break;
2465                 case '&':
2466                         memcpy(bptr, HKEY("&amp;"));
2467                         bptr += 5;
2468                         Target->BufUsed += 5;
2469                         break;
2470                 case LB:
2471                         *bptr = '<';
2472                         bptr ++;
2473                         Target->BufUsed ++;
2474                         break;
2475                 case RB:
2476                         *bptr = '>';
2477                         bptr ++;
2478                         Target->BufUsed ++;
2479                         break;
2480                 case '\n':
2481                         switch (nolinebreaks) {
2482                         case 1:
2483                                 *bptr='\0';     /* nothing */
2484                                 break;
2485                         case 2:
2486                                 memcpy(bptr, HKEY("&lt;br/&gt;"));
2487                                 bptr += 11;
2488                                 Target->BufUsed += 11;
2489                                 break;
2490                         default:
2491                                 memcpy(bptr, HKEY("\\n"));
2492                                 bptr += 2;
2493                                 Target->BufUsed += 2;                           
2494                         }
2495                         break;
2496                 case '\r':
2497                         switch (nolinebreaks) {
2498                         case 1:
2499                         case 2:
2500                                 *bptr='\0';     /* nothing */
2501                                 break;
2502                         default:
2503                                 memcpy(bptr, HKEY("\\r"));
2504                                 bptr += 2;
2505                                 Target->BufUsed += 2;
2506                                 break;
2507                         }
2508                         break;
2509                 case '"':
2510                 case QU:
2511                         *bptr = '\\';
2512                         bptr ++;
2513                         *bptr = '"';
2514                         bptr ++;
2515                         Target->BufUsed += 2;
2516                         break;
2517                 case '\\':
2518                         if ((*(aptr + 1) == 'u') &&
2519                             isxdigit(*(aptr + 2)) &&
2520                             isxdigit(*(aptr + 3)) &&
2521                             isxdigit(*(aptr + 4)) &&
2522                             isxdigit(*(aptr + 5)))
2523                         { /* oh, a unicode escaper. let it pass through. */
2524                                 memcpy(bptr, aptr, 6);
2525                                 aptr += 5;
2526                                 bptr +=6;
2527                                 Target->BufUsed += 6;
2528                         }
2529                         else 
2530                         {
2531                                 *bptr = '\\';
2532                                 bptr ++;
2533                                 *bptr = '\\';
2534                                 bptr ++;
2535                                 Target->BufUsed += 2;
2536                         }
2537                         break;
2538                 case '\b':
2539                         *bptr = '\\';
2540                         bptr ++;
2541                         *bptr = 'b';
2542                         bptr ++;
2543                         Target->BufUsed += 2;
2544                         break;
2545                 case '\f':
2546                         *bptr = '\\';
2547                         bptr ++;
2548                         *bptr = 'f';
2549                         bptr ++;
2550                         Target->BufUsed += 2;
2551                         break;
2552                 case '\t':
2553                         *bptr = '\\';
2554                         bptr ++;
2555                         *bptr = 't';
2556                         bptr ++;
2557                         Target->BufUsed += 2;
2558                         break;
2559                 case  32:
2560                         if (nbsp == 1) {
2561                                 memcpy(bptr, HKEY("&nbsp;"));
2562                                 bptr += 6;
2563                                 Target->BufUsed += 6;
2564                                 break;
2565                         }
2566                 default:
2567                         IsUtf8Sequence =  Ctdl_GetUtf8SequenceLength(aptr, eiptr);
2568                         while (IsUtf8Sequence > 0){
2569                                 *bptr = *aptr;
2570                                 Target->BufUsed ++;
2571                                 if (--IsUtf8Sequence)
2572                                         aptr++;
2573                                 bptr++;
2574                         }
2575                 }
2576                 aptr ++;
2577         }
2578         *bptr = '\0';
2579         if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
2580                 return -1;
2581         return Target->BufUsed;
2582 }
2583
2584
2585 /**
2586  * @ingroup StrBuf_DeEnCoder
2587  * @brief replace all non-Ascii characters by another
2588  * @param Buf buffer to inspect
2589  * @param repl charater to stamp over non ascii chars
2590  */
2591 void StrBufAsciify(StrBuf *Buf, const char repl)
2592 {
2593         long offset;
2594
2595         for (offset = 0; offset < Buf->BufUsed; offset ++)
2596                 if (!isascii(Buf->buf[offset]))
2597                         Buf->buf[offset] = repl;
2598         
2599 }
2600
2601 /**
2602  * @ingroup StrBuf_DeEnCoder
2603  * @brief unhide special chars hidden to the HTML escaper
2604  * @param target buffer to put the unescaped string in
2605  * @param source buffer to unescape
2606  */
2607 void StrBufEUid_unescapize(StrBuf *target, const StrBuf *source) 
2608 {
2609         int a, b, len;
2610         char hex[3];
2611
2612         if ((source == NULL) || (target == NULL) || (target->buf == NULL))
2613         {
2614                 return;
2615         }
2616
2617         if (target != NULL)
2618                 FlushStrBuf(target);
2619
2620         len = source->BufUsed;
2621         for (a = 0; a < len; ++a) {
2622                 if (target->BufUsed >= target->BufSize)
2623                         IncreaseBuf(target, 1, -1);
2624
2625                 if (source->buf[a] == '=') {
2626                         hex[0] = source->buf[a + 1];
2627                         hex[1] = source->buf[a + 2];
2628                         hex[2] = 0;
2629                         b = 0;
2630                         sscanf(hex, "%02x", &b);
2631                         target->buf[target->BufUsed] = b;
2632                         target->buf[++target->BufUsed] = 0;
2633                         a += 2;
2634                 }
2635                 else {
2636                         target->buf[target->BufUsed] = source->buf[a];
2637                         target->buf[++target->BufUsed] = 0;
2638                 }
2639         }
2640 }
2641
2642
2643 /**
2644  * @ingroup StrBuf_DeEnCoder
2645  * @brief hide special chars from the HTML escapers and friends
2646  * @param target buffer to put the escaped string in
2647  * @param source buffer to escape
2648  */
2649 void StrBufEUid_escapize(StrBuf *target, const StrBuf *source) 
2650 {
2651         int i, len;
2652
2653         if (target != NULL)
2654                 FlushStrBuf(target);
2655
2656         if ((source == NULL) || (target == NULL) || (target->buf == NULL))
2657         {
2658                 return;
2659         }
2660
2661         len = source->BufUsed;
2662         for (i=0; i<len; ++i) {
2663                 if (target->BufUsed + 4 >= target->BufSize)
2664                         IncreaseBuf(target, 1, -1);
2665                 if ( (isalnum(source->buf[i])) || 
2666                      (source->buf[i]=='-') || 
2667                      (source->buf[i]=='_') ) {
2668                         target->buf[target->BufUsed++] = source->buf[i];
2669                 }
2670                 else {
2671                         sprintf(&target->buf[target->BufUsed], 
2672                                 "=%02X", 
2673                                 (0xFF &source->buf[i]));
2674                         target->BufUsed += 3;
2675                 }
2676         }
2677         target->buf[target->BufUsed + 1] = '\0';
2678 }
2679
2680
2681 /*******************************************************************************
2682  *                      Quoted Printable de/encoding                           *
2683  *******************************************************************************/
2684
2685 /**
2686  * @ingroup StrBuf_DeEnCoder
2687  * @brief decode a buffer from base 64 encoding; destroys original
2688  * @param Buf Buffor to transform
2689  */
2690 int StrBufDecodeBase64(StrBuf *Buf)
2691 {
2692         char *xferbuf;
2693         size_t siz;
2694
2695         if (Buf == NULL)
2696                 return -1;
2697
2698         xferbuf = (char*) malloc(Buf->BufSize);
2699         if (xferbuf == NULL)
2700                 return -1;
2701
2702         *xferbuf = '\0';
2703         siz = CtdlDecodeBase64(xferbuf,
2704                                Buf->buf,
2705                                Buf->BufUsed);
2706         free(Buf->buf);
2707         Buf->buf = xferbuf;
2708         Buf->BufUsed = siz;
2709         return siz;
2710 }
2711
2712 /**
2713  * @ingroup StrBuf_DeEnCoder
2714  * @brief decode a buffer from base 64 encoding; destroys original
2715  * @param Buf Buffor to transform
2716  */
2717 int StrBufDecodeHex(StrBuf *Buf)
2718 {
2719         unsigned int ch;
2720         char *pch, *pche, *pchi;
2721
2722         if (Buf == NULL) return -1;
2723
2724         pch = pchi = Buf->buf;
2725         pche = pch + Buf->BufUsed;
2726
2727         while (pchi < pche){
2728                 ch = decode_hex(pchi);
2729                 *pch = ch;
2730                 pch ++;
2731                 pchi += 2;
2732         }
2733
2734         *pch = '\0';
2735         Buf->BufUsed = pch - Buf->buf;
2736         return Buf->BufUsed;
2737 }
2738
2739 /**
2740  * @ingroup StrBuf_DeEnCoder
2741  * @brief replace all chars >0x20 && < 0x7F with Mute
2742  * @param Mute char to put over invalid chars
2743  * @param Buf Buffor to transform
2744  */
2745 int StrBufSanitizeAscii(StrBuf *Buf, const char Mute)
2746 {
2747         unsigned char *pch;
2748
2749         if (Buf == NULL) return -1;
2750         pch = (unsigned char *)Buf->buf;
2751         while (pch < (unsigned char *)Buf->buf + Buf->BufUsed) {
2752                 if ((*pch < 0x20) || (*pch > 0x7F))
2753                         *pch = Mute;
2754                 pch ++;
2755         }
2756         return Buf->BufUsed;
2757 }
2758
2759
2760 /**
2761  * @ingroup StrBuf_DeEnCoder
2762  * @brief remove escaped strings from i.e. the url string (like %20 for blanks)
2763  * @param Buf Buffer to translate
2764  * @param StripBlanks Reduce several blanks to one?
2765  */
2766 long StrBufUnescape(StrBuf *Buf, int StripBlanks)
2767 {
2768         int a, b;
2769         char hex[3];
2770         long len;
2771
2772         if (Buf == NULL)
2773                 return -1;
2774
2775         while ((Buf->BufUsed > 0) && (isspace(Buf->buf[Buf->BufUsed - 1]))){
2776                 Buf->buf[Buf->BufUsed - 1] = '\0';
2777                 Buf->BufUsed --;
2778         }
2779
2780         a = 0; 
2781         while (a < Buf->BufUsed) {
2782                 if (Buf->buf[a] == '+')
2783                         Buf->buf[a] = ' ';
2784                 else if (Buf->buf[a] == '%') {
2785                         /* don't let % chars through, rather truncate the input. */
2786                         if (a + 2 > Buf->BufUsed) {
2787                                 Buf->buf[a] = '\0';
2788                                 Buf->BufUsed = a;
2789                         }
2790                         else {                  
2791                                 hex[0] = Buf->buf[a + 1];
2792                                 hex[1] = Buf->buf[a + 2];
2793                                 hex[2] = 0;
2794                                 b = 0;
2795                                 sscanf(hex, "%02x", &b);
2796                                 Buf->buf[a] = (char) b;
2797                                 len = Buf->BufUsed - a - 2;
2798                                 if (len > 0)
2799                                         memmove(&Buf->buf[a + 1], &Buf->buf[a + 3], len);
2800                         
2801                                 Buf->BufUsed -=2;
2802                         }
2803                 }
2804                 a++;
2805         }
2806         return a;
2807 }
2808
2809
2810 /**
2811  * @ingroup StrBuf_DeEnCoder
2812  * @brief       RFC2047-encode a header field if necessary.
2813  *              If no non-ASCII characters are found, the string
2814  *              will be copied verbatim without encoding.
2815  *
2816  * @param       target          Target buffer.
2817  * @param       source          Source string to be encoded.
2818  * @returns     encoded length; -1 if non success.
2819  */
2820 int StrBufRFC2047encode(StrBuf **target, const StrBuf *source)
2821 {
2822         const char headerStr[] = "=?UTF-8?Q?";
2823         int need_to_encode = 0;
2824         int i = 0;
2825         unsigned char ch;
2826
2827         if ((source == NULL) || 
2828             (target == NULL))
2829             return -1;
2830
2831         while ((i < source->BufUsed) &&
2832                (!IsEmptyStr (&source->buf[i])) &&
2833                (need_to_encode == 0)) {
2834                 if (((unsigned char) source->buf[i] < 32) || 
2835                     ((unsigned char) source->buf[i] > 126)) {
2836                         need_to_encode = 1;
2837                 }
2838                 i++;
2839         }
2840
2841         if (!need_to_encode) {
2842                 if (*target == NULL) {
2843                         *target = NewStrBufPlain(source->buf, source->BufUsed);
2844                 }
2845                 else {
2846                         FlushStrBuf(*target);
2847                         StrBufAppendBuf(*target, source, 0);
2848                 }
2849                 if (*target != 0)
2850                         return (*target)->BufUsed;
2851                 else
2852                         return 0;
2853         }
2854         if (*target == NULL)
2855                 *target = NewStrBufPlain(NULL, sizeof(headerStr) + source->BufUsed * 2);
2856         else if (sizeof(headerStr) + source->BufUsed >= (*target)->BufSize)
2857                 IncreaseBuf(*target, sizeof(headerStr) + source->BufUsed, 0);
2858         memcpy ((*target)->buf, headerStr, sizeof(headerStr) - 1);
2859         (*target)->BufUsed = sizeof(headerStr) - 1;
2860         for (i=0; (i < source->BufUsed); ++i) {
2861                 if ((*target)->BufUsed + 4 >= (*target)->BufSize)
2862                         IncreaseBuf(*target, 1, 0);
2863                 ch = (unsigned char) source->buf[i];
2864                 if ((ch  <  32) || 
2865                     (ch  > 126) || 
2866                     (ch ==  61) ||
2867                     (ch == '=') ||
2868                     (ch == '?') ||
2869                     (ch == '_') ||
2870                     (ch == '[') ||
2871                     (ch == ']')   )
2872                 {
2873                         sprintf(&(*target)->buf[(*target)->BufUsed], "=%02X", ch);
2874                         (*target)->BufUsed += 3;
2875                 }
2876                 else {
2877                         if (ch == ' ')
2878                                 (*target)->buf[(*target)->BufUsed] = '_';
2879                         else
2880                                 (*target)->buf[(*target)->BufUsed] = ch;
2881                         (*target)->BufUsed++;
2882                 }
2883         }
2884         
2885         if ((*target)->BufUsed + 4 >= (*target)->BufSize)
2886                 IncreaseBuf(*target, 1, 0);
2887
2888         (*target)->buf[(*target)->BufUsed++] = '?';
2889         (*target)->buf[(*target)->BufUsed++] = '=';
2890         (*target)->buf[(*target)->BufUsed] = '\0';
2891         return (*target)->BufUsed;;
2892 }
2893
2894
2895
2896 static void AddRecipient(StrBuf *Target, 
2897                          StrBuf *UserName, 
2898                          StrBuf *EmailAddress, 
2899                          StrBuf *EncBuf)
2900 {
2901         int QuoteMe = 0;
2902
2903         if (StrLength(Target) > 0) StrBufAppendBufPlain(Target, HKEY(", "), 0);
2904         if (strchr(ChrPtr(UserName), ',') != NULL) QuoteMe = 1;
2905
2906         if (QuoteMe)  StrBufAppendBufPlain(Target, HKEY("\""), 0);
2907         StrBufRFC2047encode(&EncBuf, UserName);
2908         StrBufAppendBuf(Target, EncBuf, 0);
2909         if (QuoteMe)  StrBufAppendBufPlain(Target, HKEY("\" "), 0);
2910         else          StrBufAppendBufPlain(Target, HKEY(" "), 0);
2911
2912         if (StrLength(EmailAddress) > 0){
2913                 StrBufAppendBufPlain(Target, HKEY("<"), 0);
2914                 StrBufAppendBuf(Target, EmailAddress, 0); /* TODO: what about IDN???? */
2915                 StrBufAppendBufPlain(Target, HKEY(">"), 0);
2916         }
2917 }
2918
2919
2920 /**
2921  * \brief QP encode parts of an email TO/CC/BCC vector, and strip/filter invalid parts
2922  * \param Recp Source list of email recipients
2923  * \param UserName Temporary buffer for internal use; Please provide valid buffer.
2924  * \param EmailAddress Temporary buffer for internal use; Please provide valid buffer.
2925  * \param EncBuf Temporary buffer for internal use; Please provide valid buffer.
2926  * \returns encoded & sanitized buffer with the contents of Recp; Caller owns this memory.
2927  */
2928 StrBuf *StrBufSanitizeEmailRecipientVector(const StrBuf *Recp, 
2929                                            StrBuf *UserName, 
2930                                            StrBuf *EmailAddress,
2931                                            StrBuf *EncBuf)
2932 {
2933         StrBuf *Target;
2934         const char *pch, *pche;
2935         const char *UserStart, *UserEnd, *EmailStart, *EmailEnd, *At;
2936
2937         if ((Recp == NULL) || (StrLength(Recp) == 0))
2938                 return NULL;
2939
2940         pch = ChrPtr(Recp);
2941         pche = pch + StrLength(Recp);
2942
2943         if (!CheckEncode(pch, -1, pche))
2944                 return NewStrBufDup(Recp);
2945
2946         Target = NewStrBufPlain(NULL, StrLength(Recp));
2947
2948         while ((pch != NULL) && (pch < pche))
2949         {
2950                 while (isspace(*pch)) pch++;
2951                 UserEnd = EmailStart = EmailEnd = NULL;
2952                 
2953                 if ((*pch == '"') || (*pch == '\'')) {
2954                         UserStart = pch + 1;
2955                         
2956                         UserEnd = strchr(UserStart, *pch);
2957                         if (UserEnd == NULL) 
2958                                 break; ///TODO: Userfeedback??
2959                         EmailStart = UserEnd + 1;
2960                         while (isspace(*EmailStart))
2961                                 EmailStart++;
2962                         if (UserEnd == UserStart) {
2963                                 UserStart = UserEnd = NULL;
2964                         }
2965                         
2966                         if (*EmailStart == '<') {
2967                                 EmailStart++;
2968                                 EmailEnd = strchr(EmailStart, '>');
2969                                 if (EmailEnd == NULL)
2970                                         EmailEnd = strchr(EmailStart, ',');
2971                                 
2972                         }
2973                         else {
2974                                 EmailEnd = strchr(EmailStart, ',');
2975                         }
2976                         if (EmailEnd == NULL)
2977                                 EmailEnd = pche;
2978                         pch = EmailEnd + 1;
2979                 }
2980                 else {
2981                         int gt = 0;
2982                         UserStart = pch;
2983                         EmailEnd = strchr(UserStart, ',');
2984                         if (EmailEnd == NULL) {
2985                                 EmailEnd = strchr(pch, '>');
2986                                 pch = NULL;
2987                                 if (EmailEnd != NULL) {
2988                                         gt = 1;
2989                                 }
2990                                 else {
2991                                         EmailEnd = pche;
2992                                 }
2993                         }
2994                         else {
2995
2996                                 pch = EmailEnd + 1;
2997                                 while ((EmailEnd > UserStart) && !gt &&
2998                                        ((*EmailEnd == ',') ||
2999                                         (*EmailEnd == '>') ||
3000                                         (isspace(*EmailEnd))))
3001                                 {
3002                                         if (*EmailEnd == '>')
3003                                                 gt = 1;
3004                                         else 
3005                                                 EmailEnd--;
3006                                 }
3007                                 if (EmailEnd == UserStart)
3008                                         break;
3009                         }
3010                         if (gt) {
3011                                 EmailStart = strchr(UserStart, '<');
3012                                 if ((EmailStart == NULL) || (EmailStart > EmailEnd))
3013                                         break;
3014                                 UserEnd = EmailStart;
3015
3016                                 while ((UserEnd > UserStart) && 
3017                                        isspace (*(UserEnd - 1)))
3018                                         UserEnd --;
3019                                 EmailStart ++;
3020                                 if (UserStart >= UserEnd)
3021                                         UserStart = UserEnd = NULL;
3022                         }
3023                         else { /* this is a local recipient... no domain, just a realname */
3024                                 EmailStart = UserStart;
3025                                 At = strchr(EmailStart, '@');
3026                                 if (At == NULL) {
3027                                         UserEnd = EmailEnd;
3028                                         EmailEnd = NULL;
3029                                 }
3030                                 else {
3031                                         EmailStart = UserStart;
3032                                         UserStart = NULL;
3033                                 }
3034                         }
3035                 }
3036
3037                 if ((UserStart != NULL) && (UserEnd != NULL))
3038                         StrBufPlain(UserName, UserStart, UserEnd - UserStart);
3039                 else if ((UserStart != NULL) && (UserEnd == NULL))
3040                         StrBufPlain(UserName, UserStart, UserEnd - UserStart);
3041                 else
3042                         FlushStrBuf(UserName);
3043
3044                 if ((EmailStart != NULL) && (EmailEnd != NULL))
3045                         StrBufPlain(EmailAddress, EmailStart, EmailEnd - EmailStart);
3046                 else if ((EmailStart != NULL) && (EmailEnd == NULL))
3047                         StrBufPlain(EmailAddress, EmailStart, EmailEnd - pche);
3048                 else 
3049                         FlushStrBuf(EmailAddress);
3050
3051                 AddRecipient(Target, UserName, EmailAddress, EncBuf);
3052
3053                 if (pch == NULL)
3054                         break;
3055                 
3056                 if ((pch != NULL) && (*pch == ','))
3057                         pch ++;
3058                 if (pch != NULL) while (isspace(*pch))
3059                         pch ++;
3060         }
3061         return Target;
3062 }
3063
3064
3065 /**
3066  * @ingroup StrBuf
3067  * @brief replaces all occurances of 'search' by 'replace'
3068  * @param buf Buffer to modify
3069  * @param search character to search
3070  * @param replace character to replace search by
3071  */
3072 void StrBufReplaceChars(StrBuf *buf, char search, char replace)
3073 {
3074         long i;
3075         if (buf == NULL)
3076                 return;
3077         for (i=0; i<buf->BufUsed; i++)
3078                 if (buf->buf[i] == search)
3079                         buf->buf[i] = replace;
3080
3081 }
3082
3083 /**
3084  * @ingroup StrBuf
3085  * @brief removes all \\r s from the string, or replaces them with \n if its not a combination of both.
3086  * @param buf Buffer to modify
3087  */
3088 void StrBufToUnixLF(StrBuf *buf)
3089 {
3090         char *pche, *pchS, *pchT;
3091         if (buf == NULL)
3092                 return;
3093
3094         pche = buf->buf + buf->BufUsed;
3095         pchS = pchT = buf->buf;
3096         while (pchS < pche)
3097         {
3098                 if (*pchS == '\r')
3099                 {
3100                         pchS ++;
3101                         if (*pchS != '\n') {
3102                                 *pchT = '\n';
3103                                 pchT++;
3104                         }
3105                 }
3106                 *pchT = *pchS;
3107                 pchT++; pchS++;
3108         }
3109         *pchT = '\0';
3110         buf->BufUsed = pchT - buf->buf;
3111 }
3112
3113
3114 /*******************************************************************************
3115  *                 Iconv Wrapper; RFC822 de/encoding                           *
3116  *******************************************************************************/
3117
3118 /**
3119  * @ingroup StrBuf_DeEnCoder
3120  * @brief Wrapper around iconv_open()
3121  * Our version adds aliases for non-standard Microsoft charsets
3122  * such as 'MS950', aliasing them to names like 'CP950'
3123  *
3124  * @param tocode        Target encoding
3125  * @param fromcode      Source encoding
3126  * @param pic           anonimized pointer to iconv struct
3127  */
3128 void  ctdl_iconv_open(const char *tocode, const char *fromcode, void *pic)
3129 {
3130 #ifdef HAVE_ICONV
3131         iconv_t ic = (iconv_t)(-1) ;
3132         ic = iconv_open(tocode, fromcode);
3133         if (ic == (iconv_t)(-1) ) {
3134                 char alias_fromcode[64];
3135                 if ( (strlen(fromcode) == 5) && (!strncasecmp(fromcode, "MS", 2)) ) {
3136                         safestrncpy(alias_fromcode, fromcode, sizeof alias_fromcode);
3137                         alias_fromcode[0] = 'C';
3138                         alias_fromcode[1] = 'P';
3139                         ic = iconv_open(tocode, alias_fromcode);
3140                 }
3141         }
3142         *(iconv_t *)pic = ic;
3143 #endif
3144 }
3145
3146
3147 /**
3148  * @ingroup StrBuf_DeEnCoder
3149  * @brief find one chunk of a RFC822 encoded string
3150  * @param Buffer where to search
3151  * @param bptr where to start searching
3152  * @returns found position, NULL if none.
3153  */
3154 static inline const char *FindNextEnd (const StrBuf *Buf, const char *bptr)
3155 {
3156         const char * end;
3157         /* Find the next ?Q? */
3158         if (Buf->BufUsed - (bptr - Buf->buf)  < 6)
3159                 return NULL;
3160
3161         end = strchr(bptr + 2, '?');
3162
3163         if (end == NULL)
3164                 return NULL;
3165
3166         if ((Buf->BufUsed - (end - Buf->buf) > 3) &&
3167             (((*(end + 1) == 'B') || (*(end + 1) == 'Q')) ||
3168              ((*(end + 1) == 'b') || (*(end + 1) == 'q'))) && 
3169             (*(end + 2) == '?')) {
3170                 /* skip on to the end of the cluster, the next ?= */
3171                 end = strstr(end + 3, "?=");
3172         }
3173         else
3174                 /* sort of half valid encoding, try to find an end. */
3175                 end = strstr(bptr, "?=");
3176         return end;
3177 }
3178
3179
3180
3181 /**
3182  * @ingroup StrBuf_DeEnCoder
3183  * @brief convert one buffer according to the preselected iconv pointer PIC
3184  * @param ConvertBuf buffer we need to translate
3185  * @param TmpBuf To share a workbuffer over several iterations. prepare to have it filled with useless stuff afterwards.
3186  * @param pic Pointer to the iconv-session Object
3187  */
3188 void StrBufConvert(StrBuf *ConvertBuf, StrBuf *TmpBuf, void *pic)
3189 {
3190 #ifdef HAVE_ICONV
3191         long trycount = 0;
3192         size_t siz;
3193         iconv_t ic;
3194         char *ibuf;                     /**< Buffer of characters to be converted */
3195         char *obuf;                     /**< Buffer for converted characters */
3196         size_t ibuflen;                 /**< Length of input buffer */
3197         size_t obuflen;                 /**< Length of output buffer */
3198
3199
3200         if ((ConvertBuf == NULL) || (TmpBuf == NULL))
3201                 return;
3202
3203         /* since we're converting to utf-8, one glyph may take up to 6 bytes */
3204         if (ConvertBuf->BufUsed * 6 >= TmpBuf->BufSize)
3205                 IncreaseBuf(TmpBuf, 0, ConvertBuf->BufUsed * 6);
3206 TRYAGAIN:
3207         ic = *(iconv_t*)pic;
3208         ibuf = ConvertBuf->buf;
3209         ibuflen = ConvertBuf->BufUsed;
3210         obuf = TmpBuf->buf;
3211         obuflen = TmpBuf->BufSize;
3212         
3213         siz = iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
3214
3215         if (siz < 0) {
3216                 if (errno == E2BIG) {
3217                         trycount ++;                    
3218                         IncreaseBuf(TmpBuf, 0, 0);
3219                         if (trycount < 5) 
3220                                 goto TRYAGAIN;
3221
3222                 }
3223                 else if (errno == EILSEQ){ 
3224                         /* hm, invalid utf8 sequence... what to do now? */
3225                         /* An invalid multibyte sequence has been encountered in the input */
3226                 }
3227                 else if (errno == EINVAL) {
3228                         /* An incomplete multibyte sequence has been encountered in the input. */
3229                 }
3230
3231                 FlushStrBuf(TmpBuf);
3232         }
3233         else {
3234                 TmpBuf->BufUsed = TmpBuf->BufSize - obuflen;
3235                 TmpBuf->buf[TmpBuf->BufUsed] = '\0';
3236                 
3237                 /* little card game: wheres the red lady? */
3238                 SwapBuffers(ConvertBuf, TmpBuf);
3239                 FlushStrBuf(TmpBuf);
3240         }
3241 #endif
3242 }
3243
3244
3245 /**
3246  * @ingroup StrBuf_DeEnCoder
3247  * @brief catches one RFC822 encoded segment, and decodes it.
3248  * @param Target buffer to fill with result
3249  * @param DecodeMe buffer with stuff to process
3250  * @param SegmentStart points to our current segment in DecodeMe
3251  * @param SegmentEnd Points to the end of our current segment in DecodeMe
3252  * @param ConvertBuf Workbuffer shared between several iterations. Random content; needs to be valid
3253  * @param ConvertBuf2 Workbuffer shared between several iterations. Random content; needs to be valid
3254  * @param FoundCharset Characterset to default decoding to; if we find another we will overwrite it.
3255  */
3256 inline static void DecodeSegment(StrBuf *Target, 
3257                                  const StrBuf *DecodeMe, 
3258                                  const char *SegmentStart, 
3259                                  const char *SegmentEnd, 
3260                                  StrBuf *ConvertBuf,
3261                                  StrBuf *ConvertBuf2, 
3262                                  StrBuf *FoundCharset)
3263 {
3264         StrBuf StaticBuf;
3265         char charset[128];
3266         char encoding[16];
3267 #ifdef HAVE_ICONV
3268         iconv_t ic = (iconv_t)(-1);
3269 #else
3270         void *ic = NULL;
3271 #endif
3272         /* Now we handle foreign character sets properly encoded
3273          * in RFC2047 format.
3274          */
3275         StaticBuf.buf = (char*) SegmentStart; /*< it will just be read there... */
3276         StaticBuf.BufUsed = SegmentEnd - SegmentStart;
3277         StaticBuf.BufSize = DecodeMe->BufSize - (SegmentStart - DecodeMe->buf);
3278         extract_token(charset, SegmentStart, 1, '?', sizeof charset);
3279         if (FoundCharset != NULL) {
3280                 FlushStrBuf(FoundCharset);
3281                 StrBufAppendBufPlain(FoundCharset, charset, -1, 0);
3282         }
3283         extract_token(encoding, SegmentStart, 2, '?', sizeof encoding);
3284         StrBufExtract_token(ConvertBuf, &StaticBuf, 3, '?');
3285         
3286         *encoding = toupper(*encoding);
3287         if (*encoding == 'B') { /**< base64 */
3288                 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3289                         IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3290                 ConvertBuf2->BufUsed = CtdlDecodeBase64(ConvertBuf2->buf, 
3291                                                         ConvertBuf->buf, 
3292                                                         ConvertBuf->BufUsed);
3293         }
3294         else if (*encoding == 'Q') {    /**< quoted-printable */
3295                 long pos;
3296                 
3297                 pos = 0;
3298                 while (pos < ConvertBuf->BufUsed)
3299                 {
3300                         if (ConvertBuf->buf[pos] == '_') 
3301                                 ConvertBuf->buf[pos] = ' ';
3302                         pos++;
3303                 }
3304                 
3305                 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3306                         IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3307
3308                 ConvertBuf2->BufUsed = CtdlDecodeQuotedPrintable(
3309                         ConvertBuf2->buf, 
3310                         ConvertBuf->buf,
3311                         ConvertBuf->BufUsed);
3312         }
3313         else {
3314                 StrBufAppendBuf(ConvertBuf2, ConvertBuf, 0);
3315         }
3316 #ifdef HAVE_ICONV
3317         ctdl_iconv_open("UTF-8", charset, &ic);
3318         if (ic != (iconv_t)(-1) ) {             
3319 #endif
3320                 StrBufConvert(ConvertBuf2, ConvertBuf, &ic);
3321                 StrBufAppendBuf(Target, ConvertBuf2, 0);
3322 #ifdef HAVE_ICONV
3323                 iconv_close(ic);
3324         }
3325         else {
3326                 StrBufAppendBufPlain(Target, HKEY("(unreadable)"), 0);
3327         }
3328 #endif
3329 }
3330
3331 /**
3332  * @ingroup StrBuf_DeEnCoder
3333  * @brief Handle subjects with RFC2047 encoding such as: [deprecated old syntax!]
3334  * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3335  * @param Target where to put the decoded string to 
3336  * @param DecodeMe buffer with encoded string
3337  * @param DefaultCharset if we don't find one, which should we use?
3338  * @param FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string, 
3339  *        put it here for later use where no string might be known.
3340  */
3341 void StrBuf_RFC822_to_Utf8(StrBuf *Target, const StrBuf *DecodeMe, const StrBuf* DefaultCharset, StrBuf *FoundCharset)
3342 {
3343         StrBuf *ConvertBuf;
3344         StrBuf *ConvertBuf2;
3345         ConvertBuf = NewStrBufPlain(NULL, StrLength(DecodeMe));
3346         ConvertBuf2 = NewStrBufPlain(NULL, StrLength(DecodeMe));
3347         
3348         StrBuf_RFC822_2_Utf8(Target, 
3349                              DecodeMe, 
3350                              DefaultCharset, 
3351                              FoundCharset, 
3352                              ConvertBuf, 
3353                              ConvertBuf2);
3354         FreeStrBuf(&ConvertBuf);
3355         FreeStrBuf(&ConvertBuf2);
3356 }
3357
3358 /**
3359  * @ingroup StrBuf_DeEnCoder
3360  * @brief Handle subjects with RFC2047 encoding such as:
3361  * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3362  * @param Target where to put the decoded string to 
3363  * @param DecodeMe buffer with encoded string
3364  * @param DefaultCharset if we don't find one, which should we use?
3365  * @param FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string, 
3366  *        put it here for later use where no string might be known.
3367  * @param ConvertBuf workbuffer. feed in, you shouldn't care about its content.
3368  * @param ConvertBuf2 workbuffer. feed in, you shouldn't care about its content.
3369  */
3370 void StrBuf_RFC822_2_Utf8(StrBuf *Target, 
3371                           const StrBuf *DecodeMe, 
3372                           const StrBuf* DefaultCharset, 
3373                           StrBuf *FoundCharset, 
3374                           StrBuf *ConvertBuf, 
3375                           StrBuf *ConvertBuf2)
3376 {
3377         StrBuf *DecodedInvalidBuf = NULL;
3378         const StrBuf *DecodeMee = DecodeMe;
3379         const char *start, *end, *next, *nextend, *ptr = NULL;
3380 #ifdef HAVE_ICONV
3381         iconv_t ic = (iconv_t)(-1) ;
3382 #endif
3383         const char *eptr;
3384         int passes = 0;
3385         int i;
3386         int illegal_non_rfc2047_encoding = 0;
3387
3388
3389         if (DecodeMe == NULL)
3390                 return;
3391         /* Sometimes, badly formed messages contain strings which were simply
3392          *  written out directly in some foreign character set instead of
3393          *  using RFC2047 encoding.  This is illegal but we will attempt to
3394          *  handle it anyway by converting from a user-specified default
3395          *  charset to UTF-8 if we see any nonprintable characters.
3396          */
3397         
3398         for (i=0; i<DecodeMe->BufUsed; ++i) {
3399                 if ((DecodeMe->buf[i] < 32) || (DecodeMe->buf[i] > 126)) {
3400                         illegal_non_rfc2047_encoding = 1;
3401                         break;
3402                 }
3403         }
3404
3405         if ((illegal_non_rfc2047_encoding) &&
3406             (strcasecmp(ChrPtr(DefaultCharset), "UTF-8")) && 
3407             (strcasecmp(ChrPtr(DefaultCharset), "us-ascii")) )
3408         {
3409 #ifdef HAVE_ICONV
3410                 ctdl_iconv_open("UTF-8", ChrPtr(DefaultCharset), &ic);
3411                 if (ic != (iconv_t)(-1) ) {
3412                         DecodedInvalidBuf = NewStrBufDup(DecodeMe);
3413                         StrBufConvert(DecodedInvalidBuf, ConvertBuf, &ic);///TODO: don't void const?
3414                         DecodeMee = DecodedInvalidBuf;
3415                         iconv_close(ic);
3416                 }
3417 #endif
3418         }
3419
3420         /* pre evaluate the first pair */
3421         end = NULL;
3422         start = strstr(DecodeMee->buf, "=?");
3423         eptr = DecodeMee->buf + DecodeMee->BufUsed;
3424         if (start != NULL) 
3425                 end = FindNextEnd (DecodeMee, start + 2);
3426         else {
3427                 StrBufAppendBuf(Target, DecodeMee, 0);
3428                 FreeStrBuf(&DecodedInvalidBuf);
3429                 return;
3430         }
3431
3432
3433         if (start != DecodeMee->buf) {
3434                 long nFront;
3435                 
3436                 nFront = start - DecodeMee->buf;
3437                 StrBufAppendBufPlain(Target, DecodeMee->buf, nFront, 0);
3438         }
3439         /*
3440          * Since spammers will go to all sorts of absurd lengths to get their
3441          * messages through, there are LOTS of corrupt headers out there.
3442          * So, prevent a really badly formed RFC2047 header from throwing
3443          * this function into an infinite loop.
3444          */
3445         while ((start != NULL) && 
3446                (end != NULL) && 
3447                (start < eptr) && 
3448                (end < eptr) && 
3449                (passes < 20))
3450         {
3451                 passes++;
3452                 DecodeSegment(Target, 
3453                               DecodeMee, 
3454                               start, 
3455                               end, 
3456                               ConvertBuf,
3457                               ConvertBuf2,
3458                               FoundCharset);
3459                 
3460                 next = strstr(end, "=?");
3461                 nextend = NULL;
3462                 if ((next != NULL) && 
3463                     (next < eptr))
3464                         nextend = FindNextEnd(DecodeMee, next);
3465                 if (nextend == NULL)
3466                         next = NULL;
3467
3468                 /* did we find two partitions */
3469                 if ((next != NULL) && 
3470                     ((next - end) > 2))
3471                 {
3472                         ptr = end + 2;
3473                         while ((ptr < next) && 
3474                                (isspace(*ptr) ||
3475                                 (*ptr == '\r') ||
3476                                 (*ptr == '\n') || 
3477                                 (*ptr == '\t')))
3478                                 ptr ++;
3479                         /* 
3480                          * did we find a gab just filled with blanks?
3481                          * if not, copy its stuff over.
3482                          */
3483                         if (ptr != next)
3484                         {
3485                                 StrBufAppendBufPlain(Target, 
3486                                                      end + 2, 
3487                                                      next - end - 2,
3488                                                      0);
3489                         }
3490                 }
3491                 /* our next-pair is our new first pair now. */
3492                 ptr = end + 2;
3493                 start = next;
3494                 end = nextend;
3495         }
3496         end = ptr;
3497         nextend = DecodeMee->buf + DecodeMee->BufUsed;
3498         if ((end != NULL) && (end < nextend)) {
3499                 ptr = end;
3500                 while ( (ptr < nextend) &&
3501                         (isspace(*ptr) ||
3502                          (*ptr == '\r') ||
3503                          (*ptr == '\n') || 
3504                          (*ptr == '\t')))
3505                         ptr ++;
3506                 if (ptr < nextend)
3507                         StrBufAppendBufPlain(Target, end, nextend - end, 0);
3508         }
3509         FreeStrBuf(&DecodedInvalidBuf);
3510 }
3511
3512 /*******************************************************************************
3513  *                   Manipulating UTF-8 Strings                                *
3514  *******************************************************************************/
3515
3516 /**
3517  * @ingroup StrBuf
3518  * @brief evaluate the length of an utf8 special character sequence
3519  * @param Char the character to examine
3520  * @returns width of utf8 chars in bytes; if the sequence is broken 0 is returned; 1 if its simply ASCII.
3521  */
3522 static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE)
3523 {
3524         int n = 0;
3525         unsigned char test = (1<<7);
3526
3527         if ((*CharS & 0xC0) != 0xC0) 
3528                 return 1;
3529
3530         while ((n < 8) && 
3531                ((test & ((unsigned char)*CharS)) != 0)) 
3532         {
3533                 test = test >> 1;
3534                 n ++;
3535         }
3536         if ((n > 6) || ((CharE - CharS) < n))
3537                 n = 0;
3538         return n;
3539 }
3540
3541 /**
3542  * @ingroup StrBuf
3543  * @brief detect whether this char starts an utf-8 encoded char
3544  * @param Char character to inspect
3545  * @returns yes or no
3546  */
3547 static inline int Ctdl_IsUtf8SequenceStart(const char Char)
3548 {
3549 /** 11??.???? indicates an UTF8 Sequence. */
3550         return ((Char & 0xC0) == 0xC0);
3551 }
3552
3553 /**
3554  * @ingroup StrBuf
3555  * @brief measure the number of glyphs in an UTF8 string...
3556  * @param Buf string to measure
3557  * @returns the number of glyphs in Buf
3558  */
3559 long StrBuf_Utf8StrLen(StrBuf *Buf)
3560 {
3561         int n = 0;
3562         int m = 0;
3563         char *aptr, *eptr;
3564
3565         if ((Buf == NULL) || (Buf->BufUsed == 0))
3566                 return 0;
3567         aptr = Buf->buf;
3568         eptr = Buf->buf + Buf->BufUsed;
3569         while ((aptr < eptr) && (*aptr != '\0')) {
3570                 if (Ctdl_IsUtf8SequenceStart(*aptr)){
3571                         m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
3572                         while ((aptr < eptr) && (*aptr++ != '\0')&& (m-- > 0) );
3573                         n ++;
3574                 }
3575                 else {
3576                         n++;
3577                         aptr++;
3578                 }
3579         }
3580         return n;
3581 }
3582
3583 /**
3584  * @ingroup StrBuf
3585  * @brief cuts a string after maxlen glyphs
3586  * @param Buf string to cut to maxlen glyphs
3587  * @param maxlen how long may the string become?
3588  * @returns current length of the string
3589  */
3590 long StrBuf_Utf8StrCut(StrBuf *Buf, int maxlen)
3591 {
3592         char *aptr, *eptr;
3593         int n = 0, m = 0;
3594
3595         aptr = Buf->buf;
3596         eptr = Buf->buf + Buf->BufUsed;
3597         while ((aptr < eptr) && (*aptr != '\0')) {
3598                 if (Ctdl_IsUtf8SequenceStart(*aptr)){
3599                         m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
3600                         while ((*aptr++ != '\0') && (m-- > 0));
3601                         n ++;
3602                 }
3603                 else {
3604                         n++;
3605                         aptr++;
3606                 }
3607                 if (n > maxlen) {
3608                         *aptr = '\0';
3609                         Buf->BufUsed = aptr - Buf->buf;
3610                         return Buf->BufUsed;
3611                 }                       
3612         }
3613         return Buf->BufUsed;
3614
3615 }
3616
3617
3618
3619
3620
3621 /*******************************************************************************
3622  *                               wrapping ZLib                                 *
3623  *******************************************************************************/
3624
3625 #ifdef HAVE_ZLIB
3626 #define DEF_MEM_LEVEL 8 /*< memlevel??? */
3627 #define OS_CODE 0x03    /*< unix */
3628
3629 /**
3630  * @ingroup StrBuf_DeEnCoder
3631  * @brief uses the same calling syntax as compress2(), but it
3632  *   creates a stream compatible with HTTP "Content-encoding: gzip"
3633  * @param dest compressed buffer
3634  * @param destLen length of the compresed data 
3635  * @param source source to encode
3636  * @param sourceLen length of source to encode 
3637  * @param level compression level
3638  */
3639 int ZEXPORT compress_gzip(Bytef * dest,
3640                           size_t * destLen,
3641                           const Bytef * source,
3642                           uLong sourceLen,     
3643                           int level)
3644 {
3645         const int gz_magic[2] = { 0x1f, 0x8b }; /* gzip magic header */
3646
3647         /* write gzip header */
3648         snprintf((char *) dest, *destLen, 
3649                  "%c%c%c%c%c%c%c%c%c%c",
3650                  gz_magic[0], gz_magic[1], Z_DEFLATED,
3651                  0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /* xflags */ ,
3652                  OS_CODE);
3653
3654         /* normal deflate */
3655         z_stream stream;
3656         int err;
3657         stream.next_in = (Bytef *) source;
3658         stream.avail_in = (uInt) sourceLen;
3659         stream.next_out = dest + 10L;   // after header
3660         stream.avail_out = (uInt) * destLen;
3661         if ((uLong) stream.avail_out != *destLen)
3662                 return Z_BUF_ERROR;
3663
3664         stream.zalloc = (alloc_func) 0;
3665         stream.zfree = (free_func) 0;
3666         stream.opaque = (voidpf) 0;
3667
3668         err = deflateInit2(&stream, level, Z_DEFLATED, -MAX_WBITS,
3669                            DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
3670         if (err != Z_OK)
3671                 return err;
3672
3673         err = deflate(&stream, Z_FINISH);
3674         if (err != Z_STREAM_END) {
3675                 deflateEnd(&stream);
3676                 return err == Z_OK ? Z_BUF_ERROR : err;
3677         }
3678         *destLen = stream.total_out + 10L;
3679
3680         /* write CRC and Length */
3681         uLong crc = crc32(0L, source, sourceLen);
3682         int n;
3683         for (n = 0; n < 4; ++n, ++*destLen) {
3684                 dest[*destLen] = (int) (crc & 0xff);
3685                 crc >>= 8;
3686         }
3687         uLong len = stream.total_in;
3688         for (n = 0; n < 4; ++n, ++*destLen) {
3689                 dest[*destLen] = (int) (len & 0xff);
3690                 len >>= 8;
3691         }
3692         err = deflateEnd(&stream);
3693         return err;
3694 }
3695 #endif
3696
3697
3698 /**
3699  * @ingroup StrBuf_DeEnCoder
3700  * @brief compress the buffer with gzip
3701  * Attention! If you feed this a Const String, you must maintain the uncompressed buffer yourself!
3702  * @param Buf buffer whose content is to be gzipped
3703  */
3704 int CompressBuffer(StrBuf *Buf)
3705 {
3706 #ifdef HAVE_ZLIB
3707         char *compressed_data = NULL;
3708         size_t compressed_len, bufsize;
3709         int i = 0;
3710
3711         bufsize = compressed_len = Buf->BufUsed +  (Buf->BufUsed / 100) + 100;
3712         compressed_data = malloc(compressed_len);
3713         
3714         if (compressed_data == NULL)
3715                 return -1;
3716         /* Flush some space after the used payload so valgrind shuts up... */
3717         while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
3718                 Buf->buf[Buf->BufUsed + i++] = '\0';
3719         if (compress_gzip((Bytef *) compressed_data,
3720                           &compressed_len,
3721                           (Bytef *) Buf->buf,
3722                           (uLongf) Buf->BufUsed, Z_BEST_SPEED) == Z_OK) {
3723                 if (!Buf->ConstBuf)
3724                         free(Buf->buf);
3725                 Buf->buf = compressed_data;
3726                 Buf->BufUsed = compressed_len;
3727                 Buf->BufSize = bufsize;
3728                 /* Flush some space after the used payload so valgrind shuts up... */
3729                 i = 0;
3730                 while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
3731                         Buf->buf[Buf->BufUsed + i++] = '\0';
3732                 return 1;
3733         } else {
3734                 free(compressed_data);
3735         }
3736 #endif  /* HAVE_ZLIB */
3737         return 0;
3738 }
3739
3740 /*******************************************************************************
3741  *           File I/O; Callbacks to libevent                                   *
3742  *******************************************************************************/
3743
3744 long StrBuf_read_one_chunk_callback (int fd, short event, IOBuffer *FB)
3745 {
3746         long bufremain = 0;
3747         int n;
3748         
3749         if ((FB == NULL) || (FB->Buf == NULL))
3750                 return -1;
3751
3752         /*
3753          * check whether the read pointer is somewhere in a range 
3754          * where a cut left is inexpensive
3755          */
3756
3757         if (FB->ReadWritePointer != NULL)
3758         {
3759                 long already_read;
3760                 
3761                 already_read = FB->ReadWritePointer - FB->Buf->buf;
3762                 bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
3763
3764                 if (already_read != 0) {
3765                         long unread;
3766                         
3767                         unread = FB->Buf->BufUsed - already_read;
3768
3769                         /* else nothing to compact... */
3770                         if (unread == 0) {
3771                                 FB->ReadWritePointer = FB->Buf->buf;
3772                                 bufremain = FB->Buf->BufSize;                   
3773                         }
3774                         else if ((unread < 64) || 
3775                                  (bufremain < already_read))
3776                         {
3777                                 /* 
3778                                  * if its just a tiny bit remaining, or we run out of space... 
3779                                  * lets tidy up.
3780                                  */
3781                                 FB->Buf->BufUsed = unread;
3782                                 if (unread < already_read)
3783                                         memcpy(FB->Buf->buf, FB->ReadWritePointer, unread);
3784                                 else
3785                                         memmove(FB->Buf->buf, FB->ReadWritePointer, unread);
3786                                 FB->ReadWritePointer = FB->Buf->buf;
3787                                 bufremain = FB->Buf->BufSize - unread - 1;
3788                         }
3789                         else if (bufremain < (FB->Buf->BufSize / 10))
3790                         {
3791                                 /* get a bigger buffer */ 
3792
3793                                 IncreaseBuf(FB->Buf, 0, FB->Buf->BufUsed + 1);
3794
3795                                 FB->ReadWritePointer = FB->Buf->buf + unread;
3796
3797                                 bufremain = FB->Buf->BufSize - unread - 1;
3798 /*TODO: special increase function that won't copy the already read! */
3799                         }
3800                 }
3801                 else if (bufremain < 10) {
3802                         IncreaseBuf(FB->Buf, 1, FB->Buf->BufUsed + 10);
3803                         
3804                         FB->ReadWritePointer = FB->Buf->buf;
3805                         
3806                         bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
3807                 }
3808                 
3809         }
3810         else {
3811                 FB->ReadWritePointer = FB->Buf->buf;
3812                 bufremain = FB->Buf->BufSize - 1;
3813         }
3814
3815         n = read(fd, FB->Buf->buf + FB->Buf->BufUsed, bufremain);
3816
3817         if (n > 0) {
3818                 FB->Buf->BufUsed += n;
3819                 FB->Buf->buf[FB->Buf->BufUsed] = '\0';
3820         }
3821         return n;
3822 }
3823
3824 int StrBuf_write_one_chunk_callback(int fd, short event, IOBuffer *FB)
3825 {
3826         long WriteRemain;
3827         int n;
3828
3829         if ((FB == NULL) || (FB->Buf == NULL))
3830                 return -1;
3831
3832         if (FB->ReadWritePointer != NULL)
3833         {
3834                 WriteRemain = FB->Buf->BufUsed - 
3835                         (FB->ReadWritePointer - 
3836                          FB->Buf->buf);
3837         }
3838         else {
3839                 FB->ReadWritePointer = FB->Buf->buf;
3840                 WriteRemain = FB->Buf->BufUsed;
3841         }
3842
3843         n = write(fd, FB->ReadWritePointer, WriteRemain);
3844         if (n > 0) {
3845                 FB->ReadWritePointer += n;
3846
3847                 if (FB->ReadWritePointer == 
3848                     FB->Buf->buf + FB->Buf->BufUsed)
3849                 {
3850                         FlushStrBuf(FB->Buf);
3851                         FB->ReadWritePointer = NULL;
3852                         return 0;
3853                 }
3854         // check whether we've got something to write
3855         // get the maximum chunk plus the pointer we can send
3856         // write whats there
3857         // if not all was sent, remember the send pointer for the next time
3858                 return FB->ReadWritePointer - FB->Buf->buf + FB->Buf->BufUsed;
3859         }
3860         return n;
3861 }
3862
3863 /**
3864  * @ingroup StrBuf_IO
3865  * @brief extract a "next line" from Buf; Ptr to persist across several iterations
3866  * @param LineBuf your line will be copied here.
3867  * @param FB BLOB with lines of text...
3868  * @param Ptr moved arround to keep the next-line across several iterations
3869  *        has to be &NULL on start; will be &NotNULL on end of buffer
3870  * @returns size of copied buffer
3871  */
3872 eReadState StrBufChunkSipLine(StrBuf *LineBuf, IOBuffer *FB)
3873 {
3874         const char *aptr, *ptr, *eptr;
3875         char *optr, *xptr;
3876
3877         if ((FB == NULL) || (LineBuf == NULL) || (LineBuf->buf == NULL))
3878                 return eReadFail;
3879         
3880
3881         if ((FB->Buf == NULL) || (FB->ReadWritePointer == StrBufNOTNULL)) {
3882                 FB->ReadWritePointer = StrBufNOTNULL;
3883                 return eReadFail;
3884         }
3885
3886         FlushStrBuf(LineBuf);
3887         if (FB->ReadWritePointer == NULL)
3888                 ptr = aptr = FB->Buf->buf;
3889         else
3890                 ptr = aptr = FB->ReadWritePointer;
3891
3892         optr = LineBuf->buf;
3893         eptr = FB->Buf->buf + FB->Buf->BufUsed;
3894         xptr = LineBuf->buf + LineBuf->BufSize - 1;
3895
3896         while ((ptr <= eptr) && 
3897                (*ptr != '\n') &&
3898                (*ptr != '\r') )
3899         {
3900                 *optr = *ptr;
3901                 optr++; ptr++;
3902                 if (optr == xptr) {
3903                         LineBuf->BufUsed = optr - LineBuf->buf;
3904                         IncreaseBuf(LineBuf,  1, LineBuf->BufUsed + 1);
3905                         optr = LineBuf->buf + LineBuf->BufUsed;
3906                         xptr = LineBuf->buf + LineBuf->BufSize - 1;
3907                 }
3908         }
3909
3910         if (ptr >= eptr) {
3911                 if (optr > LineBuf->buf)
3912                         optr --;
3913                 if ((*(ptr - 1) != '\r') && (*(ptr - 1) != '\n')) {
3914                         LineBuf->BufUsed = optr - LineBuf->buf;
3915                         *optr = '\0';
3916                         if ((FB->ReadWritePointer != NULL) && 
3917                             (FB->ReadWritePointer != FB->Buf->buf))
3918                         {
3919                                 /* Ok, the client application read all the data 
3920                                    it was interested in so far. Since there is more to read, 
3921                                    we now shrink the buffer, and move the rest over.
3922                                 */
3923                                 StrBufCutLeft(FB->Buf, 
3924                                               FB->ReadWritePointer - FB->Buf->buf);
3925                                 FB->ReadWritePointer = FB->Buf->buf;
3926                         }
3927                         return eMustReadMore;
3928                 }
3929         }
3930         LineBuf->BufUsed = optr - LineBuf->buf;
3931         *optr = '\0';       
3932         if ((ptr <= eptr) && (*ptr == '\r'))
3933                 ptr ++;
3934         if ((ptr <= eptr) && (*ptr == '\n'))
3935                 ptr ++;
3936         
3937         if (ptr < eptr) {
3938                 FB->ReadWritePointer = ptr;
3939         }
3940         else {
3941                 FlushStrBuf(FB->Buf);
3942                 FB->ReadWritePointer = NULL;
3943         }
3944
3945         return eReadSuccess;
3946 }
3947
3948 /**
3949  * @ingroup StrBuf_CHUNKED_IO
3950  * @brief check whether the chunk-buffer has more data waiting or not.
3951  * @param FB Chunk-Buffer to inspect
3952  */
3953 eReadState StrBufCheckBuffer(IOBuffer *FB)
3954 {
3955         if (FB == NULL)
3956                 return eReadFail;
3957         if (FB->Buf->BufUsed == 0)
3958                 return eReadSuccess;
3959         if (FB->ReadWritePointer == NULL)
3960                 return eBufferNotEmpty;
3961         if (FB->Buf->buf + FB->Buf->BufUsed > FB->ReadWritePointer)
3962                 return eBufferNotEmpty;
3963         return eReadSuccess;
3964 }
3965
3966 long IOBufferStrLength(IOBuffer *FB)
3967 {
3968         if ((FB == NULL) || (FB->Buf == NULL))
3969                 return 0;
3970         if (FB->ReadWritePointer == NULL)
3971                 return StrLength(FB->Buf);
3972         
3973         return StrLength(FB->Buf) - (FB->ReadWritePointer - FB->Buf->buf);
3974 }
3975
3976 inline static void FDIOBufferFlush(FDIOBuffer *FDB)
3977 {
3978         memset(FDB, 0, sizeof(FDIOBuffer));
3979         FDB->OtherFD = -1;
3980         FDB->SplicePipe[0] = -1;
3981         FDB->SplicePipe[1] = -1;
3982 }
3983
3984 void FDIOBufferInit(FDIOBuffer *FDB, IOBuffer *IO, int FD, long TotalSendSize)
3985 {
3986         FDIOBufferFlush(FDB);
3987         FDB->ChunkSize = 
3988                 FDB->TotalSendSize = TotalSendSize;
3989         FDB->IOB = IO;
3990 #ifdef LINUX_SPLICE
3991         if (EnableSplice)
3992                 pipe(FDB->SplicePipe);
3993         else
3994 #endif
3995                 FDB->ChunkBuffer = NewStrBufPlain(NULL, TotalSendSize + 1);
3996
3997         FDB->OtherFD = FD;
3998 }
3999
4000 void FDIOBufferDelete(FDIOBuffer *FDB)
4001 {
4002 #ifdef LINUX_SPLICE
4003         if (EnableSplice)
4004         {
4005                 if (FDB->SplicePipe[0] > 0)
4006                         close(FDB->SplicePipe[0]);
4007                 if (FDB->SplicePipe[1] > 0)
4008                         close(FDB->SplicePipe[1]);
4009         }
4010         else
4011 #endif
4012                 FreeStrBuf(&FDB->ChunkBuffer);
4013         
4014         if (FDB->OtherFD > 0)
4015                 close(FDB->OtherFD);
4016         FDIOBufferFlush(FDB);
4017 }
4018
4019 int FileSendChunked(FDIOBuffer *FDB, const char **Err)
4020 {
4021         ssize_t sent, pipesize;
4022 #ifdef LINUX_SPLICE
4023         if (EnableSplice)
4024         {
4025                 if (FDB->PipeSize == 0)
4026                 {
4027                         pipesize = splice(FDB->OtherFD,
4028                                           &FDB->TotalSentAlready, 
4029                                           FDB->SplicePipe[1],
4030                                           NULL, 
4031                                           FDB->ChunkSendRemain, 
4032                                           SPLICE_F_MOVE);
4033         
4034                         if (pipesize == -1)
4035                         {
4036                                 *Err = strerror(errno);
4037                                 return pipesize;
4038                         }
4039                         FDB->PipeSize = pipesize;
4040                 }
4041                 sent =  splice(FDB->SplicePipe[0],
4042                                NULL, 
4043                                FDB->IOB->fd,
4044                                NULL, 
4045                                FDB->PipeSize,
4046                                SPLICE_F_MORE | SPLICE_F_MOVE | SPLICE_F_NONBLOCK);
4047                 if (sent == -1)
4048                 {
4049                         *Err = strerror(errno);
4050                         return sent;
4051                 }
4052                 FDB->PipeSize -= sent;
4053                 FDB->ChunkSendRemain -= sent;
4054                 return sent;
4055         }
4056         else
4057 #endif
4058         {
4059                 char *pRead;
4060                 long nRead = 0;
4061
4062                 pRead = FDB->ChunkBuffer->buf;
4063                 while ((FDB->ChunkBuffer->BufUsed < FDB->TotalSendSize) && (nRead >= 0))
4064                 {
4065                         nRead = read(FDB->OtherFD, pRead, FDB->TotalSendSize - FDB->ChunkBuffer->BufUsed);
4066                         if (nRead > 0) {
4067                                 FDB->ChunkBuffer->BufUsed += nRead;
4068                                 FDB->ChunkBuffer->buf[FDB->ChunkBuffer->BufUsed] = '\0';
4069                         }
4070                         else if (nRead == 0) {}
4071                         else return nRead;
4072                 
4073                 }
4074
4075                 nRead = write(FDB->IOB->fd, FDB->ChunkBuffer->buf + FDB->TotalSentAlready, FDB->ChunkSendRemain);
4076
4077                 if (nRead >= 0) {
4078                         FDB->TotalSentAlready += nRead;
4079                         FDB->ChunkSendRemain -= nRead;
4080                         return FDB->ChunkSendRemain;
4081                 }
4082                 else {
4083                         return nRead;
4084                 }
4085         }
4086 }
4087
4088 int FileRecvChunked(FDIOBuffer *FDB, const char **Err)
4089 {
4090         ssize_t sent, pipesize;
4091
4092 #ifdef LINUX_SPLICE
4093         if (EnableSplice)
4094         {
4095                 if (FDB->PipeSize == 0)
4096                 {
4097                         pipesize = splice(FDB->IOB->fd,
4098                                           NULL, 
4099                                           FDB->SplicePipe[1],
4100                                           NULL, 
4101                                           FDB->ChunkSendRemain, 
4102                                           SPLICE_F_MORE | SPLICE_F_MOVE|SPLICE_F_NONBLOCK);
4103
4104                         if (pipesize == -1)
4105                         {
4106                                 *Err = strerror(errno);
4107                                 return pipesize;
4108                         }
4109                         FDB->PipeSize = pipesize;
4110                 }
4111         
4112                 sent = splice(FDB->SplicePipe[0],
4113                               NULL, 
4114                               FDB->OtherFD,
4115                               &FDB->TotalSentAlready, 
4116                               FDB->PipeSize,
4117                               SPLICE_F_MORE | SPLICE_F_MOVE);
4118
4119                 if (sent == -1)
4120                 {
4121                         *Err = strerror(errno);
4122                         return sent;
4123                 }
4124                 FDB->PipeSize -= sent;
4125                 FDB->ChunkSendRemain -= sent;
4126                 return sent;
4127         }
4128         else
4129 #endif
4130         {
4131                 sent = read(FDB->IOB->fd, FDB->ChunkBuffer->buf, FDB->ChunkSendRemain);
4132                 if (sent > 0) {
4133                         int nWritten = 0;
4134                         int rc; 
4135                 
4136                         FDB->ChunkBuffer->BufUsed = sent;
4137
4138                         while (nWritten < FDB->ChunkBuffer->BufUsed) {
4139                                 rc =  write(FDB->OtherFD, FDB->ChunkBuffer->buf + nWritten, FDB->ChunkBuffer->BufUsed - nWritten);
4140                                 if (rc < 0) {
4141                                         *Err = strerror(errno);
4142                                         return rc;
4143                                 }
4144                                 nWritten += rc;
4145
4146                         }
4147                         FDB->ChunkBuffer->BufUsed = 0;
4148                         FDB->TotalSentAlready += sent;
4149                         FDB->ChunkSendRemain -= sent;
4150                         return FDB->ChunkSendRemain;
4151                 }
4152                 else if (sent < 0) {
4153                         *Err = strerror(errno);
4154                         return sent;
4155                 }
4156                 return 0;
4157         }
4158 }
4159
4160 int FileMoveChunked(FDIOBuffer *FDB, const char **Err)
4161 {
4162         ssize_t sent, pipesize;
4163
4164 #ifdef LINUX_SPLICE
4165         if (EnableSplice)
4166         {
4167                 if (FDB->PipeSize == 0)
4168                 {
4169                         pipesize = splice(FDB->IOB->fd,
4170                                           &FDB->TotalReadAlready, 
4171                                           FDB->SplicePipe[1],
4172                                           NULL, 
4173                                           FDB->ChunkSendRemain, 
4174                                           SPLICE_F_MORE | SPLICE_F_MOVE|SPLICE_F_NONBLOCK);
4175                         
4176                         if (pipesize == -1)
4177                         {
4178                                 *Err = strerror(errno);
4179                                 return pipesize;
4180                         }
4181                         FDB->PipeSize = pipesize;
4182                 }
4183                 
4184                 sent = splice(FDB->SplicePipe[0],
4185                               NULL, 
4186                               FDB->OtherFD,
4187                               &FDB->TotalSentAlready, 
4188                               FDB->PipeSize,
4189                               SPLICE_F_MORE | SPLICE_F_MOVE);
4190                 
4191                 if (sent == -1)
4192                 {
4193                         *Err = strerror(errno);
4194                         return sent;
4195                 }
4196                 FDB->PipeSize -= sent;
4197                 FDB->ChunkSendRemain -= sent;
4198                 return sent;
4199         }
4200         else
4201 #endif  
4202         {
4203                 sent = read(FDB->IOB->fd, FDB->ChunkBuffer->buf, FDB->ChunkSendRemain);
4204                 if (sent > 0) {
4205                         int nWritten = 0;
4206                         int rc; 
4207                 
4208                         FDB->ChunkBuffer->BufUsed = sent;
4209
4210                         while (nWritten < FDB->ChunkBuffer->BufUsed) {
4211                                 rc =  write(FDB->OtherFD, FDB->ChunkBuffer->buf + nWritten, FDB->ChunkBuffer->BufUsed - nWritten);
4212                                 if (rc < 0) {
4213                                         *Err = strerror(errno);
4214                                         return rc;
4215                                 }
4216                                 nWritten += rc;
4217
4218                         }
4219                         FDB->ChunkBuffer->BufUsed = 0;
4220                         FDB->TotalSentAlready += sent;
4221                         FDB->ChunkSendRemain -= sent;
4222                         return FDB->ChunkSendRemain;
4223                 }
4224                 else if (sent < 0) {
4225                         *Err = strerror(errno);
4226                         return sent;
4227                 }
4228                 return 0;
4229         }
4230 }
4231
4232 eReadState WriteIOBAlreadyRead(FDIOBuffer *FDB, const char **Error)
4233 {
4234         int IsNonBlock;
4235         int fdflags;
4236         long rlen;
4237         long should_write;
4238         int nSuccessLess = 0;
4239         struct timeval tv;
4240         fd_set rfds;
4241
4242         fdflags = fcntl(FDB->OtherFD, F_GETFL);
4243         IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4244
4245         while ((FDB->IOB->ReadWritePointer - FDB->IOB->Buf->buf < FDB->IOB->Buf->BufUsed) &&
4246                (FDB->ChunkSendRemain > 0))
4247         {
4248                 if (IsNonBlock){
4249                         tv.tv_sec = 1; /* selectresolution; */
4250                         tv.tv_usec = 0;
4251                         
4252                         FD_ZERO(&rfds);
4253                         FD_SET(FDB->OtherFD, &rfds);
4254                         if (select(FDB->OtherFD + 1, NULL, &rfds, NULL, &tv) == -1) {
4255                                 *Error = strerror(errno);
4256                                 return eReadFail;
4257                         }
4258                 }
4259                 if (IsNonBlock && !  FD_ISSET(FDB->OtherFD, &rfds)) {
4260                         nSuccessLess ++;
4261                         continue;
4262                 }
4263
4264                 should_write = FDB->IOB->Buf->BufUsed - 
4265                         (FDB->IOB->ReadWritePointer - FDB->IOB->Buf->buf);
4266                 if (should_write > FDB->ChunkSendRemain)
4267                         should_write = FDB->ChunkSendRemain;
4268
4269                 rlen = write(FDB->OtherFD, 
4270                              FDB->IOB->ReadWritePointer, 
4271                              should_write);
4272                 if (rlen < 1) {
4273                         *Error = strerror(errno);
4274                                                 
4275                         return eReadFail;
4276                 }
4277                 FDB->TotalSentAlready += rlen;
4278                 FDB->IOB->ReadWritePointer += rlen;
4279                 FDB->ChunkSendRemain -= rlen;
4280         }
4281         if (FDB->IOB->ReadWritePointer >= FDB->IOB->Buf->buf + FDB->IOB->Buf->BufUsed)
4282         {
4283                 FlushStrBuf(FDB->IOB->Buf);
4284                 FDB->IOB->ReadWritePointer = NULL;
4285         }
4286
4287         if (FDB->ChunkSendRemain == 0)
4288                 return eReadSuccess;
4289         else 
4290                 return eMustReadMore;
4291 }
4292
4293 /*******************************************************************************
4294  *           File I/O; Prefer buffered read since its faster!                  *
4295  *******************************************************************************/
4296
4297 /**
4298  * @ingroup StrBuf_IO
4299  * @brief Read a line from socket
4300  * flushes and closes the FD on error
4301  * @param buf the buffer to get the input to
4302  * @param fd pointer to the filedescriptor to read
4303  * @param append Append to an existing string or replace?
4304  * @param Error strerror() on error 
4305  * @returns numbers of chars read
4306  */
4307 int StrBufTCP_read_line(StrBuf *buf, int *fd, int append, const char **Error)
4308 {
4309         int len, rlen, slen;
4310
4311         if ((buf == NULL) || (buf->buf == NULL)) {
4312                 *Error = strerror(EINVAL);
4313                 return -1;
4314         }
4315
4316         if (!append)
4317                 FlushStrBuf(buf);
4318
4319         slen = len = buf->BufUsed;
4320         while (1) {
4321                 rlen = read(*fd, &buf->buf[len], 1);
4322                 if (rlen < 1) {
4323                         *Error = strerror(errno);
4324                         
4325                         close(*fd);
4326                         *fd = -1;
4327                         
4328                         return -1;
4329                 }
4330                 if (buf->buf[len] == '\n')
4331                         break;
4332                 if (buf->buf[len] != '\r')
4333                         len ++;
4334                 if (len + 2 >= buf->BufSize) {
4335                         buf->BufUsed = len;
4336                         buf->buf[len+1] = '\0';
4337                         IncreaseBuf(buf, 1, -1);
4338                 }
4339         }
4340         buf->BufUsed = len;
4341         buf->buf[len] = '\0';
4342         return len - slen;
4343 }
4344
4345 /**
4346  * @ingroup StrBuf_BufferedIO
4347  * @brief Read a line from socket
4348  * flushes and closes the FD on error
4349  * @param Line the line to read from the fd / I/O Buffer
4350  * @param buf the buffer to get the input to
4351  * @param fd pointer to the filedescriptor to read
4352  * @param timeout number of successless selects until we bail out
4353  * @param selectresolution how long to wait on each select
4354  * @param Error strerror() on error 
4355  * @returns numbers of chars read
4356  */
4357 int StrBufTCP_read_buffered_line(StrBuf *Line, 
4358                                  StrBuf *buf, 
4359                                  int *fd, 
4360                                  int timeout, 
4361                                  int selectresolution, 
4362                                  const char **Error)
4363 {
4364         int len, rlen;
4365         int nSuccessLess = 0;
4366         fd_set rfds;
4367         char *pch = NULL;
4368         int fdflags;
4369         int IsNonBlock;
4370         struct timeval tv;
4371
4372         if (buf->BufUsed > 0) {
4373                 pch = strchr(buf->buf, '\n');
4374                 if (pch != NULL) {
4375                         rlen = 0;
4376                         len = pch - buf->buf;
4377                         if (len > 0 && (*(pch - 1) == '\r') )
4378                                 rlen ++;
4379                         StrBufSub(Line, buf, 0, len - rlen);
4380                         StrBufCutLeft(buf, len + 1);
4381                         return len - rlen;
4382                 }
4383         }
4384         
4385         if (buf->BufSize - buf->BufUsed < 10)
4386                 IncreaseBuf(buf, 1, -1);
4387
4388         fdflags = fcntl(*fd, F_GETFL);
4389         IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4390
4391         while ((nSuccessLess < timeout) && (pch == NULL)) {
4392                 if (IsNonBlock){
4393                         tv.tv_sec = selectresolution;
4394                         tv.tv_usec = 0;
4395                         
4396                         FD_ZERO(&rfds);
4397                         FD_SET(*fd, &rfds);
4398                         if (select(*fd + 1, NULL, &rfds, NULL, &tv) == -1) {
4399                                 *Error = strerror(errno);
4400                                 close (*fd);
4401                                 *fd = -1;
4402                                 return -1;
4403                         }
4404                 }
4405                 if (IsNonBlock && !  FD_ISSET(*fd, &rfds)) {
4406                         nSuccessLess ++;
4407                         continue;
4408                 }
4409                 rlen = read(*fd, 
4410                             &buf->buf[buf->BufUsed], 
4411                             buf->BufSize - buf->BufUsed - 1);
4412                 if (rlen < 1) {
4413                         *Error = strerror(errno);
4414                         close(*fd);
4415                         *fd = -1;
4416                         return -1;
4417                 }
4418                 else if (rlen > 0) {
4419                         nSuccessLess = 0;
4420                         buf->BufUsed += rlen;
4421                         buf->buf[buf->BufUsed] = '\0';
4422                         pch = strchr(buf->buf, '\n');
4423                         if ((pch == NULL) &&
4424                             (buf->BufUsed + 10 > buf->BufSize) &&
4425                             (IncreaseBuf(buf, 1, -1) == -1))
4426                                 return -1;
4427                         continue;
4428                 }
4429                 
4430         }
4431         if (pch != NULL) {
4432                 rlen = 0;
4433                 len = pch - buf->buf;
4434                 if (len > 0 && (*(pch - 1) == '\r') )
4435                         rlen ++;
4436                 StrBufSub(Line, buf, 0, len - rlen);
4437                 StrBufCutLeft(buf, len + 1);
4438                 return len - rlen;
4439         }
4440         return -1;
4441
4442 }
4443
4444 static const char *ErrRBLF_PreConditionFailed="StrBufTCP_read_buffered_line_fast: Wrong arguments or invalid Filedescriptor";
4445 static const char *ErrRBLF_SelectFailed="StrBufTCP_read_buffered_line_fast: Select failed without reason";
4446 static const char *ErrRBLF_NotEnoughSentFromServer="StrBufTCP_read_buffered_line_fast: No complete line was sent from peer";
4447 /**
4448  * @ingroup StrBuf_BufferedIO
4449  * @brief Read a line from socket
4450  * flushes and closes the FD on error
4451  * @param Line where to append our Line read from the fd / I/O Buffer; 
4452  * @param IOBuf the buffer to get the input to; lifetime pair to FD
4453  * @param Pos pointer to the current read position, should be NULL initialized on opening the FD it belongs to.!
4454  * @param fd pointer to the filedescriptor to read
4455  * @param timeout number of successless selects until we bail out
4456  * @param selectresolution how long to wait on each select
4457  * @param Error strerror() on error 
4458  * @returns numbers of chars read or -1 in case of error. "\n" will become 0
4459  */
4460 int StrBufTCP_read_buffered_line_fast(StrBuf *Line, 
4461                                       StrBuf *IOBuf, 
4462                                       const char **Pos,
4463                                       int *fd, 
4464                                       int timeout, 
4465                                       int selectresolution, 
4466                                       const char **Error)
4467 {
4468         const char *pche = NULL;
4469         const char *pos = NULL;
4470         const char *pLF;
4471         int len, rlen, retlen;
4472         int nSuccessLess = 0;
4473         fd_set rfds;
4474         const char *pch = NULL;
4475         int fdflags;
4476         int IsNonBlock;
4477         struct timeval tv;
4478         
4479         retlen = 0;
4480         if ((Line == NULL) ||
4481             (Pos == NULL) ||
4482             (IOBuf == NULL) ||
4483             (*fd == -1))
4484         {
4485                 if (Pos != NULL)
4486                         *Pos = NULL;
4487                 *Error = ErrRBLF_PreConditionFailed;
4488                 return -1;
4489         }
4490
4491         pos = *Pos;
4492         if ((IOBuf->BufUsed > 0) && 
4493             (pos != NULL) && 
4494             (pos < IOBuf->buf + IOBuf->BufUsed)) 
4495         {
4496                 char *pcht;
4497
4498                 pche = IOBuf->buf + IOBuf->BufUsed;
4499                 pch = pos;
4500                 pcht = Line->buf;
4501
4502                 while ((pch < pche) && (*pch != '\n'))
4503                 {
4504                         if (Line->BufUsed + 10 > Line->BufSize)
4505                         {
4506                                 long apos;
4507                                 apos = pcht - Line->buf;
4508                                 *pcht = '\0';
4509                                 IncreaseBuf(Line, 1, -1);
4510                                 pcht = Line->buf + apos;
4511                         }
4512                         *pcht++ = *pch++;
4513                         Line->BufUsed++;
4514                         retlen++;
4515                 }
4516
4517                 len = pch - pos;
4518                 if (len > 0 && (*(pch - 1) == '\r') )
4519                 {
4520                         retlen--;
4521                         len --;
4522                         pcht --;
4523                         Line->BufUsed --;
4524                 }
4525                 *pcht = '\0';
4526
4527                 if ((pch >= pche) || (*pch == '\0'))
4528                 {
4529                         FlushStrBuf(IOBuf);
4530                         *Pos = NULL;
4531                         pch = NULL;
4532                         pos = 0;
4533                 }
4534
4535                 if ((pch != NULL) && 
4536                     (pch <= pche)) 
4537                 {
4538                         if (pch + 1 >= pche) {
4539                                 *Pos = NULL;
4540                                 FlushStrBuf(IOBuf);
4541                         }
4542                         else
4543                                 *Pos = pch + 1;
4544                         
4545                         return retlen;
4546                 }
4547                 else 
4548                         FlushStrBuf(IOBuf);
4549         }
4550
4551         /* If we come here, Pos is Unset since we read everything into Line, and now go for more. */
4552         
4553         if (IOBuf->BufSize - IOBuf->BufUsed < 10)
4554                 IncreaseBuf(IOBuf, 1, -1);
4555
4556         fdflags = fcntl(*fd, F_GETFL);
4557         IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4558
4559         pLF = NULL;
4560         while ((nSuccessLess < timeout) && 
4561                (pLF == NULL) &&
4562                (*fd != -1)) {
4563                 if (IsNonBlock)
4564                 {
4565                         tv.tv_sec = 1;
4566                         tv.tv_usec = 0;
4567                 
4568                         FD_ZERO(&rfds);
4569                         FD_SET(*fd, &rfds);
4570                         if (select((*fd) + 1, &rfds, NULL, NULL, &tv) == -1) {
4571                                 *Error = strerror(errno);
4572                                 close (*fd);
4573                                 *fd = -1;
4574                                 if (*Error == NULL)
4575                                         *Error = ErrRBLF_SelectFailed;
4576                                 return -1;
4577                         }
4578                         if (! FD_ISSET(*fd, &rfds) != 0) {
4579                                 nSuccessLess ++;
4580                                 continue;
4581                         }
4582                 }
4583                 rlen = read(*fd, 
4584                             &IOBuf->buf[IOBuf->BufUsed], 
4585                             IOBuf->BufSize - IOBuf->BufUsed - 1);
4586                 if (rlen < 1) {
4587                         *Error = strerror(errno);
4588                         close(*fd);
4589                         *fd = -1;
4590                         return -1;
4591                 }
4592                 else if (rlen > 0) {
4593                         nSuccessLess = 0;
4594                         pLF = IOBuf->buf + IOBuf->BufUsed;
4595                         IOBuf->BufUsed += rlen;
4596                         IOBuf->buf[IOBuf->BufUsed] = '\0';
4597                         
4598                         pche = IOBuf->buf + IOBuf->BufUsed;
4599                         
4600                         while ((pLF < pche) && (*pLF != '\n'))
4601                                 pLF ++;
4602                         if ((pLF >= pche) || (*pLF == '\0'))
4603                                 pLF = NULL;
4604
4605                         if (IOBuf->BufUsed + 10 > IOBuf->BufSize)
4606                         {
4607                                 long apos = 0;
4608
4609                                 if (pLF != NULL) apos = pLF - IOBuf->buf;
4610                                 IncreaseBuf(IOBuf, 1, -1);      
4611                                 if (pLF != NULL) pLF = IOBuf->buf + apos;
4612                         }
4613
4614                         continue;
4615                 }
4616                 else
4617                 {
4618                         nSuccessLess++;
4619                 }
4620         }
4621         *Pos = NULL;
4622         if (pLF != NULL) {
4623                 pos = IOBuf->buf;
4624                 len = pLF - pos;
4625                 if (len > 0 && (*(pLF - 1) == '\r') )
4626                         len --;
4627                 StrBufAppendBufPlain(Line, ChrPtr(IOBuf), len, 0);
4628                 if (pLF + 1 >= IOBuf->buf + IOBuf->BufUsed)
4629                 {
4630                         FlushStrBuf(IOBuf);
4631                 }
4632                 else 
4633                         *Pos = pLF + 1;
4634                 return retlen + len;
4635         }
4636         *Error = ErrRBLF_NotEnoughSentFromServer;
4637         return -1;
4638
4639 }
4640
4641 static const char *ErrRBLF_BLOBPreConditionFailed="StrBufReadBLOB: Wrong arguments or invalid Filedescriptor";
4642 /**
4643  * @ingroup StrBuf_IO
4644  * @brief Input binary data from socket
4645  * flushes and closes the FD on error
4646  * @param Buf the buffer to get the input to
4647  * @param fd pointer to the filedescriptor to read
4648  * @param append Append to an existing string or replace?
4649  * @param nBytes the maximal number of bytes to read
4650  * @param Error strerror() on error 
4651  * @returns numbers of chars read
4652  */
4653 int StrBufReadBLOB(StrBuf *Buf, int *fd, int append, long nBytes, const char **Error)
4654 {
4655         int fdflags;
4656         int rlen;
4657         int nSuccessLess;
4658         int nRead = 0;
4659         char *ptr;
4660         int IsNonBlock;
4661         struct timeval tv;
4662         fd_set rfds;
4663
4664         if ((Buf == NULL) || (Buf->buf == NULL) || (*fd == -1))
4665         {
4666                 *Error = ErrRBLF_BLOBPreConditionFailed;
4667                 return -1;
4668         }
4669         if (!append)
4670                 FlushStrBuf(Buf);
4671         if (Buf->BufUsed + nBytes >= Buf->BufSize)
4672                 IncreaseBuf(Buf, 1, Buf->BufUsed + nBytes);
4673
4674         ptr = Buf->buf + Buf->BufUsed;
4675
4676         fdflags = fcntl(*fd, F_GETFL);
4677         IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4678         nSuccessLess = 0;
4679         while ((nRead < nBytes) && 
4680                (*fd != -1)) 
4681         {
4682                 if (IsNonBlock)
4683                 {
4684                         tv.tv_sec = 1;
4685                         tv.tv_usec = 0;
4686                 
4687                         FD_ZERO(&rfds);
4688                         FD_SET(*fd, &rfds);
4689                         if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
4690                                 *Error = strerror(errno);
4691                                 close (*fd);
4692                                 *fd = -1;
4693                                 if (*Error == NULL)
4694                                         *Error = ErrRBLF_SelectFailed;
4695                                 return -1;
4696                         }
4697                         if (! FD_ISSET(*fd, &rfds) != 0) {
4698                                 nSuccessLess ++;
4699                                 continue;
4700                         }
4701                 }
4702
4703                 if ((rlen = read(*fd, 
4704                                  ptr,
4705                                  nBytes - nRead)) == -1) {
4706                         close(*fd);
4707                         *fd = -1;
4708                         *Error = strerror(errno);
4709                         return rlen;
4710                 }
4711                 nRead += rlen;
4712                 ptr += rlen;
4713                 Buf->BufUsed += rlen;
4714         }
4715         Buf->buf[Buf->BufUsed] = '\0';
4716         return nRead;
4717 }
4718
4719 const char *ErrRBB_BLOBFPreConditionFailed = "StrBufReadBLOBBuffered: to many selects; aborting.";
4720 const char *ErrRBB_too_many_selects        = "StrBufReadBLOBBuffered: to many selects; aborting.";
4721 /**
4722  * @ingroup StrBuf_BufferedIO
4723  * @brief Input binary data from socket
4724  * flushes and closes the FD on error
4725  * @param Blob put binary thing here
4726  * @param IOBuf the buffer to get the input to
4727  * @param Pos offset inside of IOBuf
4728  * @param fd pointer to the filedescriptor to read
4729  * @param append Append to an existing string or replace?
4730  * @param nBytes the maximal number of bytes to read
4731  * @param check whether we should search for '000\n' terminators in case of timeouts
4732  * @param Error strerror() on error 
4733  * @returns numbers of chars read
4734  */
4735 int StrBufReadBLOBBuffered(StrBuf *Blob, 
4736                            StrBuf *IOBuf, 
4737                            const char **Pos,
4738                            int *fd, 
4739                            int append, 
4740                            long nBytes, 
4741                            int check, 
4742                            const char **Error)
4743 {
4744         const char *pos;
4745         int fdflags;
4746         int rlen = 0;
4747         int nRead = 0;
4748         int nAlreadyRead = 0;
4749         int IsNonBlock;
4750         char *ptr;
4751         fd_set rfds;
4752         struct timeval tv;
4753         int nSuccessLess = 0;
4754         int MaxTries;
4755
4756         if ((Blob == NULL)  ||
4757             (*fd == -1)     ||
4758             (IOBuf == NULL) ||
4759             (Pos == NULL))
4760         {
4761                 if (Pos != NULL)
4762                         *Pos = NULL;
4763                 *Error = ErrRBB_BLOBFPreConditionFailed;
4764                 return -1;
4765         }
4766
4767         if (!append)
4768                 FlushStrBuf(Blob);
4769         if (Blob->BufUsed + nBytes >= Blob->BufSize) 
4770                 IncreaseBuf(Blob, append, Blob->BufUsed + nBytes);
4771         
4772         pos = *Pos;
4773
4774         if (pos != NULL)
4775                 rlen = pos - IOBuf->buf;
4776         rlen = IOBuf->BufUsed - rlen;
4777
4778
4779         if ((IOBuf->BufUsed > 0) && 
4780             (pos != NULL) && 
4781             (pos < IOBuf->buf + IOBuf->BufUsed)) 
4782         {
4783                 if (rlen < nBytes) {
4784                         memcpy(Blob->buf + Blob->BufUsed, pos, rlen);
4785                         Blob->BufUsed += rlen;
4786                         Blob->buf[Blob->BufUsed] = '\0';
4787                         nAlreadyRead = nRead = rlen;
4788                         *Pos = NULL; 
4789                 }
4790                 if (rlen >= nBytes) {
4791                         memcpy(Blob->buf + Blob->BufUsed, pos, nBytes);
4792                         Blob->BufUsed += nBytes;
4793                         Blob->buf[Blob->BufUsed] = '\0';
4794                         if (rlen == nBytes) {
4795                                 *Pos = NULL; 
4796                                 FlushStrBuf(IOBuf);
4797                         }
4798                         else 
4799                                 *Pos += nBytes;
4800                         return nBytes;
4801                 }
4802         }
4803
4804         FlushStrBuf(IOBuf);
4805         *Pos = NULL;
4806         if (IOBuf->BufSize < nBytes - nRead)
4807                 IncreaseBuf(IOBuf, 0, nBytes - nRead);
4808         ptr = IOBuf->buf;
4809
4810         fdflags = fcntl(*fd, F_GETFL);
4811         IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4812         if (IsNonBlock)
4813                 MaxTries =   1000;
4814         else
4815                 MaxTries = 100000;
4816
4817         nBytes -= nRead;
4818         nRead = 0;
4819         while ((nSuccessLess < MaxTries) && 
4820                (nRead < nBytes) &&
4821                (*fd != -1)) {
4822                 if (IsNonBlock)
4823                 {
4824                         tv.tv_sec = 1;
4825                         tv.tv_usec = 0;
4826                 
4827                         FD_ZERO(&rfds);
4828                         FD_SET(*fd, &rfds);
4829                         if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
4830                                 *Error = strerror(errno);
4831                                 close (*fd);
4832                                 *fd = -1;
4833                                 if (*Error == NULL)
4834                                         *Error = ErrRBLF_SelectFailed;
4835                                 return -1;
4836                         }
4837                         if (! FD_ISSET(*fd, &rfds) != 0) {
4838                                 nSuccessLess ++;
4839                                 continue;
4840                         }
4841                 }
4842                 rlen = read(*fd, 
4843                             ptr,
4844                             IOBuf->BufSize - (ptr - IOBuf->buf));
4845                 if (rlen == -1) {
4846                         close(*fd);
4847                         *fd = -1;
4848                         *Error = strerror(errno);
4849                         return rlen;
4850                 }
4851                 else if (rlen == 0){
4852                         if ((check == NNN_TERM) && 
4853                             (nRead > 5) &&
4854                             (strncmp(IOBuf->buf + IOBuf->BufUsed - 5, "\n000\n", 5) == 0)) 
4855                         {
4856                                 StrBufPlain(Blob, HKEY("\n000\n"));
4857                                 StrBufCutRight(Blob, 5);
4858                                 return Blob->BufUsed;
4859                         }
4860                         else if (!IsNonBlock) 
4861                                 nSuccessLess ++;
4862                         else if (nSuccessLess > MaxTries) {
4863                                 FlushStrBuf(IOBuf);
4864                                 *Error = ErrRBB_too_many_selects;
4865                                 return -1;
4866                         }
4867                 }
4868                 else if (rlen > 0) {
4869                         nSuccessLess = 0;
4870                         nRead += rlen;
4871                         ptr += rlen;
4872                         IOBuf->BufUsed += rlen;
4873                 }
4874         }
4875         if (nSuccessLess >= MaxTries) {
4876                 FlushStrBuf(IOBuf);
4877                 *Error = ErrRBB_too_many_selects;
4878                 return -1;
4879         }
4880
4881         if (nRead > nBytes) {
4882                 *Pos = IOBuf->buf + nBytes;
4883         }
4884         Blob->buf[Blob->BufUsed] = '\0';
4885         StrBufAppendBufPlain(Blob, IOBuf->buf, nBytes, 0);
4886         if (*Pos == NULL) {
4887                 FlushStrBuf(IOBuf);
4888         }
4889         return nRead + nAlreadyRead;
4890 }
4891
4892 /**
4893  * @ingroup StrBuf_IO
4894  * @brief extract a "next line" from Buf; Ptr to persist across several iterations
4895  * @param LineBuf your line will be copied here.
4896  * @param Buf BLOB with lines of text...
4897  * @param Ptr moved arround to keep the next-line across several iterations
4898  *        has to be &NULL on start; will be &NotNULL on end of buffer
4899  * @returns size of remaining buffer
4900  */
4901 int StrBufSipLine(StrBuf *LineBuf, const StrBuf *Buf, const char **Ptr)
4902 {
4903         const char *aptr, *ptr, *eptr;
4904         char *optr, *xptr;
4905
4906         if ((Buf == NULL) ||
4907             (*Ptr == StrBufNOTNULL) ||
4908             (LineBuf == NULL)||
4909             (LineBuf->buf == NULL))
4910         {
4911                 *Ptr = StrBufNOTNULL;
4912                 return 0;
4913         }
4914
4915         FlushStrBuf(LineBuf);
4916         if (*Ptr==NULL)
4917                 ptr = aptr = Buf->buf;
4918         else
4919                 ptr = aptr = *Ptr;
4920
4921         optr = LineBuf->buf;
4922         eptr = Buf->buf + Buf->BufUsed;
4923         xptr = LineBuf->buf + LineBuf->BufSize - 1;
4924
4925         while ((ptr <= eptr) && 
4926                (*ptr != '\n') &&
4927                (*ptr != '\r') )
4928         {
4929                 *optr = *ptr;
4930                 optr++; ptr++;
4931                 if (optr == xptr) {
4932                         LineBuf->BufUsed = optr - LineBuf->buf;
4933                         IncreaseBuf(LineBuf,  1, LineBuf->BufUsed + 1);
4934                         optr = LineBuf->buf + LineBuf->BufUsed;
4935                         xptr = LineBuf->buf + LineBuf->BufSize - 1;
4936                 }
4937         }
4938
4939         if ((ptr >= eptr) && (optr > LineBuf->buf))
4940                 optr --;
4941         LineBuf->BufUsed = optr - LineBuf->buf;
4942         *optr = '\0';       
4943         if ((ptr <= eptr) && (*ptr == '\r'))
4944                 ptr ++;
4945         if ((ptr <= eptr) && (*ptr == '\n'))
4946                 ptr ++;
4947         
4948         if (ptr < eptr) {
4949                 *Ptr = ptr;
4950         }
4951         else {
4952                 *Ptr = StrBufNOTNULL;
4953         }
4954
4955         return Buf->BufUsed - (ptr - Buf->buf);
4956 }
4957
4958
4959 /**
4960  * @ingroup StrBuf_IO
4961  * @brief removes double slashes from pathnames
4962  * @param Dir directory string to filter
4963  * @param RemoveTrailingSlash allows / disallows trailing slashes
4964  */
4965 void StrBufStripSlashes(StrBuf *Dir, int RemoveTrailingSlash)
4966 {
4967         char *a, *b;
4968
4969         a = b = Dir->buf;
4970
4971         while (!IsEmptyStr(a)) {
4972                 if (*a == '/') {
4973                         while (*a == '/')
4974                                 a++;
4975                         *b = '/';
4976                         b++;
4977                 }
4978                 else {
4979                         *b = *a;
4980                         b++; a++;
4981                 }
4982         }
4983         if ((RemoveTrailingSlash) &&
4984             (b > Dir->buf) && 
4985             (*(b - 1) == '/')){
4986                 b--;
4987         }
4988         *b = '\0';
4989         Dir->BufUsed = b - Dir->buf;
4990 }
4991
4992