149f48d3fe0c4a123b8f9eb7e26ef6760f7bab56
[citadel.git] / webcit / tools.c
1 /*
2  * $Id$
3  *
4  * Miscellaneous routines 
5  */
6
7 #include <ctype.h>
8 #include <stdlib.h>
9 #include <unistd.h>
10 #include <stdio.h>
11 #include <fcntl.h>
12 #include <signal.h>
13 #include <sys/types.h>
14 #include <sys/wait.h>
15 #include <sys/socket.h>
16 #include <sys/time.h>
17 #include <limits.h>
18 #include <netinet/in.h>
19 #include <netdb.h>
20 #include <string.h>
21 #include <pwd.h>
22 #include <errno.h>
23 #include <stdarg.h>
24 #include <pthread.h>
25 #include <signal.h>
26 #include <sys/time.h>
27 #include "webcit.h"
28 #include "webserver.h"
29
30 typedef unsigned char byte;
31
32 #define FALSE 0
33 #define TRUE 1
34
35 char *ascmonths[] = {
36         "Jan", "Feb", "Mar", "Apr", "May", "Jun",
37         "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
38 };
39
40 char *ascdays[] = {
41         "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
42 };
43
44 static byte dtable[256];        /* base64 encode / decode table */
45
46 char *safestrncpy(char *dest, const char *src, size_t n)
47 {
48         if (dest == NULL || src == NULL) {
49                 abort();
50         }
51         strncpy(dest, src, n);
52         dest[n - 1] = 0;
53         return dest;
54 }
55
56
57
58 /*
59  * num_tokens()  -  discover number of parameters/tokens in a string
60  */
61 int num_tokens(char *source, char tok)
62 {
63         int a;
64         int count = 1;
65
66         if (source == NULL)
67                 return (0);
68         for (a = 0; a < strlen(source); ++a) {
69                 if (source[a] == tok)
70                         ++count;
71         }
72         return (count);
73 }
74
75 /*
76  * extract_token() - a string tokenizer
77  */
78 void extract_token(char *dest, const char *source, int parmnum, char separator, int maxlen)
79 {
80         char *d;                /* dest */
81         const char *s;          /* source */
82         int count = 0;
83         int len = 0;
84
85         dest[0] = 0;
86
87         /* Locate desired parameter */
88         s = source;
89         while (count < parmnum) {
90                 /* End of string, bail! */
91                 if (!*s) {
92                         s = NULL;
93                         break;
94                 }
95                 if (*s == separator) {
96                         count++;
97                 }
98                 s++;
99         }
100         if (!s) return;         /* Parameter not found */
101
102         for (d = dest; *s && *s != separator && ++len<maxlen; s++, d++) {
103                 *d = *s;
104         }
105         *d = 0;
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)
144                 end = strlen(source);
145
146         strcpy(&source[start], &source[end]);
147 }
148
149
150
151
152 /*
153  * extract_int()  -  extract an int parm w/o supplying a buffer
154  */
155 int extract_int(const char *source, int parmnum)
156 {
157         char buf[32];
158         
159         extract_token(buf, source, parmnum, '|', sizeof buf);
160         return(atoi(buf));
161 }
162
163 /*
164  * extract_long()  -  extract an long parm w/o supplying a buffer
165  */
166 long extract_long(const char *source, int parmnum)
167 {
168         char buf[32];
169         
170         extract_token(buf, source, parmnum, '|', sizeof buf);
171         return(atol(buf));
172 }
173
174
175
176
177
178
179 /*
180  * check for the presence of a character within a string (returns count)
181  */
182 int haschar(st, ch)
183 char st[];
184 char ch;
185 {
186         int a, b;
187         b = 0;
188         for (a = 0; a < strlen(st); ++a)
189                 if (st[a] == ch)
190                         ++b;
191         return (b);
192 }
193
194
195 /*
196  * Format a date/time stamp for output 
197  */
198 void fmt_date(char *buf, time_t thetime, int brief)
199 {
200         struct tm tm;
201         struct tm today_tm;
202         time_t today_timet;
203         int hour;
204
205         today_timet = time(NULL);
206         localtime_r(&today_timet, &today_tm);
207
208         localtime_r(&thetime, &tm);
209         hour = tm.tm_hour;
210         if (hour == 0)
211                 hour = 12;
212         else if (hour > 12)
213                 hour = hour - 12;
214
215         buf[0] = 0;
216
217         if (brief) {
218
219                 if ((tm.tm_year == today_tm.tm_year)
220                   &&(tm.tm_mon == today_tm.tm_mon)
221                   &&(tm.tm_mday == today_tm.tm_mday)) {
222                         sprintf(buf, "%2d:%02d%s",
223                                 hour, tm.tm_min,
224                                 ((tm.tm_hour >= 12) ? "pm" : "am")
225                         );
226                 }
227                 else {
228                         sprintf(buf, "%s %d %d",
229                                 ascmonths[tm.tm_mon],
230                                 tm.tm_mday,
231                                 tm.tm_year + 1900
232                         );
233                 }
234         }
235         else {
236                 sprintf(buf, "%s %d %d %2d:%02d%s",
237                         ascmonths[tm.tm_mon],
238                         tm.tm_mday,
239                         tm.tm_year + 1900,
240                         hour, tm.tm_min, ((tm.tm_hour >= 12) ? "pm" : "am")
241                 );
242         }
243 }
244
245
246
247 /*
248  * Format TIME ONLY for output 
249  */
250 void fmt_time(char *buf, time_t thetime)
251 {
252         struct tm *tm;
253         int hour;
254
255         buf[0] = 0;
256         tm = localtime(&thetime);
257         hour = tm->tm_hour;
258         if (hour == 0)
259                 hour = 12;
260         else if (hour > 12)
261                 hour = hour - 12;
262
263         sprintf(buf, "%d:%02d%s",
264                 hour, tm->tm_min, ((tm->tm_hour > 12) ? "pm" : "am")
265             );
266 }
267
268
269
270
271 /*
272  * Format a date/time stamp to the format used in HTTP headers
273  */
274 void httpdate(char *buf, time_t thetime)
275 {
276         struct tm *tm;
277
278         buf[0] = 0;
279         tm = localtime(&thetime);
280
281         sprintf(buf, "%s, %02d %s %4d %02d:%02d:%02d",
282                 ascdays[tm->tm_wday],
283                 tm->tm_mday,
284                 ascmonths[tm->tm_mon],
285                 tm->tm_year + 1900, tm->tm_hour, tm->tm_min, tm->tm_sec);
286 }
287
288
289
290
291
292 /*
293  * Utility function to "readline" from memory
294  * (returns new pointer)
295  */
296 char *memreadline(char *start, char *buf, int maxlen)
297 {
298         char ch;
299         char *ptr;
300         int len = 0;            /* tally our own length to avoid strlen() delays */
301
302         ptr = start;
303         memset(buf, 0, maxlen);
304
305         while (1) {
306                 ch = *ptr++;
307                 if ((len < (maxlen - 1)) && (ch != 13) && (ch != 10)) {
308                         buf[strlen(buf) + 1] = 0;
309                         buf[strlen(buf)] = ch;
310                         ++len;
311                 }
312                 if ((ch == 10) || (ch == 0)) {
313                         return ptr;
314                 }
315         }
316 }
317
318
319
320 /*
321  * pattern2()  -  searches for patn within search string, returns pos
322  */
323 int pattern2(char *search, char *patn)
324 {
325         int a;
326         for (a = 0; a < strlen(search); ++a) {
327                 if (!strncasecmp(&search[a], patn, strlen(patn)))
328                         return (a);
329         }
330         return (-1);
331 }
332
333
334 /*
335  * Strip leading and trailing spaces from a string
336  */
337 void striplt(char *buf)
338 {
339         if (strlen(buf) == 0) return;
340         while ((strlen(buf) > 0) && (isspace(buf[0])))
341                 strcpy(buf, &buf[1]);
342         if (strlen(buf) == 0) return;
343         while (isspace(buf[strlen(buf) - 1]))
344                 buf[strlen(buf) - 1] = 0;
345 }
346
347
348 /*
349  * Determine whether the specified message number is contained within the
350  * specified set.
351  */
352 int is_msg_in_mset(char *mset, long msgnum) {
353         int num_sets;
354         int s;
355         char setstr[SIZ], lostr[SIZ], histr[SIZ];       /* was 1024 */
356         long lo, hi;
357
358         /*
359          * Now set it for all specified messages.
360          */
361         num_sets = num_tokens(mset, ',');
362         for (s=0; s<num_sets; ++s) {
363                 extract_token(setstr, mset, s, ',', sizeof setstr);
364
365                 extract_token(lostr, setstr, 0, ':', sizeof lostr);
366                 if (num_tokens(setstr, ':') >= 2) {
367                         extract_token(histr, setstr, 1, ':', sizeof histr);
368                         if (!strcmp(histr, "*")) {
369                                 snprintf(histr, sizeof histr, "%ld", LONG_MAX);
370                         }
371                 } 
372                 else {
373                         strcpy(histr, lostr);
374                 }
375                 lo = atol(lostr);
376                 hi = atol(histr);
377
378                 if ((msgnum >= lo) && (msgnum <= hi)) return(1);
379         }
380
381         return(0);
382 }
383
384
385
386 /*
387  * Strip a boundarized substring out of a string (for example, remove
388  * parentheses and anything inside them).
389  *
390  * This improved version can strip out *multiple* boundarized substrings.
391  */
392 void stripout(char *str, char leftboundary, char rightboundary)
393 {
394         int a;
395         int lb = (-1);
396         int rb = (-1);
397
398         do {
399                 lb = (-1);
400                 rb = (-1);
401
402                 for (a = 0; a < strlen(str); ++a) {
403                         if (str[a] == leftboundary)
404                                 lb = a;
405                         if (str[a] == rightboundary)
406                                 rb = a;
407                 }
408
409                 if ((lb > 0) && (rb > lb)) {
410                         strcpy(&str[lb - 1], &str[rb + 1]);
411                 }
412
413         } while ((lb > 0) && (rb > lb));
414
415 }
416
417
418
419 /*
420  * Replacement for sleep() that uses select() in order to avoid SIGALRM
421  */
422 void sleeeeeeeeeep(int seconds)
423 {
424         struct timeval tv;
425
426         tv.tv_sec = seconds;
427         tv.tv_usec = 0;
428         select(0, NULL, NULL, NULL, &tv);
429 }
430
431
432
433 /*
434  * CtdlDecodeBase64() and CtdlEncodeBase64() are adaptations of code by
435  * John Walker, copied over from the Citadel server.
436  */
437
438 void CtdlEncodeBase64(char *dest, const char *source, size_t sourcelen)
439 {
440         int i, hiteof = FALSE;
441         int spos = 0;
442         int dpos = 0;
443         int thisline = 0;
444
445         /*  Fill dtable with character encodings.  */
446
447         for (i = 0; i < 26; i++) {
448                 dtable[i] = 'A' + i;
449                 dtable[26 + i] = 'a' + i;
450         }
451         for (i = 0; i < 10; i++) {
452                 dtable[52 + i] = '0' + i;
453         }
454         dtable[62] = '+';
455         dtable[63] = '/';
456
457         while (!hiteof) {
458                 byte igroup[3], ogroup[4];
459                 int c, n;
460
461                 igroup[0] = igroup[1] = igroup[2] = 0;
462                 for (n = 0; n < 3; n++) {
463                         if (spos >= sourcelen) {
464                                 hiteof = TRUE;
465                                 break;
466                         }
467                         c = source[spos++];
468                         igroup[n] = (byte) c;
469                 }
470                 if (n > 0) {
471                         ogroup[0] = dtable[igroup[0] >> 2];
472                         ogroup[1] =
473                             dtable[((igroup[0] & 3) << 4) |
474                                    (igroup[1] >> 4)];
475                         ogroup[2] =
476                             dtable[((igroup[1] & 0xF) << 2) |
477                                    (igroup[2] >> 6)];
478                         ogroup[3] = dtable[igroup[2] & 0x3F];
479
480                         /* Replace characters in output stream with "=" pad
481                            characters if fewer than three characters were
482                            read from the end of the input stream. */
483
484                         if (n < 3) {
485                                 ogroup[3] = '=';
486                                 if (n < 2) {
487                                         ogroup[2] = '=';
488                                 }
489                         }
490                         for (i = 0; i < 4; i++) {
491                                 dest[dpos++] = ogroup[i];
492                                 dest[dpos] = 0;
493                         }
494                         thisline += 4;
495                         if (thisline > 70) {
496                                 dest[dpos++] = '\r';
497                                 dest[dpos++] = '\n';
498                                 dest[dpos] = 0;
499                                 thisline = 0;
500                         }
501                 }
502         }
503         if (thisline > 70) {
504                 dest[dpos++] = '\r';
505                 dest[dpos++] = '\n';
506                 dest[dpos] = 0;
507                 thisline = 0;
508         }
509 }
510
511
512 /* 
513  * Convert base64-encoded to binary.  Returns the length of the decoded data.
514  * It will stop after reading 'length' bytes.
515  */
516 int CtdlDecodeBase64(char *dest, const char *source, size_t length)
517 {
518         int i, c;
519         int dpos = 0;
520         int spos = 0;
521
522         for (i = 0; i < 255; i++) {
523                 dtable[i] = 0x80;
524         }
525         for (i = 'A'; i <= 'Z'; i++) {
526                 dtable[i] = 0 + (i - 'A');
527         }
528         for (i = 'a'; i <= 'z'; i++) {
529                 dtable[i] = 26 + (i - 'a');
530         }
531         for (i = '0'; i <= '9'; i++) {
532                 dtable[i] = 52 + (i - '0');
533         }
534         dtable['+'] = 62;
535         dtable['/'] = 63;
536         dtable['='] = 0;
537
538          /*CONSTANTCONDITION*/ while (TRUE) {
539                 byte a[4], b[4], o[3];
540
541                 for (i = 0; i < 4; i++) {
542                         if (spos >= length) {
543                                 return (dpos);
544                         }
545                         c = source[spos++];
546
547                         if (c == 0) {
548                                 if (i > 0) {
549                                         return (dpos);
550                                 }
551                                 return (dpos);
552                         }
553                         if (dtable[c] & 0x80) {
554                                 /* Ignoring errors: discard invalid character */
555                                 i--;
556                                 continue;
557                         }
558                         a[i] = (byte) c;
559                         b[i] = (byte) dtable[c];
560                 }
561                 o[0] = (b[0] << 2) | (b[1] >> 4);
562                 o[1] = (b[1] << 4) | (b[2] >> 2);
563                 o[2] = (b[2] << 6) | b[3];
564                 i = a[2] == '=' ? 1 : (a[3] == '=' ? 2 : 3);
565                 if (i >= 1)
566                         dest[dpos++] = o[0];
567                 if (i >= 2)
568                         dest[dpos++] = o[1];
569                 if (i >= 3)
570                         dest[dpos++] = o[2];
571                 dest[dpos] = 0;
572                 if (i < 3) {
573                         return (dpos);
574                 }
575         }
576 }
577
578
579
580 /*
581  * Generate a new, globally unique UID parameter for a calendar etc. object
582  */
583 void generate_uuid(char *buf) {
584         static int seq = 0;
585
586         sprintf(buf, "%s-%lx-%x-%x",
587                 serv_info.serv_nodename,
588                 time(NULL),
589                 getpid(),
590                 (seq++)
591         );
592 }
593