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