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