28e5c4789c3388e35c46552aab3b6ab67a7fa202
[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
18 #if TIME_WITH_SYS_TIME
19 # include <sys/time.h>
20 # include <time.h>
21 #else
22 # if HAVE_SYS_TIME_H
23 #  include <sys/time.h>
24 # else
25 #  include <time.h>
26 # endif
27 #endif
28
29 #include "tools.h"
30 #include "citadel.h"
31
32 #define TRUE  1
33 #define FALSE 0
34
35 typedef unsigned char byte;           /* Byte type */
36 static byte dtable[256];              /* base64 encode / decode table */
37
38
39 char *safestrncpy(char *dest, const char *src, size_t n)
40 {
41         if (dest == NULL || src == NULL) {
42                 fprintf(stderr, "safestrncpy: NULL argument\n");
43                 abort();
44         }
45         strncpy(dest, src, n);
46         dest[n - 1] = 0;
47         return dest;
48 }
49
50
51
52 #ifndef HAVE_STRNCASECMP
53 int strncasecmp(char *lstr, char *rstr, int len)
54 {
55         int pos = 0;
56         char lc,rc;
57         while (pos<len) {
58                 lc=tolower(lstr[pos]);
59                 rc=tolower(rstr[pos]);
60                 if ((lc==0)&&(rc==0)) return(0);
61                 if (lc<rc) return(-1);
62                 if (lc>rc) return(1);
63                 pos=pos+1;
64         }
65         return(0);
66 }
67 #endif
68
69
70
71 /*
72  * num_tokens()  -  discover number of parameters/tokens in a string
73  */
74 int num_tokens(char *source, char tok) {
75         int a;
76         int count = 1;
77
78         for (a=0; a<strlen(source); ++a) {
79                 if (source[a]==tok) ++count;
80         }
81         return(count);
82 }
83
84 /*
85  * extract_token()  -  a smarter string tokenizer
86  */
87 void extract_token(char *dest, char *source, int parmnum, char separator) 
88 {
89         int i;
90         int len;
91         int curr_parm;
92
93         strcpy(dest,"");
94         len = 0;
95         curr_parm = 0;
96
97         if (strlen(source)==0) {
98                 return;
99                 }
100
101         for (i=0; i<strlen(source); ++i) {
102                 if (source[i]==separator) {
103                         ++curr_parm;
104                 }
105                 else if (curr_parm == parmnum) {
106                         dest[len+1] = 0;
107                         dest[len++] = source[i];
108                 }
109         }
110 }
111
112
113
114 /*
115  * remove_token()  -  a tokenizer that kills, maims, and destroys
116  */
117 void remove_token(char *source, int parmnum, char separator)
118 {
119         int i;
120         int len;
121         int curr_parm;
122         int start, end;
123
124         len = 0;
125         curr_parm = 0;
126         start = (-1);
127         end = (-1);
128
129         if (strlen(source)==0) {
130                 return;
131                 }
132
133         for (i=0; i<strlen(source); ++i) {
134                 if ( (start < 0) && (curr_parm == parmnum) ) {
135                         start = i;
136                 }
137
138                 if ( (end < 0) && (curr_parm == (parmnum+1)) ) {
139                         end = i;
140                 }
141
142                 if (source[i]==separator) {
143                         ++curr_parm;
144                 }
145         }
146
147         if (end < 0) end = strlen(source);
148
149         printf("%d .. %d\n", start, end);
150
151         strcpy(&source[start], &source[end]);
152 }
153
154
155
156
157 /*
158  * extract_int()  -  extract an int parm w/o supplying a buffer
159  */
160 int extract_int(char *source, int parmnum)
161 {
162         char buf[SIZ];
163         
164         extract_token(buf, source, parmnum, '|');
165         return(atoi(buf));
166 }
167
168 /*
169  * extract_long()  -  extract an long parm w/o supplying a buffer
170  */
171 long extract_long(char *source, long int parmnum)
172 {
173         char buf[SIZ];
174         
175         extract_token(buf, source, parmnum, '|');
176         return(atol(buf));
177 }
178
179
180
181 /*
182  * decode_base64() and encode_base64() are adaptations of code by
183  * John Walker, found in full in the file "base64.c" included with this
184  * distribution.  The difference between those functions and these is that
185  * these are intended to encode/decode small string buffers, and those are
186  * intended to encode/decode entire MIME parts.
187  */
188
189 void encode_base64(char *dest, char *source)
190 {
191     int i, hiteof = FALSE;
192     int spos = 0;
193     int dpos = 0;
194
195     /*  Fill dtable with character encodings.  */
196
197     for (i = 0; i < 26; i++) {
198         dtable[i] = 'A' + i;
199         dtable[26 + i] = 'a' + i;
200     }
201     for (i = 0; i < 10; i++) {
202         dtable[52 + i] = '0' + i;
203     }
204     dtable[62] = '+';
205     dtable[63] = '/';
206
207     while (!hiteof) {
208         byte igroup[3], ogroup[4];
209         int c, n;
210
211         igroup[0] = igroup[1] = igroup[2] = 0;
212         for (n = 0; n < 3; n++) {
213             c = source[spos++];
214             if (c == 0) {
215                 hiteof = TRUE;
216                 break;
217             }
218             igroup[n] = (byte) c;
219         }
220         if (n > 0) {
221             ogroup[0] = dtable[igroup[0] >> 2];
222             ogroup[1] = dtable[((igroup[0] & 3) << 4) | (igroup[1] >> 4)];
223             ogroup[2] = dtable[((igroup[1] & 0xF) << 2) | (igroup[2] >> 6)];
224             ogroup[3] = dtable[igroup[2] & 0x3F];
225
226             /* Replace characters in output stream with "=" pad
227                characters if fewer than three characters were
228                read from the end of the input stream. */
229
230             if (n < 3) {
231                 ogroup[3] = '=';
232                 if (n < 2) {
233                     ogroup[2] = '=';
234                 }
235             }
236             for (i = 0; i < 4; i++) {
237                 dest[dpos++] = ogroup[i];
238                 dest[dpos] = 0;
239             }
240         }
241     }
242 }
243
244
245
246 void decode_base64(char *dest, char *source)
247 {
248     int i;
249     int dpos = 0;
250     int spos = 0;
251
252     for (i = 0; i < 255; i++) {
253         dtable[i] = 0x80;
254     }
255     for (i = 'A'; i <= 'Z'; i++) {
256         dtable[i] = 0 + (i - 'A');
257     }
258     for (i = 'a'; i <= 'z'; i++) {
259         dtable[i] = 26 + (i - 'a');
260     }
261     for (i = '0'; i <= '9'; i++) {
262         dtable[i] = 52 + (i - '0');
263     }
264     dtable['+'] = 62;
265     dtable['/'] = 63;
266     dtable['='] = 0;
267
268     /*CONSTANTCONDITION*/
269     while (TRUE) {
270         byte a[4], b[4], o[3];
271
272         for (i = 0; i < 4; i++) {
273             int c = source[spos++];
274
275             if (c == 0) {
276                 if (i > 0) {
277                     return;
278                 }
279                 return;
280             }
281             if (dtable[c] & 0x80) {
282                 /* Ignoring errors: discard invalid character. */
283                 i--;
284                 continue;
285             }
286             a[i] = (byte) c;
287             b[i] = (byte) dtable[c];
288         }
289         o[0] = (b[0] << 2) | (b[1] >> 4);
290         o[1] = (b[1] << 4) | (b[2] >> 2);
291         o[2] = (b[2] << 6) | b[3];
292         i = a[2] == '=' ? 1 : (a[3] == '=' ? 2 : 3);
293         if (i>=1) dest[dpos++] = o[0];
294         if (i>=2) dest[dpos++] = o[1];
295         if (i>=3) dest[dpos++] = o[2];
296         dest[dpos] = 0;
297         if (i < 3) {
298             return;
299         }
300     }
301 }
302
303
304
305 /*
306  * Strip leading and trailing spaces from a string
307  */
308 void striplt(char *buf)
309 {
310         while ((strlen(buf) > 0) && (isspace(buf[0])))
311                 strcpy(buf, &buf[1]);
312         while (isspace(buf[strlen(buf) - 1]))
313                 buf[strlen(buf) - 1] = 0;
314 }
315
316
317
318
319
320 /* 
321  * Return the number of occurances of character ch in string st
322  */ 
323 int haschar(char *st, int ch)
324 {
325         int a, b;
326         b = 0;
327         for (a = 0; a < strlen(st); ++a)
328                 if (st[a] == ch)
329                         ++b;
330         return (b);
331 }
332
333
334
335
336 /*
337  * Compare two strings, insensitive to case, punctuation, and non-alnum chars
338  */
339 int collapsed_strcmp(char *s1, char *s2) {
340         char *c1, *c2;
341         int i, ret, pos;
342
343         c1 = malloc(strlen(s1)+1);
344         c2 = malloc(strlen(s2)+1);
345         c1[0] = 0;
346         c2[0] = 0;
347
348         pos = 0;
349         for (i=0; i<strlen(s1); ++i) {
350                 if (isalnum(s1[i])) {
351                         c1[pos] = tolower(s1[i]);
352                         c1[++pos] = 0;
353                 }
354         }
355
356         pos = 0;
357         for (i=0; i<strlen(s2); ++i) {
358                 if (isalnum(s2[i])) {
359                         c2[pos] = tolower(s2[i]);
360                         c2[++pos] = 0;
361                 }
362         }
363
364         ret = strcmp(c1, c2);
365         free(c1);
366         free(c2);
367         return(ret);
368 }
369
370
371
372 /*
373  * Format a date/time stamp for output 
374  */
375 void fmt_date(char *buf, time_t thetime) {
376         struct tm *tm;
377         int hour;
378
379         char *ascmonths[] = {
380                 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
381                 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
382         };
383
384         strcpy(buf, "");
385         tm = localtime(&thetime);
386
387         hour = tm->tm_hour;
388         if (hour == 0)  hour = 12;
389         else if (hour > 12) hour = hour - 12;
390
391         sprintf(buf, "%s %d %4d %d:%02d%s",
392                 ascmonths[tm->tm_mon],
393                 tm->tm_mday,
394                 tm->tm_year + 1900,
395                 hour,
396                 tm->tm_min,
397                 ( (tm->tm_hour >= 12) ? "pm" : "am" )
398         );
399 }
400
401
402
403 /*
404  * Determine whether the specified message number is contained within the
405  * specified set.
406  */
407 int is_msg_in_mset(char *mset, long msgnum) {
408         int num_sets;
409         int s;
410         char setstr[SIZ], lostr[SIZ], histr[SIZ];       /* was 1024 */
411         long lo, hi;
412
413         /*
414          * Now set it for all specified messages.
415          */
416         num_sets = num_tokens(mset, ',');
417         for (s=0; s<num_sets; ++s) {
418                 extract_token(setstr, mset, s, ',');
419
420                 extract_token(lostr, setstr, 0, ':');
421                 if (num_tokens(setstr, ':') >= 2) {
422                         extract_token(histr, setstr, 1, ':');
423                         if (!strcmp(histr, "*")) {
424                                 sprintf(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 }
486
487
488 /*
489  * Reduce a string down to a boundarized substring (for example, remove
490  * parentheses and anything outside them).
491  */
492 void stripallbut(char *str, char leftboundary, char rightboundary) {
493         int a;
494
495         for (a = 0; a < strlen(str); ++ a) {
496                 if (str[a] == leftboundary) strcpy(str, &str[a+1]);
497         }
498
499         for (a = 0; a < strlen(str); ++ a) {
500                 if (str[a] == rightboundary) str[a] = 0;
501         }
502
503 }