9ebc2dd593616ef94b2c9f94cd46563e8d188c87
[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 = 4*SIZ; /// TODO 64
2883                 stream->OutBuf.buf = (char*)malloc(stream->OutBuf.BufSize);
2884
2885                 err = inflateInit(&stream->zstream);
2886
2887                 if (err != Z_OK)
2888                         return NULL;/// tODO cleanup
2889                 return stream;
2890
2891         }
2892         case eZLibEncode:
2893         {
2894                 z_enc_stream *stream;
2895                 int err;
2896
2897                 stream = (z_enc_stream *) malloc(sizeof(z_enc_stream));
2898                 memset(stream, 0, sizeof(z_enc_stream));
2899                 stream->OutBuf.BufSize = 4*SIZ; /// todo 64
2900                 stream->OutBuf.buf = (char*)malloc(stream->OutBuf.BufSize);
2901                 /* write gzip header */
2902                 stream->OutBuf.BufUsed = snprintf
2903                         (stream->OutBuf.buf,
2904                          stream->OutBuf.BufSize, 
2905                          "%c%c%c%c%c%c%c%c%c%c",
2906                          gz_magic[0], gz_magic[1], Z_DEFLATED,
2907                          0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /* xflags */ ,
2908                          OS_CODE);
2909
2910                 err = deflateInit2(&stream->zstream,
2911                                    ZLibCompressionRatio,
2912                                    Z_DEFLATED,
2913                                    -MAX_WBITS,
2914                                    DEF_MEM_LEVEL,
2915                                    Z_DEFAULT_STRATEGY);
2916                 if (err != Z_OK)
2917                         return NULL;/// tODO cleanup
2918                 return stream;
2919         }
2920         case eEmtyCodec:
2921                 /// TODO
2922
2923                 break;
2924         }
2925         return NULL;
2926 }
2927
2928 void StrBufDestroyStreamContext(eStreamType type, void **vStream)
2929 {
2930         switch (type)
2931         {
2932         case eBase64Encode:
2933         case eBase64Decode:
2934                 free(*vStream);
2935                 *vStream = NULL;
2936                 break;
2937         case eZLibDecode:
2938         {
2939                 z_enc_stream *stream = (z_enc_stream *)*vStream;
2940                 (void)inflateEnd(&stream->zstream);
2941                 free(stream->OutBuf.buf);
2942                 free(stream);
2943         }
2944         case eZLibEncode:
2945         {
2946                 z_enc_stream *stream = (z_enc_stream *)*vStream;
2947                 free(stream->OutBuf.buf);
2948                 free(stream);
2949 // todo more?
2950                 *vStream = NULL;
2951                 break;
2952         }
2953         case eEmtyCodec: 
2954                 break; /// TODO
2955         }
2956 }
2957
2958 void StrBufStreamTranscode(eStreamType type, IOBuffer *Target, IOBuffer *In, const char* pIn, long pInLen, void *vStream, int LastChunk)
2959 {
2960
2961         switch (type)
2962         {
2963         case eBase64Encode:
2964         {
2965                 /// TODO
2966 /*
2967                 // base64_decodestate *state = (base64_decodestate*)vStream;
2968                 long ExpectLen;
2969                 
2970                 if (In != NULL)
2971                 {
2972                         pIn = In->buf;
2973                         pInLen = In->BufUsed;
2974                 }
2975                 if ((In == NULL) || (vStream == NULL))
2976                         return;
2977                 
2978                 ExpectLen = (pInLen / 4) * 3;
2979
2980                 if (Target->BufSize - Target->BufUsed < ExpectLen)
2981                 {
2982                         IncreaseBuf(Target, 1, Target->BufUsed + ExpectLen + 1);
2983                 }
2984
2985                 ////    ExpectLen = base64_encode_block(pIn, pInLen, Target->buf + Target->BufUsed, state);
2986                 Target->BufUsed += ExpectLen;
2987                 Target->buf[Target->BufUsed] = '\0';
2988 */
2989         }
2990         break;
2991         case eBase64Decode:
2992         {
2993 /*
2994                 base64_decodestate *state = (base64_decodestate *)vStream;
2995                 long ExpectLen;
2996                 
2997                 if (In != NULL)
2998                 {
2999                         pIn = In->buf;
3000                         pInLen = In->BufUsed;
3001                 }
3002                 if ((pIn == NULL) || (vStream == NULL))
3003                         return;
3004                 
3005                 ExpectLen = (pInLen / 4) * 3;
3006
3007                 if (Target->BufSize - Target->BufUsed < ExpectLen)
3008                 {
3009                         IncreaseBuf(Target, 1, Target->BufUsed + ExpectLen + 1);
3010                 }
3011
3012                 ExpectLen = base64_decode_block(pIn, pInLen, Target->buf + Target->BufUsed, state);
3013                 Target->BufUsed += ExpectLen;
3014                 Target->buf[Target->BufUsed] = '\0';
3015 */
3016         }
3017         break;
3018         case eZLibEncode:
3019         {
3020                 z_enc_stream *stream = (z_enc_stream *)vStream;
3021                 int org_outbuf_len = stream->zstream.total_out;
3022                 int err;
3023                 unsigned int chunkavail;
3024
3025                 if (In->ReadWritePointer != NULL)
3026                 {
3027                         stream->zstream.next_in = (Bytef *) In->ReadWritePointer;
3028                         stream->zstream.avail_in = (uInt) In->Buf->BufUsed - 
3029                                 (In->ReadWritePointer - In->Buf->buf);
3030                 }
3031                 else
3032                 {
3033                         stream->zstream.next_in = (Bytef *) In->Buf->buf;
3034                         stream->zstream.avail_in = (uInt) In->Buf->BufUsed;
3035                 }
3036                 
3037                 stream->zstream.next_out = (unsigned char*)stream->OutBuf.buf + stream->OutBuf.BufUsed;
3038                 stream->zstream.avail_out = chunkavail = (uInt) stream->OutBuf.BufSize - stream->OutBuf.BufUsed;
3039
3040                 err = deflate(&stream->zstream,  (LastChunk) ? Z_FINISH : Z_NO_FLUSH);
3041
3042                 stream->OutBuf.BufUsed += (chunkavail - stream->zstream.avail_out);
3043                 /// todo + org_outbuf_len;
3044
3045                 if (Target) SwapBuffers(Target->Buf, &stream->OutBuf);
3046
3047                 if (stream->zstream.avail_in == 0)
3048                 {
3049                         FlushStrBuf(In->Buf);
3050                         In->ReadWritePointer = NULL;
3051                 }
3052                 else
3053                 {
3054                         if (stream->zstream.avail_in < 64)
3055                         {
3056                                 memmove(In->Buf->buf,
3057                                         In->Buf->buf + In->Buf->BufUsed - stream->zstream.avail_in,
3058                                         stream->zstream.avail_in);
3059
3060                                 In->Buf->BufUsed = stream->zstream.avail_in;
3061                                 In->Buf->buf[In->Buf->BufUsed] = '\0';
3062                         }
3063                         else
3064                         {
3065                                 
3066                                 In->ReadWritePointer = In->Buf->buf + 
3067                                         (In->Buf->BufUsed - stream->zstream.avail_in);
3068                         }
3069                 }
3070
3071         }
3072         break;
3073         case eZLibDecode: {
3074                 z_enc_stream *stream = (z_enc_stream *)vStream;
3075                 int org_outbuf_len = stream->zstream.total_out;
3076                 int err;
3077
3078                 if ((stream->zstream.avail_out != 0) && (stream->zstream.next_in != NULL)) {
3079                         if (In->ReadWritePointer != NULL)
3080                         {
3081                                 stream->zstream.next_in = (Bytef *) In->ReadWritePointer;
3082                                 stream->zstream.avail_in = (uInt) In->Buf->BufUsed - 
3083                                         (In->ReadWritePointer - In->Buf->buf);
3084                         }
3085                         else
3086                         {
3087                                 stream->zstream.next_in = (Bytef *) In->Buf->buf;
3088                                 stream->zstream.avail_in = (uInt) In->Buf->BufUsed;
3089                         }
3090                 }
3091
3092                 stream->zstream.next_out = (unsigned char*)stream->OutBuf.buf + stream->OutBuf.BufUsed;
3093                 stream->zstream.avail_out = (uInt) stream->OutBuf.BufSize - stream->OutBuf.BufUsed;
3094
3095                 err = inflate(&stream->zstream, Z_NO_FLUSH);
3096
3097                 ///assert(ret != Z_STREAM_ERROR);  /* state not clobbered * /
3098                 switch (err) {
3099                 case Z_NEED_DICT:
3100                         err = Z_DATA_ERROR;     /* and fall through */
3101
3102                 case Z_DATA_ERROR:
3103                         fprintf(stderr, "sanoteuh\n");
3104                 case Z_MEM_ERROR:
3105                         (void)inflateEnd(&stream->zstream);
3106                         return err;
3107                 }
3108
3109                 stream->OutBuf.BufUsed += stream->zstream.total_out + org_outbuf_len;
3110
3111                 if (Target) SwapBuffers(Target->Buf, &stream->OutBuf);
3112
3113                 if (stream->zstream.avail_in == 0)
3114                 {
3115                         FlushStrBuf(In->Buf);
3116                         In->ReadWritePointer = NULL;
3117                 }
3118                 else
3119                 {
3120                         if (stream->zstream.avail_in < 64)
3121                         {
3122                                 memmove(In->Buf->buf,
3123                                         In->Buf->buf + In->Buf->BufUsed - stream->zstream.avail_in,
3124                                         stream->zstream.avail_in);
3125
3126                                 In->Buf->BufUsed = stream->zstream.avail_in;
3127                                 In->Buf->buf[In->Buf->BufUsed] = '\0';
3128                         }
3129                         else
3130                         {
3131                                 
3132                                 In->ReadWritePointer = In->Buf->buf + 
3133                                         (In->Buf->BufUsed - stream->zstream.avail_in);
3134                         }
3135                 }
3136         }
3137                 break;
3138         case eEmtyCodec: {
3139
3140         }
3141                 break; /// TODO
3142         }
3143 }
3144
3145 /**
3146  * @ingroup StrBuf_DeEnCoder
3147  * @brief decode a buffer from base 64 encoding; destroys original
3148  * @param Buf Buffor to transform
3149  */
3150 int StrBufDecodeHex(StrBuf *Buf)
3151 {
3152         unsigned int ch;
3153         char *pch, *pche, *pchi;
3154
3155         if (Buf == NULL) return -1;
3156
3157         pch = pchi = Buf->buf;
3158         pche = pch + Buf->BufUsed;
3159
3160         while (pchi < pche){
3161                 ch = decode_hex(pchi);
3162                 *pch = ch;
3163                 pch ++;
3164                 pchi += 2;
3165         }
3166
3167         *pch = '\0';
3168         Buf->BufUsed = pch - Buf->buf;
3169         return Buf->BufUsed;
3170 }
3171
3172 /**
3173  * @ingroup StrBuf_DeEnCoder
3174  * @brief replace all chars >0x20 && < 0x7F with Mute
3175  * @param Mute char to put over invalid chars
3176  * @param Buf Buffor to transform
3177  */
3178 int StrBufSanitizeAscii(StrBuf *Buf, const char Mute)
3179 {
3180         unsigned char *pch;
3181
3182         if (Buf == NULL) return -1;
3183         pch = (unsigned char *)Buf->buf;
3184         while (pch < (unsigned char *)Buf->buf + Buf->BufUsed) {
3185                 if ((*pch < 0x20) || (*pch > 0x7F))
3186                         *pch = Mute;
3187                 pch ++;
3188         }
3189         return Buf->BufUsed;
3190 }
3191
3192
3193 /**
3194  * @ingroup StrBuf_DeEnCoder
3195  * @brief remove escaped strings from i.e. the url string (like %20 for blanks)
3196  * @param Buf Buffer to translate
3197  * @param StripBlanks Reduce several blanks to one?
3198  */
3199 long StrBufUnescape(StrBuf *Buf, int StripBlanks)
3200 {
3201         int a, b;
3202         char hex[3];
3203         long len;
3204
3205         if (Buf == NULL)
3206                 return -1;
3207
3208         while ((Buf->BufUsed > 0) && (isspace(Buf->buf[Buf->BufUsed - 1]))){
3209                 Buf->buf[Buf->BufUsed - 1] = '\0';
3210                 Buf->BufUsed --;
3211         }
3212
3213         a = 0; 
3214         while (a < Buf->BufUsed) {
3215                 if (Buf->buf[a] == '+')
3216                         Buf->buf[a] = ' ';
3217                 else if (Buf->buf[a] == '%') {
3218                         /* don't let % chars through, rather truncate the input. */
3219                         if (a + 2 > Buf->BufUsed) {
3220                                 Buf->buf[a] = '\0';
3221                                 Buf->BufUsed = a;
3222                         }
3223                         else {                  
3224                                 hex[0] = Buf->buf[a + 1];
3225                                 hex[1] = Buf->buf[a + 2];
3226                                 hex[2] = 0;
3227                                 b = 0;
3228                                 sscanf(hex, "%02x", &b);
3229                                 Buf->buf[a] = (char) b;
3230                                 len = Buf->BufUsed - a - 2;
3231                                 if (len > 0)
3232                                         memmove(&Buf->buf[a + 1], &Buf->buf[a + 3], len);
3233                         
3234                                 Buf->BufUsed -=2;
3235                         }
3236                 }
3237                 a++;
3238         }
3239         return a;
3240 }
3241
3242
3243 /**
3244  * @ingroup StrBuf_DeEnCoder
3245  * @brief       RFC2047-encode a header field if necessary.
3246  *              If no non-ASCII characters are found, the string
3247  *              will be copied verbatim without encoding.
3248  *
3249  * @param       target          Target buffer.
3250  * @param       source          Source string to be encoded.
3251  * @returns     encoded length; -1 if non success.
3252  */
3253 int StrBufRFC2047encode(StrBuf **target, const StrBuf *source)
3254 {
3255         const char headerStr[] = "=?UTF-8?Q?";
3256         int need_to_encode = 0;
3257         int i = 0;
3258         unsigned char ch;
3259
3260         if ((source == NULL) || 
3261             (target == NULL))
3262             return -1;
3263
3264         while ((i < source->BufUsed) &&
3265                (!IsEmptyStr (&source->buf[i])) &&
3266                (need_to_encode == 0)) {
3267                 if (((unsigned char) source->buf[i] < 32) || 
3268                     ((unsigned char) source->buf[i] > 126)) {
3269                         need_to_encode = 1;
3270                 }
3271                 i++;
3272         }
3273
3274         if (!need_to_encode) {
3275                 if (*target == NULL) {
3276                         *target = NewStrBufPlain(source->buf, source->BufUsed);
3277                 }
3278                 else {
3279                         FlushStrBuf(*target);
3280                         StrBufAppendBuf(*target, source, 0);
3281                 }
3282                 if (*target != 0)
3283                         return (*target)->BufUsed;
3284                 else
3285                         return 0;
3286         }
3287         if (*target == NULL)
3288                 *target = NewStrBufPlain(NULL, sizeof(headerStr) + source->BufUsed * 2);
3289         else if (sizeof(headerStr) + source->BufUsed >= (*target)->BufSize)
3290                 IncreaseBuf(*target, sizeof(headerStr) + source->BufUsed, 0);
3291         memcpy ((*target)->buf, headerStr, sizeof(headerStr) - 1);
3292         (*target)->BufUsed = sizeof(headerStr) - 1;
3293         for (i=0; (i < source->BufUsed); ++i) {
3294                 if ((*target)->BufUsed + 4 >= (*target)->BufSize)
3295                         IncreaseBuf(*target, 1, 0);
3296                 ch = (unsigned char) source->buf[i];
3297                 if ((ch  <  32) || 
3298                     (ch  > 126) || 
3299                     (ch == '=') ||
3300                     (ch == '?') ||
3301                     (ch == '_') ||
3302                     (ch == '[') ||
3303                     (ch == ']')   )
3304                 {
3305                         sprintf(&(*target)->buf[(*target)->BufUsed], "=%02X", ch);
3306                         (*target)->BufUsed += 3;
3307                 }
3308                 else {
3309                         if (ch == ' ')
3310                                 (*target)->buf[(*target)->BufUsed] = '_';
3311                         else
3312                                 (*target)->buf[(*target)->BufUsed] = ch;
3313                         (*target)->BufUsed++;
3314                 }
3315         }
3316         
3317         if ((*target)->BufUsed + 4 >= (*target)->BufSize)
3318                 IncreaseBuf(*target, 1, 0);
3319
3320         (*target)->buf[(*target)->BufUsed++] = '?';
3321         (*target)->buf[(*target)->BufUsed++] = '=';
3322         (*target)->buf[(*target)->BufUsed] = '\0';
3323         return (*target)->BufUsed;;
3324 }
3325
3326 /**
3327  * @ingroup StrBuf_DeEnCoder
3328  * @brief       Quoted-Printable encode a message; make it < 80 columns width.
3329  * @param       source          Source string to be encoded.
3330  * @returns     buffer with encoded message.
3331  */
3332 StrBuf *StrBufRFC2047encodeMessage(const StrBuf *EncodeMe)
3333 {
3334         StrBuf *OutBuf;
3335         char *Optr, *OEptr;
3336         const char *ptr, *eptr;
3337         unsigned char ch;
3338         int LinePos;
3339
3340         OutBuf = NewStrBufPlain(NULL, StrLength(EncodeMe) * 4);
3341         Optr = OutBuf->buf;
3342         OEptr = OutBuf->buf + OutBuf->BufSize;
3343         ptr = EncodeMe->buf;
3344         eptr = EncodeMe->buf + EncodeMe->BufUsed;
3345         LinePos = 0;
3346
3347         while (ptr < eptr)
3348         {
3349                 if (Optr + 4 >= OEptr)
3350                 {
3351                         long Offset;
3352                         Offset = Optr - OutBuf->buf;
3353                         OutBuf->BufUsed = Optr - OutBuf->buf;
3354                         IncreaseBuf(OutBuf, 1, 0);
3355                         Optr = OutBuf->buf + Offset;
3356                         OEptr = OutBuf->buf + OutBuf->BufSize;
3357                 }
3358                 if (*ptr == '\r')
3359                 {
3360                         /* ignore carriage returns */
3361                         ptr ++;
3362                 }
3363                 else if (*ptr == '\n') {
3364                         /* hard line break */
3365                         memcpy(Optr, HKEY("=0A"));
3366                         Optr += 3;
3367                         LinePos += 3;
3368                         ptr ++;
3369                 }
3370                 else if (( (*ptr >= 32) && (*ptr <= 60) ) ||
3371                          ( (*ptr >= 62) && (*ptr <= 126) ))
3372                 {
3373                         *Optr = *ptr;
3374                         Optr ++;
3375                         ptr ++;
3376                         LinePos ++;
3377                 }
3378                 else {
3379                         ch = *ptr;
3380                         *Optr = '=';
3381                         Optr ++;
3382                         *Optr = HexList[ch][0];
3383                         Optr ++;
3384                         *Optr = HexList[ch][1];
3385                         Optr ++;
3386                         LinePos += 3;
3387                         ptr ++;
3388                 }
3389
3390                 if (LinePos > 72) {
3391                         /* soft line break */
3392                         if (isspace(*(Optr - 1))) {
3393                                 ch = *(Optr - 1);
3394                                 Optr --;
3395                                 *Optr = '=';
3396                                 Optr ++;
3397                                 *Optr = HexList[ch][0];
3398                                 Optr ++;
3399                                 *Optr = HexList[ch][1];
3400                                 Optr ++;
3401                                 LinePos += 3;
3402                         }
3403                         *Optr = '=';
3404                         Optr ++;
3405                         *Optr = '\n';
3406                         Optr ++;
3407                         LinePos = 0;
3408                 }
3409         }
3410         *Optr = '\0';
3411         OutBuf->BufUsed = Optr - OutBuf->buf;
3412
3413         return OutBuf;
3414 }
3415
3416
3417 static void AddRecipient(StrBuf *Target, 
3418                          StrBuf *UserName, 
3419                          StrBuf *EmailAddress, 
3420                          StrBuf *EncBuf)
3421 {
3422         int QuoteMe = 0;
3423
3424         if (StrLength(Target) > 0) StrBufAppendBufPlain(Target, HKEY(", "), 0);
3425         if (strchr(ChrPtr(UserName), ',') != NULL) QuoteMe = 1;
3426
3427         if (QuoteMe)  StrBufAppendBufPlain(Target, HKEY("\""), 0);
3428         StrBufRFC2047encode(&EncBuf, UserName);
3429         StrBufAppendBuf(Target, EncBuf, 0);
3430         if (QuoteMe)  StrBufAppendBufPlain(Target, HKEY("\" "), 0);
3431         else          StrBufAppendBufPlain(Target, HKEY(" "), 0);
3432
3433         if (StrLength(EmailAddress) > 0){
3434                 StrBufAppendBufPlain(Target, HKEY("<"), 0);
3435                 StrBufAppendBuf(Target, EmailAddress, 0); /* TODO: what about IDN???? */
3436                 StrBufAppendBufPlain(Target, HKEY(">"), 0);
3437         }
3438 }
3439
3440
3441 /**
3442  * \brief QP encode parts of an email TO/CC/BCC vector, and strip/filter invalid parts
3443  * \param Recp Source list of email recipients
3444  * \param UserName Temporary buffer for internal use; Please provide valid buffer.
3445  * \param EmailAddress Temporary buffer for internal use; Please provide valid buffer.
3446  * \param EncBuf Temporary buffer for internal use; Please provide valid buffer.
3447  * \returns encoded & sanitized buffer with the contents of Recp; Caller owns this memory.
3448  */
3449 StrBuf *StrBufSanitizeEmailRecipientVector(const StrBuf *Recp, 
3450                                            StrBuf *UserName, 
3451                                            StrBuf *EmailAddress,
3452                                            StrBuf *EncBuf)
3453 {
3454         StrBuf *Target;
3455         const char *pch, *pche;
3456         const char *UserStart, *UserEnd, *EmailStart, *EmailEnd, *At;
3457
3458         if ((Recp == NULL) || (StrLength(Recp) == 0))
3459                 return NULL;
3460
3461         pch = ChrPtr(Recp);
3462         pche = pch + StrLength(Recp);
3463
3464         if (!CheckEncode(pch, -1, pche))
3465                 return NewStrBufDup(Recp);
3466
3467         Target = NewStrBufPlain(NULL, StrLength(Recp));
3468
3469         while ((pch != NULL) && (pch < pche))
3470         {
3471                 while (isspace(*pch)) pch++;
3472                 UserEnd = EmailStart = EmailEnd = NULL;
3473                 
3474                 if ((*pch == '"') || (*pch == '\'')) {
3475                         UserStart = pch + 1;
3476                         
3477                         UserEnd = strchr(UserStart, *pch);
3478                         if (UserEnd == NULL) 
3479                                 break; ///TODO: Userfeedback??
3480                         EmailStart = UserEnd + 1;
3481                         while (isspace(*EmailStart))
3482                                 EmailStart++;
3483                         if (UserEnd == UserStart) {
3484                                 UserStart = UserEnd = NULL;
3485                         }
3486                         
3487                         if (*EmailStart == '<') {
3488                                 EmailStart++;
3489                                 EmailEnd = strchr(EmailStart, '>');
3490                                 if (EmailEnd == NULL)
3491                                         EmailEnd = strchr(EmailStart, ',');
3492                                 
3493                         }
3494                         else {
3495                                 EmailEnd = strchr(EmailStart, ',');
3496                         }
3497                         if (EmailEnd == NULL)
3498                                 EmailEnd = pche;
3499                         pch = EmailEnd + 1;
3500                 }
3501                 else {
3502                         int gt = 0;
3503                         UserStart = pch;
3504                         EmailEnd = strchr(UserStart, ',');
3505                         if (EmailEnd == NULL) {
3506                                 EmailEnd = strchr(pch, '>');
3507                                 pch = NULL;
3508                                 if (EmailEnd != NULL) {
3509                                         gt = 1;
3510                                 }
3511                                 else {
3512                                         EmailEnd = pche;
3513                                 }
3514                         }
3515                         else {
3516
3517                                 pch = EmailEnd + 1;
3518                                 while ((EmailEnd > UserStart) && !gt &&
3519                                        ((*EmailEnd == ',') ||
3520                                         (*EmailEnd == '>') ||
3521                                         (isspace(*EmailEnd))))
3522                                 {
3523                                         if (*EmailEnd == '>')
3524                                                 gt = 1;
3525                                         else 
3526                                                 EmailEnd--;
3527                                 }
3528                                 if (EmailEnd == UserStart)
3529                                         break;
3530                         }
3531                         if (gt) {
3532                                 EmailStart = strchr(UserStart, '<');
3533                                 if ((EmailStart == NULL) || (EmailStart > EmailEnd))
3534                                         break;
3535                                 UserEnd = EmailStart;
3536
3537                                 while ((UserEnd > UserStart) && 
3538                                        isspace (*(UserEnd - 1)))
3539                                         UserEnd --;
3540                                 EmailStart ++;
3541                                 if (UserStart >= UserEnd)
3542                                         UserStart = UserEnd = NULL;
3543                         }
3544                         else { /* this is a local recipient... no domain, just a realname */
3545                                 EmailStart = UserStart;
3546                                 At = strchr(EmailStart, '@');
3547                                 if (At == NULL) {
3548                                         UserEnd = EmailEnd;
3549                                         EmailEnd = NULL;
3550                                 }
3551                                 else {
3552                                         EmailStart = UserStart;
3553                                         UserStart = NULL;
3554                                 }
3555                         }
3556                 }
3557
3558                 if ((UserStart != NULL) && (UserEnd != NULL))
3559                         StrBufPlain(UserName, UserStart, UserEnd - UserStart);
3560                 else if ((UserStart != NULL) && (UserEnd == NULL))
3561                         StrBufPlain(UserName, UserStart, UserEnd - UserStart);
3562                 else
3563                         FlushStrBuf(UserName);
3564
3565                 if ((EmailStart != NULL) && (EmailEnd != NULL))
3566                         StrBufPlain(EmailAddress, EmailStart, EmailEnd - EmailStart);
3567                 else if ((EmailStart != NULL) && (EmailEnd == NULL))
3568                         StrBufPlain(EmailAddress, EmailStart, EmailEnd - pche);
3569                 else 
3570                         FlushStrBuf(EmailAddress);
3571
3572                 AddRecipient(Target, UserName, EmailAddress, EncBuf);
3573
3574                 if (pch == NULL)
3575                         break;
3576                 
3577                 if ((pch != NULL) && (*pch == ','))
3578                         pch ++;
3579                 if (pch != NULL) while (isspace(*pch))
3580                         pch ++;
3581         }
3582         return Target;
3583 }
3584
3585
3586 /**
3587  * @ingroup StrBuf
3588  * @brief replaces all occurances of 'search' by 'replace'
3589  * @param buf Buffer to modify
3590  * @param search character to search
3591  * @param replace character to replace search by
3592  */
3593 void StrBufReplaceChars(StrBuf *buf, char search, char replace)
3594 {
3595         long i;
3596         if (buf == NULL)
3597                 return;
3598         for (i=0; i<buf->BufUsed; i++)
3599                 if (buf->buf[i] == search)
3600                         buf->buf[i] = replace;
3601
3602 }
3603
3604 /**
3605  * @ingroup StrBuf
3606  * @brief removes all \\r s from the string, or replaces them with \n if its not a combination of both.
3607  * @param buf Buffer to modify
3608  */
3609 void StrBufToUnixLF(StrBuf *buf)
3610 {
3611         char *pche, *pchS, *pchT;
3612         if (buf == NULL)
3613                 return;
3614
3615         pche = buf->buf + buf->BufUsed;
3616         pchS = pchT = buf->buf;
3617         while (pchS < pche)
3618         {
3619                 if (*pchS == '\r')
3620                 {
3621                         pchS ++;
3622                         if (*pchS != '\n') {
3623                                 *pchT = '\n';
3624                                 pchT++;
3625                         }
3626                 }
3627                 *pchT = *pchS;
3628                 pchT++; pchS++;
3629         }
3630         *pchT = '\0';
3631         buf->BufUsed = pchT - buf->buf;
3632 }
3633
3634
3635 /*******************************************************************************
3636  *                 Iconv Wrapper; RFC822 de/encoding                           *
3637  *******************************************************************************/
3638
3639 /**
3640  * @ingroup StrBuf_DeEnCoder
3641  * @brief Wrapper around iconv_open()
3642  * Our version adds aliases for non-standard Microsoft charsets
3643  * such as 'MS950', aliasing them to names like 'CP950'
3644  *
3645  * @param tocode        Target encoding
3646  * @param fromcode      Source encoding
3647  * @param pic           anonimized pointer to iconv struct
3648  */
3649 void  ctdl_iconv_open(const char *tocode, const char *fromcode, void *pic)
3650 {
3651 #ifdef HAVE_ICONV
3652         iconv_t ic = (iconv_t)(-1) ;
3653         ic = iconv_open(tocode, fromcode);
3654         if (ic == (iconv_t)(-1) ) {
3655                 char alias_fromcode[64];
3656                 if ( (strlen(fromcode) == 5) && (!strncasecmp(fromcode, "MS", 2)) ) {
3657                         safestrncpy(alias_fromcode, fromcode, sizeof alias_fromcode);
3658                         alias_fromcode[0] = 'C';
3659                         alias_fromcode[1] = 'P';
3660                         ic = iconv_open(tocode, alias_fromcode);
3661                 }
3662         }
3663         *(iconv_t *)pic = ic;
3664 #endif
3665 }
3666
3667
3668 /**
3669  * @ingroup StrBuf_DeEnCoder
3670  * @brief find one chunk of a RFC822 encoded string
3671  * @param Buffer where to search
3672  * @param bptr where to start searching
3673  * @returns found position, NULL if none.
3674  */
3675 static inline const char *FindNextEnd (const StrBuf *Buf, const char *bptr)
3676 {
3677         const char * end;
3678         /* Find the next ?Q? */
3679         if (Buf->BufUsed - (bptr - Buf->buf)  < 6)
3680                 return NULL;
3681
3682         end = strchr(bptr + 2, '?');
3683
3684         if (end == NULL)
3685                 return NULL;
3686
3687         if ((Buf->BufUsed - (end - Buf->buf) > 3) &&
3688             (((*(end + 1) == 'B') || (*(end + 1) == 'Q')) ||
3689              ((*(end + 1) == 'b') || (*(end + 1) == 'q'))) && 
3690             (*(end + 2) == '?')) {
3691                 /* skip on to the end of the cluster, the next ?= */
3692                 end = strstr(end + 3, "?=");
3693         }
3694         else
3695                 /* sort of half valid encoding, try to find an end. */
3696                 end = strstr(bptr, "?=");
3697         return end;
3698 }
3699
3700
3701
3702 /**
3703  * @ingroup StrBuf_DeEnCoder
3704  * @brief convert one buffer according to the preselected iconv pointer PIC
3705  * @param ConvertBuf buffer we need to translate
3706  * @param TmpBuf To share a workbuffer over several iterations. prepare to have it filled with useless stuff afterwards.
3707  * @param pic Pointer to the iconv-session Object
3708  */
3709 void StrBufConvert(StrBuf *ConvertBuf, StrBuf *TmpBuf, void *pic)
3710 {
3711 #ifdef HAVE_ICONV
3712         long trycount = 0;
3713         size_t siz;
3714         iconv_t ic;
3715         char *ibuf;                     /**< Buffer of characters to be converted */
3716         char *obuf;                     /**< Buffer for converted characters */
3717         size_t ibuflen;                 /**< Length of input buffer */
3718         size_t obuflen;                 /**< Length of output buffer */
3719
3720
3721         if ((ConvertBuf == NULL) || (TmpBuf == NULL))
3722                 return;
3723
3724         /* since we're converting to utf-8, one glyph may take up to 6 bytes */
3725         if (ConvertBuf->BufUsed * 6 >= TmpBuf->BufSize)
3726                 IncreaseBuf(TmpBuf, 0, ConvertBuf->BufUsed * 6);
3727 TRYAGAIN:
3728         ic = *(iconv_t*)pic;
3729         ibuf = ConvertBuf->buf;
3730         ibuflen = ConvertBuf->BufUsed;
3731         obuf = TmpBuf->buf;
3732         obuflen = TmpBuf->BufSize;
3733         
3734         siz = iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
3735
3736         if (siz < 0) {
3737                 if (errno == E2BIG) {
3738                         trycount ++;                    
3739                         IncreaseBuf(TmpBuf, 0, 0);
3740                         if (trycount < 5) 
3741                                 goto TRYAGAIN;
3742
3743                 }
3744                 else if (errno == EILSEQ){ 
3745                         /* hm, invalid utf8 sequence... what to do now? */
3746                         /* An invalid multibyte sequence has been encountered in the input */
3747                 }
3748                 else if (errno == EINVAL) {
3749                         /* An incomplete multibyte sequence has been encountered in the input. */
3750                 }
3751
3752                 FlushStrBuf(TmpBuf);
3753         }
3754         else {
3755                 TmpBuf->BufUsed = TmpBuf->BufSize - obuflen;
3756                 TmpBuf->buf[TmpBuf->BufUsed] = '\0';
3757                 
3758                 /* little card game: wheres the red lady? */
3759                 SwapBuffers(ConvertBuf, TmpBuf);
3760                 FlushStrBuf(TmpBuf);
3761         }
3762 #endif
3763 }
3764
3765
3766 /**
3767  * @ingroup StrBuf_DeEnCoder
3768  * @brief catches one RFC822 encoded segment, and decodes it.
3769  * @param Target buffer to fill with result
3770  * @param DecodeMe buffer with stuff to process
3771  * @param SegmentStart points to our current segment in DecodeMe
3772  * @param SegmentEnd Points to the end of our current segment in DecodeMe
3773  * @param ConvertBuf Workbuffer shared between several iterations. Random content; needs to be valid
3774  * @param ConvertBuf2 Workbuffer shared between several iterations. Random content; needs to be valid
3775  * @param FoundCharset Characterset to default decoding to; if we find another we will overwrite it.
3776  */
3777 inline static void DecodeSegment(StrBuf *Target, 
3778                                  const StrBuf *DecodeMe, 
3779                                  const char *SegmentStart, 
3780                                  const char *SegmentEnd, 
3781                                  StrBuf *ConvertBuf,
3782                                  StrBuf *ConvertBuf2, 
3783                                  StrBuf *FoundCharset)
3784 {
3785         StrBuf StaticBuf;
3786         char charset[128];
3787         char encoding[16];
3788 #ifdef HAVE_ICONV
3789         iconv_t ic = (iconv_t)(-1);
3790 #else
3791         void *ic = NULL;
3792 #endif
3793         /* Now we handle foreign character sets properly encoded
3794          * in RFC2047 format.
3795          */
3796         StaticBuf.buf = (char*) SegmentStart; /*< it will just be read there... */
3797         StaticBuf.BufUsed = SegmentEnd - SegmentStart;
3798         StaticBuf.BufSize = DecodeMe->BufSize - (SegmentStart - DecodeMe->buf);
3799         extract_token(charset, SegmentStart, 1, '?', sizeof charset);
3800         if (FoundCharset != NULL) {
3801                 FlushStrBuf(FoundCharset);
3802                 StrBufAppendBufPlain(FoundCharset, charset, -1, 0);
3803         }
3804         extract_token(encoding, SegmentStart, 2, '?', sizeof encoding);
3805         StrBufExtract_token(ConvertBuf, &StaticBuf, 3, '?');
3806         
3807         *encoding = toupper(*encoding);
3808         if (*encoding == 'B') { /**< base64 */
3809                 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3810                         IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3811                 ConvertBuf2->BufUsed = CtdlDecodeBase64(ConvertBuf2->buf, 
3812                                                         ConvertBuf->buf, 
3813                                                         ConvertBuf->BufUsed);
3814         }
3815         else if (*encoding == 'Q') {    /**< quoted-printable */
3816                 long pos;
3817                 
3818                 pos = 0;
3819                 while (pos < ConvertBuf->BufUsed)
3820                 {
3821                         if (ConvertBuf->buf[pos] == '_') 
3822                                 ConvertBuf->buf[pos] = ' ';
3823                         pos++;
3824                 }
3825                 
3826                 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3827                         IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3828
3829                 ConvertBuf2->BufUsed = CtdlDecodeQuotedPrintable(
3830                         ConvertBuf2->buf, 
3831                         ConvertBuf->buf,
3832                         ConvertBuf->BufUsed);
3833         }
3834         else {
3835                 StrBufAppendBuf(ConvertBuf2, ConvertBuf, 0);
3836         }
3837 #ifdef HAVE_ICONV
3838         ctdl_iconv_open("UTF-8", charset, &ic);
3839         if (ic != (iconv_t)(-1) ) {             
3840 #endif
3841                 StrBufConvert(ConvertBuf2, ConvertBuf, &ic);
3842                 StrBufAppendBuf(Target, ConvertBuf2, 0);
3843 #ifdef HAVE_ICONV
3844                 iconv_close(ic);
3845         }
3846         else {
3847                 StrBufAppendBufPlain(Target, HKEY("(unreadable)"), 0);
3848         }
3849 #endif
3850 }
3851
3852 /**
3853  * @ingroup StrBuf_DeEnCoder
3854  * @brief Handle subjects with RFC2047 encoding such as: [deprecated old syntax!]
3855  * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3856  * @param Target where to put the decoded string to 
3857  * @param DecodeMe buffer with encoded string
3858  * @param DefaultCharset if we don't find one, which should we use?
3859  * @param FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string, 
3860  *        put it here for later use where no string might be known.
3861  */
3862 void StrBuf_RFC822_to_Utf8(StrBuf *Target, const StrBuf *DecodeMe, const StrBuf* DefaultCharset, StrBuf *FoundCharset)
3863 {
3864         StrBuf *ConvertBuf;
3865         StrBuf *ConvertBuf2;
3866         ConvertBuf = NewStrBufPlain(NULL, StrLength(DecodeMe));
3867         ConvertBuf2 = NewStrBufPlain(NULL, StrLength(DecodeMe));
3868         
3869         StrBuf_RFC822_2_Utf8(Target, 
3870                              DecodeMe, 
3871                              DefaultCharset, 
3872                              FoundCharset, 
3873                              ConvertBuf, 
3874                              ConvertBuf2);
3875         FreeStrBuf(&ConvertBuf);
3876         FreeStrBuf(&ConvertBuf2);
3877 }
3878
3879 /**
3880  * @ingroup StrBuf_DeEnCoder
3881  * @brief Handle subjects with RFC2047 encoding such as:
3882  * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3883  * @param Target where to put the decoded string to 
3884  * @param DecodeMe buffer with encoded string
3885  * @param DefaultCharset if we don't find one, which should we use?
3886  * @param FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string, 
3887  *        put it here for later use where no string might be known.
3888  * @param ConvertBuf workbuffer. feed in, you shouldn't care about its content.
3889  * @param ConvertBuf2 workbuffer. feed in, you shouldn't care about its content.
3890  */
3891 void StrBuf_RFC822_2_Utf8(StrBuf *Target, 
3892                           const StrBuf *DecodeMe, 
3893                           const StrBuf* DefaultCharset, 
3894                           StrBuf *FoundCharset, 
3895                           StrBuf *ConvertBuf, 
3896                           StrBuf *ConvertBuf2)
3897 {
3898         StrBuf *DecodedInvalidBuf = NULL;
3899         const StrBuf *DecodeMee = DecodeMe;
3900         const char *start, *end, *next, *nextend, *ptr = NULL;
3901 #ifdef HAVE_ICONV
3902         iconv_t ic = (iconv_t)(-1) ;
3903 #endif
3904         const char *eptr;
3905         int passes = 0;
3906         int i;
3907         int illegal_non_rfc2047_encoding = 0;
3908
3909
3910         if (DecodeMe == NULL)
3911                 return;
3912         /* Sometimes, badly formed messages contain strings which were simply
3913          *  written out directly in some foreign character set instead of
3914          *  using RFC2047 encoding.  This is illegal but we will attempt to
3915          *  handle it anyway by converting from a user-specified default
3916          *  charset to UTF-8 if we see any nonprintable characters.
3917          */
3918         
3919         for (i=0; i<DecodeMe->BufUsed; ++i) {
3920                 if ((DecodeMe->buf[i] < 32) || (DecodeMe->buf[i] > 126)) {
3921                         illegal_non_rfc2047_encoding = 1;
3922                         break;
3923                 }
3924         }
3925
3926         if ((illegal_non_rfc2047_encoding) &&
3927             (strcasecmp(ChrPtr(DefaultCharset), "UTF-8")) && 
3928             (strcasecmp(ChrPtr(DefaultCharset), "us-ascii")) )
3929         {
3930 #ifdef HAVE_ICONV
3931                 ctdl_iconv_open("UTF-8", ChrPtr(DefaultCharset), &ic);
3932                 if (ic != (iconv_t)(-1) ) {
3933                         DecodedInvalidBuf = NewStrBufDup(DecodeMe);
3934                         StrBufConvert(DecodedInvalidBuf, ConvertBuf, &ic);///TODO: don't void const?
3935                         DecodeMee = DecodedInvalidBuf;
3936                         iconv_close(ic);
3937                 }
3938 #endif
3939         }
3940
3941         /* pre evaluate the first pair */
3942         end = NULL;
3943         start = strstr(DecodeMee->buf, "=?");
3944         eptr = DecodeMee->buf + DecodeMee->BufUsed;
3945         if (start != NULL) 
3946                 end = FindNextEnd (DecodeMee, start + 2);
3947         else {
3948                 StrBufAppendBuf(Target, DecodeMee, 0);
3949                 FreeStrBuf(&DecodedInvalidBuf);
3950                 return;
3951         }
3952
3953
3954         if (start != DecodeMee->buf) {
3955                 long nFront;
3956                 
3957                 nFront = start - DecodeMee->buf;
3958                 StrBufAppendBufPlain(Target, DecodeMee->buf, nFront, 0);
3959         }
3960         /*
3961          * Since spammers will go to all sorts of absurd lengths to get their
3962          * messages through, there are LOTS of corrupt headers out there.
3963          * So, prevent a really badly formed RFC2047 header from throwing
3964          * this function into an infinite loop.
3965          */
3966         while ((start != NULL) && 
3967                (end != NULL) && 
3968                (start < eptr) && 
3969                (end < eptr) && 
3970                (passes < 20))
3971         {
3972                 passes++;
3973                 DecodeSegment(Target, 
3974                               DecodeMee, 
3975                               start, 
3976                               end, 
3977                               ConvertBuf,
3978                               ConvertBuf2,
3979                               FoundCharset);
3980                 
3981                 next = strstr(end, "=?");
3982                 nextend = NULL;
3983                 if ((next != NULL) && 
3984                     (next < eptr))
3985                         nextend = FindNextEnd(DecodeMee, next);
3986                 if (nextend == NULL)
3987                         next = NULL;
3988
3989                 /* did we find two partitions */
3990                 if ((next != NULL) && 
3991                     ((next - end) > 2))
3992                 {
3993                         ptr = end + 2;
3994                         while ((ptr < next) && 
3995                                (isspace(*ptr) ||
3996                                 (*ptr == '\r') ||
3997                                 (*ptr == '\n') || 
3998                                 (*ptr == '\t')))
3999                                 ptr ++;
4000                         /* 
4001                          * did we find a gab just filled with blanks?
4002                          * if not, copy its stuff over.
4003                          */
4004                         if (ptr != next)
4005                         {
4006                                 StrBufAppendBufPlain(Target, 
4007                                                      end + 2, 
4008                                                      next - end - 2,
4009                                                      0);
4010                         }
4011                 }
4012                 /* our next-pair is our new first pair now. */
4013                 ptr = end + 2;
4014                 start = next;
4015                 end = nextend;
4016         }
4017         end = ptr;
4018         nextend = DecodeMee->buf + DecodeMee->BufUsed;
4019         if ((end != NULL) && (end < nextend)) {
4020                 ptr = end;
4021                 while ( (ptr < nextend) &&
4022                         (isspace(*ptr) ||
4023                          (*ptr == '\r') ||
4024                          (*ptr == '\n') || 
4025                          (*ptr == '\t')))
4026                         ptr ++;
4027                 if (ptr < nextend)
4028                         StrBufAppendBufPlain(Target, end, nextend - end, 0);
4029         }
4030         FreeStrBuf(&DecodedInvalidBuf);
4031 }
4032
4033 /*******************************************************************************
4034  *                   Manipulating UTF-8 Strings                                *
4035  *******************************************************************************/
4036
4037 /**
4038  * @ingroup StrBuf
4039  * @brief evaluate the length of an utf8 special character sequence
4040  * @param Char the character to examine
4041  * @returns width of utf8 chars in bytes; if the sequence is broken 0 is returned; 1 if its simply ASCII.
4042  */
4043 static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE)
4044 {
4045         int n = 0;
4046         unsigned char test = (1<<7);
4047
4048         if ((*CharS & 0xC0) != 0xC0) 
4049                 return 1;
4050
4051         while ((n < 8) && 
4052                ((test & ((unsigned char)*CharS)) != 0)) 
4053         {
4054                 test = test >> 1;
4055                 n ++;
4056         }
4057         if ((n > 6) || ((CharE - CharS) < n))
4058                 n = 0;
4059         return n;
4060 }
4061
4062 /**
4063  * @ingroup StrBuf
4064  * @brief detect whether this char starts an utf-8 encoded char
4065  * @param Char character to inspect
4066  * @returns yes or no
4067  */
4068 static inline int Ctdl_IsUtf8SequenceStart(const char Char)
4069 {
4070 /** 11??.???? indicates an UTF8 Sequence. */
4071         return ((Char & 0xC0) == 0xC0);
4072 }
4073
4074 /**
4075  * @ingroup StrBuf
4076  * @brief measure the number of glyphs in an UTF8 string...
4077  * @param Buf string to measure
4078  * @returns the number of glyphs in Buf
4079  */
4080 long StrBuf_Utf8StrLen(StrBuf *Buf)
4081 {
4082         int n = 0;
4083         int m = 0;
4084         char *aptr, *eptr;
4085
4086         if ((Buf == NULL) || (Buf->BufUsed == 0))
4087                 return 0;
4088         aptr = Buf->buf;
4089         eptr = Buf->buf + Buf->BufUsed;
4090         while ((aptr < eptr) && (*aptr != '\0')) {
4091                 if (Ctdl_IsUtf8SequenceStart(*aptr)){
4092                         m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
4093                         while ((aptr < eptr) && (*aptr++ != '\0')&& (m-- > 0) );
4094                         n ++;
4095                 }
4096                 else {
4097                         n++;
4098                         aptr++;
4099                 }
4100         }
4101         return n;
4102 }
4103
4104 /**
4105  * @ingroup StrBuf
4106  * @brief cuts a string after maxlen glyphs
4107  * @param Buf string to cut to maxlen glyphs
4108  * @param maxlen how long may the string become?
4109  * @returns current length of the string
4110  */
4111 long StrBuf_Utf8StrCut(StrBuf *Buf, int maxlen)
4112 {
4113         char *aptr, *eptr;
4114         int n = 0, m = 0;
4115
4116         aptr = Buf->buf;
4117         eptr = Buf->buf + Buf->BufUsed;
4118         while ((aptr < eptr) && (*aptr != '\0')) {
4119                 if (Ctdl_IsUtf8SequenceStart(*aptr)){
4120                         m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
4121                         while ((*aptr++ != '\0') && (m-- > 0));
4122                         n ++;
4123                 }
4124                 else {
4125                         n++;
4126                         aptr++;
4127                 }
4128                 if (n > maxlen) {
4129                         *aptr = '\0';
4130                         Buf->BufUsed = aptr - Buf->buf;
4131                         return Buf->BufUsed;
4132                 }                       
4133         }
4134         return Buf->BufUsed;
4135
4136 }
4137
4138
4139
4140
4141
4142 /*******************************************************************************
4143  *                               wrapping ZLib                                 *
4144  *******************************************************************************/
4145
4146 /**
4147  * @ingroup StrBuf_DeEnCoder
4148  * @brief uses the same calling syntax as compress2(), but it
4149  *   creates a stream compatible with HTTP "Content-encoding: gzip"
4150  * @param dest compressed buffer
4151  * @param destLen length of the compresed data 
4152  * @param source source to encode
4153  * @param sourceLen length of source to encode 
4154  * @param level compression level
4155  */
4156 #ifdef HAVE_ZLIB
4157 int ZEXPORT compress_gzip(Bytef * dest,
4158                           size_t * destLen,
4159                           const Bytef * source,
4160                           uLong sourceLen,     
4161                           int level)
4162 {
4163         /* write gzip header */
4164         snprintf((char *) dest, *destLen, 
4165                  "%c%c%c%c%c%c%c%c%c%c",
4166                  gz_magic[0], gz_magic[1], Z_DEFLATED,
4167                  0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /* xflags */ ,
4168                  OS_CODE);
4169
4170         /* normal deflate */
4171         z_stream stream;
4172         int err;
4173         stream.next_in = (Bytef *) source;
4174         stream.avail_in = (uInt) sourceLen;
4175         stream.next_out = dest + 10L;   // after header
4176         stream.avail_out = (uInt) * destLen;
4177         if ((uLong) stream.avail_out != *destLen)
4178                 return Z_BUF_ERROR;
4179
4180         stream.zalloc = (alloc_func) 0;
4181         stream.zfree = (free_func) 0;
4182         stream.opaque = (voidpf) 0;
4183
4184         err = deflateInit2(&stream, level, Z_DEFLATED, -MAX_WBITS,
4185                            DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
4186         if (err != Z_OK)
4187                 return err;
4188
4189         err = deflate(&stream, Z_FINISH);
4190         if (err != Z_STREAM_END) {
4191                 deflateEnd(&stream);
4192                 return err == Z_OK ? Z_BUF_ERROR : err;
4193         }
4194         *destLen = stream.total_out + 10L;
4195
4196         /* write CRC and Length */
4197         uLong crc = crc32(0L, source, sourceLen);
4198         int n;
4199         for (n = 0; n < 4; ++n, ++*destLen) {
4200                 dest[*destLen] = (int) (crc & 0xff);
4201                 crc >>= 8;
4202         }
4203         uLong len = stream.total_in;
4204         for (n = 0; n < 4; ++n, ++*destLen) {
4205                 dest[*destLen] = (int) (len & 0xff);
4206                 len >>= 8;
4207         }
4208         err = deflateEnd(&stream);
4209         return err;
4210 }
4211 #endif
4212
4213
4214 /**
4215  * @ingroup StrBuf_DeEnCoder
4216  * @brief compress the buffer with gzip
4217  * Attention! If you feed this a Const String, you must maintain the uncompressed buffer yourself!
4218  * @param Buf buffer whose content is to be gzipped
4219  */
4220 int CompressBuffer(StrBuf *Buf)
4221 {
4222 #ifdef HAVE_ZLIB
4223         char *compressed_data = NULL;
4224         size_t compressed_len, bufsize;
4225         int i = 0;
4226
4227         bufsize = compressed_len = Buf->BufUsed +  (Buf->BufUsed / 100) + 100;
4228         compressed_data = malloc(compressed_len);
4229         
4230         if (compressed_data == NULL)
4231                 return -1;
4232         /* Flush some space after the used payload so valgrind shuts up... */
4233         while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
4234                 Buf->buf[Buf->BufUsed + i++] = '\0';
4235         if (compress_gzip((Bytef *) compressed_data,
4236                           &compressed_len,
4237                           (Bytef *) Buf->buf,
4238                           (uLongf) Buf->BufUsed, Z_BEST_SPEED) == Z_OK) {
4239                 if (!Buf->ConstBuf)
4240                         free(Buf->buf);
4241                 Buf->buf = compressed_data;
4242                 Buf->BufUsed = compressed_len;
4243                 Buf->BufSize = bufsize;
4244                 /* Flush some space after the used payload so valgrind shuts up... */
4245                 i = 0;
4246                 while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
4247                         Buf->buf[Buf->BufUsed + i++] = '\0';
4248                 return 1;
4249         } else {
4250                 free(compressed_data);
4251         }
4252 #endif  /* HAVE_ZLIB */
4253         return 0;
4254 }
4255
4256 /*******************************************************************************
4257  *           File I/O; Callbacks to libevent                                   *
4258  *******************************************************************************/
4259
4260 long StrBuf_read_one_chunk_callback (int fd, short event, IOBuffer *FB)
4261 {
4262         long bufremain = 0;
4263         int n;
4264         
4265         if ((FB == NULL) || (FB->Buf == NULL))
4266                 return -1;
4267
4268         /*
4269          * check whether the read pointer is somewhere in a range 
4270          * where a cut left is inexpensive
4271          */
4272
4273         if (FB->ReadWritePointer != NULL)
4274         {
4275                 long already_read;
4276                 
4277                 already_read = FB->ReadWritePointer - FB->Buf->buf;
4278                 bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
4279
4280                 if (already_read != 0) {
4281                         long unread;
4282                         
4283                         unread = FB->Buf->BufUsed - already_read;
4284
4285                         /* else nothing to compact... */
4286                         if (unread == 0) {
4287                                 FB->ReadWritePointer = FB->Buf->buf;
4288                                 bufremain = FB->Buf->BufSize;                   
4289                         }
4290                         else if ((unread < 64) || 
4291                                  (bufremain < already_read))
4292                         {
4293                                 /* 
4294                                  * if its just a tiny bit remaining, or we run out of space... 
4295                                  * lets tidy up.
4296                                  */
4297                                 FB->Buf->BufUsed = unread;
4298                                 if (unread < already_read)
4299                                         memcpy(FB->Buf->buf, FB->ReadWritePointer, unread);
4300                                 else
4301                                         memmove(FB->Buf->buf, FB->ReadWritePointer, unread);
4302                                 FB->ReadWritePointer = FB->Buf->buf;
4303                                 bufremain = FB->Buf->BufSize - unread - 1;
4304                         }
4305                         else if (bufremain < (FB->Buf->BufSize / 10))
4306                         {
4307                                 /* get a bigger buffer */ 
4308
4309                                 IncreaseBuf(FB->Buf, 0, FB->Buf->BufUsed + 1);
4310
4311                                 FB->ReadWritePointer = FB->Buf->buf + unread;
4312
4313                                 bufremain = FB->Buf->BufSize - unread - 1;
4314 /*TODO: special increase function that won't copy the already read! */
4315                         }
4316                 }
4317                 else if (bufremain < 10) {
4318                         IncreaseBuf(FB->Buf, 1, FB->Buf->BufUsed + 10);
4319                         
4320                         FB->ReadWritePointer = FB->Buf->buf;
4321                         
4322                         bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
4323                 }
4324                 
4325         }
4326         else {
4327                 FB->ReadWritePointer = FB->Buf->buf;
4328                 bufremain = FB->Buf->BufSize - 1;
4329         }
4330
4331         n = read(fd, FB->Buf->buf + FB->Buf->BufUsed, bufremain);
4332
4333         if (n > 0) {
4334                 FB->Buf->BufUsed += n;
4335                 FB->Buf->buf[FB->Buf->BufUsed] = '\0';
4336         }
4337         return n;
4338 }
4339
4340 int StrBuf_write_one_chunk_callback(int fd, short event, IOBuffer *FB)
4341 {
4342         long WriteRemain;
4343         int n;
4344
4345         if ((FB == NULL) || (FB->Buf == NULL))
4346                 return -1;
4347
4348         if (FB->ReadWritePointer != NULL)
4349         {
4350                 WriteRemain = FB->Buf->BufUsed - 
4351                         (FB->ReadWritePointer - 
4352                          FB->Buf->buf);
4353         }
4354         else {
4355                 FB->ReadWritePointer = FB->Buf->buf;
4356                 WriteRemain = FB->Buf->BufUsed;
4357         }
4358
4359         n = write(fd, FB->ReadWritePointer, WriteRemain);
4360         if (n > 0) {
4361                 FB->ReadWritePointer += n;
4362
4363                 if (FB->ReadWritePointer == 
4364                     FB->Buf->buf + FB->Buf->BufUsed)
4365                 {
4366                         FlushStrBuf(FB->Buf);
4367                         FB->ReadWritePointer = NULL;
4368                         return 0;
4369                 }
4370         // check whether we've got something to write
4371         // get the maximum chunk plus the pointer we can send
4372         // write whats there
4373         // if not all was sent, remember the send pointer for the next time
4374                 return FB->ReadWritePointer - FB->Buf->buf + FB->Buf->BufUsed;
4375         }
4376         return n;
4377 }
4378
4379 /**
4380  * @ingroup StrBuf_IO
4381  * @brief extract a "next line" from Buf; Ptr to persist across several iterations
4382  * @param LineBuf your line will be copied here.
4383  * @param FB BLOB with lines of text...
4384  * @param Ptr moved arround to keep the next-line across several iterations
4385  *        has to be &NULL on start; will be &NotNULL on end of buffer
4386  * @returns size of copied buffer
4387  */
4388 eReadState StrBufChunkSipLine(StrBuf *LineBuf, IOBuffer *FB)
4389 {
4390         const char *aptr, *ptr, *eptr;
4391         char *optr, *xptr;
4392
4393         if ((FB == NULL) || (LineBuf == NULL) || (LineBuf->buf == NULL))
4394                 return eReadFail;
4395         
4396
4397         if ((FB->Buf == NULL) || (FB->ReadWritePointer == StrBufNOTNULL)) {
4398                 FB->ReadWritePointer = StrBufNOTNULL;
4399                 return eReadFail;
4400         }
4401
4402         FlushStrBuf(LineBuf);
4403         if (FB->ReadWritePointer == NULL)
4404                 ptr = aptr = FB->Buf->buf;
4405         else
4406                 ptr = aptr = FB->ReadWritePointer;
4407
4408         optr = LineBuf->buf;
4409         eptr = FB->Buf->buf + FB->Buf->BufUsed;
4410         xptr = LineBuf->buf + LineBuf->BufSize - 1;
4411
4412         while ((ptr <= eptr) && 
4413                (*ptr != '\n') &&
4414                (*ptr != '\r') )
4415         {
4416                 *optr = *ptr;
4417                 optr++; ptr++;
4418                 if (optr == xptr) {
4419                         LineBuf->BufUsed = optr - LineBuf->buf;
4420                         IncreaseBuf(LineBuf,  1, LineBuf->BufUsed + 1);
4421                         optr = LineBuf->buf + LineBuf->BufUsed;
4422                         xptr = LineBuf->buf + LineBuf->BufSize - 1;
4423                 }
4424         }
4425
4426         if (ptr >= eptr) {
4427                 if (optr > LineBuf->buf)
4428                         optr --;
4429                 if ((*(ptr - 1) != '\r') && (*(ptr - 1) != '\n')) {
4430                         LineBuf->BufUsed = optr - LineBuf->buf;
4431                         *optr = '\0';
4432                         if ((FB->ReadWritePointer != NULL) && 
4433                             (FB->ReadWritePointer != FB->Buf->buf))
4434                         {
4435                                 /* Ok, the client application read all the data 
4436                                    it was interested in so far. Since there is more to read, 
4437                                    we now shrink the buffer, and move the rest over.
4438                                 */
4439                                 StrBufCutLeft(FB->Buf, 
4440                                               FB->ReadWritePointer - FB->Buf->buf);
4441                                 FB->ReadWritePointer = FB->Buf->buf;
4442                         }
4443                         return eMustReadMore;
4444                 }
4445         }
4446         LineBuf->BufUsed = optr - LineBuf->buf;
4447         *optr = '\0';       
4448         if ((ptr <= eptr) && (*ptr == '\r'))
4449                 ptr ++;
4450         if ((ptr <= eptr) && (*ptr == '\n'))
4451                 ptr ++;
4452         
4453         if (ptr < eptr) {
4454                 FB->ReadWritePointer = ptr;
4455         }
4456         else {
4457                 FlushStrBuf(FB->Buf);
4458                 FB->ReadWritePointer = NULL;
4459         }
4460
4461         return eReadSuccess;
4462 }
4463
4464 /**
4465  * @ingroup StrBuf_CHUNKED_IO
4466  * @brief check whether the chunk-buffer has more data waiting or not.
4467  * @param FB Chunk-Buffer to inspect
4468  */
4469 eReadState StrBufCheckBuffer(IOBuffer *FB)
4470 {
4471         if (FB == NULL)
4472                 return eReadFail;
4473         if (FB->Buf->BufUsed == 0)
4474                 return eReadSuccess;
4475         if (FB->ReadWritePointer == NULL)
4476                 return eBufferNotEmpty;
4477         if (FB->Buf->buf + FB->Buf->BufUsed > FB->ReadWritePointer)
4478                 return eBufferNotEmpty;
4479         return eReadSuccess;
4480 }
4481
4482 long IOBufferStrLength(IOBuffer *FB)
4483 {
4484         if ((FB == NULL) || (FB->Buf == NULL))
4485                 return 0;
4486         if (FB->ReadWritePointer == NULL)
4487                 return StrLength(FB->Buf);
4488         
4489         return StrLength(FB->Buf) - (FB->ReadWritePointer - FB->Buf->buf);
4490 }
4491
4492 inline static void FDIOBufferFlush(FDIOBuffer *FDB)
4493 {
4494         memset(FDB, 0, sizeof(FDIOBuffer));
4495         FDB->OtherFD = -1;
4496         FDB->SplicePipe[0] = -1;
4497         FDB->SplicePipe[1] = -1;
4498 }
4499
4500 void FDIOBufferInit(FDIOBuffer *FDB, IOBuffer *IO, int FD, long TotalSendSize)
4501 {
4502         FDIOBufferFlush(FDB);
4503
4504         FDB->TotalSendSize = TotalSendSize;
4505         if (TotalSendSize > 0)
4506                 FDB->ChunkSize = TotalSendSize;
4507         else
4508         {
4509                 TotalSendSize = SIZ * 10;
4510                 FDB->ChunkSize = TotalSendSize;
4511         }
4512         FDB->IOB = IO;
4513
4514 #ifdef LINUX_SPLICE
4515         if (EnableSplice)
4516                 pipe(FDB->SplicePipe);
4517         else
4518 #endif
4519                 FDB->ChunkBuffer = NewStrBufPlain(NULL, TotalSendSize+ 1);
4520
4521         FDB->OtherFD = FD;
4522 }
4523
4524 void FDIOBufferDelete(FDIOBuffer *FDB)
4525 {
4526 #ifdef LINUX_SPLICE
4527         if (EnableSplice)
4528         {
4529                 if (FDB->SplicePipe[0] > 0)
4530                         close(FDB->SplicePipe[0]);
4531                 if (FDB->SplicePipe[1] > 0)
4532                         close(FDB->SplicePipe[1]);
4533         }
4534         else
4535 #endif
4536                 FreeStrBuf(&FDB->ChunkBuffer);
4537         
4538         if (FDB->OtherFD > 0)
4539                 close(FDB->OtherFD);
4540         FDIOBufferFlush(FDB);
4541 }
4542
4543 int FileSendChunked(FDIOBuffer *FDB, const char **Err)
4544 {
4545         ssize_t sent, pipesize;
4546
4547         if (FDB->TotalSendSize > 0)
4548         {
4549 #ifdef LINUX_SPLICE
4550                 if (EnableSplice)
4551                 {
4552                         if (FDB->PipeSize == 0)
4553                         {
4554                                 pipesize = splice(FDB->OtherFD,
4555                                                   &FDB->TotalSentAlready, 
4556                                                   FDB->SplicePipe[1],
4557                                                   NULL, 
4558                                                   FDB->ChunkSendRemain, 
4559                                                   SPLICE_F_MOVE);
4560         
4561                                 if (pipesize == -1)
4562                                 {
4563                                         *Err = strerror(errno);
4564                                         return pipesize;
4565                                 }
4566                                 FDB->PipeSize = pipesize;
4567                         }
4568                         sent =  splice(FDB->SplicePipe[0],
4569                                        NULL, 
4570                                        FDB->IOB->fd,
4571                                        NULL, 
4572                                        FDB->PipeSize,
4573                                        SPLICE_F_MORE | SPLICE_F_MOVE | SPLICE_F_NONBLOCK);
4574                         if (sent == -1)
4575                         {
4576                                 *Err = strerror(errno);
4577                                 return sent;
4578                         }
4579                         FDB->PipeSize -= sent;
4580                         FDB->ChunkSendRemain -= sent;
4581                         return sent;
4582                 }
4583                 else
4584 #endif
4585                 {
4586                         char *pRead;
4587                         long nRead = 0;
4588
4589                         pRead = FDB->ChunkBuffer->buf;
4590                         while ((FDB->ChunkBuffer->BufUsed < FDB->TotalSendSize) && (nRead >= 0))
4591                         {
4592                                 nRead = read(FDB->OtherFD, pRead, FDB->TotalSendSize - FDB->ChunkBuffer->BufUsed);
4593                                 if (nRead > 0) {
4594                                         FDB->ChunkBuffer->BufUsed += nRead;
4595                                         FDB->ChunkBuffer->buf[FDB->ChunkBuffer->BufUsed] = '\0';
4596                                 }
4597                                 else if (nRead == 0) {}
4598                                 else return nRead;
4599                         }
4600
4601                         nRead = write(FDB->IOB->fd,
4602                                       FDB->ChunkBuffer->buf     + FDB->TotalSentAlready,
4603                                       FDB->ChunkBuffer->BufUsed - FDB->TotalSentAlready);
4604
4605                         if (nRead >= 0) {
4606                                 FDB->TotalSentAlready += nRead;
4607                                 FDB->ChunkSendRemain -= nRead;
4608                                 return FDB->ChunkSendRemain;
4609                         }
4610                         else {
4611                                 return nRead;
4612                         }
4613                 }
4614         }
4615         else
4616         {
4617 #ifdef LINUX_SPLICE
4618                 if (EnableSplice)
4619                 {
4620                         if (FDB->PipeSize == 0)
4621                         {
4622                                 pipesize = splice(FDB->OtherFD,
4623                                                   &FDB->TotalSentAlready, 
4624                                                   FDB->SplicePipe[1],
4625                                                   NULL, 
4626                                                   SIZ * 10, 
4627                                                   SPLICE_F_MOVE);
4628         
4629                                 if (pipesize == -1)
4630                                 {
4631                                         *Err = strerror(errno);
4632                                         return pipesize;
4633                                 }
4634                                 FDB->PipeSize = pipesize;
4635                                 if (pipesize == 0)
4636                                         return -1;
4637                         }
4638                         sent =  splice(FDB->SplicePipe[0],
4639                                        NULL, 
4640                                        FDB->IOB->fd,
4641                                        NULL, 
4642                                        FDB->PipeSize,
4643                                        SPLICE_F_MORE | SPLICE_F_MOVE | SPLICE_F_NONBLOCK);
4644                         if (sent == -1)
4645                         {
4646                                 *Err = strerror(errno);
4647                                 return sent;
4648                         }
4649                         FDB->PipeSize -= sent;
4650                         FDB->ChunkSendRemain -= sent;
4651                         return sent;
4652                 }
4653                 else
4654 #endif
4655                 {
4656                         char *pRead;
4657                         long nRead = 0;
4658
4659                         pRead = FDB->ChunkBuffer->buf;
4660                         while ((FDB->ChunkSendRemain == 0) && 
4661                                (FDB->ChunkBuffer->BufUsed < FDB->ChunkBuffer->BufSize) &&
4662                                (nRead >= 0))
4663                         {
4664                                 FDB->TotalSentAlready = 0;
4665                                 nRead = read(FDB->OtherFD, pRead, FDB->ChunkBuffer->BufSize - FDB->ChunkBuffer->BufUsed);
4666                                 if (nRead > 0) {
4667                                         FDB->ChunkBuffer->BufUsed += nRead;
4668                                         FDB->ChunkBuffer->buf[FDB->ChunkBuffer->BufUsed] = '\0';
4669                                         FDB->ChunkSendRemain += nRead;
4670                                 }
4671                                 else if (nRead == 0)
4672                                 {
4673                                         return -1;
4674                                 }
4675                                 else
4676                                 {
4677                                         *Err = strerror(errno);
4678                                         return nRead;
4679                                 }
4680                         }
4681
4682                         nRead = write(FDB->IOB->fd,
4683                                       FDB->ChunkBuffer->buf     + FDB->TotalSentAlready,
4684                                       FDB->ChunkBuffer->BufUsed - FDB->TotalSentAlready);
4685
4686                         if (nRead >= 0) {
4687                                 FDB->TotalSentAlready += nRead;
4688                                 FDB->ChunkSendRemain -= nRead;
4689                                 if (FDB->ChunkSendRemain == 0)
4690                                 {
4691                                         FDB->ChunkBuffer->BufUsed = 0;
4692                                         FDB->TotalSentAlready = 0;
4693                                 }
4694                                 return FDB->ChunkSendRemain;
4695                         }
4696                         else {
4697                                 return nRead;
4698                         }
4699                 }
4700         }
4701 }
4702
4703 int FileRecvChunked(FDIOBuffer *FDB, const char **Err)
4704 {
4705         ssize_t sent, pipesize;
4706
4707 #ifdef LINUX_SPLICE
4708         if (EnableSplice)
4709         {
4710                 if (FDB->PipeSize == 0)
4711                 {
4712                         pipesize = splice(FDB->IOB->fd,
4713                                           NULL, 
4714                                           FDB->SplicePipe[1],
4715                                           NULL, 
4716                                           FDB->ChunkSendRemain, 
4717                                           SPLICE_F_MORE | SPLICE_F_MOVE|SPLICE_F_NONBLOCK);
4718
4719                         if (pipesize == -1)
4720                         {
4721                                 *Err = strerror(errno);
4722                                 return pipesize;
4723                         }
4724                         FDB->PipeSize = pipesize;
4725                 }
4726         
4727                 sent = splice(FDB->SplicePipe[0],
4728                               NULL, 
4729                               FDB->OtherFD,
4730                               &FDB->TotalSentAlready, 
4731                               FDB->PipeSize,
4732                               SPLICE_F_MORE | SPLICE_F_MOVE);
4733
4734                 if (sent == -1)
4735                 {
4736                         *Err = strerror(errno);
4737                         return sent;
4738                 }
4739                 FDB->PipeSize -= sent;
4740                 FDB->ChunkSendRemain -= sent;
4741                 return sent;
4742         }
4743         else
4744 #endif
4745         {
4746                 sent = read(FDB->IOB->fd, FDB->ChunkBuffer->buf, FDB->ChunkSendRemain);
4747                 if (sent > 0) {
4748                         int nWritten = 0;
4749                         int rc; 
4750                 
4751                         FDB->ChunkBuffer->BufUsed = sent;
4752
4753                         while (nWritten < FDB->ChunkBuffer->BufUsed) {
4754                                 rc =  write(FDB->OtherFD, FDB->ChunkBuffer->buf + nWritten, FDB->ChunkBuffer->BufUsed - nWritten);
4755                                 if (rc < 0) {
4756                                         *Err = strerror(errno);
4757                                         return rc;
4758                                 }
4759                                 nWritten += rc;
4760
4761                         }
4762                         FDB->ChunkBuffer->BufUsed = 0;
4763                         FDB->TotalSentAlready += sent;
4764                         FDB->ChunkSendRemain -= sent;
4765                         return FDB->ChunkSendRemain;
4766                 }
4767                 else if (sent < 0) {
4768                         *Err = strerror(errno);
4769                         return sent;
4770                 }
4771                 return 0;
4772         }
4773 }
4774
4775 int FileMoveChunked(FDIOBuffer *FDB, const char **Err)
4776 {
4777         ssize_t sent, pipesize;
4778
4779 #ifdef LINUX_SPLICE
4780         if (EnableSplice)
4781         {
4782                 if (FDB->PipeSize == 0)
4783                 {
4784                         pipesize = splice(FDB->IOB->fd,
4785                                           &FDB->TotalReadAlready, 
4786                                           FDB->SplicePipe[1],
4787                                           NULL, 
4788                                           FDB->ChunkSendRemain, 
4789                                           SPLICE_F_MORE | SPLICE_F_MOVE|SPLICE_F_NONBLOCK);
4790                         
4791                         if (pipesize == -1)
4792                         {
4793                                 *Err = strerror(errno);
4794                                 return pipesize;
4795                         }
4796                         FDB->PipeSize = pipesize;
4797                 }
4798                 
4799                 sent = splice(FDB->SplicePipe[0],
4800                               NULL, 
4801                               FDB->OtherFD,
4802                               &FDB->TotalSentAlready, 
4803                               FDB->PipeSize,
4804                               SPLICE_F_MORE | SPLICE_F_MOVE);
4805                 
4806                 if (sent == -1)
4807                 {
4808                         *Err = strerror(errno);
4809                         return sent;
4810                 }
4811                 FDB->PipeSize -= sent;
4812                 FDB->ChunkSendRemain -= sent;
4813                 return sent;
4814         }
4815         else
4816 #endif  
4817         {
4818                 sent = read(FDB->IOB->fd, FDB->ChunkBuffer->buf, FDB->ChunkSendRemain);
4819                 if (sent > 0) {
4820                         int nWritten = 0;
4821                         int rc; 
4822                 
4823                         FDB->ChunkBuffer->BufUsed = sent;
4824
4825                         while (nWritten < FDB->ChunkBuffer->BufUsed) {
4826                                 rc =  write(FDB->OtherFD, FDB->ChunkBuffer->buf + nWritten, FDB->ChunkBuffer->BufUsed - nWritten);
4827                                 if (rc < 0) {
4828                                         *Err = strerror(errno);
4829                                         return rc;
4830                                 }
4831                                 nWritten += rc;
4832
4833                         }
4834                         FDB->ChunkBuffer->BufUsed = 0;
4835                         FDB->TotalSentAlready += sent;
4836                         FDB->ChunkSendRemain -= sent;
4837                         return FDB->ChunkSendRemain;
4838                 }
4839                 else if (sent < 0) {
4840                         *Err = strerror(errno);
4841                         return sent;
4842                 }
4843                 return 0;
4844         }
4845 }
4846
4847 eReadState WriteIOBAlreadyRead(FDIOBuffer *FDB, const char **Error)
4848 {
4849         int IsNonBlock;
4850         int fdflags;
4851         long rlen;
4852         long should_write;
4853         int nSuccessLess = 0;
4854         struct timeval tv;
4855         fd_set rfds;
4856
4857         fdflags = fcntl(FDB->OtherFD, F_GETFL);
4858         IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4859
4860         while ((FDB->IOB->ReadWritePointer - FDB->IOB->Buf->buf < FDB->IOB->Buf->BufUsed) &&
4861                (FDB->ChunkSendRemain > 0))
4862         {
4863                 if (IsNonBlock){
4864                         tv.tv_sec = 1; /* selectresolution; */
4865                         tv.tv_usec = 0;
4866                         
4867                         FD_ZERO(&rfds);
4868                         FD_SET(FDB->OtherFD, &rfds);
4869                         if (select(FDB->OtherFD + 1, NULL, &rfds, NULL, &tv) == -1) {
4870                                 *Error = strerror(errno);
4871                                 return eReadFail;
4872                         }
4873                 }
4874                 if (IsNonBlock && !  FD_ISSET(FDB->OtherFD, &rfds)) {
4875                         nSuccessLess ++;
4876                         continue;
4877                 }
4878
4879                 should_write = FDB->IOB->Buf->BufUsed - 
4880                         (FDB->IOB->ReadWritePointer - FDB->IOB->Buf->buf);
4881                 if (should_write > FDB->ChunkSendRemain)
4882                         should_write = FDB->ChunkSendRemain;
4883
4884                 rlen = write(FDB->OtherFD, 
4885                              FDB->IOB->ReadWritePointer, 
4886                              should_write);
4887                 if (rlen < 1) {
4888                         *Error = strerror(errno);
4889                                                 
4890                         return eReadFail;
4891                 }
4892                 FDB->TotalSentAlready += rlen;
4893                 FDB->IOB->ReadWritePointer += rlen;
4894                 FDB->ChunkSendRemain -= rlen;
4895         }
4896         if (FDB->IOB->ReadWritePointer >= FDB->IOB->Buf->buf + FDB->IOB->Buf->BufUsed)
4897         {
4898                 FlushStrBuf(FDB->IOB->Buf);
4899                 FDB->IOB->ReadWritePointer = NULL;
4900         }
4901
4902         if (FDB->ChunkSendRemain == 0)
4903                 return eReadSuccess;
4904         else 
4905                 return eMustReadMore;
4906 }
4907
4908 /*******************************************************************************
4909  *           File I/O; Prefer buffered read since its faster!                  *
4910  *******************************************************************************/
4911
4912 /**
4913  * @ingroup StrBuf_IO
4914  * @brief Read a line from socket
4915  * flushes and closes the FD on error
4916  * @param buf the buffer to get the input to
4917  * @param fd pointer to the filedescriptor to read
4918  * @param append Append to an existing string or replace?
4919  * @param Error strerror() on error 
4920  * @returns numbers of chars read
4921  */
4922 int StrBufTCP_read_line(StrBuf *buf, int *fd, int append, const char **Error)
4923 {
4924         int len, rlen, slen;
4925
4926         if ((buf == NULL) || (buf->buf == NULL)) {
4927                 *Error = strerror(EINVAL);
4928                 return -1;
4929         }
4930
4931         if (!append)
4932                 FlushStrBuf(buf);
4933
4934         slen = len = buf->BufUsed;
4935         while (1) {
4936                 rlen = read(*fd, &buf->buf[len], 1);
4937                 if (rlen < 1) {
4938                         *Error = strerror(errno);
4939                         
4940                         close(*fd);
4941                         *fd = -1;
4942                         
4943                         return -1;
4944                 }
4945                 if (buf->buf[len] == '\n')
4946                         break;
4947                 if (buf->buf[len] != '\r')
4948                         len ++;
4949                 if (len + 2 >= buf->BufSize) {
4950                         buf->BufUsed = len;
4951                         buf->buf[len+1] = '\0';
4952                         IncreaseBuf(buf, 1, -1);
4953                 }
4954         }
4955         buf->BufUsed = len;
4956         buf->buf[len] = '\0';
4957         return len - slen;
4958 }
4959
4960 /**
4961  * @ingroup StrBuf_BufferedIO
4962  * @brief Read a line from socket
4963  * flushes and closes the FD on error
4964  * @param Line the line to read from the fd / I/O Buffer
4965  * @param buf the buffer to get the input to
4966  * @param fd pointer to the filedescriptor to read
4967  * @param timeout number of successless selects until we bail out
4968  * @param selectresolution how long to wait on each select
4969  * @param Error strerror() on error 
4970  * @returns numbers of chars read
4971  */
4972 int StrBufTCP_read_buffered_line(StrBuf *Line, 
4973                                  StrBuf *buf, 
4974                                  int *fd, 
4975                                  int timeout, 
4976                                  int selectresolution, 
4977                                  const char **Error)
4978 {
4979         int len, rlen;
4980         int nSuccessLess = 0;
4981         fd_set rfds;
4982         char *pch = NULL;
4983         int fdflags;
4984         int IsNonBlock;
4985         struct timeval tv;
4986
4987         if (buf->BufUsed > 0) {
4988                 pch = strchr(buf->buf, '\n');
4989                 if (pch != NULL) {
4990                         rlen = 0;
4991                         len = pch - buf->buf;
4992                         if (len > 0 && (*(pch - 1) == '\r') )
4993                                 rlen ++;
4994                         StrBufSub(Line, buf, 0, len - rlen);
4995                         StrBufCutLeft(buf, len + 1);
4996                         return len - rlen;
4997                 }
4998         }
4999         
5000         if (buf->BufSize - buf->BufUsed < 10)
5001                 IncreaseBuf(buf, 1, -1);
5002
5003         fdflags = fcntl(*fd, F_GETFL);
5004         IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
5005
5006         while ((nSuccessLess < timeout) && (pch == NULL)) {
5007                 if (IsNonBlock){
5008                         tv.tv_sec = selectresolution;
5009                         tv.tv_usec = 0;
5010                         
5011                         FD_ZERO(&rfds);
5012                         FD_SET(*fd, &rfds);
5013                         if (select(*fd + 1, NULL, &rfds, NULL, &tv) == -1) {
5014                                 *Error = strerror(errno);
5015                                 close (*fd);
5016                                 *fd = -1;
5017                                 return -1;
5018                         }
5019                 }
5020                 if (IsNonBlock && !  FD_ISSET(*fd, &rfds)) {
5021                         nSuccessLess ++;
5022                         continue;
5023                 }
5024                 rlen = read(*fd, 
5025                             &buf->buf[buf->BufUsed], 
5026                             buf->BufSize - buf->BufUsed - 1);
5027                 if (rlen < 1) {
5028                         *Error = strerror(errno);
5029                         close(*fd);
5030                         *fd = -1;
5031                         return -1;
5032                 }
5033                 else if (rlen > 0) {
5034                         nSuccessLess = 0;
5035                         buf->BufUsed += rlen;
5036                         buf->buf[buf->BufUsed] = '\0';
5037                         pch = strchr(buf->buf, '\n');
5038                         if ((pch == NULL) &&
5039                             (buf->BufUsed + 10 > buf->BufSize) &&
5040                             (IncreaseBuf(buf, 1, -1) == -1))
5041                                 return -1;
5042                         continue;
5043                 }
5044                 
5045         }
5046         if (pch != NULL) {
5047                 rlen = 0;
5048                 len = pch - buf->buf;
5049                 if (len > 0 && (*(pch - 1) == '\r') )
5050                         rlen ++;
5051                 StrBufSub(Line, buf, 0, len - rlen);
5052                 StrBufCutLeft(buf, len + 1);
5053                 return len - rlen;
5054         }
5055         return -1;
5056
5057 }
5058
5059 static const char *ErrRBLF_PreConditionFailed="StrBufTCP_read_buffered_line_fast: Wrong arguments or invalid Filedescriptor";
5060 static const char *ErrRBLF_SelectFailed="StrBufTCP_read_buffered_line_fast: Select failed without reason";
5061 static const char *ErrRBLF_NotEnoughSentFromServer="StrBufTCP_read_buffered_line_fast: No complete line was sent from peer";
5062 /**
5063  * @ingroup StrBuf_BufferedIO
5064  * @brief Read a line from socket
5065  * flushes and closes the FD on error
5066  * @param Line where to append our Line read from the fd / I/O Buffer; 
5067  * @param IOBuf the buffer to get the input to; lifetime pair to FD
5068  * @param Pos pointer to the current read position, should be NULL initialized on opening the FD it belongs to.!
5069  * @param fd pointer to the filedescriptor to read
5070  * @param timeout number of successless selects until we bail out
5071  * @param selectresolution how long to wait on each select
5072  * @param Error strerror() on error 
5073  * @returns numbers of chars read or -1 in case of error. "\n" will become 0
5074  */
5075 int StrBufTCP_read_buffered_line_fast(StrBuf *Line, 
5076                                       StrBuf *IOBuf, 
5077                                       const char **Pos,
5078                                       int *fd, 
5079                                       int timeout, 
5080                                       int selectresolution, 
5081                                       const char **Error)
5082 {
5083         const char *pche = NULL;
5084         const char *pos = NULL;
5085         const char *pLF;
5086         int len, rlen, retlen;
5087         int nSuccessLess = 0;
5088         fd_set rfds;
5089         const char *pch = NULL;
5090         int fdflags;
5091         int IsNonBlock;
5092         struct timeval tv;
5093         
5094         retlen = 0;
5095         if ((Line == NULL) ||
5096             (Pos == NULL) ||
5097             (IOBuf == NULL) ||
5098             (*fd == -1))
5099         {
5100                 if (Pos != NULL)
5101                         *Pos = NULL;
5102                 *Error = ErrRBLF_PreConditionFailed;
5103                 return -1;
5104         }
5105
5106         pos = *Pos;
5107         if ((IOBuf->BufUsed > 0) && 
5108             (pos != NULL) && 
5109             (pos < IOBuf->buf + IOBuf->BufUsed)) 
5110         {
5111                 char *pcht;
5112
5113                 pche = IOBuf->buf + IOBuf->BufUsed;
5114                 pch = pos;
5115                 pcht = Line->buf;
5116
5117                 while ((pch < pche) && (*pch != '\n'))
5118                 {
5119                         if (Line->BufUsed + 10 > Line->BufSize)
5120                         {
5121                                 long apos;
5122                                 apos = pcht - Line->buf;
5123                                 *pcht = '\0';
5124                                 IncreaseBuf(Line, 1, -1);
5125                                 pcht = Line->buf + apos;
5126                         }
5127                         *pcht++ = *pch++;
5128                         Line->BufUsed++;
5129                         retlen++;
5130                 }
5131
5132                 len = pch - pos;
5133                 if (len > 0 && (*(pch - 1) == '\r') )
5134                 {
5135                         retlen--;
5136                         len --;
5137                         pcht --;
5138                         Line->BufUsed --;
5139                 }
5140                 *pcht = '\0';
5141
5142                 if ((pch >= pche) || (*pch == '\0'))
5143                 {
5144                         FlushStrBuf(IOBuf);
5145                         *Pos = NULL;
5146                         pch = NULL;
5147                         pos = 0;
5148                 }
5149
5150                 if ((pch != NULL) && 
5151                     (pch <= pche)) 
5152                 {
5153                         if (pch + 1 >= pche) {
5154                                 *Pos = NULL;
5155                                 FlushStrBuf(IOBuf);
5156                         }
5157                         else
5158                                 *Pos = pch + 1;
5159                         
5160                         return retlen;
5161                 }
5162                 else 
5163                         FlushStrBuf(IOBuf);
5164         }
5165
5166         /* If we come here, Pos is Unset since we read everything into Line, and now go for more. */
5167         
5168         if (IOBuf->BufSize - IOBuf->BufUsed < 10)
5169                 IncreaseBuf(IOBuf, 1, -1);
5170
5171         fdflags = fcntl(*fd, F_GETFL);
5172         IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
5173
5174         pLF = NULL;
5175         while ((nSuccessLess < timeout) && 
5176                (pLF == NULL) &&
5177                (*fd != -1)) {
5178                 if (IsNonBlock)
5179                 {
5180                         tv.tv_sec = 1;
5181                         tv.tv_usec = 0;
5182                 
5183                         FD_ZERO(&rfds);
5184                         FD_SET(*fd, &rfds);
5185                         if (select((*fd) + 1, &rfds, NULL, NULL, &tv) == -1) {
5186                                 *Error = strerror(errno);
5187                                 close (*fd);
5188                                 *fd = -1;
5189                                 if (*Error == NULL)
5190                                         *Error = ErrRBLF_SelectFailed;
5191                                 return -1;
5192                         }
5193                         if (! FD_ISSET(*fd, &rfds) != 0) {
5194                                 nSuccessLess ++;
5195                                 continue;
5196                         }
5197                 }
5198                 rlen = read(*fd, 
5199                             &IOBuf->buf[IOBuf->BufUsed], 
5200                             IOBuf->BufSize - IOBuf->BufUsed - 1);
5201                 if (rlen < 1) {
5202                         *Error = strerror(errno);
5203                         close(*fd);
5204                         *fd = -1;
5205                         return -1;
5206                 }
5207                 else if (rlen > 0) {
5208                         nSuccessLess = 0;
5209                         pLF = IOBuf->buf + IOBuf->BufUsed;
5210                         IOBuf->BufUsed += rlen;
5211                         IOBuf->buf[IOBuf->BufUsed] = '\0';
5212                         
5213                         pche = IOBuf->buf + IOBuf->BufUsed;
5214                         
5215                         while ((pLF < pche) && (*pLF != '\n'))
5216                                 pLF ++;
5217                         if ((pLF >= pche) || (*pLF == '\0'))
5218                                 pLF = NULL;
5219
5220                         if (IOBuf->BufUsed + 10 > IOBuf->BufSize)
5221                         {
5222                                 long apos = 0;
5223
5224                                 if (pLF != NULL) apos = pLF - IOBuf->buf;
5225                                 IncreaseBuf(IOBuf, 1, -1);      
5226                                 if (pLF != NULL) pLF = IOBuf->buf + apos;
5227                         }
5228
5229                         continue;
5230                 }
5231                 else
5232                 {
5233                         nSuccessLess++;
5234                 }
5235         }
5236         *Pos = NULL;
5237         if (pLF != NULL) {
5238                 pos = IOBuf->buf;
5239                 len = pLF - pos;
5240                 if (len > 0 && (*(pLF - 1) == '\r') )
5241                         len --;
5242                 StrBufAppendBufPlain(Line, ChrPtr(IOBuf), len, 0);
5243                 if (pLF + 1 >= IOBuf->buf + IOBuf->BufUsed)
5244                 {
5245                         FlushStrBuf(IOBuf);
5246                 }
5247                 else 
5248                         *Pos = pLF + 1;
5249                 return retlen + len;
5250         }
5251         *Error = ErrRBLF_NotEnoughSentFromServer;
5252         return -1;
5253
5254 }
5255
5256 static const char *ErrRBLF_BLOBPreConditionFailed="StrBufReadBLOB: Wrong arguments or invalid Filedescriptor";
5257 /**
5258  * @ingroup StrBuf_IO
5259  * @brief Input binary data from socket
5260  * flushes and closes the FD on error
5261  * @param Buf the buffer to get the input to
5262  * @param fd pointer to the filedescriptor to read
5263  * @param append Append to an existing string or replace?
5264  * @param nBytes the maximal number of bytes to read
5265  * @param Error strerror() on error 
5266  * @returns numbers of chars read
5267  */
5268 int StrBufReadBLOB(StrBuf *Buf, int *fd, int append, long nBytes, const char **Error)
5269 {
5270         int fdflags;
5271         int rlen;
5272         int nSuccessLess;
5273         int nRead = 0;
5274         char *ptr;
5275         int IsNonBlock;
5276         struct timeval tv;
5277         fd_set rfds;
5278
5279         if ((Buf == NULL) || (Buf->buf == NULL) || (*fd == -1))
5280         {
5281                 *Error = ErrRBLF_BLOBPreConditionFailed;
5282                 return -1;
5283         }
5284         if (!append)
5285                 FlushStrBuf(Buf);
5286         if (Buf->BufUsed + nBytes >= Buf->BufSize)
5287                 IncreaseBuf(Buf, 1, Buf->BufUsed + nBytes);
5288
5289         ptr = Buf->buf + Buf->BufUsed;
5290
5291         fdflags = fcntl(*fd, F_GETFL);
5292         IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
5293         nSuccessLess = 0;
5294         while ((nRead < nBytes) && 
5295                (*fd != -1)) 
5296         {
5297                 if (IsNonBlock)
5298                 {
5299                         tv.tv_sec = 1;
5300                         tv.tv_usec = 0;
5301                 
5302                         FD_ZERO(&rfds);
5303                         FD_SET(*fd, &rfds);
5304                         if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
5305                                 *Error = strerror(errno);
5306                                 close (*fd);
5307                                 *fd = -1;
5308                                 if (*Error == NULL)
5309                                         *Error = ErrRBLF_SelectFailed;
5310                                 return -1;
5311                         }
5312                         if (! FD_ISSET(*fd, &rfds) != 0) {
5313                                 nSuccessLess ++;
5314                                 continue;
5315                         }
5316                 }
5317
5318                 if ((rlen = read(*fd, 
5319                                  ptr,
5320                                  nBytes - nRead)) == -1) {
5321                         close(*fd);
5322                         *fd = -1;
5323                         *Error = strerror(errno);
5324                         return rlen;
5325                 }
5326                 nRead += rlen;
5327                 ptr += rlen;
5328                 Buf->BufUsed += rlen;
5329         }
5330         Buf->buf[Buf->BufUsed] = '\0';
5331         return nRead;
5332 }
5333
5334 const char *ErrRBB_BLOBFPreConditionFailed = "StrBufReadBLOBBuffered: to many selects; aborting.";
5335 const char *ErrRBB_too_many_selects        = "StrBufReadBLOBBuffered: to many selects; aborting.";
5336 /**
5337  * @ingroup StrBuf_BufferedIO
5338  * @brief Input binary data from socket
5339  * flushes and closes the FD on error
5340  * @param Blob put binary thing here
5341  * @param IOBuf the buffer to get the input to
5342  * @param Pos offset inside of IOBuf
5343  * @param fd pointer to the filedescriptor to read
5344  * @param append Append to an existing string or replace?
5345  * @param nBytes the maximal number of bytes to read
5346  * @param check whether we should search for '000\n' terminators in case of timeouts
5347  * @param Error strerror() on error 
5348  * @returns numbers of chars read
5349  */
5350 int StrBufReadBLOBBuffered(StrBuf *Blob, 
5351                            StrBuf *IOBuf, 
5352                            const char **Pos,
5353                            int *fd, 
5354                            int append, 
5355                            long nBytes, 
5356                            int check, 
5357                            const char **Error)
5358 {
5359         const char *pos;
5360         int fdflags;
5361         int rlen = 0;
5362         int nRead = 0;
5363         int nAlreadyRead = 0;
5364         int IsNonBlock;
5365         char *ptr;
5366         fd_set rfds;
5367         struct timeval tv;
5368         int nSuccessLess = 0;
5369         int MaxTries;
5370
5371         if ((Blob == NULL)  ||
5372             (*fd == -1)     ||
5373             (IOBuf == NULL) ||
5374             (Pos == NULL))
5375         {
5376                 if (Pos != NULL)
5377                         *Pos = NULL;
5378                 *Error = ErrRBB_BLOBFPreConditionFailed;
5379                 return -1;
5380         }
5381
5382         if (!append)
5383                 FlushStrBuf(Blob);
5384         if (Blob->BufUsed + nBytes >= Blob->BufSize) 
5385                 IncreaseBuf(Blob, append, Blob->BufUsed + nBytes);
5386         
5387         pos = *Pos;
5388
5389         if (pos != NULL)
5390                 rlen = pos - IOBuf->buf;
5391         rlen = IOBuf->BufUsed - rlen;
5392
5393
5394         if ((IOBuf->BufUsed > 0) && 
5395             (pos != NULL) && 
5396             (pos < IOBuf->buf + IOBuf->BufUsed)) 
5397         {
5398                 if (rlen < nBytes) {
5399                         memcpy(Blob->buf + Blob->BufUsed, pos, rlen);
5400                         Blob->BufUsed += rlen;
5401                         Blob->buf[Blob->BufUsed] = '\0';
5402                         nAlreadyRead = nRead = rlen;
5403                         *Pos = NULL; 
5404                 }
5405                 if (rlen >= nBytes) {
5406                         memcpy(Blob->buf + Blob->BufUsed, pos, nBytes);
5407                         Blob->BufUsed += nBytes;
5408                         Blob->buf[Blob->BufUsed] = '\0';
5409                         if (rlen == nBytes) {
5410                                 *Pos = NULL; 
5411                                 FlushStrBuf(IOBuf);
5412                         }
5413                         else 
5414                                 *Pos += nBytes;
5415                         return nBytes;
5416                 }
5417         }
5418
5419         FlushStrBuf(IOBuf);
5420         *Pos = NULL;
5421         if (IOBuf->BufSize < nBytes - nRead)
5422                 IncreaseBuf(IOBuf, 0, nBytes - nRead);
5423         ptr = IOBuf->buf;
5424
5425         fdflags = fcntl(*fd, F_GETFL);
5426         IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
5427         if (IsNonBlock)
5428                 MaxTries =   1000;
5429         else
5430                 MaxTries = 100000;
5431
5432         nBytes -= nRead;
5433         nRead = 0;
5434         while ((nSuccessLess < MaxTries) && 
5435                (nRead < nBytes) &&
5436                (*fd != -1)) {
5437                 if (IsNonBlock)
5438                 {
5439                         tv.tv_sec = 1;
5440                         tv.tv_usec = 0;
5441                 
5442                         FD_ZERO(&rfds);
5443                         FD_SET(*fd, &rfds);
5444                         if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
5445                                 *Error = strerror(errno);
5446                                 close (*fd);
5447                                 *fd = -1;
5448                                 if (*Error == NULL)
5449                                         *Error = ErrRBLF_SelectFailed;
5450                                 return -1;
5451                         }
5452                         if (! FD_ISSET(*fd, &rfds) != 0) {
5453                                 nSuccessLess ++;
5454                                 continue;
5455                         }
5456                 }
5457                 rlen = read(*fd, 
5458                             ptr,
5459                             IOBuf->BufSize - (ptr - IOBuf->buf));
5460                 if (rlen == -1) {
5461                         close(*fd);
5462                         *fd = -1;
5463                         *Error = strerror(errno);
5464                         return rlen;
5465                 }
5466                 else if (rlen == 0){
5467                         if ((check == NNN_TERM) && 
5468                             (nRead > 5) &&
5469                             (strncmp(IOBuf->buf + IOBuf->BufUsed - 5, "\n000\n", 5) == 0)) 
5470                         {
5471                                 StrBufPlain(Blob, HKEY("\n000\n"));
5472                                 StrBufCutRight(Blob, 5);
5473                                 return Blob->BufUsed;
5474                         }
5475                         else if (!IsNonBlock) 
5476                                 nSuccessLess ++;
5477                         else if (nSuccessLess > MaxTries) {
5478                                 FlushStrBuf(IOBuf);
5479                                 *Error = ErrRBB_too_many_selects;
5480                                 return -1;
5481                         }
5482                 }
5483                 else if (rlen > 0) {
5484                         nSuccessLess = 0;
5485                         nRead += rlen;
5486                         ptr += rlen;
5487                         IOBuf->BufUsed += rlen;
5488                 }
5489         }
5490         if (nSuccessLess >= MaxTries) {
5491                 FlushStrBuf(IOBuf);
5492                 *Error = ErrRBB_too_many_selects;
5493                 return -1;
5494         }
5495
5496         if (nRead > nBytes) {
5497                 *Pos = IOBuf->buf + nBytes;
5498         }
5499         Blob->buf[Blob->BufUsed] = '\0';
5500         StrBufAppendBufPlain(Blob, IOBuf->buf, nBytes, 0);
5501         if (*Pos == NULL) {
5502                 FlushStrBuf(IOBuf);
5503         }
5504         return nRead + nAlreadyRead;
5505 }
5506
5507 /**
5508  * @ingroup StrBuf_IO
5509  * @brief extract a "next line" from Buf; Ptr to persist across several iterations
5510  * @param LineBuf your line will be copied here.
5511  * @param Buf BLOB with lines of text...
5512  * @param Ptr moved arround to keep the next-line across several iterations
5513  *        has to be &NULL on start; will be &NotNULL on end of buffer
5514  * @returns size of remaining buffer
5515  */
5516 int StrBufSipLine(StrBuf *LineBuf, const StrBuf *Buf, const char **Ptr)
5517 {
5518         const char *aptr, *ptr, *eptr;
5519         char *optr, *xptr;
5520
5521         if ((Buf == NULL) ||
5522             (*Ptr == StrBufNOTNULL) ||
5523             (LineBuf == NULL)||
5524             (LineBuf->buf == NULL))
5525         {
5526                 *Ptr = StrBufNOTNULL;
5527                 return 0;
5528         }
5529
5530         FlushStrBuf(LineBuf);
5531         if (*Ptr==NULL)
5532                 ptr = aptr = Buf->buf;
5533         else
5534                 ptr = aptr = *Ptr;
5535
5536         optr = LineBuf->buf;
5537         eptr = Buf->buf + Buf->BufUsed;
5538         xptr = LineBuf->buf + LineBuf->BufSize - 1;
5539
5540         while ((ptr <= eptr) && 
5541                (*ptr != '\n') &&
5542                (*ptr != '\r') )
5543         {
5544                 *optr = *ptr;
5545                 optr++; ptr++;
5546                 if (optr == xptr) {
5547                         LineBuf->BufUsed = optr - LineBuf->buf;
5548                         IncreaseBuf(LineBuf,  1, LineBuf->BufUsed + 1);
5549                         optr = LineBuf->buf + LineBuf->BufUsed;
5550                         xptr = LineBuf->buf + LineBuf->BufSize - 1;
5551                 }
5552         }
5553
5554         if ((ptr >= eptr) && (optr > LineBuf->buf))
5555                 optr --;
5556         LineBuf->BufUsed = optr - LineBuf->buf;
5557         *optr = '\0';       
5558         if ((ptr <= eptr) && (*ptr == '\r'))
5559                 ptr ++;
5560         if ((ptr <= eptr) && (*ptr == '\n'))
5561                 ptr ++;
5562         
5563         if (ptr < eptr) {
5564                 *Ptr = ptr;
5565         }
5566         else {
5567                 *Ptr = StrBufNOTNULL;
5568         }
5569
5570         return Buf->BufUsed - (ptr - Buf->buf);
5571 }
5572
5573
5574 /**
5575  * @ingroup StrBuf_IO
5576  * @brief removes double slashes from pathnames
5577  * @param Dir directory string to filter
5578  * @param RemoveTrailingSlash allows / disallows trailing slashes
5579  */
5580 void StrBufStripSlashes(StrBuf *Dir, int RemoveTrailingSlash)
5581 {
5582         char *a, *b;
5583
5584         a = b = Dir->buf;
5585
5586         while (!IsEmptyStr(a)) {
5587                 if (*a == '/') {
5588                         while (*a == '/')
5589                                 a++;
5590                         *b = '/';
5591                         b++;
5592                 }
5593                 else {
5594                         *b = *a;
5595                         b++; a++;
5596                 }
5597         }
5598         if ((RemoveTrailingSlash) &&
5599             (b > Dir->buf) && 
5600             (*(b - 1) == '/')){
5601                 b--;
5602         }
5603         *b = '\0';
5604         Dir->BufUsed = b - Dir->buf;
5605 }
5606
5607