* some more input condition sanitizing
[citadel.git] / libcitadel / lib / stringbuf.c
1 #include "sysdep.h"
2 #include <ctype.h>
3 #include <errno.h>
4 #include <string.h>
5 #include <unistd.h>
6 #include <string.h>
7 #include <stdio.h>
8 #include <sys/select.h>
9 #include <fcntl.h>
10 #include <sys/types.h>
11 #define SHOW_ME_VAPPEND_PRINTF
12 #include <stdarg.h>
13 #include "libcitadel.h"
14
15 #ifdef HAVE_ICONV
16 #include <iconv.h>
17 #endif
18
19 #ifdef HAVE_BACKTRACE
20 #include <execinfo.h>
21 #endif
22
23 #ifdef HAVE_ZLIB
24 #include <zlib.h>
25 int ZEXPORT compress_gzip(Bytef * dest, size_t * destLen,
26                           const Bytef * source, uLong sourceLen, int level);
27 #endif
28 int BaseStrBufSize = 64;
29
30 const char *StrBufNOTNULL = ((char*) NULL) - 1;
31
32 const char HexList[256][3] = {
33         "00","01","02","03","04","05","06","07","08","09","0A","0B","0C","0D","0E","0F",
34         "10","11","12","13","14","15","16","17","18","19","1A","1B","1C","1D","1E","1F",
35         "20","21","22","23","24","25","26","27","28","29","2A","2B","2C","2D","2E","2F",
36         "30","31","32","33","34","35","36","37","38","39","3A","3B","3C","3D","3E","3F",
37         "40","41","42","43","44","45","46","47","48","49","4A","4B","4C","4D","4E","4F",
38         "50","51","52","53","54","55","56","57","58","59","5A","5B","5C","5D","5E","5F",
39         "60","61","62","63","64","65","66","67","68","69","6A","6B","6C","6D","6E","6F",
40         "70","71","72","73","74","75","76","77","78","79","7A","7B","7C","7D","7E","7F",
41         "80","81","82","83","84","85","86","87","88","89","8A","8B","8C","8D","8E","8F",
42         "90","91","92","93","94","95","96","97","98","99","9A","9B","9C","9D","9E","9F",
43         "A0","A1","A2","A3","A4","A5","A6","A7","A8","A9","AA","AB","AC","AD","AE","AF",
44         "B0","B1","B2","B3","B4","B5","B6","B7","B8","B9","BA","BB","BC","BD","BE","BF",
45         "C0","C1","C2","C3","C4","C5","C6","C7","C8","C9","CA","CB","CC","CD","CE","CF",
46         "D0","D1","D2","D3","D4","D5","D6","D7","D8","D9","DA","DB","DC","DD","DE","DF",
47         "E0","E1","E2","E3","E4","E5","E6","E7","E8","E9","EA","EB","EC","ED","EE","EF",
48         "F0","F1","F2","F3","F4","F5","F6","F7","F8","F9","FA","FB","FC","FD","FE","FF"};
49
50 /**
51  * @defgroup StrBuf Stringbuffer, A class for manipulating strings with dynamic buffers
52  * StrBuf is a versatile class, aiding the handling of dynamic strings
53  *  * reduce de/reallocations
54  *  * reduce the need to remeasure it
55  *  * reduce scanning over the string (in @ref StrBuf_NextTokenizer "Tokenizers")
56  *  * allow asyncroneous IO for line and Blob based operations
57  *  * reduce the use of memove in those
58  *  * Quick filling in several operations with append functions
59  */
60
61 /**
62  * @defgroup StrBuf_DeConstructors Create/Destroy StrBufs
63  * @ingroup StrBuf
64  */
65
66 /**
67  * @defgroup StrBuf_Cast Cast operators to interact with char* based code
68  * @ingroup StrBuf
69  * use these operators to interfere with code demanding char*; 
70  * if you need to own the content, smash me. Avoid, since we loose the length information.
71  */
72
73 /**
74  * @defgroup StrBuf_Filler Create/Replace/Append Content into a StrBuf
75  * @ingroup StrBuf
76  * operations to get your Strings into a StrBuf, manipulating them, or appending
77  */
78 /**
79  * @defgroup StrBuf_NextTokenizer Fast tokenizer to pull tokens in sequence 
80  * @ingroup StrBuf
81  * Quick tokenizer; demands of the user to pull its tokens in sequence
82  */
83
84 /**
85  * @defgroup StrBuf_Tokenizer tokenizer Functions; Slow ones.
86  * @ingroup StrBuf
87  * versatile tokenizer; random access to tokens, but slower; Prefer the @ref StrBuf_NextTokenizer "Next Tokenizer"
88  */
89
90 /**
91  * @defgroup StrBuf_BufferedIO Buffered IO with Asynchroneous reads and no unneeded memmoves (the fast ones)
92  * @ingroup StrBuf
93  * File IO to fill StrBufs; Works with work-buffer shared across several calls;
94  * External Cursor to maintain the current read position inside of the buffer
95  * the non-fast ones will use memove to keep the start of the buffer the read buffer (which is slower) 
96  */
97
98 /**
99  * @defgroup StrBuf_IO FileIO; Prefer @ref StrBuf_BufferedIO
100  * @ingroup StrBuf
101  * Slow I/O; avoid.
102  */
103
104 /**
105  * @defgroup StrBuf_DeEnCoder functions to translate the contents of a buffer
106  * @ingroup StrBuf
107  * these functions translate the content of a buffer into another representation;
108  * some are combined Fillers and encoders
109  */
110
111 /**
112  * Private Structure for the Stringbuffer
113  */
114 struct StrBuf {
115         char *buf;         /**< the pointer to the dynamic buffer */
116         long BufSize;      /**< how many spcae do we optain */
117         long BufUsed;      /**< StNumber of Chars used excluding the trailing \\0 */
118         int ConstBuf;      /**< are we just a wrapper arround a static buffer and musn't we be changed? */
119 #ifdef SIZE_DEBUG
120         long nIncreases;   /**< for profiling; cound how many times we needed more */
121         char bt [SIZ];     /**< Stacktrace of last increase */
122         char bt_lastinc [SIZ]; /**< How much did we increase last time? */
123 #endif
124 };
125
126
127 static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE);
128 static inline int Ctdl_IsUtf8SequenceStart(const char Char);
129
130 #ifdef SIZE_DEBUG
131 #ifdef HAVE_BACKTRACE
132 static void StrBufBacktrace(StrBuf *Buf, int which)
133 {
134         int n;
135         char *pstart, *pch;
136         void *stack_frames[50];
137         size_t size, i;
138         char **strings;
139
140         if (which)
141                 pstart = pch = Buf->bt;
142         else
143                 pstart = pch = Buf->bt_lastinc;
144         size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
145         strings = backtrace_symbols(stack_frames, size);
146         for (i = 0; i < size; i++) {
147                 if (strings != NULL)
148                         n = snprintf(pch, SIZ - (pch - pstart), "%s\\n", strings[i]);
149                 else
150                         n = snprintf(pch, SIZ - (pch - pstart), "%p\\n", stack_frames[i]);
151                 pch += n;
152         }
153         free(strings);
154
155
156 }
157 #endif
158
159 void dbg_FreeStrBuf(StrBuf *FreeMe, char *FromWhere)
160 {
161         if (hFreeDbglog == -1){
162                 pid_t pid = getpid();
163                 char path [SIZ];
164                 snprintf(path, SIZ, "/tmp/libcitadel_strbuf_realloc.log.%d", pid);
165                 hFreeDbglog = open(path, O_APPEND|O_CREAT|O_WRONLY);
166         }
167         if ((*FreeMe)->nIncreases > 0)
168         {
169                 char buf[SIZ * 3];
170                 long n;
171                 n = snprintf(buf, SIZ * 3, "%c+|%ld|%ld|%ld|%s|%s|\n",
172                              FromWhere,
173                              (*FreeMe)->nIncreases,
174                              (*FreeMe)->BufUsed,
175                              (*FreeMe)->BufSize,
176                              (*FreeMe)->bt,
177                              (*FreeMe)->bt_lastinc);
178                 n = write(hFreeDbglog, buf, n);
179         }
180         else
181         {
182                 char buf[128];
183                 long n;
184                 n = snprintf(buf, 128, "%c_|0|%ld%ld|\n",
185                              FromWhere,
186                              (*FreeMe)->BufUsed,
187                              (*FreeMe)->BufSize);
188                 n = write(hFreeDbglog, buf, n);
189         }
190 }
191
192 void dbg_IncreaseBuf(StrBuf *IncMe)
193 {
194         Buf->nIncreases++;
195 #ifdef HAVE_BACKTRACE
196         StrBufBacktrace(Buf, 1);
197 #endif
198 }
199
200 void dbg_Init(StrBuf *Buf)
201 {
202         Buf->nIncreases = 0;
203         Buf->bt[0] = '\0';
204         Buf->bt_lastinc[0] = '\0';
205 #ifdef HAVE_BACKTRACE
206         StrBufBacktrace(Buf, 0);
207 #endif
208 }
209
210 #else
211 /* void it... */
212 #define dbg_FreeStrBuf(a, b)
213 #define dbg_IncreaseBuf(a)
214 #define dbg_Init(a)
215
216 #endif
217
218 /** 
219  * @ingroup StrBuf_Cast
220  * @brief Cast operator to Plain String 
221  * @note if the buffer is altered by StrBuf operations, this pointer may become 
222  *  invalid. So don't lean on it after altering the buffer!
223  *  Since this operation is considered cheap, rather call it often than risking
224  *  your pointer to become invalid!
225  * @param Str the string we want to get the c-string representation for
226  * @returns the Pointer to the Content. Don't mess with it!
227  */
228 inline const char *ChrPtr(const StrBuf *Str)
229 {
230         if (Str == NULL)
231                 return "";
232         return Str->buf;
233 }
234
235 /**
236  * @ingroup StrBuf_Cast
237  * @brief since we know strlen()'s result, provide it here.
238  * @param Str the string to return the length to
239  * @returns contentlength of the buffer
240  */
241 inline int StrLength(const StrBuf *Str)
242 {
243         return (Str != NULL) ? Str->BufUsed : 0;
244 }
245
246 /**
247  * @ingroup StrBuf_DeConstructors
248  * @brief local utility function to resize the buffer
249  * @param Buf the buffer whichs storage we should increase
250  * @param KeepOriginal should we copy the original buffer or just start over with a new one
251  * @param DestSize what should fit in after?
252  */
253 static int IncreaseBuf(StrBuf *Buf, int KeepOriginal, int DestSize)
254 {
255         char *NewBuf;
256         size_t NewSize = Buf->BufSize * 2;
257
258         if (Buf->ConstBuf)
259                 return -1;
260                 
261         if (DestSize > 0)
262                 while (NewSize <= DestSize)
263                         NewSize *= 2;
264
265         NewBuf= (char*) malloc(NewSize);
266         if (NewBuf == NULL)
267                 return -1;
268
269         if (KeepOriginal && (Buf->BufUsed > 0))
270         {
271                 memcpy(NewBuf, Buf->buf, Buf->BufUsed);
272         }
273         else
274         {
275                 NewBuf[0] = '\0';
276                 Buf->BufUsed = 0;
277         }
278         free (Buf->buf);
279         Buf->buf = NewBuf;
280         Buf->BufSize = NewSize;
281
282         dbg_IncreaseBuf(Buf);
283
284         return Buf->BufSize;
285 }
286
287 /**
288  * @ingroup StrBuf_DeConstructors
289  * @brief shrink an _EMPTY_ buffer if its Buffer superseeds threshhold to NewSize. Buffercontent is thoroughly ignored and flushed.
290  * @param Buf Buffer to shrink (has to be empty)
291  * @param ThreshHold if the buffer is bigger then this, its readjusted
292  * @param NewSize if we Shrink it, how big are we going to be afterwards?
293  */
294 void ReAdjustEmptyBuf(StrBuf *Buf, long ThreshHold, long NewSize)
295 {
296         if ((Buf != NULL) && (Buf->BufUsed > ThreshHold)) {
297                 free(Buf->buf);
298                 Buf->buf = (char*) malloc(NewSize);
299                 Buf->BufUsed = 0;
300                 Buf->BufSize = NewSize;
301         }
302 }
303
304 /**
305  * @ingroup StrBuf_DeConstructors
306  * @brief shrink long term buffers to their real size so they don't waste memory
307  * @param Buf buffer to shrink
308  * @param Force if not set, will just executed if the buffer is much to big; set for lifetime strings
309  * @returns physical size of the buffer
310  */
311 long StrBufShrinkToFit(StrBuf *Buf, int Force)
312 {
313         if (Buf == NULL)
314                 return -1;
315         if (Force || 
316             (Buf->BufUsed + (Buf->BufUsed / 3) > Buf->BufSize))
317         {
318                 char *TmpBuf = (char*) malloc(Buf->BufUsed + 1);
319                 memcpy (TmpBuf, Buf->buf, Buf->BufUsed + 1);
320                 Buf->BufSize = Buf->BufUsed + 1;
321                 free(Buf->buf);
322                 Buf->buf = TmpBuf;
323         }
324         return Buf->BufUsed;
325 }
326
327 /**
328  * @ingroup StrBuf_DeConstructors
329  * @brief Allocate a new buffer with default buffer size
330  * @returns the new stringbuffer
331  */
332 StrBuf* NewStrBuf(void)
333 {
334         StrBuf *NewBuf;
335
336         NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
337         NewBuf->buf = (char*) malloc(BaseStrBufSize);
338         NewBuf->buf[0] = '\0';
339         NewBuf->BufSize = BaseStrBufSize;
340         NewBuf->BufUsed = 0;
341         NewBuf->ConstBuf = 0;
342
343         dbg_Init (NewBuf);
344
345         return NewBuf;
346 }
347
348 /** 
349  * @ingroup StrBuf_DeConstructors
350  * @brief Copy Constructor; returns a duplicate of CopyMe
351  * @param CopyMe Buffer to faxmilate
352  * @returns the new stringbuffer
353  */
354 StrBuf* NewStrBufDup(const StrBuf *CopyMe)
355 {
356         StrBuf *NewBuf;
357         
358         if (CopyMe == NULL)
359                 return NewStrBuf();
360
361         NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
362         NewBuf->buf = (char*) malloc(CopyMe->BufSize);
363         memcpy(NewBuf->buf, CopyMe->buf, CopyMe->BufUsed + 1);
364         NewBuf->BufUsed = CopyMe->BufUsed;
365         NewBuf->BufSize = CopyMe->BufSize;
366         NewBuf->ConstBuf = 0;
367
368         dbg_Init(NewBuf);
369
370         return NewBuf;
371 }
372
373 /**
374  * @ingroup StrBuf_DeConstructors
375  * @brief create a new Buffer using an existing c-string
376  * this function should also be used if you want to pre-suggest
377  * the buffer size to allocate in conjunction with ptr == NULL
378  * @param ptr the c-string to copy; may be NULL to create a blank instance
379  * @param nChars How many chars should we copy; -1 if we should measure the length ourselves
380  * @returns the new stringbuffer
381  */
382 StrBuf* NewStrBufPlain(const char* ptr, int nChars)
383 {
384         StrBuf *NewBuf;
385         size_t Siz = BaseStrBufSize;
386         size_t CopySize;
387
388         NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
389         if (nChars < 0)
390                 CopySize = strlen((ptr != NULL)?ptr:"");
391         else
392                 CopySize = nChars;
393
394         while (Siz <= CopySize)
395                 Siz *= 2;
396
397         NewBuf->buf = (char*) malloc(Siz);
398         NewBuf->BufSize = Siz;
399         if (ptr != NULL) {
400                 memcpy(NewBuf->buf, ptr, CopySize);
401                 NewBuf->buf[CopySize] = '\0';
402                 NewBuf->BufUsed = CopySize;
403         }
404         else {
405                 NewBuf->buf[0] = '\0';
406                 NewBuf->BufUsed = 0;
407         }
408         NewBuf->ConstBuf = 0;
409
410         dbg_Init(NewBuf)
411
412                 return NewBuf;
413 }
414
415 /**
416  * @ingroup StrBuf_DeConstructors
417  * @brief Set an existing buffer from a c-string
418  * @param Buf buffer to load
419  * @param ptr c-string to put into 
420  * @param nChars set to -1 if we should work 0-terminated
421  * @returns the new length of the string
422  */
423 int StrBufPlain(StrBuf *Buf, const char* ptr, int nChars)
424 {
425         size_t Siz = Buf->BufSize;
426         size_t CopySize;
427
428         if (nChars < 0)
429                 CopySize = strlen(ptr);
430         else
431                 CopySize = nChars;
432
433         while (Siz <= CopySize)
434                 Siz *= 2;
435
436         if (Siz != Buf->BufSize)
437                 IncreaseBuf(Buf, 0, Siz);
438         memcpy(Buf->buf, ptr, CopySize);
439         Buf->buf[CopySize] = '\0';
440         Buf->BufUsed = CopySize;
441         Buf->ConstBuf = 0;
442         return CopySize;
443 }
444
445
446 /**
447  * @ingroup StrBuf_DeConstructors
448  * @brief use strbuf as wrapper for a string constant for easy handling
449  * @param StringConstant a string to wrap
450  * @param SizeOfStrConstant should be sizeof(StringConstant)-1
451  */
452 StrBuf* _NewConstStrBuf(const char* StringConstant, size_t SizeOfStrConstant)
453 {
454         StrBuf *NewBuf;
455
456         NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
457         NewBuf->buf = (char*) StringConstant;
458         NewBuf->BufSize = SizeOfStrConstant;
459         NewBuf->BufUsed = SizeOfStrConstant;
460         NewBuf->ConstBuf = 1;
461
462         dbg_Init(NewBuf);
463
464         return NewBuf;
465 }
466
467
468 /**
469  * @ingroup StrBuf_DeConstructors
470  * @brief flush the content of a Buf; keep its struct
471  * @param buf Buffer to flush
472  */
473 int FlushStrBuf(StrBuf *buf)
474 {
475         if (buf == NULL)
476                 return -1;
477         if (buf->ConstBuf)
478                 return -1;       
479         buf->buf[0] ='\0';
480         buf->BufUsed = 0;
481         return 0;
482 }
483
484 /**
485  * @ingroup StrBuf_DeConstructors
486  * @brief wipe the content of a Buf thoroughly (overwrite it -> expensive); keep its struct
487  * @param buf Buffer to wipe
488  */
489 int FLUSHStrBuf(StrBuf *buf)
490 {
491         if (buf == NULL)
492                 return -1;
493         if (buf->ConstBuf)
494                 return -1;
495         if (buf->BufUsed > 0) {
496                 memset(buf->buf, 0, buf->BufUsed);
497                 buf->BufUsed = 0;
498         }
499         return 0;
500 }
501
502 #ifdef SIZE_DEBUG
503 int hFreeDbglog = -1;
504 #endif
505 /**
506  * @ingroup StrBuf_DeConstructors
507  * @brief Release a Buffer
508  * Its a double pointer, so it can NULL your pointer
509  * so fancy SIG11 appear instead of random results
510  * @param FreeMe Pointer Pointer to the buffer to free
511  */
512 void FreeStrBuf (StrBuf **FreeMe)
513 {
514         if (*FreeMe == NULL)
515                 return;
516
517         dbg_FreeStrBuf(FreeMe, 'F');
518
519         if (!(*FreeMe)->ConstBuf) 
520                 free((*FreeMe)->buf);
521         free(*FreeMe);
522         *FreeMe = NULL;
523 }
524
525 /**
526  * @ingroup StrBuf_DeConstructors
527  * @brief flatten a Buffer to the Char * we return 
528  * Its a double pointer, so it can NULL your pointer
529  * so fancy SIG11 appear instead of random results
530  * The Callee then owns the buffer and is responsible for freeing it.
531  * @param SmashMe Pointer Pointer to the buffer to release Buf from and free
532  * @returns the pointer of the buffer; Callee owns the memory thereafter.
533  */
534 char *SmashStrBuf (StrBuf **SmashMe)
535 {
536         char *Ret;
537
538         if ((SmashMe == NULL) || (*SmashMe == NULL))
539                 return NULL;
540         
541         dbg_FreeStrBuf(SmashMe, 'S');
542
543         Ret = (*SmashMe)->buf;
544         free(*SmashMe);
545         *SmashMe = NULL;
546         return Ret;
547 }
548
549 /**
550  * @ingroup StrBuf_DeConstructors
551  * @brief Release the buffer
552  * If you want put your StrBuf into a Hash, use this as Destructor.
553  * @param VFreeMe untyped pointer to a StrBuf. be shure to do the right thing [TM]
554  */
555 void HFreeStrBuf (void *VFreeMe)
556 {
557         StrBuf *FreeMe = (StrBuf*)VFreeMe;
558         if (FreeMe == NULL)
559                 return;
560
561         dbg_FreeStrBuf(SmashMe, 'H');
562
563         if (!FreeMe->ConstBuf) 
564                 free(FreeMe->buf);
565         free(FreeMe);
566 }
567
568
569 /*******************************************************************************
570  *                      Simple string transformations                          *
571  *******************************************************************************/
572
573 /**
574  * @ingroup StrBuf
575  * @brief Wrapper around atol
576  */
577 long StrTol(const StrBuf *Buf)
578 {
579         if (Buf == NULL)
580                 return 0;
581         if(Buf->BufUsed > 0)
582                 return atol(Buf->buf);
583         else
584                 return 0;
585 }
586
587 /**
588  * @ingroup StrBuf
589  * @brief Wrapper around atoi
590  */
591 int StrToi(const StrBuf *Buf)
592 {
593         if (Buf == NULL)
594                 return 0;
595         if (Buf->BufUsed > 0)
596                 return atoi(Buf->buf);
597         else
598                 return 0;
599 }
600
601 /**
602  * @ingroup StrBuf
603  * @brief Checks to see if the string is a pure number 
604  * @param Buf The buffer to inspect
605  * @returns 1 if its a pure number, 0, if not.
606  */
607 int StrBufIsNumber(const StrBuf *Buf) {
608         char * pEnd;
609         if ((Buf == NULL) || (Buf->BufUsed == 0)) {
610                 return 0;
611         }
612         strtoll(Buf->buf, &pEnd, 10);
613         if (pEnd == Buf->buf)
614                 return 0;
615         if ((pEnd != NULL) && (pEnd == Buf->buf + Buf->BufUsed))
616                 return 1;
617         if (Buf->buf == pEnd)
618                 return 0;
619         return 0;
620
621 /**
622  * @ingroup StrBuf
623  * @brief modifies a Single char of the Buf
624  * You can point to it via char* or a zero-based integer
625  * @param Buf The buffer to manipulate
626  * @param ptr char* to zero; use NULL if unused
627  * @param nThChar zero based pointer into the string; use -1 if unused
628  * @param PeekValue The Character to place into the position
629  */
630 long StrBufPeek(StrBuf *Buf, const char* ptr, long nThChar, char PeekValue)
631 {
632         if (Buf == NULL)
633                 return -1;
634         if (ptr != NULL)
635                 nThChar = ptr - Buf->buf;
636         if ((nThChar < 0) || (nThChar > Buf->BufUsed))
637                 return -1;
638         Buf->buf[nThChar] = PeekValue;
639         return nThChar;
640 }
641
642 /**
643  * @ingroup StrBuf
644  * @brief Append a StringBuffer to the buffer
645  * @param Buf Buffer to modify
646  * @param AppendBuf Buffer to copy at the end of our buffer
647  * @param Offset Should we start copying from an offset?
648  */
649 void StrBufAppendBuf(StrBuf *Buf, const StrBuf *AppendBuf, unsigned long Offset)
650 {
651         if ((AppendBuf == NULL) || (Buf == NULL) || (AppendBuf->buf == NULL))
652                 return;
653
654         if (Buf->BufSize - Offset < AppendBuf->BufUsed + Buf->BufUsed + 1)
655                 IncreaseBuf(Buf, 
656                             (Buf->BufUsed > 0), 
657                             AppendBuf->BufUsed + Buf->BufUsed);
658
659         memcpy(Buf->buf + Buf->BufUsed, 
660                AppendBuf->buf + Offset, 
661                AppendBuf->BufUsed - Offset);
662         Buf->BufUsed += AppendBuf->BufUsed - Offset;
663         Buf->buf[Buf->BufUsed] = '\0';
664 }
665
666
667 /**
668  * @ingroup StrBuf
669  * @brief Append a C-String to the buffer
670  * @param Buf Buffer to modify
671  * @param AppendBuf Buffer to copy at the end of our buffer
672  * @param AppendSize number of bytes to copy; set to -1 if we should count it in advance
673  * @param Offset Should we start copying from an offset?
674  */
675 void StrBufAppendBufPlain(StrBuf *Buf, const char *AppendBuf, long AppendSize, unsigned long Offset)
676 {
677         long aps;
678         long BufSizeRequired;
679
680         if ((AppendBuf == NULL) || (Buf == NULL))
681                 return;
682
683         if (AppendSize < 0 )
684                 aps = strlen(AppendBuf + Offset);
685         else
686                 aps = AppendSize - Offset;
687
688         BufSizeRequired = Buf->BufUsed + aps + 1;
689         if (Buf->BufSize <= BufSizeRequired)
690                 IncreaseBuf(Buf, (Buf->BufUsed > 0), BufSizeRequired);
691
692         memcpy(Buf->buf + Buf->BufUsed, 
693                AppendBuf + Offset, 
694                aps);
695         Buf->BufUsed += aps;
696         Buf->buf[Buf->BufUsed] = '\0';
697 }
698
699 /**
700  * @ingroup StrBuf
701  * @brief sprintf like function appending the formated string to the buffer
702  * vsnprintf version to wrap into own calls
703  * @param Buf Buffer to extend by format and Params
704  * @param format printf alike format to add
705  * @param ap va_list containing the items for format
706  */
707 void StrBufVAppendPrintf(StrBuf *Buf, const char *format, va_list ap)
708 {
709         va_list apl;
710         size_t BufSize;
711         size_t nWritten;
712         size_t Offset;
713         size_t newused;
714
715         if ((Buf == NULL)  || (format == NULL))
716                 return;
717
718         BufSize = Buf->BufSize;
719         nWritten = Buf->BufSize + 1;
720         Offset = Buf->BufUsed;
721         newused = Offset + nWritten;
722         
723         while (newused >= BufSize) {
724                 va_copy(apl, ap);
725                 nWritten = vsnprintf(Buf->buf + Offset, 
726                                      Buf->BufSize - Offset, 
727                                      format, apl);
728                 va_end(apl);
729                 newused = Offset + nWritten;
730                 if (newused >= Buf->BufSize) {
731                         IncreaseBuf(Buf, 1, newused);
732                         newused = Buf->BufSize + 1;
733                 }
734                 else {
735                         Buf->BufUsed = Offset + nWritten;
736                         BufSize = Buf->BufSize;
737                 }
738
739         }
740 }
741
742 /**
743  * @ingroup StrBuf
744  * @brief sprintf like function appending the formated string to the buffer
745  * @param Buf Buffer to extend by format and Params
746  * @param format printf alike format to add
747  */
748 void StrBufAppendPrintf(StrBuf *Buf, const char *format, ...)
749 {
750         size_t BufSize;
751         size_t nWritten;
752         size_t Offset;
753         size_t newused;
754         va_list arg_ptr;
755         
756         if ((Buf == NULL)  || (format == NULL))
757                 return;
758
759         BufSize = Buf->BufSize;
760         nWritten = Buf->BufSize + 1;
761         Offset = Buf->BufUsed;
762         newused = Offset + nWritten;
763
764         while (newused >= BufSize) {
765                 va_start(arg_ptr, format);
766                 nWritten = vsnprintf(Buf->buf + Buf->BufUsed, 
767                                      Buf->BufSize - Buf->BufUsed, 
768                                      format, arg_ptr);
769                 va_end(arg_ptr);
770                 newused = Buf->BufUsed + nWritten;
771                 if (newused >= Buf->BufSize) {
772                         IncreaseBuf(Buf, 1, newused);
773                         newused = Buf->BufSize + 1;
774                 }
775                 else {
776                         Buf->BufUsed += nWritten;
777                         BufSize = Buf->BufSize;
778                 }
779
780         }
781 }
782
783 /**
784  * @ingroup StrBuf
785  * @brief sprintf like function putting the formated string into the buffer
786  * @param Buf Buffer to extend by format and Parameters
787  * @param format printf alike format to add
788  */
789 void StrBufPrintf(StrBuf *Buf, const char *format, ...)
790 {
791         size_t nWritten;
792         va_list arg_ptr;
793         
794         if ((Buf == NULL)  || (format == NULL))
795                 return;
796
797         nWritten = Buf->BufSize + 1;
798         while (nWritten >= Buf->BufSize) {
799                 va_start(arg_ptr, format);
800                 nWritten = vsnprintf(Buf->buf, Buf->BufSize, format, arg_ptr);
801                 va_end(arg_ptr);
802                 if (nWritten >= Buf->BufSize) {
803                         IncreaseBuf(Buf, 0, 0);
804                         nWritten = Buf->BufSize + 1;
805                         continue;
806                 }
807                 Buf->BufUsed = nWritten ;
808         }
809 }
810
811 /**
812  * @ingroup StrBuf
813  * @brief Callback for cURL to append the webserver reply to a buffer
814  * @param ptr pre-defined by the cURL API; see man 3 curl for mre info
815  * @param size pre-defined by the cURL API; see man 3 curl for mre info
816  * @param nmemb pre-defined by the cURL API; see man 3 curl for mre info
817  * @param stream pre-defined by the cURL API; see man 3 curl for mre info
818  */
819 size_t CurlFillStrBuf_callback(void *ptr, size_t size, size_t nmemb, void *stream)
820 {
821
822         StrBuf *Target;
823
824         Target = stream;
825         if (ptr == NULL)
826                 return 0;
827
828         StrBufAppendBufPlain(Target, ptr, size * nmemb, 0);
829         return size * nmemb;
830 }
831
832
833 /**
834  * @ingroup StrBuf
835  * @brief extracts a substring from Source into dest
836  * @param dest buffer to place substring into
837  * @param Source string to copy substring from
838  * @param Offset chars to skip from start
839  * @param nChars number of chars to copy
840  * @returns the number of chars copied; may be different from nChars due to the size of Source
841  */
842 int StrBufSub(StrBuf *dest, const StrBuf *Source, unsigned long Offset, size_t nChars)
843 {
844         size_t NCharsRemain;
845         if (Offset > Source->BufUsed)
846         {
847                 FlushStrBuf(dest);
848                 return 0;
849         }
850         if (Offset + nChars < Source->BufUsed)
851         {
852                 if (nChars >= dest->BufSize)
853                         IncreaseBuf(dest, 0, nChars + 1);
854                 memcpy(dest->buf, Source->buf + Offset, nChars);
855                 dest->BufUsed = nChars;
856                 dest->buf[dest->BufUsed] = '\0';
857                 return nChars;
858         }
859         NCharsRemain = Source->BufUsed - Offset;
860         if (NCharsRemain  >= dest->BufSize)
861                 IncreaseBuf(dest, 0, NCharsRemain + 1);
862         memcpy(dest->buf, Source->buf + Offset, NCharsRemain);
863         dest->BufUsed = NCharsRemain;
864         dest->buf[dest->BufUsed] = '\0';
865         return NCharsRemain;
866 }
867
868 /**
869  * @ingroup StrBuf
870  * @brief Cut nChars from the start of the string
871  * @param Buf Buffer to modify
872  * @param nChars how many chars should be skipped?
873  */
874 void StrBufCutLeft(StrBuf *Buf, int nChars)
875 {
876         if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
877         if (nChars >= Buf->BufUsed) {
878                 FlushStrBuf(Buf);
879                 return;
880         }
881         memmove(Buf->buf, Buf->buf + nChars, Buf->BufUsed - nChars);
882         Buf->BufUsed -= nChars;
883         Buf->buf[Buf->BufUsed] = '\0';
884 }
885
886 /**
887  * @ingroup StrBuf
888  * @brief Cut the trailing n Chars from the string
889  * @param Buf Buffer to modify
890  * @param nChars how many chars should be trunkated?
891  */
892 void StrBufCutRight(StrBuf *Buf, int nChars)
893 {
894         if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
895         if (nChars >= Buf->BufUsed) {
896                 FlushStrBuf(Buf);
897                 return;
898         }
899         Buf->BufUsed -= nChars;
900         Buf->buf[Buf->BufUsed] = '\0';
901 }
902
903 /**
904  * @ingroup StrBuf
905  * @brief Cut the string after n Chars
906  * @param Buf Buffer to modify
907  * @param AfternChars after how many chars should we trunkate the string?
908  * @param At if non-null and points inside of our string, cut it there.
909  */
910 void StrBufCutAt(StrBuf *Buf, int AfternChars, const char *At)
911 {
912         if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
913         if (At != NULL){
914                 AfternChars = At - Buf->buf;
915         }
916
917         if ((AfternChars < 0) || (AfternChars >= Buf->BufUsed))
918                 return;
919         Buf->BufUsed = AfternChars;
920         Buf->buf[Buf->BufUsed] = '\0';
921 }
922
923
924 /**
925  * @ingroup StrBuf
926  * @brief Strip leading and trailing spaces from a string; with premeasured and adjusted length.
927  * @param Buf the string to modify
928  */
929 void StrBufTrim(StrBuf *Buf)
930 {
931         int delta = 0;
932         if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
933
934         while ((Buf->BufUsed > delta) && (isspace(Buf->buf[delta]))){
935                 delta ++;
936         }
937         if (delta > 0) StrBufCutLeft(Buf, delta);
938
939         if (Buf->BufUsed == 0) return;
940         while (isspace(Buf->buf[Buf->BufUsed - 1])){
941                 Buf->BufUsed --;
942         }
943         Buf->buf[Buf->BufUsed] = '\0';
944 }
945
946 /**
947  * @ingroup StrBuf
948  * @brief uppercase the contents of a buffer
949  * @param Buf the buffer to translate
950  */
951 void StrBufUpCase(StrBuf *Buf) 
952 {
953         char *pch, *pche;
954
955         if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
956
957         pch = Buf->buf;
958         pche = pch + Buf->BufUsed;
959         while (pch < pche) {
960                 *pch = toupper(*pch);
961                 pch ++;
962         }
963 }
964
965
966 /**
967  * @ingroup StrBuf
968  * @brief lowercase the contents of a buffer
969  * @param Buf the buffer to translate
970  */
971 void StrBufLowerCase(StrBuf *Buf) 
972 {
973         char *pch, *pche;
974
975         if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
976
977         pch = Buf->buf;
978         pche = pch + Buf->BufUsed;
979         while (pch < pche) {
980                 *pch = tolower(*pch);
981                 pch ++;
982         }
983 }
984
985
986 /*******************************************************************************
987  *           a tokenizer that kills, maims, and destroys                       *
988  *******************************************************************************/
989
990 /**
991  * @ingroup StrBuf_Tokenizer
992  * @brief Counts the numbmer of tokens in a buffer
993  * @param source String to count tokens in
994  * @param tok    Tokenizer char to count
995  * @returns numbers of tokenizer chars found
996  */
997 int StrBufNum_tokens(const StrBuf *source, char tok)
998 {
999         char *pch, *pche;
1000         long NTokens;
1001         if ((source == NULL) || (source->BufUsed == 0))
1002                 return 0;
1003         if ((source->BufUsed == 1) && (*source->buf == tok))
1004                 return 2;
1005         NTokens = 1;
1006         pch = source->buf;
1007         pche = pch + source->BufUsed;
1008         while (pch < pche)
1009         {
1010                 if (*pch == tok)
1011                         NTokens ++;
1012                 pch ++;
1013         }
1014         return NTokens;
1015 }
1016
1017 /**
1018  * @ingroup StrBuf_Tokenizer
1019  * @brief a string tokenizer
1020  * @param Source StringBuffer to read into
1021  * @param parmnum n'th Parameter to remove
1022  * @param separator tokenizer character
1023  * @returns -1 if not found, else length of token.
1024  */
1025 int StrBufRemove_token(StrBuf *Source, int parmnum, char separator)
1026 {
1027         int ReducedBy;
1028         char *d, *s, *end;              /* dest, source */
1029         int count = 0;
1030
1031         /* Find desired @parameter */
1032         end = Source->buf + Source->BufUsed;
1033         d = Source->buf;
1034         while ((d <= end) && 
1035                (count < parmnum))
1036         {
1037                 /* End of string, bail! */
1038                 if (!*d) {
1039                         d = NULL;
1040                         break;
1041                 }
1042                 if (*d == separator) {
1043                         count++;
1044                 }
1045                 d++;
1046         }
1047         if ((d == NULL) || (d >= end))
1048                 return 0;               /* @Parameter not found */
1049
1050         /* Find next @parameter */
1051         s = d;
1052         while ((s <= end) && 
1053                (*s && *s != separator))
1054         {
1055                 s++;
1056         }
1057         if (*s == separator)
1058                 s++;
1059         ReducedBy = d - s;
1060
1061         /* Hack and slash */
1062         if (s >= end) {
1063                 return 0;
1064         }
1065         else if (*s) {
1066                 memmove(d, s, Source->BufUsed - (s - Source->buf));
1067                 Source->BufUsed += ReducedBy;
1068                 Source->buf[Source->BufUsed] = '\0';
1069         }
1070         else if (d == Source->buf) {
1071                 *d = 0;
1072                 Source->BufUsed = 0;
1073         }
1074         else {
1075                 *--d = '\0';
1076                 Source->BufUsed += ReducedBy;
1077         }
1078         /*
1079         while (*s) {
1080                 *d++ = *s++;
1081         }
1082         *d = 0;
1083         */
1084         return ReducedBy;
1085 }
1086
1087
1088 /**
1089  * @ingroup StrBuf_Tokenizer
1090  * @brief a string tokenizer
1091  * @param dest Destination StringBuffer
1092  * @param Source StringBuffer to read into
1093  * @param parmnum n'th Parameter to extract
1094  * @param separator tokenizer character
1095  * @returns -1 if not found, else length of token.
1096  */
1097 int StrBufExtract_token(StrBuf *dest, const StrBuf *Source, int parmnum, char separator)
1098 {
1099         const char *s, *e;              //* source * /
1100         int len = 0;                    //* running total length of extracted string * /
1101         int current_token = 0;          //* token currently being processed * /
1102          
1103         if (dest != NULL) {
1104                 dest->buf[0] = '\0';
1105                 dest->BufUsed = 0;
1106         }
1107         else
1108                 return(-1);
1109
1110         if ((Source == NULL) || (Source->BufUsed ==0)) {
1111                 return(-1);
1112         }
1113         s = Source->buf;
1114         e = s + Source->BufUsed;
1115
1116         //cit_backtrace();
1117         //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
1118
1119         while ((s<e) && !IsEmptyStr(s)) {
1120                 if (*s == separator) {
1121                         ++current_token;
1122                 }
1123                 if (len >= dest->BufSize) {
1124                         dest->BufUsed = len;
1125                         if (IncreaseBuf(dest, 1, -1) < 0) {
1126                                 dest->BufUsed --;
1127                                 break;
1128                         }
1129                 }
1130                 if ( (current_token == parmnum) && 
1131                      (*s != separator)) {
1132                         dest->buf[len] = *s;
1133                         ++len;
1134                 }
1135                 else if (current_token > parmnum) {
1136                         break;
1137                 }
1138                 ++s;
1139         }
1140         
1141         dest->buf[len] = '\0';
1142         dest->BufUsed = len;
1143                 
1144         if (current_token < parmnum) {
1145                 //lprintf (CTDL_DEBUG,"test <!: %s\n", dest);
1146                 return(-1);
1147         }
1148         //lprintf (CTDL_DEBUG,"test <: %d; %s\n", len, dest);
1149         return(len);
1150 }
1151
1152
1153
1154
1155
1156 /**
1157  * @ingroup StrBuf_Tokenizer
1158  * @brief a string tokenizer to fetch an integer
1159  * @param Source String containing tokens
1160  * @param parmnum n'th Parameter to extract
1161  * @param separator tokenizer character
1162  * @returns 0 if not found, else integer representation of the token
1163  */
1164 int StrBufExtract_int(const StrBuf* Source, int parmnum, char separator)
1165 {
1166         StrBuf tmp;
1167         char buf[64];
1168         
1169         tmp.buf = buf;
1170         buf[0] = '\0';
1171         tmp.BufSize = 64;
1172         tmp.BufUsed = 0;
1173         tmp.ConstBuf = 1;
1174         if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
1175                 return(atoi(buf));
1176         else
1177                 return 0;
1178 }
1179
1180 /**
1181  * @ingroup StrBuf_Tokenizer
1182  * @brief a string tokenizer to fetch a long integer
1183  * @param Source String containing tokens
1184  * @param parmnum n'th Parameter to extract
1185  * @param separator tokenizer character
1186  * @returns 0 if not found, else long integer representation of the token
1187  */
1188 long StrBufExtract_long(const StrBuf* Source, int parmnum, char separator)
1189 {
1190         StrBuf tmp;
1191         char buf[64];
1192         
1193         tmp.buf = buf;
1194         buf[0] = '\0';
1195         tmp.BufSize = 64;
1196         tmp.BufUsed = 0;
1197         tmp.ConstBuf = 1;
1198         if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
1199                 return(atoi(buf));
1200         else
1201                 return 0;
1202 }
1203
1204
1205 /**
1206  * @ingroup StrBuf_Tokenizer
1207  * @brief a string tokenizer to fetch an unsigned long
1208  * @param Source String containing tokens
1209  * @param parmnum n'th Parameter to extract
1210  * @param separator tokenizer character
1211  * @returns 0 if not found, else unsigned long representation of the token
1212  */
1213 unsigned long StrBufExtract_unsigned_long(const StrBuf* Source, int parmnum, char separator)
1214 {
1215         StrBuf tmp;
1216         char buf[64];
1217         char *pnum;
1218         
1219         tmp.buf = buf;
1220         buf[0] = '\0';
1221         tmp.BufSize = 64;
1222         tmp.BufUsed = 0;
1223         tmp.ConstBuf = 1;
1224         if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0) {
1225                 pnum = &buf[0];
1226                 if (*pnum == '-')
1227                         pnum ++;
1228                 return (unsigned long) atol(pnum);
1229         }
1230         else 
1231                 return 0;
1232 }
1233
1234
1235
1236 /**
1237  * @ingroup StrBuf_NextTokenizer
1238  * @brief a string tokenizer; Bounds checker
1239  *  function to make shure whether StrBufExtract_NextToken and friends have reached the end of the string.
1240  * @param Source our tokenbuffer
1241  * @param pStart the token iterator pointer to inspect
1242  * @returns whether the revolving pointer is inside of the search range
1243  */
1244 int StrBufHaveNextToken(const StrBuf *Source, const char **pStart)
1245 {
1246         if ((Source == NULL) || 
1247             (*pStart == StrBufNOTNULL) ||
1248             (Source->BufUsed == 0))
1249         {
1250                 return 0;
1251         }
1252         if (*pStart == NULL)
1253         {
1254                 return 1;
1255         }
1256         else if (*pStart > Source->buf + Source->BufUsed)
1257         {
1258                 return 0;
1259         }
1260         else if (*pStart <= Source->buf)
1261         {
1262                 return 0;
1263         }
1264
1265         return 1;
1266 }
1267
1268 /**
1269  * @ingroup StrBuf_NextTokenizer
1270  * @brief a string tokenizer
1271  * @param dest Destination StringBuffer
1272  * @param Source StringBuffer to read into
1273  * @param pStart pointer to the end of the last token. Feed with NULL on start.
1274  * @param separator tokenizer 
1275  * @returns -1 if not found, else length of token.
1276  */
1277 int StrBufExtract_NextToken(StrBuf *dest, const StrBuf *Source, const char **pStart, char separator)
1278 {
1279         const char *s;          /* source */
1280         const char *EndBuffer;  /* end stop of source buffer */
1281         int current_token = 0;  /* token currently being processed */
1282         int len = 0;            /* running total length of extracted string */
1283
1284         if ((Source          == NULL) || 
1285             (Source->BufUsed == 0)      ) 
1286         {
1287                 *pStart = StrBufNOTNULL;
1288                 return -1;
1289         }
1290          
1291         EndBuffer = Source->buf + Source->BufUsed;
1292
1293         if (dest != NULL) 
1294         {
1295                 dest->buf[0] = '\0';
1296                 dest->BufUsed = 0;
1297         }
1298         else
1299         {
1300                 *pStart = EndBuffer + 1;
1301                 return -1;
1302         }
1303
1304         if (*pStart == NULL)
1305         {
1306                 *pStart = Source->buf; /* we're starting to examine this buffer. */
1307         }
1308         else if ((*pStart < Source->buf) || 
1309                  (*pStart > EndBuffer  )   ) 
1310         {
1311                 return -1; /* no more tokens to find. */
1312         }
1313
1314         s = *pStart;
1315         /* start to find the next token */
1316         while ((s <= EndBuffer)      && 
1317                (current_token == 0) ) 
1318         {
1319                 if (*s == separator) 
1320                 {
1321                         /* we found the next token */
1322                         ++current_token;
1323                 }
1324
1325                 if (len >= dest->BufSize) 
1326                 {
1327                         /* our Dest-buffer isn't big enough, increase it. */
1328                         dest->BufUsed = len;
1329
1330                         if (IncreaseBuf(dest, 1, -1) < 0) {
1331                                 /* WHUT? no more mem? bail out. */
1332                                 s = EndBuffer;
1333                                 dest->BufUsed --;
1334                                 break;
1335                         }
1336                 }
1337
1338                 if ( (current_token == 0 ) &&   /* are we in our target token? */
1339                      (!IsEmptyStr(s)     ) &&
1340                      (separator     != *s)    ) /* don't copy the token itself */
1341                 {
1342                         dest->buf[len] = *s;    /* Copy the payload */
1343                         ++len;                  /* remember the bigger size. */
1344                 }
1345
1346                 ++s;
1347         }
1348
1349         /* did we reach the end? */
1350         if ((s > EndBuffer)) {
1351                 EndBuffer = StrBufNOTNULL;
1352                 *pStart = EndBuffer;
1353         }
1354         else {
1355                 *pStart = s;  /* remember the position for the next run */
1356         }
1357
1358         /* sanitize our extracted token */
1359         dest->buf[len] = '\0';
1360         dest->BufUsed  = len;
1361
1362         return (len);
1363 }
1364
1365
1366 /**
1367  * @ingroup StrBuf_NextTokenizer
1368  * @brief a string tokenizer
1369  * @param Source StringBuffer to read from
1370  * @param pStart pointer to the end of the last token. Feed with NULL.
1371  * @param separator tokenizer character
1372  * @param nTokens number of tokens to fastforward over
1373  * @returns -1 if not found, else length of token.
1374  */
1375 int StrBufSkip_NTokenS(const StrBuf *Source, const char **pStart, char separator, int nTokens)
1376 {
1377         const char *s, *EndBuffer;      //* source * /
1378         int len = 0;                    //* running total length of extracted string * /
1379         int current_token = 0;          //* token currently being processed * /
1380
1381         if ((Source == NULL) || 
1382             (Source->BufUsed ==0)) {
1383                 return(-1);
1384         }
1385         if (nTokens == 0)
1386                 return Source->BufUsed;
1387
1388         if (*pStart == NULL)
1389                 *pStart = Source->buf;
1390
1391         EndBuffer = Source->buf + Source->BufUsed;
1392
1393         if ((*pStart < Source->buf) || 
1394             (*pStart >  EndBuffer)) {
1395                 return (-1);
1396         }
1397
1398
1399         s = *pStart;
1400
1401         //cit_backtrace();
1402         //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
1403
1404         while ((s<EndBuffer) && !IsEmptyStr(s)) {
1405                 if (*s == separator) {
1406                         ++current_token;
1407                 }
1408                 if (current_token >= nTokens) {
1409                         break;
1410                 }
1411                 ++s;
1412         }
1413         *pStart = s;
1414         (*pStart) ++;
1415
1416         return(len);
1417 }
1418
1419 /**
1420  * @ingroup StrBuf_NextTokenizer
1421  * @brief a string tokenizer to fetch an integer
1422  * @param Source StringBuffer to read from
1423  * @param pStart Cursor on the tokenstring
1424  * @param separator tokenizer character
1425  * @returns 0 if not found, else integer representation of the token
1426  */
1427 int StrBufExtractNext_int(const StrBuf* Source, const char **pStart, char separator)
1428 {
1429         StrBuf tmp;
1430         char buf[64];
1431         
1432         tmp.buf = buf;
1433         buf[0] = '\0';
1434         tmp.BufSize = 64;
1435         tmp.BufUsed = 0;
1436         tmp.ConstBuf = 1;
1437         if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0)
1438                 return(atoi(buf));
1439         else
1440                 return 0;
1441 }
1442
1443 /**
1444  * @ingroup StrBuf_NextTokenizer
1445  * @brief a string tokenizer to fetch a long integer
1446  * @param Source StringBuffer to read from
1447  * @param pStart Cursor on the tokenstring
1448  * @param separator tokenizer character
1449  * @returns 0 if not found, else long integer representation of the token
1450  */
1451 long StrBufExtractNext_long(const StrBuf* Source, const char **pStart, char separator)
1452 {
1453         StrBuf tmp;
1454         char buf[64];
1455         
1456         tmp.buf = buf;
1457         buf[0] = '\0';
1458         tmp.BufSize = 64;
1459         tmp.BufUsed = 0;
1460         tmp.ConstBuf = 1;
1461         if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0)
1462                 return(atoi(buf));
1463         else
1464                 return 0;
1465 }
1466
1467
1468 /**
1469  * @ingroup StrBuf_NextTokenizer
1470  * @brief a string tokenizer to fetch an unsigned long
1471  * @param Source StringBuffer to read from
1472  * @param pStart Cursor on the tokenstring
1473  * @param separator tokenizer character
1474  * @returns 0 if not found, else unsigned long representation of the token
1475  */
1476 unsigned long StrBufExtractNext_unsigned_long(const StrBuf* Source, const char **pStart, char separator)
1477 {
1478         StrBuf tmp;
1479         char buf[64];
1480         char *pnum;
1481         
1482         tmp.buf = buf;
1483         buf[0] = '\0';
1484         tmp.BufSize = 64;
1485         tmp.BufUsed = 0;
1486         tmp.ConstBuf = 1;
1487         if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0) {
1488                 pnum = &buf[0];
1489                 if (*pnum == '-')
1490                         pnum ++;
1491                 return (unsigned long) atol(pnum);
1492         }
1493         else 
1494                 return 0;
1495 }
1496
1497
1498
1499
1500
1501 /*******************************************************************************
1502  *                             Escape Appending                                *
1503  *******************************************************************************/
1504
1505 /** 
1506  * @ingroup StrBuf_DeEnCoder
1507  * @brief Escape a string for feeding out as a URL while appending it to a Buffer
1508  * @param OutBuf the output buffer
1509  * @param In Buffer to encode
1510  * @param PlainIn way in from plain old c strings
1511  */
1512 void StrBufUrlescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
1513 {
1514         const char *pch, *pche;
1515         char *pt, *pte;
1516         int len;
1517         
1518         if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1519                 return;
1520         if (PlainIn != NULL) {
1521                 len = strlen(PlainIn);
1522                 pch = PlainIn;
1523                 pche = pch + len;
1524         }
1525         else {
1526                 pch = In->buf;
1527                 pche = pch + In->BufUsed;
1528                 len = In->BufUsed;
1529         }
1530
1531         if (len == 0) 
1532                 return;
1533
1534         pt = OutBuf->buf + OutBuf->BufUsed;
1535         pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1536
1537         while (pch < pche) {
1538                 if (pt >= pte) {
1539                         IncreaseBuf(OutBuf, 1, -1);
1540                         pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1541                         pt = OutBuf->buf + OutBuf->BufUsed;
1542                 }
1543
1544                 if((*pch >= 'a' && *pch <= 'z') ||
1545                    (*pch >= '@' && *pch <= 'Z') || /* @ A-Z */
1546                    (*pch >= '0' && *pch <= ':') || /* 0-9 : */
1547                    (*pch == '!') || (*pch == '_') || 
1548                    (*pch == ',') || (*pch == '.') || 
1549                    (*pch == ','))
1550                 {
1551                         *(pt++) = *(pch++);
1552                         OutBuf->BufUsed++;
1553                 }                       
1554                 else {
1555                         *pt = '%';
1556                         *(pt + 1) = HexList[(unsigned char)*pch][0];
1557                         *(pt + 2) = HexList[(unsigned char)*pch][1];
1558                         pt += 3;
1559                         OutBuf->BufUsed += 3;
1560                         pch ++;
1561                 }
1562         }
1563         *pt = '\0';
1564 }
1565
1566 /**
1567  * @ingroup StrBuf_DeEnCoder
1568  * @brief Append a string, escaping characters which have meaning in HTML.  
1569  *
1570  * @param Target        target buffer
1571  * @param Source        source buffer; set to NULL if you just have a C-String
1572  * @param PlainIn       Plain-C string to append; set to NULL if unused
1573  * @param nbsp          If nonzero, spaces are converted to non-breaking spaces.
1574  * @param nolinebreaks  if set to 1, linebreaks are removed from the string.
1575  *                      if set to 2, linebreaks are replaced by &ltbr/&gt
1576  */
1577 long StrEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
1578 {
1579         const char *aptr, *eiptr;
1580         char *bptr, *eptr;
1581         long len;
1582
1583         if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
1584                 return -1;
1585
1586         if (PlainIn != NULL) {
1587                 aptr = PlainIn;
1588                 len = strlen(PlainIn);
1589                 eiptr = aptr + len;
1590         }
1591         else {
1592                 aptr = Source->buf;
1593                 eiptr = aptr + Source->BufUsed;
1594                 len = Source->BufUsed;
1595         }
1596
1597         if (len == 0) 
1598                 return -1;
1599
1600         bptr = Target->buf + Target->BufUsed;
1601         eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in...  */
1602
1603         while (aptr < eiptr){
1604                 if(bptr >= eptr) {
1605                         IncreaseBuf(Target, 1, -1);
1606                         eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in...  */
1607                         bptr = Target->buf + Target->BufUsed;
1608                 }
1609                 if (*aptr == '<') {
1610                         memcpy(bptr, "&lt;", 4);
1611                         bptr += 4;
1612                         Target->BufUsed += 4;
1613                 }
1614                 else if (*aptr == '>') {
1615                         memcpy(bptr, "&gt;", 4);
1616                         bptr += 4;
1617                         Target->BufUsed += 4;
1618                 }
1619                 else if (*aptr == '&') {
1620                         memcpy(bptr, "&amp;", 5);
1621                         bptr += 5;
1622                         Target->BufUsed += 5;
1623                 }
1624                 else if (*aptr == '"') {
1625                         memcpy(bptr, "&quot;", 6);
1626                         bptr += 6;
1627                         Target->BufUsed += 6;
1628                 }
1629                 else if (*aptr == '\'') {
1630                         memcpy(bptr, "&#39;", 5);
1631                         bptr += 5;
1632                         Target->BufUsed += 5;
1633                 }
1634                 else if (*aptr == LB) {
1635                         *bptr = '<';
1636                         bptr ++;
1637                         Target->BufUsed ++;
1638                 }
1639                 else if (*aptr == RB) {
1640                         *bptr = '>';
1641                         bptr ++;
1642                         Target->BufUsed ++;
1643                 }
1644                 else if (*aptr == QU) {
1645                         *bptr ='"';
1646                         bptr ++;
1647                         Target->BufUsed ++;
1648                 }
1649                 else if ((*aptr == 32) && (nbsp == 1)) {
1650                         memcpy(bptr, "&nbsp;", 6);
1651                         bptr += 6;
1652                         Target->BufUsed += 6;
1653                 }
1654                 else if ((*aptr == '\n') && (nolinebreaks == 1)) {
1655                         *bptr='\0';     /* nothing */
1656                 }
1657                 else if ((*aptr == '\n') && (nolinebreaks == 2)) {
1658                         memcpy(bptr, "&lt;br/&gt;", 11);
1659                         bptr += 11;
1660                         Target->BufUsed += 11;
1661                 }
1662
1663
1664                 else if ((*aptr == '\r') && (nolinebreaks != 0)) {
1665                         *bptr='\0';     /* nothing */
1666                 }
1667                 else{
1668                         *bptr = *aptr;
1669                         bptr++;
1670                         Target->BufUsed ++;
1671                 }
1672                 aptr ++;
1673         }
1674         *bptr = '\0';
1675         if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
1676                 return -1;
1677         return Target->BufUsed;
1678 }
1679
1680 /**
1681  * @ingroup StrBuf_DeEnCoder
1682  * @brief Append a string, escaping characters which have meaning in HTML.  
1683  * Converts linebreaks into blanks; escapes single quotes
1684  * @param Target        target buffer
1685  * @param Source        source buffer; set to NULL if you just have a C-String
1686  * @param PlainIn       Plain-C string to append; set to NULL if unused
1687  */
1688 void StrMsgEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
1689 {
1690         const char *aptr, *eiptr;
1691         char *tptr, *eptr;
1692         long len;
1693
1694         if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
1695                 return ;
1696
1697         if (PlainIn != NULL) {
1698                 aptr = PlainIn;
1699                 len = strlen(PlainIn);
1700                 eiptr = aptr + len;
1701         }
1702         else {
1703                 aptr = Source->buf;
1704                 eiptr = aptr + Source->BufUsed;
1705                 len = Source->BufUsed;
1706         }
1707
1708         if (len == 0) 
1709                 return;
1710
1711         eptr = Target->buf + Target->BufSize - 8; 
1712         tptr = Target->buf + Target->BufUsed;
1713         
1714         while (aptr < eiptr){
1715                 if(tptr >= eptr) {
1716                         IncreaseBuf(Target, 1, -1);
1717                         eptr = Target->buf + Target->BufSize - 8; 
1718                         tptr = Target->buf + Target->BufUsed;
1719                 }
1720                
1721                 if (*aptr == '\n') {
1722                         *tptr = ' ';
1723                         Target->BufUsed++;
1724                 }
1725                 else if (*aptr == '\r') {
1726                         *tptr = ' ';
1727                         Target->BufUsed++;
1728                 }
1729                 else if (*aptr == '\'') {
1730                         *(tptr++) = '&';
1731                         *(tptr++) = '#';
1732                         *(tptr++) = '3';
1733                         *(tptr++) = '9';
1734                         *tptr = ';';
1735                         Target->BufUsed += 5;
1736                 } else {
1737                         *tptr = *aptr;
1738                         Target->BufUsed++;
1739                 }
1740                 tptr++; aptr++;
1741         }
1742         *tptr = '\0';
1743 }
1744
1745
1746
1747 /**
1748  * @ingroup StrBuf_DeEnCoder
1749  * @brief Append a string, escaping characters which have meaning in ICAL.  
1750  * [\n,] 
1751  * @param Target        target buffer
1752  * @param Source        source buffer; set to NULL if you just have a C-String
1753  * @param PlainIn       Plain-C string to append; set to NULL if unused
1754  */
1755 void StrIcalEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
1756 {
1757         const char *aptr, *eiptr;
1758         char *tptr, *eptr;
1759         long len;
1760
1761         if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
1762                 return ;
1763
1764         if (PlainIn != NULL) {
1765                 aptr = PlainIn;
1766                 len = strlen(PlainIn);
1767                 eiptr = aptr + len;
1768         }
1769         else {
1770                 aptr = Source->buf;
1771                 eiptr = aptr + Source->BufUsed;
1772                 len = Source->BufUsed;
1773         }
1774
1775         if (len == 0) 
1776                 return;
1777
1778         eptr = Target->buf + Target->BufSize - 8; 
1779         tptr = Target->buf + Target->BufUsed;
1780         
1781         while (aptr < eiptr){
1782                 if(tptr + 3 >= eptr) {
1783                         IncreaseBuf(Target, 1, -1);
1784                         eptr = Target->buf + Target->BufSize - 8; 
1785                         tptr = Target->buf + Target->BufUsed;
1786                 }
1787                
1788                 if (*aptr == '\n') {
1789                         *tptr = '\\';
1790                         Target->BufUsed++;
1791                         tptr++;
1792                         *tptr = 'n';
1793                         Target->BufUsed++;
1794                 }
1795                 else if (*aptr == '\r') {
1796                         *tptr = '\\';
1797                         Target->BufUsed++;
1798                         tptr++;
1799                         *tptr = 'r';
1800                         Target->BufUsed++;
1801                 }
1802                 else if (*aptr == ',') {
1803                         *tptr = '\\';
1804                         Target->BufUsed++;
1805                         tptr++;
1806                         *tptr = ',';
1807                         Target->BufUsed++;
1808                 } else {
1809                         *tptr = *aptr;
1810                         Target->BufUsed++;
1811                 }
1812                 tptr++; aptr++;
1813         }
1814         *tptr = '\0';
1815 }
1816
1817 /**
1818  * @ingroup StrBuf_DeEnCoder
1819  * @brief Append a string, escaping characters which have meaning in JavaScript strings .  
1820  *
1821  * @param Target        target buffer
1822  * @param Source        source buffer; set to NULL if you just have a C-String
1823  * @param PlainIn       Plain-C string to append; set to NULL if unused
1824  * @returns size of result or -1
1825  */
1826 long StrECMAEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
1827 {
1828         const char *aptr, *eiptr;
1829         char *bptr, *eptr;
1830         long len;
1831
1832         if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
1833                 return -1;
1834
1835         if (PlainIn != NULL) {
1836                 aptr = PlainIn;
1837                 len = strlen(PlainIn);
1838                 eiptr = aptr + len;
1839         }
1840         else {
1841                 aptr = Source->buf;
1842                 eiptr = aptr + Source->BufUsed;
1843                 len = Source->BufUsed;
1844         }
1845
1846         if (len == 0) 
1847                 return -1;
1848
1849         bptr = Target->buf + Target->BufUsed;
1850         eptr = Target->buf + Target->BufSize - 3; /* our biggest unit to put in...  */
1851
1852         while (aptr < eiptr){
1853                 if(bptr >= eptr) {
1854                         IncreaseBuf(Target, 1, -1);
1855                         eptr = Target->buf + Target->BufSize - 3; 
1856                         bptr = Target->buf + Target->BufUsed;
1857                 }
1858                 if (*aptr == '"') {
1859                         *bptr = '\\';
1860                         bptr ++;
1861                         *bptr = '"';
1862                         bptr ++;
1863                         Target->BufUsed += 2;
1864                 } else if (*aptr == '\\') {
1865                         *bptr = '\\';
1866                         bptr ++;
1867                         *bptr = '\\';
1868                         bptr ++;
1869                         Target->BufUsed += 2;
1870                 }
1871                 else{
1872                         *bptr = *aptr;
1873                         bptr++;
1874                         Target->BufUsed ++;
1875                 }
1876                 aptr ++;
1877         }
1878         *bptr = '\0';
1879         if ((bptr == eptr - 1 ) && !IsEmptyStr(aptr) )
1880                 return -1;
1881         return Target->BufUsed;
1882 }
1883
1884 /**
1885  * @ingroup StrBuf_DeEnCoder
1886  * @brief Append a string, escaping characters which have meaning in HTML + json.  
1887  *
1888  * @param Target        target buffer
1889  * @param Source        source buffer; set to NULL if you just have a C-String
1890  * @param PlainIn       Plain-C string to append; set to NULL if unused
1891  * @param nbsp          If nonzero, spaces are converted to non-breaking spaces.
1892  * @param nolinebreaks  if set to 1, linebreaks are removed from the string.
1893  *                      if set to 2, linebreaks are replaced by &ltbr/&gt
1894  */
1895 long StrHtmlEcmaEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
1896 {
1897         const char *aptr, *eiptr;
1898         char *bptr, *eptr;
1899         long len;
1900         int IsUtf8Sequence = 0;
1901
1902         if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
1903                 return -1;
1904
1905         if (PlainIn != NULL) {
1906                 aptr = PlainIn;
1907                 len = strlen(PlainIn);
1908                 eiptr = aptr + len;
1909         }
1910         else {
1911                 aptr = Source->buf;
1912                 eiptr = aptr + Source->BufUsed;
1913                 len = Source->BufUsed;
1914         }
1915
1916         if (len == 0) 
1917                 return -1;
1918
1919         bptr = Target->buf + Target->BufUsed;
1920         eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in...  */
1921
1922         while (aptr < eiptr){
1923                 if(bptr >= eptr) {
1924                         IncreaseBuf(Target, 1, -1);
1925                         eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in...  */
1926                         bptr = Target->buf + Target->BufUsed;
1927                 }
1928                 if (*aptr == '<') {
1929                         memcpy(bptr, "&lt;", 4);
1930                         bptr += 4;
1931                         Target->BufUsed += 4;
1932                 }
1933                 else if (*aptr == '>') {
1934                         memcpy(bptr, "&gt;", 4);
1935                         bptr += 4;
1936                         Target->BufUsed += 4;
1937                 }
1938                 else if (*aptr == '&') {
1939                         memcpy(bptr, "&amp;", 5);
1940                         bptr += 5;
1941                         Target->BufUsed += 5;
1942                 }
1943                 else if (*aptr == LB) {
1944                         *bptr = '<';
1945                         bptr ++;
1946                         Target->BufUsed ++;
1947                 }
1948                 else if (*aptr == RB) {
1949                         *bptr = '>';
1950                         bptr ++;
1951                         Target->BufUsed ++;
1952                 }
1953                 else if ((*aptr == 32) && (nbsp == 1)) {
1954                         memcpy(bptr, "&nbsp;", 6);
1955                         bptr += 6;
1956                         Target->BufUsed += 6;
1957                 }
1958                 else if ((*aptr == '\n') && (nolinebreaks == 1)) {
1959                         *bptr='\0';     /* nothing */
1960                 }
1961                 else if ((*aptr == '\n') && (nolinebreaks == 2)) {
1962                         memcpy(bptr, "&lt;br/&gt;", 11);
1963                         bptr += 11;
1964                         Target->BufUsed += 11;
1965                 }
1966
1967                 else if ((*aptr == '\r') && (nolinebreaks != 0)) {
1968                         *bptr='\0';     /* nothing */
1969                 }
1970
1971                 else if ((*aptr == '"') || (*aptr == QU)) {
1972                         *bptr = '\\';
1973                         bptr ++;
1974                         *bptr = '"';
1975                         bptr ++;
1976                         Target->BufUsed += 2;
1977                 } else if (*aptr == '\\') {
1978                         *bptr = '\\';
1979                         bptr ++;
1980                         *bptr = '\\';
1981                         bptr ++;
1982                         Target->BufUsed += 2;
1983                 }
1984                 else {
1985                         if (((unsigned char)*aptr) >= 0x20)
1986                         {
1987                                 IsUtf8Sequence =  Ctdl_GetUtf8SequenceLength(aptr, eiptr);
1988                                 
1989                                 *bptr = *aptr;
1990                                 Target->BufUsed ++;
1991                                 while (IsUtf8Sequence > 1){
1992                                         if(bptr + IsUtf8Sequence >= eptr) {
1993                                                 IncreaseBuf(Target, 1, -1);
1994                                                 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in...  */
1995                                                 bptr = Target->buf + Target->BufUsed - 1;
1996                                         }
1997                                         bptr++; aptr++;
1998                                         IsUtf8Sequence --;
1999                                         *bptr = *aptr;
2000                                         Target->BufUsed ++;
2001                                 }
2002                                 bptr++;
2003                         }
2004
2005                 }
2006                 aptr ++;
2007         }
2008         *bptr = '\0';
2009         if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
2010                 return -1;
2011         return Target->BufUsed;
2012 }
2013
2014 /**
2015  * @ingroup StrBuf_DeEnCoder
2016  * @brief unhide special chars hidden to the HTML escaper
2017  * @param target buffer to put the unescaped string in
2018  * @param source buffer to unescape
2019  */
2020 void StrBufEUid_unescapize(StrBuf *target, const StrBuf *source) 
2021 {
2022         int a, b, len;
2023         char hex[3];
2024
2025         if (target != NULL)
2026                 FlushStrBuf(target);
2027
2028         if (source == NULL ||target == NULL)
2029         {
2030                 return;
2031         }
2032
2033         len = source->BufUsed;
2034         for (a = 0; a < len; ++a) {
2035                 if (target->BufUsed >= target->BufSize)
2036                         IncreaseBuf(target, 1, -1);
2037
2038                 if (source->buf[a] == '=') {
2039                         hex[0] = source->buf[a + 1];
2040                         hex[1] = source->buf[a + 2];
2041                         hex[2] = 0;
2042                         b = 0;
2043                         sscanf(hex, "%02x", &b);
2044                         target->buf[target->BufUsed] = b;
2045                         target->buf[++target->BufUsed] = 0;
2046                         a += 2;
2047                 }
2048                 else {
2049                         target->buf[target->BufUsed] = source->buf[a];
2050                         target->buf[++target->BufUsed] = 0;
2051                 }
2052         }
2053 }
2054
2055
2056 /**
2057  * @ingroup StrBuf_DeEnCoder
2058  * @brief hide special chars from the HTML escapers and friends
2059  * @param target buffer to put the escaped string in
2060  * @param source buffer to escape
2061  */
2062 void StrBufEUid_escapize(StrBuf *target, const StrBuf *source) 
2063 {
2064         int i, len;
2065
2066         if (target != NULL)
2067                 FlushStrBuf(target);
2068
2069         if (source == NULL ||target == NULL)
2070         {
2071                 return;
2072         }
2073
2074         len = source->BufUsed;
2075         for (i=0; i<len; ++i) {
2076                 if (target->BufUsed + 4 >= target->BufSize)
2077                         IncreaseBuf(target, 1, -1);
2078                 if ( (isalnum(source->buf[i])) || 
2079                      (source->buf[i]=='-') || 
2080                      (source->buf[i]=='_') ) {
2081                         target->buf[target->BufUsed++] = source->buf[i];
2082                 }
2083                 else {
2084                         sprintf(&target->buf[target->BufUsed], 
2085                                 "=%02X", 
2086                                 (0xFF &source->buf[i]));
2087                         target->BufUsed += 3;
2088                 }
2089         }
2090         target->buf[target->BufUsed + 1] = '\0';
2091 }
2092
2093
2094 /*******************************************************************************
2095  *                      Quoted Printable de/encoding                           *
2096  *******************************************************************************/
2097
2098 /**
2099  * @ingroup StrBuf_DeEnCoder
2100  * @brief decode a buffer from base 64 encoding; destroys original
2101  * @param Buf Buffor to transform
2102  */
2103 int StrBufDecodeBase64(StrBuf *Buf)
2104 {
2105         char *xferbuf;
2106         size_t siz;
2107         if (Buf == NULL) return -1;
2108
2109         xferbuf = (char*) malloc(Buf->BufSize);
2110         siz = CtdlDecodeBase64(xferbuf,
2111                                Buf->buf,
2112                                Buf->BufUsed);
2113         free(Buf->buf);
2114         Buf->buf = xferbuf;
2115         Buf->BufUsed = siz;
2116         return siz;
2117 }
2118
2119 /**
2120  * @ingroup StrBuf_DeEnCoder
2121  * @brief decode a buffer from base 64 encoding; destroys original
2122  * @param Buf Buffor to transform
2123  */
2124 int StrBufDecodeHex(StrBuf *Buf)
2125 {
2126         unsigned int ch;
2127         char *pch, *pche, *pchi;
2128
2129         if (Buf == NULL) return -1;
2130
2131         pch = pchi = Buf->buf;
2132         pche = pch + Buf->BufUsed;
2133
2134         while (pchi < pche){
2135                 ch = decode_hex(pchi);
2136                 *pch = ch;
2137                 pch ++;
2138                 pchi += 2;
2139         }
2140
2141         *pch = '\0';
2142         Buf->BufUsed = pch - Buf->buf;
2143         return Buf->BufUsed;
2144 }
2145
2146 /**
2147  * @ingroup StrBuf_DeEnCoder
2148  * @brief replace all chars >0x20 && < 0x7F with Mute
2149  * @param Mute char to put over invalid chars
2150  * @param Buf Buffor to transform
2151  */
2152 int StrBufSanitizeAscii(StrBuf *Buf, const char Mute)
2153 {
2154         unsigned char *pch;
2155
2156         if (Buf == NULL) return -1;
2157         pch = (unsigned char *)Buf->buf;
2158         while (pch < (unsigned char *)Buf->buf + Buf->BufUsed) {
2159                 if ((*pch < 0x20) || (*pch > 0x7F))
2160                         *pch = Mute;
2161                 pch ++;
2162         }
2163         return Buf->BufUsed;
2164 }
2165
2166
2167 /**
2168  * @ingroup StrBuf_DeEnCoder
2169  * @brief remove escaped strings from i.e. the url string (like %20 for blanks)
2170  * @param Buf Buffer to translate
2171  * @param StripBlanks Reduce several blanks to one?
2172  */
2173 long StrBufUnescape(StrBuf *Buf, int StripBlanks)
2174 {
2175         int a, b;
2176         char hex[3];
2177         long len;
2178
2179         if (Buf == NULL)
2180                 return -1;
2181
2182         while ((Buf->BufUsed > 0) && (isspace(Buf->buf[Buf->BufUsed - 1]))){
2183                 Buf->buf[Buf->BufUsed - 1] = '\0';
2184                 Buf->BufUsed --;
2185         }
2186
2187         a = 0; 
2188         while (a < Buf->BufUsed) {
2189                 if (Buf->buf[a] == '+')
2190                         Buf->buf[a] = ' ';
2191                 else if (Buf->buf[a] == '%') {
2192                         /* don't let % chars through, rather truncate the input. */
2193                         if (a + 2 > Buf->BufUsed) {
2194                                 Buf->buf[a] = '\0';
2195                                 Buf->BufUsed = a;
2196                         }
2197                         else {                  
2198                                 hex[0] = Buf->buf[a + 1];
2199                                 hex[1] = Buf->buf[a + 2];
2200                                 hex[2] = 0;
2201                                 b = 0;
2202                                 sscanf(hex, "%02x", &b);
2203                                 Buf->buf[a] = (char) b;
2204                                 len = Buf->BufUsed - a - 2;
2205                                 if (len > 0)
2206                                         memmove(&Buf->buf[a + 1], &Buf->buf[a + 3], len);
2207                         
2208                                 Buf->BufUsed -=2;
2209                         }
2210                 }
2211                 a++;
2212         }
2213         return a;
2214 }
2215
2216
2217 /**
2218  * @ingroup StrBuf_DeEnCoder
2219  * @brief       RFC2047-encode a header field if necessary.
2220  *              If no non-ASCII characters are found, the string
2221  *              will be copied verbatim without encoding.
2222  *
2223  * @param       target          Target buffer.
2224  * @param       source          Source string to be encoded.
2225  * @returns     encoded length; -1 if non success.
2226  */
2227 int StrBufRFC2047encode(StrBuf **target, const StrBuf *source)
2228 {
2229         const char headerStr[] = "=?UTF-8?Q?";
2230         int need_to_encode = 0;
2231         int i = 0;
2232         unsigned char ch;
2233
2234         if ((source == NULL) || 
2235             (target == NULL))
2236             return -1;
2237
2238         while ((i < source->BufUsed) &&
2239                (!IsEmptyStr (&source->buf[i])) &&
2240                (need_to_encode == 0)) {
2241                 if (((unsigned char) source->buf[i] < 32) || 
2242                     ((unsigned char) source->buf[i] > 126)) {
2243                         need_to_encode = 1;
2244                 }
2245                 i++;
2246         }
2247
2248         if (!need_to_encode) {
2249                 if (*target == NULL) {
2250                         *target = NewStrBufPlain(source->buf, source->BufUsed);
2251                 }
2252                 else {
2253                         FlushStrBuf(*target);
2254                         StrBufAppendBuf(*target, source, 0);
2255                 }
2256                 return (*target)->BufUsed;
2257         }
2258         if (*target == NULL)
2259                 *target = NewStrBufPlain(NULL, sizeof(headerStr) + source->BufUsed * 2);
2260         else if (sizeof(headerStr) + source->BufUsed >= (*target)->BufSize)
2261                 IncreaseBuf(*target, sizeof(headerStr) + source->BufUsed, 0);
2262         memcpy ((*target)->buf, headerStr, sizeof(headerStr) - 1);
2263         (*target)->BufUsed = sizeof(headerStr) - 1;
2264         for (i=0; (i < source->BufUsed); ++i) {
2265                 if ((*target)->BufUsed + 4 >= (*target)->BufSize)
2266                         IncreaseBuf(*target, 1, 0);
2267                 ch = (unsigned char) source->buf[i];
2268                 if ((ch < 32) || (ch > 126) || (ch == 61)) {
2269                         sprintf(&(*target)->buf[(*target)->BufUsed], "=%02X", ch);
2270                         (*target)->BufUsed += 3;
2271                 }
2272                 else {
2273                         (*target)->buf[(*target)->BufUsed] = ch;
2274                         (*target)->BufUsed++;
2275                 }
2276         }
2277         
2278         if ((*target)->BufUsed + 4 >= (*target)->BufSize)
2279                 IncreaseBuf(*target, 1, 0);
2280
2281         (*target)->buf[(*target)->BufUsed++] = '?';
2282         (*target)->buf[(*target)->BufUsed++] = '=';
2283         (*target)->buf[(*target)->BufUsed] = '\0';
2284         return (*target)->BufUsed;;
2285 }
2286
2287 /**
2288  * @ingroup StrBuf
2289  * @brief replaces all occurances of 'search' by 'replace'
2290  * @param buf Buffer to modify
2291  * @param search character to search
2292  * @param replace character to replace search by
2293  */
2294 void StrBufReplaceChars(StrBuf *buf, char search, char replace)
2295 {
2296         long i;
2297         if (buf == NULL)
2298                 return;
2299         for (i=0; i<buf->BufUsed; i++)
2300                 if (buf->buf[i] == search)
2301                         buf->buf[i] = replace;
2302
2303 }
2304
2305
2306 /*******************************************************************************
2307  *                 Iconv Wrapper; RFC822 de/encoding                           *
2308  *******************************************************************************/
2309
2310 /**
2311  * @ingroup StrBuf_DeEnCoder
2312  * @brief Wrapper around iconv_open()
2313  * Our version adds aliases for non-standard Microsoft charsets
2314  * such as 'MS950', aliasing them to names like 'CP950'
2315  *
2316  * @param tocode        Target encoding
2317  * @param fromcode      Source encoding
2318  * @param pic           anonimized pointer to iconv struct
2319  */
2320 void  ctdl_iconv_open(const char *tocode, const char *fromcode, void *pic)
2321 {
2322 #ifdef HAVE_ICONV
2323         iconv_t ic = (iconv_t)(-1) ;
2324         ic = iconv_open(tocode, fromcode);
2325         if (ic == (iconv_t)(-1) ) {
2326                 char alias_fromcode[64];
2327                 if ( (strlen(fromcode) == 5) && (!strncasecmp(fromcode, "MS", 2)) ) {
2328                         safestrncpy(alias_fromcode, fromcode, sizeof alias_fromcode);
2329                         alias_fromcode[0] = 'C';
2330                         alias_fromcode[1] = 'P';
2331                         ic = iconv_open(tocode, alias_fromcode);
2332                 }
2333         }
2334         *(iconv_t *)pic = ic;
2335 #endif
2336 }
2337
2338
2339 /**
2340  * @ingroup StrBuf_DeEnCoder
2341  * @brief find one chunk of a RFC822 encoded string
2342  * @param Buffer where to search
2343  * @param bptr where to start searching
2344  * @returns found position, NULL if none.
2345  */
2346 static inline char *FindNextEnd (const StrBuf *Buf, char *bptr)
2347 {
2348         char * end;
2349         /* Find the next ?Q? */
2350         if (Buf->BufUsed - (bptr - Buf->buf)  < 6)
2351                 return NULL;
2352
2353         end = strchr(bptr + 2, '?');
2354
2355         if (end == NULL)
2356                 return NULL;
2357
2358         if ((Buf->BufUsed - (end - Buf->buf) > 3) &&
2359             ((*(end + 1) == 'B') || (*(end + 1) == 'Q')) && 
2360             (*(end + 2) == '?')) {
2361                 /* skip on to the end of the cluster, the next ?= */
2362                 end = strstr(end + 3, "?=");
2363         }
2364         else
2365                 /* sort of half valid encoding, try to find an end. */
2366                 end = strstr(bptr, "?=");
2367         return end;
2368 }
2369
2370 /**
2371  * @ingroup StrBuf
2372  * @brief swaps the contents of two StrBufs
2373  * this is to be used to have cheap switched between a work-buffer and a target buffer 
2374  * @param A First one
2375  * @param B second one
2376  */
2377 static inline void SwapBuffers(StrBuf *A, StrBuf *B)
2378 {
2379         StrBuf C;
2380
2381         memcpy(&C, A, sizeof(*A));
2382         memcpy(A, B, sizeof(*B));
2383         memcpy(B, &C, sizeof(C));
2384
2385 }
2386
2387
2388 /**
2389  * @ingroup StrBuf_DeEnCoder
2390  * @brief convert one buffer according to the preselected iconv pointer PIC
2391  * @param ConvertBuf buffer we need to translate
2392  * @param TmpBuf To share a workbuffer over several iterations. prepare to have it filled with useless stuff afterwards.
2393  * @param pic Pointer to the iconv-session Object
2394  */
2395 void StrBufConvert(StrBuf *ConvertBuf, StrBuf *TmpBuf, void *pic)
2396 {
2397 #ifdef HAVE_ICONV
2398         long trycount = 0;
2399         size_t siz;
2400         iconv_t ic;
2401         char *ibuf;                     /**< Buffer of characters to be converted */
2402         char *obuf;                     /**< Buffer for converted characters */
2403         size_t ibuflen;                 /**< Length of input buffer */
2404         size_t obuflen;                 /**< Length of output buffer */
2405
2406
2407         /* since we're converting to utf-8, one glyph may take up to 6 bytes */
2408         if (ConvertBuf->BufUsed * 6 >= TmpBuf->BufSize)
2409                 IncreaseBuf(TmpBuf, 0, ConvertBuf->BufUsed * 6);
2410 TRYAGAIN:
2411         ic = *(iconv_t*)pic;
2412         ibuf = ConvertBuf->buf;
2413         ibuflen = ConvertBuf->BufUsed;
2414         obuf = TmpBuf->buf;
2415         obuflen = TmpBuf->BufSize;
2416         
2417         siz = iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
2418
2419         if (siz < 0) {
2420                 if (errno == E2BIG) {
2421                         trycount ++;                    
2422                         IncreaseBuf(TmpBuf, 0, 0);
2423                         if (trycount < 5) 
2424                                 goto TRYAGAIN;
2425
2426                 }
2427                 else if (errno == EILSEQ){ 
2428                         /* hm, invalid utf8 sequence... what to do now? */
2429                         /* An invalid multibyte sequence has been encountered in the input */
2430                 }
2431                 else if (errno == EINVAL) {
2432                         /* An incomplete multibyte sequence has been encountered in the input. */
2433                 }
2434
2435                 FlushStrBuf(TmpBuf);
2436         }
2437         else {
2438                 TmpBuf->BufUsed = TmpBuf->BufSize - obuflen;
2439                 TmpBuf->buf[TmpBuf->BufUsed] = '\0';
2440                 
2441                 /* little card game: wheres the red lady? */
2442                 SwapBuffers(ConvertBuf, TmpBuf);
2443                 FlushStrBuf(TmpBuf);
2444         }
2445 #endif
2446 }
2447
2448
2449 /**
2450  * @ingroup StrBuf_DeEnCoder
2451  * @brief catches one RFC822 encoded segment, and decodes it.
2452  * @param Target buffer to fill with result
2453  * @param DecodeMe buffer with stuff to process
2454  * @param SegmentStart points to our current segment in DecodeMe
2455  * @param SegmentEnd Points to the end of our current segment in DecodeMe
2456  * @param ConvertBuf Workbuffer shared between several iterations. Random content; needs to be valid
2457  * @param ConvertBuf2 Workbuffer shared between several iterations. Random content; needs to be valid
2458  * @param FoundCharset Characterset to default decoding to; if we find another we will overwrite it.
2459  */
2460 inline static void DecodeSegment(StrBuf *Target, 
2461                                  const StrBuf *DecodeMe, 
2462                                  char *SegmentStart, 
2463                                  char *SegmentEnd, 
2464                                  StrBuf *ConvertBuf,
2465                                  StrBuf *ConvertBuf2, 
2466                                  StrBuf *FoundCharset)
2467 {
2468         StrBuf StaticBuf;
2469         char charset[128];
2470         char encoding[16];
2471 #ifdef HAVE_ICONV
2472         iconv_t ic = (iconv_t)(-1);
2473 #else
2474         void *ic = NULL;
2475 #endif
2476         /* Now we handle foreign character sets properly encoded
2477          * in RFC2047 format.
2478          */
2479         StaticBuf.buf = SegmentStart;
2480         StaticBuf.BufUsed = SegmentEnd - SegmentStart;
2481         StaticBuf.BufSize = DecodeMe->BufSize - (SegmentStart - DecodeMe->buf);
2482         extract_token(charset, SegmentStart, 1, '?', sizeof charset);
2483         if (FoundCharset != NULL) {
2484                 FlushStrBuf(FoundCharset);
2485                 StrBufAppendBufPlain(FoundCharset, charset, -1, 0);
2486         }
2487         extract_token(encoding, SegmentStart, 2, '?', sizeof encoding);
2488         StrBufExtract_token(ConvertBuf, &StaticBuf, 3, '?');
2489         
2490         *encoding = toupper(*encoding);
2491         if (*encoding == 'B') { /**< base64 */
2492                 ConvertBuf2->BufUsed = CtdlDecodeBase64(ConvertBuf2->buf, 
2493                                                         ConvertBuf->buf, 
2494                                                         ConvertBuf->BufUsed);
2495         }
2496         else if (*encoding == 'Q') {    /**< quoted-printable */
2497                 long pos;
2498                 
2499                 pos = 0;
2500                 while (pos < ConvertBuf->BufUsed)
2501                 {
2502                         if (ConvertBuf->buf[pos] == '_') 
2503                                 ConvertBuf->buf[pos] = ' ';
2504                         pos++;
2505                 }
2506                 
2507                 ConvertBuf2->BufUsed = CtdlDecodeQuotedPrintable(
2508                         ConvertBuf2->buf, 
2509                         ConvertBuf->buf,
2510                         ConvertBuf->BufUsed);
2511         }
2512         else {
2513                 StrBufAppendBuf(ConvertBuf2, ConvertBuf, 0);
2514         }
2515 #ifdef HAVE_ICONV
2516         ctdl_iconv_open("UTF-8", charset, &ic);
2517         if (ic != (iconv_t)(-1) ) {             
2518 #endif
2519                 StrBufConvert(ConvertBuf2, ConvertBuf, &ic);
2520                 StrBufAppendBuf(Target, ConvertBuf2, 0);
2521 #ifdef HAVE_ICONV
2522                 iconv_close(ic);
2523         }
2524         else {
2525                 StrBufAppendBufPlain(Target, HKEY("(unreadable)"), 0);
2526         }
2527 #endif
2528 }
2529
2530 /**
2531  * @ingroup StrBuf_DeEnCoder
2532  * @brief Handle subjects with RFC2047 encoding such as:
2533  * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
2534  * @param Target where to put the decoded string to 
2535  * @param DecodeMe buffer with encoded string
2536  * @param DefaultCharset if we don't find one, which should we use?
2537  * @param FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string, 
2538  *        put it here for later use where no string might be known.
2539  */
2540 void StrBuf_RFC822_to_Utf8(StrBuf *Target, const StrBuf *DecodeMe, const StrBuf* DefaultCharset, StrBuf *FoundCharset)
2541 {
2542         StrBuf *DecodedInvalidBuf = NULL;
2543         StrBuf *ConvertBuf, *ConvertBuf2;
2544         const StrBuf *DecodeMee = DecodeMe;
2545         char *start, *end, *next, *nextend, *ptr = NULL;
2546 #ifdef HAVE_ICONV
2547         iconv_t ic = (iconv_t)(-1) ;
2548 #endif
2549         const char *eptr;
2550         int passes = 0;
2551         int i, len, delta;
2552         int illegal_non_rfc2047_encoding = 0;
2553
2554         /* Sometimes, badly formed messages contain strings which were simply
2555          *  written out directly in some foreign character set instead of
2556          *  using RFC2047 encoding.  This is illegal but we will attempt to
2557          *  handle it anyway by converting from a user-specified default
2558          *  charset to UTF-8 if we see any nonprintable characters.
2559          */
2560         
2561         len = StrLength(DecodeMe);
2562         for (i=0; i<DecodeMe->BufUsed; ++i) {
2563                 if ((DecodeMe->buf[i] < 32) || (DecodeMe->buf[i] > 126)) {
2564                         illegal_non_rfc2047_encoding = 1;
2565                         break;
2566                 }
2567         }
2568
2569         ConvertBuf = NewStrBufPlain(NULL, StrLength(DecodeMe));
2570         if ((illegal_non_rfc2047_encoding) &&
2571             (strcasecmp(ChrPtr(DefaultCharset), "UTF-8")) && 
2572             (strcasecmp(ChrPtr(DefaultCharset), "us-ascii")) )
2573         {
2574 #ifdef HAVE_ICONV
2575                 ctdl_iconv_open("UTF-8", ChrPtr(DefaultCharset), &ic);
2576                 if (ic != (iconv_t)(-1) ) {
2577                         DecodedInvalidBuf = NewStrBufDup(DecodeMe);
2578                         StrBufConvert(DecodedInvalidBuf, ConvertBuf, &ic);///TODO: don't void const?
2579                         DecodeMee = DecodedInvalidBuf;
2580                         iconv_close(ic);
2581                 }
2582 #endif
2583         }
2584
2585         /* pre evaluate the first pair */
2586         nextend = end = NULL;
2587         len = StrLength(DecodeMee);
2588         start = strstr(DecodeMee->buf, "=?");
2589         eptr = DecodeMee->buf + DecodeMee->BufUsed;
2590         if (start != NULL) 
2591                 end = FindNextEnd (DecodeMee, start);
2592         else {
2593                 StrBufAppendBuf(Target, DecodeMee, 0);
2594                 FreeStrBuf(&ConvertBuf);
2595                 FreeStrBuf(&DecodedInvalidBuf);
2596                 return;
2597         }
2598
2599         ConvertBuf2 = NewStrBufPlain(NULL, StrLength(DecodeMee));
2600
2601         if (start != DecodeMee->buf) {
2602                 long nFront;
2603                 
2604                 nFront = start - DecodeMee->buf;
2605                 StrBufAppendBufPlain(Target, DecodeMee->buf, nFront, 0);
2606                 len -= nFront;
2607         }
2608         /*
2609          * Since spammers will go to all sorts of absurd lengths to get their
2610          * messages through, there are LOTS of corrupt headers out there.
2611          * So, prevent a really badly formed RFC2047 header from throwing
2612          * this function into an infinite loop.
2613          */
2614         while ((start != NULL) && 
2615                (end != NULL) && 
2616                (start < eptr) && 
2617                (end < eptr) && 
2618                (passes < 20))
2619         {
2620                 passes++;
2621                 DecodeSegment(Target, 
2622                               DecodeMee, 
2623                               start, 
2624                               end, 
2625                               ConvertBuf,
2626                               ConvertBuf2,
2627                               FoundCharset);
2628                 
2629                 next = strstr(end, "=?");
2630                 nextend = NULL;
2631                 if ((next != NULL) && 
2632                     (next < eptr))
2633                         nextend = FindNextEnd(DecodeMee, next);
2634                 if (nextend == NULL)
2635                         next = NULL;
2636
2637                 /* did we find two partitions */
2638                 if ((next != NULL) && 
2639                     ((next - end) > 2))
2640                 {
2641                         ptr = end + 2;
2642                         while ((ptr < next) && 
2643                                (isspace(*ptr) ||
2644                                 (*ptr == '\r') ||
2645                                 (*ptr == '\n') || 
2646                                 (*ptr == '\t')))
2647                                 ptr ++;
2648                         /* did we find a gab just filled with blanks? */
2649                         if (ptr == next)
2650                         {
2651                                 long gap = next - start;
2652                                 memmove (end + 2,
2653                                          next,
2654                                          len - (gap));
2655                                 len -= gap;
2656                                 /* now terminate the gab at the end */
2657                                 delta = (next - end) - 2; ////TODO: const! 
2658                                 ((StrBuf*)DecodeMee)->BufUsed -= delta;
2659                                 ((StrBuf*)DecodeMee)->buf[DecodeMee->BufUsed] = '\0';
2660
2661                                 /* move next to its new location. */
2662                                 next -= delta;
2663                                 nextend -= delta;
2664                         }
2665                 }
2666                 /* our next-pair is our new first pair now. */
2667                 ptr = end + 2;
2668                 start = next;
2669                 end = nextend;
2670         }
2671         end = ptr;
2672         nextend = DecodeMee->buf + DecodeMee->BufUsed;
2673         if ((end != NULL) && (end < nextend)) {
2674                 ptr = end;
2675                 while ( (ptr < nextend) &&
2676                         (isspace(*ptr) ||
2677                          (*ptr == '\r') ||
2678                          (*ptr == '\n') || 
2679                          (*ptr == '\t')))
2680                         ptr ++;
2681                 if (ptr < nextend)
2682                         StrBufAppendBufPlain(Target, end, nextend - end, 0);
2683         }
2684         FreeStrBuf(&ConvertBuf);
2685         FreeStrBuf(&ConvertBuf2);
2686         FreeStrBuf(&DecodedInvalidBuf);
2687 }
2688
2689 /*******************************************************************************
2690  *                   Manipulating UTF-8 Strings                                *
2691  *******************************************************************************/
2692
2693 /**
2694  * @ingroup StrBuf
2695  * @brief evaluate the length of an utf8 special character sequence
2696  * @param Char the character to examine
2697  * @returns width of utf8 chars in bytes
2698  */
2699 static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE)
2700 {
2701         int n = 1;
2702         char test = (1<<7);
2703         
2704         while ((n < 8) && ((test & *CharS) != 0)) {
2705                 test = test << 1;
2706                 n ++;
2707         }
2708         if ((n > 6) || ((CharE - CharS) < n))
2709                 n = 1;
2710         return n;
2711 }
2712
2713 /**
2714  * @ingroup StrBuf
2715  * @brief detect whether this char starts an utf-8 encoded char
2716  * @param Char character to inspect
2717  * @returns yes or no
2718  */
2719 static inline int Ctdl_IsUtf8SequenceStart(const char Char)
2720 {
2721 /** 11??.???? indicates an UTF8 Sequence. */
2722         return ((Char & 0xC0) != 0);
2723 }
2724
2725 /**
2726  * @ingroup StrBuf
2727  * @brief measure the number of glyphs in an UTF8 string...
2728  * @param Buf string to measure
2729  * @returns the number of glyphs in Buf
2730  */
2731 long StrBuf_Utf8StrLen(StrBuf *Buf)
2732 {
2733         int n = 0;
2734         int m = 0;
2735         char *aptr, *eptr;
2736
2737         if ((Buf == NULL) || (Buf->BufUsed == 0))
2738                 return 0;
2739         aptr = Buf->buf;
2740         eptr = Buf->buf + Buf->BufUsed;
2741         while ((aptr < eptr) && (*aptr != '\0')) {
2742                 if (Ctdl_IsUtf8SequenceStart(*aptr)){
2743                         m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
2744                         while ((aptr < eptr) && (*aptr++ != '\0')&& (m-- > 0) );
2745                         n ++;
2746                 }
2747                 else {
2748                         n++;
2749                         aptr++;
2750                 }
2751         }
2752         return n;
2753 }
2754
2755 /**
2756  * @ingroup StrBuf
2757  * @brief cuts a string after maxlen glyphs
2758  * @param Buf string to cut to maxlen glyphs
2759  * @param maxlen how long may the string become?
2760  * @returns current length of the string
2761  */
2762 long StrBuf_Utf8StrCut(StrBuf *Buf, int maxlen)
2763 {
2764         char *aptr, *eptr;
2765         int n = 0, m = 0;
2766
2767         aptr = Buf->buf;
2768         eptr = Buf->buf + Buf->BufUsed;
2769         while ((aptr < eptr) && (*aptr != '\0')) {
2770                 if (Ctdl_IsUtf8SequenceStart(*aptr)){
2771                         m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
2772                         while ((*aptr++ != '\0') && (m-- > 0));
2773                         n ++;
2774                 }
2775                 else {
2776                         n++;
2777                         aptr++;
2778                 }
2779                 if (n > maxlen) {
2780                         *aptr = '\0';
2781                         Buf->BufUsed = aptr - Buf->buf;
2782                         return Buf->BufUsed;
2783                 }                       
2784         }
2785         return Buf->BufUsed;
2786
2787 }
2788
2789
2790
2791
2792
2793 /*******************************************************************************
2794  *                               wrapping ZLib                                 *
2795  *******************************************************************************/
2796
2797 #ifdef HAVE_ZLIB
2798 #define DEF_MEM_LEVEL 8 /*< memlevel??? */
2799 #define OS_CODE 0x03    /*< unix */
2800
2801 /**
2802  * @ingroup StrBuf_DeEnCoder
2803  * @brief uses the same calling syntax as compress2(), but it
2804  *   creates a stream compatible with HTTP "Content-encoding: gzip"
2805  * @param dest compressed buffer
2806  * @param destLen length of the compresed data 
2807  * @param source source to encode
2808  * @param sourceLen length of source to encode 
2809  * @param level compression level
2810  */
2811 int ZEXPORT compress_gzip(Bytef * dest,
2812                           size_t * destLen,
2813                           const Bytef * source,
2814                           uLong sourceLen,     
2815                           int level)
2816 {
2817         const int gz_magic[2] = { 0x1f, 0x8b }; /* gzip magic header */
2818
2819         /* write gzip header */
2820         snprintf((char *) dest, *destLen, 
2821                  "%c%c%c%c%c%c%c%c%c%c",
2822                  gz_magic[0], gz_magic[1], Z_DEFLATED,
2823                  0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /* xflags */ ,
2824                  OS_CODE);
2825
2826         /* normal deflate */
2827         z_stream stream;
2828         int err;
2829         stream.next_in = (Bytef *) source;
2830         stream.avail_in = (uInt) sourceLen;
2831         stream.next_out = dest + 10L;   // after header
2832         stream.avail_out = (uInt) * destLen;
2833         if ((uLong) stream.avail_out != *destLen)
2834                 return Z_BUF_ERROR;
2835
2836         stream.zalloc = (alloc_func) 0;
2837         stream.zfree = (free_func) 0;
2838         stream.opaque = (voidpf) 0;
2839
2840         err = deflateInit2(&stream, level, Z_DEFLATED, -MAX_WBITS,
2841                            DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
2842         if (err != Z_OK)
2843                 return err;
2844
2845         err = deflate(&stream, Z_FINISH);
2846         if (err != Z_STREAM_END) {
2847                 deflateEnd(&stream);
2848                 return err == Z_OK ? Z_BUF_ERROR : err;
2849         }
2850         *destLen = stream.total_out + 10L;
2851
2852         /* write CRC and Length */
2853         uLong crc = crc32(0L, source, sourceLen);
2854         int n;
2855         for (n = 0; n < 4; ++n, ++*destLen) {
2856                 dest[*destLen] = (int) (crc & 0xff);
2857                 crc >>= 8;
2858         }
2859         uLong len = stream.total_in;
2860         for (n = 0; n < 4; ++n, ++*destLen) {
2861                 dest[*destLen] = (int) (len & 0xff);
2862                 len >>= 8;
2863         }
2864         err = deflateEnd(&stream);
2865         return err;
2866 }
2867 #endif
2868
2869
2870 /**
2871  * @ingroup StrBuf_DeEnCoder
2872  * @brief compress the buffer with gzip
2873  * Attention! If you feed this a Const String, you must maintain the uncompressed buffer yourself!
2874  * @param Buf buffer whose content is to be gzipped
2875  */
2876 int CompressBuffer(StrBuf *Buf)
2877 {
2878 #ifdef HAVE_ZLIB
2879         char *compressed_data = NULL;
2880         size_t compressed_len, bufsize;
2881         int i = 0;
2882
2883         bufsize = compressed_len = Buf->BufUsed +  (Buf->BufUsed / 100) + 100;
2884         compressed_data = malloc(compressed_len);
2885         
2886         if (compressed_data == NULL)
2887                 return -1;
2888         /* Flush some space after the used payload so valgrind shuts up... */
2889         while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
2890                 Buf->buf[Buf->BufUsed + i++] = '\0';
2891         if (compress_gzip((Bytef *) compressed_data,
2892                           &compressed_len,
2893                           (Bytef *) Buf->buf,
2894                           (uLongf) Buf->BufUsed, Z_BEST_SPEED) == Z_OK) {
2895                 if (!Buf->ConstBuf)
2896                         free(Buf->buf);
2897                 Buf->buf = compressed_data;
2898                 Buf->BufUsed = compressed_len;
2899                 Buf->BufSize = bufsize;
2900                 /* Flush some space after the used payload so valgrind shuts up... */
2901                 i = 0;
2902                 while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
2903                         Buf->buf[Buf->BufUsed + i++] = '\0';
2904                 return 1;
2905         } else {
2906                 free(compressed_data);
2907         }
2908 #endif  /* HAVE_ZLIB */
2909         return 0;
2910 }
2911
2912
2913
2914 /*******************************************************************************
2915  *           File I/O; Prefer buffered read since its faster!                  *
2916  *******************************************************************************/
2917
2918 /**
2919  * @ingroup StrBuf_IO
2920  * @brief Read a line from socket
2921  * flushes and closes the FD on error
2922  * @param buf the buffer to get the input to
2923  * @param fd pointer to the filedescriptor to read
2924  * @param append Append to an existing string or replace?
2925  * @param Error strerror() on error 
2926  * @returns numbers of chars read
2927  */
2928 int StrBufTCP_read_line(StrBuf *buf, int *fd, int append, const char **Error)
2929 {
2930         int len, rlen, slen;
2931
2932         if (!append)
2933                 FlushStrBuf(buf);
2934
2935         slen = len = buf->BufUsed;
2936         while (1) {
2937                 rlen = read(*fd, &buf->buf[len], 1);
2938                 if (rlen < 1) {
2939                         *Error = strerror(errno);
2940                         
2941                         close(*fd);
2942                         *fd = -1;
2943                         
2944                         return -1;
2945                 }
2946                 if (buf->buf[len] == '\n')
2947                         break;
2948                 if (buf->buf[len] != '\r')
2949                         len ++;
2950                 if (len + 2 >= buf->BufSize) {
2951                         buf->BufUsed = len;
2952                         buf->buf[len+1] = '\0';
2953                         IncreaseBuf(buf, 1, -1);
2954                 }
2955         }
2956         buf->BufUsed = len;
2957         buf->buf[len] = '\0';
2958         return len - slen;
2959 }
2960
2961 /**
2962  * @ingroup StrBuf_BufferedIO
2963  * @brief Read a line from socket
2964  * flushes and closes the FD on error
2965  * @param Line the line to read from the fd / I/O Buffer
2966  * @param buf the buffer to get the input to
2967  * @param fd pointer to the filedescriptor to read
2968  * @param timeout number of successless selects until we bail out
2969  * @param selectresolution how long to wait on each select
2970  * @param Error strerror() on error 
2971  * @returns numbers of chars read
2972  */
2973 int StrBufTCP_read_buffered_line(StrBuf *Line, 
2974                                  StrBuf *buf, 
2975                                  int *fd, 
2976                                  int timeout, 
2977                                  int selectresolution, 
2978                                  const char **Error)
2979 {
2980         int len, rlen;
2981         int nSuccessLess = 0;
2982         fd_set rfds;
2983         char *pch = NULL;
2984         int fdflags;
2985         int IsNonBlock;
2986         struct timeval tv;
2987
2988         if (buf->BufUsed > 0) {
2989                 pch = strchr(buf->buf, '\n');
2990                 if (pch != NULL) {
2991                         rlen = 0;
2992                         len = pch - buf->buf;
2993                         if (len > 0 && (*(pch - 1) == '\r') )
2994                                 rlen ++;
2995                         StrBufSub(Line, buf, 0, len - rlen);
2996                         StrBufCutLeft(buf, len + 1);
2997                         return len - rlen;
2998                 }
2999         }
3000         
3001         if (buf->BufSize - buf->BufUsed < 10)
3002                 IncreaseBuf(buf, 1, -1);
3003
3004         fdflags = fcntl(*fd, F_GETFL);
3005         IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
3006
3007         while ((nSuccessLess < timeout) && (pch == NULL)) {
3008                 if (IsNonBlock){
3009                         tv.tv_sec = selectresolution;
3010                         tv.tv_usec = 0;
3011                         
3012                         FD_ZERO(&rfds);
3013                         FD_SET(*fd, &rfds);
3014                         if (select(*fd + 1, NULL, &rfds, NULL, &tv) == -1) {
3015                                 *Error = strerror(errno);
3016                                 close (*fd);
3017                                 *fd = -1;
3018                                 return -1;
3019                         }
3020                 }
3021                 if (IsNonBlock && !  FD_ISSET(*fd, &rfds)) {
3022                         nSuccessLess ++;
3023                         continue;
3024                 }
3025                 rlen = read(*fd, 
3026                             &buf->buf[buf->BufUsed], 
3027                             buf->BufSize - buf->BufUsed - 1);
3028                 if (rlen < 1) {
3029                         *Error = strerror(errno);
3030                         close(*fd);
3031                         *fd = -1;
3032                         return -1;
3033                 }
3034                 else if (rlen > 0) {
3035                         nSuccessLess = 0;
3036                         buf->BufUsed += rlen;
3037                         buf->buf[buf->BufUsed] = '\0';
3038                         if (buf->BufUsed + 10 > buf->BufSize) {
3039                                 IncreaseBuf(buf, 1, -1);
3040                         }
3041                         pch = strchr(buf->buf, '\n');
3042                         continue;
3043                 }
3044                 
3045         }
3046         if (pch != NULL) {
3047                 rlen = 0;
3048                 len = pch - buf->buf;
3049                 if (len > 0 && (*(pch - 1) == '\r') )
3050                         rlen ++;
3051                 StrBufSub(Line, buf, 0, len - rlen);
3052                 StrBufCutLeft(buf, len + 1);
3053                 return len - rlen;
3054         }
3055         return -1;
3056
3057 }
3058
3059 static const char *ErrRBLF_PreConditionFailed="StrBufTCP_read_buffered_line_fast: Wrong arguments or invalid Filedescriptor";
3060 static const char *ErrRBLF_SelectFailed="StrBufTCP_read_buffered_line_fast: Select failed without reason";
3061 static const char *ErrRBLF_NotEnoughSentFromServer="StrBufTCP_read_buffered_line_fast: No complete line was sent from peer";
3062 /**
3063  * @ingroup StrBuf_BufferedIO
3064  * @brief Read a line from socket
3065  * flushes and closes the FD on error
3066  * @param Line Line to read from the fd / I/O Buffer
3067  * @param IOBuf the buffer to get the input to
3068  * @param Pos pointer to the current read position, should be NULL initialized!
3069  * @param fd pointer to the filedescriptor to read
3070  * @param timeout number of successless selects until we bail out
3071  * @param selectresolution how long to wait on each select
3072  * @param Error strerror() on error 
3073  * @returns numbers of chars read
3074  */
3075 int StrBufTCP_read_buffered_line_fast(StrBuf *Line, 
3076                                       StrBuf *IOBuf, 
3077                                       const char **Pos,
3078                                       int *fd, 
3079                                       int timeout, 
3080                                       int selectresolution, 
3081                                       const char **Error)
3082 {
3083         const char *pche = NULL;
3084         const char *pos = NULL;
3085         int len, rlen;
3086         int nSuccessLess = 0;
3087         fd_set rfds;
3088         const char *pch = NULL;
3089         int fdflags;
3090         int IsNonBlock;
3091         struct timeval tv;
3092         
3093         if ((Line == NULL) ||
3094             (Pos == NULL) ||
3095             (IOBuf == NULL) ||
3096             (*fd == -1))
3097         {
3098                 if (Pos != NULL)
3099                         *Pos = NULL;
3100                 *Error = ErrRBLF_PreConditionFailed;
3101                 return -1;
3102         }
3103
3104         pos = *Pos;
3105         if ((IOBuf->BufUsed > 0) && 
3106             (pos != NULL) && 
3107             (pos < IOBuf->buf + IOBuf->BufUsed)) 
3108         {
3109                 pche = IOBuf->buf + IOBuf->BufUsed;
3110                 pch = pos;
3111                 while ((pch < pche) && (*pch != '\n'))
3112                         pch ++;
3113                 if ((pch >= pche) || (*pch == '\0'))
3114                         pch = NULL;
3115                 if ((pch != NULL) && 
3116                     (pch <= pche)) 
3117                 {
3118                         rlen = 0;
3119                         len = pch - pos;
3120                         if (len > 0 && (*(pch - 1) == '\r') )
3121                                 rlen ++;
3122                         StrBufSub(Line, IOBuf, (pos - IOBuf->buf), len - rlen);
3123                         *Pos = pch + 1;
3124                         return len - rlen;
3125                 }
3126         }
3127         
3128         if (pos != NULL) {
3129                 if (pos > pche)
3130                         FlushStrBuf(IOBuf);
3131                 else 
3132                         StrBufCutLeft(IOBuf, (pos - IOBuf->buf));
3133                 *Pos = NULL;
3134         }
3135         
3136         if (IOBuf->BufSize - IOBuf->BufUsed < 10) {
3137                 IncreaseBuf(IOBuf, 1, -1);
3138                 *Pos = NULL;
3139         }
3140
3141         fdflags = fcntl(*fd, F_GETFL);
3142         IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
3143
3144         pch = NULL;
3145         while ((nSuccessLess < timeout) && 
3146                (pch == NULL) &&
3147                (*fd != -1)) {
3148                 if (IsNonBlock)
3149                 {
3150                         tv.tv_sec = 1;
3151                         tv.tv_usec = 0;
3152                 
3153                         FD_ZERO(&rfds);
3154                         FD_SET(*fd, &rfds);
3155                         if (select((*fd) + 1, &rfds, NULL, NULL, &tv) == -1) {
3156                                 *Error = strerror(errno);
3157                                 close (*fd);
3158                                 *fd = -1;
3159                                 if (*Error == NULL)
3160                                         *Error = ErrRBLF_SelectFailed;
3161                                 return -1;
3162                         }
3163                         if (! FD_ISSET(*fd, &rfds) != 0) {
3164                                 nSuccessLess ++;
3165                                 continue;
3166                         }
3167                 }
3168                 rlen = read(*fd, 
3169                             &IOBuf->buf[IOBuf->BufUsed], 
3170                             IOBuf->BufSize - IOBuf->BufUsed - 1);
3171                 if (rlen < 1) {
3172                         *Error = strerror(errno);
3173                         close(*fd);
3174                         *fd = -1;
3175                         return -1;
3176                 }
3177                 else if (rlen > 0) {
3178                         nSuccessLess = 0;
3179                         IOBuf->BufUsed += rlen;
3180                         IOBuf->buf[IOBuf->BufUsed] = '\0';
3181                         if (IOBuf->BufUsed + 10 > IOBuf->BufSize) {
3182                                 IncreaseBuf(IOBuf, 1, -1);
3183                                 *Pos = NULL;
3184                         }
3185                         
3186                         pche = IOBuf->buf + IOBuf->BufUsed;
3187                         pch = IOBuf->buf;
3188                         while ((pch < pche) && (*pch != '\n'))
3189                                 pch ++;
3190                         if ((pch >= pche) || (*pch == '\0'))
3191                                 pch = NULL;
3192                         continue;
3193                 }
3194         }
3195         if (pch != NULL) {
3196                 pos = IOBuf->buf;
3197                 rlen = 0;
3198                 len = pch - pos;
3199                 if (len > 0 && (*(pch - 1) == '\r') )
3200                         rlen ++;
3201                 StrBufSub(Line, IOBuf, 0, len - rlen);
3202                 *Pos = pos + len + 1;
3203                 return len - rlen;
3204         }
3205         *Error = ErrRBLF_NotEnoughSentFromServer;
3206         return -1;
3207
3208 }
3209
3210 static const char *ErrRBLF_BLOBPreConditionFailed="StrBufReadBLOB: Wrong arguments or invalid Filedescriptor";
3211 /**
3212  * @ingroup StrBuf_IO
3213  * @brief Input binary data from socket
3214  * flushes and closes the FD on error
3215  * @param Buf the buffer to get the input to
3216  * @param fd pointer to the filedescriptor to read
3217  * @param append Append to an existing string or replace?
3218  * @param nBytes the maximal number of bytes to read
3219  * @param Error strerror() on error 
3220  * @returns numbers of chars read
3221  */
3222 int StrBufReadBLOB(StrBuf *Buf, int *fd, int append, long nBytes, const char **Error)
3223 {
3224         int fdflags;
3225         int len, rlen, slen;
3226         int nSuccessLess;
3227         int nRead = 0;
3228         char *ptr;
3229         int IsNonBlock;
3230         struct timeval tv;
3231         fd_set rfds;
3232
3233         if ((Buf == NULL) || (*fd == -1))
3234         {
3235                 *Error = ErrRBLF_BLOBPreConditionFailed;
3236                 return -1;
3237         }
3238         if (!append)
3239                 FlushStrBuf(Buf);
3240         if (Buf->BufUsed + nBytes >= Buf->BufSize)
3241                 IncreaseBuf(Buf, 1, Buf->BufUsed + nBytes);
3242
3243         ptr = Buf->buf + Buf->BufUsed;
3244
3245         slen = len = Buf->BufUsed;
3246
3247         fdflags = fcntl(*fd, F_GETFL);
3248         IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
3249         nSuccessLess = 0;
3250         while ((nRead < nBytes) && 
3251                (*fd != -1)) 
3252         {
3253                 if (IsNonBlock)
3254                 {
3255                         tv.tv_sec = 1;
3256                         tv.tv_usec = 0;
3257                 
3258                         FD_ZERO(&rfds);
3259                         FD_SET(*fd, &rfds);
3260                         if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
3261                                 *Error = strerror(errno);
3262                                 close (*fd);
3263                                 *fd = -1;
3264                                 if (*Error == NULL)
3265                                         *Error = ErrRBLF_SelectFailed;
3266                                 return -1;
3267                         }
3268                         if (! FD_ISSET(*fd, &rfds) != 0) {
3269                                 nSuccessLess ++;
3270                                 continue;
3271                         }
3272                 }
3273
3274                 if ((rlen = read(*fd, 
3275                                  ptr,
3276                                  nBytes - nRead)) == -1) {
3277                         close(*fd);
3278                         *fd = -1;
3279                         *Error = strerror(errno);
3280                         return rlen;
3281                 }
3282                 nRead += rlen;
3283                 ptr += rlen;
3284                 Buf->BufUsed += rlen;
3285         }
3286         Buf->buf[Buf->BufUsed] = '\0';
3287         return nRead;
3288 }
3289
3290 const char *ErrRBB_BLOBFPreConditionFailed = "StrBufReadBLOBBuffered: to many selects; aborting.";
3291 const char *ErrRBB_too_many_selects = "StrBufReadBLOBBuffered: to many selects; aborting.";
3292 /**
3293  * @ingroup StrBuf_BufferedIO
3294  * @brief Input binary data from socket
3295  * flushes and closes the FD on error
3296  * @param Blob put binary thing here
3297  * @param IOBuf the buffer to get the input to
3298  * @param Pos offset inside of IOBuf
3299  * @param fd pointer to the filedescriptor to read
3300  * @param append Append to an existing string or replace?
3301  * @param nBytes the maximal number of bytes to read
3302  * @param check whether we should search for '000\n' terminators in case of timeouts
3303  * @param Error strerror() on error 
3304  * @returns numbers of chars read
3305  */
3306 int StrBufReadBLOBBuffered(StrBuf *Blob, 
3307                            StrBuf *IOBuf, 
3308                            const char **Pos,
3309                            int *fd, 
3310                            int append, 
3311                            long nBytes, 
3312                            int check, 
3313                            const char **Error)
3314 {
3315         const char *pche;
3316         const char *pos;
3317         int nSelects = 0;
3318         int SelRes;
3319         int fdflags;
3320         int len = 0;
3321         int rlen, slen;
3322         int nRead = 0;
3323         int nAlreadyRead = 0;
3324         int IsNonBlock;
3325         char *ptr;
3326         fd_set rfds;
3327         const char *pch;
3328         struct timeval tv;
3329         int nSuccessLess;
3330
3331         if ((Blob == NULL) || (*fd == -1) || (IOBuf == NULL) || (Pos == NULL))
3332         {
3333                 if (*Pos != NULL)
3334                         *Pos = NULL;
3335                 *Error = ErrRBB_BLOBFPreConditionFailed;
3336                 return -1;
3337         }
3338
3339         if (!append)
3340                 FlushStrBuf(Blob);
3341         if (Blob->BufUsed + nBytes >= Blob->BufSize) 
3342                 IncreaseBuf(Blob, append, Blob->BufUsed + nBytes);
3343         
3344         pos = *Pos;
3345
3346         if (pos > 0)
3347                 len = pos - IOBuf->buf;
3348         rlen = IOBuf->BufUsed - len;
3349
3350
3351         if ((IOBuf->BufUsed > 0) && 
3352             (pos != NULL) && 
3353             (pos < IOBuf->buf + IOBuf->BufUsed)) 
3354         {
3355                 pche = IOBuf->buf + IOBuf->BufUsed;
3356                 pch = pos;
3357
3358                 if (rlen < nBytes) {
3359                         memcpy(Blob->buf + Blob->BufUsed, pos, rlen);
3360                         Blob->BufUsed += rlen;
3361                         Blob->buf[Blob->BufUsed] = '\0';
3362                         nAlreadyRead = nRead = rlen;
3363                         *Pos = NULL; 
3364                 }
3365                 if (rlen >= nBytes) {
3366                         memcpy(Blob->buf + Blob->BufUsed, pos, nBytes);
3367                         Blob->BufUsed += nBytes;
3368                         Blob->buf[Blob->BufUsed] = '\0';
3369                         if (rlen == nBytes) {
3370                                 *Pos = NULL; 
3371                                 FlushStrBuf(IOBuf);
3372                         }
3373                         else 
3374                                 *Pos += nBytes;
3375                         return nBytes;
3376                 }
3377         }
3378
3379         FlushStrBuf(IOBuf);
3380         *Pos = NULL;
3381         if (IOBuf->BufSize < nBytes - nRead)
3382                 IncreaseBuf(IOBuf, 0, nBytes - nRead);
3383         ptr = IOBuf->buf;
3384
3385         slen = len = Blob->BufUsed;
3386
3387         fdflags = fcntl(*fd, F_GETFL);
3388         IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
3389
3390         SelRes = 1;
3391         nBytes -= nRead;
3392         nRead = 0;
3393         while ((nRead < nBytes) &&
3394                (*fd != -1)) {
3395                 if (IsNonBlock)
3396                 {
3397                         tv.tv_sec = 1;
3398                         tv.tv_usec = 0;
3399                 
3400                         FD_ZERO(&rfds);
3401                         FD_SET(*fd, &rfds);
3402                         if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
3403                                 *Error = strerror(errno);
3404                                 close (*fd);
3405                                 *fd = -1;
3406                                 if (*Error == NULL)
3407                                         *Error = ErrRBLF_SelectFailed;
3408                                 return -1;
3409                         }
3410                         if (! FD_ISSET(*fd, &rfds) != 0) {
3411                                 nSuccessLess ++;
3412                                 continue;
3413                         }
3414                 }
3415                 nSuccessLess = 0;
3416                 rlen = read(*fd, 
3417                             ptr,
3418                             nBytes - nRead);
3419                 if (rlen == -1) {
3420                         close(*fd);
3421                         *fd = -1;
3422                         *Error = strerror(errno);
3423                         return rlen;
3424                 }
3425                 else if (rlen == 0){
3426                         nSuccessLess ++;
3427                         if ((check == NNN_TERM) && 
3428                             (nRead > 5) &&
3429                             (strncmp(IOBuf->buf + IOBuf->BufUsed - 5, "\n000\n", 5) == 0)) 
3430                         {
3431                                 StrBufPlain(Blob, HKEY("\n000\n"));
3432                                 StrBufCutRight(Blob, 5);
3433                                 return Blob->BufUsed;
3434                         }
3435                         if (nSelects > 10) {
3436                                 FlushStrBuf(IOBuf);
3437                                 *Error = ErrRBB_too_many_selects;
3438                                 return -1;
3439                         }
3440                 }
3441                 else if (rlen > 0) {
3442                         nRead += rlen;
3443                         ptr += rlen;
3444                         IOBuf->BufUsed += rlen;
3445                 }
3446         }
3447         if (nRead > nBytes) {
3448                 *Pos = IOBuf->buf + nBytes;
3449         }
3450         Blob->buf[Blob->BufUsed] = '\0';
3451         StrBufAppendBufPlain(Blob, IOBuf->buf, nBytes, 0);
3452         if (*Pos == NULL) {
3453                 FlushStrBuf(IOBuf);
3454         }
3455         return nRead + nAlreadyRead;
3456 }
3457
3458 /**
3459  * @ingroup StrBuf_IO
3460  * @brief extract a "next line" from Buf; Ptr to persist across several iterations
3461  * @param LineBuf your line will be copied here.
3462  * @param Buf BLOB with lines of text...
3463  * @param Ptr moved arround to keep the next-line across several iterations
3464  *        has to be &NULL on start; will be &NotNULL on end of buffer
3465  * @returns size of copied buffer
3466  */
3467 int StrBufSipLine(StrBuf *LineBuf, StrBuf *Buf, const char **Ptr)
3468 {
3469         const char *aptr, *ptr, *eptr;
3470         char *optr, *xptr;
3471
3472         if ((Buf == NULL) || (*Ptr == StrBufNOTNULL)) {
3473                 *Ptr = StrBufNOTNULL;
3474                 return 0;
3475         }
3476
3477         FlushStrBuf(LineBuf);
3478         if (*Ptr==NULL)
3479                 ptr = aptr = Buf->buf;
3480         else
3481                 ptr = aptr = *Ptr;
3482
3483         optr = LineBuf->buf;
3484         eptr = Buf->buf + Buf->BufUsed;
3485         xptr = LineBuf->buf + LineBuf->BufSize - 1;
3486
3487         while ((ptr <= eptr) && 
3488                (*ptr != '\n') &&
3489                (*ptr != '\r') )
3490         {
3491                 *optr = *ptr;
3492                 optr++; ptr++;
3493                 if (optr == xptr) {
3494                         LineBuf->BufUsed = optr - LineBuf->buf;
3495                         IncreaseBuf(LineBuf,  1, LineBuf->BufUsed + 1);
3496                         optr = LineBuf->buf + LineBuf->BufUsed;
3497                         xptr = LineBuf->buf + LineBuf->BufSize - 1;
3498                 }
3499         }
3500
3501         if ((ptr >= eptr) && (optr > LineBuf->buf))
3502                 optr --;
3503         LineBuf->BufUsed = optr - LineBuf->buf;
3504         *optr = '\0';       
3505         if ((ptr <= eptr) && (*ptr == '\r'))
3506                 ptr ++;
3507         if ((ptr <= eptr) && (*ptr == '\n'))
3508                 ptr ++;
3509         
3510         if (ptr < eptr) {
3511                 *Ptr = ptr;
3512         }
3513         else {
3514                 *Ptr = StrBufNOTNULL;
3515         }
3516
3517         return Buf->BufUsed - (ptr - Buf->buf);
3518 }
3519
3520
3521 /**
3522  * @ingroup StrBuf_IO
3523  * @brief removes double slashes from pathnames
3524  * @param Dir directory string to filter
3525  * @param RemoveTrailingSlash allows / disallows trailing slashes
3526  */
3527 void StrBufStripSlashes(StrBuf *Dir, int RemoveTrailingSlash)
3528 {
3529         char *a, *b;
3530
3531         a = b = Dir->buf;
3532
3533         while (!IsEmptyStr(a)) {
3534                 if (*a == '/') {
3535                         while (*a == '/')
3536                                 a++;
3537                         *b = '/';
3538                         b++;
3539                 }
3540                 else {
3541                         *b = *a;
3542                         b++; a++;
3543                 }
3544         }
3545         if ((RemoveTrailingSlash) && (*(b - 1) != '/')){
3546                 *b = '/';
3547                 b++;
3548         }
3549         *b = '\0';
3550         Dir->BufUsed = b - Dir->buf;
3551 }
3552
3553