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