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