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