* add gzip + configure detection -> re-bootstrap!
[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         
258         while (Offset + nWritten >= Buf->BufSize) {
259                 nWritten = vsnprintf(Buf->buf + Offset, 
260                                      Buf->BufSize - Offset, 
261                                      format, ap);
262                 Buf->BufUsed = Offset + nWritten ;
263                 if (nWritten >= Buf->BufSize)
264                         IncreaseBuf(Buf, 0, 0);
265         }
266 }
267
268 void StrBufPrintf(StrBuf *Buf, const char *format, ...)
269 {
270         size_t nWritten = Buf->BufSize + 1;
271         va_list arg_ptr;
272         
273         while (nWritten >= Buf->BufSize) {
274                 va_start(arg_ptr, format);
275                 nWritten = vsnprintf(Buf->buf, Buf->BufSize, format, arg_ptr);
276                 va_end(arg_ptr);
277                 Buf->BufUsed = nWritten ;
278                 if (nWritten >= Buf->BufSize)
279                         IncreaseBuf(Buf, 0, 0);
280         }
281 }
282
283
284 /**
285  * \brief a string tokenizer
286  * \param dest Destination StringBuffer
287  * \param Source StringBuffer to read into
288  * \param separator tokenizer param
289  * \returns -1 if not found, else length of token.
290  */
291 int StrBufExtract_token(StrBuf *dest, const StrBuf *Source, int parmnum, char separator)
292 {
293         const char *s, *e;              //* source * /
294         int len = 0;                    //* running total length of extracted string * /
295         int current_token = 0;          //* token currently being processed * /
296
297         if ((Source == NULL) || (Source->BufUsed ==0)) {
298                 return(-1);
299         }
300         s = Source->buf;
301         e = s + Source->BufUsed;
302         if (dest == NULL) {
303                 return(-1);
304         }
305
306         //cit_backtrace();
307         //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
308         dest->buf[0] = '\0';
309         dest->BufUsed = 0;
310
311         while ((s<e) && !IsEmptyStr(s)) {
312                 if (*s == separator) {
313                         ++current_token;
314                 }
315                 if (len >= dest->BufSize)
316                         if (!IncreaseBuf(dest, 1, -1))
317                                 break;
318                 if ( (current_token == parmnum) && 
319                      (*s != separator)) {
320                         dest->buf[len] = *s;
321                         ++len;
322                 }
323                 else if (current_token > parmnum) {
324                         break;
325                 }
326                 ++s;
327         }
328         
329         dest->buf[len] = '\0';
330         dest->BufUsed = len;
331                 
332         if (current_token < parmnum) {
333                 //lprintf (CTDL_DEBUG,"test <!: %s\n", dest);
334                 return(-1);
335         }
336         //lprintf (CTDL_DEBUG,"test <: %d; %s\n", len, dest);
337         return(len);
338 }
339
340
341 /*
342  * extract_int()  -  extract an int parm w/o supplying a buffer
343  */
344 int StrBufExtract_int(const StrBuf* Source, int parmnum, char separator)
345 {
346         StrBuf tmp;
347         char buf[64];
348         
349         tmp.buf = buf;
350         buf[0] = '\0';
351         tmp.BufSize = 64;
352         tmp.BufUsed = 0;
353         if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
354                 return(atoi(buf));
355         else
356                 return 0;
357 }
358
359 /*
360  * extract_long()  -  extract an long parm w/o supplying a buffer
361  */
362 long StrBufExtract_long(const StrBuf* Source, int parmnum, char separator)
363 {
364         StrBuf tmp;
365         char buf[64];
366         
367         tmp.buf = buf;
368         buf[0] = '\0';
369         tmp.BufSize = 64;
370         tmp.BufUsed = 0;
371         if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
372                 return(atoi(buf));
373         else
374                 return 0;
375 }
376
377
378 /*
379  * extract_unsigned_long() - extract an unsigned long parm
380  */
381 unsigned long StrBufExtract_unsigned_long(const StrBuf* Source, int parmnum, char separator)
382 {
383         StrBuf tmp;
384         char buf[64];
385         
386         tmp.buf = buf;
387         buf[0] = '\0';
388         tmp.BufSize = 64;
389         tmp.BufUsed = 0;
390         if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
391                 return(atoi(buf));
392         else 
393                 return 0;
394 }
395
396
397
398 /**
399  * \brief Input binary data from socket
400  * \param buf the buffer to get the input to
401  * \param bytes the maximal number of bytes to read
402  */
403 int StrBufTCP_read_line(StrBuf *buf, int *fd, int append, const char **Error)
404 {
405         int len, rlen, slen;
406
407         if (!append)
408                 FlushStrBuf(buf);
409
410         slen = len = buf->BufUsed;
411         while (1) {
412                 rlen = read(*fd, &buf->buf[len], 1);
413                 if (rlen < 1) {
414                         *Error = strerror(errno);
415                         
416                         close(*fd);
417                         *fd = -1;
418                         
419                         return -1;
420                 }
421                 if (buf->buf[len] == '\n')
422                         break;
423                 if (buf->buf[len] != '\r')
424                         len ++;
425                 if (!(len < buf->BufSize)) {
426                         buf->BufUsed = len;
427                         buf->buf[len+1] = '\0';
428                         IncreaseBuf(buf, 1, -1);
429                 }
430         }
431         buf->BufUsed = len;
432         buf->buf[len] = '\0';
433         return len - slen;
434 }
435
436 /**
437  * \brief Input binary data from socket
438  * \param buf the buffer to get the input to
439  * \param bytes the maximal number of bytes to read
440  */
441 int StrBufReadBLOB(StrBuf *Buf, int *fd, int append, long nBytes, const char **Error)
442 {
443         fd_set wset;
444         int fdflags;
445         int len, rlen, slen;
446         int nRead = 0;
447         char *ptr;
448
449         if ((Buf == NULL) || (*fd == -1))
450                 return -1;
451         if (!append)
452                 FlushStrBuf(Buf);
453         if (Buf->BufUsed + nBytes > Buf->BufSize)
454                 IncreaseBuf(Buf, 1, Buf->BufUsed + nBytes);
455
456         ptr = Buf->buf + Buf->BufUsed;
457
458         slen = len = Buf->BufUsed;
459
460         fdflags = fcntl(*fd, F_GETFL);
461
462         while (nRead < nBytes) {
463                if ((fdflags & O_NONBLOCK) == O_NONBLOCK) {
464                         FD_ZERO(&wset);
465                         FD_SET(*fd, &wset);
466                         if (select(*fd + 1, NULL, &wset, NULL, NULL) == -1) {
467                                 *Error = strerror(errno);
468                                 return -1;
469                         }
470                 }
471
472                 if ((rlen = read(*fd, 
473                                  ptr,
474                                  nBytes - nRead)) == -1) {
475                         close(*fd);
476                         *fd = -1;
477                         *Error = strerror(errno);
478                         return rlen;
479                 }
480                 nRead += rlen;
481                 Buf->BufUsed += rlen;
482         }
483         Buf->buf[Buf->BufUsed] = '\0';
484         return nRead;
485 }
486
487 void StrBufCutLeft(StrBuf *Buf, int nChars)
488 {
489         if (nChars >= Buf->BufUsed) {
490                 FlushStrBuf(Buf);
491                 return;
492         }
493         memmove(Buf->buf, Buf->buf + nChars, Buf->BufUsed - nChars);
494         Buf->BufUsed -= nChars;
495         Buf->buf[Buf->BufUsed] = '\0';
496 }
497
498 void StrBufCutRight(StrBuf *Buf, int nChars)
499 {
500         if (nChars >= Buf->BufUsed) {
501                 FlushStrBuf(Buf);
502                 return;
503         }
504         Buf->BufUsed -= nChars;
505         Buf->buf[Buf->BufUsed] = '\0';
506 }
507
508
509 /*
510  * string conversion function
511  */
512 void StrBufEUid_unescapize(StrBuf *target, StrBuf *source) 
513 {
514         int a, b, len;
515         char hex[3];
516
517         if (target != NULL)
518                 FlushStrBuf(target);
519
520         if (source == NULL ||target == NULL)
521         {
522                 return;
523         }
524
525         len = source->BufUsed;
526         for (a = 0; a < len; ++a) {
527                 if (target->BufUsed >= target->BufSize)
528                         IncreaseBuf(target, 1, -1);
529
530                 if (source->buf[a] == '=') {
531                         hex[0] = source->buf[a + 1];
532                         hex[1] = source->buf[a + 2];
533                         hex[2] = 0;
534                         b = 0;
535                         sscanf(hex, "%02x", &b);
536                         target->buf[target->BufUsed] = b;
537                         target->buf[++target->BufUsed] = 0;
538                         a += 2;
539                 }
540                 else {
541                         target->buf[target->BufUsed] = source->buf[a];
542                         target->buf[++target->BufUsed] = 0;
543                 }
544         }
545 }
546
547
548 /*
549  * string conversion function
550  */
551 void StrBufEUid_escapize(StrBuf *target, StrBuf *source) 
552 {
553         int i, len;
554
555         if (target != NULL)
556                 FlushStrBuf(target);
557
558         if (source == NULL ||target == NULL)
559         {
560                 return;
561         }
562
563         len = source->BufUsed;
564         for (i=0; i<len; ++i) {
565                 if (target->BufUsed + 4 >= target->BufSize)
566                         IncreaseBuf(target, 1, -1);
567                 if ( (isalnum(source->buf[i])) || 
568                      (source->buf[i]=='-') || 
569                      (source->buf[i]=='_') ) {
570                         target->buf[target->BufUsed++] = source->buf[i];
571                 }
572                 else {
573                         sprintf(&target->buf[target->BufUsed], 
574                                 "=%02X", 
575                                 (0xFF &source->buf[i]));
576                         target->BufUsed += 3;
577                 }
578         }
579         target->buf[target->BufUsed + 1] = '\0';
580 }
581
582 /*
583  * \brief uses the same calling syntax as compress2(), but it
584  * creates a stream compatible with HTTP "Content-encoding: gzip"
585  */
586 #ifdef HAVE_ZLIB
587 #define DEF_MEM_LEVEL 8 /*< memlevel??? */
588 #define OS_CODE 0x03    /*< unix */
589 int ZEXPORT compress_gzip(Bytef * dest,         /*< compressed buffer*/
590                           size_t * destLen,     /*< length of the compresed data */
591                           const Bytef * source, /*< source to encode */
592                           uLong sourceLen,      /*< length of source to encode */
593                           int level)            /*< compression level */
594 {
595         const int gz_magic[2] = { 0x1f, 0x8b }; /* gzip magic header */
596
597         /* write gzip header */
598         snprintf((char *) dest, *destLen, 
599                  "%c%c%c%c%c%c%c%c%c%c",
600                  gz_magic[0], gz_magic[1], Z_DEFLATED,
601                  0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /* xflags */ ,
602                  OS_CODE);
603
604         /* normal deflate */
605         z_stream stream;
606         int err;
607         stream.next_in = (Bytef *) source;
608         stream.avail_in = (uInt) sourceLen;
609         stream.next_out = dest + 10L;   // after header
610         stream.avail_out = (uInt) * destLen;
611         if ((uLong) stream.avail_out != *destLen)
612                 return Z_BUF_ERROR;
613
614         stream.zalloc = (alloc_func) 0;
615         stream.zfree = (free_func) 0;
616         stream.opaque = (voidpf) 0;
617
618         err = deflateInit2(&stream, level, Z_DEFLATED, -MAX_WBITS,
619                            DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
620         if (err != Z_OK)
621                 return err;
622
623         err = deflate(&stream, Z_FINISH);
624         if (err != Z_STREAM_END) {
625                 deflateEnd(&stream);
626                 return err == Z_OK ? Z_BUF_ERROR : err;
627         }
628         *destLen = stream.total_out + 10L;
629
630         /* write CRC and Length */
631         uLong crc = crc32(0L, source, sourceLen);
632         int n;
633         for (n = 0; n < 4; ++n, ++*destLen) {
634                 dest[*destLen] = (int) (crc & 0xff);
635                 crc >>= 8;
636         }
637         uLong len = stream.total_in;
638         for (n = 0; n < 4; ++n, ++*destLen) {
639                 dest[*destLen] = (int) (len & 0xff);
640                 len >>= 8;
641         }
642         err = deflateEnd(&stream);
643         return err;
644 }
645 #endif
646
647
648 /**
649  * Attention! If you feed this a Const String, you must maintain the uncompressed buffer yourself!
650  */
651 int CompressBuffer(StrBuf *Buf)
652 {
653 #ifdef HAVE_ZLIB
654         char *compressed_data = NULL;
655         size_t compressed_len, bufsize;
656         
657         bufsize = compressed_len = ((Buf->BufUsed * 101) / 100) + 100;
658         compressed_data = malloc(compressed_len);
659         
660         if (compress_gzip((Bytef *) compressed_data,
661                           &compressed_len,
662                           (Bytef *) Buf->buf,
663                           (uLongf) Buf->BufUsed, Z_BEST_SPEED) == Z_OK) {
664                 if (!ConstBuf)
665                         free(Buf->buf);
666                 Buf->buf = compressed_data;
667                 Buf->BufUsed = compressed_len;
668                 Buf->BufSize = bufsize;
669                 return 1;
670         } else {
671                 free(compressed_data);
672         }
673 #endif  /* HAVE_ZLIB */
674         return 0;
675 }