]> code.citadel.org Git - citadel.git/blob - webcit/tools.c
upsie. some lines vanished.
[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 position in buf
197  * \param buf the buffer to create string in???
198  * \param maxlen how big may the buffer get?
199  * \return new pointer to read string
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  * \param mset stringset???
261  * \param msgnum a citadel server indexnumber
262  * \return yes / no ???
263  */
264 int is_msg_in_mset(char *mset, long msgnum) {
265         int num_sets;
266         int s;
267         char setstr[SIZ], lostr[SIZ], histr[SIZ];       /* was 1024 */
268         long lo, hi;
269
270         /*
271          * Now set it for all specified messages.
272          */
273         num_sets = num_tokens(mset, ',');
274         for (s=0; s<num_sets; ++s) {
275                 extract_token(setstr, mset, s, ',', sizeof setstr);
276
277                 extract_token(lostr, setstr, 0, ':', sizeof lostr);
278                 if (num_tokens(setstr, ':') >= 2) {
279                         extract_token(histr, setstr, 1, ':', sizeof histr);
280                         if (!strcmp(histr, "*")) {
281                                 snprintf(histr, sizeof histr, "%ld", LONG_MAX);
282                         }
283                 } 
284                 else {
285                         strcpy(histr, lostr);
286                 }
287                 lo = atol(lostr);
288                 hi = atol(histr);
289
290                 if ((msgnum >= lo) && (msgnum <= hi)) return(1);
291         }
292
293         return(0);
294 }
295
296
297
298 /**
299  * \brief Strip a boundarized substring out of a string
300  * (for example, remove
301  * parentheses and anything inside them).
302  *
303  * This improved version can strip out *multiple* boundarized substrings.
304  * \param str the string to process
305  * \param leftboundary the boundary character on the left side of the target string 
306  * \param rightboundary the boundary character on the right side of the target string
307  */
308 void stripout(char *str, char leftboundary, char rightboundary)
309 {
310         int a;
311         int lb = (-1);
312         int rb = (-1);
313
314         do {
315                 lb = (-1);
316                 rb = (-1);
317
318                 for (a = 0; a < strlen(str); ++a) {
319                         if (str[a] == leftboundary)
320                                 lb = a;
321                         if (str[a] == rightboundary)
322                                 rb = a;
323                 }
324
325                 if ((lb > 0) && (rb > lb)) {
326                         strcpy(&str[lb - 1], &str[rb + 1]);
327                 }
328
329         } while ((lb > 0) && (rb > lb));
330
331 }
332
333
334
335 /**
336  * \brief Replacement for sleep() that uses select() in order to avoid SIGALRM
337  * \param seconds how many seconds should we sleep?
338  */
339 void sleeeeeeeeeep(int seconds)
340 {
341         struct timeval tv;
342
343         tv.tv_sec = seconds;
344         tv.tv_usec = 0;
345         select(0, NULL, NULL, NULL, &tv);
346 }
347
348
349
350 /**
351  * \brief encode a string into base64 to for example tunnel it through mail transport
352  * CtdlDecodeBase64() and CtdlEncodeBase64() are adaptations of code by
353  * John Walker, copied over from the Citadel server.
354  * \param dest encrypted string
355  * \param source the string to encrypt
356  * \param sourcelen the length of the source data (may contain string terminators)
357  */
358
359 void CtdlEncodeBase64(char *dest, const char *source, size_t sourcelen)
360 {
361         int i, hiteof = FALSE;
362         int spos = 0;
363         int dpos = 0;
364         int thisline = 0;
365
366         /**  Fill dtable with character encodings.  */
367
368         for (i = 0; i < 26; i++) {
369                 dtable[i] = 'A' + i;
370                 dtable[26 + i] = 'a' + i;
371         }
372         for (i = 0; i < 10; i++) {
373                 dtable[52 + i] = '0' + i;
374         }
375         dtable[62] = '+';
376         dtable[63] = '/';
377
378         while (!hiteof) {
379                 byte igroup[3], ogroup[4];
380                 int c, n;
381
382                 igroup[0] = igroup[1] = igroup[2] = 0;
383                 for (n = 0; n < 3; n++) {
384                         if (spos >= sourcelen) {
385                                 hiteof = TRUE;
386                                 break;
387                         }
388                         c = source[spos++];
389                         igroup[n] = (byte) c;
390                 }
391                 if (n > 0) {
392                         ogroup[0] = dtable[igroup[0] >> 2];
393                         ogroup[1] =
394                             dtable[((igroup[0] & 3) << 4) |
395                                    (igroup[1] >> 4)];
396                         ogroup[2] =
397                             dtable[((igroup[1] & 0xF) << 2) |
398                                    (igroup[2] >> 6)];
399                         ogroup[3] = dtable[igroup[2] & 0x3F];
400
401                         /**
402                          * Replace characters in output stream with "=" pad
403                          * characters if fewer than three characters were
404                          * read from the end of the input stream. 
405                          */
406
407                         if (n < 3) {
408                                 ogroup[3] = '=';
409                                 if (n < 2) {
410                                         ogroup[2] = '=';
411                                 }
412                         }
413                         for (i = 0; i < 4; i++) {
414                                 dest[dpos++] = ogroup[i];
415                                 dest[dpos] = 0;
416                         }
417                         thisline += 4;
418                         if (thisline > 70) {
419                                 dest[dpos++] = '\r';
420                                 dest[dpos++] = '\n';
421                                 dest[dpos] = 0;
422                                 thisline = 0;
423                         }
424                 }
425         }
426         if (thisline > 70) {
427                 dest[dpos++] = '\r';
428                 dest[dpos++] = '\n';
429                 dest[dpos] = 0;
430                 thisline = 0;
431         }
432 }
433
434
435 /**
436  * \brief Convert base64-encoded to binary.  
437  * It will stop after reading 'length' bytes.
438  * \param dest the destination buffer 
439  * \param source the encrypted string
440  * \param length the maximal length of dest???
441  * \return the length actual length of the decoded data.
442  */
443 int CtdlDecodeBase64(char *dest, const char *source, size_t length)
444 {
445         int i, c;
446         int dpos = 0;
447         int spos = 0;
448
449         for (i = 0; i < 255; i++) {
450                 dtable[i] = 0x80;
451         }
452         for (i = 'A'; i <= 'Z'; i++) {
453                 dtable[i] = 0 + (i - 'A');
454         }
455         for (i = 'a'; i <= 'z'; i++) {
456                 dtable[i] = 26 + (i - 'a');
457         }
458         for (i = '0'; i <= '9'; i++) {
459                 dtable[i] = 52 + (i - '0');
460         }
461         dtable['+'] = 62;
462         dtable['/'] = 63;
463         dtable['='] = 0;
464
465         /**CONSTANTCONDITION*/ while (TRUE) {
466                 byte a[4], b[4], o[3];
467
468                 for (i = 0; i < 4; i++) {
469                         if (spos >= length) {
470                                 return (dpos);
471                         }
472                         c = source[spos++];
473
474                         if (c == 0) {
475                                 if (i > 0) {
476                                         return (dpos);
477                                 }
478                                 return (dpos);
479                         }
480                         if (dtable[c] & 0x80) {
481                                 /** Ignoring errors: discard invalid character */
482                                 i--;
483                                 continue;
484                         }
485                         a[i] = (byte) c;
486                         b[i] = (byte) dtable[c];
487                 }
488                 o[0] = (b[0] << 2) | (b[1] >> 4);
489                 o[1] = (b[1] << 4) | (b[2] >> 2);
490                 o[2] = (b[2] << 6) | b[3];
491                 i = a[2] == '=' ? 1 : (a[3] == '=' ? 2 : 3);
492                 if (i >= 1)
493                         dest[dpos++] = o[0];
494                 if (i >= 2)
495                         dest[dpos++] = o[1];
496                 if (i >= 3)
497                         dest[dpos++] = o[2];
498                 dest[dpos] = 0;
499                 if (i < 3) {
500                         return (dpos);
501                 }
502         }
503 }
504
505
506
507 /**
508  * \brief Generate a new, globally unique UID parameter for a calendar etc. object
509  * \param buf uid to create ????
510  */
511 void generate_uuid(char *buf) {
512         static int seq = 0;
513
514         sprintf(buf, "%s-%lx-%x-%x",
515                 serv_info.serv_nodename,
516                 (long)time(NULL),
517                 getpid(),
518                 (seq++)
519         );
520 }
521
522
523 /**
524  * \brief Local replacement for controversial C library function that generates
525  * names for temporary files.  Included to shut up compiler warnings.
526  * \todo return a fd to the file instead of the name for security reasons
527  * \param name the created filename
528  * \param len the length of the filename
529  */
530 void CtdlMakeTempFileName(char *name, int len) {
531         int i = 0;
532
533         while (i++, i < 100) {
534                 snprintf(name, len, "/tmp/ctdl.%04x.%04x",
535                         getpid(),
536                         rand()
537                 );
538                 if (!access(name, F_OK)) {
539                         return;
540                 }
541         }
542 }
543
544
545
546 /*@}*/