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