* All OS-level includes are now included from webcit.h instead of from
[citadel.git] / webcit / tools.c
1 /*
2  * $Id$
3  *
4  * Miscellaneous routines 
5  */
6
7 #include "webcit.h"
8 #include "webserver.h"
9
10
11 typedef unsigned char byte;
12
13 #define FALSE 0
14 #define TRUE 1
15
16 static byte dtable[256];        /* base64 encode / decode table */
17
18 char *safestrncpy(char *dest, const char *src, size_t n)
19 {
20         if (dest == NULL || src == NULL) {
21                 abort();
22         }
23         strncpy(dest, src, n);
24         dest[n - 1] = 0;
25         return dest;
26 }
27
28
29
30 /*
31  * num_tokens()  -  discover number of parameters/tokens in a string
32  */
33 int num_tokens(char *source, char tok)
34 {
35         int a;
36         int count = 1;
37
38         if (source == NULL)
39                 return (0);
40         for (a = 0; a < strlen(source); ++a) {
41                 if (source[a] == tok)
42                         ++count;
43         }
44         return (count);
45 }
46
47 /*
48  * extract_token() - a string tokenizer
49  */
50 void extract_token(char *dest, const char *source, int parmnum, char separator, int maxlen)
51 {
52         char *d;                /* dest */
53         const char *s;          /* source */
54         int count = 0;
55         int len = 0;
56
57         dest[0] = 0;
58
59         /* Locate desired parameter */
60         s = source;
61         while (count < parmnum) {
62                 /* End of string, bail! */
63                 if (!*s) {
64                         s = NULL;
65                         break;
66                 }
67                 if (*s == separator) {
68                         count++;
69                 }
70                 s++;
71         }
72         if (!s) return;         /* Parameter not found */
73
74         for (d = dest; *s && *s != separator && ++len<maxlen; s++, d++) {
75                 *d = *s;
76         }
77         *d = 0;
78 }
79
80
81
82 /*
83  * remove_token()  -  a tokenizer that kills, maims, and destroys
84  */
85 void remove_token(char *source, int parmnum, char separator)
86 {
87         int i;
88         int len;
89         int curr_parm;
90         int start, end;
91
92         len = 0;
93         curr_parm = 0;
94         start = (-1);
95         end = (-1);
96
97         if (strlen(source) == 0) {
98                 return;
99         }
100
101         for (i = 0; i < strlen(source); ++i) {
102                 if ((start < 0) && (curr_parm == parmnum)) {
103                         start = i;
104                 }
105
106                 if ((end < 0) && (curr_parm == (parmnum + 1))) {
107                         end = i;
108                 }
109
110                 if (source[i] == separator) {
111                         ++curr_parm;
112                 }
113         }
114
115         if (end < 0)
116                 end = strlen(source);
117
118         strcpy(&source[start], &source[end]);
119 }
120
121
122
123
124 /*
125  * extract_int()  -  extract an int parm w/o supplying a buffer
126  */
127 int extract_int(const char *source, int parmnum)
128 {
129         char buf[32];
130         
131         extract_token(buf, source, parmnum, '|', sizeof buf);
132         return(atoi(buf));
133 }
134
135 /*
136  * extract_long()  -  extract an long parm w/o supplying a buffer
137  */
138 long extract_long(const char *source, int parmnum)
139 {
140         char buf[32];
141         
142         extract_token(buf, source, parmnum, '|', sizeof buf);
143         return(atol(buf));
144 }
145
146
147
148
149
150
151 /*
152  * check for the presence of a character within a string (returns count)
153  */
154 int haschar(st, ch)
155 char st[];
156 char ch;
157 {
158         int a, b;
159         b = 0;
160         for (a = 0; a < strlen(st); ++a)
161                 if (st[a] == ch)
162                         ++b;
163         return (b);
164 }
165
166
167 /*
168  * Utility function to "readline" from memory
169  * (returns new pointer)
170  */
171 char *memreadline(char *start, char *buf, int maxlen)
172 {
173         char ch;
174         char *ptr;
175         int len = 0;            /* tally our own length to avoid strlen() delays */
176
177         ptr = start;
178         memset(buf, 0, maxlen);
179
180         while (1) {
181                 ch = *ptr++;
182                 if ((len < (maxlen - 1)) && (ch != 13) && (ch != 10)) {
183                         buf[strlen(buf) + 1] = 0;
184                         buf[strlen(buf)] = ch;
185                         ++len;
186                 }
187                 if ((ch == 10) || (ch == 0)) {
188                         return ptr;
189                 }
190         }
191 }
192
193
194
195 /*
196  * pattern2()  -  searches for patn within search string, returns pos
197  */
198 int pattern2(char *search, char *patn)
199 {
200         int a;
201         for (a = 0; a < strlen(search); ++a) {
202                 if (!strncasecmp(&search[a], patn, strlen(patn)))
203                         return (a);
204         }
205         return (-1);
206 }
207
208
209 /*
210  * Strip leading and trailing spaces from a string
211  */
212 void striplt(char *buf)
213 {
214         if (strlen(buf) == 0) return;
215         while ((strlen(buf) > 0) && (isspace(buf[0])))
216                 strcpy(buf, &buf[1]);
217         if (strlen(buf) == 0) return;
218         while (isspace(buf[strlen(buf) - 1]))
219                 buf[strlen(buf) - 1] = 0;
220 }
221
222
223 /*
224  * Determine whether the specified message number is contained within the
225  * specified set.
226  */
227 int is_msg_in_mset(char *mset, long msgnum) {
228         int num_sets;
229         int s;
230         char setstr[SIZ], lostr[SIZ], histr[SIZ];       /* was 1024 */
231         long lo, hi;
232
233         /*
234          * Now set it for all specified messages.
235          */
236         num_sets = num_tokens(mset, ',');
237         for (s=0; s<num_sets; ++s) {
238                 extract_token(setstr, mset, s, ',', sizeof setstr);
239
240                 extract_token(lostr, setstr, 0, ':', sizeof lostr);
241                 if (num_tokens(setstr, ':') >= 2) {
242                         extract_token(histr, setstr, 1, ':', sizeof histr);
243                         if (!strcmp(histr, "*")) {
244                                 snprintf(histr, sizeof histr, "%ld", LONG_MAX);
245                         }
246                 } 
247                 else {
248                         strcpy(histr, lostr);
249                 }
250                 lo = atol(lostr);
251                 hi = atol(histr);
252
253                 if ((msgnum >= lo) && (msgnum <= hi)) return(1);
254         }
255
256         return(0);
257 }
258
259
260
261 /*
262  * Strip a boundarized substring out of a string (for example, remove
263  * parentheses and anything inside them).
264  *
265  * This improved version can strip out *multiple* boundarized substrings.
266  */
267 void stripout(char *str, char leftboundary, char rightboundary)
268 {
269         int a;
270         int lb = (-1);
271         int rb = (-1);
272
273         do {
274                 lb = (-1);
275                 rb = (-1);
276
277                 for (a = 0; a < strlen(str); ++a) {
278                         if (str[a] == leftboundary)
279                                 lb = a;
280                         if (str[a] == rightboundary)
281                                 rb = a;
282                 }
283
284                 if ((lb > 0) && (rb > lb)) {
285                         strcpy(&str[lb - 1], &str[rb + 1]);
286                 }
287
288         } while ((lb > 0) && (rb > lb));
289
290 }
291
292
293
294 /*
295  * Replacement for sleep() that uses select() in order to avoid SIGALRM
296  */
297 void sleeeeeeeeeep(int seconds)
298 {
299         struct timeval tv;
300
301         tv.tv_sec = seconds;
302         tv.tv_usec = 0;
303         select(0, NULL, NULL, NULL, &tv);
304 }
305
306
307
308 /*
309  * CtdlDecodeBase64() and CtdlEncodeBase64() are adaptations of code by
310  * John Walker, copied over from the Citadel server.
311  */
312
313 void CtdlEncodeBase64(char *dest, const char *source, size_t sourcelen)
314 {
315         int i, hiteof = FALSE;
316         int spos = 0;
317         int dpos = 0;
318         int thisline = 0;
319
320         /*  Fill dtable with character encodings.  */
321
322         for (i = 0; i < 26; i++) {
323                 dtable[i] = 'A' + i;
324                 dtable[26 + i] = 'a' + i;
325         }
326         for (i = 0; i < 10; i++) {
327                 dtable[52 + i] = '0' + i;
328         }
329         dtable[62] = '+';
330         dtable[63] = '/';
331
332         while (!hiteof) {
333                 byte igroup[3], ogroup[4];
334                 int c, n;
335
336                 igroup[0] = igroup[1] = igroup[2] = 0;
337                 for (n = 0; n < 3; n++) {
338                         if (spos >= sourcelen) {
339                                 hiteof = TRUE;
340                                 break;
341                         }
342                         c = source[spos++];
343                         igroup[n] = (byte) c;
344                 }
345                 if (n > 0) {
346                         ogroup[0] = dtable[igroup[0] >> 2];
347                         ogroup[1] =
348                             dtable[((igroup[0] & 3) << 4) |
349                                    (igroup[1] >> 4)];
350                         ogroup[2] =
351                             dtable[((igroup[1] & 0xF) << 2) |
352                                    (igroup[2] >> 6)];
353                         ogroup[3] = dtable[igroup[2] & 0x3F];
354
355                         /* Replace characters in output stream with "=" pad
356                            characters if fewer than three characters were
357                            read from the end of the input stream. */
358
359                         if (n < 3) {
360                                 ogroup[3] = '=';
361                                 if (n < 2) {
362                                         ogroup[2] = '=';
363                                 }
364                         }
365                         for (i = 0; i < 4; i++) {
366                                 dest[dpos++] = ogroup[i];
367                                 dest[dpos] = 0;
368                         }
369                         thisline += 4;
370                         if (thisline > 70) {
371                                 dest[dpos++] = '\r';
372                                 dest[dpos++] = '\n';
373                                 dest[dpos] = 0;
374                                 thisline = 0;
375                         }
376                 }
377         }
378         if (thisline > 70) {
379                 dest[dpos++] = '\r';
380                 dest[dpos++] = '\n';
381                 dest[dpos] = 0;
382                 thisline = 0;
383         }
384 }
385
386
387 /* 
388  * Convert base64-encoded to binary.  Returns the length of the decoded data.
389  * It will stop after reading 'length' bytes.
390  */
391 int CtdlDecodeBase64(char *dest, const char *source, size_t length)
392 {
393         int i, c;
394         int dpos = 0;
395         int spos = 0;
396
397         for (i = 0; i < 255; i++) {
398                 dtable[i] = 0x80;
399         }
400         for (i = 'A'; i <= 'Z'; i++) {
401                 dtable[i] = 0 + (i - 'A');
402         }
403         for (i = 'a'; i <= 'z'; i++) {
404                 dtable[i] = 26 + (i - 'a');
405         }
406         for (i = '0'; i <= '9'; i++) {
407                 dtable[i] = 52 + (i - '0');
408         }
409         dtable['+'] = 62;
410         dtable['/'] = 63;
411         dtable['='] = 0;
412
413          /*CONSTANTCONDITION*/ while (TRUE) {
414                 byte a[4], b[4], o[3];
415
416                 for (i = 0; i < 4; i++) {
417                         if (spos >= length) {
418                                 return (dpos);
419                         }
420                         c = source[spos++];
421
422                         if (c == 0) {
423                                 if (i > 0) {
424                                         return (dpos);
425                                 }
426                                 return (dpos);
427                         }
428                         if (dtable[c] & 0x80) {
429                                 /* Ignoring errors: discard invalid character */
430                                 i--;
431                                 continue;
432                         }
433                         a[i] = (byte) c;
434                         b[i] = (byte) dtable[c];
435                 }
436                 o[0] = (b[0] << 2) | (b[1] >> 4);
437                 o[1] = (b[1] << 4) | (b[2] >> 2);
438                 o[2] = (b[2] << 6) | b[3];
439                 i = a[2] == '=' ? 1 : (a[3] == '=' ? 2 : 3);
440                 if (i >= 1)
441                         dest[dpos++] = o[0];
442                 if (i >= 2)
443                         dest[dpos++] = o[1];
444                 if (i >= 3)
445                         dest[dpos++] = o[2];
446                 dest[dpos] = 0;
447                 if (i < 3) {
448                         return (dpos);
449                 }
450         }
451 }
452
453
454
455 /*
456  * Generate a new, globally unique UID parameter for a calendar etc. object
457  */
458 void generate_uuid(char *buf) {
459         static int seq = 0;
460
461         sprintf(buf, "%s-%lx-%x-%x",
462                 serv_info.serv_nodename,
463                 (long)time(NULL),
464                 getpid(),
465                 (seq++)
466         );
467 }
468
469
470
471 /*
472  * bmstrstr() is a variant of strstr() that uses the Boyer-Moore search
473  * algorithm, and can use any caller-supplied string compare function whose
474  * calling syntax is similar to strncmp().  For example, we can supply it
475  * with strncasecmp() to do a case-insensitive search.
476  * 
477  * Original code: copyright (c) 1997-1998 by Urs Janssen <urs@tin.org>
478  * Modifications: copyright (c) 2003 by Art Cancro <ajc@uncensored.citadel.org>
479  */
480 char *bmstrstr(char *text, char *pattern,
481         int (*cmpfunc)(const char *, const char *, size_t) )
482 {
483         register unsigned char *p, *t;
484         register int i, j, *delta;
485         register size_t p1;
486         int deltaspace[256];
487         size_t textlen;
488         size_t patlen;
489
490         if (text == NULL) return(NULL);
491         if (pattern == NULL) return(NULL);
492
493         textlen = strlen(text);
494         patlen = strlen(pattern);
495
496         /* algorithm fails if pattern is empty */
497         if ((p1 = patlen) == 0)
498                 return (text);
499
500         /* code below fails (whenever i is unsigned) if pattern too long */
501         if (p1 > textlen)
502                 return (NULL);
503
504         /* set up deltas */
505         delta = deltaspace;
506         for (i = 0; i <= 255; i++)
507                 delta[i] = p1;
508         for (p = (unsigned char *) pattern, i = p1; --i > 0;)
509                 delta[*p++] = i;
510
511         /*
512          * From now on, we want patlen - 1.
513          * In the loop below, p points to the end of the pattern,
514          * t points to the end of the text to be tested against the
515          * pattern, and i counts the amount of text remaining, not
516          * including the part to be tested.
517          */
518         p1--;
519         p = (unsigned char *) pattern + p1;
520         t = (unsigned char *) text + p1;
521         i = textlen - patlen;
522         while (1) {
523                 if (tolower(*p) == tolower(*t)
524                    && cmpfunc((p - p1), (t - p1), p1) == 0)
525                         return ((char *) t - p1);
526                 j = delta[*t];
527                 if (i < j)
528                         break;
529                 i -= j;
530                 t += j;
531         }
532         return (NULL);
533 }
534