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