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