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