4f381dd4813d0766bc4d9adeff105bc809568459
[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)
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                 UserStart = 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                                 At = strchr(EmailStart, '@');
2906                         }
2907                         else { /* this is a local recipient... no domain, just a realname */
2908                                 EmailStart = UserStart;
2909                                 At = strchr(EmailStart, '@');
2910                                 if (At == NULL) {
2911                                         UserEnd = EmailEnd;
2912                                         EmailEnd = NULL;
2913                                 }
2914                                 else {
2915                                         EmailStart = UserStart;
2916                                         UserStart = NULL;
2917                                 }
2918                         }
2919                 }
2920
2921                 if ((UserStart != NULL) && (UserEnd != NULL))
2922                         StrBufPlain(UserName, UserStart, UserEnd - UserStart);
2923                 else if ((UserStart != NULL) && (UserEnd == NULL))
2924                         StrBufPlain(UserName, UserStart, UserEnd - UserStart);
2925                 else
2926                         FlushStrBuf(UserName);
2927
2928                 if ((EmailStart != NULL) && (EmailEnd != NULL))
2929                         StrBufPlain(EmailAddress, EmailStart, EmailEnd - EmailStart);
2930                 else if ((EmailStart != NULL) && (EmailEnd == NULL))
2931                         StrBufPlain(EmailAddress, EmailStart, EmailEnd - pche);
2932                 else 
2933                         FlushStrBuf(EmailAddress);
2934
2935                 AddRecipient(Target, UserName, EmailAddress, EncBuf);
2936
2937                 if (pch == NULL)
2938                         break;
2939                 
2940                 if ((pch != NULL) && (*pch == ','))
2941                         pch ++;
2942                 if (pch != NULL) while (isspace(*pch))
2943                         pch ++;
2944         }
2945         return Target;
2946 }
2947
2948
2949 /**
2950  * @ingroup StrBuf
2951  * @brief replaces all occurances of 'search' by 'replace'
2952  * @param buf Buffer to modify
2953  * @param search character to search
2954  * @param replace character to replace search by
2955  */
2956 void StrBufReplaceChars(StrBuf *buf, char search, char replace)
2957 {
2958         long i;
2959         if (buf == NULL)
2960                 return;
2961         for (i=0; i<buf->BufUsed; i++)
2962                 if (buf->buf[i] == search)
2963                         buf->buf[i] = replace;
2964
2965 }
2966
2967 /**
2968  * @ingroup StrBuf
2969  * @brief removes all \\r s from the string, or replaces them with \n if its not a combination of both.
2970  * @param buf Buffer to modify
2971  */
2972 void StrBufToUnixLF(StrBuf *buf)
2973 {
2974         char *pche, *pchS, *pchT;
2975         if (buf == NULL)
2976                 return;
2977
2978         pche = buf->buf + buf->BufUsed;
2979         pchS = pchT = buf->buf;
2980         while (pchS < pche)
2981         {
2982                 if (*pchS == '\r')
2983                 {
2984                         pchS ++;
2985                         if (*pchS != '\n') {
2986                                 *pchT = '\n';
2987                                 pchT++;
2988                         }
2989                 }
2990                 *pchT = *pchS;
2991                 pchT++; pchS++;
2992         }
2993         *pchT = '\0';
2994         buf->BufUsed = pchT - buf->buf;
2995 }
2996
2997
2998 /*******************************************************************************
2999  *                 Iconv Wrapper; RFC822 de/encoding                           *
3000  *******************************************************************************/
3001
3002 /**
3003  * @ingroup StrBuf_DeEnCoder
3004  * @brief Wrapper around iconv_open()
3005  * Our version adds aliases for non-standard Microsoft charsets
3006  * such as 'MS950', aliasing them to names like 'CP950'
3007  *
3008  * @param tocode        Target encoding
3009  * @param fromcode      Source encoding
3010  * @param pic           anonimized pointer to iconv struct
3011  */
3012 void  ctdl_iconv_open(const char *tocode, const char *fromcode, void *pic)
3013 {
3014 #ifdef HAVE_ICONV
3015         iconv_t ic = (iconv_t)(-1) ;
3016         ic = iconv_open(tocode, fromcode);
3017         if (ic == (iconv_t)(-1) ) {
3018                 char alias_fromcode[64];
3019                 if ( (strlen(fromcode) == 5) && (!strncasecmp(fromcode, "MS", 2)) ) {
3020                         safestrncpy(alias_fromcode, fromcode, sizeof alias_fromcode);
3021                         alias_fromcode[0] = 'C';
3022                         alias_fromcode[1] = 'P';
3023                         ic = iconv_open(tocode, alias_fromcode);
3024                 }
3025         }
3026         *(iconv_t *)pic = ic;
3027 #endif
3028 }
3029
3030
3031 /**
3032  * @ingroup StrBuf_DeEnCoder
3033  * @brief find one chunk of a RFC822 encoded string
3034  * @param Buffer where to search
3035  * @param bptr where to start searching
3036  * @returns found position, NULL if none.
3037  */
3038 static inline const char *FindNextEnd (const StrBuf *Buf, const char *bptr)
3039 {
3040         const char * end;
3041         /* Find the next ?Q? */
3042         if (Buf->BufUsed - (bptr - Buf->buf)  < 6)
3043                 return NULL;
3044
3045         end = strchr(bptr + 2, '?');
3046
3047         if (end == NULL)
3048                 return NULL;
3049
3050         if ((Buf->BufUsed - (end - Buf->buf) > 3) &&
3051             (((*(end + 1) == 'B') || (*(end + 1) == 'Q')) ||
3052              ((*(end + 1) == 'b') || (*(end + 1) == 'q'))) && 
3053             (*(end + 2) == '?')) {
3054                 /* skip on to the end of the cluster, the next ?= */
3055                 end = strstr(end + 3, "?=");
3056         }
3057         else
3058                 /* sort of half valid encoding, try to find an end. */
3059                 end = strstr(bptr, "?=");
3060         return end;
3061 }
3062
3063
3064
3065 /**
3066  * @ingroup StrBuf_DeEnCoder
3067  * @brief convert one buffer according to the preselected iconv pointer PIC
3068  * @param ConvertBuf buffer we need to translate
3069  * @param TmpBuf To share a workbuffer over several iterations. prepare to have it filled with useless stuff afterwards.
3070  * @param pic Pointer to the iconv-session Object
3071  */
3072 void StrBufConvert(StrBuf *ConvertBuf, StrBuf *TmpBuf, void *pic)
3073 {
3074 #ifdef HAVE_ICONV
3075         long trycount = 0;
3076         size_t siz;
3077         iconv_t ic;
3078         char *ibuf;                     /**< Buffer of characters to be converted */
3079         char *obuf;                     /**< Buffer for converted characters */
3080         size_t ibuflen;                 /**< Length of input buffer */
3081         size_t obuflen;                 /**< Length of output buffer */
3082
3083
3084         /* since we're converting to utf-8, one glyph may take up to 6 bytes */
3085         if (ConvertBuf->BufUsed * 6 >= TmpBuf->BufSize)
3086                 IncreaseBuf(TmpBuf, 0, ConvertBuf->BufUsed * 6);
3087 TRYAGAIN:
3088         ic = *(iconv_t*)pic;
3089         ibuf = ConvertBuf->buf;
3090         ibuflen = ConvertBuf->BufUsed;
3091         obuf = TmpBuf->buf;
3092         obuflen = TmpBuf->BufSize;
3093         
3094         siz = iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
3095
3096         if (siz < 0) {
3097                 if (errno == E2BIG) {
3098                         trycount ++;                    
3099                         IncreaseBuf(TmpBuf, 0, 0);
3100                         if (trycount < 5) 
3101                                 goto TRYAGAIN;
3102
3103                 }
3104                 else if (errno == EILSEQ){ 
3105                         /* hm, invalid utf8 sequence... what to do now? */
3106                         /* An invalid multibyte sequence has been encountered in the input */
3107                 }
3108                 else if (errno == EINVAL) {
3109                         /* An incomplete multibyte sequence has been encountered in the input. */
3110                 }
3111
3112                 FlushStrBuf(TmpBuf);
3113         }
3114         else {
3115                 TmpBuf->BufUsed = TmpBuf->BufSize - obuflen;
3116                 TmpBuf->buf[TmpBuf->BufUsed] = '\0';
3117                 
3118                 /* little card game: wheres the red lady? */
3119                 SwapBuffers(ConvertBuf, TmpBuf);
3120                 FlushStrBuf(TmpBuf);
3121         }
3122 #endif
3123 }
3124
3125
3126 /**
3127  * @ingroup StrBuf_DeEnCoder
3128  * @brief catches one RFC822 encoded segment, and decodes it.
3129  * @param Target buffer to fill with result
3130  * @param DecodeMe buffer with stuff to process
3131  * @param SegmentStart points to our current segment in DecodeMe
3132  * @param SegmentEnd Points to the end of our current segment in DecodeMe
3133  * @param ConvertBuf Workbuffer shared between several iterations. Random content; needs to be valid
3134  * @param ConvertBuf2 Workbuffer shared between several iterations. Random content; needs to be valid
3135  * @param FoundCharset Characterset to default decoding to; if we find another we will overwrite it.
3136  */
3137 inline static void DecodeSegment(StrBuf *Target, 
3138                                  const StrBuf *DecodeMe, 
3139                                  const char *SegmentStart, 
3140                                  const char *SegmentEnd, 
3141                                  StrBuf *ConvertBuf,
3142                                  StrBuf *ConvertBuf2, 
3143                                  StrBuf *FoundCharset)
3144 {
3145         StrBuf StaticBuf;
3146         char charset[128];
3147         char encoding[16];
3148 #ifdef HAVE_ICONV
3149         iconv_t ic = (iconv_t)(-1);
3150 #else
3151         void *ic = NULL;
3152 #endif
3153         /* Now we handle foreign character sets properly encoded
3154          * in RFC2047 format.
3155          */
3156         StaticBuf.buf = (char*) SegmentStart; /*< it will just be read there... */
3157         StaticBuf.BufUsed = SegmentEnd - SegmentStart;
3158         StaticBuf.BufSize = DecodeMe->BufSize - (SegmentStart - DecodeMe->buf);
3159         extract_token(charset, SegmentStart, 1, '?', sizeof charset);
3160         if (FoundCharset != NULL) {
3161                 FlushStrBuf(FoundCharset);
3162                 StrBufAppendBufPlain(FoundCharset, charset, -1, 0);
3163         }
3164         extract_token(encoding, SegmentStart, 2, '?', sizeof encoding);
3165         StrBufExtract_token(ConvertBuf, &StaticBuf, 3, '?');
3166         
3167         *encoding = toupper(*encoding);
3168         if (*encoding == 'B') { /**< base64 */
3169                 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3170                         IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3171                 ConvertBuf2->BufUsed = CtdlDecodeBase64(ConvertBuf2->buf, 
3172                                                         ConvertBuf->buf, 
3173                                                         ConvertBuf->BufUsed);
3174         }
3175         else if (*encoding == 'Q') {    /**< quoted-printable */
3176                 long pos;
3177                 
3178                 pos = 0;
3179                 while (pos < ConvertBuf->BufUsed)
3180                 {
3181                         if (ConvertBuf->buf[pos] == '_') 
3182                                 ConvertBuf->buf[pos] = ' ';
3183                         pos++;
3184                 }
3185                 
3186                 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3187                         IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3188
3189                 ConvertBuf2->BufUsed = CtdlDecodeQuotedPrintable(
3190                         ConvertBuf2->buf, 
3191                         ConvertBuf->buf,
3192                         ConvertBuf->BufUsed);
3193         }
3194         else {
3195                 StrBufAppendBuf(ConvertBuf2, ConvertBuf, 0);
3196         }
3197 #ifdef HAVE_ICONV
3198         ctdl_iconv_open("UTF-8", charset, &ic);
3199         if (ic != (iconv_t)(-1) ) {             
3200 #endif
3201                 StrBufConvert(ConvertBuf2, ConvertBuf, &ic);
3202                 StrBufAppendBuf(Target, ConvertBuf2, 0);
3203 #ifdef HAVE_ICONV
3204                 iconv_close(ic);
3205         }
3206         else {
3207                 StrBufAppendBufPlain(Target, HKEY("(unreadable)"), 0);
3208         }
3209 #endif
3210 }
3211
3212 /**
3213  * @ingroup StrBuf_DeEnCoder
3214  * @brief Handle subjects with RFC2047 encoding such as: [deprecated old syntax!]
3215  * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3216  * @param Target where to put the decoded string to 
3217  * @param DecodeMe buffer with encoded string
3218  * @param DefaultCharset if we don't find one, which should we use?
3219  * @param FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string, 
3220  *        put it here for later use where no string might be known.
3221  */
3222 void StrBuf_RFC822_to_Utf8(StrBuf *Target, const StrBuf *DecodeMe, const StrBuf* DefaultCharset, StrBuf *FoundCharset)
3223 {
3224         StrBuf *ConvertBuf;
3225         StrBuf *ConvertBuf2;
3226         ConvertBuf = NewStrBufPlain(NULL, StrLength(DecodeMe));
3227         ConvertBuf2 = NewStrBufPlain(NULL, StrLength(DecodeMe));
3228         
3229         StrBuf_RFC822_2_Utf8(Target, 
3230                              DecodeMe, 
3231                              DefaultCharset, 
3232                              FoundCharset, 
3233                              ConvertBuf, 
3234                              ConvertBuf2);
3235         FreeStrBuf(&ConvertBuf);
3236         FreeStrBuf(&ConvertBuf2);
3237 }
3238
3239 /**
3240  * @ingroup StrBuf_DeEnCoder
3241  * @brief Handle subjects with RFC2047 encoding such as:
3242  * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3243  * @param Target where to put the decoded string to 
3244  * @param DecodeMe buffer with encoded string
3245  * @param DefaultCharset if we don't find one, which should we use?
3246  * @param FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string, 
3247  *        put it here for later use where no string might be known.
3248  * @param ConvertBuf workbuffer. feed in, you shouldn't care about its content.
3249  * @param ConvertBuf2 workbuffer. feed in, you shouldn't care about its content.
3250  */
3251 void StrBuf_RFC822_2_Utf8(StrBuf *Target, 
3252                           const StrBuf *DecodeMe, 
3253                           const StrBuf* DefaultCharset, 
3254                           StrBuf *FoundCharset, 
3255                           StrBuf *ConvertBuf, 
3256                           StrBuf *ConvertBuf2)
3257 {
3258         StrBuf *DecodedInvalidBuf = NULL;
3259         const StrBuf *DecodeMee = DecodeMe;
3260         const char *start, *end, *next, *nextend, *ptr = NULL;
3261 #ifdef HAVE_ICONV
3262         iconv_t ic = (iconv_t)(-1) ;
3263 #endif
3264         const char *eptr;
3265         int passes = 0;
3266         int i, len;
3267         int illegal_non_rfc2047_encoding = 0;
3268
3269         /* Sometimes, badly formed messages contain strings which were simply
3270          *  written out directly in some foreign character set instead of
3271          *  using RFC2047 encoding.  This is illegal but we will attempt to
3272          *  handle it anyway by converting from a user-specified default
3273          *  charset to UTF-8 if we see any nonprintable characters.
3274          */
3275         
3276         len = StrLength(DecodeMe);
3277         for (i=0; i<DecodeMe->BufUsed; ++i) {
3278                 if ((DecodeMe->buf[i] < 32) || (DecodeMe->buf[i] > 126)) {
3279                         illegal_non_rfc2047_encoding = 1;
3280                         break;
3281                 }
3282         }
3283
3284         if ((illegal_non_rfc2047_encoding) &&
3285             (strcasecmp(ChrPtr(DefaultCharset), "UTF-8")) && 
3286             (strcasecmp(ChrPtr(DefaultCharset), "us-ascii")) )
3287         {
3288 #ifdef HAVE_ICONV
3289                 ctdl_iconv_open("UTF-8", ChrPtr(DefaultCharset), &ic);
3290                 if (ic != (iconv_t)(-1) ) {
3291                         DecodedInvalidBuf = NewStrBufDup(DecodeMe);
3292                         StrBufConvert(DecodedInvalidBuf, ConvertBuf, &ic);///TODO: don't void const?
3293                         DecodeMee = DecodedInvalidBuf;
3294                         iconv_close(ic);
3295                 }
3296 #endif
3297         }
3298
3299         /* pre evaluate the first pair */
3300         nextend = end = NULL;
3301         len = StrLength(DecodeMee);
3302         start = strstr(DecodeMee->buf, "=?");
3303         eptr = DecodeMee->buf + DecodeMee->BufUsed;
3304         if (start != NULL) 
3305                 end = FindNextEnd (DecodeMee, start + 2);
3306         else {
3307                 StrBufAppendBuf(Target, DecodeMee, 0);
3308                 FreeStrBuf(&DecodedInvalidBuf);
3309                 return;
3310         }
3311
3312
3313         if (start != DecodeMee->buf) {
3314                 long nFront;
3315                 
3316                 nFront = start - DecodeMee->buf;
3317                 StrBufAppendBufPlain(Target, DecodeMee->buf, nFront, 0);
3318                 len -= nFront;
3319         }
3320         /*
3321          * Since spammers will go to all sorts of absurd lengths to get their
3322          * messages through, there are LOTS of corrupt headers out there.
3323          * So, prevent a really badly formed RFC2047 header from throwing
3324          * this function into an infinite loop.
3325          */
3326         while ((start != NULL) && 
3327                (end != NULL) && 
3328                (start < eptr) && 
3329                (end < eptr) && 
3330                (passes < 20))
3331         {
3332                 passes++;
3333                 DecodeSegment(Target, 
3334                               DecodeMee, 
3335                               start, 
3336                               end, 
3337                               ConvertBuf,
3338                               ConvertBuf2,
3339                               FoundCharset);
3340                 
3341                 next = strstr(end, "=?");
3342                 nextend = NULL;
3343                 if ((next != NULL) && 
3344                     (next < eptr))
3345                         nextend = FindNextEnd(DecodeMee, next);
3346                 if (nextend == NULL)
3347                         next = NULL;
3348
3349                 /* did we find two partitions */
3350                 if ((next != NULL) && 
3351                     ((next - end) > 2))
3352                 {
3353                         ptr = end + 2;
3354                         while ((ptr < next) && 
3355                                (isspace(*ptr) ||
3356                                 (*ptr == '\r') ||
3357                                 (*ptr == '\n') || 
3358                                 (*ptr == '\t')))
3359                                 ptr ++;
3360                         /* 
3361                          * did we find a gab just filled with blanks?
3362                          * if not, copy its stuff over.
3363                          */
3364                         if (ptr != next)
3365                         {
3366                                 StrBufAppendBufPlain(Target, 
3367                                                      end + 2, 
3368                                                      next - end - 2,
3369                                                      0);
3370                         }
3371                 }
3372                 /* our next-pair is our new first pair now. */
3373                 ptr = end + 2;
3374                 start = next;
3375                 end = nextend;
3376         }
3377         end = ptr;
3378         nextend = DecodeMee->buf + DecodeMee->BufUsed;
3379         if ((end != NULL) && (end < nextend)) {
3380                 ptr = end;
3381                 while ( (ptr < nextend) &&
3382                         (isspace(*ptr) ||
3383                          (*ptr == '\r') ||
3384                          (*ptr == '\n') || 
3385                          (*ptr == '\t')))
3386                         ptr ++;
3387                 if (ptr < nextend)
3388                         StrBufAppendBufPlain(Target, end, nextend - end, 0);
3389         }
3390         FreeStrBuf(&DecodedInvalidBuf);
3391 }
3392
3393 /*******************************************************************************
3394  *                   Manipulating UTF-8 Strings                                *
3395  *******************************************************************************/
3396
3397 /**
3398  * @ingroup StrBuf
3399  * @brief evaluate the length of an utf8 special character sequence
3400  * @param Char the character to examine
3401  * @returns width of utf8 chars in bytes; if the sequence is broken 0 is returned; 1 if its simply ASCII.
3402  */
3403 static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE)
3404 {
3405         int n = 0;
3406         unsigned char test = (1<<7);
3407
3408         if ((*CharS & 0xC0) != 0xC0) 
3409                 return 1;
3410
3411         while ((n < 8) && 
3412                ((test & ((unsigned char)*CharS)) != 0)) 
3413         {
3414                 test = test >> 1;
3415                 n ++;
3416         }
3417         if ((n > 6) || ((CharE - CharS) < n))
3418                 n = 0;
3419         return n;
3420 }
3421
3422 /**
3423  * @ingroup StrBuf
3424  * @brief detect whether this char starts an utf-8 encoded char
3425  * @param Char character to inspect
3426  * @returns yes or no
3427  */
3428 static inline int Ctdl_IsUtf8SequenceStart(const char Char)
3429 {
3430 /** 11??.???? indicates an UTF8 Sequence. */
3431         return ((Char & 0xC0) == 0xC0);
3432 }
3433
3434 /**
3435  * @ingroup StrBuf
3436  * @brief measure the number of glyphs in an UTF8 string...
3437  * @param Buf string to measure
3438  * @returns the number of glyphs in Buf
3439  */
3440 long StrBuf_Utf8StrLen(StrBuf *Buf)
3441 {
3442         int n = 0;
3443         int m = 0;
3444         char *aptr, *eptr;
3445
3446         if ((Buf == NULL) || (Buf->BufUsed == 0))
3447                 return 0;
3448         aptr = Buf->buf;
3449         eptr = Buf->buf + Buf->BufUsed;
3450         while ((aptr < eptr) && (*aptr != '\0')) {
3451                 if (Ctdl_IsUtf8SequenceStart(*aptr)){
3452                         m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
3453                         while ((aptr < eptr) && (*aptr++ != '\0')&& (m-- > 0) );
3454                         n ++;
3455                 }
3456                 else {
3457                         n++;
3458                         aptr++;
3459                 }
3460         }
3461         return n;
3462 }
3463
3464 /**
3465  * @ingroup StrBuf
3466  * @brief cuts a string after maxlen glyphs
3467  * @param Buf string to cut to maxlen glyphs
3468  * @param maxlen how long may the string become?
3469  * @returns current length of the string
3470  */
3471 long StrBuf_Utf8StrCut(StrBuf *Buf, int maxlen)
3472 {
3473         char *aptr, *eptr;
3474         int n = 0, m = 0;
3475
3476         aptr = Buf->buf;
3477         eptr = Buf->buf + Buf->BufUsed;
3478         while ((aptr < eptr) && (*aptr != '\0')) {
3479                 if (Ctdl_IsUtf8SequenceStart(*aptr)){
3480                         m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
3481                         while ((*aptr++ != '\0') && (m-- > 0));
3482                         n ++;
3483                 }
3484                 else {
3485                         n++;
3486                         aptr++;
3487                 }
3488                 if (n > maxlen) {
3489                         *aptr = '\0';
3490                         Buf->BufUsed = aptr - Buf->buf;
3491                         return Buf->BufUsed;
3492                 }                       
3493         }
3494         return Buf->BufUsed;
3495
3496 }
3497
3498
3499
3500
3501
3502 /*******************************************************************************
3503  *                               wrapping ZLib                                 *
3504  *******************************************************************************/
3505
3506 #ifdef HAVE_ZLIB
3507 #define DEF_MEM_LEVEL 8 /*< memlevel??? */
3508 #define OS_CODE 0x03    /*< unix */
3509
3510 /**
3511  * @ingroup StrBuf_DeEnCoder
3512  * @brief uses the same calling syntax as compress2(), but it
3513  *   creates a stream compatible with HTTP "Content-encoding: gzip"
3514  * @param dest compressed buffer
3515  * @param destLen length of the compresed data 
3516  * @param source source to encode
3517  * @param sourceLen length of source to encode 
3518  * @param level compression level
3519  */
3520 int ZEXPORT compress_gzip(Bytef * dest,
3521                           size_t * destLen,
3522                           const Bytef * source,
3523                           uLong sourceLen,     
3524                           int level)
3525 {
3526         const int gz_magic[2] = { 0x1f, 0x8b }; /* gzip magic header */
3527
3528         /* write gzip header */
3529         snprintf((char *) dest, *destLen, 
3530                  "%c%c%c%c%c%c%c%c%c%c",
3531                  gz_magic[0], gz_magic[1], Z_DEFLATED,
3532                  0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /* xflags */ ,
3533                  OS_CODE);
3534
3535         /* normal deflate */
3536         z_stream stream;
3537         int err;
3538         stream.next_in = (Bytef *) source;
3539         stream.avail_in = (uInt) sourceLen;
3540         stream.next_out = dest + 10L;   // after header
3541         stream.avail_out = (uInt) * destLen;
3542         if ((uLong) stream.avail_out != *destLen)
3543                 return Z_BUF_ERROR;
3544
3545         stream.zalloc = (alloc_func) 0;
3546         stream.zfree = (free_func) 0;
3547         stream.opaque = (voidpf) 0;
3548
3549         err = deflateInit2(&stream, level, Z_DEFLATED, -MAX_WBITS,
3550                            DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
3551         if (err != Z_OK)
3552                 return err;
3553
3554         err = deflate(&stream, Z_FINISH);
3555         if (err != Z_STREAM_END) {
3556                 deflateEnd(&stream);
3557                 return err == Z_OK ? Z_BUF_ERROR : err;
3558         }
3559         *destLen = stream.total_out + 10L;
3560
3561         /* write CRC and Length */
3562         uLong crc = crc32(0L, source, sourceLen);
3563         int n;
3564         for (n = 0; n < 4; ++n, ++*destLen) {
3565                 dest[*destLen] = (int) (crc & 0xff);
3566                 crc >>= 8;
3567         }
3568         uLong len = stream.total_in;
3569         for (n = 0; n < 4; ++n, ++*destLen) {
3570                 dest[*destLen] = (int) (len & 0xff);
3571                 len >>= 8;
3572         }
3573         err = deflateEnd(&stream);
3574         return err;
3575 }
3576 #endif
3577
3578
3579 /**
3580  * @ingroup StrBuf_DeEnCoder
3581  * @brief compress the buffer with gzip
3582  * Attention! If you feed this a Const String, you must maintain the uncompressed buffer yourself!
3583  * @param Buf buffer whose content is to be gzipped
3584  */
3585 int CompressBuffer(StrBuf *Buf)
3586 {
3587 #ifdef HAVE_ZLIB
3588         char *compressed_data = NULL;
3589         size_t compressed_len, bufsize;
3590         int i = 0;
3591
3592         bufsize = compressed_len = Buf->BufUsed +  (Buf->BufUsed / 100) + 100;
3593         compressed_data = malloc(compressed_len);
3594         
3595         if (compressed_data == NULL)
3596                 return -1;
3597         /* Flush some space after the used payload so valgrind shuts up... */
3598         while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
3599                 Buf->buf[Buf->BufUsed + i++] = '\0';
3600         if (compress_gzip((Bytef *) compressed_data,
3601                           &compressed_len,
3602                           (Bytef *) Buf->buf,
3603                           (uLongf) Buf->BufUsed, Z_BEST_SPEED) == Z_OK) {
3604                 if (!Buf->ConstBuf)
3605                         free(Buf->buf);
3606                 Buf->buf = compressed_data;
3607                 Buf->BufUsed = compressed_len;
3608                 Buf->BufSize = bufsize;
3609                 /* Flush some space after the used payload so valgrind shuts up... */
3610                 i = 0;
3611                 while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
3612                         Buf->buf[Buf->BufUsed + i++] = '\0';
3613                 return 1;
3614         } else {
3615                 free(compressed_data);
3616         }
3617 #endif  /* HAVE_ZLIB */
3618         return 0;
3619 }
3620
3621 /*******************************************************************************
3622  *           File I/O; Callbacks to libevent                                   *
3623  *******************************************************************************/
3624
3625 long StrBuf_read_one_chunk_callback (int fd, short event, IOBuffer *FB)
3626 {
3627         long bufremain = 0;
3628         int n;
3629         
3630         if ((FB == NULL) || (FB->Buf == NULL))
3631                 return -1;
3632
3633         /*
3634          * check whether the read pointer is somewhere in a range 
3635          * where a cut left is inexpensive
3636          */
3637
3638         if (FB->ReadWritePointer != NULL)
3639         {
3640                 long already_read;
3641                 
3642                 already_read = FB->ReadWritePointer - FB->Buf->buf;
3643                 bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
3644
3645                 if (already_read != 0) {
3646                         long unread;
3647                         
3648                         unread = FB->Buf->BufUsed - already_read;
3649
3650                         /* else nothing to compact... */
3651                         if (unread == 0) {
3652                                 FB->ReadWritePointer = FB->Buf->buf;
3653                                 bufremain = FB->Buf->BufSize;                   
3654                         }
3655                         else if ((unread < 64) || 
3656                                  (bufremain < already_read))
3657                         {
3658                                 /* 
3659                                  * if its just a tiny bit remaining, or we run out of space... 
3660                                  * lets tidy up.
3661                                  */
3662                                 FB->Buf->BufUsed = unread;
3663                                 if (unread < already_read)
3664                                         memcpy(FB->Buf->buf, FB->ReadWritePointer, unread);
3665                                 else
3666                                         memmove(FB->Buf->buf, FB->ReadWritePointer, unread);
3667                                 FB->ReadWritePointer = FB->Buf->buf;
3668                                 bufremain = FB->Buf->BufSize - unread - 1;
3669                         }
3670                         else if (bufremain < (FB->Buf->BufSize / 10))
3671                         {
3672                                 /* get a bigger buffer */ 
3673
3674                                 IncreaseBuf(FB->Buf, 0, FB->Buf->BufUsed + 1);
3675
3676                                 FB->ReadWritePointer = FB->Buf->buf + unread;
3677
3678                                 bufremain = FB->Buf->BufSize - unread - 1;
3679 /*TODO: special increase function that won't copy the already read! */
3680                         }
3681                 }
3682                 else if (bufremain < 10) {
3683                         IncreaseBuf(FB->Buf, 1, FB->Buf->BufUsed + 10);
3684                         
3685                         FB->ReadWritePointer = FB->Buf->buf;
3686                         
3687                         bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
3688                 }
3689                 
3690         }
3691         else {
3692                 FB->ReadWritePointer = FB->Buf->buf;
3693                 bufremain = FB->Buf->BufSize - 1;
3694         }
3695
3696         n = read(fd, FB->Buf->buf + FB->Buf->BufUsed, bufremain);
3697
3698         if (n > 0) {
3699                 FB->Buf->BufUsed += n;
3700                 FB->Buf->buf[FB->Buf->BufUsed] = '\0';
3701         }
3702         return n;
3703 }
3704
3705 int StrBuf_write_one_chunk_callback(int fd, short event, IOBuffer *FB)
3706 {
3707         long WriteRemain;
3708         int n;
3709
3710         if ((FB == NULL) || (FB->Buf == NULL))
3711                 return -1;
3712
3713         if (FB->ReadWritePointer != NULL)
3714         {
3715                 WriteRemain = FB->Buf->BufUsed - 
3716                         (FB->ReadWritePointer - 
3717                          FB->Buf->buf);
3718         }
3719         else {
3720                 FB->ReadWritePointer = FB->Buf->buf;
3721                 WriteRemain = FB->Buf->BufUsed;
3722         }
3723
3724         n = write(fd, FB->ReadWritePointer, WriteRemain);
3725         if (n > 0) {
3726                 FB->ReadWritePointer += n;
3727
3728                 if (FB->ReadWritePointer == 
3729                     FB->Buf->buf + FB->Buf->BufUsed)
3730                 {
3731                         FlushStrBuf(FB->Buf);
3732                         FB->ReadWritePointer = NULL;
3733                         return 0;
3734                 }
3735         // check whether we've got something to write
3736         // get the maximum chunk plus the pointer we can send
3737         // write whats there
3738         // if not all was sent, remember the send pointer for the next time
3739                 return FB->ReadWritePointer - FB->Buf->buf + FB->Buf->BufUsed;
3740         }
3741         return n;
3742 }
3743
3744 /**
3745  * @ingroup StrBuf_IO
3746  * @brief extract a "next line" from Buf; Ptr to persist across several iterations
3747  * @param LineBuf your line will be copied here.
3748  * @param FB BLOB with lines of text...
3749  * @param Ptr moved arround to keep the next-line across several iterations
3750  *        has to be &NULL on start; will be &NotNULL on end of buffer
3751  * @returns size of copied buffer
3752  */
3753 eReadState StrBufChunkSipLine(StrBuf *LineBuf, IOBuffer *FB)
3754 {
3755         const char *aptr, *ptr, *eptr;
3756         char *optr, *xptr;
3757
3758         if ((FB->Buf == NULL) || (FB->ReadWritePointer == StrBufNOTNULL)) {
3759                 FB->ReadWritePointer = StrBufNOTNULL;
3760                 return eReadFail;
3761         }
3762
3763         FlushStrBuf(LineBuf);
3764         if (FB->ReadWritePointer == NULL)
3765                 ptr = aptr = FB->Buf->buf;
3766         else
3767                 ptr = aptr = FB->ReadWritePointer;
3768
3769         optr = LineBuf->buf;
3770         eptr = FB->Buf->buf + FB->Buf->BufUsed;
3771         xptr = LineBuf->buf + LineBuf->BufSize - 1;
3772
3773         while ((ptr <= eptr) && 
3774                (*ptr != '\n') &&
3775                (*ptr != '\r') )
3776         {
3777                 *optr = *ptr;
3778                 optr++; ptr++;
3779                 if (optr == xptr) {
3780                         LineBuf->BufUsed = optr - LineBuf->buf;
3781                         IncreaseBuf(LineBuf,  1, LineBuf->BufUsed + 1);
3782                         optr = LineBuf->buf + LineBuf->BufUsed;
3783                         xptr = LineBuf->buf + LineBuf->BufSize - 1;
3784                 }
3785         }
3786
3787         if (ptr >= eptr) {
3788                 if (optr > LineBuf->buf)
3789                         optr --;
3790                 if ((*(ptr - 1) != '\r') && (*(ptr - 1) != '\n')) {
3791                         LineBuf->BufUsed = optr - LineBuf->buf;
3792                         *optr = '\0';
3793                         if ((FB->ReadWritePointer != NULL) && 
3794                             (FB->ReadWritePointer != FB->Buf->buf))
3795                         {
3796                                 /* Ok, the client application read all the data 
3797                                    it was interested in so far. Since there is more to read, 
3798                                    we now shrink the buffer, and move the rest over.
3799                                 */
3800                                 StrBufCutLeft(FB->Buf, 
3801                                               FB->ReadWritePointer - FB->Buf->buf);
3802                                 FB->ReadWritePointer = FB->Buf->buf;
3803                         }
3804                         return eMustReadMore;
3805                 }
3806         }
3807         LineBuf->BufUsed = optr - LineBuf->buf;
3808         *optr = '\0';       
3809         if ((ptr <= eptr) && (*ptr == '\r'))
3810                 ptr ++;
3811         if ((ptr <= eptr) && (*ptr == '\n'))
3812                 ptr ++;
3813         
3814         if (ptr < eptr) {
3815                 FB->ReadWritePointer = ptr;
3816         }
3817         else {
3818                 FlushStrBuf(FB->Buf);
3819                 FB->ReadWritePointer = NULL;
3820         }
3821
3822         return eReadSuccess;
3823 }
3824
3825 /**
3826  * @ingroup StrBuf_CHUNKED_IO
3827  * @brief check whether the chunk-buffer has more data waiting or not.
3828  * @param FB Chunk-Buffer to inspect
3829  */
3830 eReadState StrBufCheckBuffer(IOBuffer *FB)
3831 {
3832         if (FB == NULL)
3833                 return eReadFail;
3834         if (FB->Buf->BufUsed == 0)
3835                 return eReadSuccess;
3836         if (FB->ReadWritePointer == NULL)
3837                 return eBufferNotEmpty;
3838         if (FB->Buf->buf + FB->Buf->BufUsed > FB->ReadWritePointer)
3839                 return eBufferNotEmpty;
3840         return eReadSuccess;
3841 }
3842
3843 long IOBufferStrLength(IOBuffer *FB)
3844 {
3845         if (FB->ReadWritePointer == NULL)
3846                 return StrLength(FB->Buf);
3847         
3848         return StrLength(FB->Buf) - (FB->ReadWritePointer - FB->Buf->buf);
3849 }
3850
3851 void FDIOBufferInit(FDIOBuffer *FDB, IOBuffer *IO, int FD, long TotalSendSize)
3852 {
3853         memset(FDB, 0, sizeof(FDIOBuffer));
3854         FDB->ChunkSize = 
3855                 FDB->TotalSendSize = TotalSendSize;
3856         FDB->IOB = IO;
3857 #ifndef LINUX_SENDFILE
3858         FDB->ChunkBuffer = NewStrBufPlain(NULL, TotalSendSize + 1);
3859 #else
3860         pipe(FDB->SplicePipe);
3861 #endif
3862         FDB->OtherFD = FD;
3863 }
3864
3865 void FDIOBufferDelete(FDIOBuffer *FDB)
3866 {
3867 #ifndef LINUX_SENDFILE
3868         FreeStrBuf(&FDB->ChunkBuffer);
3869 #else
3870         close(FDB->SplicePipe[0]);
3871         close(FDB->SplicePipe[1]);
3872         
3873 #endif
3874         close(FDB->OtherFD);
3875         memset(FDB, 0, sizeof(FDIOBuffer));     
3876 }
3877
3878 int FileSendChunked(FDIOBuffer *FDB, const char **Err)
3879 {
3880         char *pRead;
3881         long nRead = 0;
3882         
3883 #ifdef LINUX_SENDFILE
3884         ssize_t sent;
3885         sent = sendfile(FDB->IOB->fd, FDB->OtherFD, &FDB->TotalSentAlready, FDB->ChunkSendRemain);
3886         if (sent == -1)
3887         {
3888                 *Err = strerror(errno);
3889                 return sent;
3890         }
3891         FDB->ChunkSendRemain -= sent;
3892         FDB->TotalSentAlready += sent;
3893         return FDB->ChunkSendRemain;
3894 #else
3895         pRead = FDB->ChunkBuffer->buf;
3896         while ((FDB->ChunkBuffer->BufUsed < FDB->TotalSendSize) && (nRead >= 0))
3897         {
3898                 nRead = read(FDB->OtherFD, pRead, FDB->TotalSendSize - FDB->ChunkBuffer->BufUsed);
3899                 if (nRead > 0) {
3900                         FDB->ChunkBuffer->BufUsed += nRead;
3901                         FDB->ChunkBuffer->buf[FDB->ChunkBuffer->BufUsed] = '\0';
3902                 }
3903                 else if (nRead == 0) {}
3904                 else return nRead;
3905                 
3906         }
3907
3908         nRead = write(FDB->IOB->fd, FDB->ChunkBuffer->buf + FDB->TotalSentAlready, FDB->ChunkSendRemain);
3909
3910         if (nRead >= 0) {
3911                 FDB->TotalSentAlready += nRead;
3912                 FDB->ChunkSendRemain -= nRead;
3913                 return FDB->ChunkSendRemain;
3914         }
3915         else {
3916                 return nRead;
3917         }
3918 #endif
3919 }
3920
3921 int FileRecvChunked(FDIOBuffer *FDB, const char **Err)
3922 {
3923         ssize_t sent, pipesize;
3924
3925 #ifdef LINUX_SENDFILE
3926
3927         pipesize = splice(FDB->IOB->fd, NULL, 
3928                           FDB->SplicePipe[1], NULL, 
3929                           FDB->ChunkSendRemain, 
3930                           SPLICE_F_MORE | SPLICE_F_MOVE|SPLICE_F_NONBLOCK);
3931         if (pipesize == -1)
3932         {
3933                 *Err = strerror(errno);
3934                 return pipesize;
3935         }
3936         
3937         sent = splice(FDB->SplicePipe[0], NULL, 
3938                       FDB->OtherFD, &FDB->TotalSentAlready, 
3939                       pipesize, SPLICE_F_MORE | SPLICE_F_MOVE);
3940         if (sent == -1)
3941         {
3942                 *Err = strerror(errno);
3943                 return sent;
3944         }
3945         FDB->ChunkSendRemain -= sent;
3946         return sent;
3947 #else
3948         
3949         sent = read(FDB->IOB->fd, FDB->ChunkBuffer->buf, FDB->ChunkSendRemain);
3950         if (sent > 0) {
3951                 int nWritten = 0;
3952                 int rc; 
3953                 
3954                 FDB->ChunkBuffer->BufUsed = sent;
3955
3956                 while (nWritten < FDB->ChunkBuffer->BufUsed) {
3957                         rc =  write(FDB->OtherFD, FDB->ChunkBuffer->buf + nWritten, FDB->ChunkBuffer->BufUsed - nWritten);
3958                         if (rc < 0) {
3959                                 *Err = strerror(errno);
3960                                 return rc;
3961                         }
3962                         nWritten += rc;
3963
3964                 }
3965                 FDB->ChunkBuffer->BufUsed = 0;
3966                 FDB->TotalSentAlready += sent;
3967                 FDB->ChunkSendRemain -= sent;
3968                 return FDB->ChunkSendRemain;
3969         }
3970         else if (sent < 0) {
3971                 *Err = strerror(errno);
3972                 return sent;
3973         }
3974
3975 #endif
3976         return 0;
3977 }
3978
3979 eReadState WriteIOBAlreadyRead(FDIOBuffer *FDB, const char **Error)
3980 {
3981         int IsNonBlock;
3982         int fdflags;
3983         long rlen;
3984         long should_write;
3985         int nSuccessLess = 0;
3986         struct timeval tv;
3987         fd_set rfds;
3988
3989         fdflags = fcntl(FDB->OtherFD, F_GETFL);
3990         IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
3991
3992         while ((FDB->IOB->ReadWritePointer - FDB->IOB->Buf->buf < FDB->IOB->Buf->BufUsed) &&
3993                (FDB->ChunkSendRemain > 0))
3994         {
3995                 if (IsNonBlock){
3996                         tv.tv_sec = 1; /* selectresolution; */
3997                         tv.tv_usec = 0;
3998                         
3999                         FD_ZERO(&rfds);
4000                         FD_SET(FDB->OtherFD, &rfds);
4001                         if (select(FDB->OtherFD + 1, NULL, &rfds, NULL, &tv) == -1) {
4002                                 *Error = strerror(errno);
4003                                 return eReadFail;
4004                         }
4005                 }
4006                 if (IsNonBlock && !  FD_ISSET(FDB->OtherFD, &rfds)) {
4007                         nSuccessLess ++;
4008                         continue;
4009                 }
4010
4011                 should_write = FDB->IOB->Buf->BufUsed - 
4012                         (FDB->IOB->ReadWritePointer - FDB->IOB->Buf->buf);
4013                 if (should_write > FDB->ChunkSendRemain)
4014                         should_write = FDB->ChunkSendRemain;
4015
4016                 rlen = write(FDB->OtherFD, 
4017                              FDB->IOB->ReadWritePointer, 
4018                              should_write);
4019                 if (rlen < 1) {
4020                         *Error = strerror(errno);
4021                                                 
4022                         return eReadFail;
4023                 }
4024                 FDB->TotalSentAlready += rlen;
4025                 FDB->IOB->ReadWritePointer += rlen;
4026                 FDB->ChunkSendRemain -= rlen;
4027         }
4028         if (FDB->IOB->ReadWritePointer >= FDB->IOB->Buf->buf + FDB->IOB->Buf->BufUsed)
4029         {
4030                 FlushStrBuf(FDB->IOB->Buf);
4031                 FDB->IOB->ReadWritePointer = NULL;
4032         }
4033
4034         if (FDB->ChunkSendRemain == 0)
4035                 return eReadSuccess;
4036         else 
4037                 return eMustReadMore;
4038 }
4039
4040 /*******************************************************************************
4041  *           File I/O; Prefer buffered read since its faster!                  *
4042  *******************************************************************************/
4043
4044 /**
4045  * @ingroup StrBuf_IO
4046  * @brief Read a line from socket
4047  * flushes and closes the FD on error
4048  * @param buf the buffer to get the input to
4049  * @param fd pointer to the filedescriptor to read
4050  * @param append Append to an existing string or replace?
4051  * @param Error strerror() on error 
4052  * @returns numbers of chars read
4053  */
4054 int StrBufTCP_read_line(StrBuf *buf, int *fd, int append, const char **Error)
4055 {
4056         int len, rlen, slen;
4057
4058         if (!append)
4059                 FlushStrBuf(buf);
4060
4061         slen = len = buf->BufUsed;
4062         while (1) {
4063                 rlen = read(*fd, &buf->buf[len], 1);
4064                 if (rlen < 1) {
4065                         *Error = strerror(errno);
4066                         
4067                         close(*fd);
4068                         *fd = -1;
4069                         
4070                         return -1;
4071                 }
4072                 if (buf->buf[len] == '\n')
4073                         break;
4074                 if (buf->buf[len] != '\r')
4075                         len ++;
4076                 if (len + 2 >= buf->BufSize) {
4077                         buf->BufUsed = len;
4078                         buf->buf[len+1] = '\0';
4079                         IncreaseBuf(buf, 1, -1);
4080                 }
4081         }
4082         buf->BufUsed = len;
4083         buf->buf[len] = '\0';
4084         return len - slen;
4085 }
4086
4087 /**
4088  * @ingroup StrBuf_BufferedIO
4089  * @brief Read a line from socket
4090  * flushes and closes the FD on error
4091  * @param Line the line to read from the fd / I/O Buffer
4092  * @param buf the buffer to get the input to
4093  * @param fd pointer to the filedescriptor to read
4094  * @param timeout number of successless selects until we bail out
4095  * @param selectresolution how long to wait on each select
4096  * @param Error strerror() on error 
4097  * @returns numbers of chars read
4098  */
4099 int StrBufTCP_read_buffered_line(StrBuf *Line, 
4100                                  StrBuf *buf, 
4101                                  int *fd, 
4102                                  int timeout, 
4103                                  int selectresolution, 
4104                                  const char **Error)
4105 {
4106         int len, rlen;
4107         int nSuccessLess = 0;
4108         fd_set rfds;
4109         char *pch = NULL;
4110         int fdflags;
4111         int IsNonBlock;
4112         struct timeval tv;
4113
4114         if (buf->BufUsed > 0) {
4115                 pch = strchr(buf->buf, '\n');
4116                 if (pch != NULL) {
4117                         rlen = 0;
4118                         len = pch - buf->buf;
4119                         if (len > 0 && (*(pch - 1) == '\r') )
4120                                 rlen ++;
4121                         StrBufSub(Line, buf, 0, len - rlen);
4122                         StrBufCutLeft(buf, len + 1);
4123                         return len - rlen;
4124                 }
4125         }
4126         
4127         if (buf->BufSize - buf->BufUsed < 10)
4128                 IncreaseBuf(buf, 1, -1);
4129
4130         fdflags = fcntl(*fd, F_GETFL);
4131         IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4132
4133         while ((nSuccessLess < timeout) && (pch == NULL)) {
4134                 if (IsNonBlock){
4135                         tv.tv_sec = selectresolution;
4136                         tv.tv_usec = 0;
4137                         
4138                         FD_ZERO(&rfds);
4139                         FD_SET(*fd, &rfds);
4140                         if (select(*fd + 1, NULL, &rfds, NULL, &tv) == -1) {
4141                                 *Error = strerror(errno);
4142                                 close (*fd);
4143                                 *fd = -1;
4144                                 return -1;
4145                         }
4146                 }
4147                 if (IsNonBlock && !  FD_ISSET(*fd, &rfds)) {
4148                         nSuccessLess ++;
4149                         continue;
4150                 }
4151                 rlen = read(*fd, 
4152                             &buf->buf[buf->BufUsed], 
4153                             buf->BufSize - buf->BufUsed - 1);
4154                 if (rlen < 1) {
4155                         *Error = strerror(errno);
4156                         close(*fd);
4157                         *fd = -1;
4158                         return -1;
4159                 }
4160                 else if (rlen > 0) {
4161                         nSuccessLess = 0;
4162                         buf->BufUsed += rlen;
4163                         buf->buf[buf->BufUsed] = '\0';
4164                         if (buf->BufUsed + 10 > buf->BufSize) {
4165                                 IncreaseBuf(buf, 1, -1);
4166                         }
4167                         pch = strchr(buf->buf, '\n');
4168                         continue;
4169                 }
4170                 
4171         }
4172         if (pch != NULL) {
4173                 rlen = 0;
4174                 len = pch - buf->buf;
4175                 if (len > 0 && (*(pch - 1) == '\r') )
4176                         rlen ++;
4177                 StrBufSub(Line, buf, 0, len - rlen);
4178                 StrBufCutLeft(buf, len + 1);
4179                 return len - rlen;
4180         }
4181         return -1;
4182
4183 }
4184
4185 static const char *ErrRBLF_PreConditionFailed="StrBufTCP_read_buffered_line_fast: Wrong arguments or invalid Filedescriptor";
4186 static const char *ErrRBLF_SelectFailed="StrBufTCP_read_buffered_line_fast: Select failed without reason";
4187 static const char *ErrRBLF_NotEnoughSentFromServer="StrBufTCP_read_buffered_line_fast: No complete line was sent from peer";
4188 /**
4189  * @ingroup StrBuf_BufferedIO
4190  * @brief Read a line from socket
4191  * flushes and closes the FD on error
4192  * @param Line where to append our Line read from the fd / I/O Buffer; 
4193  * @param IOBuf the buffer to get the input to; lifetime pair to FD
4194  * @param Pos pointer to the current read position, should be NULL initialized on opening the FD it belongs to.!
4195  * @param fd pointer to the filedescriptor to read
4196  * @param timeout number of successless selects until we bail out
4197  * @param selectresolution how long to wait on each select
4198  * @param Error strerror() on error 
4199  * @returns numbers of chars read or -1 in case of error. "\n" will become 0
4200  */
4201 int StrBufTCP_read_buffered_line_fast(StrBuf *Line, 
4202                                       StrBuf *IOBuf, 
4203                                       const char **Pos,
4204                                       int *fd, 
4205                                       int timeout, 
4206                                       int selectresolution, 
4207                                       const char **Error)
4208 {
4209         const char *pche = NULL;
4210         const char *pos = NULL;
4211         const char *pLF;
4212         int len, rlen, retlen;
4213         int nSuccessLess = 0;
4214         fd_set rfds;
4215         const char *pch = NULL;
4216         int fdflags;
4217         int IsNonBlock;
4218         struct timeval tv;
4219         
4220         retlen = 0;
4221         if ((Line == NULL) ||
4222             (Pos == NULL) ||
4223             (IOBuf == NULL) ||
4224             (*fd == -1))
4225         {
4226                 if (Pos != NULL)
4227                         *Pos = NULL;
4228                 *Error = ErrRBLF_PreConditionFailed;
4229                 return -1;
4230         }
4231
4232         pos = *Pos;
4233         if ((IOBuf->BufUsed > 0) && 
4234             (pos != NULL) && 
4235             (pos < IOBuf->buf + IOBuf->BufUsed)) 
4236         {
4237                 char *pcht;
4238
4239                 pche = IOBuf->buf + IOBuf->BufUsed;
4240                 pch = pos;
4241                 pcht = Line->buf;
4242
4243                 while ((pch < pche) && (*pch != '\n'))
4244                 {
4245                         if (Line->BufUsed + 10 > Line->BufSize)
4246                         {
4247                                 long apos;
4248                                 apos = pcht - Line->buf;
4249                                 *pcht = '\0';
4250                                 IncreaseBuf(Line, 1, -1);
4251                                 pcht = Line->buf + apos;
4252                         }
4253                         *pcht++ = *pch++;
4254                         Line->BufUsed++;
4255                         retlen++;
4256                 }
4257
4258                 len = pch - pos;
4259                 if (len > 0 && (*(pch - 1) == '\r') )
4260                 {
4261                         retlen--;
4262                         len --;
4263                         pcht --;
4264                         Line->BufUsed --;
4265                 }
4266                 *pcht = '\0';
4267
4268                 if ((pch >= pche) || (*pch == '\0'))
4269                 {
4270                         FlushStrBuf(IOBuf);
4271                         *Pos = NULL;
4272                         pch = NULL;
4273                         pos = 0;
4274                 }
4275
4276                 if ((pch != NULL) && 
4277                     (pch <= pche)) 
4278                 {
4279                         if (pch + 1 >= pche) {
4280                                 *Pos = NULL;
4281                                 FlushStrBuf(IOBuf);
4282                         }
4283                         else
4284                                 *Pos = pch + 1;
4285                         
4286                         return retlen;
4287                 }
4288                 else 
4289                         FlushStrBuf(IOBuf);
4290         }
4291
4292         /* If we come here, Pos is Unset since we read everything into Line, and now go for more. */
4293         
4294         if (IOBuf->BufSize - IOBuf->BufUsed < 10)
4295                 IncreaseBuf(IOBuf, 1, -1);
4296
4297         fdflags = fcntl(*fd, F_GETFL);
4298         IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4299
4300         pLF = NULL;
4301         while ((nSuccessLess < timeout) && 
4302                (pLF == NULL) &&
4303                (*fd != -1)) {
4304                 if (IsNonBlock)
4305                 {
4306                         tv.tv_sec = 1;
4307                         tv.tv_usec = 0;
4308                 
4309                         FD_ZERO(&rfds);
4310                         FD_SET(*fd, &rfds);
4311                         if (select((*fd) + 1, &rfds, NULL, NULL, &tv) == -1) {
4312                                 *Error = strerror(errno);
4313                                 close (*fd);
4314                                 *fd = -1;
4315                                 if (*Error == NULL)
4316                                         *Error = ErrRBLF_SelectFailed;
4317                                 return -1;
4318                         }
4319                         if (! FD_ISSET(*fd, &rfds) != 0) {
4320                                 nSuccessLess ++;
4321                                 continue;
4322                         }
4323                 }
4324                 rlen = read(*fd, 
4325                             &IOBuf->buf[IOBuf->BufUsed], 
4326                             IOBuf->BufSize - IOBuf->BufUsed - 1);
4327                 if (rlen < 1) {
4328                         *Error = strerror(errno);
4329                         close(*fd);
4330                         *fd = -1;
4331                         return -1;
4332                 }
4333                 else if (rlen > 0) {
4334                         nSuccessLess = 0;
4335                         pLF = IOBuf->buf + IOBuf->BufUsed;
4336                         IOBuf->BufUsed += rlen;
4337                         IOBuf->buf[IOBuf->BufUsed] = '\0';
4338                         
4339                         pche = IOBuf->buf + IOBuf->BufUsed;
4340                         
4341                         while ((pLF < pche) && (*pLF != '\n'))
4342                                 pLF ++;
4343                         if ((pLF >= pche) || (*pLF == '\0'))
4344                                 pLF = NULL;
4345
4346                         if (IOBuf->BufUsed + 10 > IOBuf->BufSize)
4347                         {
4348                                 long apos = 0;
4349
4350                                 if (pLF != NULL) apos = pLF - IOBuf->buf;
4351                                 IncreaseBuf(IOBuf, 1, -1);      
4352                                 if (pLF != NULL) pLF = IOBuf->buf + apos;
4353                         }
4354
4355                         continue;
4356                 }
4357         }
4358         *Pos = NULL;
4359         if (pLF != NULL) {
4360                 pos = IOBuf->buf;
4361                 len = pLF - pos;
4362                 if (len > 0 && (*(pLF - 1) == '\r') )
4363                         len --;
4364                 StrBufAppendBufPlain(Line, ChrPtr(IOBuf), len, 0);
4365                 if (pLF + 1 >= IOBuf->buf + IOBuf->BufUsed)
4366                 {
4367                         FlushStrBuf(IOBuf);
4368                 }
4369                 else 
4370                         *Pos = pLF + 1;
4371                 return retlen + len;
4372         }
4373         *Error = ErrRBLF_NotEnoughSentFromServer;
4374         return -1;
4375
4376 }
4377
4378 static const char *ErrRBLF_BLOBPreConditionFailed="StrBufReadBLOB: Wrong arguments or invalid Filedescriptor";
4379 /**
4380  * @ingroup StrBuf_IO
4381  * @brief Input binary data from socket
4382  * flushes and closes the FD on error
4383  * @param Buf the buffer to get the input to
4384  * @param fd pointer to the filedescriptor to read
4385  * @param append Append to an existing string or replace?
4386  * @param nBytes the maximal number of bytes to read
4387  * @param Error strerror() on error 
4388  * @returns numbers of chars read
4389  */
4390 int StrBufReadBLOB(StrBuf *Buf, int *fd, int append, long nBytes, const char **Error)
4391 {
4392         int fdflags;
4393         int rlen;
4394         int nSuccessLess;
4395         int nRead = 0;
4396         char *ptr;
4397         int IsNonBlock;
4398         struct timeval tv;
4399         fd_set rfds;
4400
4401         if ((Buf == NULL) || (*fd == -1))
4402         {
4403                 *Error = ErrRBLF_BLOBPreConditionFailed;
4404                 return -1;
4405         }
4406         if (!append)
4407                 FlushStrBuf(Buf);
4408         if (Buf->BufUsed + nBytes >= Buf->BufSize)
4409                 IncreaseBuf(Buf, 1, Buf->BufUsed + nBytes);
4410
4411         ptr = Buf->buf + Buf->BufUsed;
4412
4413         fdflags = fcntl(*fd, F_GETFL);
4414         IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4415         nSuccessLess = 0;
4416         while ((nRead < nBytes) && 
4417                (*fd != -1)) 
4418         {
4419                 if (IsNonBlock)
4420                 {
4421                         tv.tv_sec = 1;
4422                         tv.tv_usec = 0;
4423                 
4424                         FD_ZERO(&rfds);
4425                         FD_SET(*fd, &rfds);
4426                         if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
4427                                 *Error = strerror(errno);
4428                                 close (*fd);
4429                                 *fd = -1;
4430                                 if (*Error == NULL)
4431                                         *Error = ErrRBLF_SelectFailed;
4432                                 return -1;
4433                         }
4434                         if (! FD_ISSET(*fd, &rfds) != 0) {
4435                                 nSuccessLess ++;
4436                                 continue;
4437                         }
4438                 }
4439
4440                 if ((rlen = read(*fd, 
4441                                  ptr,
4442                                  nBytes - nRead)) == -1) {
4443                         close(*fd);
4444                         *fd = -1;
4445                         *Error = strerror(errno);
4446                         return rlen;
4447                 }
4448                 nRead += rlen;
4449                 ptr += rlen;
4450                 Buf->BufUsed += rlen;
4451         }
4452         Buf->buf[Buf->BufUsed] = '\0';
4453         return nRead;
4454 }
4455
4456 const char *ErrRBB_BLOBFPreConditionFailed = "StrBufReadBLOBBuffered: to many selects; aborting.";
4457 const char *ErrRBB_too_many_selects        = "StrBufReadBLOBBuffered: to many selects; aborting.";
4458 /**
4459  * @ingroup StrBuf_BufferedIO
4460  * @brief Input binary data from socket
4461  * flushes and closes the FD on error
4462  * @param Blob put binary thing here
4463  * @param IOBuf the buffer to get the input to
4464  * @param Pos offset inside of IOBuf
4465  * @param fd pointer to the filedescriptor to read
4466  * @param append Append to an existing string or replace?
4467  * @param nBytes the maximal number of bytes to read
4468  * @param check whether we should search for '000\n' terminators in case of timeouts
4469  * @param Error strerror() on error 
4470  * @returns numbers of chars read
4471  */
4472 int StrBufReadBLOBBuffered(StrBuf *Blob, 
4473                            StrBuf *IOBuf, 
4474                            const char **Pos,
4475                            int *fd, 
4476                            int append, 
4477                            long nBytes, 
4478                            int check, 
4479                            const char **Error)
4480 {
4481         const char *pos;
4482         int fdflags;
4483         int len = 0;
4484         int rlen;
4485         int nRead = 0;
4486         int nAlreadyRead = 0;
4487         int IsNonBlock;
4488         char *ptr;
4489         fd_set rfds;
4490         struct timeval tv;
4491         int nSuccessLess = 0;
4492         int MaxTries;
4493
4494         if ((Blob == NULL) || (*fd == -1) || (IOBuf == NULL) || (Pos == NULL))
4495         {
4496                 if (*Pos != NULL)
4497                         *Pos = NULL;
4498                 *Error = ErrRBB_BLOBFPreConditionFailed;
4499                 return -1;
4500         }
4501
4502         if (!append)
4503                 FlushStrBuf(Blob);
4504         if (Blob->BufUsed + nBytes >= Blob->BufSize) 
4505                 IncreaseBuf(Blob, append, Blob->BufUsed + nBytes);
4506         
4507         pos = *Pos;
4508
4509         if (pos != NULL)
4510                 len = pos - IOBuf->buf;
4511         rlen = IOBuf->BufUsed - len;
4512
4513
4514         if ((IOBuf->BufUsed > 0) && 
4515             (pos != NULL) && 
4516             (pos < IOBuf->buf + IOBuf->BufUsed)) 
4517         {
4518                 if (rlen < nBytes) {
4519                         memcpy(Blob->buf + Blob->BufUsed, pos, rlen);
4520                         Blob->BufUsed += rlen;
4521                         Blob->buf[Blob->BufUsed] = '\0';
4522                         nAlreadyRead = nRead = rlen;
4523                         *Pos = NULL; 
4524                 }
4525                 if (rlen >= nBytes) {
4526                         memcpy(Blob->buf + Blob->BufUsed, pos, nBytes);
4527                         Blob->BufUsed += nBytes;
4528                         Blob->buf[Blob->BufUsed] = '\0';
4529                         if (rlen == nBytes) {
4530                                 *Pos = NULL; 
4531                                 FlushStrBuf(IOBuf);
4532                         }
4533                         else 
4534                                 *Pos += nBytes;
4535                         return nBytes;
4536                 }
4537         }
4538
4539         FlushStrBuf(IOBuf);
4540         *Pos = NULL;
4541         if (IOBuf->BufSize < nBytes - nRead)
4542                 IncreaseBuf(IOBuf, 0, nBytes - nRead);
4543         ptr = IOBuf->buf;
4544
4545         len = Blob->BufUsed;
4546
4547         fdflags = fcntl(*fd, F_GETFL);
4548         IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4549         if (IsNonBlock)
4550                 MaxTries =   1000;
4551         else
4552                 MaxTries = 100000;
4553
4554         nBytes -= nRead;
4555         nRead = 0;
4556         while ((nSuccessLess < MaxTries) && 
4557                (nRead < nBytes) &&
4558                (*fd != -1)) {
4559                 if (IsNonBlock)
4560                 {
4561                         tv.tv_sec = 1;
4562                         tv.tv_usec = 0;
4563                 
4564                         FD_ZERO(&rfds);
4565                         FD_SET(*fd, &rfds);
4566                         if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
4567                                 *Error = strerror(errno);
4568                                 close (*fd);
4569                                 *fd = -1;
4570                                 if (*Error == NULL)
4571                                         *Error = ErrRBLF_SelectFailed;
4572                                 return -1;
4573                         }
4574                         if (! FD_ISSET(*fd, &rfds) != 0) {
4575                                 nSuccessLess ++;
4576                                 continue;
4577                         }
4578                 }
4579                 rlen = read(*fd, 
4580                             ptr,
4581                             IOBuf->BufSize - (ptr - IOBuf->buf));
4582                 if (rlen == -1) {
4583                         close(*fd);
4584                         *fd = -1;
4585                         *Error = strerror(errno);
4586                         return rlen;
4587                 }
4588                 else if (rlen == 0){
4589                         if ((check == NNN_TERM) && 
4590                             (nRead > 5) &&
4591                             (strncmp(IOBuf->buf + IOBuf->BufUsed - 5, "\n000\n", 5) == 0)) 
4592                         {
4593                                 StrBufPlain(Blob, HKEY("\n000\n"));
4594                                 StrBufCutRight(Blob, 5);
4595                                 return Blob->BufUsed;
4596                         }
4597                         else if (!IsNonBlock) 
4598                                 nSuccessLess ++;
4599                         else if (nSuccessLess > MaxTries) {
4600                                 FlushStrBuf(IOBuf);
4601                                 *Error = ErrRBB_too_many_selects;
4602                                 return -1;
4603                         }
4604                 }
4605                 else if (rlen > 0) {
4606                         nSuccessLess = 0;
4607                         nRead += rlen;
4608                         ptr += rlen;
4609                         IOBuf->BufUsed += rlen;
4610                 }
4611         }
4612         if (nSuccessLess >= MaxTries) {
4613                 FlushStrBuf(IOBuf);
4614                 *Error = ErrRBB_too_many_selects;
4615                 return -1;
4616         }
4617
4618         if (nRead > nBytes) {
4619                 *Pos = IOBuf->buf + nBytes;
4620         }
4621         Blob->buf[Blob->BufUsed] = '\0';
4622         StrBufAppendBufPlain(Blob, IOBuf->buf, nBytes, 0);
4623         if (*Pos == NULL) {
4624                 FlushStrBuf(IOBuf);
4625         }
4626         return nRead + nAlreadyRead;
4627 }
4628
4629 /**
4630  * @ingroup StrBuf_IO
4631  * @brief extract a "next line" from Buf; Ptr to persist across several iterations
4632  * @param LineBuf your line will be copied here.
4633  * @param Buf BLOB with lines of text...
4634  * @param Ptr moved arround to keep the next-line across several iterations
4635  *        has to be &NULL on start; will be &NotNULL on end of buffer
4636  * @returns size of remaining buffer
4637  */
4638 int StrBufSipLine(StrBuf *LineBuf, const StrBuf *Buf, const char **Ptr)
4639 {
4640         const char *aptr, *ptr, *eptr;
4641         char *optr, *xptr;
4642
4643         if ((Buf == NULL) || (*Ptr == StrBufNOTNULL)) {
4644                 *Ptr = StrBufNOTNULL;
4645                 return 0;
4646         }
4647
4648         FlushStrBuf(LineBuf);
4649         if (*Ptr==NULL)
4650                 ptr = aptr = Buf->buf;
4651         else
4652                 ptr = aptr = *Ptr;
4653
4654         optr = LineBuf->buf;
4655         eptr = Buf->buf + Buf->BufUsed;
4656         xptr = LineBuf->buf + LineBuf->BufSize - 1;
4657
4658         while ((ptr <= eptr) && 
4659                (*ptr != '\n') &&
4660                (*ptr != '\r') )
4661         {
4662                 *optr = *ptr;
4663                 optr++; ptr++;
4664                 if (optr == xptr) {
4665                         LineBuf->BufUsed = optr - LineBuf->buf;
4666                         IncreaseBuf(LineBuf,  1, LineBuf->BufUsed + 1);
4667                         optr = LineBuf->buf + LineBuf->BufUsed;
4668                         xptr = LineBuf->buf + LineBuf->BufSize - 1;
4669                 }
4670         }
4671
4672         if ((ptr >= eptr) && (optr > LineBuf->buf))
4673                 optr --;
4674         LineBuf->BufUsed = optr - LineBuf->buf;
4675         *optr = '\0';       
4676         if ((ptr <= eptr) && (*ptr == '\r'))
4677                 ptr ++;
4678         if ((ptr <= eptr) && (*ptr == '\n'))
4679                 ptr ++;
4680         
4681         if (ptr < eptr) {
4682                 *Ptr = ptr;
4683         }
4684         else {
4685                 *Ptr = StrBufNOTNULL;
4686         }
4687
4688         return Buf->BufUsed - (ptr - Buf->buf);
4689 }
4690
4691
4692 /**
4693  * @ingroup StrBuf_IO
4694  * @brief removes double slashes from pathnames
4695  * @param Dir directory string to filter
4696  * @param RemoveTrailingSlash allows / disallows trailing slashes
4697  */
4698 void StrBufStripSlashes(StrBuf *Dir, int RemoveTrailingSlash)
4699 {
4700         char *a, *b;
4701
4702         a = b = Dir->buf;
4703
4704         while (!IsEmptyStr(a)) {
4705                 if (*a == '/') {
4706                         while (*a == '/')
4707                                 a++;
4708                         *b = '/';
4709                         b++;
4710                 }
4711                 else {
4712                         *b = *a;
4713                         b++; a++;
4714                 }
4715         }
4716         if ((RemoveTrailingSlash) &&
4717             (b > Dir->buf) && 
4718             (*(b - 1) == '/')){
4719                 b--;
4720         }
4721         *b = '\0';
4722         Dir->BufUsed = b - Dir->buf;
4723 }
4724
4725