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