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