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