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