98c3bb584a8405d8fe61024223d03bfa6926326b
[citadel.git] / webcit / tools.c
1 /*
2  * $Id$
3  */
4 /**
5  * \defgroup MiscRout Miscellaneous routines 
6  * \ingroup tools
7  */
8
9 /*@{*/
10 #include "webcit.h"
11 #include "webserver.h"
12
13
14 typedef unsigned char byte; /**< byte data type */
15
16 #define FALSE 0 /**< no. */
17 #define TRUE 1  /**< yes. */
18
19 static byte dtable[256];        /**< base64 encode / decode table */
20
21 /**
22  * \brief sanitize strncopy.
23  * \param dest destination string
24  * \param src source string
25  * \param n length of source to copy 
26  * \return result string
27  */
28 char *safestrncpy(char *dest, const char *src, size_t n)
29 {
30         if (dest == NULL || src == NULL) {
31                 abort();
32         }
33         strncpy(dest, src, n);
34         dest[n - 1] = 0;
35         return dest;
36 }
37
38
39
40 /**
41  * \brief discover number of parameters/tokens in a string
42  * \param source string to inspect
43  * \param tok seperation token
44  * \return number of tokenized parts found
45  */
46 int num_tokens(char *source, char tok)
47 {
48         int a = 0;
49         int count = 1;
50
51         if (source == NULL)
52                 return (0);
53         for (a = 0; a < strlen(source); ++a) {
54                 if (source[a] == tok)
55                         ++count;
56         }
57         return (count);
58 }
59
60 /**
61  * brief a string tokenizer
62  * \param dest destination string 
63  * \param source the string to grab tokens from
64  * \param parmnum the n'th token to grab
65  * \param separator the tokenizer string
66  * \param maxlen the length of dest
67  */
68 void extract_token(char *dest, const char *source, int parmnum, char separator, int maxlen)
69 {
70         char *d;                /* dest */
71         const char *s;          /* source */
72         int count = 0;
73         int len = 0;
74
75         dest[0] = 0;
76
77         /* Locate desired parameter */
78         s = source;
79         while (count < parmnum) {
80                 /* End of string, bail! */
81                 if (!*s) {
82                         s = NULL;
83                         break;
84                 }
85                 if (*s == separator) {
86                         count++;
87                 }
88                 s++;
89         }
90         if (!s) return;         /* Parameter not found */
91
92         for (d = dest; *s && *s != separator && ++len<maxlen; s++, d++) {
93                 *d = *s;
94         }
95         *d = 0;
96 }
97
98
99
100 /**
101  * \brief a tokenizer that kills, maims, and destroys
102  * \param source the string to process
103  * \param parmnum which token to kill
104  * \param separator the tokenizer string
105  */
106 void remove_token(char *source, int parmnum, char separator)
107 {
108         int i;
109         int len, slen;
110         int curr_parm;
111         int start, end;
112
113         len = 0;
114         curr_parm = 0;
115         start = (-1);
116         end = (-1);
117
118         slen = strlen(source);
119         if (slen == 0) {
120                 return;
121         }
122
123         for (i = 0; 
124              ( (i < slen)  && (end == -1) ); 
125              ++i) {
126                 if ((start < 0) && (curr_parm == parmnum)) {
127                         start = i;
128                 }
129
130                 if ((end < 0) && (curr_parm == (parmnum + 1))) {
131                         end = i;
132                 }
133
134                 if (source[i] == separator) {
135                         ++curr_parm;
136                 }
137         }
138
139         if (end < 0)
140                 end = slen;
141
142         memmove(&source[start], &source[end], slen - end + 1);
143 }
144
145
146
147
148 /**
149  * \brief extract an int parm w/o supplying a buffer
150  * \param source the string to locate the int in
151  * \param parmnum the n'th token to grab the int from
152  * \return the integer
153  */
154 int extract_int(const char *source, int parmnum)
155 {
156         char buf[32];
157         
158         extract_token(buf, source, parmnum, '|', sizeof buf);
159         return(atoi(buf));
160 }
161
162 /**
163  * \brief extract an long parm w/o supplying a buffer
164  * \param source string to examine
165  * \param parmnum n'th token to search long in
166  * \return the found long value
167  */
168 long extract_long(const char *source, int parmnum)
169 {
170         char buf[32];
171         
172         extract_token(buf, source, parmnum, '|', sizeof buf);
173         return(atol(buf));
174 }
175
176
177
178
179
180
181 /**
182  * \brief check for the presence of a character within a string (returns count)
183  * \param st the string to examine
184  * \param ch the char to search
185  * \return the position inside of st
186  */
187 int haschar(char *st,char ch)
188 {
189         int a, b;
190         b = 0;
191         for (a = 0; a < strlen(st); ++a)
192                 if (st[a] == ch)
193                         ++b;
194         return (b);
195 }
196
197
198 /** 
199  * \brief Utility function to "readline" from memory
200  * \param start Location in memory from which we are reading.
201  * \param buf the buffer to place the string in.
202  * \param maxlen Size of string buffer
203  * \return Pointer to the source memory right after we stopped reading.
204  */
205 char *memreadline(char *start, char *buf, int maxlen)
206 {
207         char ch;
208         char *ptr;
209         int len = 0;            /**< tally our own length to avoid strlen() delays */
210
211         ptr = start;
212         memset(buf, 0, maxlen);
213
214         while (1) {
215                 ch = *ptr++;
216                 if ((len < (maxlen - 1)) && (ch != 13) && (ch != 10)) {
217                         buf[strlen(buf) + 1] = 0;
218                         buf[strlen(buf)] = ch;
219                         ++len;
220                 }
221                 if ((ch == 10) || (ch == 0)) {
222                         return ptr;
223                 }
224         }
225 }
226
227
228
229 /**
230  * \brief searches for a  paternn within asearch string
231  * \param search the string to search 
232  * \param patn the pattern to find in string
233  * \returns position in string
234  */
235 int pattern2(char *search, char *patn)
236 {
237         int a;
238         for (a = 0; a < strlen(search); ++a) {
239                 if (!strncasecmp(&search[a], patn, strlen(patn)))
240                         return (a);
241         }
242         return (-1);
243 }
244
245
246 /**
247  * \brief Strip leading and trailing spaces from a string
248  * \param buf the string to modify
249  */
250 void striplt(char *buf)
251 {
252         long len;
253
254         len = strlen(buf);
255         if (len == 0) return;
256         while ((len > 0) && (isspace(buf[0]))){
257                 memmove (buf, &buf[1], len);
258                 len --;
259         }
260         if (len == 0) return;
261         while (isspace(buf[len - 1])){
262                 buf[len - 1] = 0;
263                 len --;
264         }
265 }
266
267
268 /**
269  * \brief Determine whether the specified message number is contained within the
270  * specified set.
271  *
272  * \param mset Message set string
273  * \param msgnum Message number we are looking for
274  *
275  * \return Nonzero if the specified message number is in the specified message set string.
276  */
277 int is_msg_in_mset(char *mset, long msgnum) {
278         int num_sets;
279         int s;
280         char setstr[SIZ], lostr[SIZ], histr[SIZ];       /* was 1024 */
281         long lo, hi;
282
283         /*
284          * Now set it for all specified messages.
285          */
286         num_sets = num_tokens(mset, ',');
287         for (s=0; s<num_sets; ++s) {
288                 extract_token(setstr, mset, s, ',', sizeof setstr);
289
290                 extract_token(lostr, setstr, 0, ':', sizeof lostr);
291                 if (num_tokens(setstr, ':') >= 2) {
292                         extract_token(histr, setstr, 1, ':', sizeof histr);
293                         if (!strcmp(histr, "*")) {
294                                 snprintf(histr, sizeof histr, "%ld", LONG_MAX);
295                         }
296                 } 
297                 else {
298                         strcpy(histr, lostr);
299                 }
300                 lo = atol(lostr);
301                 hi = atol(histr);
302
303                 if ((msgnum >= lo) && (msgnum <= hi)) return(1);
304         }
305
306         return(0);
307 }
308
309
310
311 /**
312  * \brief Strip a boundarized substring out of a string
313  * (for example, remove
314  * parentheses and anything inside them).
315  *
316  * This improved version can strip out *multiple* boundarized substrings.
317  * \param str the string to process
318  * \param leftboundary the boundary character on the left side of the target string 
319  * \param rightboundary the boundary character on the right side of the target string
320  */
321 void stripout(char *str, char leftboundary, char rightboundary)
322 {
323         int a;
324         int lb = (-1);
325         int rb = (-1);
326
327         do {
328                 lb = (-1);
329                 rb = (-1);
330
331                 for (a = 0; a < strlen(str); ++a) {
332                         if (str[a] == leftboundary)
333                                 lb = a;
334                         if (str[a] == rightboundary)
335                                 rb = a;
336                 }
337
338                 if ((lb > 0) && (rb > lb)) {
339                         strcpy(&str[lb - 1], &str[rb + 1]);
340                 }
341
342         } while ((lb > 0) && (rb > lb));
343
344 }
345
346
347
348 /**
349  * \brief Replacement for sleep() that uses select() in order to avoid SIGALRM
350  * \param seconds how many seconds should we sleep?
351  */
352 void sleeeeeeeeeep(int seconds)
353 {
354         struct timeval tv;
355
356         tv.tv_sec = seconds;
357         tv.tv_usec = 0;
358         select(0, NULL, NULL, NULL, &tv);
359 }
360
361
362
363 /**
364  * \brief encode a string into base64 to for example tunnel it through mail transport
365  * CtdlDecodeBase64() and CtdlEncodeBase64() are adaptations of code by
366  * John Walker, copied over from the Citadel server.
367  * \param dest encrypted string
368  * \param source the string to encrypt
369  * \param sourcelen the length of the source data (may contain string terminators)
370  */
371
372 void CtdlEncodeBase64(char *dest, const char *source, size_t sourcelen, int linebreaks)
373 {
374         int i, hiteof = FALSE;
375         int spos = 0;
376         int dpos = 0;
377         int thisline = 0;
378
379         /**  Fill dtable with character encodings.  */
380
381         for (i = 0; i < 26; i++) {
382                 dtable[i] = 'A' + i;
383                 dtable[26 + i] = 'a' + i;
384         }
385         for (i = 0; i < 10; i++) {
386                 dtable[52 + i] = '0' + i;
387         }
388         dtable[62] = '+';
389         dtable[63] = '/';
390
391         while (!hiteof) {
392                 byte igroup[3], ogroup[4];
393                 int c, n;
394
395                 igroup[0] = igroup[1] = igroup[2] = 0;
396                 for (n = 0; n < 3; n++) {
397                         if (spos >= sourcelen) {
398                                 hiteof = TRUE;
399                                 break;
400                         }
401                         c = source[spos++];
402                         igroup[n] = (byte) c;
403                 }
404                 if (n > 0) {
405                         ogroup[0] = dtable[igroup[0] >> 2];
406                         ogroup[1] =
407                             dtable[((igroup[0] & 3) << 4) |
408                                    (igroup[1] >> 4)];
409                         ogroup[2] =
410                             dtable[((igroup[1] & 0xF) << 2) |
411                                    (igroup[2] >> 6)];
412                         ogroup[3] = dtable[igroup[2] & 0x3F];
413
414                         /**
415                          * Replace characters in output stream with "=" pad
416                          * characters if fewer than three characters were
417                          * read from the end of the input stream. 
418                          */
419
420                         if (n < 3) {
421                                 ogroup[3] = '=';
422                                 if (n < 2) {
423                                         ogroup[2] = '=';
424                                 }
425                         }
426                         for (i = 0; i < 4; i++) {
427                                 dest[dpos++] = ogroup[i];
428                                 dest[dpos] = 0;
429                         }
430                         thisline += 4;
431                         if ( (linebreaks) && (thisline > 70) ) {
432                                 dest[dpos++] = '\r';
433                                 dest[dpos++] = '\n';
434                                 dest[dpos] = 0;
435                                 thisline = 0;
436                         }
437                 }
438         }
439         if ( (linebreaks) && (thisline > 70) ) {
440                 dest[dpos++] = '\r';
441                 dest[dpos++] = '\n';
442                 dest[dpos] = 0;
443                 thisline = 0;
444         }
445 }
446
447
448 /**
449  * \brief Convert base64-encoded to binary.  
450  * It will stop after reading 'length' bytes.
451  *
452  * \param dest The destination buffer 
453  * \param source The base64 data to be decoded.
454  * \param length The number of bytes to decode.
455  * \return The actual length of the decoded data.
456  */
457 int CtdlDecodeBase64(char *dest, const char *source, size_t length)
458 {
459         int i, c;
460         int dpos = 0;
461         int spos = 0;
462
463         for (i = 0; i < 255; i++) {
464                 dtable[i] = 0x80;
465         }
466         for (i = 'A'; i <= 'Z'; i++) {
467                 dtable[i] = 0 + (i - 'A');
468         }
469         for (i = 'a'; i <= 'z'; i++) {
470                 dtable[i] = 26 + (i - 'a');
471         }
472         for (i = '0'; i <= '9'; i++) {
473                 dtable[i] = 52 + (i - '0');
474         }
475         dtable['+'] = 62;
476         dtable['/'] = 63;
477         dtable['='] = 0;
478
479         /**CONSTANTCONDITION*/ while (TRUE) {
480                 byte a[4], b[4], o[3];
481
482                 for (i = 0; i < 4; i++) {
483                         if (spos >= length) {
484                                 return (dpos);
485                         }
486                         c = source[spos++];
487
488                         if (c == 0) {
489                                 if (i > 0) {
490                                         return (dpos);
491                                 }
492                                 return (dpos);
493                         }
494                         if (dtable[c] & 0x80) {
495                                 /** Ignoring errors: discard invalid character */
496                                 i--;
497                                 continue;
498                         }
499                         a[i] = (byte) c;
500                         b[i] = (byte) dtable[c];
501                 }
502                 o[0] = (b[0] << 2) | (b[1] >> 4);
503                 o[1] = (b[1] << 4) | (b[2] >> 2);
504                 o[2] = (b[2] << 6) | b[3];
505                 i = a[2] == '=' ? 1 : (a[3] == '=' ? 2 : 3);
506                 if (i >= 1)
507                         dest[dpos++] = o[0];
508                 if (i >= 2)
509                         dest[dpos++] = o[1];
510                 if (i >= 3)
511                         dest[dpos++] = o[2];
512                 dest[dpos] = 0;
513                 if (i < 3) {
514                         return (dpos);
515                 }
516         }
517 }
518
519
520
521 /**
522  * \brief Generate a new, globally unique UID parameter for a calendar etc. object
523  *
524  * \param buf String buffer into which our newly created UUID should be placed
525  */
526 void generate_uuid(char *buf) {
527         static int seq = 0;
528
529         sprintf(buf, "%s-%lx-%lx-%x",
530                 serv_info.serv_nodename,
531                 (long)time(NULL),
532                 (long)getpid(),
533                 (seq++)
534         );
535 }
536
537
538 /**
539  * \brief Local replacement for controversial C library function that generates
540  * names for temporary files.  Included to shut up compiler warnings.
541  * \todo return a fd to the file instead of the name for security reasons
542  * \param name the created filename
543  * \param len the length of the filename
544  */
545 void CtdlMakeTempFileName(char *name, int len) {
546         int i = 0;
547
548         while (i++, i < 100) {
549                 snprintf(name, len, "/tmp/ctdl.%04x.%04x",
550                         getpid(),
551                         rand()
552                 );
553                 if (!access(name, F_OK)) {
554                         return;
555                 }
556         }
557 }
558
559
560
561 /*
562  * \brief       case-insensitive substring search
563  *
564  *              This uses the Boyer-Moore search algorithm and is therefore quite fast.
565  *              The code is roughly based on the strstr() replacement from 'tin' written
566  *              by Urs Jannsen.
567  *
568  * \param       text    String to be searched
569  * \param       pattern String to search for
570  */
571 char *bmstrcasestr(char *text, char *pattern) {
572
573         register unsigned char *p, *t;
574         register int i, j, *delta;
575         register size_t p1;
576         int deltaspace[256];
577         size_t textlen;
578         size_t patlen;
579
580         textlen = strlen (text);
581         patlen = strlen (pattern);
582
583         /* algorithm fails if pattern is empty */
584         if ((p1 = patlen) == 0)
585                 return (text);
586
587         /* code below fails (whenever i is unsigned) if pattern too long */
588         if (p1 > textlen)
589                 return (NULL);
590
591         /* set up deltas */
592         delta = deltaspace;
593         for (i = 0; i <= 255; i++)
594                 delta[i] = p1;
595         for (p = (unsigned char *) pattern, i = p1; --i > 0;)
596                 delta[tolower(*p++)] = i;
597
598         /*
599          * From now on, we want patlen - 1.
600          * In the loop below, p points to the end of the pattern,
601          * t points to the end of the text to be tested against the
602          * pattern, and i counts the amount of text remaining, not
603          * including the part to be tested.
604          */
605         p1--;
606         p = (unsigned char *) pattern + p1;
607         t = (unsigned char *) text + p1;
608         i = textlen - patlen;
609         while(1) {
610                 if (tolower(p[0]) == tolower(t[0])) {
611                         if (strncasecmp ((const char *)(p - p1), (const char *)(t - p1), p1) == 0) {
612                                 return ((char *)t - p1);
613                         }
614                 }
615                 j = delta[tolower(t[0])];
616                 if (i < j)
617                         break;
618                 i -= j;
619                 t += j;
620         }
621         return (NULL);
622 }
623
624
625
626
627
628 /*@}*/