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