9204a74cd06a646589f304169667d4e2a2fd8d95
[citadel.git] / webcit / tools.c
1 /*
2  * $Id$
3  */
4 /**
5  * \defgroup MiscRout Miscellaneous routines 
6  */
7
8 /*@{*/
9 #include "webcit.h"
10 #include "webserver.h"
11
12
13 typedef unsigned char byte; /**< byte data type */
14
15 #define FALSE 0 /**< no. */
16 #define TRUE 1  /**< yes. */
17
18 static byte dtable[256];        /**< base64 encode / decode table */
19
20 /**
21  * \brief sanitize strncopy.
22  * \param dest destination string
23  * \param src source string
24  * \param n length of source to copy 
25  * \return result string
26  */
27 char *safestrncpy(char *dest, const char *src, size_t n)
28 {
29         if (dest == NULL || src == NULL) {
30                 abort();
31         }
32         strncpy(dest, src, n);
33         dest[n - 1] = 0;
34         return dest;
35 }
36
37
38
39 /**
40  * \brief discover number of parameters/tokens in a string
41  * \param source string to inspect
42  * \param tok seperation token
43  * \return number of tokenized parts found
44  */
45 int num_tokens(char *source, char tok)
46 {
47         int a;
48         int count = 1;
49
50         if (source == NULL)
51                 return (0);
52         for (a = 0; a < strlen(source); ++a) {
53                 if (source[a] == tok)
54                         ++count;
55         }
56         return (count);
57 }
58
59 /**
60  * brief a string tokenizer
61  * \param dest destination string 
62  * \param source the string to grab tokens from
63  * \param parmnum the n'th token to grab
64  * \param separator the tokenizer string
65  * \param maxlen the length of dest
66  */
67 void extract_token(char *dest, const char *source, int parmnum, char separator, int maxlen)
68 {
69         char *d;                /* dest */
70         const char *s;          /* source */
71         int count = 0;
72         int len = 0;
73
74         dest[0] = 0;
75
76         /* Locate desired parameter */
77         s = source;
78         while (count < parmnum) {
79                 /* End of string, bail! */
80                 if (!*s) {
81                         s = NULL;
82                         break;
83                 }
84                 if (*s == separator) {
85                         count++;
86                 }
87                 s++;
88         }
89         if (!s) return;         /* Parameter not found */
90
91         for (d = dest; *s && *s != separator && ++len<maxlen; s++, d++) {
92                 *d = *s;
93         }
94         *d = 0;
95 }
96
97
98
99 /**
100  * \brief a tokenizer that kills, maims, and destroys
101  * \param source the string to process
102  * \param parmnum which token to kill
103  * \param separator the tokenizer string
104  */
105 void remove_token(char *source, int parmnum, char separator)
106 {
107         int i;
108         int len;
109         int curr_parm;
110         int start, end;
111
112         len = 0;
113         curr_parm = 0;
114         start = (-1);
115         end = (-1);
116
117         if (strlen(source) == 0) {
118                 return;
119         }
120
121         for (i = 0; i < strlen(source); ++i) {
122                 if ((start < 0) && (curr_parm == parmnum)) {
123                         start = i;
124                 }
125
126                 if ((end < 0) && (curr_parm == (parmnum + 1))) {
127                         end = i;
128                 }
129
130                 if (source[i] == separator) {
131                         ++curr_parm;
132                 }
133         }
134
135         if (end < 0)
136                 end = strlen(source);
137
138         strcpy(&source[start], &source[end]);
139 }
140
141
142
143
144 /**
145  * \brief extract an int parm w/o supplying a buffer
146  * \param source the string to locate the int in
147  * \param parmnum the n'th token to grab the int from
148  * \return the integer
149  */
150 int extract_int(const char *source, int parmnum)
151 {
152         char buf[32];
153         
154         extract_token(buf, source, parmnum, '|', sizeof buf);
155         return(atoi(buf));
156 }
157
158 /**
159  * \brief extract an long parm w/o supplying a buffer
160  * \param source string to examine
161  * \param parmnum n'th token to search long in
162  * \return the found long value
163  */
164 long extract_long(const char *source, int parmnum)
165 {
166         char buf[32];
167         
168         extract_token(buf, source, parmnum, '|', sizeof buf);
169         return(atol(buf));
170 }
171
172
173
174
175
176
177 /**
178  * \brief check for the presence of a character within a string (returns count)
179  * \param st the string to examine
180  * \param ch the char to search
181  * \return the position inside of st
182  */
183 int haschar(char *st,char ch)
184 {
185         int a, b;
186         b = 0;
187         for (a = 0; a < strlen(st); ++a)
188                 if (st[a] == ch)
189                         ++b;
190         return (b);
191 }
192
193
194 /** 
195  * \brief Utility function to "readline" from memory
196  * \param start Location in memory from which we are reading.
197  * \param buf the buffer to place the string in.
198  * \param maxlen Size of string buffer
199  * \return Pointer to the source memory right after we stopped reading.
200  */
201 char *memreadline(char *start, char *buf, int maxlen)
202 {
203         char ch;
204         char *ptr;
205         int len = 0;            /**< tally our own length to avoid strlen() delays */
206
207         ptr = start;
208         memset(buf, 0, maxlen);
209
210         while (1) {
211                 ch = *ptr++;
212                 if ((len < (maxlen - 1)) && (ch != 13) && (ch != 10)) {
213                         buf[strlen(buf) + 1] = 0;
214                         buf[strlen(buf)] = ch;
215                         ++len;
216                 }
217                 if ((ch == 10) || (ch == 0)) {
218                         return ptr;
219                 }
220         }
221 }
222
223
224
225 /**
226  * \brief searches for a  paternn within asearch string
227  * \param search the string to search 
228  * \param patn the pattern to find in string
229  * \returns position in string
230  */
231 int pattern2(char *search, char *patn)
232 {
233         int a;
234         for (a = 0; a < strlen(search); ++a) {
235                 if (!strncasecmp(&search[a], patn, strlen(patn)))
236                         return (a);
237         }
238         return (-1);
239 }
240
241
242 /**
243  * \brief Strip leading and trailing spaces from a string
244  * \param buf the string to modify
245  */
246 void striplt(char *buf)
247 {
248         if (strlen(buf) == 0) return;
249         while ((strlen(buf) > 0) && (isspace(buf[0])))
250                 strcpy(buf, &buf[1]);
251         if (strlen(buf) == 0) return;
252         while (isspace(buf[strlen(buf) - 1]))
253                 buf[strlen(buf) - 1] = 0;
254 }
255
256
257 /**
258  * \brief Determine whether the specified message number is contained within the
259  * specified set.
260  *
261  * \param mset Message set string
262  * \param msgnum Message number we are looking for
263  *
264  * \return Nonzero if the specified message number is in the specified message set string.
265  */
266 int is_msg_in_mset(char *mset, long msgnum) {
267         int num_sets;
268         int s;
269         char setstr[SIZ], lostr[SIZ], histr[SIZ];       /* was 1024 */
270         long lo, hi;
271
272         /*
273          * Now set it for all specified messages.
274          */
275         num_sets = num_tokens(mset, ',');
276         for (s=0; s<num_sets; ++s) {
277                 extract_token(setstr, mset, s, ',', sizeof setstr);
278
279                 extract_token(lostr, setstr, 0, ':', sizeof lostr);
280                 if (num_tokens(setstr, ':') >= 2) {
281                         extract_token(histr, setstr, 1, ':', sizeof histr);
282                         if (!strcmp(histr, "*")) {
283                                 snprintf(histr, sizeof histr, "%ld", LONG_MAX);
284                         }
285                 } 
286                 else {
287                         strcpy(histr, lostr);
288                 }
289                 lo = atol(lostr);
290                 hi = atol(histr);
291
292                 if ((msgnum >= lo) && (msgnum <= hi)) return(1);
293         }
294
295         return(0);
296 }
297
298
299
300 /**
301  * \brief Strip a boundarized substring out of a string
302  * (for example, remove
303  * parentheses and anything inside them).
304  *
305  * This improved version can strip out *multiple* boundarized substrings.
306  * \param str the string to process
307  * \param leftboundary the boundary character on the left side of the target string 
308  * \param rightboundary the boundary character on the right side of the target string
309  */
310 void stripout(char *str, char leftboundary, char rightboundary)
311 {
312         int a;
313         int lb = (-1);
314         int rb = (-1);
315
316         do {
317                 lb = (-1);
318                 rb = (-1);
319
320                 for (a = 0; a < strlen(str); ++a) {
321                         if (str[a] == leftboundary)
322                                 lb = a;
323                         if (str[a] == rightboundary)
324                                 rb = a;
325                 }
326
327                 if ((lb > 0) && (rb > lb)) {
328                         strcpy(&str[lb - 1], &str[rb + 1]);
329                 }
330
331         } while ((lb > 0) && (rb > lb));
332
333 }
334
335
336
337 /**
338  * \brief Replacement for sleep() that uses select() in order to avoid SIGALRM
339  * \param seconds how many seconds should we sleep?
340  */
341 void sleeeeeeeeeep(int seconds)
342 {
343         struct timeval tv;
344
345         tv.tv_sec = seconds;
346         tv.tv_usec = 0;
347         select(0, NULL, NULL, NULL, &tv);
348 }
349
350
351
352 /**
353  * \brief encode a string into base64 to for example tunnel it through mail transport
354  * CtdlDecodeBase64() and CtdlEncodeBase64() are adaptations of code by
355  * John Walker, copied over from the Citadel server.
356  * \param dest encrypted string
357  * \param source the string to encrypt
358  * \param sourcelen the length of the source data (may contain string terminators)
359  */
360
361 void CtdlEncodeBase64(char *dest, const char *source, size_t sourcelen)
362 {
363         int i, hiteof = FALSE;
364         int spos = 0;
365         int dpos = 0;
366         int thisline = 0;
367
368         /**  Fill dtable with character encodings.  */
369
370         for (i = 0; i < 26; i++) {
371                 dtable[i] = 'A' + i;
372                 dtable[26 + i] = 'a' + i;
373         }
374         for (i = 0; i < 10; i++) {
375                 dtable[52 + i] = '0' + i;
376         }
377         dtable[62] = '+';
378         dtable[63] = '/';
379
380         while (!hiteof) {
381                 byte igroup[3], ogroup[4];
382                 int c, n;
383
384                 igroup[0] = igroup[1] = igroup[2] = 0;
385                 for (n = 0; n < 3; n++) {
386                         if (spos >= sourcelen) {
387                                 hiteof = TRUE;
388                                 break;
389                         }
390                         c = source[spos++];
391                         igroup[n] = (byte) c;
392                 }
393                 if (n > 0) {
394                         ogroup[0] = dtable[igroup[0] >> 2];
395                         ogroup[1] =
396                             dtable[((igroup[0] & 3) << 4) |
397                                    (igroup[1] >> 4)];
398                         ogroup[2] =
399                             dtable[((igroup[1] & 0xF) << 2) |
400                                    (igroup[2] >> 6)];
401                         ogroup[3] = dtable[igroup[2] & 0x3F];
402
403                         /**
404                          * Replace characters in output stream with "=" pad
405                          * characters if fewer than three characters were
406                          * read from the end of the input stream. 
407                          */
408
409                         if (n < 3) {
410                                 ogroup[3] = '=';
411                                 if (n < 2) {
412                                         ogroup[2] = '=';
413                                 }
414                         }
415                         for (i = 0; i < 4; i++) {
416                                 dest[dpos++] = ogroup[i];
417                                 dest[dpos] = 0;
418                         }
419                         thisline += 4;
420                         if (thisline > 70) {
421                                 dest[dpos++] = '\r';
422                                 dest[dpos++] = '\n';
423                                 dest[dpos] = 0;
424                                 thisline = 0;
425                         }
426                 }
427         }
428         if (thisline > 70) {
429                 dest[dpos++] = '\r';
430                 dest[dpos++] = '\n';
431                 dest[dpos] = 0;
432                 thisline = 0;
433         }
434 }
435
436
437 /**
438  * \brief Convert base64-encoded to binary.  
439  * It will stop after reading 'length' bytes.
440  *
441  * \param dest The destination buffer 
442  * \param source The base64 data to be decoded.
443  * \param length The number of bytes to decode.
444  * \return The actual length of the decoded data.
445  */
446 int CtdlDecodeBase64(char *dest, const char *source, size_t length)
447 {
448         int i, c;
449         int dpos = 0;
450         int spos = 0;
451
452         for (i = 0; i < 255; i++) {
453                 dtable[i] = 0x80;
454         }
455         for (i = 'A'; i <= 'Z'; i++) {
456                 dtable[i] = 0 + (i - 'A');
457         }
458         for (i = 'a'; i <= 'z'; i++) {
459                 dtable[i] = 26 + (i - 'a');
460         }
461         for (i = '0'; i <= '9'; i++) {
462                 dtable[i] = 52 + (i - '0');
463         }
464         dtable['+'] = 62;
465         dtable['/'] = 63;
466         dtable['='] = 0;
467
468         /**CONSTANTCONDITION*/ while (TRUE) {
469                 byte a[4], b[4], o[3];
470
471                 for (i = 0; i < 4; i++) {
472                         if (spos >= length) {
473                                 return (dpos);
474                         }
475                         c = source[spos++];
476
477                         if (c == 0) {
478                                 if (i > 0) {
479                                         return (dpos);
480                                 }
481                                 return (dpos);
482                         }
483                         if (dtable[c] & 0x80) {
484                                 /** Ignoring errors: discard invalid character */
485                                 i--;
486                                 continue;
487                         }
488                         a[i] = (byte) c;
489                         b[i] = (byte) dtable[c];
490                 }
491                 o[0] = (b[0] << 2) | (b[1] >> 4);
492                 o[1] = (b[1] << 4) | (b[2] >> 2);
493                 o[2] = (b[2] << 6) | b[3];
494                 i = a[2] == '=' ? 1 : (a[3] == '=' ? 2 : 3);
495                 if (i >= 1)
496                         dest[dpos++] = o[0];
497                 if (i >= 2)
498                         dest[dpos++] = o[1];
499                 if (i >= 3)
500                         dest[dpos++] = o[2];
501                 dest[dpos] = 0;
502                 if (i < 3) {
503                         return (dpos);
504                 }
505         }
506 }
507
508
509
510 /**
511  * \brief Generate a new, globally unique UID parameter for a calendar etc. object
512  *
513  * \param buf String buffer into which our newly created UUID should be placed
514  */
515 void generate_uuid(char *buf) {
516         static int seq = 0;
517
518         sprintf(buf, "%s-%lx-%x-%x",
519                 serv_info.serv_nodename,
520                 (long)time(NULL),
521                 getpid(),
522                 (seq++)
523         );
524 }
525
526
527 /**
528  * \brief Local replacement for controversial C library function that generates
529  * names for temporary files.  Included to shut up compiler warnings.
530  * \todo return a fd to the file instead of the name for security reasons
531  * \param name the created filename
532  * \param len the length of the filename
533  */
534 void CtdlMakeTempFileName(char *name, int len) {
535         int i = 0;
536
537         while (i++, i < 100) {
538                 snprintf(name, len, "/tmp/ctdl.%04x.%04x",
539                         getpid(),
540                         rand()
541                 );
542                 if (!access(name, F_OK)) {
543                         return;
544                 }
545         }
546 }
547
548
549
550 /*@}*/