Done with doxygenizing
[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(st, ch)
184 char st[];
185 char ch;
186 {
187         int a, b;
188         b = 0;
189         for (a = 0; a < strlen(st); ++a)
190                 if (st[a] == ch)
191                         ++b;
192         return (b);
193 }
194
195
196 /** 
197  * \brief Utility function to "readline" from memory
198  * \param start position in buf
199  * \param buf the buffer to create string in???
200  * \param maxlen how big may the buffer get?
201  * \return new pointer to read string
202  */
203 char *memreadline(char *start, char *buf, int maxlen)
204 {
205         char ch;
206         char *ptr;
207         int len = 0;            /**< tally our own length to avoid strlen() delays */
208
209         ptr = start;
210         memset(buf, 0, maxlen);
211
212         while (1) {
213                 ch = *ptr++;
214                 if ((len < (maxlen - 1)) && (ch != 13) && (ch != 10)) {
215                         buf[strlen(buf) + 1] = 0;
216                         buf[strlen(buf)] = ch;
217                         ++len;
218                 }
219                 if ((ch == 10) || (ch == 0)) {
220                         return ptr;
221                 }
222         }
223 }
224
225
226
227 /**
228  * \brief searches for a  paternn within asearch string
229  * \param search the string to search 
230  * \param patn the pattern to find in string
231  * \returns position in string
232  */
233 int pattern2(char *search, char *patn)
234 {
235         int a;
236         for (a = 0; a < strlen(search); ++a) {
237                 if (!strncasecmp(&search[a], patn, strlen(patn)))
238                         return (a);
239         }
240         return (-1);
241 }
242
243
244 /**
245  * \brief Strip leading and trailing spaces from a string
246  * \param buf the string to modify
247  */
248 void striplt(char *buf)
249 {
250         if (strlen(buf) == 0) return;
251         while ((strlen(buf) > 0) && (isspace(buf[0])))
252                 strcpy(buf, &buf[1]);
253         if (strlen(buf) == 0) return;
254         while (isspace(buf[strlen(buf) - 1]))
255                 buf[strlen(buf) - 1] = 0;
256 }
257
258
259 /**
260  * \brief Determine whether the specified message number is contained within the
261  * specified set.
262  * \param mset stringset???
263  * \param msgnum a citadel server indexnumber
264  * \return yes / no ???
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  * \param dest the destination buffer 
441  * \param source the encrypted string
442  * \param length the maximal length of dest???
443  * \return the length actual length of the decoded data.
444  */
445 int CtdlDecodeBase64(char *dest, const char *source, size_t length)
446 {
447         int i, c;
448         int dpos = 0;
449         int spos = 0;
450
451         for (i = 0; i < 255; i++) {
452                 dtable[i] = 0x80;
453         }
454         for (i = 'A'; i <= 'Z'; i++) {
455                 dtable[i] = 0 + (i - 'A');
456         }
457         for (i = 'a'; i <= 'z'; i++) {
458                 dtable[i] = 26 + (i - 'a');
459         }
460         for (i = '0'; i <= '9'; i++) {
461                 dtable[i] = 52 + (i - '0');
462         }
463         dtable['+'] = 62;
464         dtable['/'] = 63;
465         dtable['='] = 0;
466
467         /**CONSTANTCONDITION*/ while (TRUE) {
468                 byte a[4], b[4], o[3];
469
470                 for (i = 0; i < 4; i++) {
471                         if (spos >= length) {
472                                 return (dpos);
473                         }
474                         c = source[spos++];
475
476                         if (c == 0) {
477                                 if (i > 0) {
478                                         return (dpos);
479                                 }
480                                 return (dpos);
481                         }
482                         if (dtable[c] & 0x80) {
483                                 /** Ignoring errors: discard invalid character */
484                                 i--;
485                                 continue;
486                         }
487                         a[i] = (byte) c;
488                         b[i] = (byte) dtable[c];
489                 }
490                 o[0] = (b[0] << 2) | (b[1] >> 4);
491                 o[1] = (b[1] << 4) | (b[2] >> 2);
492                 o[2] = (b[2] << 6) | b[3];
493                 i = a[2] == '=' ? 1 : (a[3] == '=' ? 2 : 3);
494                 if (i >= 1)
495                         dest[dpos++] = o[0];
496                 if (i >= 2)
497                         dest[dpos++] = o[1];
498                 if (i >= 3)
499                         dest[dpos++] = o[2];
500                 dest[dpos] = 0;
501                 if (i < 3) {
502                         return (dpos);
503                 }
504         }
505 }
506
507
508
509 /**
510  * \brief Generate a new, globally unique UID parameter for a calendar etc. object
511  * \param buf uid to create ????
512  */
513 void generate_uuid(char *buf) {
514         static int seq = 0;
515
516         sprintf(buf, "%s-%lx-%x-%x",
517                 serv_info.serv_nodename,
518                 (long)time(NULL),
519                 getpid(),
520                 (seq++)
521         );
522 }
523
524
525 /**
526  * \brief Local replacement for controversial C library function that generates
527  * names for temporary files.  Included to shut up compiler warnings.
528  * \todo return a fd to the file instead of the name for security reasons
529  * \param name the created filename
530  * \param len the length of the filename
531  */
532 void CtdlMakeTempFileName(char *name, int len) {
533         int i = 0;
534
535         while (i++, i < 100) {
536                 snprintf(name, len, "/tmp/ctdl.%04x.%04x",
537                         getpid(),
538                         rand()
539                 );
540                 if (!access(name, F_OK)) {
541                         return;
542                 }
543         }
544 }
545
546
547
548 /*@}*/