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