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