]> code.citadel.org Git - citadel.git/blob - libcitadel/lib/stringbuf.c
* New StrBufPeek function to replace a special char inside of the string
[citadel.git] / libcitadel / lib / stringbuf.c
1 #include <ctype.h>
2 #include <errno.h>
3 #include <string.h>
4 #include <unistd.h>
5 #include <string.h>
6 #include <stdio.h>
7 #include <sys/select.h>
8 #include <fcntl.h>
9 #define SHOW_ME_VAPPEND_PRINTF
10 #include <stdarg.h>
11 #include "libcitadel.h"
12
13
14 struct StrBuf {
15         char *buf;
16         long BufSize;
17         long BufUsed;
18         int ConstBuf;
19 };
20
21
22 inline const char *ChrPtr(const StrBuf *Str)
23 {
24         if (Str == NULL)
25                 return "";
26         return Str->buf;
27 }
28
29 inline int StrLength(const StrBuf *Str)
30 {
31         return (Str != NULL) ? Str->BufUsed : 0;
32 }
33
34 StrBuf* NewStrBuf(void)
35 {
36         StrBuf *NewBuf;
37
38         NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
39         NewBuf->buf = (char*) malloc(SIZ);
40         NewBuf->buf[0] = '\0';
41         NewBuf->BufSize = SIZ;
42         NewBuf->BufUsed = 0;
43         NewBuf->ConstBuf = 0;
44         return NewBuf;
45 }
46
47 StrBuf* NewStrBufDup(const StrBuf *CopyMe)
48 {
49         StrBuf *NewBuf;
50         
51         if (CopyMe == NULL)
52                 return NewStrBuf();
53
54         NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
55         NewBuf->buf = (char*) malloc(CopyMe->BufSize);
56         memcpy(NewBuf->buf, CopyMe->buf, CopyMe->BufUsed + 1);
57         NewBuf->BufUsed = CopyMe->BufUsed;
58         NewBuf->BufSize = CopyMe->BufSize;
59         NewBuf->ConstBuf = 0;
60         return NewBuf;
61 }
62
63 StrBuf* NewStrBufPlain(const char* ptr, int nChars)
64 {
65         StrBuf *NewBuf;
66         size_t Siz = SIZ;
67         size_t CopySize;
68
69         NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
70         if (nChars < 0)
71                 CopySize = strlen((ptr != NULL)?ptr:"");
72         else
73                 CopySize = nChars;
74
75         while (Siz <= CopySize)
76                 Siz *= 2;
77
78         NewBuf->buf = (char*) malloc(Siz);
79         NewBuf->BufSize = Siz;
80         if (ptr != NULL) {
81                 memcpy(NewBuf->buf, ptr, CopySize);
82                 NewBuf->buf[CopySize] = '\0';
83                 NewBuf->BufUsed = CopySize;
84         }
85         else {
86                 NewBuf->buf[0] = '\0';
87                 NewBuf->BufUsed = 0;
88         }
89         NewBuf->ConstBuf = 0;
90         return NewBuf;
91 }
92
93
94 StrBuf* _NewConstStrBuf(const char* StringConstant, size_t SizeOfStrConstant)
95 {
96         StrBuf *NewBuf;
97
98         NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
99         NewBuf->buf = (char*) StringConstant;
100         NewBuf->BufSize = SizeOfStrConstant;
101         NewBuf->BufUsed = SizeOfStrConstant;
102         NewBuf->ConstBuf = 1;
103         return NewBuf;
104 }
105
106
107 static int IncreaseBuf(StrBuf *Buf, int KeepOriginal, int DestSize)
108 {
109         char *NewBuf;
110         size_t NewSize = Buf->BufSize * 2;
111
112         if (Buf->ConstBuf)
113                 return -1;
114                 
115         if (DestSize > 0)
116                 while (NewSize < DestSize)
117                         NewSize *= 2;
118
119         NewBuf= (char*) malloc(NewSize);
120         if (KeepOriginal)
121         {
122                 memcpy(NewBuf, Buf->buf, Buf->BufUsed);
123         }
124         else
125         {
126                 NewBuf[0] = '\0';
127                 Buf->BufUsed = 0;
128         }
129         free (Buf->buf);
130         Buf->buf = NewBuf;
131         Buf->BufSize *= 2;
132         return Buf->BufSize;
133 }
134
135 int FlushStrBuf(StrBuf *buf)
136 {
137         if (buf->ConstBuf)
138                 return -1;       
139         buf->buf[0] ='\0';
140         buf->BufUsed = 0;
141         return 0;
142 }
143
144 void FreeStrBuf (StrBuf **FreeMe)
145 {
146         if (*FreeMe == NULL)
147                 return;
148         if (!(*FreeMe)->ConstBuf) 
149                 free((*FreeMe)->buf);
150         free(*FreeMe);
151         *FreeMe = NULL;
152 }
153
154 void HFreeStrBuf (void *VFreeMe)
155 {
156         StrBuf *FreeMe = (StrBuf*)VFreeMe;
157         if (FreeMe == NULL)
158                 return;
159         if (!FreeMe->ConstBuf) 
160                 free(FreeMe->buf);
161         free(FreeMe);
162 }
163
164 long StrTol(const StrBuf *Buf)
165 {
166         if(Buf->BufUsed > 0)
167                 return atol(Buf->buf);
168         else
169                 return 0;
170 }
171
172 int StrToi(const StrBuf *Buf)
173 {
174         if(Buf->BufUsed > 0)
175                 return atoi(Buf->buf);
176         else
177                 return 0;
178 }
179
180 long StrBufPeek(StrBuf *Buf, const char* ptr, long nThChar, char PeekValue)
181 {
182         if (Buf == NULL)
183                 return -1;
184         if (ptr != NULL)
185                 nThChar = ptr - Buf->buf;
186         if ((nThChar < 0) || (nThChar > Buf->BufUsed))
187                 return -1;
188         Buf->buf[nThChar] = PeekValue;
189         return nThChar;
190 }
191
192
193 int StrBufPlain(StrBuf *Buf, const char* ptr, int nChars)
194 {
195         size_t Siz = Buf->BufSize;
196         size_t CopySize;
197
198         if (nChars < 0)
199                 CopySize = strlen(ptr);
200         else
201                 CopySize = nChars;
202
203         while (Siz <= CopySize)
204                 Siz *= 2;
205
206         if (Siz != Buf->BufSize)
207                 IncreaseBuf(Buf, 0, Siz);
208         memcpy(Buf->buf, ptr, CopySize);
209         Buf->buf[CopySize] = '\0';
210         Buf->BufUsed = CopySize;
211         Buf->ConstBuf = 0;
212         return CopySize;
213 }
214
215 void StrBufAppendBuf(StrBuf *Buf, const StrBuf *AppendBuf, size_t Offset)
216 {
217         if ((AppendBuf == NULL) || (Buf == NULL))
218                 return;
219
220         if (Buf->BufSize - Offset < AppendBuf->BufUsed + Buf->BufUsed)
221                 IncreaseBuf(Buf, 
222                             (Buf->BufUsed > 0), 
223                             AppendBuf->BufUsed + Buf->BufUsed);
224
225         memcpy(Buf->buf + Buf->BufUsed, 
226                AppendBuf->buf + Offset, 
227                AppendBuf->BufUsed - Offset);
228         Buf->BufUsed += AppendBuf->BufUsed - Offset;
229         Buf->buf[Buf->BufUsed] = '\0';
230 }
231
232
233 void StrBufAppendBufPlain(StrBuf *Buf, const char *AppendBuf, long AppendSize, size_t Offset)
234 {
235         long aps;
236
237         if ((AppendBuf == NULL) || (Buf == NULL))
238                 return;
239
240         if (AppendSize < 0 )
241                 aps = strlen(AppendBuf + Offset);
242         else
243                 aps = AppendSize - Offset;
244
245         if (Buf->BufSize < Buf->BufUsed + aps)
246                 IncreaseBuf(Buf, (Buf->BufUsed > 0), Buf->BufUsed + aps);
247
248         memcpy(Buf->buf + Buf->BufUsed, 
249                AppendBuf + Offset, 
250                aps);
251         Buf->BufUsed += aps;
252         Buf->buf[Buf->BufUsed] = '\0';
253 }
254
255
256 inline int StrBufNum_tokens(const StrBuf *source, char tok)
257 {
258         return num_tokens(source->buf, tok);
259 }
260
261
262 int StrBufSub(StrBuf *dest, const StrBuf *Source, size_t Offset, size_t nChars)
263 {
264         size_t NCharsRemain;
265         if (Offset > Source->BufUsed)
266         {
267                 FlushStrBuf(dest);
268                 return 0;
269         }
270         if (Offset + nChars < Source->BufUsed)
271         {
272                 if (nChars > dest->BufSize)
273                         IncreaseBuf(dest, 0, nChars + 1);
274                 memcpy(dest->buf, Source->buf + Offset, nChars);
275                 dest->BufUsed = nChars;
276                 dest->buf[dest->BufUsed] = '\0';
277                 return nChars;
278         }
279         NCharsRemain = Source->BufUsed - Offset;
280         if (NCharsRemain > dest->BufSize)
281                 IncreaseBuf(dest, 0, NCharsRemain + 1);
282         memcpy(dest->buf, Source->buf + Offset, NCharsRemain);
283         dest->BufUsed = NCharsRemain;
284         dest->buf[dest->BufUsed] = '\0';
285         return NCharsRemain;
286 }
287
288
289 void StrBufVAppendPrintf(StrBuf *Buf, const char *format, va_list ap)
290 {
291         size_t nWritten = Buf->BufSize + 1;
292         size_t Offset = Buf->BufUsed;
293         size_t newused = Offset + nWritten;
294         
295         while (newused >= Buf->BufSize) {
296                 nWritten = vsnprintf(Buf->buf + Offset, 
297                                      Buf->BufSize - Offset, 
298                                      format, ap);
299                 newused = Offset + nWritten;
300                 if (newused >= Buf->BufSize)
301                         IncreaseBuf(Buf, 1, 0);
302                 else
303                         Buf->BufUsed = Offset + nWritten ;
304
305         }
306 }
307
308 void StrBufAppendPrintf(StrBuf *Buf, const char *format, ...)
309 {
310         size_t nWritten = Buf->BufSize + 1;
311         size_t Offset = Buf->BufUsed;
312         size_t newused = Offset + nWritten;
313         va_list arg_ptr;
314         
315         while (newused >= Buf->BufSize) {
316                 va_start(arg_ptr, format);
317                 nWritten = vsnprintf(Buf->buf + Offset, 
318                                      Buf->BufSize - Offset, 
319                                      format, arg_ptr);
320                 va_end(arg_ptr);
321                 newused = Offset + nWritten;
322                 if (newused >= Buf->BufSize)
323                         IncreaseBuf(Buf, 1, 0);
324                 else
325                         Buf->BufUsed = Offset + nWritten ;
326
327         }
328 }
329
330 void StrBufPrintf(StrBuf *Buf, const char *format, ...)
331 {
332         size_t nWritten = Buf->BufSize + 1;
333         va_list arg_ptr;
334         
335         while (nWritten >= Buf->BufSize) {
336                 va_start(arg_ptr, format);
337                 nWritten = vsnprintf(Buf->buf, Buf->BufSize, format, arg_ptr);
338                 va_end(arg_ptr);
339                 Buf->BufUsed = nWritten ;
340                 if (nWritten >= Buf->BufSize)
341                         IncreaseBuf(Buf, 0, 0);
342         }
343 }
344
345
346 /**
347  * \brief a string tokenizer
348  * \param dest Destination StringBuffer
349  * \param Source StringBuffer to read into
350  * \param separator tokenizer param
351  * \returns -1 if not found, else length of token.
352  */
353 int StrBufExtract_token(StrBuf *dest, const StrBuf *Source, int parmnum, char separator)
354 {
355         const char *s, *e;              //* source * /
356         int len = 0;                    //* running total length of extracted string * /
357         int current_token = 0;          //* token currently being processed * /
358
359         if ((Source == NULL) || (Source->BufUsed ==0)) {
360                 return(-1);
361         }
362         s = Source->buf;
363         e = s + Source->BufUsed;
364         if (dest == NULL) {
365                 return(-1);
366         }
367
368         //cit_backtrace();
369         //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
370         dest->buf[0] = '\0';
371         dest->BufUsed = 0;
372
373         while ((s<e) && !IsEmptyStr(s)) {
374                 if (*s == separator) {
375                         ++current_token;
376                 }
377                 if (len >= dest->BufSize)
378                         if (!IncreaseBuf(dest, 1, -1))
379                                 break;
380                 if ( (current_token == parmnum) && 
381                      (*s != separator)) {
382                         dest->buf[len] = *s;
383                         ++len;
384                 }
385                 else if (current_token > parmnum) {
386                         break;
387                 }
388                 ++s;
389         }
390         
391         dest->buf[len] = '\0';
392         dest->BufUsed = len;
393                 
394         if (current_token < parmnum) {
395                 //lprintf (CTDL_DEBUG,"test <!: %s\n", dest);
396                 return(-1);
397         }
398         //lprintf (CTDL_DEBUG,"test <: %d; %s\n", len, dest);
399         return(len);
400 }
401
402
403 /*
404  * extract_int()  -  extract an int parm w/o supplying a buffer
405  */
406 int StrBufExtract_int(const StrBuf* Source, int parmnum, char separator)
407 {
408         StrBuf tmp;
409         char buf[64];
410         
411         tmp.buf = buf;
412         buf[0] = '\0';
413         tmp.BufSize = 64;
414         tmp.BufUsed = 0;
415         if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
416                 return(atoi(buf));
417         else
418                 return 0;
419 }
420
421 /*
422  * extract_long()  -  extract an long parm w/o supplying a buffer
423  */
424 long StrBufExtract_long(const StrBuf* Source, int parmnum, char separator)
425 {
426         StrBuf tmp;
427         char buf[64];
428         
429         tmp.buf = buf;
430         buf[0] = '\0';
431         tmp.BufSize = 64;
432         tmp.BufUsed = 0;
433         if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
434                 return(atoi(buf));
435         else
436                 return 0;
437 }
438
439
440 /*
441  * extract_unsigned_long() - extract an unsigned long parm
442  */
443 unsigned long StrBufExtract_unsigned_long(const StrBuf* Source, int parmnum, char separator)
444 {
445         StrBuf tmp;
446         char buf[64];
447         
448         tmp.buf = buf;
449         buf[0] = '\0';
450         tmp.BufSize = 64;
451         tmp.BufUsed = 0;
452         if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
453                 return(atoi(buf));
454         else 
455                 return 0;
456 }
457
458
459
460 /**
461  * \brief Input binary data from socket
462  * \param buf the buffer to get the input to
463  * \param bytes the maximal number of bytes to read
464  */
465 int StrBufTCP_read_line(StrBuf *buf, int *fd, int append, const char **Error)
466 {
467         int len, rlen, slen;
468
469         if (!append)
470                 FlushStrBuf(buf);
471
472         slen = len = buf->BufUsed;
473         while (1) {
474                 rlen = read(*fd, &buf->buf[len], 1);
475                 if (rlen < 1) {
476                         *Error = strerror(errno);
477                         
478                         close(*fd);
479                         *fd = -1;
480                         
481                         return -1;
482                 }
483                 if (buf->buf[len] == '\n')
484                         break;
485                 if (buf->buf[len] != '\r')
486                         len ++;
487                 if (!(len < buf->BufSize)) {
488                         buf->BufUsed = len;
489                         buf->buf[len+1] = '\0';
490                         IncreaseBuf(buf, 1, -1);
491                 }
492         }
493         buf->BufUsed = len;
494         buf->buf[len] = '\0';
495         return len - slen;
496 }
497
498 /**
499  * \brief Input binary data from socket
500  * \param buf the buffer to get the input to
501  * \param bytes the maximal number of bytes to read
502  */
503 int StrBufReadBLOB(StrBuf *Buf, int *fd, int append, long nBytes, const char **Error)
504 {
505         fd_set wset;
506         int fdflags;
507         int len, rlen, slen;
508         int nRead = 0;
509         char *ptr;
510
511         if ((Buf == NULL) || (*fd == -1))
512                 return -1;
513         if (!append)
514                 FlushStrBuf(Buf);
515         if (Buf->BufUsed + nBytes > Buf->BufSize)
516                 IncreaseBuf(Buf, 1, Buf->BufUsed + nBytes);
517
518         ptr = Buf->buf + Buf->BufUsed;
519
520         slen = len = Buf->BufUsed;
521
522         fdflags = fcntl(*fd, F_GETFL);
523
524         while (nRead < nBytes) {
525                if ((fdflags & O_NONBLOCK) == O_NONBLOCK) {
526                         FD_ZERO(&wset);
527                         FD_SET(*fd, &wset);
528                         if (select(*fd + 1, NULL, &wset, NULL, NULL) == -1) {
529                                 *Error = strerror(errno);
530                                 return -1;
531                         }
532                 }
533
534                 if ((rlen = read(*fd, 
535                                  ptr,
536                                  nBytes - nRead)) == -1) {
537                         close(*fd);
538                         *fd = -1;
539                         *Error = strerror(errno);
540                         return rlen;
541                 }
542                 nRead += rlen;
543                 Buf->BufUsed += rlen;
544         }
545         Buf->buf[Buf->BufUsed] = '\0';
546         return nRead;
547 }
548
549 void StrBufCutLeft(StrBuf *Buf, int nChars)
550 {
551         if (nChars >= Buf->BufUsed) {
552                 FlushStrBuf(Buf);
553                 return;
554         }
555         memmove(Buf->buf, Buf->buf + nChars, Buf->BufUsed - nChars);
556         Buf->BufUsed -= nChars;
557         Buf->buf[Buf->BufUsed] = '\0';
558 }
559
560 void StrBufCutRight(StrBuf *Buf, int nChars)
561 {
562         if (nChars >= Buf->BufUsed) {
563                 FlushStrBuf(Buf);
564                 return;
565         }
566         Buf->BufUsed -= nChars;
567         Buf->buf[Buf->BufUsed] = '\0';
568 }
569
570
571 /*
572  * string conversion function
573  */
574 void StrBufEUid_unescapize(StrBuf *target, const StrBuf *source) 
575 {
576         int a, b, len;
577         char hex[3];
578
579         if (target != NULL)
580                 FlushStrBuf(target);
581
582         if (source == NULL ||target == NULL)
583         {
584                 return;
585         }
586
587         len = source->BufUsed;
588         for (a = 0; a < len; ++a) {
589                 if (target->BufUsed >= target->BufSize)
590                         IncreaseBuf(target, 1, -1);
591
592                 if (source->buf[a] == '=') {
593                         hex[0] = source->buf[a + 1];
594                         hex[1] = source->buf[a + 2];
595                         hex[2] = 0;
596                         b = 0;
597                         sscanf(hex, "%02x", &b);
598                         target->buf[target->BufUsed] = b;
599                         target->buf[++target->BufUsed] = 0;
600                         a += 2;
601                 }
602                 else {
603                         target->buf[target->BufUsed] = source->buf[a];
604                         target->buf[++target->BufUsed] = 0;
605                 }
606         }
607 }
608
609
610 /*
611  * string conversion function
612  */
613 void StrBufEUid_escapize(StrBuf *target, const StrBuf *source) 
614 {
615         int i, len;
616
617         if (target != NULL)
618                 FlushStrBuf(target);
619
620         if (source == NULL ||target == NULL)
621         {
622                 return;
623         }
624
625         len = source->BufUsed;
626         for (i=0; i<len; ++i) {
627                 if (target->BufUsed + 4 >= target->BufSize)
628                         IncreaseBuf(target, 1, -1);
629                 if ( (isalnum(source->buf[i])) || 
630                      (source->buf[i]=='-') || 
631                      (source->buf[i]=='_') ) {
632                         target->buf[target->BufUsed++] = source->buf[i];
633                 }
634                 else {
635                         sprintf(&target->buf[target->BufUsed], 
636                                 "=%02X", 
637                                 (0xFF &source->buf[i]));
638                         target->BufUsed += 3;
639                 }
640         }
641         target->buf[target->BufUsed + 1] = '\0';
642 }
643
644 /*
645  * \brief uses the same calling syntax as compress2(), but it
646  * creates a stream compatible with HTTP "Content-encoding: gzip"
647  */
648 #ifdef HAVE_ZLIB
649 #define DEF_MEM_LEVEL 8 /*< memlevel??? */
650 #define OS_CODE 0x03    /*< unix */
651 int ZEXPORT compress_gzip(Bytef * dest,         /*< compressed buffer*/
652                           size_t * destLen,     /*< length of the compresed data */
653                           const Bytef * source, /*< source to encode */
654                           uLong sourceLen,      /*< length of source to encode */
655                           int level)            /*< compression level */
656 {
657         const int gz_magic[2] = { 0x1f, 0x8b }; /* gzip magic header */
658
659         /* write gzip header */
660         snprintf((char *) dest, *destLen, 
661                  "%c%c%c%c%c%c%c%c%c%c",
662                  gz_magic[0], gz_magic[1], Z_DEFLATED,
663                  0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /* xflags */ ,
664                  OS_CODE);
665
666         /* normal deflate */
667         z_stream stream;
668         int err;
669         stream.next_in = (Bytef *) source;
670         stream.avail_in = (uInt) sourceLen;
671         stream.next_out = dest + 10L;   // after header
672         stream.avail_out = (uInt) * destLen;
673         if ((uLong) stream.avail_out != *destLen)
674                 return Z_BUF_ERROR;
675
676         stream.zalloc = (alloc_func) 0;
677         stream.zfree = (free_func) 0;
678         stream.opaque = (voidpf) 0;
679
680         err = deflateInit2(&stream, level, Z_DEFLATED, -MAX_WBITS,
681                            DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
682         if (err != Z_OK)
683                 return err;
684
685         err = deflate(&stream, Z_FINISH);
686         if (err != Z_STREAM_END) {
687                 deflateEnd(&stream);
688                 return err == Z_OK ? Z_BUF_ERROR : err;
689         }
690         *destLen = stream.total_out + 10L;
691
692         /* write CRC and Length */
693         uLong crc = crc32(0L, source, sourceLen);
694         int n;
695         for (n = 0; n < 4; ++n, ++*destLen) {
696                 dest[*destLen] = (int) (crc & 0xff);
697                 crc >>= 8;
698         }
699         uLong len = stream.total_in;
700         for (n = 0; n < 4; ++n, ++*destLen) {
701                 dest[*destLen] = (int) (len & 0xff);
702                 len >>= 8;
703         }
704         err = deflateEnd(&stream);
705         return err;
706 }
707 #endif
708
709
710 /**
711  * Attention! If you feed this a Const String, you must maintain the uncompressed buffer yourself!
712  */
713 int CompressBuffer(StrBuf *Buf)
714 {
715 #ifdef HAVE_ZLIB
716         char *compressed_data = NULL;
717         size_t compressed_len, bufsize;
718         
719         bufsize = compressed_len = ((Buf->BufUsed * 101) / 100) + 100;
720         compressed_data = malloc(compressed_len);
721         
722         if (compress_gzip((Bytef *) compressed_data,
723                           &compressed_len,
724                           (Bytef *) Buf->buf,
725                           (uLongf) Buf->BufUsed, Z_BEST_SPEED) == Z_OK) {
726                 if (!ConstBuf)
727                         free(Buf->buf);
728                 Buf->buf = compressed_data;
729                 Buf->BufUsed = compressed_len;
730                 Buf->BufSize = bufsize;
731                 return 1;
732         } else {
733                 free(compressed_data);
734         }
735 #endif  /* HAVE_ZLIB */
736         return 0;
737 }
738
739 int StrBufDecodeBase64(StrBuf *Buf)
740 {
741         char *xferbuf;
742         size_t siz;
743         if (Buf == NULL) return -1;
744
745         xferbuf = (char*) malloc(Buf->BufSize);
746         siz = CtdlDecodeBase64(xferbuf,
747                                Buf->buf,
748                                Buf->BufUsed);
749         free(Buf->buf);
750         Buf->buf = xferbuf;
751         Buf->BufUsed = siz;
752         return siz;
753 }
754
755
756 /*   
757  * remove escaped strings from i.e. the url string (like %20 for blanks)
758  */
759 long StrBufUnescape(StrBuf *Buf, int StripBlanks)
760 {
761         int a, b;
762         char hex[3];
763         long len;
764
765         while ((Buf->BufUsed > 0) && (isspace(Buf->buf[Buf->BufUsed - 1]))){
766                 Buf->buf[Buf->BufUsed - 1] = '\0';
767                 Buf->BufUsed --;
768         }
769
770         a = 0; 
771         while (a < Buf->BufUsed) {
772                 if (Buf->buf[a] == '+')
773                         Buf->buf[a] = ' ';
774                 else if (Buf->buf[a] == '%') {
775                         /* don't let % chars through, rather truncate the input. */
776                         if (a + 2 > Buf->BufUsed) {
777                                 Buf->buf[a] = '\0';
778                                 Buf->BufUsed = a;
779                         }
780                         else {                  
781                                 hex[0] = Buf->buf[a + 1];
782                                 hex[1] = Buf->buf[a + 2];
783                                 hex[2] = 0;
784                                 b = 0;
785                                 sscanf(hex, "%02x", &b);
786                                 Buf->buf[a] = (char) b;
787                                 len = Buf->BufUsed - a - 2;
788                                 if (len > 0)
789                                         memmove(&Buf->buf[a + 1], &Buf->buf[a + 3], len);
790                         
791                                 Buf->BufUsed -=2;
792                         }
793                 }
794                 a++;
795         }
796         return a;
797 }
798
799
800 /**
801  * \brief       RFC2047-encode a header field if necessary.
802  *              If no non-ASCII characters are found, the string
803  *              will be copied verbatim without encoding.
804  *
805  * \param       target          Target buffer.
806  * \param       source          Source string to be encoded.
807  * \returns     encoded length; -1 if non success.
808  */
809 int StrBufRFC2047encode(StrBuf **target, const StrBuf *source)
810 {
811         const char headerStr[] = "=?UTF-8?Q?";
812         int need_to_encode = 0;
813         int i = 0;
814         unsigned char ch;
815
816         if ((source == NULL) || 
817             (target == NULL))
818             return -1;
819
820         while ((i < source->BufUsed) &&
821                (!IsEmptyStr (&source->buf[i])) &&
822                (need_to_encode == 0)) {
823                 if (((unsigned char) source->buf[i] < 32) || 
824                     ((unsigned char) source->buf[i] > 126)) {
825                         need_to_encode = 1;
826                 }
827                 i++;
828         }
829
830         if (!need_to_encode) {
831                 if (*target == NULL) {
832                         *target = NewStrBufPlain(source->buf, source->BufUsed);
833                 }
834                 else {
835                         FlushStrBuf(*target);
836                         StrBufAppendBuf(*target, source, 0);
837                 }
838                 return (*target)->BufUsed;
839         }
840         if (*target == NULL)
841                 *target = NewStrBufPlain(NULL, sizeof(headerStr) + source->BufUsed * 2);
842         else if (sizeof(headerStr) + source->BufUsed > (*target)->BufSize)
843                 IncreaseBuf(*target, sizeof(headerStr) + source->BufUsed, 0);
844         memcpy ((*target)->buf, headerStr, sizeof(headerStr) - 1);
845         (*target)->BufUsed = sizeof(headerStr) - 1;
846         for (i=0; (i < source->BufUsed); ++i) {
847                 if ((*target)->BufUsed + 4 > (*target)->BufSize)
848                         IncreaseBuf(*target, 1, 0);
849                 ch = (unsigned char) source->buf[i];
850                 if ((ch < 32) || (ch > 126) || (ch == 61)) {
851                         sprintf(&(*target)->buf[(*target)->BufUsed], "=%02X", ch);
852                         (*target)->BufUsed += 3;
853                 }
854                 else {
855                         (*target)->buf[(*target)->BufUsed] = ch;
856                         (*target)->BufUsed++;
857                 }
858         }
859         
860         if ((*target)->BufUsed + 4 > (*target)->BufSize)
861                 IncreaseBuf(*target, 1, 0);
862
863         (*target)->buf[(*target)->BufUsed++] = '?';
864         (*target)->buf[(*target)->BufUsed++] = '=';
865         (*target)->buf[(*target)->BufUsed] = '\0';
866         return (*target)->BufUsed;;
867 }
868
869 void StrBufReplaceChars(StrBuf *buf, char search, char replace)
870 {
871         long i;
872         if (buf == NULL)
873                 return;
874         for (i=0; i<buf->BufUsed; i++)
875                 if (buf->buf[i] == search)
876                         buf->buf[i] = replace;
877
878 }