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