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