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