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