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