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