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