* msgbase.c: when a summary mode message list is requested, and the room
[citadel.git] / citadel / tools.c
1 /*
2  * $Id$
3  *
4  * Utility functions that are used by both the client and server.
5  *
6  */
7
8 #include "sysdep.h"
9 #include <stdlib.h>
10 #include <unistd.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <ctype.h>
14 #include <string.h>
15 #include <stdarg.h>
16
17 #if TIME_WITH_SYS_TIME
18 # include <sys/time.h>
19 # include <time.h>
20 #else
21 # if HAVE_SYS_TIME_H
22 #  include <sys/time.h>
23 # else
24 #  include <time.h>
25 # endif
26 #endif
27
28 #include "tools.h"
29 #include "citadel.h"
30
31 #define TRUE  1
32 #define FALSE 0
33
34 typedef unsigned char byte;           /* Byte type */
35 static byte dtable[256];              /* base64 encode / decode table */
36
37 /* Month strings for date conversions */
38 char *ascmonths[12] = {
39         "Jan", "Feb", "Mar", "Apr", "May", "Jun",
40         "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
41 };
42
43 char *safestrncpy(char *dest, const char *src, size_t n)
44 {
45         if (dest == NULL || src == NULL) {
46                 fprintf(stderr, "safestrncpy: NULL argument\n");
47                 abort();
48         }
49         strncpy(dest, src, n);
50         dest[n - 1] = 0;
51         return dest;
52 }
53
54
55
56 #ifndef HAVE_STRNCASECMP
57 int strncasecmp(char *lstr, char *rstr, int len)
58 {
59         int pos = 0;
60         char lc,rc;
61         while (pos<len) {
62                 lc=tolower(lstr[pos]);
63                 rc=tolower(rstr[pos]);
64                 if ((lc==0)&&(rc==0)) return(0);
65                 if (lc<rc) return(-1);
66                 if (lc>rc) return(1);
67                 pos=pos+1;
68         }
69         return(0);
70 }
71 #endif
72
73
74
75 /*
76  * num_tokens()  -  discover number of parameters/tokens in a string
77  */
78 int num_tokens(const char *source, char tok) {
79         int a;
80         int count = 1;
81
82         if (source == NULL) return(0);
83         for (a=0; a<strlen(source); ++a) {
84                 if (source[a]==tok) ++count;
85         }
86         return(count);
87 }
88
89
90 /*
91  * extract_token() - a string tokenizer
92  */
93 void extract_token(char *dest, const char *source, int parmnum, char separator, int maxlen)
94 {
95         char *d;                /* dest */
96         const char *s;          /* source */
97         int count = 0;
98         int len = 0;
99
100         strcpy(dest, "");
101
102         /* Locate desired parameter */
103         s = source;
104         while (count < parmnum) {
105                 /* End of string, bail! */
106                 if (!*s) {
107                         s = NULL;
108                         break;
109                 }
110                 if (*s == separator) {
111                         count++;
112                 }
113                 s++;
114         }
115         if (!s) return;         /* Parameter not found */
116
117         for (d = dest; *s && *s != separator && ++len<maxlen; s++, d++) {
118                 *d = *s;
119         }
120         *d = 0;
121 }
122
123
124 /*
125  * remove_token() - a tokenizer that kills, maims, and destroys
126  */
127 void remove_token(char *source, int parmnum, char separator)
128 {
129         char *d, *s;            /* dest, source */
130         int count = 0;
131
132         /* Find desired parameter */
133         d = source;
134         while (count < parmnum) {
135                 /* End of string, bail! */
136                 if (!*d) {
137                         d = NULL;
138                         break;
139                 }
140                 if (*d == separator) {
141                         count++;
142                 }
143                 d++;
144         }
145         if (!d) return;         /* Parameter not found */
146
147         /* Find next parameter */
148         s = d;
149         while (*s && *s != separator) {
150                 s++;
151         }
152
153         /* Hack and slash */
154         if (*s)
155                 strcpy(d, ++s);
156         else if (d == source)
157                 *d = 0;
158         else
159                 *--d = 0;
160         /*
161         while (*s) {
162                 *d++ = *s++;
163         }
164         *d = 0;
165         */
166 }
167
168
169 /*
170  * extract_int()  -  extract an int parm w/o supplying a buffer
171  */
172 int extract_int(const char *source, int parmnum)
173 {
174         char buf[32];
175         
176         extract_token(buf, source, parmnum, '|', sizeof buf);
177         return(atoi(buf));
178 }
179
180 /*
181  * extract_long()  -  extract an long parm w/o supplying a buffer
182  */
183 long extract_long(const char *source, int parmnum)
184 {
185         char buf[32];
186         
187         extract_token(buf, source, parmnum, '|', sizeof buf);
188         return(atol(buf));
189 }
190
191
192 /*
193  * extract_unsigned_long() - extract an unsigned long parm
194  */
195 unsigned long extract_unsigned_long(const char *source, int parmnum)
196 {
197         char buf[32];
198
199         extract_token(buf, source, parmnum, '|', sizeof buf);
200         return strtoul(buf, NULL, 10);
201 }
202
203
204 /*
205  * CtdlDecodeBase64() and CtdlEncodeBase64() are adaptations of code by
206  * John Walker, found in full in the file "base64.c" included with this
207  * distribution.  We are moving in the direction of eventually discarding
208  * the separate executables, and using the ones in our code exclusively.
209  */
210
211 void CtdlEncodeBase64(char *dest, const char *source, size_t sourcelen)
212 {
213     int i, hiteof = FALSE;
214     int spos = 0;
215     int dpos = 0;
216
217     /*  Fill dtable with character encodings.  */
218
219     for (i = 0; i < 26; i++) {
220         dtable[i] = 'A' + i;
221         dtable[26 + i] = 'a' + i;
222     }
223     for (i = 0; i < 10; i++) {
224         dtable[52 + i] = '0' + i;
225     }
226     dtable[62] = '+';
227     dtable[63] = '/';
228
229     while (!hiteof) {
230         byte igroup[3], ogroup[4];
231         int c, n;
232
233         igroup[0] = igroup[1] = igroup[2] = 0;
234         for (n = 0; n < 3; n++) {
235             if (spos >= sourcelen) {
236                 hiteof = TRUE;
237                 break;
238             }
239             c = source[spos++];
240             igroup[n] = (byte) c;
241         }
242         if (n > 0) {
243             ogroup[0] = dtable[igroup[0] >> 2];
244             ogroup[1] = dtable[((igroup[0] & 3) << 4) | (igroup[1] >> 4)];
245             ogroup[2] = dtable[((igroup[1] & 0xF) << 2) | (igroup[2] >> 6)];
246             ogroup[3] = dtable[igroup[2] & 0x3F];
247
248             /* Replace characters in output stream with "=" pad
249                characters if fewer than three characters were
250                read from the end of the input stream. */
251
252             if (n < 3) {
253                 ogroup[3] = '=';
254                 if (n < 2) {
255                     ogroup[2] = '=';
256                 }
257             }
258             for (i = 0; i < 4; i++) {
259                 dest[dpos++] = ogroup[i];
260                 dest[dpos] = 0;
261             }
262         }
263     }
264 }
265
266
267 /* 
268  * Convert base64-encoded to binary.  Returns the length of the decoded data.
269  * It will stop after reading 'length' bytes.
270  */
271 int CtdlDecodeBase64(char *dest, const char *source, size_t length)
272 {
273     int i, c;
274     int dpos = 0;
275     int spos = 0;
276
277     for (i = 0; i < 255; i++) {
278         dtable[i] = 0x80;
279     }
280     for (i = 'A'; i <= 'Z'; i++) {
281         dtable[i] = 0 + (i - 'A');
282     }
283     for (i = 'a'; i <= 'z'; i++) {
284         dtable[i] = 26 + (i - 'a');
285     }
286     for (i = '0'; i <= '9'; i++) {
287         dtable[i] = 52 + (i - '0');
288     }
289     dtable['+'] = 62;
290     dtable['/'] = 63;
291     dtable['='] = 0;
292
293     /*CONSTANTCONDITION*/
294     while (TRUE) {
295         byte a[4], b[4], o[3];
296
297         for (i = 0; i < 4; i++) {
298             if (spos >= length) {
299                 return(dpos);
300             }
301             c = source[spos++];
302
303             if (c == 0) {
304                 if (i > 0) {
305                     return(dpos);
306                 }
307                 return(dpos);
308             }
309             if (dtable[c] & 0x80) {
310                 /* Ignoring errors: discard invalid character. */
311                 i--;
312                 continue;
313             }
314             a[i] = (byte) c;
315             b[i] = (byte) dtable[c];
316         }
317         o[0] = (b[0] << 2) | (b[1] >> 4);
318         o[1] = (b[1] << 4) | (b[2] >> 2);
319         o[2] = (b[2] << 6) | b[3];
320         i = a[2] == '=' ? 1 : (a[3] == '=' ? 2 : 3);
321         if (i>=1) dest[dpos++] = o[0];
322         if (i>=2) dest[dpos++] = o[1];
323         if (i>=3) dest[dpos++] = o[2];
324         dest[dpos] = 0;
325         if (i < 3) {
326             return(dpos);
327         }
328     }
329 }
330
331
332
333 /*
334  * Strip leading and trailing spaces from a string
335  */
336 void striplt(char *buf)
337 {
338         if (strlen(buf) == 0) return;
339         while ((strlen(buf) > 0) && (isspace(buf[0])))
340                 strcpy(buf, &buf[1]);
341         if (strlen(buf) == 0) return;
342         while ((strlen(buf) > 0) && (isspace(buf[strlen(buf) - 1])))
343                 buf[strlen(buf) - 1] = 0;
344 }
345
346
347
348
349
350 /* 
351  * Return the number of occurances of character ch in string st
352  */ 
353 int haschar(const char *st, int ch)
354 {
355         int a, b;
356         b = 0;
357         for (a = 0; a < strlen(st); ++a)
358                 if (st[a] == ch)
359                         ++b;
360         return (b);
361 }
362
363
364
365
366
367 /*
368  * Format a date/time stamp for output 
369  * seconds is whether to print the seconds
370  */
371 void fmt_date(char *buf, size_t n, time_t thetime, int seconds) {
372         struct tm tm;
373         int hour;
374
375         strcpy(buf, "");
376         localtime_r(&thetime, &tm);
377
378         hour = tm.tm_hour;
379         if (hour == 0)  hour = 12;
380         else if (hour > 12) hour = hour - 12;
381
382         if (seconds) {
383                 snprintf(buf, n, "%s %d %4d %d:%02d:%02d%s",
384                         ascmonths[tm.tm_mon],
385                         tm.tm_mday,
386                         tm.tm_year + 1900,
387                         hour,
388                         tm.tm_min,
389                         tm.tm_sec,
390                         ( (tm.tm_hour >= 12) ? "pm" : "am" )
391                 );
392         } else {
393                 snprintf(buf, n, "%s %d %4d %d:%02d%s",
394                         ascmonths[tm.tm_mon],
395                         tm.tm_mday,
396                         tm.tm_year + 1900,
397                         hour,
398                         tm.tm_min,
399                         ( (tm.tm_hour >= 12) ? "pm" : "am" )
400                 );
401         }
402 }
403
404
405
406 /*
407  * Determine whether the specified message number is contained within the
408  * specified sequence set.
409  */
410 int is_msg_in_sequence_set(char *mset, long msgnum) {
411         int num_sets;
412         int s;
413         char setstr[128], lostr[128], histr[128];
414         long lo, hi;
415
416         num_sets = num_tokens(mset, ',');
417         for (s=0; s<num_sets; ++s) {
418                 extract_token(setstr, mset, s, ',', sizeof setstr);
419
420                 extract_token(lostr, setstr, 0, ':', sizeof lostr);
421                 if (num_tokens(setstr, ':') >= 2) {
422                         extract_token(histr, setstr, 1, ':', sizeof histr);
423                         if (!strcmp(histr, "*")) {
424                                 snprintf(histr, sizeof histr, "%ld", LONG_MAX);
425                         }
426                 } 
427                 else {
428                         strcpy(histr, lostr);
429                 }
430                 lo = atol(lostr);
431                 hi = atol(histr);
432
433                 if ((msgnum >= lo) && (msgnum <= hi)) return(1);
434         }
435
436         return(0);
437 }
438
439
440 /*
441  * Utility function to "readline" from memory
442  * (returns new pointer)
443  */
444 char *memreadline(char *start, char *buf, int maxlen)
445 {
446         char ch;
447         char *ptr;
448         int len = 0;    /* tally our own length to avoid strlen() delays */
449
450         ptr = start;
451         memset(buf, 0, maxlen);
452
453         while (1) {
454                 ch = *ptr++;
455                 if ( (len < (maxlen - 1)) && (ch != 13) && (ch != 10) ) {
456                         buf[strlen(buf) + 1] = 0;
457                         buf[strlen(buf)] = ch;
458                         ++len;
459                 }
460                 if ((ch == 10) || (ch == 0)) {
461                         return ptr;
462                 }
463         }
464 }
465
466
467 /*
468  * Strip a boundarized substring out of a string (for example, remove
469  * parentheses and anything inside them).
470  */
471 void stripout(char *str, char leftboundary, char rightboundary) {
472         int a;
473         int lb = (-1);
474         int rb = (-1);
475
476         for (a = 0; a < strlen(str); ++a) {
477                 if (str[a] == leftboundary) lb = a;
478                 if (str[a] == rightboundary) rb = a;
479         }
480
481         if ( (lb > 0) && (rb > lb) ) {
482                 strcpy(&str[lb - 1], &str[rb + 1]);
483         }
484
485         else if ( (lb == 0) && (rb > lb) ) {
486                 strcpy(str, &str[rb + 1]);
487         }
488
489 }
490
491
492 /*
493  * Reduce a string down to a boundarized substring (for example, remove
494  * parentheses and anything outside them).
495  */
496 void stripallbut(char *str, char leftboundary, char rightboundary) {
497         int a;
498
499         for (a = 0; a < strlen(str); ++ a) {
500                 if (str[a] == leftboundary) strcpy(str, &str[a+1]);
501         }
502
503         for (a = 0; a < strlen(str); ++ a) {
504                 if (str[a] == rightboundary) str[a] = 0;
505         }
506
507 }
508
509 char *myfgets(char *s, int size, FILE *stream) {
510         char *ret = fgets(s, size, stream);
511         char *nl;
512
513         if (ret != NULL) {
514                 nl = strchr(s, '\n');
515
516                 if (nl != NULL)
517                         *nl = 0;
518         }
519
520         return ret;
521 }
522
523 /*
524  * Escape a string for feeding out as a URL.
525  * Output buffer must be big enough to handle escape expansion!
526  */
527 void urlesc(char *outbuf, char *strbuf)
528 {
529         int a, b, c;
530         char *ec = " #&;`'|*?-~<>^()[]{}$\\";
531
532         strcpy(outbuf, "");
533
534         for (a = 0; a < (int)strlen(strbuf); ++a) {
535                 c = 0;
536                 for (b = 0; b < strlen(ec); ++b) {
537                         if (strbuf[a] == ec[b])
538                                 c = 1;
539                 }
540                 b = strlen(outbuf);
541                 if (c == 1)
542                         sprintf(&outbuf[b], "%%%02x", strbuf[a]);
543                 else
544                         sprintf(&outbuf[b], "%c", strbuf[a]);
545         }
546 }
547
548
549
550 /*
551  * In our world, we want strcpy() to be able to work with overlapping strings.
552  */
553 #ifdef strcpy
554 #undef strcpy
555 #endif
556 char *strcpy(char *dest, const char *src) {
557         memmove(dest, src, (strlen(src) + 1) );
558         return(dest);
559 }
560
561
562 /*
563  * Generate a new, globally unique UID parameter for a calendar etc. object
564  */
565 void generate_uuid(char *buf) {
566         static int seq = 0;
567
568         sprintf(buf, "%lx-%x-%x",
569                 time(NULL),
570                 getpid(),
571                 (seq++)
572         );
573 }
574
575 /*
576  * bmstrcasestr() -- case-insensitive substring search
577  *
578  * This uses the Boyer-Moore search algorithm and is therefore quite fast.
579  * The code is roughly based on the strstr() replacement from 'tin' written
580  * by Urs Jannsen.
581  */
582 char *bmstrcasestr(char *text, char *pattern) {
583
584         register unsigned char *p, *t;
585         register int i, j, *delta;
586         register size_t p1;
587         int deltaspace[256];
588         size_t textlen;
589         size_t patlen;
590
591         textlen = strlen (text);
592         patlen = strlen (pattern);
593
594         /* algorithm fails if pattern is empty */
595         if ((p1 = patlen) == 0)
596                 return (text);
597
598         /* code below fails (whenever i is unsigned) if pattern too long */
599         if (p1 > textlen)
600                 return (NULL);
601
602         /* set up deltas */
603         delta = deltaspace;
604         for (i = 0; i <= 255; i++)
605                 delta[i] = p1;
606         for (p = (unsigned char *) pattern, i = p1; --i > 0;)
607                 delta[tolower(*p++)] = i;
608
609         /*
610          * From now on, we want patlen - 1.
611          * In the loop below, p points to the end of the pattern,
612          * t points to the end of the text to be tested against the
613          * pattern, and i counts the amount of text remaining, not
614          * including the part to be tested.
615          */
616         p1--;
617         p = (unsigned char *) pattern + p1;
618         t = (unsigned char *) text + p1;
619         i = textlen - patlen;
620         while(1) {
621                 if (tolower(p[0]) == tolower(t[0])) {
622                         if (strncasecmp ((const char *)(p - p1), (const char *)(t - p1), p1) == 0) {
623                                 return ((char *)t - p1);
624                         }
625                 }
626                 j = delta[tolower(t[0])];
627                 if (i < j)
628                         break;
629                 i -= j;
630                 t += j;
631         }
632         return (NULL);
633 }
634
635
636