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