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