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