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