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