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