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