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