we mustn't cut the string if we don't find a boundary.
[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 in hex encoding 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  */
1936 void StrBufHexEscAppend(StrBuf *OutBuf, const StrBuf *In, const unsigned char *PlainIn, long PlainInLen)
1937 {
1938         const unsigned char *pch, *pche;
1939         char *pt, *pte;
1940         int len;
1941         
1942         if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1943                 return;
1944         if (PlainIn != NULL) {
1945                 if (PlainInLen < 0)
1946                         len = strlen((const char*)PlainIn);
1947                 else
1948                         len = PlainInLen;
1949                 pch = PlainIn;
1950                 pche = pch + len;
1951         }
1952         else {
1953                 pch = (const unsigned char*)In->buf;
1954                 pche = pch + In->BufUsed;
1955                 len = In->BufUsed;
1956         }
1957
1958         if (len == 0) 
1959                 return;
1960
1961         pt = OutBuf->buf + OutBuf->BufUsed;
1962         pte = OutBuf->buf + OutBuf->BufSize - 3; /**< we max append 3 chars at once plus the \0 */
1963
1964         while (pch < pche) {
1965                 if (pt >= pte) {
1966                         IncreaseBuf(OutBuf, 1, -1);
1967                         pte = OutBuf->buf + OutBuf->BufSize - 3; /**< we max append 3 chars at once plus the \0 */
1968                         pt = OutBuf->buf + OutBuf->BufUsed;
1969                 }
1970
1971                 *pt = HexList[*pch][0];
1972                 pt ++;
1973                 *pt = HexList[*pch][1];
1974                 pt ++; pch ++; OutBuf->BufUsed += 2;
1975         }
1976         *pt = '\0';
1977 }
1978
1979 void StrBufBase64Append(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn, long PlainInLen, int linebreaks)
1980 {
1981         const char *pch;
1982         char *pt;
1983         int len;
1984         long ExpectLen;
1985         
1986         if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1987                 return;
1988         if (PlainIn != NULL) {
1989                 if (PlainInLen < 0)
1990                         len = strlen(PlainIn);
1991                 else
1992                         len = PlainInLen;
1993                 pch = PlainIn;
1994         }
1995         else {
1996                 pch = In->buf;
1997                 len = In->BufUsed;
1998         }
1999
2000         if (len == 0) 
2001                 return;
2002
2003         ExpectLen = ((len * 134) / 100) + OutBuf->BufUsed;
2004
2005         if (ExpectLen > OutBuf->BufSize)
2006                 if (IncreaseBuf(OutBuf, 1, ExpectLen) < ExpectLen)
2007                         return;
2008
2009         pt = OutBuf->buf + OutBuf->BufUsed;
2010
2011         len = CtdlEncodeBase64(pt, pch, len, linebreaks);
2012
2013         pt += len;
2014         OutBuf->BufUsed += len;
2015         *pt = '\0';
2016 }
2017
2018 /** 
2019  * @ingroup StrBuf_DeEnCoder
2020  * @brief append a string in hex encoding to the buffer
2021  * @param OutBuf the output buffer
2022  * @param In Buffer to encode
2023  * @param PlainIn way in from plain old c strings
2024  */
2025 void StrBufHexescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
2026 {
2027         StrBufHexEscAppend(OutBuf, In, (const unsigned char*) PlainIn, -1);
2028 }
2029
2030 /**
2031  * @ingroup StrBuf_DeEnCoder
2032  * @brief Append a string, escaping characters which have meaning in HTML.  
2033  *
2034  * @param Target        target buffer
2035  * @param Source        source buffer; set to NULL if you just have a C-String
2036  * @param PlainIn       Plain-C string to append; set to NULL if unused
2037  * @param nbsp          If nonzero, spaces are converted to non-breaking spaces.
2038  * @param nolinebreaks  if set to 1, linebreaks are removed from the string.
2039  *                      if set to 2, linebreaks are replaced by &ltbr/&gt
2040  */
2041 long StrEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
2042 {
2043         const char *aptr, *eiptr;
2044         char *bptr, *eptr;
2045         long len;
2046
2047         if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2048                 return -1;
2049
2050         if (PlainIn != NULL) {
2051                 aptr = PlainIn;
2052                 len = strlen(PlainIn);
2053                 eiptr = aptr + len;
2054         }
2055         else {
2056                 aptr = Source->buf;
2057                 eiptr = aptr + Source->BufUsed;
2058                 len = Source->BufUsed;
2059         }
2060
2061         if (len == 0) 
2062                 return -1;
2063
2064         bptr = Target->buf + Target->BufUsed;
2065         eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in...  */
2066
2067         while (aptr < eiptr){
2068                 if(bptr >= eptr) {
2069                         IncreaseBuf(Target, 1, -1);
2070                         eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in...  */
2071                         bptr = Target->buf + Target->BufUsed;
2072                 }
2073                 if (*aptr == '<') {
2074                         memcpy(bptr, "&lt;", 4);
2075                         bptr += 4;
2076                         Target->BufUsed += 4;
2077                 }
2078                 else if (*aptr == '>') {
2079                         memcpy(bptr, "&gt;", 4);
2080                         bptr += 4;
2081                         Target->BufUsed += 4;
2082                 }
2083                 else if (*aptr == '&') {
2084                         memcpy(bptr, "&amp;", 5);
2085                         bptr += 5;
2086                         Target->BufUsed += 5;
2087                 }
2088                 else if (*aptr == '"') {
2089                         memcpy(bptr, "&quot;", 6);
2090                         bptr += 6;
2091                         Target->BufUsed += 6;
2092                 }
2093                 else if (*aptr == '\'') {
2094                         memcpy(bptr, "&#39;", 5);
2095                         bptr += 5;
2096                         Target->BufUsed += 5;
2097                 }
2098                 else if (*aptr == LB) {
2099                         *bptr = '<';
2100                         bptr ++;
2101                         Target->BufUsed ++;
2102                 }
2103                 else if (*aptr == RB) {
2104                         *bptr = '>';
2105                         bptr ++;
2106                         Target->BufUsed ++;
2107                 }
2108                 else if (*aptr == QU) {
2109                         *bptr ='"';
2110                         bptr ++;
2111                         Target->BufUsed ++;
2112                 }
2113                 else if ((*aptr == 32) && (nbsp == 1)) {
2114                         memcpy(bptr, "&nbsp;", 6);
2115                         bptr += 6;
2116                         Target->BufUsed += 6;
2117                 }
2118                 else if ((*aptr == '\n') && (nolinebreaks == 1)) {
2119                         *bptr='\0';     /* nothing */
2120                 }
2121                 else if ((*aptr == '\n') && (nolinebreaks == 2)) {
2122                         memcpy(bptr, "&lt;br/&gt;", 11);
2123                         bptr += 11;
2124                         Target->BufUsed += 11;
2125                 }
2126
2127
2128                 else if ((*aptr == '\r') && (nolinebreaks != 0)) {
2129                         *bptr='\0';     /* nothing */
2130                 }
2131                 else{
2132                         *bptr = *aptr;
2133                         bptr++;
2134                         Target->BufUsed ++;
2135                 }
2136                 aptr ++;
2137         }
2138         *bptr = '\0';
2139         if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
2140                 return -1;
2141         return Target->BufUsed;
2142 }
2143
2144 /**
2145  * @ingroup StrBuf_DeEnCoder
2146  * @brief Append a string, escaping characters which have meaning in HTML.  
2147  * Converts linebreaks into blanks; escapes single quotes
2148  * @param Target        target buffer
2149  * @param Source        source buffer; set to NULL if you just have a C-String
2150  * @param PlainIn       Plain-C string to append; set to NULL if unused
2151  */
2152 void StrMsgEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2153 {
2154         const char *aptr, *eiptr;
2155         char *tptr, *eptr;
2156         long len;
2157
2158         if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2159                 return ;
2160
2161         if (PlainIn != NULL) {
2162                 aptr = PlainIn;
2163                 len = strlen(PlainIn);
2164                 eiptr = aptr + len;
2165         }
2166         else {
2167                 aptr = Source->buf;
2168                 eiptr = aptr + Source->BufUsed;
2169                 len = Source->BufUsed;
2170         }
2171
2172         if (len == 0) 
2173                 return;
2174
2175         eptr = Target->buf + Target->BufSize - 8; 
2176         tptr = Target->buf + Target->BufUsed;
2177         
2178         while (aptr < eiptr){
2179                 if(tptr >= eptr) {
2180                         IncreaseBuf(Target, 1, -1);
2181                         eptr = Target->buf + Target->BufSize - 8; 
2182                         tptr = Target->buf + Target->BufUsed;
2183                 }
2184                
2185                 if (*aptr == '\n') {
2186                         *tptr = ' ';
2187                         Target->BufUsed++;
2188                 }
2189                 else if (*aptr == '\r') {
2190                         *tptr = ' ';
2191                         Target->BufUsed++;
2192                 }
2193                 else if (*aptr == '\'') {
2194                         *(tptr++) = '&';
2195                         *(tptr++) = '#';
2196                         *(tptr++) = '3';
2197                         *(tptr++) = '9';
2198                         *tptr = ';';
2199                         Target->BufUsed += 5;
2200                 } else {
2201                         *tptr = *aptr;
2202                         Target->BufUsed++;
2203                 }
2204                 tptr++; aptr++;
2205         }
2206         *tptr = '\0';
2207 }
2208
2209
2210
2211 /**
2212  * @ingroup StrBuf_DeEnCoder
2213  * @brief Append a string, escaping characters which have meaning in ICAL.  
2214  * [\n,] 
2215  * @param Target        target buffer
2216  * @param Source        source buffer; set to NULL if you just have a C-String
2217  * @param PlainIn       Plain-C string to append; set to NULL if unused
2218  */
2219 void StrIcalEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2220 {
2221         const char *aptr, *eiptr;
2222         char *tptr, *eptr;
2223         long len;
2224
2225         if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2226                 return ;
2227
2228         if (PlainIn != NULL) {
2229                 aptr = PlainIn;
2230                 len = strlen(PlainIn);
2231                 eiptr = aptr + len;
2232         }
2233         else {
2234                 aptr = Source->buf;
2235                 eiptr = aptr + Source->BufUsed;
2236                 len = Source->BufUsed;
2237         }
2238
2239         if (len == 0) 
2240                 return;
2241
2242         eptr = Target->buf + Target->BufSize - 8; 
2243         tptr = Target->buf + Target->BufUsed;
2244         
2245         while (aptr < eiptr){
2246                 if(tptr + 3 >= eptr) {
2247                         IncreaseBuf(Target, 1, -1);
2248                         eptr = Target->buf + Target->BufSize - 8; 
2249                         tptr = Target->buf + Target->BufUsed;
2250                 }
2251                
2252                 if (*aptr == '\n') {
2253                         *tptr = '\\';
2254                         Target->BufUsed++;
2255                         tptr++;
2256                         *tptr = 'n';
2257                         Target->BufUsed++;
2258                 }
2259                 else if (*aptr == '\r') {
2260                         *tptr = '\\';
2261                         Target->BufUsed++;
2262                         tptr++;
2263                         *tptr = 'r';
2264                         Target->BufUsed++;
2265                 }
2266                 else if (*aptr == ',') {
2267                         *tptr = '\\';
2268                         Target->BufUsed++;
2269                         tptr++;
2270                         *tptr = ',';
2271                         Target->BufUsed++;
2272                 } else {
2273                         *tptr = *aptr;
2274                         Target->BufUsed++;
2275                 }
2276                 tptr++; aptr++;
2277         }
2278         *tptr = '\0';
2279 }
2280
2281 /**
2282  * @ingroup StrBuf_DeEnCoder
2283  * @brief Append a string, escaping characters which have meaning in JavaScript strings .  
2284  *
2285  * @param Target        target buffer
2286  * @param Source        source buffer; set to NULL if you just have a C-String
2287  * @param PlainIn       Plain-C string to append; set to NULL if unused
2288  * @returns size of result or -1
2289  */
2290 long StrECMAEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2291 {
2292         const char *aptr, *eiptr;
2293         char *bptr, *eptr;
2294         long len;
2295         int IsUtf8Sequence;
2296
2297         if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2298                 return -1;
2299
2300         if (PlainIn != NULL) {
2301                 aptr = PlainIn;
2302                 len = strlen(PlainIn);
2303                 eiptr = aptr + len;
2304         }
2305         else {
2306                 aptr = Source->buf;
2307                 eiptr = aptr + Source->BufUsed;
2308                 len = Source->BufUsed;
2309         }
2310
2311         if (len == 0) 
2312                 return -1;
2313
2314         bptr = Target->buf + Target->BufUsed;
2315         eptr = Target->buf + Target->BufSize - 7; /* our biggest unit to put in...  */
2316
2317         while (aptr < eiptr){
2318                 if(bptr >= eptr) {
2319                         IncreaseBuf(Target, 1, -1);
2320                         eptr = Target->buf + Target->BufSize - 7; /* our biggest unit to put in...  */
2321                         bptr = Target->buf + Target->BufUsed;
2322                 }
2323                 switch (*aptr) {
2324                 case '\n':
2325                         memcpy(bptr, HKEY("\\n"));
2326                         bptr += 2;
2327                         Target->BufUsed += 2;                           
2328                         break;
2329                 case '\r':
2330                         memcpy(bptr, HKEY("\\r"));
2331                         bptr += 2;
2332                         Target->BufUsed += 2;
2333                         break;
2334                 case '"':
2335                         *bptr = '\\';
2336                         bptr ++;
2337                         *bptr = '"';
2338                         bptr ++;
2339                         Target->BufUsed += 2;
2340                         break;
2341                 case '\\':
2342                         if ((*(aptr + 1) == 'u') &&
2343                             isxdigit(*(aptr + 2)) &&
2344                             isxdigit(*(aptr + 3)) &&
2345                             isxdigit(*(aptr + 4)) &&
2346                             isxdigit(*(aptr + 5)))
2347                         { /* oh, a unicode escaper. let it pass through. */
2348                                 memcpy(bptr, aptr, 6);
2349                                 aptr += 5;
2350                                 bptr +=6;
2351                                 Target->BufUsed += 6;
2352                         }
2353                         else 
2354                         {
2355                                 *bptr = '\\';
2356                                 bptr ++;
2357                                 *bptr = '\\';
2358                                 bptr ++;
2359                                 Target->BufUsed += 2;
2360                         }
2361                         break;
2362                 case '\b':
2363                         *bptr = '\\';
2364                         bptr ++;
2365                         *bptr = 'b';
2366                         bptr ++;
2367                         Target->BufUsed += 2;
2368                         break;
2369                 case '\f':
2370                         *bptr = '\\';
2371                         bptr ++;
2372                         *bptr = 'f';
2373                         bptr ++;
2374                         Target->BufUsed += 2;
2375                         break;
2376                 case '\t':
2377                         *bptr = '\\';
2378                         bptr ++;
2379                         *bptr = 't';
2380                         bptr ++;
2381                         Target->BufUsed += 2;
2382                         break;
2383                 default:
2384                         IsUtf8Sequence =  Ctdl_GetUtf8SequenceLength(aptr, eiptr);
2385                         while (IsUtf8Sequence > 0){
2386                                 *bptr = *aptr;
2387                                 Target->BufUsed ++;
2388                                 if (--IsUtf8Sequence)
2389                                         aptr++;
2390                                 bptr++;
2391                         }
2392                 }
2393                 aptr ++;
2394         }
2395         *bptr = '\0';
2396         if ((bptr == eptr - 1 ) && !IsEmptyStr(aptr) )
2397                 return -1;
2398         return Target->BufUsed;
2399 }
2400
2401 /**
2402  * @ingroup StrBuf_DeEnCoder
2403  * @brief Append a string, escaping characters which have meaning in HTML + json.  
2404  *
2405  * @param Target        target buffer
2406  * @param Source        source buffer; set to NULL if you just have a C-String
2407  * @param PlainIn       Plain-C string to append; set to NULL if unused
2408  * @param nbsp          If nonzero, spaces are converted to non-breaking spaces.
2409  * @param nolinebreaks  if set to 1, linebreaks are removed from the string.
2410  *                      if set to 2, linebreaks are replaced by &ltbr/&gt
2411  */
2412 long StrHtmlEcmaEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
2413 {
2414         const char *aptr, *eiptr;
2415         char *bptr, *eptr;
2416         long len;
2417         int IsUtf8Sequence = 0;
2418
2419         if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2420                 return -1;
2421
2422         if (PlainIn != NULL) {
2423                 aptr = PlainIn;
2424                 len = strlen(PlainIn);
2425                 eiptr = aptr + len;
2426         }
2427         else {
2428                 aptr = Source->buf;
2429                 eiptr = aptr + Source->BufUsed;
2430                 len = Source->BufUsed;
2431         }
2432
2433         if (len == 0) 
2434                 return -1;
2435
2436         bptr = Target->buf + Target->BufUsed;
2437         eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in...  */
2438
2439         while (aptr < eiptr){
2440                 if(bptr >= eptr) {
2441                         IncreaseBuf(Target, 1, -1);
2442                         eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in...  */
2443                         bptr = Target->buf + Target->BufUsed;
2444                 }
2445                 switch (*aptr) {
2446                 case '<':
2447                         memcpy(bptr, HKEY("&lt;"));
2448                         bptr += 4;
2449                         Target->BufUsed += 4;
2450                         break;
2451                 case '>':
2452                         memcpy(bptr, HKEY("&gt;"));
2453                         bptr += 4;
2454                         Target->BufUsed += 4;
2455                         break;
2456                 case '&':
2457                         memcpy(bptr, HKEY("&amp;"));
2458                         bptr += 5;
2459                         Target->BufUsed += 5;
2460                         break;
2461                 case LB:
2462                         *bptr = '<';
2463                         bptr ++;
2464                         Target->BufUsed ++;
2465                         break;
2466                 case RB:
2467                         *bptr = '>';
2468                         bptr ++;
2469                         Target->BufUsed ++;
2470                         break;
2471                 case '\n':
2472                         switch (nolinebreaks) {
2473                         case 1:
2474                                 *bptr='\0';     /* nothing */
2475                                 break;
2476                         case 2:
2477                                 memcpy(bptr, HKEY("&lt;br/&gt;"));
2478                                 bptr += 11;
2479                                 Target->BufUsed += 11;
2480                                 break;
2481                         default:
2482                                 memcpy(bptr, HKEY("\\n"));
2483                                 bptr += 2;
2484                                 Target->BufUsed += 2;                           
2485                         }
2486                         break;
2487                 case '\r':
2488                         switch (nolinebreaks) {
2489                         case 1:
2490                         case 2:
2491                                 *bptr='\0';     /* nothing */
2492                                 break;
2493                         default:
2494                                 memcpy(bptr, HKEY("\\r"));
2495                                 bptr += 2;
2496                                 Target->BufUsed += 2;
2497                                 break;
2498                         }
2499                         break;
2500                 case '"':
2501                 case QU:
2502                         *bptr = '\\';
2503                         bptr ++;
2504                         *bptr = '"';
2505                         bptr ++;
2506                         Target->BufUsed += 2;
2507                         break;
2508                 case '\\':
2509                         if ((*(aptr + 1) == 'u') &&
2510                             isxdigit(*(aptr + 2)) &&
2511                             isxdigit(*(aptr + 3)) &&
2512                             isxdigit(*(aptr + 4)) &&
2513                             isxdigit(*(aptr + 5)))
2514                         { /* oh, a unicode escaper. let it pass through. */
2515                                 memcpy(bptr, aptr, 6);
2516                                 aptr += 5;
2517                                 bptr +=6;
2518                                 Target->BufUsed += 6;
2519                         }
2520                         else 
2521                         {
2522                                 *bptr = '\\';
2523                                 bptr ++;
2524                                 *bptr = '\\';
2525                                 bptr ++;
2526                                 Target->BufUsed += 2;
2527                         }
2528                         break;
2529                 case '\b':
2530                         *bptr = '\\';
2531                         bptr ++;
2532                         *bptr = 'b';
2533                         bptr ++;
2534                         Target->BufUsed += 2;
2535                         break;
2536                 case '\f':
2537                         *bptr = '\\';
2538                         bptr ++;
2539                         *bptr = 'f';
2540                         bptr ++;
2541                         Target->BufUsed += 2;
2542                         break;
2543                 case '\t':
2544                         *bptr = '\\';
2545                         bptr ++;
2546                         *bptr = 't';
2547                         bptr ++;
2548                         Target->BufUsed += 2;
2549                         break;
2550                 case  32:
2551                         if (nbsp == 1) {
2552                                 memcpy(bptr, HKEY("&nbsp;"));
2553                                 bptr += 6;
2554                                 Target->BufUsed += 6;
2555                                 break;
2556                         }
2557                 default:
2558                         IsUtf8Sequence =  Ctdl_GetUtf8SequenceLength(aptr, eiptr);
2559                         while (IsUtf8Sequence > 0){
2560                                 *bptr = *aptr;
2561                                 Target->BufUsed ++;
2562                                 if (--IsUtf8Sequence)
2563                                         aptr++;
2564                                 bptr++;
2565                         }
2566                 }
2567                 aptr ++;
2568         }
2569         *bptr = '\0';
2570         if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
2571                 return -1;
2572         return Target->BufUsed;
2573 }
2574
2575
2576 /**
2577  * @ingroup StrBuf_DeEnCoder
2578  * @brief replace all non-Ascii characters by another
2579  * @param Buf buffer to inspect
2580  * @param repl charater to stamp over non ascii chars
2581  */
2582 void StrBufAsciify(StrBuf *Buf, const char repl)
2583 {
2584         long offset;
2585
2586         for (offset = 0; offset < Buf->BufUsed; offset ++)
2587                 if (!isascii(Buf->buf[offset]))
2588                         Buf->buf[offset] = repl;
2589         
2590 }
2591
2592 /**
2593  * @ingroup StrBuf_DeEnCoder
2594  * @brief unhide special chars hidden to the HTML escaper
2595  * @param target buffer to put the unescaped string in
2596  * @param source buffer to unescape
2597  */
2598 void StrBufEUid_unescapize(StrBuf *target, const StrBuf *source) 
2599 {
2600         int a, b, len;
2601         char hex[3];
2602
2603         if ((source == NULL) || (target == NULL) || (target->buf == NULL))
2604         {
2605                 return;
2606         }
2607
2608         if (target != NULL)
2609                 FlushStrBuf(target);
2610
2611         len = source->BufUsed;
2612         for (a = 0; a < len; ++a) {
2613                 if (target->BufUsed >= target->BufSize)
2614                         IncreaseBuf(target, 1, -1);
2615
2616                 if (source->buf[a] == '=') {
2617                         hex[0] = source->buf[a + 1];
2618                         hex[1] = source->buf[a + 2];
2619                         hex[2] = 0;
2620                         b = 0;
2621                         sscanf(hex, "%02x", &b);
2622                         target->buf[target->BufUsed] = b;
2623                         target->buf[++target->BufUsed] = 0;
2624                         a += 2;
2625                 }
2626                 else {
2627                         target->buf[target->BufUsed] = source->buf[a];
2628                         target->buf[++target->BufUsed] = 0;
2629                 }
2630         }
2631 }
2632
2633
2634 /**
2635  * @ingroup StrBuf_DeEnCoder
2636  * @brief hide special chars from the HTML escapers and friends
2637  * @param target buffer to put the escaped string in
2638  * @param source buffer to escape
2639  */
2640 void StrBufEUid_escapize(StrBuf *target, const StrBuf *source) 
2641 {
2642         int i, len;
2643
2644         if (target != NULL)
2645                 FlushStrBuf(target);
2646
2647         if ((source == NULL) || (target == NULL) || (target->buf == NULL))
2648         {
2649                 return;
2650         }
2651
2652         len = source->BufUsed;
2653         for (i=0; i<len; ++i) {
2654                 if (target->BufUsed + 4 >= target->BufSize)
2655                         IncreaseBuf(target, 1, -1);
2656                 if ( (isalnum(source->buf[i])) || 
2657                      (source->buf[i]=='-') || 
2658                      (source->buf[i]=='_') ) {
2659                         target->buf[target->BufUsed++] = source->buf[i];
2660                 }
2661                 else {
2662                         sprintf(&target->buf[target->BufUsed], 
2663                                 "=%02X", 
2664                                 (0xFF &source->buf[i]));
2665                         target->BufUsed += 3;
2666                 }
2667         }
2668         target->buf[target->BufUsed + 1] = '\0';
2669 }
2670
2671
2672 /*******************************************************************************
2673  *                      Quoted Printable de/encoding                           *
2674  *******************************************************************************/
2675
2676 /**
2677  * @ingroup StrBuf_DeEnCoder
2678  * @brief decode a buffer from base 64 encoding; destroys original
2679  * @param Buf Buffor to transform
2680  */
2681 int StrBufDecodeBase64(StrBuf *Buf)
2682 {
2683         char *xferbuf;
2684         size_t siz;
2685
2686         if (Buf == NULL)
2687                 return -1;
2688
2689         xferbuf = (char*) malloc(Buf->BufSize);
2690         if (xferbuf == NULL)
2691                 return -1;
2692
2693         *xferbuf = '\0';
2694         siz = CtdlDecodeBase64(xferbuf,
2695                                Buf->buf,
2696                                Buf->BufUsed);
2697         free(Buf->buf);
2698         Buf->buf = xferbuf;
2699         Buf->BufUsed = siz;
2700         return siz;
2701 }
2702
2703 /**
2704  * @ingroup StrBuf_DeEnCoder
2705  * @brief decode a buffer from base 64 encoding; destroys original
2706  * @param Buf Buffor to transform
2707  */
2708 int StrBufDecodeHex(StrBuf *Buf)
2709 {
2710         unsigned int ch;
2711         char *pch, *pche, *pchi;
2712
2713         if (Buf == NULL) return -1;
2714
2715         pch = pchi = Buf->buf;
2716         pche = pch + Buf->BufUsed;
2717
2718         while (pchi < pche){
2719                 ch = decode_hex(pchi);
2720                 *pch = ch;
2721                 pch ++;
2722                 pchi += 2;
2723         }
2724
2725         *pch = '\0';
2726         Buf->BufUsed = pch - Buf->buf;
2727         return Buf->BufUsed;
2728 }
2729
2730 /**
2731  * @ingroup StrBuf_DeEnCoder
2732  * @brief replace all chars >0x20 && < 0x7F with Mute
2733  * @param Mute char to put over invalid chars
2734  * @param Buf Buffor to transform
2735  */
2736 int StrBufSanitizeAscii(StrBuf *Buf, const char Mute)
2737 {
2738         unsigned char *pch;
2739
2740         if (Buf == NULL) return -1;
2741         pch = (unsigned char *)Buf->buf;
2742         while (pch < (unsigned char *)Buf->buf + Buf->BufUsed) {
2743                 if ((*pch < 0x20) || (*pch > 0x7F))
2744                         *pch = Mute;
2745                 pch ++;
2746         }
2747         return Buf->BufUsed;
2748 }
2749
2750
2751 /**
2752  * @ingroup StrBuf_DeEnCoder
2753  * @brief remove escaped strings from i.e. the url string (like %20 for blanks)
2754  * @param Buf Buffer to translate
2755  * @param StripBlanks Reduce several blanks to one?
2756  */
2757 long StrBufUnescape(StrBuf *Buf, int StripBlanks)
2758 {
2759         int a, b;
2760         char hex[3];
2761         long len;
2762
2763         if (Buf == NULL)
2764                 return -1;
2765
2766         while ((Buf->BufUsed > 0) && (isspace(Buf->buf[Buf->BufUsed - 1]))){
2767                 Buf->buf[Buf->BufUsed - 1] = '\0';
2768                 Buf->BufUsed --;
2769         }
2770
2771         a = 0; 
2772         while (a < Buf->BufUsed) {
2773                 if (Buf->buf[a] == '+')
2774                         Buf->buf[a] = ' ';
2775                 else if (Buf->buf[a] == '%') {
2776                         /* don't let % chars through, rather truncate the input. */
2777                         if (a + 2 > Buf->BufUsed) {
2778                                 Buf->buf[a] = '\0';
2779                                 Buf->BufUsed = a;
2780                         }
2781                         else {                  
2782                                 hex[0] = Buf->buf[a + 1];
2783                                 hex[1] = Buf->buf[a + 2];
2784                                 hex[2] = 0;
2785                                 b = 0;
2786                                 sscanf(hex, "%02x", &b);
2787                                 Buf->buf[a] = (char) b;
2788                                 len = Buf->BufUsed - a - 2;
2789                                 if (len > 0)
2790                                         memmove(&Buf->buf[a + 1], &Buf->buf[a + 3], len);
2791                         
2792                                 Buf->BufUsed -=2;
2793                         }
2794                 }
2795                 a++;
2796         }
2797         return a;
2798 }
2799
2800
2801 /**
2802  * @ingroup StrBuf_DeEnCoder
2803  * @brief       RFC2047-encode a header field if necessary.
2804  *              If no non-ASCII characters are found, the string
2805  *              will be copied verbatim without encoding.
2806  *
2807  * @param       target          Target buffer.
2808  * @param       source          Source string to be encoded.
2809  * @returns     encoded length; -1 if non success.
2810  */
2811 int StrBufRFC2047encode(StrBuf **target, const StrBuf *source)
2812 {
2813         const char headerStr[] = "=?UTF-8?Q?";
2814         int need_to_encode = 0;
2815         int i = 0;
2816         unsigned char ch;
2817
2818         if ((source == NULL) || 
2819             (target == NULL))
2820             return -1;
2821
2822         while ((i < source->BufUsed) &&
2823                (!IsEmptyStr (&source->buf[i])) &&
2824                (need_to_encode == 0)) {
2825                 if (((unsigned char) source->buf[i] < 32) || 
2826                     ((unsigned char) source->buf[i] > 126)) {
2827                         need_to_encode = 1;
2828                 }
2829                 i++;
2830         }
2831
2832         if (!need_to_encode) {
2833                 if (*target == NULL) {
2834                         *target = NewStrBufPlain(source->buf, source->BufUsed);
2835                 }
2836                 else {
2837                         FlushStrBuf(*target);
2838                         StrBufAppendBuf(*target, source, 0);
2839                 }
2840                 if (*target != 0)
2841                         return (*target)->BufUsed;
2842                 else
2843                         return 0;
2844         }
2845         if (*target == NULL)
2846                 *target = NewStrBufPlain(NULL, sizeof(headerStr) + source->BufUsed * 2);
2847         else if (sizeof(headerStr) + source->BufUsed >= (*target)->BufSize)
2848                 IncreaseBuf(*target, sizeof(headerStr) + source->BufUsed, 0);
2849         memcpy ((*target)->buf, headerStr, sizeof(headerStr) - 1);
2850         (*target)->BufUsed = sizeof(headerStr) - 1;
2851         for (i=0; (i < source->BufUsed); ++i) {
2852                 if ((*target)->BufUsed + 4 >= (*target)->BufSize)
2853                         IncreaseBuf(*target, 1, 0);
2854                 ch = (unsigned char) source->buf[i];
2855                 if ((ch  <  32) || 
2856                     (ch  > 126) || 
2857                     (ch ==  61) ||
2858                     (ch == '=') ||
2859                     (ch == '?') ||
2860                     (ch == '_') ||
2861                     (ch == '[') ||
2862                     (ch == ']')   )
2863                 {
2864                         sprintf(&(*target)->buf[(*target)->BufUsed], "=%02X", ch);
2865                         (*target)->BufUsed += 3;
2866                 }
2867                 else {
2868                         if (ch == ' ')
2869                                 (*target)->buf[(*target)->BufUsed] = '_';
2870                         else
2871                                 (*target)->buf[(*target)->BufUsed] = ch;
2872                         (*target)->BufUsed++;
2873                 }
2874         }
2875         
2876         if ((*target)->BufUsed + 4 >= (*target)->BufSize)
2877                 IncreaseBuf(*target, 1, 0);
2878
2879         (*target)->buf[(*target)->BufUsed++] = '?';
2880         (*target)->buf[(*target)->BufUsed++] = '=';
2881         (*target)->buf[(*target)->BufUsed] = '\0';
2882         return (*target)->BufUsed;;
2883 }
2884
2885
2886
2887 static void AddRecipient(StrBuf *Target, 
2888                          StrBuf *UserName, 
2889                          StrBuf *EmailAddress, 
2890                          StrBuf *EncBuf)
2891 {
2892         int QuoteMe = 0;
2893
2894         if (StrLength(Target) > 0) StrBufAppendBufPlain(Target, HKEY(", "), 0);
2895         if (strchr(ChrPtr(UserName), ',') != NULL) QuoteMe = 1;
2896
2897         if (QuoteMe)  StrBufAppendBufPlain(Target, HKEY("\""), 0);
2898         StrBufRFC2047encode(&EncBuf, UserName);
2899         StrBufAppendBuf(Target, EncBuf, 0);
2900         if (QuoteMe)  StrBufAppendBufPlain(Target, HKEY("\" "), 0);
2901         else          StrBufAppendBufPlain(Target, HKEY(" "), 0);
2902
2903         if (StrLength(EmailAddress) > 0){
2904                 StrBufAppendBufPlain(Target, HKEY("<"), 0);
2905                 StrBufAppendBuf(Target, EmailAddress, 0); /* TODO: what about IDN???? */
2906                 StrBufAppendBufPlain(Target, HKEY(">"), 0);
2907         }
2908 }
2909
2910
2911 /**
2912  * \brief QP encode parts of an email TO/CC/BCC vector, and strip/filter invalid parts
2913  * \param Recp Source list of email recipients
2914  * \param UserName Temporary buffer for internal use; Please provide valid buffer.
2915  * \param EmailAddress Temporary buffer for internal use; Please provide valid buffer.
2916  * \param EncBuf Temporary buffer for internal use; Please provide valid buffer.
2917  * \returns encoded & sanitized buffer with the contents of Recp; Caller owns this memory.
2918  */
2919 StrBuf *StrBufSanitizeEmailRecipientVector(const StrBuf *Recp, 
2920                                            StrBuf *UserName, 
2921                                            StrBuf *EmailAddress,
2922                                            StrBuf *EncBuf)
2923 {
2924         StrBuf *Target;
2925         const char *pch, *pche;
2926         const char *UserStart, *UserEnd, *EmailStart, *EmailEnd, *At;
2927
2928         if ((Recp == NULL) || (StrLength(Recp) == 0))
2929                 return NULL;
2930
2931         pch = ChrPtr(Recp);
2932         pche = pch + StrLength(Recp);
2933
2934         if (!CheckEncode(pch, -1, pche))
2935                 return NewStrBufDup(Recp);
2936
2937         Target = NewStrBufPlain(NULL, StrLength(Recp));
2938
2939         while ((pch != NULL) && (pch < pche))
2940         {
2941                 while (isspace(*pch)) pch++;
2942                 UserEnd = EmailStart = EmailEnd = NULL;
2943                 
2944                 if ((*pch == '"') || (*pch == '\'')) {
2945                         UserStart = pch + 1;
2946                         
2947                         UserEnd = strchr(UserStart, *pch);
2948                         if (UserEnd == NULL) 
2949                                 break; ///TODO: Userfeedback??
2950                         EmailStart = UserEnd + 1;
2951                         while (isspace(*EmailStart))
2952                                 EmailStart++;
2953                         if (UserEnd == UserStart) {
2954                                 UserStart = UserEnd = NULL;
2955                         }
2956                         
2957                         if (*EmailStart == '<') {
2958                                 EmailStart++;
2959                                 EmailEnd = strchr(EmailStart, '>');
2960                                 if (EmailEnd == NULL)
2961                                         EmailEnd = strchr(EmailStart, ',');
2962                                 
2963                         }
2964                         else {
2965                                 EmailEnd = strchr(EmailStart, ',');
2966                         }
2967                         if (EmailEnd == NULL)
2968                                 EmailEnd = pche;
2969                         pch = EmailEnd + 1;
2970                 }
2971                 else {
2972                         int gt = 0;
2973                         UserStart = pch;
2974                         EmailEnd = strchr(UserStart, ',');
2975                         if (EmailEnd == NULL) {
2976                                 EmailEnd = strchr(pch, '>');
2977                                 pch = NULL;
2978                                 if (EmailEnd != NULL) {
2979                                         gt = 1;
2980                                 }
2981                                 else {
2982                                         EmailEnd = pche;
2983                                 }
2984                         }
2985                         else {
2986
2987                                 pch = EmailEnd + 1;
2988                                 while ((EmailEnd > UserStart) && !gt &&
2989                                        ((*EmailEnd == ',') ||
2990                                         (*EmailEnd == '>') ||
2991                                         (isspace(*EmailEnd))))
2992                                 {
2993                                         if (*EmailEnd == '>')
2994                                                 gt = 1;
2995                                         else 
2996                                                 EmailEnd--;
2997                                 }
2998                                 if (EmailEnd == UserStart)
2999                                         break;
3000                         }
3001                         if (gt) {
3002                                 EmailStart = strchr(UserStart, '<');
3003                                 if ((EmailStart == NULL) || (EmailStart > EmailEnd))
3004                                         break;
3005                                 UserEnd = EmailStart;
3006
3007                                 while ((UserEnd > UserStart) && 
3008                                        isspace (*(UserEnd - 1)))
3009                                         UserEnd --;
3010                                 EmailStart ++;
3011                                 if (UserStart >= UserEnd)
3012                                         UserStart = UserEnd = NULL;
3013                         }
3014                         else { /* this is a local recipient... no domain, just a realname */
3015                                 EmailStart = UserStart;
3016                                 At = strchr(EmailStart, '@');
3017                                 if (At == NULL) {
3018                                         UserEnd = EmailEnd;
3019                                         EmailEnd = NULL;
3020                                 }
3021                                 else {
3022                                         EmailStart = UserStart;
3023                                         UserStart = NULL;
3024                                 }
3025                         }
3026                 }
3027
3028                 if ((UserStart != NULL) && (UserEnd != NULL))
3029                         StrBufPlain(UserName, UserStart, UserEnd - UserStart);
3030                 else if ((UserStart != NULL) && (UserEnd == NULL))
3031                         StrBufPlain(UserName, UserStart, UserEnd - UserStart);
3032                 else
3033                         FlushStrBuf(UserName);
3034
3035                 if ((EmailStart != NULL) && (EmailEnd != NULL))
3036                         StrBufPlain(EmailAddress, EmailStart, EmailEnd - EmailStart);
3037                 else if ((EmailStart != NULL) && (EmailEnd == NULL))
3038                         StrBufPlain(EmailAddress, EmailStart, EmailEnd - pche);
3039                 else 
3040                         FlushStrBuf(EmailAddress);
3041
3042                 AddRecipient(Target, UserName, EmailAddress, EncBuf);
3043
3044                 if (pch == NULL)
3045                         break;
3046                 
3047                 if ((pch != NULL) && (*pch == ','))
3048                         pch ++;
3049                 if (pch != NULL) while (isspace(*pch))
3050                         pch ++;
3051         }
3052         return Target;
3053 }
3054
3055
3056 /**
3057  * @ingroup StrBuf
3058  * @brief replaces all occurances of 'search' by 'replace'
3059  * @param buf Buffer to modify
3060  * @param search character to search
3061  * @param replace character to replace search by
3062  */
3063 void StrBufReplaceChars(StrBuf *buf, char search, char replace)
3064 {
3065         long i;
3066         if (buf == NULL)
3067                 return;
3068         for (i=0; i<buf->BufUsed; i++)
3069                 if (buf->buf[i] == search)
3070                         buf->buf[i] = replace;
3071
3072 }
3073
3074 /**
3075  * @ingroup StrBuf
3076  * @brief removes all \\r s from the string, or replaces them with \n if its not a combination of both.
3077  * @param buf Buffer to modify
3078  */
3079 void StrBufToUnixLF(StrBuf *buf)
3080 {
3081         char *pche, *pchS, *pchT;
3082         if (buf == NULL)
3083                 return;
3084
3085         pche = buf->buf + buf->BufUsed;
3086         pchS = pchT = buf->buf;
3087         while (pchS < pche)
3088         {
3089                 if (*pchS == '\r')
3090                 {
3091                         pchS ++;
3092                         if (*pchS != '\n') {
3093                                 *pchT = '\n';
3094                                 pchT++;
3095                         }
3096                 }
3097                 *pchT = *pchS;
3098                 pchT++; pchS++;
3099         }
3100         *pchT = '\0';
3101         buf->BufUsed = pchT - buf->buf;
3102 }
3103
3104
3105 /*******************************************************************************
3106  *                 Iconv Wrapper; RFC822 de/encoding                           *
3107  *******************************************************************************/
3108
3109 /**
3110  * @ingroup StrBuf_DeEnCoder
3111  * @brief Wrapper around iconv_open()
3112  * Our version adds aliases for non-standard Microsoft charsets
3113  * such as 'MS950', aliasing them to names like 'CP950'
3114  *
3115  * @param tocode        Target encoding
3116  * @param fromcode      Source encoding
3117  * @param pic           anonimized pointer to iconv struct
3118  */
3119 void  ctdl_iconv_open(const char *tocode, const char *fromcode, void *pic)
3120 {
3121 #ifdef HAVE_ICONV
3122         iconv_t ic = (iconv_t)(-1) ;
3123         ic = iconv_open(tocode, fromcode);
3124         if (ic == (iconv_t)(-1) ) {
3125                 char alias_fromcode[64];
3126                 if ( (strlen(fromcode) == 5) && (!strncasecmp(fromcode, "MS", 2)) ) {
3127                         safestrncpy(alias_fromcode, fromcode, sizeof alias_fromcode);
3128                         alias_fromcode[0] = 'C';
3129                         alias_fromcode[1] = 'P';
3130                         ic = iconv_open(tocode, alias_fromcode);
3131                 }
3132         }
3133         *(iconv_t *)pic = ic;
3134 #endif
3135 }
3136
3137
3138 /**
3139  * @ingroup StrBuf_DeEnCoder
3140  * @brief find one chunk of a RFC822 encoded string
3141  * @param Buffer where to search
3142  * @param bptr where to start searching
3143  * @returns found position, NULL if none.
3144  */
3145 static inline const char *FindNextEnd (const StrBuf *Buf, const char *bptr)
3146 {
3147         const char * end;
3148         /* Find the next ?Q? */
3149         if (Buf->BufUsed - (bptr - Buf->buf)  < 6)
3150                 return NULL;
3151
3152         end = strchr(bptr + 2, '?');
3153
3154         if (end == NULL)
3155                 return NULL;
3156
3157         if ((Buf->BufUsed - (end - Buf->buf) > 3) &&
3158             (((*(end + 1) == 'B') || (*(end + 1) == 'Q')) ||
3159              ((*(end + 1) == 'b') || (*(end + 1) == 'q'))) && 
3160             (*(end + 2) == '?')) {
3161                 /* skip on to the end of the cluster, the next ?= */
3162                 end = strstr(end + 3, "?=");
3163         }
3164         else
3165                 /* sort of half valid encoding, try to find an end. */
3166                 end = strstr(bptr, "?=");
3167         return end;
3168 }
3169
3170
3171
3172 /**
3173  * @ingroup StrBuf_DeEnCoder
3174  * @brief convert one buffer according to the preselected iconv pointer PIC
3175  * @param ConvertBuf buffer we need to translate
3176  * @param TmpBuf To share a workbuffer over several iterations. prepare to have it filled with useless stuff afterwards.
3177  * @param pic Pointer to the iconv-session Object
3178  */
3179 void StrBufConvert(StrBuf *ConvertBuf, StrBuf *TmpBuf, void *pic)
3180 {
3181 #ifdef HAVE_ICONV
3182         long trycount = 0;
3183         size_t siz;
3184         iconv_t ic;
3185         char *ibuf;                     /**< Buffer of characters to be converted */
3186         char *obuf;                     /**< Buffer for converted characters */
3187         size_t ibuflen;                 /**< Length of input buffer */
3188         size_t obuflen;                 /**< Length of output buffer */
3189
3190
3191         if ((ConvertBuf == NULL) || (TmpBuf == NULL))
3192                 return;
3193
3194         /* since we're converting to utf-8, one glyph may take up to 6 bytes */
3195         if (ConvertBuf->BufUsed * 6 >= TmpBuf->BufSize)
3196                 IncreaseBuf(TmpBuf, 0, ConvertBuf->BufUsed * 6);
3197 TRYAGAIN:
3198         ic = *(iconv_t*)pic;
3199         ibuf = ConvertBuf->buf;
3200         ibuflen = ConvertBuf->BufUsed;
3201         obuf = TmpBuf->buf;
3202         obuflen = TmpBuf->BufSize;
3203         
3204         siz = iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
3205
3206         if (siz < 0) {
3207                 if (errno == E2BIG) {
3208                         trycount ++;                    
3209                         IncreaseBuf(TmpBuf, 0, 0);
3210                         if (trycount < 5) 
3211                                 goto TRYAGAIN;
3212
3213                 }
3214                 else if (errno == EILSEQ){ 
3215                         /* hm, invalid utf8 sequence... what to do now? */
3216                         /* An invalid multibyte sequence has been encountered in the input */
3217                 }
3218                 else if (errno == EINVAL) {
3219                         /* An incomplete multibyte sequence has been encountered in the input. */
3220                 }
3221
3222                 FlushStrBuf(TmpBuf);
3223         }
3224         else {
3225                 TmpBuf->BufUsed = TmpBuf->BufSize - obuflen;
3226                 TmpBuf->buf[TmpBuf->BufUsed] = '\0';
3227                 
3228                 /* little card game: wheres the red lady? */
3229                 SwapBuffers(ConvertBuf, TmpBuf);
3230                 FlushStrBuf(TmpBuf);
3231         }
3232 #endif
3233 }
3234
3235
3236 /**
3237  * @ingroup StrBuf_DeEnCoder
3238  * @brief catches one RFC822 encoded segment, and decodes it.
3239  * @param Target buffer to fill with result
3240  * @param DecodeMe buffer with stuff to process
3241  * @param SegmentStart points to our current segment in DecodeMe
3242  * @param SegmentEnd Points to the end of our current segment in DecodeMe
3243  * @param ConvertBuf Workbuffer shared between several iterations. Random content; needs to be valid
3244  * @param ConvertBuf2 Workbuffer shared between several iterations. Random content; needs to be valid
3245  * @param FoundCharset Characterset to default decoding to; if we find another we will overwrite it.
3246  */
3247 inline static void DecodeSegment(StrBuf *Target, 
3248                                  const StrBuf *DecodeMe, 
3249                                  const char *SegmentStart, 
3250                                  const char *SegmentEnd, 
3251                                  StrBuf *ConvertBuf,
3252                                  StrBuf *ConvertBuf2, 
3253                                  StrBuf *FoundCharset)
3254 {
3255         StrBuf StaticBuf;
3256         char charset[128];
3257         char encoding[16];
3258 #ifdef HAVE_ICONV
3259         iconv_t ic = (iconv_t)(-1);
3260 #else
3261         void *ic = NULL;
3262 #endif
3263         /* Now we handle foreign character sets properly encoded
3264          * in RFC2047 format.
3265          */
3266         StaticBuf.buf = (char*) SegmentStart; /*< it will just be read there... */
3267         StaticBuf.BufUsed = SegmentEnd - SegmentStart;
3268         StaticBuf.BufSize = DecodeMe->BufSize - (SegmentStart - DecodeMe->buf);
3269         extract_token(charset, SegmentStart, 1, '?', sizeof charset);
3270         if (FoundCharset != NULL) {
3271                 FlushStrBuf(FoundCharset);
3272                 StrBufAppendBufPlain(FoundCharset, charset, -1, 0);
3273         }
3274         extract_token(encoding, SegmentStart, 2, '?', sizeof encoding);
3275         StrBufExtract_token(ConvertBuf, &StaticBuf, 3, '?');
3276         
3277         *encoding = toupper(*encoding);
3278         if (*encoding == 'B') { /**< base64 */
3279                 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3280                         IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3281                 ConvertBuf2->BufUsed = CtdlDecodeBase64(ConvertBuf2->buf, 
3282                                                         ConvertBuf->buf, 
3283                                                         ConvertBuf->BufUsed);
3284         }
3285         else if (*encoding == 'Q') {    /**< quoted-printable */
3286                 long pos;
3287                 
3288                 pos = 0;
3289                 while (pos < ConvertBuf->BufUsed)
3290                 {
3291                         if (ConvertBuf->buf[pos] == '_') 
3292                                 ConvertBuf->buf[pos] = ' ';
3293                         pos++;
3294                 }
3295                 
3296                 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3297                         IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3298
3299                 ConvertBuf2->BufUsed = CtdlDecodeQuotedPrintable(
3300                         ConvertBuf2->buf, 
3301                         ConvertBuf->buf,
3302                         ConvertBuf->BufUsed);
3303         }
3304         else {
3305                 StrBufAppendBuf(ConvertBuf2, ConvertBuf, 0);
3306         }
3307 #ifdef HAVE_ICONV
3308         ctdl_iconv_open("UTF-8", charset, &ic);
3309         if (ic != (iconv_t)(-1) ) {             
3310 #endif
3311                 StrBufConvert(ConvertBuf2, ConvertBuf, &ic);
3312                 StrBufAppendBuf(Target, ConvertBuf2, 0);
3313 #ifdef HAVE_ICONV
3314                 iconv_close(ic);
3315         }
3316         else {
3317                 StrBufAppendBufPlain(Target, HKEY("(unreadable)"), 0);
3318         }
3319 #endif
3320 }
3321
3322 /**
3323  * @ingroup StrBuf_DeEnCoder
3324  * @brief Handle subjects with RFC2047 encoding such as: [deprecated old syntax!]
3325  * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3326  * @param Target where to put the decoded string to 
3327  * @param DecodeMe buffer with encoded string
3328  * @param DefaultCharset if we don't find one, which should we use?
3329  * @param FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string, 
3330  *        put it here for later use where no string might be known.
3331  */
3332 void StrBuf_RFC822_to_Utf8(StrBuf *Target, const StrBuf *DecodeMe, const StrBuf* DefaultCharset, StrBuf *FoundCharset)
3333 {
3334         StrBuf *ConvertBuf;
3335         StrBuf *ConvertBuf2;
3336         ConvertBuf = NewStrBufPlain(NULL, StrLength(DecodeMe));
3337         ConvertBuf2 = NewStrBufPlain(NULL, StrLength(DecodeMe));
3338         
3339         StrBuf_RFC822_2_Utf8(Target, 
3340                              DecodeMe, 
3341                              DefaultCharset, 
3342                              FoundCharset, 
3343                              ConvertBuf, 
3344                              ConvertBuf2);
3345         FreeStrBuf(&ConvertBuf);
3346         FreeStrBuf(&ConvertBuf2);
3347 }
3348
3349 /**
3350  * @ingroup StrBuf_DeEnCoder
3351  * @brief Handle subjects with RFC2047 encoding such as:
3352  * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3353  * @param Target where to put the decoded string to 
3354  * @param DecodeMe buffer with encoded string
3355  * @param DefaultCharset if we don't find one, which should we use?
3356  * @param FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string, 
3357  *        put it here for later use where no string might be known.
3358  * @param ConvertBuf workbuffer. feed in, you shouldn't care about its content.
3359  * @param ConvertBuf2 workbuffer. feed in, you shouldn't care about its content.
3360  */
3361 void StrBuf_RFC822_2_Utf8(StrBuf *Target, 
3362                           const StrBuf *DecodeMe, 
3363                           const StrBuf* DefaultCharset, 
3364                           StrBuf *FoundCharset, 
3365                           StrBuf *ConvertBuf, 
3366                           StrBuf *ConvertBuf2)
3367 {
3368         StrBuf *DecodedInvalidBuf = NULL;
3369         const StrBuf *DecodeMee = DecodeMe;
3370         const char *start, *end, *next, *nextend, *ptr = NULL;
3371 #ifdef HAVE_ICONV
3372         iconv_t ic = (iconv_t)(-1) ;
3373 #endif
3374         const char *eptr;
3375         int passes = 0;
3376         int i;
3377         int illegal_non_rfc2047_encoding = 0;
3378
3379
3380         if (DecodeMe == NULL)
3381                 return;
3382         /* Sometimes, badly formed messages contain strings which were simply
3383          *  written out directly in some foreign character set instead of
3384          *  using RFC2047 encoding.  This is illegal but we will attempt to
3385          *  handle it anyway by converting from a user-specified default
3386          *  charset to UTF-8 if we see any nonprintable characters.
3387          */
3388         
3389         for (i=0; i<DecodeMe->BufUsed; ++i) {
3390                 if ((DecodeMe->buf[i] < 32) || (DecodeMe->buf[i] > 126)) {
3391                         illegal_non_rfc2047_encoding = 1;
3392                         break;
3393                 }
3394         }
3395
3396         if ((illegal_non_rfc2047_encoding) &&
3397             (strcasecmp(ChrPtr(DefaultCharset), "UTF-8")) && 
3398             (strcasecmp(ChrPtr(DefaultCharset), "us-ascii")) )
3399         {
3400 #ifdef HAVE_ICONV
3401                 ctdl_iconv_open("UTF-8", ChrPtr(DefaultCharset), &ic);
3402                 if (ic != (iconv_t)(-1) ) {
3403                         DecodedInvalidBuf = NewStrBufDup(DecodeMe);
3404                         StrBufConvert(DecodedInvalidBuf, ConvertBuf, &ic);///TODO: don't void const?
3405                         DecodeMee = DecodedInvalidBuf;
3406                         iconv_close(ic);
3407                 }
3408 #endif
3409         }
3410
3411         /* pre evaluate the first pair */
3412         end = NULL;
3413         start = strstr(DecodeMee->buf, "=?");
3414         eptr = DecodeMee->buf + DecodeMee->BufUsed;
3415         if (start != NULL) 
3416                 end = FindNextEnd (DecodeMee, start + 2);
3417         else {
3418                 StrBufAppendBuf(Target, DecodeMee, 0);
3419                 FreeStrBuf(&DecodedInvalidBuf);
3420                 return;
3421         }
3422
3423
3424         if (start != DecodeMee->buf) {
3425                 long nFront;
3426                 
3427                 nFront = start - DecodeMee->buf;
3428                 StrBufAppendBufPlain(Target, DecodeMee->buf, nFront, 0);
3429         }
3430         /*
3431          * Since spammers will go to all sorts of absurd lengths to get their
3432          * messages through, there are LOTS of corrupt headers out there.
3433          * So, prevent a really badly formed RFC2047 header from throwing
3434          * this function into an infinite loop.
3435          */
3436         while ((start != NULL) && 
3437                (end != NULL) && 
3438                (start < eptr) && 
3439                (end < eptr) && 
3440                (passes < 20))
3441         {
3442                 passes++;
3443                 DecodeSegment(Target, 
3444                               DecodeMee, 
3445                               start, 
3446                               end, 
3447                               ConvertBuf,
3448                               ConvertBuf2,
3449                               FoundCharset);
3450                 
3451                 next = strstr(end, "=?");
3452                 nextend = NULL;
3453                 if ((next != NULL) && 
3454                     (next < eptr))
3455                         nextend = FindNextEnd(DecodeMee, next);
3456                 if (nextend == NULL)
3457                         next = NULL;
3458
3459                 /* did we find two partitions */
3460                 if ((next != NULL) && 
3461                     ((next - end) > 2))
3462                 {
3463                         ptr = end + 2;
3464                         while ((ptr < next) && 
3465                                (isspace(*ptr) ||
3466                                 (*ptr == '\r') ||
3467                                 (*ptr == '\n') || 
3468                                 (*ptr == '\t')))
3469                                 ptr ++;
3470                         /* 
3471                          * did we find a gab just filled with blanks?
3472                          * if not, copy its stuff over.
3473                          */
3474                         if (ptr != next)
3475                         {
3476                                 StrBufAppendBufPlain(Target, 
3477                                                      end + 2, 
3478                                                      next - end - 2,
3479                                                      0);
3480                         }
3481                 }
3482                 /* our next-pair is our new first pair now. */
3483                 ptr = end + 2;
3484                 start = next;
3485                 end = nextend;
3486         }
3487         end = ptr;
3488         nextend = DecodeMee->buf + DecodeMee->BufUsed;
3489         if ((end != NULL) && (end < nextend)) {
3490                 ptr = end;
3491                 while ( (ptr < nextend) &&
3492                         (isspace(*ptr) ||
3493                          (*ptr == '\r') ||
3494                          (*ptr == '\n') || 
3495                          (*ptr == '\t')))
3496                         ptr ++;
3497                 if (ptr < nextend)
3498                         StrBufAppendBufPlain(Target, end, nextend - end, 0);
3499         }
3500         FreeStrBuf(&DecodedInvalidBuf);
3501 }
3502
3503 /*******************************************************************************
3504  *                   Manipulating UTF-8 Strings                                *
3505  *******************************************************************************/
3506
3507 /**
3508  * @ingroup StrBuf
3509  * @brief evaluate the length of an utf8 special character sequence
3510  * @param Char the character to examine
3511  * @returns width of utf8 chars in bytes; if the sequence is broken 0 is returned; 1 if its simply ASCII.
3512  */
3513 static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE)
3514 {
3515         int n = 0;
3516         unsigned char test = (1<<7);
3517
3518         if ((*CharS & 0xC0) != 0xC0) 
3519                 return 1;
3520
3521         while ((n < 8) && 
3522                ((test & ((unsigned char)*CharS)) != 0)) 
3523         {
3524                 test = test >> 1;
3525                 n ++;
3526         }
3527         if ((n > 6) || ((CharE - CharS) < n))
3528                 n = 0;
3529         return n;
3530 }
3531
3532 /**
3533  * @ingroup StrBuf
3534  * @brief detect whether this char starts an utf-8 encoded char
3535  * @param Char character to inspect
3536  * @returns yes or no
3537  */
3538 static inline int Ctdl_IsUtf8SequenceStart(const char Char)
3539 {
3540 /** 11??.???? indicates an UTF8 Sequence. */
3541         return ((Char & 0xC0) == 0xC0);
3542 }
3543
3544 /**
3545  * @ingroup StrBuf
3546  * @brief measure the number of glyphs in an UTF8 string...
3547  * @param Buf string to measure
3548  * @returns the number of glyphs in Buf
3549  */
3550 long StrBuf_Utf8StrLen(StrBuf *Buf)
3551 {
3552         int n = 0;
3553         int m = 0;
3554         char *aptr, *eptr;
3555
3556         if ((Buf == NULL) || (Buf->BufUsed == 0))
3557                 return 0;
3558         aptr = Buf->buf;
3559         eptr = Buf->buf + Buf->BufUsed;
3560         while ((aptr < eptr) && (*aptr != '\0')) {
3561                 if (Ctdl_IsUtf8SequenceStart(*aptr)){
3562                         m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
3563                         while ((aptr < eptr) && (*aptr++ != '\0')&& (m-- > 0) );
3564                         n ++;
3565                 }
3566                 else {
3567                         n++;
3568                         aptr++;
3569                 }
3570         }
3571         return n;
3572 }
3573
3574 /**
3575  * @ingroup StrBuf
3576  * @brief cuts a string after maxlen glyphs
3577  * @param Buf string to cut to maxlen glyphs
3578  * @param maxlen how long may the string become?
3579  * @returns current length of the string
3580  */
3581 long StrBuf_Utf8StrCut(StrBuf *Buf, int maxlen)
3582 {
3583         char *aptr, *eptr;
3584         int n = 0, m = 0;
3585
3586         aptr = Buf->buf;
3587         eptr = Buf->buf + Buf->BufUsed;
3588         while ((aptr < eptr) && (*aptr != '\0')) {
3589                 if (Ctdl_IsUtf8SequenceStart(*aptr)){
3590                         m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
3591                         while ((*aptr++ != '\0') && (m-- > 0));
3592                         n ++;
3593                 }
3594                 else {
3595                         n++;
3596                         aptr++;
3597                 }
3598                 if (n > maxlen) {
3599                         *aptr = '\0';
3600                         Buf->BufUsed = aptr - Buf->buf;
3601                         return Buf->BufUsed;
3602                 }                       
3603         }
3604         return Buf->BufUsed;
3605
3606 }
3607
3608
3609
3610
3611
3612 /*******************************************************************************
3613  *                               wrapping ZLib                                 *
3614  *******************************************************************************/
3615
3616 #ifdef HAVE_ZLIB
3617 #define DEF_MEM_LEVEL 8 /*< memlevel??? */
3618 #define OS_CODE 0x03    /*< unix */
3619
3620 /**
3621  * @ingroup StrBuf_DeEnCoder
3622  * @brief uses the same calling syntax as compress2(), but it
3623  *   creates a stream compatible with HTTP "Content-encoding: gzip"
3624  * @param dest compressed buffer
3625  * @param destLen length of the compresed data 
3626  * @param source source to encode
3627  * @param sourceLen length of source to encode 
3628  * @param level compression level
3629  */
3630 int ZEXPORT compress_gzip(Bytef * dest,
3631                           size_t * destLen,
3632                           const Bytef * source,
3633                           uLong sourceLen,     
3634                           int level)
3635 {
3636         const int gz_magic[2] = { 0x1f, 0x8b }; /* gzip magic header */
3637
3638         /* write gzip header */
3639         snprintf((char *) dest, *destLen, 
3640                  "%c%c%c%c%c%c%c%c%c%c",
3641                  gz_magic[0], gz_magic[1], Z_DEFLATED,
3642                  0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /* xflags */ ,
3643                  OS_CODE);
3644
3645         /* normal deflate */
3646         z_stream stream;
3647         int err;
3648         stream.next_in = (Bytef *) source;
3649         stream.avail_in = (uInt) sourceLen;
3650         stream.next_out = dest + 10L;   // after header
3651         stream.avail_out = (uInt) * destLen;
3652         if ((uLong) stream.avail_out != *destLen)
3653                 return Z_BUF_ERROR;
3654
3655         stream.zalloc = (alloc_func) 0;
3656         stream.zfree = (free_func) 0;
3657         stream.opaque = (voidpf) 0;
3658
3659         err = deflateInit2(&stream, level, Z_DEFLATED, -MAX_WBITS,
3660                            DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
3661         if (err != Z_OK)
3662                 return err;
3663
3664         err = deflate(&stream, Z_FINISH);
3665         if (err != Z_STREAM_END) {
3666                 deflateEnd(&stream);
3667                 return err == Z_OK ? Z_BUF_ERROR : err;
3668         }
3669         *destLen = stream.total_out + 10L;
3670
3671         /* write CRC and Length */
3672         uLong crc = crc32(0L, source, sourceLen);
3673         int n;
3674         for (n = 0; n < 4; ++n, ++*destLen) {
3675                 dest[*destLen] = (int) (crc & 0xff);
3676                 crc >>= 8;
3677         }
3678         uLong len = stream.total_in;
3679         for (n = 0; n < 4; ++n, ++*destLen) {
3680                 dest[*destLen] = (int) (len & 0xff);
3681                 len >>= 8;
3682         }
3683         err = deflateEnd(&stream);
3684         return err;
3685 }
3686 #endif
3687
3688
3689 /**
3690  * @ingroup StrBuf_DeEnCoder
3691  * @brief compress the buffer with gzip
3692  * Attention! If you feed this a Const String, you must maintain the uncompressed buffer yourself!
3693  * @param Buf buffer whose content is to be gzipped
3694  */
3695 int CompressBuffer(StrBuf *Buf)
3696 {
3697 #ifdef HAVE_ZLIB
3698         char *compressed_data = NULL;
3699         size_t compressed_len, bufsize;
3700         int i = 0;
3701
3702         bufsize = compressed_len = Buf->BufUsed +  (Buf->BufUsed / 100) + 100;
3703         compressed_data = malloc(compressed_len);
3704         
3705         if (compressed_data == NULL)
3706                 return -1;
3707         /* Flush some space after the used payload so valgrind shuts up... */
3708         while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
3709                 Buf->buf[Buf->BufUsed + i++] = '\0';
3710         if (compress_gzip((Bytef *) compressed_data,
3711                           &compressed_len,
3712                           (Bytef *) Buf->buf,
3713                           (uLongf) Buf->BufUsed, Z_BEST_SPEED) == Z_OK) {
3714                 if (!Buf->ConstBuf)
3715                         free(Buf->buf);
3716                 Buf->buf = compressed_data;
3717                 Buf->BufUsed = compressed_len;
3718                 Buf->BufSize = bufsize;
3719                 /* Flush some space after the used payload so valgrind shuts up... */
3720                 i = 0;
3721                 while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
3722                         Buf->buf[Buf->BufUsed + i++] = '\0';
3723                 return 1;
3724         } else {
3725                 free(compressed_data);
3726         }
3727 #endif  /* HAVE_ZLIB */
3728         return 0;
3729 }
3730
3731 /*******************************************************************************
3732  *           File I/O; Callbacks to libevent                                   *
3733  *******************************************************************************/
3734
3735 long StrBuf_read_one_chunk_callback (int fd, short event, IOBuffer *FB)
3736 {
3737         long bufremain = 0;
3738         int n;
3739         
3740         if ((FB == NULL) || (FB->Buf == NULL))
3741                 return -1;
3742
3743         /*
3744          * check whether the read pointer is somewhere in a range 
3745          * where a cut left is inexpensive
3746          */
3747
3748         if (FB->ReadWritePointer != NULL)
3749         {
3750                 long already_read;
3751                 
3752                 already_read = FB->ReadWritePointer - FB->Buf->buf;
3753                 bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
3754
3755                 if (already_read != 0) {
3756                         long unread;
3757                         
3758                         unread = FB->Buf->BufUsed - already_read;
3759
3760                         /* else nothing to compact... */
3761                         if (unread == 0) {
3762                                 FB->ReadWritePointer = FB->Buf->buf;
3763                                 bufremain = FB->Buf->BufSize;                   
3764                         }
3765                         else if ((unread < 64) || 
3766                                  (bufremain < already_read))
3767                         {
3768                                 /* 
3769                                  * if its just a tiny bit remaining, or we run out of space... 
3770                                  * lets tidy up.
3771                                  */
3772                                 FB->Buf->BufUsed = unread;
3773                                 if (unread < already_read)
3774                                         memcpy(FB->Buf->buf, FB->ReadWritePointer, unread);
3775                                 else
3776                                         memmove(FB->Buf->buf, FB->ReadWritePointer, unread);
3777                                 FB->ReadWritePointer = FB->Buf->buf;
3778                                 bufremain = FB->Buf->BufSize - unread - 1;
3779                         }
3780                         else if (bufremain < (FB->Buf->BufSize / 10))
3781                         {
3782                                 /* get a bigger buffer */ 
3783
3784                                 IncreaseBuf(FB->Buf, 0, FB->Buf->BufUsed + 1);
3785
3786                                 FB->ReadWritePointer = FB->Buf->buf + unread;
3787
3788                                 bufremain = FB->Buf->BufSize - unread - 1;
3789 /*TODO: special increase function that won't copy the already read! */
3790                         }
3791                 }
3792                 else if (bufremain < 10) {
3793                         IncreaseBuf(FB->Buf, 1, FB->Buf->BufUsed + 10);
3794                         
3795                         FB->ReadWritePointer = FB->Buf->buf;
3796                         
3797                         bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
3798                 }
3799                 
3800         }
3801         else {
3802                 FB->ReadWritePointer = FB->Buf->buf;
3803                 bufremain = FB->Buf->BufSize - 1;
3804         }
3805
3806         n = read(fd, FB->Buf->buf + FB->Buf->BufUsed, bufremain);
3807
3808         if (n > 0) {
3809                 FB->Buf->BufUsed += n;
3810                 FB->Buf->buf[FB->Buf->BufUsed] = '\0';
3811         }
3812         return n;
3813 }
3814
3815 int StrBuf_write_one_chunk_callback(int fd, short event, IOBuffer *FB)
3816 {
3817         long WriteRemain;
3818         int n;
3819
3820         if ((FB == NULL) || (FB->Buf == NULL))
3821                 return -1;
3822
3823         if (FB->ReadWritePointer != NULL)
3824         {
3825                 WriteRemain = FB->Buf->BufUsed - 
3826                         (FB->ReadWritePointer - 
3827                          FB->Buf->buf);
3828         }
3829         else {
3830                 FB->ReadWritePointer = FB->Buf->buf;
3831                 WriteRemain = FB->Buf->BufUsed;
3832         }
3833
3834         n = write(fd, FB->ReadWritePointer, WriteRemain);
3835         if (n > 0) {
3836                 FB->ReadWritePointer += n;
3837
3838                 if (FB->ReadWritePointer == 
3839                     FB->Buf->buf + FB->Buf->BufUsed)
3840                 {
3841                         FlushStrBuf(FB->Buf);
3842                         FB->ReadWritePointer = NULL;
3843                         return 0;
3844                 }
3845         // check whether we've got something to write
3846         // get the maximum chunk plus the pointer we can send
3847         // write whats there
3848         // if not all was sent, remember the send pointer for the next time
3849                 return FB->ReadWritePointer - FB->Buf->buf + FB->Buf->BufUsed;
3850         }
3851         return n;
3852 }
3853
3854 /**
3855  * @ingroup StrBuf_IO
3856  * @brief extract a "next line" from Buf; Ptr to persist across several iterations
3857  * @param LineBuf your line will be copied here.
3858  * @param FB BLOB with lines of text...
3859  * @param Ptr moved arround to keep the next-line across several iterations
3860  *        has to be &NULL on start; will be &NotNULL on end of buffer
3861  * @returns size of copied buffer
3862  */
3863 eReadState StrBufChunkSipLine(StrBuf *LineBuf, IOBuffer *FB)
3864 {
3865         const char *aptr, *ptr, *eptr;
3866         char *optr, *xptr;
3867
3868         if ((FB == NULL) || (LineBuf == NULL) || (LineBuf->buf == NULL))
3869                 return eReadFail;
3870         
3871
3872         if ((FB->Buf == NULL) || (FB->ReadWritePointer == StrBufNOTNULL)) {
3873                 FB->ReadWritePointer = StrBufNOTNULL;
3874                 return eReadFail;
3875         }
3876
3877         FlushStrBuf(LineBuf);
3878         if (FB->ReadWritePointer == NULL)
3879                 ptr = aptr = FB->Buf->buf;
3880         else
3881                 ptr = aptr = FB->ReadWritePointer;
3882
3883         optr = LineBuf->buf;
3884         eptr = FB->Buf->buf + FB->Buf->BufUsed;
3885         xptr = LineBuf->buf + LineBuf->BufSize - 1;
3886
3887         while ((ptr <= eptr) && 
3888                (*ptr != '\n') &&
3889                (*ptr != '\r') )
3890         {
3891                 *optr = *ptr;
3892                 optr++; ptr++;
3893                 if (optr == xptr) {
3894                         LineBuf->BufUsed = optr - LineBuf->buf;
3895                         IncreaseBuf(LineBuf,  1, LineBuf->BufUsed + 1);
3896                         optr = LineBuf->buf + LineBuf->BufUsed;
3897                         xptr = LineBuf->buf + LineBuf->BufSize - 1;
3898                 }
3899         }
3900
3901         if (ptr >= eptr) {
3902                 if (optr > LineBuf->buf)
3903                         optr --;
3904                 if ((*(ptr - 1) != '\r') && (*(ptr - 1) != '\n')) {
3905                         LineBuf->BufUsed = optr - LineBuf->buf;
3906                         *optr = '\0';
3907                         if ((FB->ReadWritePointer != NULL) && 
3908                             (FB->ReadWritePointer != FB->Buf->buf))
3909                         {
3910                                 /* Ok, the client application read all the data 
3911                                    it was interested in so far. Since there is more to read, 
3912                                    we now shrink the buffer, and move the rest over.
3913                                 */
3914                                 StrBufCutLeft(FB->Buf, 
3915                                               FB->ReadWritePointer - FB->Buf->buf);
3916                                 FB->ReadWritePointer = FB->Buf->buf;
3917                         }
3918                         return eMustReadMore;
3919                 }
3920         }
3921         LineBuf->BufUsed = optr - LineBuf->buf;
3922         *optr = '\0';       
3923         if ((ptr <= eptr) && (*ptr == '\r'))
3924                 ptr ++;
3925         if ((ptr <= eptr) && (*ptr == '\n'))
3926                 ptr ++;
3927         
3928         if (ptr < eptr) {
3929                 FB->ReadWritePointer = ptr;
3930         }
3931         else {
3932                 FlushStrBuf(FB->Buf);
3933                 FB->ReadWritePointer = NULL;
3934         }
3935
3936         return eReadSuccess;
3937 }
3938
3939 /**
3940  * @ingroup StrBuf_CHUNKED_IO
3941  * @brief check whether the chunk-buffer has more data waiting or not.
3942  * @param FB Chunk-Buffer to inspect
3943  */
3944 eReadState StrBufCheckBuffer(IOBuffer *FB)
3945 {
3946         if (FB == NULL)
3947                 return eReadFail;
3948         if (FB->Buf->BufUsed == 0)
3949                 return eReadSuccess;
3950         if (FB->ReadWritePointer == NULL)
3951                 return eBufferNotEmpty;
3952         if (FB->Buf->buf + FB->Buf->BufUsed > FB->ReadWritePointer)
3953                 return eBufferNotEmpty;
3954         return eReadSuccess;
3955 }
3956
3957 long IOBufferStrLength(IOBuffer *FB)
3958 {
3959         if ((FB == NULL) || (FB->Buf == NULL))
3960                 return 0;
3961         if (FB->ReadWritePointer == NULL)
3962                 return StrLength(FB->Buf);
3963         
3964         return StrLength(FB->Buf) - (FB->ReadWritePointer - FB->Buf->buf);
3965 }
3966
3967 inline static void FDIOBufferFlush(FDIOBuffer *FDB)
3968 {
3969         memset(FDB, 0, sizeof(FDIOBuffer));
3970         FDB->OtherFD = -1;
3971         FDB->SplicePipe[0] = -1;
3972         FDB->SplicePipe[1] = -1;
3973 }
3974
3975 void FDIOBufferInit(FDIOBuffer *FDB, IOBuffer *IO, int FD, long TotalSendSize)
3976 {
3977         FDIOBufferFlush(FDB);
3978         FDB->ChunkSize = 
3979                 FDB->TotalSendSize = TotalSendSize;
3980         FDB->IOB = IO;
3981 #ifdef LINUX_SPLICE
3982         if (EnableSplice)
3983                 pipe(FDB->SplicePipe);
3984         else
3985 #endif
3986                 FDB->ChunkBuffer = NewStrBufPlain(NULL, TotalSendSize + 1);
3987
3988         FDB->OtherFD = FD;
3989 }
3990
3991 void FDIOBufferDelete(FDIOBuffer *FDB)
3992 {
3993 #ifdef LINUX_SPLICE
3994         if (EnableSplice)
3995         {
3996                 if (FDB->SplicePipe[0] > 0)
3997                         close(FDB->SplicePipe[0]);
3998                 if (FDB->SplicePipe[1] > 0)
3999                         close(FDB->SplicePipe[1]);
4000         }
4001         else
4002 #endif
4003                 FreeStrBuf(&FDB->ChunkBuffer);
4004         
4005         if (FDB->OtherFD > 0)
4006                 close(FDB->OtherFD);
4007         FDIOBufferFlush(FDB);
4008 }
4009
4010 int FileSendChunked(FDIOBuffer *FDB, const char **Err)
4011 {
4012         ssize_t sent, pipesize;
4013 #ifdef LINUX_SPLICE
4014         if (EnableSplice)
4015         {
4016                 if (FDB->PipeSize == 0)
4017                 {
4018                         pipesize = splice(FDB->OtherFD,
4019                                           &FDB->TotalSentAlready, 
4020                                           FDB->SplicePipe[1],
4021                                           NULL, 
4022                                           FDB->ChunkSendRemain, 
4023                                           SPLICE_F_MOVE);
4024         
4025                         if (pipesize == -1)
4026                         {
4027                                 *Err = strerror(errno);
4028                                 return pipesize;
4029                         }
4030                         FDB->PipeSize = pipesize;
4031                 }
4032                 sent =  splice(FDB->SplicePipe[0],
4033                                NULL, 
4034                                FDB->IOB->fd,
4035                                NULL, 
4036                                FDB->PipeSize,
4037                                SPLICE_F_MORE | SPLICE_F_MOVE | SPLICE_F_NONBLOCK);
4038                 if (sent == -1)
4039                 {
4040                         *Err = strerror(errno);
4041                         return sent;
4042                 }
4043                 FDB->PipeSize -= sent;
4044                 FDB->ChunkSendRemain -= sent;
4045                 return sent;
4046         }
4047         else
4048 #endif
4049         {
4050                 char *pRead;
4051                 long nRead = 0;
4052
4053                 pRead = FDB->ChunkBuffer->buf;
4054                 while ((FDB->ChunkBuffer->BufUsed < FDB->TotalSendSize) && (nRead >= 0))
4055                 {
4056                         nRead = read(FDB->OtherFD, pRead, FDB->TotalSendSize - FDB->ChunkBuffer->BufUsed);
4057                         if (nRead > 0) {
4058                                 FDB->ChunkBuffer->BufUsed += nRead;
4059                                 FDB->ChunkBuffer->buf[FDB->ChunkBuffer->BufUsed] = '\0';
4060                         }
4061                         else if (nRead == 0) {}
4062                         else return nRead;
4063                 
4064                 }
4065
4066                 nRead = write(FDB->IOB->fd, FDB->ChunkBuffer->buf + FDB->TotalSentAlready, FDB->ChunkSendRemain);
4067
4068                 if (nRead >= 0) {
4069                         FDB->TotalSentAlready += nRead;
4070                         FDB->ChunkSendRemain -= nRead;
4071                         return FDB->ChunkSendRemain;
4072                 }
4073                 else {
4074                         return nRead;
4075                 }
4076         }
4077 }
4078
4079 int FileRecvChunked(FDIOBuffer *FDB, const char **Err)
4080 {
4081         ssize_t sent, pipesize;
4082
4083 #ifdef LINUX_SPLICE
4084         if (EnableSplice)
4085         {
4086                 if (FDB->PipeSize == 0)
4087                 {
4088                         pipesize = splice(FDB->IOB->fd,
4089                                           NULL, 
4090                                           FDB->SplicePipe[1],
4091                                           NULL, 
4092                                           FDB->ChunkSendRemain, 
4093                                           SPLICE_F_MORE | SPLICE_F_MOVE|SPLICE_F_NONBLOCK);
4094
4095                         if (pipesize == -1)
4096                         {
4097                                 *Err = strerror(errno);
4098                                 return pipesize;
4099                         }
4100                         FDB->PipeSize = pipesize;
4101                 }
4102         
4103                 sent = splice(FDB->SplicePipe[0],
4104                               NULL, 
4105                               FDB->OtherFD,
4106                               &FDB->TotalSentAlready, 
4107                               FDB->PipeSize,
4108                               SPLICE_F_MORE | SPLICE_F_MOVE);
4109
4110                 if (sent == -1)
4111                 {
4112                         *Err = strerror(errno);
4113                         return sent;
4114                 }
4115                 FDB->PipeSize -= sent;
4116                 FDB->ChunkSendRemain -= sent;
4117                 return sent;
4118         }
4119         else
4120 #endif
4121         {
4122                 sent = read(FDB->IOB->fd, FDB->ChunkBuffer->buf, FDB->ChunkSendRemain);
4123                 if (sent > 0) {
4124                         int nWritten = 0;
4125                         int rc; 
4126                 
4127                         FDB->ChunkBuffer->BufUsed = sent;
4128
4129                         while (nWritten < FDB->ChunkBuffer->BufUsed) {
4130                                 rc =  write(FDB->OtherFD, FDB->ChunkBuffer->buf + nWritten, FDB->ChunkBuffer->BufUsed - nWritten);
4131                                 if (rc < 0) {
4132                                         *Err = strerror(errno);
4133                                         return rc;
4134                                 }
4135                                 nWritten += rc;
4136
4137                         }
4138                         FDB->ChunkBuffer->BufUsed = 0;
4139                         FDB->TotalSentAlready += sent;
4140                         FDB->ChunkSendRemain -= sent;
4141                         return FDB->ChunkSendRemain;
4142                 }
4143                 else if (sent < 0) {
4144                         *Err = strerror(errno);
4145                         return sent;
4146                 }
4147                 return 0;
4148         }
4149 }
4150
4151 int FileMoveChunked(FDIOBuffer *FDB, const char **Err)
4152 {
4153         ssize_t sent, pipesize;
4154
4155 #ifdef LINUX_SPLICE
4156         if (EnableSplice)
4157         {
4158                 if (FDB->PipeSize == 0)
4159                 {
4160                         pipesize = splice(FDB->IOB->fd,
4161                                           &FDB->TotalReadAlready, 
4162                                           FDB->SplicePipe[1],
4163                                           NULL, 
4164                                           FDB->ChunkSendRemain, 
4165                                           SPLICE_F_MORE | SPLICE_F_MOVE|SPLICE_F_NONBLOCK);
4166                         
4167                         if (pipesize == -1)
4168                         {
4169                                 *Err = strerror(errno);
4170                                 return pipesize;
4171                         }
4172                         FDB->PipeSize = pipesize;
4173                 }
4174                 
4175                 sent = splice(FDB->SplicePipe[0],
4176                               NULL, 
4177                               FDB->OtherFD,
4178                               &FDB->TotalSentAlready, 
4179                               FDB->PipeSize,
4180                               SPLICE_F_MORE | SPLICE_F_MOVE);
4181                 
4182                 if (sent == -1)
4183                 {
4184                         *Err = strerror(errno);
4185                         return sent;
4186                 }
4187                 FDB->PipeSize -= sent;
4188                 FDB->ChunkSendRemain -= sent;
4189                 return sent;
4190         }
4191         else
4192 #endif  
4193         {
4194                 sent = read(FDB->IOB->fd, FDB->ChunkBuffer->buf, FDB->ChunkSendRemain);
4195                 if (sent > 0) {
4196                         int nWritten = 0;
4197                         int rc; 
4198                 
4199                         FDB->ChunkBuffer->BufUsed = sent;
4200
4201                         while (nWritten < FDB->ChunkBuffer->BufUsed) {
4202                                 rc =  write(FDB->OtherFD, FDB->ChunkBuffer->buf + nWritten, FDB->ChunkBuffer->BufUsed - nWritten);
4203                                 if (rc < 0) {
4204                                         *Err = strerror(errno);
4205                                         return rc;
4206                                 }
4207                                 nWritten += rc;
4208
4209                         }
4210                         FDB->ChunkBuffer->BufUsed = 0;
4211                         FDB->TotalSentAlready += sent;
4212                         FDB->ChunkSendRemain -= sent;
4213                         return FDB->ChunkSendRemain;
4214                 }
4215                 else if (sent < 0) {
4216                         *Err = strerror(errno);
4217                         return sent;
4218                 }
4219                 return 0;
4220         }
4221 }
4222
4223 eReadState WriteIOBAlreadyRead(FDIOBuffer *FDB, const char **Error)
4224 {
4225         int IsNonBlock;
4226         int fdflags;
4227         long rlen;
4228         long should_write;
4229         int nSuccessLess = 0;
4230         struct timeval tv;
4231         fd_set rfds;
4232
4233         fdflags = fcntl(FDB->OtherFD, F_GETFL);
4234         IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4235
4236         while ((FDB->IOB->ReadWritePointer - FDB->IOB->Buf->buf < FDB->IOB->Buf->BufUsed) &&
4237                (FDB->ChunkSendRemain > 0))
4238         {
4239                 if (IsNonBlock){
4240                         tv.tv_sec = 1; /* selectresolution; */
4241                         tv.tv_usec = 0;
4242                         
4243                         FD_ZERO(&rfds);
4244                         FD_SET(FDB->OtherFD, &rfds);
4245                         if (select(FDB->OtherFD + 1, NULL, &rfds, NULL, &tv) == -1) {
4246                                 *Error = strerror(errno);
4247                                 return eReadFail;
4248                         }
4249                 }
4250                 if (IsNonBlock && !  FD_ISSET(FDB->OtherFD, &rfds)) {
4251                         nSuccessLess ++;
4252                         continue;
4253                 }
4254
4255                 should_write = FDB->IOB->Buf->BufUsed - 
4256                         (FDB->IOB->ReadWritePointer - FDB->IOB->Buf->buf);
4257                 if (should_write > FDB->ChunkSendRemain)
4258                         should_write = FDB->ChunkSendRemain;
4259
4260                 rlen = write(FDB->OtherFD, 
4261                              FDB->IOB->ReadWritePointer, 
4262                              should_write);
4263                 if (rlen < 1) {
4264                         *Error = strerror(errno);
4265                                                 
4266                         return eReadFail;
4267                 }
4268                 FDB->TotalSentAlready += rlen;
4269                 FDB->IOB->ReadWritePointer += rlen;
4270                 FDB->ChunkSendRemain -= rlen;
4271         }
4272         if (FDB->IOB->ReadWritePointer >= FDB->IOB->Buf->buf + FDB->IOB->Buf->BufUsed)
4273         {
4274                 FlushStrBuf(FDB->IOB->Buf);
4275                 FDB->IOB->ReadWritePointer = NULL;
4276         }
4277
4278         if (FDB->ChunkSendRemain == 0)
4279                 return eReadSuccess;
4280         else 
4281                 return eMustReadMore;
4282 }
4283
4284 /*******************************************************************************
4285  *           File I/O; Prefer buffered read since its faster!                  *
4286  *******************************************************************************/
4287
4288 /**
4289  * @ingroup StrBuf_IO
4290  * @brief Read a line from socket
4291  * flushes and closes the FD on error
4292  * @param buf the buffer to get the input to
4293  * @param fd pointer to the filedescriptor to read
4294  * @param append Append to an existing string or replace?
4295  * @param Error strerror() on error 
4296  * @returns numbers of chars read
4297  */
4298 int StrBufTCP_read_line(StrBuf *buf, int *fd, int append, const char **Error)
4299 {
4300         int len, rlen, slen;
4301
4302         if ((buf == NULL) || (buf->buf == NULL)) {
4303                 *Error = strerror(EINVAL);
4304                 return -1;
4305         }
4306
4307         if (!append)
4308                 FlushStrBuf(buf);
4309
4310         slen = len = buf->BufUsed;
4311         while (1) {
4312                 rlen = read(*fd, &buf->buf[len], 1);
4313                 if (rlen < 1) {
4314                         *Error = strerror(errno);
4315                         
4316                         close(*fd);
4317                         *fd = -1;
4318                         
4319                         return -1;
4320                 }
4321                 if (buf->buf[len] == '\n')
4322                         break;
4323                 if (buf->buf[len] != '\r')
4324                         len ++;
4325                 if (len + 2 >= buf->BufSize) {
4326                         buf->BufUsed = len;
4327                         buf->buf[len+1] = '\0';
4328                         IncreaseBuf(buf, 1, -1);
4329                 }
4330         }
4331         buf->BufUsed = len;
4332         buf->buf[len] = '\0';
4333         return len - slen;
4334 }
4335
4336 /**
4337  * @ingroup StrBuf_BufferedIO
4338  * @brief Read a line from socket
4339  * flushes and closes the FD on error
4340  * @param Line the line to read from the fd / I/O Buffer
4341  * @param buf the buffer to get the input to
4342  * @param fd pointer to the filedescriptor to read
4343  * @param timeout number of successless selects until we bail out
4344  * @param selectresolution how long to wait on each select
4345  * @param Error strerror() on error 
4346  * @returns numbers of chars read
4347  */
4348 int StrBufTCP_read_buffered_line(StrBuf *Line, 
4349                                  StrBuf *buf, 
4350                                  int *fd, 
4351                                  int timeout, 
4352                                  int selectresolution, 
4353                                  const char **Error)
4354 {
4355         int len, rlen;
4356         int nSuccessLess = 0;
4357         fd_set rfds;
4358         char *pch = NULL;
4359         int fdflags;
4360         int IsNonBlock;
4361         struct timeval tv;
4362
4363         if (buf->BufUsed > 0) {
4364                 pch = strchr(buf->buf, '\n');
4365                 if (pch != NULL) {
4366                         rlen = 0;
4367                         len = pch - buf->buf;
4368                         if (len > 0 && (*(pch - 1) == '\r') )
4369                                 rlen ++;
4370                         StrBufSub(Line, buf, 0, len - rlen);
4371                         StrBufCutLeft(buf, len + 1);
4372                         return len - rlen;
4373                 }
4374         }
4375         
4376         if (buf->BufSize - buf->BufUsed < 10)
4377                 IncreaseBuf(buf, 1, -1);
4378
4379         fdflags = fcntl(*fd, F_GETFL);
4380         IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4381
4382         while ((nSuccessLess < timeout) && (pch == NULL)) {
4383                 if (IsNonBlock){
4384                         tv.tv_sec = selectresolution;
4385                         tv.tv_usec = 0;
4386                         
4387                         FD_ZERO(&rfds);
4388                         FD_SET(*fd, &rfds);
4389                         if (select(*fd + 1, NULL, &rfds, NULL, &tv) == -1) {
4390                                 *Error = strerror(errno);
4391                                 close (*fd);
4392                                 *fd = -1;
4393                                 return -1;
4394                         }
4395                 }
4396                 if (IsNonBlock && !  FD_ISSET(*fd, &rfds)) {
4397                         nSuccessLess ++;
4398                         continue;
4399                 }
4400                 rlen = read(*fd, 
4401                             &buf->buf[buf->BufUsed], 
4402                             buf->BufSize - buf->BufUsed - 1);
4403                 if (rlen < 1) {
4404                         *Error = strerror(errno);
4405                         close(*fd);
4406                         *fd = -1;
4407                         return -1;
4408                 }
4409                 else if (rlen > 0) {
4410                         nSuccessLess = 0;
4411                         buf->BufUsed += rlen;
4412                         buf->buf[buf->BufUsed] = '\0';
4413                         pch = strchr(buf->buf, '\n');
4414                         if ((pch == NULL) &&
4415                             (buf->BufUsed + 10 > buf->BufSize) &&
4416                             (IncreaseBuf(buf, 1, -1) == -1))
4417                                 return -1;
4418                         continue;
4419                 }
4420                 
4421         }
4422         if (pch != NULL) {
4423                 rlen = 0;
4424                 len = pch - buf->buf;
4425                 if (len > 0 && (*(pch - 1) == '\r') )
4426                         rlen ++;
4427                 StrBufSub(Line, buf, 0, len - rlen);
4428                 StrBufCutLeft(buf, len + 1);
4429                 return len - rlen;
4430         }
4431         return -1;
4432
4433 }
4434
4435 static const char *ErrRBLF_PreConditionFailed="StrBufTCP_read_buffered_line_fast: Wrong arguments or invalid Filedescriptor";
4436 static const char *ErrRBLF_SelectFailed="StrBufTCP_read_buffered_line_fast: Select failed without reason";
4437 static const char *ErrRBLF_NotEnoughSentFromServer="StrBufTCP_read_buffered_line_fast: No complete line was sent from peer";
4438 /**
4439  * @ingroup StrBuf_BufferedIO
4440  * @brief Read a line from socket
4441  * flushes and closes the FD on error
4442  * @param Line where to append our Line read from the fd / I/O Buffer; 
4443  * @param IOBuf the buffer to get the input to; lifetime pair to FD
4444  * @param Pos pointer to the current read position, should be NULL initialized on opening the FD it belongs to.!
4445  * @param fd pointer to the filedescriptor to read
4446  * @param timeout number of successless selects until we bail out
4447  * @param selectresolution how long to wait on each select
4448  * @param Error strerror() on error 
4449  * @returns numbers of chars read or -1 in case of error. "\n" will become 0
4450  */
4451 int StrBufTCP_read_buffered_line_fast(StrBuf *Line, 
4452                                       StrBuf *IOBuf, 
4453                                       const char **Pos,
4454                                       int *fd, 
4455                                       int timeout, 
4456                                       int selectresolution, 
4457                                       const char **Error)
4458 {
4459         const char *pche = NULL;
4460         const char *pos = NULL;
4461         const char *pLF;
4462         int len, rlen, retlen;
4463         int nSuccessLess = 0;
4464         fd_set rfds;
4465         const char *pch = NULL;
4466         int fdflags;
4467         int IsNonBlock;
4468         struct timeval tv;
4469         
4470         retlen = 0;
4471         if ((Line == NULL) ||
4472             (Pos == NULL) ||
4473             (IOBuf == NULL) ||
4474             (*fd == -1))
4475         {
4476                 if (Pos != NULL)
4477                         *Pos = NULL;
4478                 *Error = ErrRBLF_PreConditionFailed;
4479                 return -1;
4480         }
4481
4482         pos = *Pos;
4483         if ((IOBuf->BufUsed > 0) && 
4484             (pos != NULL) && 
4485             (pos < IOBuf->buf + IOBuf->BufUsed)) 
4486         {
4487                 char *pcht;
4488
4489                 pche = IOBuf->buf + IOBuf->BufUsed;
4490                 pch = pos;
4491                 pcht = Line->buf;
4492
4493                 while ((pch < pche) && (*pch != '\n'))
4494                 {
4495                         if (Line->BufUsed + 10 > Line->BufSize)
4496                         {
4497                                 long apos;
4498                                 apos = pcht - Line->buf;
4499                                 *pcht = '\0';
4500                                 IncreaseBuf(Line, 1, -1);
4501                                 pcht = Line->buf + apos;
4502                         }
4503                         *pcht++ = *pch++;
4504                         Line->BufUsed++;
4505                         retlen++;
4506                 }
4507
4508                 len = pch - pos;
4509                 if (len > 0 && (*(pch - 1) == '\r') )
4510                 {
4511                         retlen--;
4512                         len --;
4513                         pcht --;
4514                         Line->BufUsed --;
4515                 }
4516                 *pcht = '\0';
4517
4518                 if ((pch >= pche) || (*pch == '\0'))
4519                 {
4520                         FlushStrBuf(IOBuf);
4521                         *Pos = NULL;
4522                         pch = NULL;
4523                         pos = 0;
4524                 }
4525
4526                 if ((pch != NULL) && 
4527                     (pch <= pche)) 
4528                 {
4529                         if (pch + 1 >= pche) {
4530                                 *Pos = NULL;
4531                                 FlushStrBuf(IOBuf);
4532                         }
4533                         else
4534                                 *Pos = pch + 1;
4535                         
4536                         return retlen;
4537                 }
4538                 else 
4539                         FlushStrBuf(IOBuf);
4540         }
4541
4542         /* If we come here, Pos is Unset since we read everything into Line, and now go for more. */
4543         
4544         if (IOBuf->BufSize - IOBuf->BufUsed < 10)
4545                 IncreaseBuf(IOBuf, 1, -1);
4546
4547         fdflags = fcntl(*fd, F_GETFL);
4548         IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4549
4550         pLF = NULL;
4551         while ((nSuccessLess < timeout) && 
4552                (pLF == NULL) &&
4553                (*fd != -1)) {
4554                 if (IsNonBlock)
4555                 {
4556                         tv.tv_sec = 1;
4557                         tv.tv_usec = 0;
4558                 
4559                         FD_ZERO(&rfds);
4560                         FD_SET(*fd, &rfds);
4561                         if (select((*fd) + 1, &rfds, NULL, NULL, &tv) == -1) {
4562                                 *Error = strerror(errno);
4563                                 close (*fd);
4564                                 *fd = -1;
4565                                 if (*Error == NULL)
4566                                         *Error = ErrRBLF_SelectFailed;
4567                                 return -1;
4568                         }
4569                         if (! FD_ISSET(*fd, &rfds) != 0) {
4570                                 nSuccessLess ++;
4571                                 continue;
4572                         }
4573                 }
4574                 rlen = read(*fd, 
4575                             &IOBuf->buf[IOBuf->BufUsed], 
4576                             IOBuf->BufSize - IOBuf->BufUsed - 1);
4577                 if (rlen < 1) {
4578                         *Error = strerror(errno);
4579                         close(*fd);
4580                         *fd = -1;
4581                         return -1;
4582                 }
4583                 else if (rlen > 0) {
4584                         nSuccessLess = 0;
4585                         pLF = IOBuf->buf + IOBuf->BufUsed;
4586                         IOBuf->BufUsed += rlen;
4587                         IOBuf->buf[IOBuf->BufUsed] = '\0';
4588                         
4589                         pche = IOBuf->buf + IOBuf->BufUsed;
4590                         
4591                         while ((pLF < pche) && (*pLF != '\n'))
4592                                 pLF ++;
4593                         if ((pLF >= pche) || (*pLF == '\0'))
4594                                 pLF = NULL;
4595
4596                         if (IOBuf->BufUsed + 10 > IOBuf->BufSize)
4597                         {
4598                                 long apos = 0;
4599
4600                                 if (pLF != NULL) apos = pLF - IOBuf->buf;
4601                                 IncreaseBuf(IOBuf, 1, -1);      
4602                                 if (pLF != NULL) pLF = IOBuf->buf + apos;
4603                         }
4604
4605                         continue;
4606                 }
4607                 else
4608                 {
4609                         nSuccessLess++;
4610                 }
4611         }
4612         *Pos = NULL;
4613         if (pLF != NULL) {
4614                 pos = IOBuf->buf;
4615                 len = pLF - pos;
4616                 if (len > 0 && (*(pLF - 1) == '\r') )
4617                         len --;
4618                 StrBufAppendBufPlain(Line, ChrPtr(IOBuf), len, 0);
4619                 if (pLF + 1 >= IOBuf->buf + IOBuf->BufUsed)
4620                 {
4621                         FlushStrBuf(IOBuf);
4622                 }
4623                 else 
4624                         *Pos = pLF + 1;
4625                 return retlen + len;
4626         }
4627         *Error = ErrRBLF_NotEnoughSentFromServer;
4628         return -1;
4629
4630 }
4631
4632 static const char *ErrRBLF_BLOBPreConditionFailed="StrBufReadBLOB: Wrong arguments or invalid Filedescriptor";
4633 /**
4634  * @ingroup StrBuf_IO
4635  * @brief Input binary data from socket
4636  * flushes and closes the FD on error
4637  * @param Buf the buffer to get the input to
4638  * @param fd pointer to the filedescriptor to read
4639  * @param append Append to an existing string or replace?
4640  * @param nBytes the maximal number of bytes to read
4641  * @param Error strerror() on error 
4642  * @returns numbers of chars read
4643  */
4644 int StrBufReadBLOB(StrBuf *Buf, int *fd, int append, long nBytes, const char **Error)
4645 {
4646         int fdflags;
4647         int rlen;
4648         int nSuccessLess;
4649         int nRead = 0;
4650         char *ptr;
4651         int IsNonBlock;
4652         struct timeval tv;
4653         fd_set rfds;
4654
4655         if ((Buf == NULL) || (Buf->buf == NULL) || (*fd == -1))
4656         {
4657                 *Error = ErrRBLF_BLOBPreConditionFailed;
4658                 return -1;
4659         }
4660         if (!append)
4661                 FlushStrBuf(Buf);
4662         if (Buf->BufUsed + nBytes >= Buf->BufSize)
4663                 IncreaseBuf(Buf, 1, Buf->BufUsed + nBytes);
4664
4665         ptr = Buf->buf + Buf->BufUsed;
4666
4667         fdflags = fcntl(*fd, F_GETFL);
4668         IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4669         nSuccessLess = 0;
4670         while ((nRead < nBytes) && 
4671                (*fd != -1)) 
4672         {
4673                 if (IsNonBlock)
4674                 {
4675                         tv.tv_sec = 1;
4676                         tv.tv_usec = 0;
4677                 
4678                         FD_ZERO(&rfds);
4679                         FD_SET(*fd, &rfds);
4680                         if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
4681                                 *Error = strerror(errno);
4682                                 close (*fd);
4683                                 *fd = -1;
4684                                 if (*Error == NULL)
4685                                         *Error = ErrRBLF_SelectFailed;
4686                                 return -1;
4687                         }
4688                         if (! FD_ISSET(*fd, &rfds) != 0) {
4689                                 nSuccessLess ++;
4690                                 continue;
4691                         }
4692                 }
4693
4694                 if ((rlen = read(*fd, 
4695                                  ptr,
4696                                  nBytes - nRead)) == -1) {
4697                         close(*fd);
4698                         *fd = -1;
4699                         *Error = strerror(errno);
4700                         return rlen;
4701                 }
4702                 nRead += rlen;
4703                 ptr += rlen;
4704                 Buf->BufUsed += rlen;
4705         }
4706         Buf->buf[Buf->BufUsed] = '\0';
4707         return nRead;
4708 }
4709
4710 const char *ErrRBB_BLOBFPreConditionFailed = "StrBufReadBLOBBuffered: to many selects; aborting.";
4711 const char *ErrRBB_too_many_selects        = "StrBufReadBLOBBuffered: to many selects; aborting.";
4712 /**
4713  * @ingroup StrBuf_BufferedIO
4714  * @brief Input binary data from socket
4715  * flushes and closes the FD on error
4716  * @param Blob put binary thing here
4717  * @param IOBuf the buffer to get the input to
4718  * @param Pos offset inside of IOBuf
4719  * @param fd pointer to the filedescriptor to read
4720  * @param append Append to an existing string or replace?
4721  * @param nBytes the maximal number of bytes to read
4722  * @param check whether we should search for '000\n' terminators in case of timeouts
4723  * @param Error strerror() on error 
4724  * @returns numbers of chars read
4725  */
4726 int StrBufReadBLOBBuffered(StrBuf *Blob, 
4727                            StrBuf *IOBuf, 
4728                            const char **Pos,
4729                            int *fd, 
4730                            int append, 
4731                            long nBytes, 
4732                            int check, 
4733                            const char **Error)
4734 {
4735         const char *pos;
4736         int fdflags;
4737         int rlen = 0;
4738         int nRead = 0;
4739         int nAlreadyRead = 0;
4740         int IsNonBlock;
4741         char *ptr;
4742         fd_set rfds;
4743         struct timeval tv;
4744         int nSuccessLess = 0;
4745         int MaxTries;
4746
4747         if ((Blob == NULL)  ||
4748             (*fd == -1)     ||
4749             (IOBuf == NULL) ||
4750             (Pos == NULL))
4751         {
4752                 if (Pos != NULL)
4753                         *Pos = NULL;
4754                 *Error = ErrRBB_BLOBFPreConditionFailed;
4755                 return -1;
4756         }
4757
4758         if (!append)
4759                 FlushStrBuf(Blob);
4760         if (Blob->BufUsed + nBytes >= Blob->BufSize) 
4761                 IncreaseBuf(Blob, append, Blob->BufUsed + nBytes);
4762         
4763         pos = *Pos;
4764
4765         if (pos != NULL)
4766                 rlen = pos - IOBuf->buf;
4767         rlen = IOBuf->BufUsed - rlen;
4768
4769
4770         if ((IOBuf->BufUsed > 0) && 
4771             (pos != NULL) && 
4772             (pos < IOBuf->buf + IOBuf->BufUsed)) 
4773         {
4774                 if (rlen < nBytes) {
4775                         memcpy(Blob->buf + Blob->BufUsed, pos, rlen);
4776                         Blob->BufUsed += rlen;
4777                         Blob->buf[Blob->BufUsed] = '\0';
4778                         nAlreadyRead = nRead = rlen;
4779                         *Pos = NULL; 
4780                 }
4781                 if (rlen >= nBytes) {
4782                         memcpy(Blob->buf + Blob->BufUsed, pos, nBytes);
4783                         Blob->BufUsed += nBytes;
4784                         Blob->buf[Blob->BufUsed] = '\0';
4785                         if (rlen == nBytes) {
4786                                 *Pos = NULL; 
4787                                 FlushStrBuf(IOBuf);
4788                         }
4789                         else 
4790                                 *Pos += nBytes;
4791                         return nBytes;
4792                 }
4793         }
4794
4795         FlushStrBuf(IOBuf);
4796         *Pos = NULL;
4797         if (IOBuf->BufSize < nBytes - nRead)
4798                 IncreaseBuf(IOBuf, 0, nBytes - nRead);
4799         ptr = IOBuf->buf;
4800
4801         fdflags = fcntl(*fd, F_GETFL);
4802         IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4803         if (IsNonBlock)
4804                 MaxTries =   1000;
4805         else
4806                 MaxTries = 100000;
4807
4808         nBytes -= nRead;
4809         nRead = 0;
4810         while ((nSuccessLess < MaxTries) && 
4811                (nRead < nBytes) &&
4812                (*fd != -1)) {
4813                 if (IsNonBlock)
4814                 {
4815                         tv.tv_sec = 1;
4816                         tv.tv_usec = 0;
4817                 
4818                         FD_ZERO(&rfds);
4819                         FD_SET(*fd, &rfds);
4820                         if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
4821                                 *Error = strerror(errno);
4822                                 close (*fd);
4823                                 *fd = -1;
4824                                 if (*Error == NULL)
4825                                         *Error = ErrRBLF_SelectFailed;
4826                                 return -1;
4827                         }
4828                         if (! FD_ISSET(*fd, &rfds) != 0) {
4829                                 nSuccessLess ++;
4830                                 continue;
4831                         }
4832                 }
4833                 rlen = read(*fd, 
4834                             ptr,
4835                             IOBuf->BufSize - (ptr - IOBuf->buf));
4836                 if (rlen == -1) {
4837                         close(*fd);
4838                         *fd = -1;
4839                         *Error = strerror(errno);
4840                         return rlen;
4841                 }
4842                 else if (rlen == 0){
4843                         if ((check == NNN_TERM) && 
4844                             (nRead > 5) &&
4845                             (strncmp(IOBuf->buf + IOBuf->BufUsed - 5, "\n000\n", 5) == 0)) 
4846                         {
4847                                 StrBufPlain(Blob, HKEY("\n000\n"));
4848                                 StrBufCutRight(Blob, 5);
4849                                 return Blob->BufUsed;
4850                         }
4851                         else if (!IsNonBlock) 
4852                                 nSuccessLess ++;
4853                         else if (nSuccessLess > MaxTries) {
4854                                 FlushStrBuf(IOBuf);
4855                                 *Error = ErrRBB_too_many_selects;
4856                                 return -1;
4857                         }
4858                 }
4859                 else if (rlen > 0) {
4860                         nSuccessLess = 0;
4861                         nRead += rlen;
4862                         ptr += rlen;
4863                         IOBuf->BufUsed += rlen;
4864                 }
4865         }
4866         if (nSuccessLess >= MaxTries) {
4867                 FlushStrBuf(IOBuf);
4868                 *Error = ErrRBB_too_many_selects;
4869                 return -1;
4870         }
4871
4872         if (nRead > nBytes) {
4873                 *Pos = IOBuf->buf + nBytes;
4874         }
4875         Blob->buf[Blob->BufUsed] = '\0';
4876         StrBufAppendBufPlain(Blob, IOBuf->buf, nBytes, 0);
4877         if (*Pos == NULL) {
4878                 FlushStrBuf(IOBuf);
4879         }
4880         return nRead + nAlreadyRead;
4881 }
4882
4883 /**
4884  * @ingroup StrBuf_IO
4885  * @brief extract a "next line" from Buf; Ptr to persist across several iterations
4886  * @param LineBuf your line will be copied here.
4887  * @param Buf BLOB with lines of text...
4888  * @param Ptr moved arround to keep the next-line across several iterations
4889  *        has to be &NULL on start; will be &NotNULL on end of buffer
4890  * @returns size of remaining buffer
4891  */
4892 int StrBufSipLine(StrBuf *LineBuf, const StrBuf *Buf, const char **Ptr)
4893 {
4894         const char *aptr, *ptr, *eptr;
4895         char *optr, *xptr;
4896
4897         if ((Buf == NULL) ||
4898             (*Ptr == StrBufNOTNULL) ||
4899             (LineBuf == NULL)||
4900             (LineBuf->buf == NULL))
4901         {
4902                 *Ptr = StrBufNOTNULL;
4903                 return 0;
4904         }
4905
4906         FlushStrBuf(LineBuf);
4907         if (*Ptr==NULL)
4908                 ptr = aptr = Buf->buf;
4909         else
4910                 ptr = aptr = *Ptr;
4911
4912         optr = LineBuf->buf;
4913         eptr = Buf->buf + Buf->BufUsed;
4914         xptr = LineBuf->buf + LineBuf->BufSize - 1;
4915
4916         while ((ptr <= eptr) && 
4917                (*ptr != '\n') &&
4918                (*ptr != '\r') )
4919         {
4920                 *optr = *ptr;
4921                 optr++; ptr++;
4922                 if (optr == xptr) {
4923                         LineBuf->BufUsed = optr - LineBuf->buf;
4924                         IncreaseBuf(LineBuf,  1, LineBuf->BufUsed + 1);
4925                         optr = LineBuf->buf + LineBuf->BufUsed;
4926                         xptr = LineBuf->buf + LineBuf->BufSize - 1;
4927                 }
4928         }
4929
4930         if ((ptr >= eptr) && (optr > LineBuf->buf))
4931                 optr --;
4932         LineBuf->BufUsed = optr - LineBuf->buf;
4933         *optr = '\0';       
4934         if ((ptr <= eptr) && (*ptr == '\r'))
4935                 ptr ++;
4936         if ((ptr <= eptr) && (*ptr == '\n'))
4937                 ptr ++;
4938         
4939         if (ptr < eptr) {
4940                 *Ptr = ptr;
4941         }
4942         else {
4943                 *Ptr = StrBufNOTNULL;
4944         }
4945
4946         return Buf->BufUsed - (ptr - Buf->buf);
4947 }
4948
4949
4950 /**
4951  * @ingroup StrBuf_IO
4952  * @brief removes double slashes from pathnames
4953  * @param Dir directory string to filter
4954  * @param RemoveTrailingSlash allows / disallows trailing slashes
4955  */
4956 void StrBufStripSlashes(StrBuf *Dir, int RemoveTrailingSlash)
4957 {
4958         char *a, *b;
4959
4960         a = b = Dir->buf;
4961
4962         while (!IsEmptyStr(a)) {
4963                 if (*a == '/') {
4964                         while (*a == '/')
4965                                 a++;
4966                         *b = '/';
4967                         b++;
4968                 }
4969                 else {
4970                         *b = *a;
4971                         b++; a++;
4972                 }
4973         }
4974         if ((RemoveTrailingSlash) &&
4975             (b > Dir->buf) && 
4976             (*(b - 1) == '/')){
4977                 b--;
4978         }
4979         *b = '\0';
4980         Dir->BufUsed = b - Dir->buf;
4981 }
4982
4983