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