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