* Fixed an error in the template that was causing the 'c_creataide' field to load...
[citadel.git] / libcitadel / lib / stringbuf.c
blob9b03a0e6c10fc89951a2099717c04eadb19841d4
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"
14 #ifdef HAVE_ICONV
15 #include <iconv.h>
16 #endif
18 #ifdef HAVE_ZLIB
19 #include <zlib.h>
20 #endif
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
29 /**
30 * Private Structure for the Stringbuffer
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? */
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!
49 inline const char *ChrPtr(const StrBuf *Str)
51 if (Str == NULL)
52 return "";
53 return Str->buf;
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
61 inline int StrLength(const StrBuf *Str)
63 return (Str != NULL) ? Str->BufUsed : 0;
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?
72 static int IncreaseBuf(StrBuf *Buf, int KeepOriginal, int DestSize)
74 char *NewBuf;
75 size_t NewSize = Buf->BufSize * 2;
77 if (Buf->ConstBuf)
78 return -1;
80 if (DestSize > 0)
81 while (NewSize <= DestSize)
82 NewSize *= 2;
84 NewBuf= (char*) malloc(NewSize);
85 if (KeepOriginal && (Buf->BufUsed > 0))
87 memcpy(NewBuf, Buf->buf, Buf->BufUsed);
89 else
91 NewBuf[0] = '\0';
92 Buf->BufUsed = 0;
94 free (Buf->buf);
95 Buf->buf = NewBuf;
96 Buf->BufSize *= 2;
97 return Buf->BufSize;
101 * Allocate a new buffer with default buffer size
102 * \returns the new stringbuffer
104 StrBuf* NewStrBuf(void)
106 StrBuf *NewBuf;
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;
117 /**
118 * \brief Copy Constructor; returns a duplicate of CopyMe
119 * \params CopyMe Buffer to faxmilate
120 * \returns the new stringbuffer
122 StrBuf* NewStrBufDup(const StrBuf *CopyMe)
124 StrBuf *NewBuf;
126 if (CopyMe == NULL)
127 return NewStrBuf();
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;
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
146 StrBuf* NewStrBufPlain(const char* ptr, int nChars)
148 StrBuf *NewBuf;
149 size_t Siz = SIZ;
150 size_t CopySize;
152 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
153 if (nChars < 0)
154 CopySize = strlen((ptr != NULL)?ptr:"");
155 else
156 CopySize = nChars;
158 while (Siz <= CopySize)
159 Siz *= 2;
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;
168 else {
169 NewBuf->buf[0] = '\0';
170 NewBuf->BufUsed = 0;
172 NewBuf->ConstBuf = 0;
173 return NewBuf;
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
182 int StrBufPlain(StrBuf *Buf, const char* ptr, int nChars)
184 size_t Siz = Buf->BufSize;
185 size_t CopySize;
187 if (nChars < 0)
188 CopySize = strlen(ptr);
189 else
190 CopySize = nChars;
192 while (Siz <= CopySize)
193 Siz *= 2;
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;
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
210 StrBuf* _NewConstStrBuf(const char* StringConstant, size_t SizeOfStrConstant)
212 StrBuf *NewBuf;
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;
224 * \brief flush the content of a Buf; keep its struct
225 * \param buf Buffer to flush
227 int FlushStrBuf(StrBuf *buf)
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;
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
244 void FreeStrBuf (StrBuf **FreeMe)
246 if (*FreeMe == NULL)
247 return;
248 if (!(*FreeMe)->ConstBuf)
249 free((*FreeMe)->buf);
250 free(*FreeMe);
251 *FreeMe = NULL;
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]
259 void HFreeStrBuf (void *VFreeMe)
261 StrBuf *FreeMe = (StrBuf*)VFreeMe;
262 if (FreeMe == NULL)
263 return;
264 if (!FreeMe->ConstBuf)
265 free(FreeMe->buf);
266 free(FreeMe);
270 * \brief Wrapper around atol
272 long StrTol(const StrBuf *Buf)
274 if (Buf == NULL)
275 return 0;
276 if(Buf->BufUsed > 0)
277 return atol(Buf->buf);
278 else
279 return 0;
283 * \brief Wrapper around atoi
285 int StrToi(const StrBuf *Buf)
287 if (Buf == NULL)
288 return 0;
289 if (Buf->BufUsed > 0)
290 return atoi(Buf->buf);
291 else
292 return 0;
295 * \brief Checks to see if the string is a pure number
297 int StrBufIsNumber(const StrBuf *Buf) {
298 if (Buf == NULL) {
299 return 0;
301 char * pEnd;
302 strtoll(Buf->buf, &pEnd, 10);
303 if (pEnd == NULL && ((Buf->buf)-pEnd) != 0) {
304 return 1;
306 return 0;
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
315 long StrBufPeek(StrBuf *Buf, const char* ptr, long nThChar, char PeekValue)
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;
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?
333 void StrBufAppendBuf(StrBuf *Buf, const StrBuf *AppendBuf, unsigned long Offset)
335 if ((AppendBuf == NULL) || (Buf == NULL) || (AppendBuf->buf == NULL))
336 return;
338 if (Buf->BufSize - Offset < AppendBuf->BufUsed + Buf->BufUsed)
339 IncreaseBuf(Buf,
340 (Buf->BufUsed > 0),
341 AppendBuf->BufUsed + Buf->BufUsed);
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';
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?
358 void StrBufAppendBufPlain(StrBuf *Buf, const char *AppendBuf, long AppendSize, unsigned long Offset)
360 long aps;
361 long BufSizeRequired;
363 if ((AppendBuf == NULL) || (Buf == NULL))
364 return;
366 if (AppendSize < 0 )
367 aps = strlen(AppendBuf + Offset);
368 else
369 aps = AppendSize - Offset;
371 BufSizeRequired = Buf->BufUsed + aps + 1;
372 if (Buf->BufSize <= BufSizeRequired)
373 IncreaseBuf(Buf, (Buf->BufUsed > 0), BufSizeRequired);
375 memcpy(Buf->buf + Buf->BufUsed,
376 AppendBuf + Offset,
377 aps);
378 Buf->BufUsed += aps;
379 Buf->buf[Buf->BufUsed] = '\0';
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
389 void StrBufUrlescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
391 const char *pch, *pche;
392 char *pt, *pte;
393 int b, c, len;
394 const char ec[] = " +#&;`'|*?-~<>^()[]{}/$\"\\";
395 int eclen = sizeof(ec) -1;
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;
404 else {
405 pch = In->buf;
406 pche = pch + In->BufUsed;
407 len = In->BufUsed;
410 if (len == 0)
411 return;
413 pt = OutBuf->buf + OutBuf->BufUsed;
414 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
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;
423 c = 0;
424 for (b = 0; b < eclen; ++b) {
425 if (*pch == ec[b]) {
426 c = 1;
427 b += eclen;
430 if (c == 1) {
431 sprintf(pt,"%%%02X", *pch);
432 pt += 3;
433 OutBuf->BufUsed += 3;
434 pch ++;
436 else {
437 *(pt++) = *(pch++);
438 OutBuf->BufUsed++;
441 *pt = '\0';
445 * \brief Append a string, escaping characters which have meaning in HTML.
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 to 1, linebreaks are removed from the string.
452 * if set to 2, linebreaks are replaced by &ltbr/&gt
454 long StrEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
456 const char *aptr, *eiptr;
457 char *bptr, *eptr;
458 long len;
460 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
461 return -1;
463 if (PlainIn != NULL) {
464 aptr = PlainIn;
465 len = strlen(PlainIn);
466 eiptr = aptr + len;
468 else {
469 aptr = Source->buf;
470 eiptr = aptr + Source->BufUsed;
471 len = Source->BufUsed;
474 if (len == 0)
475 return -1;
477 bptr = Target->buf + Target->BufUsed;
478 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
480 while (aptr < eiptr){
481 if(bptr >= eptr) {
482 IncreaseBuf(Target, 1, -1);
483 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
484 bptr = Target->buf + Target->BufUsed;
486 if (*aptr == '<') {
487 memcpy(bptr, "&lt;", 4);
488 bptr += 4;
489 Target->BufUsed += 4;
491 else if (*aptr == '>') {
492 memcpy(bptr, "&gt;", 4);
493 bptr += 4;
494 Target->BufUsed += 4;
496 else if (*aptr == '&') {
497 memcpy(bptr, "&amp;", 5);
498 bptr += 5;
499 Target->BufUsed += 5;
501 else if (*aptr == '"') {
502 memcpy(bptr, "&quot;", 6);
503 bptr += 6;
504 Target->BufUsed += 6;
506 else if (*aptr == '\'') {
507 memcpy(bptr, "&#39;", 5);
508 bptr += 5;
509 Target->BufUsed += 5;
511 else if (*aptr == LB) {
512 *bptr = '<';
513 bptr ++;
514 Target->BufUsed ++;
516 else if (*aptr == RB) {
517 *bptr = '>';
518 bptr ++;
519 Target->BufUsed ++;
521 else if (*aptr == QU) {
522 *bptr ='"';
523 bptr ++;
524 Target->BufUsed ++;
526 else if ((*aptr == 32) && (nbsp == 1)) {
527 memcpy(bptr, "&nbsp;", 6);
528 bptr += 6;
529 Target->BufUsed += 6;
531 else if ((*aptr == '\n') && (nolinebreaks == 1)) {
532 *bptr='\0'; /* nothing */
534 else if ((*aptr == '\n') && (nolinebreaks == 2)) {
535 memcpy(bptr, "&lt;br/&gt;", 11);
536 bptr += 11;
537 Target->BufUsed += 11;
541 else if ((*aptr == '\r') && (nolinebreaks != 0)) {
542 *bptr='\0'; /* nothing */
544 else{
545 *bptr = *aptr;
546 bptr++;
547 Target->BufUsed ++;
549 aptr ++;
551 *bptr = '\0';
552 if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
553 return -1;
554 return Target->BufUsed;
558 * \brief Append a string, escaping characters which have meaning in HTML.
559 * Converts linebreaks into blanks; escapes single quotes
560 * \param Target target buffer
561 * \param Source source buffer; set to NULL if you just have a C-String
562 * \param PlainIn Plain-C string to append; set to NULL if unused
564 void StrMsgEscAppend(StrBuf *Target, StrBuf *Source, const char *PlainIn)
566 const char *aptr, *eiptr;
567 char *tptr, *eptr;
568 long len;
570 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
571 return ;
573 if (PlainIn != NULL) {
574 aptr = PlainIn;
575 len = strlen(PlainIn);
576 eiptr = aptr + len;
578 else {
579 aptr = Source->buf;
580 eiptr = aptr + Source->BufUsed;
581 len = Source->BufUsed;
584 if (len == 0)
585 return;
587 eptr = Target->buf + Target->BufSize - 6;
588 tptr = Target->buf + Target->BufUsed;
590 while (aptr < eiptr){
591 if(tptr >= eptr) {
592 IncreaseBuf(Target, 1, -1);
593 eptr = Target->buf + Target->BufSize - 6;
594 tptr = Target->buf + Target->BufUsed;
597 if (*aptr == '\n') {
598 *tptr = ' ';
599 Target->BufUsed++;
601 else if (*aptr == '\r') {
602 *tptr = ' ';
603 Target->BufUsed++;
605 else if (*aptr == '\'') {
606 *(tptr++) = '&';
607 *(tptr++) = '#';
608 *(tptr++) = '3';
609 *(tptr++) = '9';
610 *tptr = ';';
611 Target->BufUsed += 5;
612 } else {
613 *tptr = *aptr;
614 Target->BufUsed++;
616 tptr++; aptr++;
618 *tptr = '\0';
622 * \brief Append a string, escaping characters which have meaning in JavaScript strings .
624 * \param Target target buffer
625 * \param Source source buffer; set to NULL if you just have a C-String
626 * \param PlainIn Plain-C string to append; set to NULL if unused
628 long StrECMAEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
630 const char *aptr, *eiptr;
631 char *bptr, *eptr;
632 long len;
634 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
635 return -1;
637 if (PlainIn != NULL) {
638 aptr = PlainIn;
639 len = strlen(PlainIn);
640 eiptr = aptr + len;
642 else {
643 aptr = Source->buf;
644 eiptr = aptr + Source->BufUsed;
645 len = Source->BufUsed;
648 if (len == 0)
649 return -1;
651 bptr = Target->buf + Target->BufUsed;
652 eptr = Target->buf + Target->BufSize - 2; /* our biggest unit to put in... */
654 while (aptr < eiptr){
655 if(bptr >= eptr) {
656 IncreaseBuf(Target, 1, -1);
657 eptr = Target->buf + Target->BufSize - 2;
658 bptr = Target->buf + Target->BufUsed;
660 else if (*aptr == '"') {
661 memcpy(bptr, "\\\"", 2);
662 bptr += 2;
663 Target->BufUsed += 2;
664 } else if (*aptr == '\\') {
665 memcpy(bptr, "\\\\", 2);
666 bptr += 2;
667 Target->BufUsed += 2;
669 else{
670 *bptr = *aptr;
671 bptr++;
672 Target->BufUsed ++;
674 aptr ++;
676 *bptr = '\0';
677 if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
678 return -1;
679 return Target->BufUsed;
683 * \brief extracts a substring from Source into dest
684 * \param dest buffer to place substring into
685 * \param Source string to copy substring from
686 * \param Offset chars to skip from start
687 * \param nChars number of chars to copy
688 * \returns the number of chars copied; may be different from nChars due to the size of Source
690 int StrBufSub(StrBuf *dest, const StrBuf *Source, unsigned long Offset, size_t nChars)
692 size_t NCharsRemain;
693 if (Offset > Source->BufUsed)
695 FlushStrBuf(dest);
696 return 0;
698 if (Offset + nChars < Source->BufUsed)
700 if (nChars > dest->BufSize)
701 IncreaseBuf(dest, 0, nChars + 1);
702 memcpy(dest->buf, Source->buf + Offset, nChars);
703 dest->BufUsed = nChars;
704 dest->buf[dest->BufUsed] = '\0';
705 return nChars;
707 NCharsRemain = Source->BufUsed - Offset;
708 if (NCharsRemain > dest->BufSize)
709 IncreaseBuf(dest, 0, NCharsRemain + 1);
710 memcpy(dest->buf, Source->buf + Offset, NCharsRemain);
711 dest->BufUsed = NCharsRemain;
712 dest->buf[dest->BufUsed] = '\0';
713 return NCharsRemain;
717 * \brief sprintf like function appending the formated string to the buffer
718 * vsnprintf version to wrap into own calls
719 * \param Buf Buffer to extend by format and params
720 * \param format printf alike format to add
721 * \param ap va_list containing the items for format
723 void StrBufVAppendPrintf(StrBuf *Buf, const char *format, va_list ap)
725 va_list apl;
726 size_t BufSize = Buf->BufSize;
727 size_t nWritten = Buf->BufSize + 1;
728 size_t Offset = Buf->BufUsed;
729 size_t newused = Offset + nWritten;
731 while (newused >= BufSize) {
732 va_copy(apl, ap);
733 nWritten = vsnprintf(Buf->buf + Offset,
734 Buf->BufSize - Offset,
735 format, apl);
736 va_end(apl);
737 newused = Offset + nWritten;
738 if (newused >= Buf->BufSize) {
739 IncreaseBuf(Buf, 1, newused);
741 else {
742 Buf->BufUsed = Offset + nWritten;
743 BufSize = Buf->BufSize;
750 * \brief sprintf like function appending the formated string to the buffer
751 * \param Buf Buffer to extend by format and params
752 * \param format printf alike format to add
753 * \param ap va_list containing the items for format
755 void StrBufAppendPrintf(StrBuf *Buf, const char *format, ...)
757 size_t BufSize = Buf->BufSize;
758 size_t nWritten = Buf->BufSize + 1;
759 size_t Offset = Buf->BufUsed;
760 size_t newused = Offset + nWritten;
761 va_list arg_ptr;
763 while (newused >= BufSize) {
764 va_start(arg_ptr, format);
765 nWritten = vsnprintf(Buf->buf + Buf->BufUsed,
766 Buf->BufSize - Buf->BufUsed,
767 format, arg_ptr);
768 va_end(arg_ptr);
769 newused = Buf->BufUsed + nWritten;
770 if (newused >= Buf->BufSize) {
771 IncreaseBuf(Buf, 1, newused);
773 else {
774 Buf->BufUsed += nWritten;
775 BufSize = Buf->BufSize;
782 * \brief sprintf like function putting the formated string into the buffer
783 * \param Buf Buffer to extend by format and params
784 * \param format printf alike format to add
785 * \param ap va_list containing the items for format
787 void StrBufPrintf(StrBuf *Buf, const char *format, ...)
789 size_t nWritten = Buf->BufSize + 1;
790 va_list arg_ptr;
792 while (nWritten >= Buf->BufSize) {
793 va_start(arg_ptr, format);
794 nWritten = vsnprintf(Buf->buf, Buf->BufSize, format, arg_ptr);
795 va_end(arg_ptr);
796 Buf->BufUsed = nWritten ;
797 if (nWritten >= Buf->BufSize)
798 IncreaseBuf(Buf, 0, 0);
804 * \brief Counts the numbmer of tokens in a buffer
805 * \param Source String to count tokens in
806 * \param tok Tokenizer char to count
807 * \returns numbers of tokenizer chars found
809 inline int StrBufNum_tokens(const StrBuf *source, char tok)
811 if (source == NULL)
812 return 0;
813 return num_tokens(source->buf, tok);
817 * remove_token() - a tokenizer that kills, maims, and destroys
820 * \brief a string tokenizer
821 * \param Source StringBuffer to read into
822 * \param parmnum n'th parameter to remove
823 * \param separator tokenizer param
824 * \returns -1 if not found, else length of token.
826 int StrBufRemove_token(StrBuf *Source, int parmnum, char separator)
828 int ReducedBy;
829 char *d, *s; /* dest, source */
830 int count = 0;
832 /* Find desired parameter */
833 d = Source->buf;
834 while (count < parmnum) {
835 /* End of string, bail! */
836 if (!*d) {
837 d = NULL;
838 break;
840 if (*d == separator) {
841 count++;
843 d++;
845 if (!d) return 0; /* Parameter not found */
847 /* Find next parameter */
848 s = d;
849 while (*s && *s != separator) {
850 s++;
853 ReducedBy = d - s;
855 /* Hack and slash */
856 if (*s) {
857 memmove(d, s, Source->BufUsed - (s - Source->buf));
858 Source->BufUsed -= (ReducedBy + 1);
860 else if (d == Source->buf) {
861 *d = 0;
862 Source->BufUsed = 0;
864 else {
865 *--d = 0;
866 Source->BufUsed -= (ReducedBy + 1);
869 while (*s) {
870 *d++ = *s++;
872 *d = 0;
874 return ReducedBy;
879 * \brief a string tokenizer
880 * \param dest Destination StringBuffer
881 * \param Source StringBuffer to read into
882 * \param parmnum n'th parameter to extract
883 * \param separator tokenizer param
884 * \returns -1 if not found, else length of token.
886 int StrBufExtract_token(StrBuf *dest, const StrBuf *Source, int parmnum, char separator)
888 const char *s, *e; //* source * /
889 int len = 0; //* running total length of extracted string * /
890 int current_token = 0; //* token currently being processed * /
892 if (dest != NULL) {
893 dest->buf[0] = '\0';
894 dest->BufUsed = 0;
896 else
897 return(-1);
899 if ((Source == NULL) || (Source->BufUsed ==0)) {
900 return(-1);
902 s = Source->buf;
903 e = s + Source->BufUsed;
905 //cit_backtrace();
906 //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
908 while ((s<e) && !IsEmptyStr(s)) {
909 if (*s == separator) {
910 ++current_token;
912 if (len >= dest->BufSize)
913 if (!IncreaseBuf(dest, 1, -1))
914 break;
915 if ( (current_token == parmnum) &&
916 (*s != separator)) {
917 dest->buf[len] = *s;
918 ++len;
920 else if (current_token > parmnum) {
921 break;
923 ++s;
926 dest->buf[len] = '\0';
927 dest->BufUsed = len;
929 if (current_token < parmnum) {
930 //lprintf (CTDL_DEBUG,"test <!: %s\n", dest);
931 return(-1);
933 //lprintf (CTDL_DEBUG,"test <: %d; %s\n", len, dest);
934 return(len);
939 * \brief a string tokenizer to fetch an integer
940 * \param dest Destination StringBuffer
941 * \param parmnum n'th parameter to extract
942 * \param separator tokenizer param
943 * \returns 0 if not found, else integer representation of the token
945 int StrBufExtract_int(const StrBuf* Source, int parmnum, char separator)
947 StrBuf tmp;
948 char buf[64];
950 tmp.buf = buf;
951 buf[0] = '\0';
952 tmp.BufSize = 64;
953 tmp.BufUsed = 0;
954 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
955 return(atoi(buf));
956 else
957 return 0;
961 * \brief a string tokenizer to fetch a long integer
962 * \param dest Destination StringBuffer
963 * \param parmnum n'th parameter to extract
964 * \param separator tokenizer param
965 * \returns 0 if not found, else long integer representation of the token
967 long StrBufExtract_long(const StrBuf* Source, int parmnum, char separator)
969 StrBuf tmp;
970 char buf[64];
972 tmp.buf = buf;
973 buf[0] = '\0';
974 tmp.BufSize = 64;
975 tmp.BufUsed = 0;
976 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
977 return(atoi(buf));
978 else
979 return 0;
984 * \brief a string tokenizer to fetch an unsigned long
985 * \param dest Destination StringBuffer
986 * \param parmnum n'th parameter to extract
987 * \param separator tokenizer param
988 * \returns 0 if not found, else unsigned long representation of the token
990 unsigned long StrBufExtract_unsigned_long(const StrBuf* Source, int parmnum, char separator)
992 StrBuf tmp;
993 char buf[64];
994 char *pnum;
996 tmp.buf = buf;
997 buf[0] = '\0';
998 tmp.BufSize = 64;
999 tmp.BufUsed = 0;
1000 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0) {
1001 pnum = &buf[0];
1002 if (*pnum == '-')
1003 pnum ++;
1004 return (unsigned long) atol(pnum);
1006 else
1007 return 0;
1013 * \brief Read a line from socket
1014 * flushes and closes the FD on error
1015 * \param buf the buffer to get the input to
1016 * \param fd pointer to the filedescriptor to read
1017 * \param append Append to an existing string or replace?
1018 * \param Error strerror() on error
1019 * \returns numbers of chars read
1021 int StrBufTCP_read_line(StrBuf *buf, int *fd, int append, const char **Error)
1023 int len, rlen, slen;
1025 if (!append)
1026 FlushStrBuf(buf);
1028 slen = len = buf->BufUsed;
1029 while (1) {
1030 rlen = read(*fd, &buf->buf[len], 1);
1031 if (rlen < 1) {
1032 *Error = strerror(errno);
1034 close(*fd);
1035 *fd = -1;
1037 return -1;
1039 if (buf->buf[len] == '\n')
1040 break;
1041 if (buf->buf[len] != '\r')
1042 len ++;
1043 if (!(len < buf->BufSize)) {
1044 buf->BufUsed = len;
1045 buf->buf[len+1] = '\0';
1046 IncreaseBuf(buf, 1, -1);
1049 buf->BufUsed = len;
1050 buf->buf[len] = '\0';
1051 return len - slen;
1055 * \brief Read a line from socket
1056 * flushes and closes the FD on error
1057 * \param buf the buffer to get the input to
1058 * \param fd pointer to the filedescriptor to read
1059 * \param append Append to an existing string or replace?
1060 * \param Error strerror() on error
1061 * \returns numbers of chars read
1063 int StrBufTCP_read_buffered_line(StrBuf *Line,
1064 StrBuf *buf,
1065 int *fd,
1066 int timeout,
1067 int selectresolution,
1068 const char **Error)
1070 int len, rlen;
1071 int nSuccessLess = 0;
1072 fd_set rfds;
1073 char *pch = NULL;
1074 int fdflags;
1075 struct timeval tv;
1077 if (buf->BufUsed > 0) {
1078 pch = strchr(buf->buf, '\n');
1079 if (pch != NULL) {
1080 rlen = 0;
1081 len = pch - buf->buf;
1082 if (len > 0 && (*(pch - 1) == '\r') )
1083 rlen ++;
1084 StrBufSub(Line, buf, 0, len - rlen);
1085 StrBufCutLeft(buf, len + 1);
1086 return len - rlen;
1090 if (buf->BufSize - buf->BufUsed < 10)
1091 IncreaseBuf(buf, 1, -1);
1093 fdflags = fcntl(*fd, F_GETFL);
1094 if ((fdflags & O_NONBLOCK) == O_NONBLOCK)
1095 return -1;
1097 while ((nSuccessLess < timeout) && (pch == NULL)) {
1098 tv.tv_sec = selectresolution;
1099 tv.tv_usec = 0;
1101 FD_ZERO(&rfds);
1102 FD_SET(*fd, &rfds);
1103 if (select(*fd + 1, NULL, &rfds, NULL, &tv) == -1) {
1104 *Error = strerror(errno);
1105 close (*fd);
1106 *fd = -1;
1107 return -1;
1109 if (FD_ISSET(*fd, &rfds)) {
1110 rlen = read(*fd,
1111 &buf->buf[buf->BufUsed],
1112 buf->BufSize - buf->BufUsed - 1);
1113 if (rlen < 1) {
1114 *Error = strerror(errno);
1115 close(*fd);
1116 *fd = -1;
1117 return -1;
1119 else if (rlen > 0) {
1120 nSuccessLess = 0;
1121 buf->BufUsed += rlen;
1122 buf->buf[buf->BufUsed] = '\0';
1123 if (buf->BufUsed + 10 > buf->BufSize) {
1124 IncreaseBuf(buf, 1, -1);
1126 pch = strchr(buf->buf, '\n');
1127 continue;
1130 nSuccessLess ++;
1132 if (pch != NULL) {
1133 rlen = 0;
1134 len = pch - buf->buf;
1135 if (len > 0 && (*(pch - 1) == '\r') )
1136 rlen ++;
1137 StrBufSub(Line, buf, 0, len - rlen);
1138 StrBufCutLeft(buf, len + 1);
1139 return len - rlen;
1141 return -1;
1146 * \brief Input binary data from socket
1147 * flushes and closes the FD on error
1148 * \param buf the buffer to get the input to
1149 * \param fd pointer to the filedescriptor to read
1150 * \param append Append to an existing string or replace?
1151 * \param nBytes the maximal number of bytes to read
1152 * \param Error strerror() on error
1153 * \returns numbers of chars read
1155 int StrBufReadBLOB(StrBuf *Buf, int *fd, int append, long nBytes, const char **Error)
1157 fd_set wset;
1158 int fdflags;
1159 int len, rlen, slen;
1160 int nRead = 0;
1161 char *ptr;
1163 if ((Buf == NULL) || (*fd == -1))
1164 return -1;
1165 if (!append)
1166 FlushStrBuf(Buf);
1167 if (Buf->BufUsed + nBytes > Buf->BufSize)
1168 IncreaseBuf(Buf, 1, Buf->BufUsed + nBytes);
1170 ptr = Buf->buf + Buf->BufUsed;
1172 slen = len = Buf->BufUsed;
1174 fdflags = fcntl(*fd, F_GETFL);
1176 while (nRead < nBytes) {
1177 if ((fdflags & O_NONBLOCK) == O_NONBLOCK) {
1178 FD_ZERO(&wset);
1179 FD_SET(*fd, &wset);
1180 if (select(*fd + 1, NULL, &wset, NULL, NULL) == -1) {
1181 *Error = strerror(errno);
1182 return -1;
1186 if ((rlen = read(*fd,
1187 ptr,
1188 nBytes - nRead)) == -1) {
1189 close(*fd);
1190 *fd = -1;
1191 *Error = strerror(errno);
1192 return rlen;
1194 nRead += rlen;
1195 ptr += rlen;
1196 Buf->BufUsed += rlen;
1198 Buf->buf[Buf->BufUsed] = '\0';
1199 return nRead;
1203 * \brief Cut nChars from the start of the string
1204 * \param Buf Buffer to modify
1205 * \param nChars how many chars should be skipped?
1207 void StrBufCutLeft(StrBuf *Buf, int nChars)
1209 if (nChars >= Buf->BufUsed) {
1210 FlushStrBuf(Buf);
1211 return;
1213 memmove(Buf->buf, Buf->buf + nChars, Buf->BufUsed - nChars);
1214 Buf->BufUsed -= nChars;
1215 Buf->buf[Buf->BufUsed] = '\0';
1219 * \brief Cut the trailing n Chars from the string
1220 * \param Buf Buffer to modify
1221 * \param nChars how many chars should be trunkated?
1223 void StrBufCutRight(StrBuf *Buf, int nChars)
1225 if (nChars >= Buf->BufUsed) {
1226 FlushStrBuf(Buf);
1227 return;
1229 Buf->BufUsed -= nChars;
1230 Buf->buf[Buf->BufUsed] = '\0';
1234 * \brief Cut the string after n Chars
1235 * \param Buf Buffer to modify
1236 * \param AfternChars after how many chars should we trunkate the string?
1237 * \param At if non-null and points inside of our string, cut it there.
1239 void StrBufCutAt(StrBuf *Buf, int AfternChars, const char *At)
1241 if (At != NULL){
1242 AfternChars = At - Buf->buf;
1245 if ((AfternChars < 0) || (AfternChars >= Buf->BufUsed))
1246 return;
1247 Buf->BufUsed = AfternChars;
1248 Buf->buf[Buf->BufUsed] = '\0';
1253 * Strip leading and trailing spaces from a string; with premeasured and adjusted length.
1254 * buf - the string to modify
1255 * len - length of the string.
1257 void StrBufTrim(StrBuf *Buf)
1259 int delta = 0;
1260 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1262 while ((Buf->BufUsed > delta) && (isspace(Buf->buf[delta]))){
1263 delta ++;
1265 if (delta > 0) StrBufCutLeft(Buf, delta);
1267 if (Buf->BufUsed == 0) return;
1268 while (isspace(Buf->buf[Buf->BufUsed - 1])){
1269 Buf->BufUsed --;
1271 Buf->buf[Buf->BufUsed] = '\0';
1275 void StrBufUpCase(StrBuf *Buf)
1277 char *pch, *pche;
1279 pch = Buf->buf;
1280 pche = pch + Buf->BufUsed;
1281 while (pch < pche) {
1282 *pch = toupper(*pch);
1283 pch ++;
1288 void StrBufLowerCase(StrBuf *Buf)
1290 char *pch, *pche;
1292 pch = Buf->buf;
1293 pche = pch + Buf->BufUsed;
1294 while (pch < pche) {
1295 *pch = tolower(*pch);
1296 pch ++;
1302 * \brief unhide special chars hidden to the HTML escaper
1303 * \param target buffer to put the unescaped string in
1304 * \param source buffer to unescape
1306 void StrBufEUid_unescapize(StrBuf *target, const StrBuf *source)
1308 int a, b, len;
1309 char hex[3];
1311 if (target != NULL)
1312 FlushStrBuf(target);
1314 if (source == NULL ||target == NULL)
1316 return;
1319 len = source->BufUsed;
1320 for (a = 0; a < len; ++a) {
1321 if (target->BufUsed >= target->BufSize)
1322 IncreaseBuf(target, 1, -1);
1324 if (source->buf[a] == '=') {
1325 hex[0] = source->buf[a + 1];
1326 hex[1] = source->buf[a + 2];
1327 hex[2] = 0;
1328 b = 0;
1329 sscanf(hex, "%02x", &b);
1330 target->buf[target->BufUsed] = b;
1331 target->buf[++target->BufUsed] = 0;
1332 a += 2;
1334 else {
1335 target->buf[target->BufUsed] = source->buf[a];
1336 target->buf[++target->BufUsed] = 0;
1343 * \brief hide special chars from the HTML escapers and friends
1344 * \param target buffer to put the escaped string in
1345 * \param source buffer to escape
1347 void StrBufEUid_escapize(StrBuf *target, const StrBuf *source)
1349 int i, len;
1351 if (target != NULL)
1352 FlushStrBuf(target);
1354 if (source == NULL ||target == NULL)
1356 return;
1359 len = source->BufUsed;
1360 for (i=0; i<len; ++i) {
1361 if (target->BufUsed + 4 >= target->BufSize)
1362 IncreaseBuf(target, 1, -1);
1363 if ( (isalnum(source->buf[i])) ||
1364 (source->buf[i]=='-') ||
1365 (source->buf[i]=='_') ) {
1366 target->buf[target->BufUsed++] = source->buf[i];
1368 else {
1369 sprintf(&target->buf[target->BufUsed],
1370 "=%02X",
1371 (0xFF &source->buf[i]));
1372 target->BufUsed += 3;
1375 target->buf[target->BufUsed + 1] = '\0';
1379 * \brief uses the same calling syntax as compress2(), but it
1380 * creates a stream compatible with HTTP "Content-encoding: gzip"
1382 #ifdef HAVE_ZLIB
1383 #define DEF_MEM_LEVEL 8 /*< memlevel??? */
1384 #define OS_CODE 0x03 /*< unix */
1385 int ZEXPORT compress_gzip(Bytef * dest, /*< compressed buffer*/
1386 size_t * destLen, /*< length of the compresed data */
1387 const Bytef * source, /*< source to encode */
1388 uLong sourceLen, /*< length of source to encode */
1389 int level) /*< compression level */
1391 const int gz_magic[2] = { 0x1f, 0x8b }; /* gzip magic header */
1393 /* write gzip header */
1394 snprintf((char *) dest, *destLen,
1395 "%c%c%c%c%c%c%c%c%c%c",
1396 gz_magic[0], gz_magic[1], Z_DEFLATED,
1397 0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /* xflags */ ,
1398 OS_CODE);
1400 /* normal deflate */
1401 z_stream stream;
1402 int err;
1403 stream.next_in = (Bytef *) source;
1404 stream.avail_in = (uInt) sourceLen;
1405 stream.next_out = dest + 10L; // after header
1406 stream.avail_out = (uInt) * destLen;
1407 if ((uLong) stream.avail_out != *destLen)
1408 return Z_BUF_ERROR;
1410 stream.zalloc = (alloc_func) 0;
1411 stream.zfree = (free_func) 0;
1412 stream.opaque = (voidpf) 0;
1414 err = deflateInit2(&stream, level, Z_DEFLATED, -MAX_WBITS,
1415 DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
1416 if (err != Z_OK)
1417 return err;
1419 err = deflate(&stream, Z_FINISH);
1420 if (err != Z_STREAM_END) {
1421 deflateEnd(&stream);
1422 return err == Z_OK ? Z_BUF_ERROR : err;
1424 *destLen = stream.total_out + 10L;
1426 /* write CRC and Length */
1427 uLong crc = crc32(0L, source, sourceLen);
1428 int n;
1429 for (n = 0; n < 4; ++n, ++*destLen) {
1430 dest[*destLen] = (int) (crc & 0xff);
1431 crc >>= 8;
1433 uLong len = stream.total_in;
1434 for (n = 0; n < 4; ++n, ++*destLen) {
1435 dest[*destLen] = (int) (len & 0xff);
1436 len >>= 8;
1438 err = deflateEnd(&stream);
1439 return err;
1441 #endif
1445 * Attention! If you feed this a Const String, you must maintain the uncompressed buffer yourself!
1447 int CompressBuffer(StrBuf *Buf)
1449 #ifdef HAVE_ZLIB
1450 char *compressed_data = NULL;
1451 size_t compressed_len, bufsize;
1453 bufsize = compressed_len = ((Buf->BufUsed * 101) / 100) + 100;
1454 compressed_data = malloc(compressed_len);
1456 if (compress_gzip((Bytef *) compressed_data,
1457 &compressed_len,
1458 (Bytef *) Buf->buf,
1459 (uLongf) Buf->BufUsed, Z_BEST_SPEED) == Z_OK) {
1460 if (!Buf->ConstBuf)
1461 free(Buf->buf);
1462 Buf->buf = compressed_data;
1463 Buf->BufUsed = compressed_len;
1464 Buf->BufSize = bufsize;
1465 return 1;
1466 } else {
1467 free(compressed_data);
1469 #endif /* HAVE_ZLIB */
1470 return 0;
1474 * \brief decode a buffer from base 64 encoding; destroys original
1475 * \param Buf Buffor to transform
1477 int StrBufDecodeBase64(StrBuf *Buf)
1479 char *xferbuf;
1480 size_t siz;
1481 if (Buf == NULL) return -1;
1483 xferbuf = (char*) malloc(Buf->BufSize);
1484 siz = CtdlDecodeBase64(xferbuf,
1485 Buf->buf,
1486 Buf->BufUsed);
1487 free(Buf->buf);
1488 Buf->buf = xferbuf;
1489 Buf->BufUsed = siz;
1490 return siz;
1495 * \brief remove escaped strings from i.e. the url string (like %20 for blanks)
1496 * \param Buf Buffer to translate
1497 * \param StripBlanks Reduce several blanks to one?
1499 long StrBufUnescape(StrBuf *Buf, int StripBlanks)
1501 int a, b;
1502 char hex[3];
1503 long len;
1505 while ((Buf->BufUsed > 0) && (isspace(Buf->buf[Buf->BufUsed - 1]))){
1506 Buf->buf[Buf->BufUsed - 1] = '\0';
1507 Buf->BufUsed --;
1510 a = 0;
1511 while (a < Buf->BufUsed) {
1512 if (Buf->buf[a] == '+')
1513 Buf->buf[a] = ' ';
1514 else if (Buf->buf[a] == '%') {
1515 /* don't let % chars through, rather truncate the input. */
1516 if (a + 2 > Buf->BufUsed) {
1517 Buf->buf[a] = '\0';
1518 Buf->BufUsed = a;
1520 else {
1521 hex[0] = Buf->buf[a + 1];
1522 hex[1] = Buf->buf[a + 2];
1523 hex[2] = 0;
1524 b = 0;
1525 sscanf(hex, "%02x", &b);
1526 Buf->buf[a] = (char) b;
1527 len = Buf->BufUsed - a - 2;
1528 if (len > 0)
1529 memmove(&Buf->buf[a + 1], &Buf->buf[a + 3], len);
1531 Buf->BufUsed -=2;
1534 a++;
1536 return a;
1541 * \brief RFC2047-encode a header field if necessary.
1542 * If no non-ASCII characters are found, the string
1543 * will be copied verbatim without encoding.
1545 * \param target Target buffer.
1546 * \param source Source string to be encoded.
1547 * \returns encoded length; -1 if non success.
1549 int StrBufRFC2047encode(StrBuf **target, const StrBuf *source)
1551 const char headerStr[] = "=?UTF-8?Q?";
1552 int need_to_encode = 0;
1553 int i = 0;
1554 unsigned char ch;
1556 if ((source == NULL) ||
1557 (target == NULL))
1558 return -1;
1560 while ((i < source->BufUsed) &&
1561 (!IsEmptyStr (&source->buf[i])) &&
1562 (need_to_encode == 0)) {
1563 if (((unsigned char) source->buf[i] < 32) ||
1564 ((unsigned char) source->buf[i] > 126)) {
1565 need_to_encode = 1;
1567 i++;
1570 if (!need_to_encode) {
1571 if (*target == NULL) {
1572 *target = NewStrBufPlain(source->buf, source->BufUsed);
1574 else {
1575 FlushStrBuf(*target);
1576 StrBufAppendBuf(*target, source, 0);
1578 return (*target)->BufUsed;
1580 if (*target == NULL)
1581 *target = NewStrBufPlain(NULL, sizeof(headerStr) + source->BufUsed * 2);
1582 else if (sizeof(headerStr) + source->BufUsed > (*target)->BufSize)
1583 IncreaseBuf(*target, sizeof(headerStr) + source->BufUsed, 0);
1584 memcpy ((*target)->buf, headerStr, sizeof(headerStr) - 1);
1585 (*target)->BufUsed = sizeof(headerStr) - 1;
1586 for (i=0; (i < source->BufUsed); ++i) {
1587 if ((*target)->BufUsed + 4 > (*target)->BufSize)
1588 IncreaseBuf(*target, 1, 0);
1589 ch = (unsigned char) source->buf[i];
1590 if ((ch < 32) || (ch > 126) || (ch == 61)) {
1591 sprintf(&(*target)->buf[(*target)->BufUsed], "=%02X", ch);
1592 (*target)->BufUsed += 3;
1594 else {
1595 (*target)->buf[(*target)->BufUsed] = ch;
1596 (*target)->BufUsed++;
1600 if ((*target)->BufUsed + 4 > (*target)->BufSize)
1601 IncreaseBuf(*target, 1, 0);
1603 (*target)->buf[(*target)->BufUsed++] = '?';
1604 (*target)->buf[(*target)->BufUsed++] = '=';
1605 (*target)->buf[(*target)->BufUsed] = '\0';
1606 return (*target)->BufUsed;;
1610 * \brief replaces all occurances of 'search' by 'replace'
1611 * \param buf Buffer to modify
1612 * \param search character to search
1613 * \param relpace character to replace search by
1615 void StrBufReplaceChars(StrBuf *buf, char search, char replace)
1617 long i;
1618 if (buf == NULL)
1619 return;
1620 for (i=0; i<buf->BufUsed; i++)
1621 if (buf->buf[i] == search)
1622 buf->buf[i] = replace;
1629 * Wrapper around iconv_open()
1630 * Our version adds aliases for non-standard Microsoft charsets
1631 * such as 'MS950', aliasing them to names like 'CP950'
1633 * tocode Target encoding
1634 * fromcode Source encoding
1636 void ctdl_iconv_open(const char *tocode, const char *fromcode, void *pic)
1638 #ifdef HAVE_ICONV
1639 iconv_t ic = (iconv_t)(-1) ;
1640 ic = iconv_open(tocode, fromcode);
1641 if (ic == (iconv_t)(-1) ) {
1642 char alias_fromcode[64];
1643 if ( (strlen(fromcode) == 5) && (!strncasecmp(fromcode, "MS", 2)) ) {
1644 safestrncpy(alias_fromcode, fromcode, sizeof alias_fromcode);
1645 alias_fromcode[0] = 'C';
1646 alias_fromcode[1] = 'P';
1647 ic = iconv_open(tocode, alias_fromcode);
1650 *(iconv_t *)pic = ic;
1651 #endif
1656 static inline char *FindNextEnd (const StrBuf *Buf, char *bptr)
1658 char * end;
1659 /* Find the next ?Q? */
1660 if (Buf->BufUsed - (bptr - Buf->buf) < 6)
1661 return NULL;
1663 end = strchr(bptr + 2, '?');
1665 if (end == NULL)
1666 return NULL;
1668 if ((Buf->BufUsed - (end - Buf->buf) > 3) &&
1669 ((*(end + 1) == 'B') || (*(end + 1) == 'Q')) &&
1670 (*(end + 2) == '?')) {
1671 /* skip on to the end of the cluster, the next ?= */
1672 end = strstr(end + 3, "?=");
1674 else
1675 /* sort of half valid encoding, try to find an end. */
1676 end = strstr(bptr, "?=");
1677 return end;
1681 void StrBufConvert(StrBuf *ConvertBuf, StrBuf *TmpBuf, void *pic)
1683 #ifdef HAVE_ICONV
1684 int BufSize;
1685 iconv_t ic;
1686 char *ibuf; /**< Buffer of characters to be converted */
1687 char *obuf; /**< Buffer for converted characters */
1688 size_t ibuflen; /**< Length of input buffer */
1689 size_t obuflen; /**< Length of output buffer */
1692 if (ConvertBuf->BufUsed > TmpBuf->BufSize)
1693 IncreaseBuf(TmpBuf, 0, ConvertBuf->BufUsed);
1695 ic = *(iconv_t*)pic;
1696 ibuf = ConvertBuf->buf;
1697 ibuflen = ConvertBuf->BufUsed;
1698 obuf = TmpBuf->buf;
1699 obuflen = TmpBuf->BufSize;
1701 iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
1703 /* little card game: wheres the red lady? */
1704 ibuf = ConvertBuf->buf;
1705 BufSize = ConvertBuf->BufSize;
1707 ConvertBuf->buf = TmpBuf->buf;
1708 ConvertBuf->BufSize = TmpBuf->BufSize;
1709 ConvertBuf->BufUsed = TmpBuf->BufSize - obuflen;
1710 ConvertBuf->buf[ConvertBuf->BufUsed] = '\0';
1712 TmpBuf->buf = ibuf;
1713 TmpBuf->BufSize = BufSize;
1714 TmpBuf->BufUsed = 0;
1715 TmpBuf->buf[0] = '\0';
1716 #endif
1722 inline static void DecodeSegment(StrBuf *Target,
1723 const StrBuf *DecodeMe,
1724 char *SegmentStart,
1725 char *SegmentEnd,
1726 StrBuf *ConvertBuf,
1727 StrBuf *ConvertBuf2,
1728 StrBuf *FoundCharset)
1730 StrBuf StaticBuf;
1731 char charset[128];
1732 char encoding[16];
1733 iconv_t ic = (iconv_t)(-1);
1735 /* Now we handle foreign character sets properly encoded
1736 * in RFC2047 format.
1738 StaticBuf.buf = SegmentStart;
1739 StaticBuf.BufUsed = SegmentEnd - SegmentStart;
1740 StaticBuf.BufSize = DecodeMe->BufSize - (SegmentStart - DecodeMe->buf);
1741 extract_token(charset, SegmentStart, 1, '?', sizeof charset);
1742 if (FoundCharset != NULL) {
1743 FlushStrBuf(FoundCharset);
1744 StrBufAppendBufPlain(FoundCharset, charset, -1, 0);
1746 extract_token(encoding, SegmentStart, 2, '?', sizeof encoding);
1747 StrBufExtract_token(ConvertBuf, &StaticBuf, 3, '?');
1749 *encoding = toupper(*encoding);
1750 if (*encoding == 'B') { /**< base64 */
1751 ConvertBuf2->BufUsed = CtdlDecodeBase64(ConvertBuf2->buf,
1752 ConvertBuf->buf,
1753 ConvertBuf->BufUsed);
1755 else if (*encoding == 'Q') { /**< quoted-printable */
1756 long pos;
1758 pos = 0;
1759 while (pos < ConvertBuf->BufUsed)
1761 if (ConvertBuf->buf[pos] == '_')
1762 ConvertBuf->buf[pos] = ' ';
1763 pos++;
1766 ConvertBuf2->BufUsed = CtdlDecodeQuotedPrintable(
1767 ConvertBuf2->buf,
1768 ConvertBuf->buf,
1769 ConvertBuf->BufUsed);
1771 else {
1772 StrBufAppendBuf(ConvertBuf2, ConvertBuf, 0);
1775 ctdl_iconv_open("UTF-8", charset, &ic);
1776 if (ic != (iconv_t)(-1) ) {
1777 StrBufConvert(ConvertBuf2, ConvertBuf, &ic);
1778 StrBufAppendBuf(Target, ConvertBuf2, 0);
1779 iconv_close(ic);
1781 else {
1782 StrBufAppendBufPlain(Target, HKEY("(unreadable)"), 0);
1786 * Handle subjects with RFC2047 encoding such as:
1787 * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
1789 void StrBuf_RFC822_to_Utf8(StrBuf *Target, const StrBuf *DecodeMe, const StrBuf* DefaultCharset, StrBuf *FoundCharset)
1791 StrBuf *ConvertBuf, *ConvertBuf2;
1792 char *start, *end, *next, *nextend, *ptr = NULL;
1793 iconv_t ic = (iconv_t)(-1) ;
1794 const char *eptr;
1795 int passes = 0;
1796 int i, len, delta;
1797 int illegal_non_rfc2047_encoding = 0;
1799 /* Sometimes, badly formed messages contain strings which were simply
1800 * written out directly in some foreign character set instead of
1801 * using RFC2047 encoding. This is illegal but we will attempt to
1802 * handle it anyway by converting from a user-specified default
1803 * charset to UTF-8 if we see any nonprintable characters.
1806 len = StrLength(DecodeMe);
1807 for (i=0; i<DecodeMe->BufUsed; ++i) {
1808 if ((DecodeMe->buf[i] < 32) || (DecodeMe->buf[i] > 126)) {
1809 illegal_non_rfc2047_encoding = 1;
1810 break;
1814 ConvertBuf = NewStrBufPlain(NULL, StrLength(DecodeMe));
1815 if ((illegal_non_rfc2047_encoding) &&
1816 (strcasecmp(ChrPtr(DefaultCharset), "UTF-8")) &&
1817 (strcasecmp(ChrPtr(DefaultCharset), "us-ascii")) )
1819 ctdl_iconv_open("UTF-8", ChrPtr(DefaultCharset), &ic);
1820 if (ic != (iconv_t)(-1) ) {
1821 StrBufConvert((StrBuf*)DecodeMe, ConvertBuf, &ic);///TODO: don't void const?
1822 iconv_close(ic);
1826 /* pre evaluate the first pair */
1827 nextend = end = NULL;
1828 len = StrLength(DecodeMe);
1829 start = strstr(DecodeMe->buf, "=?");
1830 eptr = DecodeMe->buf + DecodeMe->BufUsed;
1831 if (start != NULL)
1832 end = FindNextEnd (DecodeMe, start);
1833 else {
1834 StrBufAppendBuf(Target, DecodeMe, 0);
1835 FreeStrBuf(&ConvertBuf);
1836 return;
1839 ConvertBuf2 = NewStrBufPlain(NULL, StrLength(DecodeMe));
1841 if (start != DecodeMe->buf)
1842 StrBufAppendBufPlain(Target, DecodeMe->buf, start - DecodeMe->buf, 0);
1844 * Since spammers will go to all sorts of absurd lengths to get their
1845 * messages through, there are LOTS of corrupt headers out there.
1846 * So, prevent a really badly formed RFC2047 header from throwing
1847 * this function into an infinite loop.
1849 while ((start != NULL) &&
1850 (end != NULL) &&
1851 (start < eptr) &&
1852 (end < eptr) &&
1853 (passes < 20))
1855 passes++;
1856 DecodeSegment(Target,
1857 DecodeMe,
1858 start,
1859 end,
1860 ConvertBuf,
1861 ConvertBuf2,
1862 FoundCharset);
1864 next = strstr(end, "=?");
1865 nextend = NULL;
1866 if ((next != NULL) &&
1867 (next < eptr))
1868 nextend = FindNextEnd(DecodeMe, next);
1869 if (nextend == NULL)
1870 next = NULL;
1872 /* did we find two partitions */
1873 if ((next != NULL) &&
1874 ((next - end) > 2))
1876 ptr = end + 2;
1877 while ((ptr < next) &&
1878 (isspace(*ptr) ||
1879 (*ptr == '\r') ||
1880 (*ptr == '\n') ||
1881 (*ptr == '\t')))
1882 ptr ++;
1883 /* did we find a gab just filled with blanks? */
1884 if (ptr == next)
1886 memmove (end + 2,
1887 next,
1888 len - (next - start));
1890 /* now terminate the gab at the end */
1891 delta = (next - end) - 2; ////TODO: const!
1892 ((StrBuf*)DecodeMe)->BufUsed -= delta;
1893 ((StrBuf*)DecodeMe)->buf[DecodeMe->BufUsed] = '\0';
1895 /* move next to its new location. */
1896 next -= delta;
1897 nextend -= delta;
1900 /* our next-pair is our new first pair now. */
1901 ptr = end + 2;
1902 start = next;
1903 end = nextend;
1905 end = ptr;
1906 nextend = DecodeMe->buf + DecodeMe->BufUsed;
1907 if ((end != NULL) && (end < nextend)) {
1908 ptr = end;
1909 while ( (ptr < nextend) &&
1910 (isspace(*ptr) ||
1911 (*ptr == '\r') ||
1912 (*ptr == '\n') ||
1913 (*ptr == '\t')))
1914 ptr ++;
1915 if (ptr < nextend)
1916 StrBufAppendBufPlain(Target, end, nextend - end, 0);
1918 FreeStrBuf(&ConvertBuf);
1919 FreeStrBuf(&ConvertBuf2);
1924 long StrBuf_Utf8StrLen(StrBuf *Buf)
1926 return Ctdl_Utf8StrLen(Buf->buf);
1929 long StrBuf_Utf8StrCut(StrBuf *Buf, int maxlen)
1931 char *CutAt;
1933 CutAt = Ctdl_Utf8StrCut(Buf->buf, maxlen);
1934 if (CutAt != NULL) {
1935 Buf->BufUsed = CutAt - Buf->buf;
1936 Buf->buf[Buf->BufUsed] = '\0';
1938 return Buf->BufUsed;
1943 int StrBufSipLine(StrBuf *LineBuf, StrBuf *Buf, const char **Ptr)
1945 const char *aptr, *ptr, *eptr;
1946 char *optr, *xptr;
1948 if (Buf == NULL)
1949 return 0;
1951 if (*Ptr==NULL)
1952 ptr = aptr = Buf->buf;
1953 else
1954 ptr = aptr = *Ptr;
1956 optr = LineBuf->buf;
1957 eptr = Buf->buf + Buf->BufUsed;
1958 xptr = LineBuf->buf + LineBuf->BufSize;
1960 while ((*ptr != '\n') &&
1961 (*ptr != '\r') &&
1962 (ptr < eptr))
1964 *optr = *ptr;
1965 optr++; ptr++;
1966 if (optr == xptr) {
1967 LineBuf->BufUsed = optr - LineBuf->buf;
1968 IncreaseBuf(LineBuf, 1, LineBuf->BufUsed + 1);
1969 optr = LineBuf->buf + LineBuf->BufUsed;
1970 xptr = LineBuf->buf + LineBuf->BufSize;
1973 LineBuf->BufUsed = optr - LineBuf->buf;
1974 *optr = '\0';
1975 if (*ptr == '\r')
1976 ptr ++;
1977 if (*ptr == '\n')
1978 ptr ++;
1980 *Ptr = ptr;
1982 return Buf->BufUsed - (ptr - Buf->buf);