a4c7c2c20dfa98e8aff0034e4b4f28b13f3bf3ff
[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  * Local replacement for controversial C library function that generates
472  * names for temporary files.  Included to shut up compiler warnings.
473  */
474 void CtdlMakeTempFileName(char *name, int len) {
475         int i = 0;
476
477         while (i++, i < 100) {
478                 snprintf(name, len, "/tmp/ctdl.%04x.%04x",
479                         getpid(),
480                         rand()
481                 );
482                 if (!access(name, F_OK)) {
483                         return;
484                 }
485         }
486 }