* fix new wprintf backend to handle buffer increases
[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(StrBuf *Str)
23 {
24         if (Str == NULL)
25                 return "";
26         return Str->buf;
27 }
28
29 inline int StrLength(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
48 StrBuf* NewStrBufPlain(const char* ptr, int nChars)
49 {
50         StrBuf *NewBuf;
51         size_t Siz = SIZ;
52         size_t CopySize;
53
54         NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
55         if (nChars < 0)
56                 CopySize = strlen((ptr != NULL)?ptr:"");
57         else
58                 CopySize = nChars;
59
60         while (Siz <= CopySize)
61                 Siz *= 2;
62
63         NewBuf->buf = (char*) malloc(Siz);
64         NewBuf->BufSize = Siz;
65         if (ptr != NULL) {
66                 memcpy(NewBuf->buf, ptr, CopySize);
67                 NewBuf->buf[CopySize] = '\0';
68                 NewBuf->BufUsed = CopySize;
69         }
70         else {
71                 NewBuf->buf[0] = '\0';
72                 NewBuf->BufUsed = 0;
73         }
74         NewBuf->ConstBuf = 0;
75         return NewBuf;
76 }
77
78
79 StrBuf* _NewConstStrBuf(const char* StringConstant, size_t SizeOfStrConstant)
80 {
81         StrBuf *NewBuf;
82
83         NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
84         NewBuf->buf = (char*) StringConstant;
85         NewBuf->BufSize = SizeOfStrConstant;
86         NewBuf->BufUsed = SizeOfStrConstant;
87         NewBuf->ConstBuf = 1;
88         return NewBuf;
89 }
90
91
92 static int IncreaseBuf(StrBuf *Buf, int KeepOriginal, int DestSize)
93 {
94         char *NewBuf;
95         size_t NewSize = Buf->BufSize * 2;
96
97         if (Buf->ConstBuf)
98                 return -1;
99                 
100         if (DestSize > 0)
101                 while (NewSize < DestSize)
102                         NewSize *= 2;
103
104         NewBuf= (char*) malloc(NewSize);
105         if (KeepOriginal)
106         {
107                 memcpy(NewBuf, Buf->buf, Buf->BufUsed);
108         }
109         else
110         {
111                 NewBuf[0] = '\0';
112                 Buf->BufUsed = 0;
113         }
114         free (Buf->buf);
115         Buf->buf = NewBuf;
116         Buf->BufSize *= 2;
117         return Buf->BufSize;
118 }
119
120 int FlushStrBuf(StrBuf *buf)
121 {
122         if (buf->ConstBuf)
123                 return -1;       
124         buf->buf[0] ='\0';
125         buf->BufUsed = 0;
126         return 0;
127 }
128
129 void FreeStrBuf (StrBuf **FreeMe)
130 {
131         if (*FreeMe == NULL)
132                 return;
133         if (!(*FreeMe)->ConstBuf) 
134                 free((*FreeMe)->buf);
135         free(*FreeMe);
136         *FreeMe = NULL;
137 }
138
139 void HFreeStrBuf (void *VFreeMe)
140 {
141         StrBuf *FreeMe = (StrBuf*)VFreeMe;
142         if (FreeMe == NULL)
143                 return;
144         if (!FreeMe->ConstBuf) 
145                 free(FreeMe->buf);
146         free(FreeMe);
147 }
148
149 long StrTol(StrBuf *Buf)
150 {
151         if(Buf->BufUsed > 0)
152                 return atol(Buf->buf);
153         else
154                 return 0;
155 }
156
157 int StrBufPlain(StrBuf *Buf, const char* ptr, int nChars)
158 {
159         size_t Siz = Buf->BufSize;
160         size_t CopySize;
161
162         if (nChars < 0)
163                 CopySize = strlen(ptr);
164         else
165                 CopySize = nChars;
166
167         while (Siz <= CopySize)
168                 Siz *= 2;
169
170         if (Siz != Buf->BufSize)
171                 IncreaseBuf(Buf, 0, Siz);
172         memcpy(Buf->buf, ptr, CopySize);
173         Buf->buf[CopySize] = '\0';
174         Buf->BufUsed = CopySize;
175         Buf->ConstBuf = 0;
176         return CopySize;
177 }
178
179 void StrBufAppendBuf(StrBuf *Buf, StrBuf *AppendBuf, size_t Offset)
180 {
181         if ((AppendBuf == NULL) || (Buf == NULL))
182                 return;
183
184         if (Buf->BufSize - Offset < AppendBuf->BufUsed + Buf->BufUsed)
185                 IncreaseBuf(Buf, 
186                             (Buf->BufUsed > 0), 
187                             AppendBuf->BufUsed + Buf->BufUsed);
188
189         memcpy(Buf->buf + Buf->BufUsed, 
190                AppendBuf->buf + Offset, 
191                AppendBuf->BufUsed - Offset);
192         Buf->BufUsed += AppendBuf->BufUsed - Offset;
193         Buf->buf[Buf->BufUsed] = '\0';
194 }
195
196
197 void StrBufAppendBufPlain(StrBuf *Buf, const char *AppendBuf, long AppendSize, size_t Offset)
198 {
199         long aps;
200
201         if ((AppendBuf == NULL) || (Buf == NULL))
202                 return;
203
204         if (AppendSize < 0 )
205                 aps = strlen(AppendBuf + Offset);
206         else
207                 aps = AppendSize - Offset;
208
209         if (Buf->BufSize < Buf->BufUsed + aps)
210                 IncreaseBuf(Buf, (Buf->BufUsed > 0), Buf->BufUsed + aps);
211
212         memcpy(Buf->buf + Buf->BufUsed, 
213                AppendBuf + Offset, 
214                aps);
215         Buf->BufUsed += aps;
216         Buf->buf[Buf->BufUsed] = '\0';
217 }
218
219
220 inline int StrBufNum_tokens(const StrBuf *source, char tok)
221 {
222         return num_tokens(source->buf, tok);
223 }
224
225
226 int StrBufSub(StrBuf *dest, const StrBuf *Source, size_t Offset, size_t nChars)
227 {
228         size_t NCharsRemain;
229         if (Offset > Source->BufUsed)
230         {
231                 FlushStrBuf(dest);
232                 return 0;
233         }
234         if (Offset + nChars < Source->BufUsed)
235         {
236                 if (nChars > dest->BufSize)
237                         IncreaseBuf(dest, 0, nChars + 1);
238                 memcpy(dest->buf, Source->buf + Offset, nChars);
239                 dest->BufUsed = nChars;
240                 dest->buf[dest->BufUsed] = '\0';
241                 return nChars;
242         }
243         NCharsRemain = Source->BufUsed - Offset;
244         if (NCharsRemain > dest->BufSize)
245                 IncreaseBuf(dest, 0, NCharsRemain + 1);
246         memcpy(dest->buf, Source->buf + Offset, NCharsRemain);
247         dest->BufUsed = NCharsRemain;
248         dest->buf[dest->BufUsed] = '\0';
249         return NCharsRemain;
250 }
251
252
253 void StrBufVAppendPrintf(StrBuf *Buf, const char *format, va_list ap)
254 {
255         size_t nWritten = Buf->BufSize + 1;
256         size_t Offset = Buf->BufUsed;
257         size_t newused = Offset + nWritten;
258         
259         while (newused >= Buf->BufSize) {
260                 nWritten = vsnprintf(Buf->buf + Offset, 
261                                      Buf->BufSize - Offset, 
262                                      format, ap);
263                 newused = Offset + nWritten;
264                 if (newused >= Buf->BufSize)
265                         IncreaseBuf(Buf, 1, 0);
266                 else
267                         Buf->BufUsed = Offset + nWritten ;
268
269         }
270 }
271
272 void StrBufPrintf(StrBuf *Buf, const char *format, ...)
273 {
274         size_t nWritten = Buf->BufSize + 1;
275         va_list arg_ptr;
276         
277         while (nWritten >= Buf->BufSize) {
278                 va_start(arg_ptr, format);
279                 nWritten = vsnprintf(Buf->buf, Buf->BufSize, format, arg_ptr);
280                 va_end(arg_ptr);
281                 Buf->BufUsed = nWritten ;
282                 if (nWritten >= Buf->BufSize)
283                         IncreaseBuf(Buf, 0, 0);
284         }
285 }
286
287
288 /**
289  * \brief a string tokenizer
290  * \param dest Destination StringBuffer
291  * \param Source StringBuffer to read into
292  * \param separator tokenizer param
293  * \returns -1 if not found, else length of token.
294  */
295 int StrBufExtract_token(StrBuf *dest, const StrBuf *Source, int parmnum, char separator)
296 {
297         const char *s, *e;              //* source * /
298         int len = 0;                    //* running total length of extracted string * /
299         int current_token = 0;          //* token currently being processed * /
300
301         if ((Source == NULL) || (Source->BufUsed ==0)) {
302                 return(-1);
303         }
304         s = Source->buf;
305         e = s + Source->BufUsed;
306         if (dest == NULL) {
307                 return(-1);
308         }
309
310         //cit_backtrace();
311         //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
312         dest->buf[0] = '\0';
313         dest->BufUsed = 0;
314
315         while ((s<e) && !IsEmptyStr(s)) {
316                 if (*s == separator) {
317                         ++current_token;
318                 }
319                 if (len >= dest->BufSize)
320                         if (!IncreaseBuf(dest, 1, -1))
321                                 break;
322                 if ( (current_token == parmnum) && 
323                      (*s != separator)) {
324                         dest->buf[len] = *s;
325                         ++len;
326                 }
327                 else if (current_token > parmnum) {
328                         break;
329                 }
330                 ++s;
331         }
332         
333         dest->buf[len] = '\0';
334         dest->BufUsed = len;
335                 
336         if (current_token < parmnum) {
337                 //lprintf (CTDL_DEBUG,"test <!: %s\n", dest);
338                 return(-1);
339         }
340         //lprintf (CTDL_DEBUG,"test <: %d; %s\n", len, dest);
341         return(len);
342 }
343
344
345 /*
346  * extract_int()  -  extract an int parm w/o supplying a buffer
347  */
348 int StrBufExtract_int(const StrBuf* Source, int parmnum, char separator)
349 {
350         StrBuf tmp;
351         char buf[64];
352         
353         tmp.buf = buf;
354         buf[0] = '\0';
355         tmp.BufSize = 64;
356         tmp.BufUsed = 0;
357         if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
358                 return(atoi(buf));
359         else
360                 return 0;
361 }
362
363 /*
364  * extract_long()  -  extract an long parm w/o supplying a buffer
365  */
366 long StrBufExtract_long(const StrBuf* Source, int parmnum, char separator)
367 {
368         StrBuf tmp;
369         char buf[64];
370         
371         tmp.buf = buf;
372         buf[0] = '\0';
373         tmp.BufSize = 64;
374         tmp.BufUsed = 0;
375         if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
376                 return(atoi(buf));
377         else
378                 return 0;
379 }
380
381
382 /*
383  * extract_unsigned_long() - extract an unsigned long parm
384  */
385 unsigned long StrBufExtract_unsigned_long(const StrBuf* Source, int parmnum, char separator)
386 {
387         StrBuf tmp;
388         char buf[64];
389         
390         tmp.buf = buf;
391         buf[0] = '\0';
392         tmp.BufSize = 64;
393         tmp.BufUsed = 0;
394         if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
395                 return(atoi(buf));
396         else 
397                 return 0;
398 }
399
400
401
402 /**
403  * \brief Input binary data from socket
404  * \param buf the buffer to get the input to
405  * \param bytes the maximal number of bytes to read
406  */
407 int StrBufTCP_read_line(StrBuf *buf, int *fd, int append, const char **Error)
408 {
409         int len, rlen, slen;
410
411         if (!append)
412                 FlushStrBuf(buf);
413
414         slen = len = buf->BufUsed;
415         while (1) {
416                 rlen = read(*fd, &buf->buf[len], 1);
417                 if (rlen < 1) {
418                         *Error = strerror(errno);
419                         
420                         close(*fd);
421                         *fd = -1;
422                         
423                         return -1;
424                 }
425                 if (buf->buf[len] == '\n')
426                         break;
427                 if (buf->buf[len] != '\r')
428                         len ++;
429                 if (!(len < buf->BufSize)) {
430                         buf->BufUsed = len;
431                         buf->buf[len+1] = '\0';
432                         IncreaseBuf(buf, 1, -1);
433                 }
434         }
435         buf->BufUsed = len;
436         buf->buf[len] = '\0';
437         return len - slen;
438 }
439
440 /**
441  * \brief Input binary data from socket
442  * \param buf the buffer to get the input to
443  * \param bytes the maximal number of bytes to read
444  */
445 int StrBufReadBLOB(StrBuf *Buf, int *fd, int append, long nBytes, const char **Error)
446 {
447         fd_set wset;
448         int fdflags;
449         int len, rlen, slen;
450         int nRead = 0;
451         char *ptr;
452
453         if ((Buf == NULL) || (*fd == -1))
454                 return -1;
455         if (!append)
456                 FlushStrBuf(Buf);
457         if (Buf->BufUsed + nBytes > Buf->BufSize)
458                 IncreaseBuf(Buf, 1, Buf->BufUsed + nBytes);
459
460         ptr = Buf->buf + Buf->BufUsed;
461
462         slen = len = Buf->BufUsed;
463
464         fdflags = fcntl(*fd, F_GETFL);
465
466         while (nRead < nBytes) {
467                if ((fdflags & O_NONBLOCK) == O_NONBLOCK) {
468                         FD_ZERO(&wset);
469                         FD_SET(*fd, &wset);
470                         if (select(*fd + 1, NULL, &wset, NULL, NULL) == -1) {
471                                 *Error = strerror(errno);
472                                 return -1;
473                         }
474                 }
475
476                 if ((rlen = read(*fd, 
477                                  ptr,
478                                  nBytes - nRead)) == -1) {
479                         close(*fd);
480                         *fd = -1;
481                         *Error = strerror(errno);
482                         return rlen;
483                 }
484                 nRead += rlen;
485                 Buf->BufUsed += rlen;
486         }
487         Buf->buf[Buf->BufUsed] = '\0';
488         return nRead;
489 }
490
491 void StrBufCutLeft(StrBuf *Buf, int nChars)
492 {
493         if (nChars >= Buf->BufUsed) {
494                 FlushStrBuf(Buf);
495                 return;
496         }
497         memmove(Buf->buf, Buf->buf + nChars, Buf->BufUsed - nChars);
498         Buf->BufUsed -= nChars;
499         Buf->buf[Buf->BufUsed] = '\0';
500 }
501
502 void StrBufCutRight(StrBuf *Buf, int nChars)
503 {
504         if (nChars >= Buf->BufUsed) {
505                 FlushStrBuf(Buf);
506                 return;
507         }
508         Buf->BufUsed -= nChars;
509         Buf->buf[Buf->BufUsed] = '\0';
510 }
511
512
513 /*
514  * string conversion function
515  */
516 void StrBufEUid_unescapize(StrBuf *target, StrBuf *source) 
517 {
518         int a, b, len;
519         char hex[3];
520
521         if (target != NULL)
522                 FlushStrBuf(target);
523
524         if (source == NULL ||target == NULL)
525         {
526                 return;
527         }
528
529         len = source->BufUsed;
530         for (a = 0; a < len; ++a) {
531                 if (target->BufUsed >= target->BufSize)
532                         IncreaseBuf(target, 1, -1);
533
534                 if (source->buf[a] == '=') {
535                         hex[0] = source->buf[a + 1];
536                         hex[1] = source->buf[a + 2];
537                         hex[2] = 0;
538                         b = 0;
539                         sscanf(hex, "%02x", &b);
540                         target->buf[target->BufUsed] = b;
541                         target->buf[++target->BufUsed] = 0;
542                         a += 2;
543                 }
544                 else {
545                         target->buf[target->BufUsed] = source->buf[a];
546                         target->buf[++target->BufUsed] = 0;
547                 }
548         }
549 }
550
551
552 /*
553  * string conversion function
554  */
555 void StrBufEUid_escapize(StrBuf *target, StrBuf *source) 
556 {
557         int i, len;
558
559         if (target != NULL)
560                 FlushStrBuf(target);
561
562         if (source == NULL ||target == NULL)
563         {
564                 return;
565         }
566
567         len = source->BufUsed;
568         for (i=0; i<len; ++i) {
569                 if (target->BufUsed + 4 >= target->BufSize)
570                         IncreaseBuf(target, 1, -1);
571                 if ( (isalnum(source->buf[i])) || 
572                      (source->buf[i]=='-') || 
573                      (source->buf[i]=='_') ) {
574                         target->buf[target->BufUsed++] = source->buf[i];
575                 }
576                 else {
577                         sprintf(&target->buf[target->BufUsed], 
578                                 "=%02X", 
579                                 (0xFF &source->buf[i]));
580                         target->BufUsed += 3;
581                 }
582         }
583         target->buf[target->BufUsed + 1] = '\0';
584 }
585
586 /*
587  * \brief uses the same calling syntax as compress2(), but it
588  * creates a stream compatible with HTTP "Content-encoding: gzip"
589  */
590 #ifdef HAVE_ZLIB
591 #define DEF_MEM_LEVEL 8 /*< memlevel??? */
592 #define OS_CODE 0x03    /*< unix */
593 int ZEXPORT compress_gzip(Bytef * dest,         /*< compressed buffer*/
594                           size_t * destLen,     /*< length of the compresed data */
595                           const Bytef * source, /*< source to encode */
596                           uLong sourceLen,      /*< length of source to encode */
597                           int level)            /*< compression level */
598 {
599         const int gz_magic[2] = { 0x1f, 0x8b }; /* gzip magic header */
600
601         /* write gzip header */
602         snprintf((char *) dest, *destLen, 
603                  "%c%c%c%c%c%c%c%c%c%c",
604                  gz_magic[0], gz_magic[1], Z_DEFLATED,
605                  0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /* xflags */ ,
606                  OS_CODE);
607
608         /* normal deflate */
609         z_stream stream;
610         int err;
611         stream.next_in = (Bytef *) source;
612         stream.avail_in = (uInt) sourceLen;
613         stream.next_out = dest + 10L;   // after header
614         stream.avail_out = (uInt) * destLen;
615         if ((uLong) stream.avail_out != *destLen)
616                 return Z_BUF_ERROR;
617
618         stream.zalloc = (alloc_func) 0;
619         stream.zfree = (free_func) 0;
620         stream.opaque = (voidpf) 0;
621
622         err = deflateInit2(&stream, level, Z_DEFLATED, -MAX_WBITS,
623                            DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
624         if (err != Z_OK)
625                 return err;
626
627         err = deflate(&stream, Z_FINISH);
628         if (err != Z_STREAM_END) {
629                 deflateEnd(&stream);
630                 return err == Z_OK ? Z_BUF_ERROR : err;
631         }
632         *destLen = stream.total_out + 10L;
633
634         /* write CRC and Length */
635         uLong crc = crc32(0L, source, sourceLen);
636         int n;
637         for (n = 0; n < 4; ++n, ++*destLen) {
638                 dest[*destLen] = (int) (crc & 0xff);
639                 crc >>= 8;
640         }
641         uLong len = stream.total_in;
642         for (n = 0; n < 4; ++n, ++*destLen) {
643                 dest[*destLen] = (int) (len & 0xff);
644                 len >>= 8;
645         }
646         err = deflateEnd(&stream);
647         return err;
648 }
649 #endif
650
651
652 /**
653  * Attention! If you feed this a Const String, you must maintain the uncompressed buffer yourself!
654  */
655 int CompressBuffer(StrBuf *Buf)
656 {
657 #ifdef HAVE_ZLIB
658         char *compressed_data = NULL;
659         size_t compressed_len, bufsize;
660         
661         bufsize = compressed_len = ((Buf->BufUsed * 101) / 100) + 100;
662         compressed_data = malloc(compressed_len);
663         
664         if (compress_gzip((Bytef *) compressed_data,
665                           &compressed_len,
666                           (Bytef *) Buf->buf,
667                           (uLongf) Buf->BufUsed, Z_BEST_SPEED) == Z_OK) {
668                 if (!ConstBuf)
669                         free(Buf->buf);
670                 Buf->buf = compressed_data;
671                 Buf->BufUsed = compressed_len;
672                 Buf->BufSize = bufsize;
673                 return 1;
674         } else {
675                 free(compressed_data);
676         }
677 #endif  /* HAVE_ZLIB */
678         return 0;
679 }
680
681 int StrBufDecodeBase64(StrBuf *Buf)
682 {
683         char *xferbuf;
684         size_t siz;
685         if (Buf == NULL) return -1;
686
687         xferbuf = (char*) malloc(Buf->BufSize);
688         siz = CtdlDecodeBase64(xferbuf,
689                                Buf->buf,
690                                Buf->BufUsed);
691         free(Buf->buf);
692         Buf->buf = xferbuf;
693         Buf->BufUsed = siz;
694         return siz;
695 }