]> code.citadel.org Git - citadel.git/blob - libcitadel/lib/stringbuf.c
* fix (ab) qp decode test
[citadel.git] / libcitadel / lib / stringbuf.c
1 #include "../sysdep.h"
2 #include <ctype.h>
3 #include <errno.h>
4 #include <string.h>
5 #include <unistd.h>
6 #include <string.h>
7 #include <stdio.h>
8 #include <sys/select.h>
9 #include <fcntl.h>
10 #define SHOW_ME_VAPPEND_PRINTF
11 #include <stdarg.h>
12 #include "libcitadel.h"
13
14 #ifdef HAVE_ICONV
15 #include <iconv.h>
16 #endif
17
18 #ifdef HAVE_ZLIB
19 #include <zlib.h>
20 #endif
21
22
23 #ifdef HAVE_ZLIB
24 #include <zlib.h>
25 int ZEXPORT compress_gzip(Bytef * dest, size_t * destLen,
26                           const Bytef * source, uLong sourceLen, int level);
27 #endif
28
29 /**
30  * Private Structure for the Stringbuffer
31  */
32 struct StrBuf {
33         char *buf;         /**< the pointer to the dynamic buffer */
34         long BufSize;      /**< how many spcae do we optain */
35         long BufUsed;      /**< Number of Chars used excluding the trailing \0 */
36         int ConstBuf;      /**< are we just a wrapper arround a static buffer and musn't we be changed? */
37 };
38
39
40 /** 
41  * \Brief Cast operator to Plain String 
42  * Note: if the buffer is altered by StrBuf operations, this pointer may become 
43  *  invalid. So don't lean on it after altering the buffer!
44  *  Since this operation is considered cheap, rather call it often than risking
45  *  your pointer to become invalid!
46  * \param Str the string we want to get the c-string representation for
47  * \returns the Pointer to the Content. Don't mess with it!
48  */
49 inline const char *ChrPtr(const StrBuf *Str)
50 {
51         if (Str == NULL)
52                 return "";
53         return Str->buf;
54 }
55
56 /**
57  * \brief since we know strlen()'s result, provide it here.
58  * \param Str the string to return the length to
59  * \returns contentlength of the buffer
60  */
61 inline int StrLength(const StrBuf *Str)
62 {
63         return (Str != NULL) ? Str->BufUsed : 0;
64 }
65
66 /**
67  * \brief local utility function to resize the buffer
68  * \param Buf the buffer whichs storage we should increase
69  * \param KeepOriginal should we copy the original buffer or just start over with a new one
70  * \param DestSize what should fit in after?
71  */
72 static int IncreaseBuf(StrBuf *Buf, int KeepOriginal, int DestSize)
73 {
74         char *NewBuf;
75         size_t NewSize = Buf->BufSize * 2;
76
77         if (Buf->ConstBuf)
78                 return -1;
79                 
80         if (DestSize > 0)
81                 while (NewSize < DestSize)
82                         NewSize *= 2;
83
84         NewBuf= (char*) malloc(NewSize);
85         if (KeepOriginal && (Buf->BufUsed > 0))
86         {
87                 memcpy(NewBuf, Buf->buf, Buf->BufUsed);
88         }
89         else
90         {
91                 NewBuf[0] = '\0';
92                 Buf->BufUsed = 0;
93         }
94         free (Buf->buf);
95         Buf->buf = NewBuf;
96         Buf->BufSize *= 2;
97         return Buf->BufSize;
98 }
99
100 /**
101  * Allocate a new buffer with default buffer size
102  * \returns the new stringbuffer
103  */
104 StrBuf* NewStrBuf(void)
105 {
106         StrBuf *NewBuf;
107
108         NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
109         NewBuf->buf = (char*) malloc(SIZ);
110         NewBuf->buf[0] = '\0';
111         NewBuf->BufSize = SIZ;
112         NewBuf->BufUsed = 0;
113         NewBuf->ConstBuf = 0;
114         return NewBuf;
115 }
116
117 /** 
118  * \brief Copy Constructor; returns a duplicate of CopyMe
119  * \params CopyMe Buffer to faxmilate
120  * \returns the new stringbuffer
121  */
122 StrBuf* NewStrBufDup(const StrBuf *CopyMe)
123 {
124         StrBuf *NewBuf;
125         
126         if (CopyMe == NULL)
127                 return NewStrBuf();
128
129         NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
130         NewBuf->buf = (char*) malloc(CopyMe->BufSize);
131         memcpy(NewBuf->buf, CopyMe->buf, CopyMe->BufUsed + 1);
132         NewBuf->BufUsed = CopyMe->BufUsed;
133         NewBuf->BufSize = CopyMe->BufSize;
134         NewBuf->ConstBuf = 0;
135         return NewBuf;
136 }
137
138 /**
139  * \brief create a new Buffer using an existing c-string
140  * this function should also be used if you want to pre-suggest
141  * the buffer size to allocate in conjunction with ptr == NULL
142  * \param ptr the c-string to copy; may be NULL to create a blank instance
143  * \param nChars How many chars should we copy; -1 if we should measure the length ourselves
144  * \returns the new stringbuffer
145  */
146 StrBuf* NewStrBufPlain(const char* ptr, int nChars)
147 {
148         StrBuf *NewBuf;
149         size_t Siz = SIZ;
150         size_t CopySize;
151
152         NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
153         if (nChars < 0)
154                 CopySize = strlen((ptr != NULL)?ptr:"");
155         else
156                 CopySize = nChars;
157
158         while (Siz <= CopySize)
159                 Siz *= 2;
160
161         NewBuf->buf = (char*) malloc(Siz);
162         NewBuf->BufSize = Siz;
163         if (ptr != NULL) {
164                 memcpy(NewBuf->buf, ptr, CopySize);
165                 NewBuf->buf[CopySize] = '\0';
166                 NewBuf->BufUsed = CopySize;
167         }
168         else {
169                 NewBuf->buf[0] = '\0';
170                 NewBuf->BufUsed = 0;
171         }
172         NewBuf->ConstBuf = 0;
173         return NewBuf;
174 }
175
176 /**
177  * \brief Set an existing buffer from a c-string
178  * \param ptr c-string to put into 
179  * \param nChars set to -1 if we should work 0-terminated
180  * \returns the new length of the string
181  */
182 int StrBufPlain(StrBuf *Buf, const char* ptr, int nChars)
183 {
184         size_t Siz = Buf->BufSize;
185         size_t CopySize;
186
187         if (nChars < 0)
188                 CopySize = strlen(ptr);
189         else
190                 CopySize = nChars;
191
192         while (Siz <= CopySize)
193                 Siz *= 2;
194
195         if (Siz != Buf->BufSize)
196                 IncreaseBuf(Buf, 0, Siz);
197         memcpy(Buf->buf, ptr, CopySize);
198         Buf->buf[CopySize] = '\0';
199         Buf->BufUsed = CopySize;
200         Buf->ConstBuf = 0;
201         return CopySize;
202 }
203
204
205 /**
206  * \brief use strbuf as wrapper for a string constant for easy handling
207  * \param StringConstant a string to wrap
208  * \param SizeOfConstant should be sizeof(StringConstant)-1
209  */
210 StrBuf* _NewConstStrBuf(const char* StringConstant, size_t SizeOfStrConstant)
211 {
212         StrBuf *NewBuf;
213
214         NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
215         NewBuf->buf = (char*) StringConstant;
216         NewBuf->BufSize = SizeOfStrConstant;
217         NewBuf->BufUsed = SizeOfStrConstant;
218         NewBuf->ConstBuf = 1;
219         return NewBuf;
220 }
221
222
223 /**
224  * \brief flush the content of a Buf; keep its struct
225  * \param buf Buffer to flush
226  */
227 int FlushStrBuf(StrBuf *buf)
228 {
229         if (buf == NULL)
230                 return -1;
231         if (buf->ConstBuf)
232                 return -1;       
233         buf->buf[0] ='\0';
234         buf->BufUsed = 0;
235         return 0;
236 }
237
238 /**
239  * \brief Release a Buffer
240  * Its a double pointer, so it can NULL your pointer
241  * so fancy SIG11 appear instead of random results
242  * \param FreeMe Pointer Pointer to the buffer to free
243  */
244 void FreeStrBuf (StrBuf **FreeMe)
245 {
246         if (*FreeMe == NULL)
247                 return;
248         if (!(*FreeMe)->ConstBuf) 
249                 free((*FreeMe)->buf);
250         free(*FreeMe);
251         *FreeMe = NULL;
252 }
253
254 /**
255  * \brief Release the buffer
256  * If you want put your StrBuf into a Hash, use this as Destructor.
257  * \param VFreeMe untyped pointer to a StrBuf. be shure to do the right thing [TM]
258  */
259 void HFreeStrBuf (void *VFreeMe)
260 {
261         StrBuf *FreeMe = (StrBuf*)VFreeMe;
262         if (FreeMe == NULL)
263                 return;
264         if (!FreeMe->ConstBuf) 
265                 free(FreeMe->buf);
266         free(FreeMe);
267 }
268
269 /**
270  * \brief Wrapper around atol
271  */
272 long StrTol(const StrBuf *Buf)
273 {
274         if(Buf->BufUsed > 0)
275                 return atol(Buf->buf);
276         else
277                 return 0;
278 }
279
280 /**
281  * \brief Wrapper around atoi
282  */
283 int StrToi(const StrBuf *Buf)
284 {
285         if(Buf->BufUsed > 0)
286                 return atoi(Buf->buf);
287         else
288                 return 0;
289 }
290
291 /**
292  * \brief modifies a Single char of the Buf
293  * You can point to it via char* or a zero-based integer
294  * \param ptr char* to zero; use NULL if unused
295  * \param nThChar zero based pointer into the string; use -1 if unused
296  * \param PeekValue The Character to place into the position
297  */
298 long StrBufPeek(StrBuf *Buf, const char* ptr, long nThChar, char PeekValue)
299 {
300         if (Buf == NULL)
301                 return -1;
302         if (ptr != NULL)
303                 nThChar = ptr - Buf->buf;
304         if ((nThChar < 0) || (nThChar > Buf->BufUsed))
305                 return -1;
306         Buf->buf[nThChar] = PeekValue;
307         return nThChar;
308 }
309
310 /**
311  * \brief Append a StringBuffer to the buffer
312  * \param Buf Buffer to modify
313  * \param AppendBuf Buffer to copy at the end of our buffer
314  * \param Offset Should we start copying from an offset?
315  */
316 void StrBufAppendBuf(StrBuf *Buf, const StrBuf *AppendBuf, size_t Offset)
317 {
318         if ((AppendBuf == NULL) || (Buf == NULL))
319                 return;
320
321         if (Buf->BufSize - Offset < AppendBuf->BufUsed + Buf->BufUsed)
322                 IncreaseBuf(Buf, 
323                             (Buf->BufUsed > 0), 
324                             AppendBuf->BufUsed + Buf->BufUsed);
325
326         memcpy(Buf->buf + Buf->BufUsed, 
327                AppendBuf->buf + Offset, 
328                AppendBuf->BufUsed - Offset);
329         Buf->BufUsed += AppendBuf->BufUsed - Offset;
330         Buf->buf[Buf->BufUsed] = '\0';
331 }
332
333
334 /**
335  * \brief Append a C-String to the buffer
336  * \param Buf Buffer to modify
337  * \param AppendBuf Buffer to copy at the end of our buffer
338  * \param AppendSize number of bytes to copy; set to -1 if we should count it in advance
339  * \param Offset Should we start copying from an offset?
340  */
341 void StrBufAppendBufPlain(StrBuf *Buf, const char *AppendBuf, long AppendSize, size_t Offset)
342 {
343         long aps;
344
345         if ((AppendBuf == NULL) || (Buf == NULL))
346                 return;
347
348         if (AppendSize < 0 )
349                 aps = strlen(AppendBuf + Offset);
350         else
351                 aps = AppendSize - Offset;
352
353         if (Buf->BufSize < Buf->BufUsed + aps)
354                 IncreaseBuf(Buf, (Buf->BufUsed > 0), Buf->BufUsed + aps);
355
356         memcpy(Buf->buf + Buf->BufUsed, 
357                AppendBuf + Offset, 
358                aps);
359         Buf->BufUsed += aps;
360         Buf->buf[Buf->BufUsed] = '\0';
361 }
362
363
364 /** 
365  * \brief Escape a string for feeding out as a URL while appending it to a Buffer
366  * \param outbuf the output buffer
367  * \param oblen the size of outbuf to sanitize
368  * \param strbuf the input buffer
369  */
370 void StrBufUrlescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
371 {
372         const char *pch, *pche;
373         char *pt, *pte;
374         int b, c, len;
375         const char ec[] = " +#&;`'|*?-~<>^()[]{}/$\"\\";
376         int eclen = sizeof(ec) -1;
377
378         if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
379                 return;
380         if (PlainIn != NULL) {
381                 len = strlen(PlainIn);
382                 pch = PlainIn;
383                 pche = pch + len;
384         }
385         else {
386                 pch = In->buf;
387                 pche = pch + In->BufUsed;
388                 len = In->BufUsed;
389         }
390
391         if (len == 0) 
392                 return;
393
394         pt = OutBuf->buf + OutBuf->BufUsed;
395         pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
396
397         while (pch < pche) {
398                 if (pt >= pte) {
399                         IncreaseBuf(OutBuf, 1, -1);
400                         pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
401                         pt = OutBuf->buf + OutBuf->BufUsed;
402                 }
403                 
404                 c = 0;
405                 for (b = 0; b < eclen; ++b) {
406                         if (*pch == ec[b]) {
407                                 c = 1;
408                                 b += eclen;
409                         }
410                 }
411                 if (c == 1) {
412                         sprintf(pt,"%%%02X", *pch);
413                         pt += 3;
414                         OutBuf->BufUsed += 3;
415                         pch ++;
416                 }
417                 else {
418                         *(pt++) = *(pch++);
419                         OutBuf->BufUsed++;
420                 }
421         }
422         *pt = '\0';
423 }
424
425 /*
426  * \brief Append a string, escaping characters which have meaning in HTML.  
427  *
428  * \param Target        target buffer
429  * \param Source        source buffer; set to NULL if you just have a C-String
430  * \param PlainIn       Plain-C string to append; set to NULL if unused
431  * \param nbsp          If nonzero, spaces are converted to non-breaking spaces.
432  * \param nolinebreaks  if set, linebreaks are removed from the string.
433  */
434 long StrEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
435 {
436         const char *aptr, *eiptr;
437         char *bptr, *eptr;
438         long len;
439
440         if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
441                 return -1;
442
443         if (PlainIn != NULL) {
444                 aptr = PlainIn;
445                 len = strlen(PlainIn);
446                 eiptr = aptr + len;
447         }
448         else {
449                 aptr = Source->buf;
450                 eiptr = aptr + Source->BufUsed;
451                 len = Source->BufUsed;
452         }
453
454         if (len == 0) 
455                 return -1;
456
457         bptr = Target->buf + Target->BufUsed;
458         eptr = Target->buf + Target->BufSize - 6; /* our biggest unit to put in...  */
459
460         while (aptr < eiptr){
461                 if(bptr >= eptr) {
462                         IncreaseBuf(Target, 1, -1);
463                         eptr = Target->buf + Target->BufSize - 6; 
464                         bptr = Target->buf + Target->BufUsed;
465                 }
466                 if (*aptr == '<') {
467                         memcpy(bptr, "&lt;", 4);
468                         bptr += 4;
469                         Target->BufUsed += 4;
470                 }
471                 else if (*aptr == '>') {
472                         memcpy(bptr, "&gt;", 4);
473                         bptr += 4;
474                         Target->BufUsed += 4;
475                 }
476                 else if (*aptr == '&') {
477                         memcpy(bptr, "&amp;", 5);
478                         bptr += 5;
479                         Target->BufUsed += 5;
480                 }
481                 else if (*aptr == '\"') {
482                         memcpy(bptr, "&quot;", 6);
483                         bptr += 6;
484                         Target->BufUsed += 6;
485                 }
486                 else if (*aptr == '\'') {
487                         memcpy(bptr, "&#39;", 5);
488                         bptr += 5;
489                         Target->BufUsed += 5;
490                 }
491                 else if (*aptr == LB) {
492                         *bptr = '<';
493                         bptr ++;
494                         Target->BufUsed ++;
495                 }
496                 else if (*aptr == RB) {
497                         *bptr = '>';
498                         bptr ++;
499                         Target->BufUsed ++;
500                 }
501                 else if (*aptr == QU) {
502                         *bptr ='"';
503                         bptr ++;
504                         Target->BufUsed ++;
505                 }
506                 else if ((*aptr == 32) && (nbsp == 1)) {
507                         memcpy(bptr, "&nbsp;", 6);
508                         bptr += 6;
509                         Target->BufUsed += 6;
510                 }
511                 else if ((*aptr == '\n') && (nolinebreaks)) {
512                         *bptr='\0';     /* nothing */
513                 }
514                 else if ((*aptr == '\r') && (nolinebreaks)) {
515                         *bptr='\0';     /* nothing */
516                 }
517                 else{
518                         *bptr = *aptr;
519                         bptr++;
520                         Target->BufUsed ++;
521                 }
522                 aptr ++;
523         }
524         *bptr = '\0';
525         if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
526                 return -1;
527         return Target->BufUsed;
528 }
529
530 /*
531  * \brief Append a string, escaping characters which have meaning in HTML.  
532  * Converts linebreaks into blanks; escapes single quotes
533  * \param Target        target buffer
534  * \param Source        source buffer; set to NULL if you just have a C-String
535  * \param PlainIn       Plain-C string to append; set to NULL if unused
536  */
537 void StrMsgEscAppend(StrBuf *Target, StrBuf *Source, const char *PlainIn)
538 {
539         const char *aptr, *eiptr;
540         char *tptr, *eptr;
541         long len;
542
543         if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
544                 return ;
545
546         if (PlainIn != NULL) {
547                 aptr = PlainIn;
548                 len = strlen(PlainIn);
549                 eiptr = aptr + len;
550         }
551         else {
552                 aptr = Source->buf;
553                 eiptr = aptr + Source->BufUsed;
554                 len = Source->BufUsed;
555         }
556
557         if (len == 0) 
558                 return;
559
560         eptr = Target->buf + Target->BufSize - 6; 
561         tptr = Target->buf + Target->BufUsed;
562         
563         while (aptr < eiptr){
564                 if(tptr >= eptr) {
565                         IncreaseBuf(Target, 1, -1);
566                         eptr = Target->buf + Target->BufSize - 6; 
567                         tptr = Target->buf + Target->BufUsed;
568                 }
569                
570                 if (*aptr == '\n') {
571                         *tptr = ' ';
572                         Target->BufUsed++;
573                 }
574                 else if (*aptr == '\r') {
575                         *tptr = ' ';
576                         Target->BufUsed++;
577                 }
578                 else if (*aptr == '\'') {
579                         *(tptr++) = '&';
580                         *(tptr++) = '#';
581                         *(tptr++) = '3';
582                         *(tptr++) = '9';
583                         *tptr = ';';
584                         Target->BufUsed += 5;
585                 } else {
586                         *tptr = *aptr;
587                         Target->BufUsed++;
588                 }
589                 tptr++; aptr++;
590         }
591         *tptr = '\0';
592 }
593
594
595 /**
596  * \brief extracts a substring from Source into dest
597  * \param dest buffer to place substring into
598  * \param Source string to copy substring from
599  * \param Offset chars to skip from start
600  * \param nChars number of chars to copy
601  * \returns the number of chars copied; may be different from nChars due to the size of Source
602  */
603 int StrBufSub(StrBuf *dest, const StrBuf *Source, size_t Offset, size_t nChars)
604 {
605         size_t NCharsRemain;
606         if (Offset > Source->BufUsed)
607         {
608                 FlushStrBuf(dest);
609                 return 0;
610         }
611         if (Offset + nChars < Source->BufUsed)
612         {
613                 if (nChars > dest->BufSize)
614                         IncreaseBuf(dest, 0, nChars + 1);
615                 memcpy(dest->buf, Source->buf + Offset, nChars);
616                 dest->BufUsed = nChars;
617                 dest->buf[dest->BufUsed] = '\0';
618                 return nChars;
619         }
620         NCharsRemain = Source->BufUsed - Offset;
621         if (NCharsRemain > dest->BufSize)
622                 IncreaseBuf(dest, 0, NCharsRemain + 1);
623         memcpy(dest->buf, Source->buf + Offset, NCharsRemain);
624         dest->BufUsed = NCharsRemain;
625         dest->buf[dest->BufUsed] = '\0';
626         return NCharsRemain;
627 }
628
629 /**
630  * \brief sprintf like function appending the formated string to the buffer
631  * vsnprintf version to wrap into own calls
632  * \param Buf Buffer to extend by format and params
633  * \param format printf alike format to add
634  * \param ap va_list containing the items for format
635  */
636 void StrBufVAppendPrintf(StrBuf *Buf, const char *format, va_list ap)
637 {
638         va_list apl;
639         size_t BufSize = Buf->BufSize;
640         size_t nWritten = Buf->BufSize + 1;
641         size_t Offset = Buf->BufUsed;
642         size_t newused = Offset + nWritten;
643         
644         while (newused >= BufSize) {
645                 va_copy(apl, ap);
646                 nWritten = vsnprintf(Buf->buf + Offset, 
647                                      Buf->BufSize - Offset, 
648                                      format, apl);
649                 va_end(apl);
650                 newused = Offset + nWritten;
651                 if (newused >= Buf->BufSize) {
652                         IncreaseBuf(Buf, 1, newused);
653                 }
654                 else {
655                         Buf->BufUsed = Offset + nWritten;
656                         BufSize = Buf->BufSize;
657                 }
658
659         }
660 }
661
662 /**
663  * \brief sprintf like function appending the formated string to the buffer
664  * \param Buf Buffer to extend by format and params
665  * \param format printf alike format to add
666  * \param ap va_list containing the items for format
667  */
668 void StrBufAppendPrintf(StrBuf *Buf, const char *format, ...)
669 {
670         size_t BufSize = Buf->BufSize;
671         size_t nWritten = Buf->BufSize + 1;
672         size_t Offset = Buf->BufUsed;
673         size_t newused = Offset + nWritten;
674         va_list arg_ptr;
675         
676         while (newused >= BufSize) {
677                 va_start(arg_ptr, format);
678                 nWritten = vsnprintf(Buf->buf + Buf->BufUsed, 
679                                      Buf->BufSize - Buf->BufUsed, 
680                                      format, arg_ptr);
681                 va_end(arg_ptr);
682                 newused = Buf->BufUsed + nWritten;
683                 if (newused >= Buf->BufSize) {
684                         IncreaseBuf(Buf, 1, newused);
685                 }
686                 else {
687                         Buf->BufUsed += nWritten;
688                         BufSize = Buf->BufSize;
689                 }
690
691         }
692 }
693
694 /**
695  * \brief sprintf like function putting the formated string into the buffer
696  * \param Buf Buffer to extend by format and params
697  * \param format printf alike format to add
698  * \param ap va_list containing the items for format
699  */
700 void StrBufPrintf(StrBuf *Buf, const char *format, ...)
701 {
702         size_t nWritten = Buf->BufSize + 1;
703         va_list arg_ptr;
704         
705         while (nWritten >= Buf->BufSize) {
706                 va_start(arg_ptr, format);
707                 nWritten = vsnprintf(Buf->buf, Buf->BufSize, format, arg_ptr);
708                 va_end(arg_ptr);
709                 Buf->BufUsed = nWritten ;
710                 if (nWritten >= Buf->BufSize)
711                         IncreaseBuf(Buf, 0, 0);
712         }
713 }
714
715
716 /**
717  * \brief Counts the numbmer of tokens in a buffer
718  * \param Source String to count tokens in
719  * \param tok    Tokenizer char to count
720  * \returns numbers of tokenizer chars found
721  */
722 inline int StrBufNum_tokens(const StrBuf *source, char tok)
723 {
724         if (source == NULL)
725                 return 0;
726         return num_tokens(source->buf, tok);
727 }
728
729 /*
730  * remove_token() - a tokenizer that kills, maims, and destroys
731  */
732 /**
733  * \brief a string tokenizer
734  * \param Source StringBuffer to read into
735  * \param parmnum n'th parameter to remove
736  * \param separator tokenizer param
737  * \returns -1 if not found, else length of token.
738  */
739 int StrBufRemove_token(StrBuf *Source, int parmnum, char separator)
740 {
741         int ReducedBy;
742         char *d, *s;            /* dest, source */
743         int count = 0;
744
745         /* Find desired parameter */
746         d = Source->buf;
747         while (count < parmnum) {
748                 /* End of string, bail! */
749                 if (!*d) {
750                         d = NULL;
751                         break;
752                 }
753                 if (*d == separator) {
754                         count++;
755                 }
756                 d++;
757         }
758         if (!d) return 0;               /* Parameter not found */
759
760         /* Find next parameter */
761         s = d;
762         while (*s && *s != separator) {
763                 s++;
764         }
765
766         ReducedBy = d - s;
767
768         /* Hack and slash */
769         if (*s) {
770                 memmove(d, s, Source->BufUsed - (s - Source->buf));
771                 Source->BufUsed -= (ReducedBy + 1);
772         }
773         else if (d == Source->buf) {
774                 *d = 0;
775                 Source->BufUsed = 0;
776         }
777         else {
778                 *--d = 0;
779                 Source->BufUsed -= (ReducedBy + 1);
780         }
781         /*
782         while (*s) {
783                 *d++ = *s++;
784         }
785         *d = 0;
786         */
787         return ReducedBy;
788 }
789
790
791 /**
792  * \brief a string tokenizer
793  * \param dest Destination StringBuffer
794  * \param Source StringBuffer to read into
795  * \param parmnum n'th parameter to extract
796  * \param separator tokenizer param
797  * \returns -1 if not found, else length of token.
798  */
799 int StrBufExtract_token(StrBuf *dest, const StrBuf *Source, int parmnum, char separator)
800 {
801         const char *s, *e;              //* source * /
802         int len = 0;                    //* running total length of extracted string * /
803         int current_token = 0;          //* token currently being processed * /
804
805         if ((Source == NULL) || (Source->BufUsed ==0)) {
806                 return(-1);
807         }
808         s = Source->buf;
809         e = s + Source->BufUsed;
810         if (dest == NULL) {
811                 return(-1);
812         }
813
814         //cit_backtrace();
815         //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
816         dest->buf[0] = '\0';
817         dest->BufUsed = 0;
818
819         while ((s<e) && !IsEmptyStr(s)) {
820                 if (*s == separator) {
821                         ++current_token;
822                 }
823                 if (len >= dest->BufSize)
824                         if (!IncreaseBuf(dest, 1, -1))
825                                 break;
826                 if ( (current_token == parmnum) && 
827                      (*s != separator)) {
828                         dest->buf[len] = *s;
829                         ++len;
830                 }
831                 else if (current_token > parmnum) {
832                         break;
833                 }
834                 ++s;
835         }
836         
837         dest->buf[len] = '\0';
838         dest->BufUsed = len;
839                 
840         if (current_token < parmnum) {
841                 //lprintf (CTDL_DEBUG,"test <!: %s\n", dest);
842                 return(-1);
843         }
844         //lprintf (CTDL_DEBUG,"test <: %d; %s\n", len, dest);
845         return(len);
846 }
847
848
849 /**
850  * \brief a string tokenizer to fetch an integer
851  * \param dest Destination StringBuffer
852  * \param parmnum n'th parameter to extract
853  * \param separator tokenizer param
854  * \returns 0 if not found, else integer representation of the token
855  */
856 int StrBufExtract_int(const StrBuf* Source, int parmnum, char separator)
857 {
858         StrBuf tmp;
859         char buf[64];
860         
861         tmp.buf = buf;
862         buf[0] = '\0';
863         tmp.BufSize = 64;
864         tmp.BufUsed = 0;
865         if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
866                 return(atoi(buf));
867         else
868                 return 0;
869 }
870
871 /**
872  * \brief a string tokenizer to fetch a long integer
873  * \param dest Destination StringBuffer
874  * \param parmnum n'th parameter to extract
875  * \param separator tokenizer param
876  * \returns 0 if not found, else long integer representation of the token
877  */
878 long StrBufExtract_long(const StrBuf* Source, int parmnum, char separator)
879 {
880         StrBuf tmp;
881         char buf[64];
882         
883         tmp.buf = buf;
884         buf[0] = '\0';
885         tmp.BufSize = 64;
886         tmp.BufUsed = 0;
887         if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
888                 return(atoi(buf));
889         else
890                 return 0;
891 }
892
893
894 /**
895  * \brief a string tokenizer to fetch an unsigned long
896  * \param dest Destination StringBuffer
897  * \param parmnum n'th parameter to extract
898  * \param separator tokenizer param
899  * \returns 0 if not found, else unsigned long representation of the token
900  */
901 unsigned long StrBufExtract_unsigned_long(const StrBuf* Source, int parmnum, char separator)
902 {
903         StrBuf tmp;
904         char buf[64];
905         char *pnum;
906         
907         tmp.buf = buf;
908         buf[0] = '\0';
909         tmp.BufSize = 64;
910         tmp.BufUsed = 0;
911         if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0) {
912                 pnum = &buf[0];
913                 if (*pnum == '-')
914                         pnum ++;
915                 return (unsigned long) atol(pnum);
916         }
917         else 
918                 return 0;
919 }
920
921
922
923 /**
924  * \brief Read a line from socket
925  * flushes and closes the FD on error
926  * \param buf the buffer to get the input to
927  * \param fd pointer to the filedescriptor to read
928  * \param append Append to an existing string or replace?
929  * \param Error strerror() on error 
930  * \returns numbers of chars read
931  */
932 int StrBufTCP_read_line(StrBuf *buf, int *fd, int append, const char **Error)
933 {
934         int len, rlen, slen;
935
936         if (!append)
937                 FlushStrBuf(buf);
938
939         slen = len = buf->BufUsed;
940         while (1) {
941                 rlen = read(*fd, &buf->buf[len], 1);
942                 if (rlen < 1) {
943                         *Error = strerror(errno);
944                         
945                         close(*fd);
946                         *fd = -1;
947                         
948                         return -1;
949                 }
950                 if (buf->buf[len] == '\n')
951                         break;
952                 if (buf->buf[len] != '\r')
953                         len ++;
954                 if (!(len < buf->BufSize)) {
955                         buf->BufUsed = len;
956                         buf->buf[len+1] = '\0';
957                         IncreaseBuf(buf, 1, -1);
958                 }
959         }
960         buf->BufUsed = len;
961         buf->buf[len] = '\0';
962         return len - slen;
963 }
964
965 /**
966  * \brief Read a line from socket
967  * flushes and closes the FD on error
968  * \param buf the buffer to get the input to
969  * \param fd pointer to the filedescriptor to read
970  * \param append Append to an existing string or replace?
971  * \param Error strerror() on error 
972  * \returns numbers of chars read
973  */
974 int StrBufTCP_read_buffered_line(StrBuf *Line, 
975                                  StrBuf *buf, 
976                                  int *fd, 
977                                  int timeout, 
978                                  int selectresolution, 
979                                  const char **Error)
980 {
981         int len, rlen;
982         int nSuccessLess = 0;
983         fd_set rfds;
984         char *pch = NULL;
985         int fdflags;
986         struct timeval tv;
987
988         if (buf->BufUsed > 0) {
989                 pch = strchr(buf->buf, '\n');
990                 if (pch != NULL) {
991                         rlen = 0;
992                         len = pch - buf->buf;
993                         if (len > 0 && (*(pch - 1) == '\r') )
994                                 rlen ++;
995                         StrBufSub(Line, buf, 0, len - rlen);
996                         StrBufCutLeft(buf, len + 1);
997                         return len - rlen;
998                 }
999         }
1000         
1001         if (buf->BufSize - buf->BufUsed < 10)
1002                 IncreaseBuf(buf, 1, -1);
1003
1004         fdflags = fcntl(*fd, F_GETFL);
1005         if ((fdflags & O_NONBLOCK) == O_NONBLOCK)
1006                 return -1;
1007
1008         while ((nSuccessLess < timeout) && (pch == NULL)) {
1009                 tv.tv_sec = selectresolution;
1010                 tv.tv_usec = 0;
1011                 
1012                 FD_ZERO(&rfds);
1013                 FD_SET(*fd, &rfds);
1014                 if (select(*fd + 1, NULL, &rfds, NULL, &tv) == -1) {
1015                         *Error = strerror(errno);
1016                         close (*fd);
1017                         *fd = -1;
1018                         return -1;
1019                 }               
1020                 if (FD_ISSET(*fd, &rfds)) {
1021                         rlen = read(*fd, 
1022                                     &buf->buf[buf->BufUsed], 
1023                                     buf->BufSize - buf->BufUsed - 1);
1024                         if (rlen < 1) {
1025                                 *Error = strerror(errno);
1026                                 close(*fd);
1027                                 *fd = -1;
1028                                 return -1;
1029                         }
1030                         else if (rlen > 0) {
1031                                 nSuccessLess = 0;
1032                                 buf->BufUsed += rlen;
1033                                 buf->buf[buf->BufUsed] = '\0';
1034                                 if (buf->BufUsed + 10 > buf->BufSize) {
1035                                         IncreaseBuf(buf, 1, -1);
1036                                 }
1037                                 pch = strchr(buf->buf, '\n');
1038                                 continue;
1039                         }
1040                 }
1041                 nSuccessLess ++;
1042         }
1043         if (pch != NULL) {
1044                 rlen = 0;
1045                 len = pch - buf->buf;
1046                 if (len > 0 && (*(pch - 1) == '\r') )
1047                         rlen ++;
1048                 StrBufSub(Line, buf, 0, len - rlen);
1049                 StrBufCutLeft(buf, len + 1);
1050                 return len - rlen;
1051         }
1052         return -1;
1053
1054 }
1055
1056 /**
1057  * \brief Input binary data from socket
1058  * flushes and closes the FD on error
1059  * \param buf the buffer to get the input to
1060  * \param fd pointer to the filedescriptor to read
1061  * \param append Append to an existing string or replace?
1062  * \param nBytes the maximal number of bytes to read
1063  * \param Error strerror() on error 
1064  * \returns numbers of chars read
1065  */
1066 int StrBufReadBLOB(StrBuf *Buf, int *fd, int append, long nBytes, const char **Error)
1067 {
1068         fd_set wset;
1069         int fdflags;
1070         int len, rlen, slen;
1071         int nRead = 0;
1072         char *ptr;
1073
1074         if ((Buf == NULL) || (*fd == -1))
1075                 return -1;
1076         if (!append)
1077                 FlushStrBuf(Buf);
1078         if (Buf->BufUsed + nBytes > Buf->BufSize)
1079                 IncreaseBuf(Buf, 1, Buf->BufUsed + nBytes);
1080
1081         ptr = Buf->buf + Buf->BufUsed;
1082
1083         slen = len = Buf->BufUsed;
1084
1085         fdflags = fcntl(*fd, F_GETFL);
1086
1087         while (nRead < nBytes) {
1088                if ((fdflags & O_NONBLOCK) == O_NONBLOCK) {
1089                         FD_ZERO(&wset);
1090                         FD_SET(*fd, &wset);
1091                         if (select(*fd + 1, NULL, &wset, NULL, NULL) == -1) {
1092                                 *Error = strerror(errno);
1093                                 return -1;
1094                         }
1095                 }
1096
1097                 if ((rlen = read(*fd, 
1098                                  ptr,
1099                                  nBytes - nRead)) == -1) {
1100                         close(*fd);
1101                         *fd = -1;
1102                         *Error = strerror(errno);
1103                         return rlen;
1104                 }
1105                 nRead += rlen;
1106                 ptr += rlen;
1107                 Buf->BufUsed += rlen;
1108         }
1109         Buf->buf[Buf->BufUsed] = '\0';
1110         return nRead;
1111 }
1112
1113 /**
1114  * \brief Cut nChars from the start of the string
1115  * \param Buf Buffer to modify
1116  * \param nChars how many chars should be skipped?
1117  */
1118 void StrBufCutLeft(StrBuf *Buf, int nChars)
1119 {
1120         if (nChars >= Buf->BufUsed) {
1121                 FlushStrBuf(Buf);
1122                 return;
1123         }
1124         memmove(Buf->buf, Buf->buf + nChars, Buf->BufUsed - nChars);
1125         Buf->BufUsed -= nChars;
1126         Buf->buf[Buf->BufUsed] = '\0';
1127 }
1128
1129 /**
1130  * \brief Cut the trailing n Chars from the string
1131  * \param Buf Buffer to modify
1132  * \param nChars how many chars should be trunkated?
1133  */
1134 void StrBufCutRight(StrBuf *Buf, int nChars)
1135 {
1136         if (nChars >= Buf->BufUsed) {
1137                 FlushStrBuf(Buf);
1138                 return;
1139         }
1140         Buf->BufUsed -= nChars;
1141         Buf->buf[Buf->BufUsed] = '\0';
1142 }
1143
1144
1145 /*
1146  * Strip leading and trailing spaces from a string; with premeasured and adjusted length.
1147  * buf - the string to modify
1148  * len - length of the string. 
1149  */
1150 void StrBufTrim(StrBuf *Buf)
1151 {
1152         int delta = 0;
1153         if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1154
1155         while ((Buf->BufUsed > delta) && (isspace(Buf->buf[delta]))){
1156                 delta ++;
1157         }
1158         if (delta > 0) StrBufCutLeft(Buf, delta);
1159
1160         if (Buf->BufUsed == 0) return;
1161         while (isspace(Buf->buf[Buf->BufUsed - 1])){
1162                 Buf->BufUsed --;
1163         }
1164         Buf->buf[Buf->BufUsed] = '\0';
1165 }
1166
1167
1168 void StrBufUpCase(StrBuf *Buf) 
1169 {
1170         char *pch, *pche;
1171
1172         pch = Buf->buf;
1173         pche = pch + Buf->BufUsed;
1174         while (pch < pche) {
1175                 *pch = toupper(*pch);
1176                 pch ++;
1177         }
1178 }
1179
1180
1181 /**
1182  * \brief unhide special chars hidden to the HTML escaper
1183  * \param target buffer to put the unescaped string in
1184  * \param source buffer to unescape
1185  */
1186 void StrBufEUid_unescapize(StrBuf *target, const StrBuf *source) 
1187 {
1188         int a, b, len;
1189         char hex[3];
1190
1191         if (target != NULL)
1192                 FlushStrBuf(target);
1193
1194         if (source == NULL ||target == NULL)
1195         {
1196                 return;
1197         }
1198
1199         len = source->BufUsed;
1200         for (a = 0; a < len; ++a) {
1201                 if (target->BufUsed >= target->BufSize)
1202                         IncreaseBuf(target, 1, -1);
1203
1204                 if (source->buf[a] == '=') {
1205                         hex[0] = source->buf[a + 1];
1206                         hex[1] = source->buf[a + 2];
1207                         hex[2] = 0;
1208                         b = 0;
1209                         sscanf(hex, "%02x", &b);
1210                         target->buf[target->BufUsed] = b;
1211                         target->buf[++target->BufUsed] = 0;
1212                         a += 2;
1213                 }
1214                 else {
1215                         target->buf[target->BufUsed] = source->buf[a];
1216                         target->buf[++target->BufUsed] = 0;
1217                 }
1218         }
1219 }
1220
1221
1222 /**
1223  * \brief hide special chars from the HTML escapers and friends
1224  * \param target buffer to put the escaped string in
1225  * \param source buffer to escape
1226  */
1227 void StrBufEUid_escapize(StrBuf *target, const StrBuf *source) 
1228 {
1229         int i, len;
1230
1231         if (target != NULL)
1232                 FlushStrBuf(target);
1233
1234         if (source == NULL ||target == NULL)
1235         {
1236                 return;
1237         }
1238
1239         len = source->BufUsed;
1240         for (i=0; i<len; ++i) {
1241                 if (target->BufUsed + 4 >= target->BufSize)
1242                         IncreaseBuf(target, 1, -1);
1243                 if ( (isalnum(source->buf[i])) || 
1244                      (source->buf[i]=='-') || 
1245                      (source->buf[i]=='_') ) {
1246                         target->buf[target->BufUsed++] = source->buf[i];
1247                 }
1248                 else {
1249                         sprintf(&target->buf[target->BufUsed], 
1250                                 "=%02X", 
1251                                 (0xFF &source->buf[i]));
1252                         target->BufUsed += 3;
1253                 }
1254         }
1255         target->buf[target->BufUsed + 1] = '\0';
1256 }
1257
1258 /*
1259  * \brief uses the same calling syntax as compress2(), but it
1260  * creates a stream compatible with HTTP "Content-encoding: gzip"
1261  */
1262 #ifdef HAVE_ZLIB
1263 #define DEF_MEM_LEVEL 8 /*< memlevel??? */
1264 #define OS_CODE 0x03    /*< unix */
1265 int ZEXPORT compress_gzip(Bytef * dest,         /*< compressed buffer*/
1266                           size_t * destLen,     /*< length of the compresed data */
1267                           const Bytef * source, /*< source to encode */
1268                           uLong sourceLen,      /*< length of source to encode */
1269                           int level)            /*< compression level */
1270 {
1271         const int gz_magic[2] = { 0x1f, 0x8b }; /* gzip magic header */
1272
1273         /* write gzip header */
1274         snprintf((char *) dest, *destLen, 
1275                  "%c%c%c%c%c%c%c%c%c%c",
1276                  gz_magic[0], gz_magic[1], Z_DEFLATED,
1277                  0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /* xflags */ ,
1278                  OS_CODE);
1279
1280         /* normal deflate */
1281         z_stream stream;
1282         int err;
1283         stream.next_in = (Bytef *) source;
1284         stream.avail_in = (uInt) sourceLen;
1285         stream.next_out = dest + 10L;   // after header
1286         stream.avail_out = (uInt) * destLen;
1287         if ((uLong) stream.avail_out != *destLen)
1288                 return Z_BUF_ERROR;
1289
1290         stream.zalloc = (alloc_func) 0;
1291         stream.zfree = (free_func) 0;
1292         stream.opaque = (voidpf) 0;
1293
1294         err = deflateInit2(&stream, level, Z_DEFLATED, -MAX_WBITS,
1295                            DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
1296         if (err != Z_OK)
1297                 return err;
1298
1299         err = deflate(&stream, Z_FINISH);
1300         if (err != Z_STREAM_END) {
1301                 deflateEnd(&stream);
1302                 return err == Z_OK ? Z_BUF_ERROR : err;
1303         }
1304         *destLen = stream.total_out + 10L;
1305
1306         /* write CRC and Length */
1307         uLong crc = crc32(0L, source, sourceLen);
1308         int n;
1309         for (n = 0; n < 4; ++n, ++*destLen) {
1310                 dest[*destLen] = (int) (crc & 0xff);
1311                 crc >>= 8;
1312         }
1313         uLong len = stream.total_in;
1314         for (n = 0; n < 4; ++n, ++*destLen) {
1315                 dest[*destLen] = (int) (len & 0xff);
1316                 len >>= 8;
1317         }
1318         err = deflateEnd(&stream);
1319         return err;
1320 }
1321 #endif
1322
1323
1324 /**
1325  * Attention! If you feed this a Const String, you must maintain the uncompressed buffer yourself!
1326  */
1327 int CompressBuffer(StrBuf *Buf)
1328 {
1329 #ifdef HAVE_ZLIB
1330         char *compressed_data = NULL;
1331         size_t compressed_len, bufsize;
1332         
1333         bufsize = compressed_len = ((Buf->BufUsed * 101) / 100) + 100;
1334         compressed_data = malloc(compressed_len);
1335         
1336         if (compress_gzip((Bytef *) compressed_data,
1337                           &compressed_len,
1338                           (Bytef *) Buf->buf,
1339                           (uLongf) Buf->BufUsed, Z_BEST_SPEED) == Z_OK) {
1340                 if (!Buf->ConstBuf)
1341                         free(Buf->buf);
1342                 Buf->buf = compressed_data;
1343                 Buf->BufUsed = compressed_len;
1344                 Buf->BufSize = bufsize;
1345                 return 1;
1346         } else {
1347                 free(compressed_data);
1348         }
1349 #endif  /* HAVE_ZLIB */
1350         return 0;
1351 }
1352
1353 /**
1354  * \brief decode a buffer from base 64 encoding; destroys original
1355  * \param Buf Buffor to transform
1356  */
1357 int StrBufDecodeBase64(StrBuf *Buf)
1358 {
1359         char *xferbuf;
1360         size_t siz;
1361         if (Buf == NULL) return -1;
1362
1363         xferbuf = (char*) malloc(Buf->BufSize);
1364         siz = CtdlDecodeBase64(xferbuf,
1365                                Buf->buf,
1366                                Buf->BufUsed);
1367         free(Buf->buf);
1368         Buf->buf = xferbuf;
1369         Buf->BufUsed = siz;
1370         return siz;
1371 }
1372
1373
1374 /**
1375  * \brief  remove escaped strings from i.e. the url string (like %20 for blanks)
1376  * \param Buf Buffer to translate
1377  * \param StripBlanks Reduce several blanks to one?
1378  */
1379 long StrBufUnescape(StrBuf *Buf, int StripBlanks)
1380 {
1381         int a, b;
1382         char hex[3];
1383         long len;
1384
1385         while ((Buf->BufUsed > 0) && (isspace(Buf->buf[Buf->BufUsed - 1]))){
1386                 Buf->buf[Buf->BufUsed - 1] = '\0';
1387                 Buf->BufUsed --;
1388         }
1389
1390         a = 0; 
1391         while (a < Buf->BufUsed) {
1392                 if (Buf->buf[a] == '+')
1393                         Buf->buf[a] = ' ';
1394                 else if (Buf->buf[a] == '%') {
1395                         /* don't let % chars through, rather truncate the input. */
1396                         if (a + 2 > Buf->BufUsed) {
1397                                 Buf->buf[a] = '\0';
1398                                 Buf->BufUsed = a;
1399                         }
1400                         else {                  
1401                                 hex[0] = Buf->buf[a + 1];
1402                                 hex[1] = Buf->buf[a + 2];
1403                                 hex[2] = 0;
1404                                 b = 0;
1405                                 sscanf(hex, "%02x", &b);
1406                                 Buf->buf[a] = (char) b;
1407                                 len = Buf->BufUsed - a - 2;
1408                                 if (len > 0)
1409                                         memmove(&Buf->buf[a + 1], &Buf->buf[a + 3], len);
1410                         
1411                                 Buf->BufUsed -=2;
1412                         }
1413                 }
1414                 a++;
1415         }
1416         return a;
1417 }
1418
1419
1420 /**
1421  * \brief       RFC2047-encode a header field if necessary.
1422  *              If no non-ASCII characters are found, the string
1423  *              will be copied verbatim without encoding.
1424  *
1425  * \param       target          Target buffer.
1426  * \param       source          Source string to be encoded.
1427  * \returns     encoded length; -1 if non success.
1428  */
1429 int StrBufRFC2047encode(StrBuf **target, const StrBuf *source)
1430 {
1431         const char headerStr[] = "=?UTF-8?Q?";
1432         int need_to_encode = 0;
1433         int i = 0;
1434         unsigned char ch;
1435
1436         if ((source == NULL) || 
1437             (target == NULL))
1438             return -1;
1439
1440         while ((i < source->BufUsed) &&
1441                (!IsEmptyStr (&source->buf[i])) &&
1442                (need_to_encode == 0)) {
1443                 if (((unsigned char) source->buf[i] < 32) || 
1444                     ((unsigned char) source->buf[i] > 126)) {
1445                         need_to_encode = 1;
1446                 }
1447                 i++;
1448         }
1449
1450         if (!need_to_encode) {
1451                 if (*target == NULL) {
1452                         *target = NewStrBufPlain(source->buf, source->BufUsed);
1453                 }
1454                 else {
1455                         FlushStrBuf(*target);
1456                         StrBufAppendBuf(*target, source, 0);
1457                 }
1458                 return (*target)->BufUsed;
1459         }
1460         if (*target == NULL)
1461                 *target = NewStrBufPlain(NULL, sizeof(headerStr) + source->BufUsed * 2);
1462         else if (sizeof(headerStr) + source->BufUsed > (*target)->BufSize)
1463                 IncreaseBuf(*target, sizeof(headerStr) + source->BufUsed, 0);
1464         memcpy ((*target)->buf, headerStr, sizeof(headerStr) - 1);
1465         (*target)->BufUsed = sizeof(headerStr) - 1;
1466         for (i=0; (i < source->BufUsed); ++i) {
1467                 if ((*target)->BufUsed + 4 > (*target)->BufSize)
1468                         IncreaseBuf(*target, 1, 0);
1469                 ch = (unsigned char) source->buf[i];
1470                 if ((ch < 32) || (ch > 126) || (ch == 61)) {
1471                         sprintf(&(*target)->buf[(*target)->BufUsed], "=%02X", ch);
1472                         (*target)->BufUsed += 3;
1473                 }
1474                 else {
1475                         (*target)->buf[(*target)->BufUsed] = ch;
1476                         (*target)->BufUsed++;
1477                 }
1478         }
1479         
1480         if ((*target)->BufUsed + 4 > (*target)->BufSize)
1481                 IncreaseBuf(*target, 1, 0);
1482
1483         (*target)->buf[(*target)->BufUsed++] = '?';
1484         (*target)->buf[(*target)->BufUsed++] = '=';
1485         (*target)->buf[(*target)->BufUsed] = '\0';
1486         return (*target)->BufUsed;;
1487 }
1488
1489 /**
1490  * \brief replaces all occurances of 'search' by 'replace'
1491  * \param buf Buffer to modify
1492  * \param search character to search
1493  * \param relpace character to replace search by
1494  */
1495 void StrBufReplaceChars(StrBuf *buf, char search, char replace)
1496 {
1497         long i;
1498         if (buf == NULL)
1499                 return;
1500         for (i=0; i<buf->BufUsed; i++)
1501                 if (buf->buf[i] == search)
1502                         buf->buf[i] = replace;
1503
1504 }
1505
1506
1507
1508 /*
1509  * Wrapper around iconv_open()
1510  * Our version adds aliases for non-standard Microsoft charsets
1511  * such as 'MS950', aliasing them to names like 'CP950'
1512  *
1513  * tocode       Target encoding
1514  * fromcode     Source encoding
1515  */
1516 void  ctdl_iconv_open(const char *tocode, const char *fromcode, void *pic)
1517 {
1518 #ifdef HAVE_ICONV
1519         iconv_t ic = (iconv_t)(-1) ;
1520         ic = iconv_open(tocode, fromcode);
1521         if (ic == (iconv_t)(-1) ) {
1522                 char alias_fromcode[64];
1523                 if ( (strlen(fromcode) == 5) && (!strncasecmp(fromcode, "MS", 2)) ) {
1524                         safestrncpy(alias_fromcode, fromcode, sizeof alias_fromcode);
1525                         alias_fromcode[0] = 'C';
1526                         alias_fromcode[1] = 'P';
1527                         ic = iconv_open(tocode, alias_fromcode);
1528                 }
1529         }
1530         *(iconv_t *)pic = ic;
1531 #endif
1532 }
1533
1534
1535
1536 static inline char *FindNextEnd (StrBuf *Buf, char *bptr)
1537 {
1538         char * end;
1539         /* Find the next ?Q? */
1540         if (Buf->BufUsed - (bptr - Buf->buf)  < 6)
1541                 return NULL;
1542
1543         end = strchr(bptr + 2, '?');
1544
1545         if (end == NULL)
1546                 return NULL;
1547
1548         if ((Buf->BufUsed - (end - Buf->buf) > 3) &&
1549             ((*(end + 1) == 'B') || (*(end + 1) == 'Q')) && 
1550             (*(end + 2) == '?')) {
1551                 /* skip on to the end of the cluster, the next ?= */
1552                 end = strstr(end + 3, "?=");
1553         }
1554         else
1555                 /* sort of half valid encoding, try to find an end. */
1556                 end = strstr(bptr, "?=");
1557         return end;
1558 }
1559
1560
1561 void StrBufConvert(StrBuf *ConvertBuf, StrBuf *TmpBuf, void *pic)
1562 {
1563 #ifdef HAVE_ICONV
1564         int BufSize;
1565         iconv_t ic;
1566         char *ibuf;                     /**< Buffer of characters to be converted */
1567         char *obuf;                     /**< Buffer for converted characters */
1568         size_t ibuflen;                 /**< Length of input buffer */
1569         size_t obuflen;                 /**< Length of output buffer */
1570
1571
1572         if (ConvertBuf->BufUsed > TmpBuf->BufSize)
1573                 IncreaseBuf(TmpBuf, 0, ConvertBuf->BufUsed);
1574
1575         ic = *(iconv_t*)pic;
1576         ibuf = ConvertBuf->buf;
1577         ibuflen = ConvertBuf->BufUsed;
1578         obuf = TmpBuf->buf;
1579         obuflen = TmpBuf->BufSize;
1580         
1581         iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
1582
1583         /* little card game: wheres the red lady? */
1584         ibuf = ConvertBuf->buf;
1585         BufSize = ConvertBuf->BufSize;
1586
1587         ConvertBuf->buf = TmpBuf->buf;
1588         ConvertBuf->BufSize = TmpBuf->BufSize;
1589         ConvertBuf->BufUsed = TmpBuf->BufSize - obuflen;
1590         ConvertBuf->buf[ConvertBuf->BufUsed] = '\0';
1591         
1592         TmpBuf->buf = ibuf;
1593         TmpBuf->BufSize = BufSize;
1594         TmpBuf->BufUsed = 0;
1595         TmpBuf->buf[0] = '\0';
1596 #endif
1597 }
1598
1599
1600
1601
1602 inline static void DecodeSegment(StrBuf *Target, 
1603                                  StrBuf *DecodeMe, 
1604                                  char *SegmentStart, 
1605                                  char *SegmentEnd, 
1606                                  StrBuf *ConvertBuf,
1607                                  StrBuf *ConvertBuf2, 
1608                                  StrBuf *FoundCharset)
1609 {
1610         StrBuf StaticBuf;
1611         char charset[128];
1612         char encoding[16];
1613         iconv_t ic = (iconv_t)(-1);
1614
1615         /* Now we handle foreign character sets properly encoded
1616          * in RFC2047 format.
1617          */
1618         StaticBuf.buf = SegmentStart;
1619         StaticBuf.BufUsed = SegmentEnd - SegmentStart;
1620         StaticBuf.BufSize = DecodeMe->BufSize - (SegmentStart - DecodeMe->buf);
1621         extract_token(charset, SegmentStart, 1, '?', sizeof charset);
1622         if (FoundCharset != NULL) {
1623                 FlushStrBuf(FoundCharset);
1624                 StrBufAppendBufPlain(FoundCharset, charset, -1, 0);
1625         }
1626         extract_token(encoding, SegmentStart, 2, '?', sizeof encoding);
1627         StrBufExtract_token(ConvertBuf, &StaticBuf, 3, '?');
1628         
1629         *encoding = toupper(*encoding);
1630         if (*encoding == 'B') { /**< base64 */
1631                 ConvertBuf2->BufUsed = CtdlDecodeBase64(ConvertBuf2->buf, 
1632                                                         ConvertBuf->buf, 
1633                                                         ConvertBuf->BufUsed);
1634         }
1635         else if (*encoding == 'Q') {    /**< quoted-printable */
1636                 long pos;
1637                 
1638                 pos = 0;
1639                 while (pos < ConvertBuf->BufUsed)
1640                 {
1641                         if (ConvertBuf->buf[pos] == '_') 
1642                                 ConvertBuf->buf[pos] = ' ';
1643                         pos++;
1644                 }
1645                 
1646                 ConvertBuf2->BufUsed = CtdlDecodeQuotedPrintable(
1647                         ConvertBuf2->buf, 
1648                         ConvertBuf->buf,
1649                         ConvertBuf->BufUsed);
1650         }
1651         else {
1652                 StrBufAppendBuf(ConvertBuf2, ConvertBuf, 0);
1653         }
1654
1655         ctdl_iconv_open("UTF-8", charset, &ic);
1656         if (ic != (iconv_t)(-1) ) {             
1657                 StrBufConvert(ConvertBuf2, ConvertBuf, &ic);
1658                 StrBufAppendBuf(Target, ConvertBuf2, 0);
1659                 iconv_close(ic);
1660         }
1661         else {
1662                 StrBufAppendBufPlain(Target, HKEY("(unreadable)"), 0);
1663         }
1664 }
1665 /*
1666  * Handle subjects with RFC2047 encoding such as:
1667  * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
1668  */
1669 void StrBuf_RFC822_to_Utf8(StrBuf *Target, StrBuf *DecodeMe, const StrBuf* DefaultCharset, StrBuf *FoundCharset)
1670 {
1671         StrBuf *ConvertBuf, *ConvertBuf2;
1672         char *start, *end, *next, *nextend, *ptr = NULL;
1673         iconv_t ic = (iconv_t)(-1) ;
1674         const char *eptr;
1675         int passes = 0;
1676         int i, len, delta;
1677         int illegal_non_rfc2047_encoding = 0;
1678
1679         /* Sometimes, badly formed messages contain strings which were simply
1680          *  written out directly in some foreign character set instead of
1681          *  using RFC2047 encoding.  This is illegal but we will attempt to
1682          *  handle it anyway by converting from a user-specified default
1683          *  charset to UTF-8 if we see any nonprintable characters.
1684          */
1685         
1686         len = StrLength(DecodeMe);
1687         for (i=0; i<DecodeMe->BufUsed; ++i) {
1688                 if ((DecodeMe->buf[i] < 32) || (DecodeMe->buf[i] > 126)) {
1689                         illegal_non_rfc2047_encoding = 1;
1690                         break;
1691                 }
1692         }
1693
1694         ConvertBuf = NewStrBufPlain(NULL, StrLength(DecodeMe));
1695         if ((illegal_non_rfc2047_encoding) &&
1696             (strcasecmp(ChrPtr(DefaultCharset), "UTF-8")) && 
1697             (strcasecmp(ChrPtr(DefaultCharset), "us-ascii")) )
1698         {
1699                 ctdl_iconv_open("UTF-8", ChrPtr(DefaultCharset), &ic);
1700                 if (ic != (iconv_t)(-1) ) {
1701                         StrBufConvert(DecodeMe, ConvertBuf, &ic);
1702                         iconv_close(ic);
1703                 }
1704         }
1705
1706         /* pre evaluate the first pair */
1707         nextend = end = NULL;
1708         len = StrLength(DecodeMe);
1709         start = strstr(DecodeMe->buf, "=?");
1710         eptr = DecodeMe->buf + DecodeMe->BufUsed;
1711         if (start != NULL) 
1712                 end = FindNextEnd (DecodeMe, start);
1713         else {
1714                 StrBufAppendBuf(Target, DecodeMe, 0);
1715                 FreeStrBuf(&ConvertBuf);
1716                 return;
1717         }
1718
1719         ConvertBuf2 = NewStrBufPlain(NULL, StrLength(DecodeMe));
1720
1721         if (start != DecodeMe->buf)
1722                 StrBufAppendBufPlain(Target, DecodeMe->buf, start - DecodeMe->buf, 0);
1723         /*
1724          * Since spammers will go to all sorts of absurd lengths to get their
1725          * messages through, there are LOTS of corrupt headers out there.
1726          * So, prevent a really badly formed RFC2047 header from throwing
1727          * this function into an infinite loop.
1728          */
1729         while ((start != NULL) && 
1730                (end != NULL) && 
1731                (start < eptr) && 
1732                (end < eptr) && 
1733                (passes < 20))
1734         {
1735                 passes++;
1736                 DecodeSegment(Target, 
1737                               DecodeMe, 
1738                               start, 
1739                               end, 
1740                               ConvertBuf,
1741                               ConvertBuf2,
1742                               FoundCharset);
1743                 
1744                 next = strstr(end, "=?");
1745                 nextend = NULL;
1746                 if ((next != NULL) && 
1747                     (next < eptr))
1748                         nextend = FindNextEnd(DecodeMe, next);
1749                 if (nextend == NULL)
1750                         next = NULL;
1751
1752                 /* did we find two partitions */
1753                 if ((next != NULL) && 
1754                     ((next - end) > 2))
1755                 {
1756                         ptr = end + 2;
1757                         while ((ptr < next) && 
1758                                (isspace(*ptr) ||
1759                                 (*ptr == '\r') ||
1760                                 (*ptr == '\n') || 
1761                                 (*ptr == '\t')))
1762                                 ptr ++;
1763                         /* did we find a gab just filled with blanks? */
1764                         if (ptr == next)
1765                         {
1766                                 memmove (end + 2,
1767                                          next,
1768                                          len - (next - start));
1769                                 
1770                                 /* now terminate the gab at the end */
1771                                 delta = (next - end) - 2;
1772                                 DecodeMe->BufUsed -= delta;
1773                                 DecodeMe->buf[DecodeMe->BufUsed] = '\0';
1774
1775                                 /* move next to its new location. */
1776                                 next -= delta;
1777                                 nextend -= delta;
1778                         }
1779                 }
1780                 /* our next-pair is our new first pair now. */
1781                 ptr = end + 2;
1782                 start = next;
1783                 end = nextend;
1784         }
1785         end = ptr;
1786         nextend = DecodeMe->buf + DecodeMe->BufUsed;
1787         if ((end != NULL) && (end < nextend)) {
1788                 ptr = end;
1789                 while ( (ptr < nextend) &&
1790                         (isspace(*ptr) ||
1791                          (*ptr == '\r') ||
1792                          (*ptr == '\n') || 
1793                          (*ptr == '\t')))
1794                         ptr ++;
1795                 if (ptr < nextend)
1796                         StrBufAppendBufPlain(Target, end, nextend - end, 0);
1797         }
1798         FreeStrBuf(&ConvertBuf);
1799         FreeStrBuf(&ConvertBuf2);
1800 }
1801
1802
1803
1804 long StrBuf_Utf8StrLen(StrBuf *Buf)
1805 {
1806         return Ctdl_Utf8StrLen(Buf->buf);
1807 }
1808
1809 long StrBuf_Utf8StrCut(StrBuf *Buf, int maxlen)
1810 {
1811         char *CutAt;
1812
1813         CutAt = Ctdl_Utf8StrCut(Buf->buf, maxlen);
1814         if (CutAt != NULL) {
1815                 Buf->BufUsed = CutAt - Buf->buf;
1816                 Buf->buf[Buf->BufUsed] = '\0';
1817         }
1818         return Buf->BufUsed;    
1819 }
1820
1821
1822
1823 int StrBufSipLine(StrBuf *LineBuf, StrBuf *Buf, const char **Ptr)
1824 {
1825         const char *aptr, *ptr, *eptr;
1826         char *optr, *xptr;
1827
1828         if (Buf == NULL)
1829                 return 0;
1830
1831         if (*Ptr==NULL)
1832                 ptr = aptr = Buf->buf;
1833         else
1834                 ptr = aptr = *Ptr;
1835
1836         optr = LineBuf->buf;
1837         eptr = Buf->buf + Buf->BufUsed;
1838         xptr = LineBuf->buf + LineBuf->BufSize;
1839
1840         while ((*ptr != '\n') &&
1841                (*ptr != '\r') &&
1842                (ptr < eptr))
1843         {
1844                 *optr = *ptr;
1845                 optr++; ptr++;
1846                 if (optr == xptr) {
1847                         LineBuf->BufUsed = optr - LineBuf->buf;
1848                         IncreaseBuf(LineBuf,  1, LineBuf->BufUsed + 1);
1849                         optr = LineBuf->buf + LineBuf->BufUsed;
1850                         xptr = LineBuf->buf + LineBuf->BufSize;
1851                 }
1852         }
1853         LineBuf->BufUsed = optr - LineBuf->buf;
1854         *optr = '\0';       
1855         if (*ptr == '\r')
1856                 ptr ++;
1857         if (*ptr == '\n')
1858                 ptr ++;
1859
1860         *Ptr = ptr;
1861
1862         return Buf->BufUsed - (ptr - Buf->buf);
1863 }