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