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