* create format strings different for solaris
[citadel.git] / citadel / tools.c
1 /*
2  * $Id$
3  *
4  * Utility functions that are used by both the client and server.
5  *
6  */
7
8 #include "sysdep.h"
9 #include <stdlib.h>
10 #include <unistd.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <ctype.h>
14 #include <string.h>
15 #include <stdarg.h>
16
17 #if TIME_WITH_SYS_TIME
18 # include <sys/time.h>
19 # include <time.h>
20 #else
21 # if HAVE_SYS_TIME_H
22 #  include <sys/time.h>
23 # else
24 #  include <time.h>
25 # endif
26 #endif
27
28 #include "tools.h"
29 #include "citadel.h"
30
31 #define TRUE  1
32 #define FALSE 0
33
34 typedef unsigned char byte;           /* Byte type */
35 static byte dtable[256];              /* base64 encode / decode table */
36
37 /* Month strings for date conversions */
38 char *ascmonths[12] = {
39         "Jan", "Feb", "Mar", "Apr", "May", "Jun",
40         "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
41 };
42
43 char *safestrncpy(char *dest, const char *src, size_t n)
44 {
45         if (dest == NULL || src == NULL) {
46                 fprintf(stderr, "safestrncpy: NULL argument\n");
47                 abort();
48         }
49         strncpy(dest, src, n);
50         dest[n - 1] = 0;
51         return dest;
52 }
53
54
55
56 #ifndef HAVE_STRNCASECMP
57 int strncasecmp(char *lstr, char *rstr, int len)
58 {
59         int pos = 0;
60         char lc,rc;
61         while (pos<len) {
62                 lc=tolower(lstr[pos]);
63                 rc=tolower(rstr[pos]);
64                 if ((lc==0)&&(rc==0)) return(0);
65                 if (lc<rc) return(-1);
66                 if (lc>rc) return(1);
67                 pos=pos+1;
68         }
69         return(0);
70 }
71 #endif
72
73
74
75 /*
76  * num_tokens()  -  discover number of parameters/tokens in a string
77  */
78 int num_tokens(const char *source, char tok) {
79         int a;
80         int count = 1;
81
82         if (source == NULL) return(0);
83         for (a=0; a<strlen(source); ++a) {
84                 if (source[a]==tok) ++count;
85         }
86         return(count);
87 }
88
89
90 /*
91  * extract_token() - a string tokenizer
92  */
93 void extract_token(char *dest, const char *source, int parmnum, char separator, int maxlen)
94 {
95         char *d;                /* dest */
96         const char *s;          /* source */
97         int count = 0;
98         int len = 0;
99
100         strcpy(dest, "");
101
102         /* Locate desired parameter */
103         s = source;
104         while (count < parmnum) {
105                 /* End of string, bail! */
106                 if (!*s) {
107                         s = NULL;
108                         break;
109                 }
110                 if (*s == separator) {
111                         count++;
112                 }
113                 s++;
114         }
115         if (!s) return;         /* Parameter not found */
116
117         for (d = dest; *s && *s != separator && ++len<maxlen; s++, d++) {
118                 *d = *s;
119         }
120         *d = 0;
121 }
122
123
124 /*
125  * remove_token() - a tokenizer that kills, maims, and destroys
126  */
127 void remove_token(char *source, int parmnum, char separator)
128 {
129         char *d, *s;            /* dest, source */
130         int count = 0;
131
132         /* Find desired parameter */
133         d = source;
134         while (count < parmnum) {
135                 /* End of string, bail! */
136                 if (!*d) {
137                         d = NULL;
138                         break;
139                 }
140                 if (*d == separator) {
141                         count++;
142                 }
143                 d++;
144         }
145         if (!d) return;         /* Parameter not found */
146
147         /* Find next parameter */
148         s = d;
149         while (*s && *s != separator) {
150                 s++;
151         }
152
153         /* Hack and slash */
154         if (*s)
155                 strcpy(d, ++s);
156         else if (d == source)
157                 *d = 0;
158         else
159                 *--d = 0;
160         /*
161         while (*s) {
162                 *d++ = *s++;
163         }
164         *d = 0;
165         */
166 }
167
168
169 /*
170  * extract_int()  -  extract an int parm w/o supplying a buffer
171  */
172 int extract_int(const char *source, int parmnum)
173 {
174         char buf[32];
175         
176         extract_token(buf, source, parmnum, '|', sizeof buf);
177         return(atoi(buf));
178 }
179
180 /*
181  * extract_long()  -  extract an long parm w/o supplying a buffer
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  * extract_unsigned_long() - extract an unsigned long parm
194  */
195 unsigned long extract_unsigned_long(const char *source, int parmnum)
196 {
197         char buf[32];
198
199         extract_token(buf, source, parmnum, '|', sizeof buf);
200         return strtoul(buf, NULL, 10);
201 }
202
203
204 /*
205  * CtdlDecodeBase64() and CtdlEncodeBase64() are adaptations of code by
206  * John Walker, found in full in the file "base64.c" included with this
207  * distribution.  We are moving in the direction of eventually discarding
208  * the separate executables, and using the ones in our code exclusively.
209  */
210
211 void CtdlEncodeBase64(char *dest, const char *source, size_t sourcelen)
212 {
213     int i, hiteof = FALSE;
214     int spos = 0;
215     int dpos = 0;
216
217     /*  Fill dtable with character encodings.  */
218
219     for (i = 0; i < 26; i++) {
220         dtable[i] = 'A' + i;
221         dtable[26 + i] = 'a' + i;
222     }
223     for (i = 0; i < 10; i++) {
224         dtable[52 + i] = '0' + i;
225     }
226     dtable[62] = '+';
227     dtable[63] = '/';
228
229     while (!hiteof) {
230         byte igroup[3], ogroup[4];
231         int c, n;
232
233         igroup[0] = igroup[1] = igroup[2] = 0;
234         for (n = 0; n < 3; n++) {
235             if (spos >= sourcelen) {
236                 hiteof = TRUE;
237                 break;
238             }
239             c = source[spos++];
240             igroup[n] = (byte) c;
241         }
242         if (n > 0) {
243             ogroup[0] = dtable[igroup[0] >> 2];
244             ogroup[1] = dtable[((igroup[0] & 3) << 4) | (igroup[1] >> 4)];
245             ogroup[2] = dtable[((igroup[1] & 0xF) << 2) | (igroup[2] >> 6)];
246             ogroup[3] = dtable[igroup[2] & 0x3F];
247
248             /* Replace characters in output stream with "=" pad
249                characters if fewer than three characters were
250                read from the end of the input stream. */
251
252             if (n < 3) {
253                 ogroup[3] = '=';
254                 if (n < 2) {
255                     ogroup[2] = '=';
256                 }
257             }
258             for (i = 0; i < 4; i++) {
259                 dest[dpos++] = ogroup[i];
260                 dest[dpos] = 0;
261             }
262         }
263     }
264 }
265
266
267 /* 
268  * Convert base64-encoded to binary.  Returns the length of the decoded data.
269  * It will stop after reading 'length' bytes.
270  */
271 int CtdlDecodeBase64(char *dest, const char *source, size_t length)
272 {
273     int i, c;
274     int dpos = 0;
275     int spos = 0;
276
277     for (i = 0; i < 255; i++) {
278         dtable[i] = 0x80;
279     }
280     for (i = 'A'; i <= 'Z'; i++) {
281         dtable[i] = 0 + (i - 'A');
282     }
283     for (i = 'a'; i <= 'z'; i++) {
284         dtable[i] = 26 + (i - 'a');
285     }
286     for (i = '0'; i <= '9'; i++) {
287         dtable[i] = 52 + (i - '0');
288     }
289     dtable['+'] = 62;
290     dtable['/'] = 63;
291     dtable['='] = 0;
292
293     /*CONSTANTCONDITION*/
294     while (TRUE) {
295         byte a[4], b[4], o[3];
296
297         for (i = 0; i < 4; i++) {
298             if (spos >= length) {
299                 return(dpos);
300             }
301             c = source[spos++];
302
303             if (c == 0) {
304                 if (i > 0) {
305                     return(dpos);
306                 }
307                 return(dpos);
308             }
309             if (dtable[c] & 0x80) {
310                 /* Ignoring errors: discard invalid character. */
311                 i--;
312                 continue;
313             }
314             a[i] = (byte) c;
315             b[i] = (byte) dtable[c];
316         }
317         o[0] = (b[0] << 2) | (b[1] >> 4);
318         o[1] = (b[1] << 4) | (b[2] >> 2);
319         o[2] = (b[2] << 6) | b[3];
320         i = a[2] == '=' ? 1 : (a[3] == '=' ? 2 : 3);
321         if (i>=1) dest[dpos++] = o[0];
322         if (i>=2) dest[dpos++] = o[1];
323         if (i>=3) dest[dpos++] = o[2];
324         dest[dpos] = 0;
325         if (i < 3) {
326             return(dpos);
327         }
328     }
329 }
330
331 /*
332  * if we send out non ascii subjects, we encode it this way.
333  */
334 char *rfc2047encode(char *line, long length)
335 {
336         char *AlreadyEncoded;
337         char *result;
338         long end;
339 #define UTF8_HEADER "=?UTF-8?B?"
340
341         /* check if we're already done */
342         AlreadyEncoded = strstr(line, "=?");
343         if ((AlreadyEncoded != NULL) &&
344             ((strstr(AlreadyEncoded, "?B?") != NULL)||
345              (strstr(AlreadyEncoded, "?Q?") != NULL)))
346         {
347                 return strdup(line);
348         }
349
350         result = (char*) malloc(strlen(UTF8_HEADER) + 4 + length * 2);
351         strncpy (result, UTF8_HEADER, strlen (UTF8_HEADER));
352         CtdlEncodeBase64(result + strlen(UTF8_HEADER), line, length);
353         end = strlen (result);
354         result[end]='?';
355         result[end+1]='=';
356         result[end+2]='\0';
357         return result;
358 }
359
360
361 /*
362  * Strip leading and trailing spaces from a string
363  */
364 void striplt(char *buf)
365 {
366         if (strlen(buf) == 0) return;
367         while ((strlen(buf) > 0) && (isspace(buf[0])))
368                 strcpy(buf, &buf[1]);
369         if (strlen(buf) == 0) return;
370         while ((strlen(buf) > 0) && (isspace(buf[strlen(buf) - 1])))
371                 buf[strlen(buf) - 1] = 0;
372 }
373
374
375
376
377
378 /* 
379  * Return the number of occurances of character ch in string st
380  */ 
381 int haschar(const char *st, int ch)
382 {
383         int a, b;
384         b = 0;
385         for (a = 0; a < strlen(st); ++a)
386                 if (st[a] == ch)
387                         ++b;
388         return (b);
389 }
390
391
392
393
394
395 /*
396  * Format a date/time stamp for output 
397  * seconds is whether to print the seconds
398  */
399 void fmt_date(char *buf, size_t n, time_t thetime, int seconds) {
400         struct tm tm;
401         int hour;
402
403         strcpy(buf, "");
404         localtime_r(&thetime, &tm);
405
406         hour = tm.tm_hour;
407         if (hour == 0)  hour = 12;
408         else if (hour > 12) hour = hour - 12;
409
410         if (seconds) {
411                 snprintf(buf, n, "%s %d %4d %d:%02d:%02d%s",
412                         ascmonths[tm.tm_mon],
413                         tm.tm_mday,
414                         tm.tm_year + 1900,
415                         hour,
416                         tm.tm_min,
417                         tm.tm_sec,
418                         ( (tm.tm_hour >= 12) ? "pm" : "am" )
419                 );
420         } else {
421                 snprintf(buf, n, "%s %d %4d %d:%02d%s",
422                         ascmonths[tm.tm_mon],
423                         tm.tm_mday,
424                         tm.tm_year + 1900,
425                         hour,
426                         tm.tm_min,
427                         ( (tm.tm_hour >= 12) ? "pm" : "am" )
428                 );
429         }
430 }
431
432
433
434 /*
435  * Determine whether the specified message number is contained within the
436  * specified sequence set.
437  */
438 int is_msg_in_sequence_set(char *mset, long msgnum) {
439         int num_sets;
440         int s;
441         char setstr[128], lostr[128], histr[128];
442         long lo, hi;
443
444         num_sets = num_tokens(mset, ',');
445         for (s=0; s<num_sets; ++s) {
446                 extract_token(setstr, mset, s, ',', sizeof setstr);
447
448                 extract_token(lostr, setstr, 0, ':', sizeof lostr);
449                 if (num_tokens(setstr, ':') >= 2) {
450                         extract_token(histr, setstr, 1, ':', sizeof histr);
451                         if (!strcmp(histr, "*")) {
452                                 snprintf(histr, sizeof histr, "%ld", LONG_MAX);
453                         }
454                 } 
455                 else {
456                         strcpy(histr, lostr);
457                 }
458                 lo = atol(lostr);
459                 hi = atol(histr);
460
461                 if ((msgnum >= lo) && (msgnum <= hi)) return(1);
462         }
463
464         return(0);
465 }
466
467
468 /*
469  * Utility function to "readline" from memory
470  * (returns new pointer)
471  */
472 char *memreadline(char *start, char *buf, int maxlen)
473 {
474         char ch;
475         char *ptr;
476         int len = 0;    /* tally our own length to avoid strlen() delays */
477
478         ptr = start;
479         memset(buf, 0, maxlen);
480
481         while (1) {
482                 ch = *ptr++;
483                 if ( (len < (maxlen - 1)) && (ch != 13) && (ch != 10) ) {
484                         buf[strlen(buf) + 1] = 0;
485                         buf[strlen(buf)] = ch;
486                         ++len;
487                 }
488                 if ((ch == 10) || (ch == 0)) {
489                         return ptr;
490                 }
491         }
492 }
493
494
495 /*
496  * Strip a boundarized substring out of a string (for example, remove
497  * parentheses and anything inside them).
498  */
499 void stripout(char *str, char leftboundary, char rightboundary) {
500         int a;
501         int lb = (-1);
502         int rb = (-1);
503
504         for (a = 0; a < strlen(str); ++a) {
505                 if (str[a] == leftboundary) lb = a;
506                 if (str[a] == rightboundary) rb = a;
507         }
508
509         if ( (lb > 0) && (rb > lb) ) {
510                 strcpy(&str[lb - 1], &str[rb + 1]);
511         }
512
513         else if ( (lb == 0) && (rb > lb) ) {
514                 strcpy(str, &str[rb + 1]);
515         }
516
517 }
518
519
520 /*
521  * Reduce a string down to a boundarized substring (for example, remove
522  * parentheses and anything outside them).
523  */
524 void stripallbut(char *str, char leftboundary, char rightboundary) {
525         int a;
526
527         for (a = 0; a < strlen(str); ++ a) {
528                 if (str[a] == leftboundary) strcpy(str, &str[a+1]);
529         }
530
531         for (a = 0; a < strlen(str); ++ a) {
532                 if (str[a] == rightboundary) str[a] = 0;
533         }
534
535 }
536
537 char *myfgets(char *s, int size, FILE *stream) {
538         char *ret = fgets(s, size, stream);
539         char *nl;
540
541         if (ret != NULL) {
542                 nl = strchr(s, '\n');
543
544                 if (nl != NULL)
545                         *nl = 0;
546         }
547
548         return ret;
549 }
550
551 /*
552  * Escape a string for feeding out as a URL.
553  * Output buffer must be big enough to handle escape expansion!
554  */
555 void urlesc(char *outbuf, char *strbuf)
556 {
557         int a, b, c;
558         char *ec = " #&;`'|*?-~<>^()[]{}$\\";
559
560         strcpy(outbuf, "");
561
562         for (a = 0; a < (int)strlen(strbuf); ++a) {
563                 c = 0;
564                 for (b = 0; b < strlen(ec); ++b) {
565                         if (strbuf[a] == ec[b])
566                                 c = 1;
567                 }
568                 b = strlen(outbuf);
569                 if (c == 1)
570                         sprintf(&outbuf[b], "%%%02x", strbuf[a]);
571                 else
572                         sprintf(&outbuf[b], "%c", strbuf[a]);
573         }
574 }
575
576
577
578 /*
579  * In our world, we want strcpy() to be able to work with overlapping strings.
580  */
581 #ifdef strcpy
582 #undef strcpy
583 #endif
584 char *strcpy(char *dest, const char *src) {
585         memmove(dest, src, (strlen(src) + 1) );
586         return(dest);
587 }
588
589
590 /*
591  * Generate a new, globally unique UID parameter for a calendar etc. object
592  */
593 void generate_uuid(char *buf) {
594         static int seq = 0;
595
596         sprintf(buf, "%lx-"F_XPID_T"-%x",
597                 time(NULL),
598                 getpid(),
599                 (seq++)
600         );
601 }
602
603 /*
604  * bmstrcasestr() -- case-insensitive substring search
605  *
606  * This uses the Boyer-Moore search algorithm and is therefore quite fast.
607  * The code is roughly based on the strstr() replacement from 'tin' written
608  * by Urs Jannsen.
609  */
610 char *bmstrcasestr(char *text, char *pattern) {
611
612         register unsigned char *p, *t;
613         register int i, j, *delta;
614         register size_t p1;
615         int deltaspace[256];
616         size_t textlen;
617         size_t patlen;
618
619         textlen = strlen (text);
620         patlen = strlen (pattern);
621
622         /* algorithm fails if pattern is empty */
623         if ((p1 = patlen) == 0)
624                 return (text);
625
626         /* code below fails (whenever i is unsigned) if pattern too long */
627         if (p1 > textlen)
628                 return (NULL);
629
630         /* set up deltas */
631         delta = deltaspace;
632         for (i = 0; i <= 255; i++)
633                 delta[i] = p1;
634         for (p = (unsigned char *) pattern, i = p1; --i > 0;)
635                 delta[tolower(*p++)] = i;
636
637         /*
638          * From now on, we want patlen - 1.
639          * In the loop below, p points to the end of the pattern,
640          * t points to the end of the text to be tested against the
641          * pattern, and i counts the amount of text remaining, not
642          * including the part to be tested.
643          */
644         p1--;
645         p = (unsigned char *) pattern + p1;
646         t = (unsigned char *) text + p1;
647         i = textlen - patlen;
648         while(1) {
649                 if (tolower(p[0]) == tolower(t[0])) {
650                         if (strncasecmp ((const char *)(p - p1), (const char *)(t - p1), p1) == 0) {
651                                 return ((char *)t - p1);
652                         }
653                 }
654                 j = delta[tolower(t[0])];
655                 if (i < j)
656                         break;
657                 i -= j;
658                 t += j;
659         }
660         return (NULL);
661 }
662
663
664
665 /*
666  * Local replacement for controversial C library function that generates
667  * names for temporary files.  Included to shut up compiler warnings.
668  */
669 void CtdlMakeTempFileName(char *name, int len) {
670         int i = 0;
671
672         while (i++, i < 100) {
673                 snprintf(name, len, "/tmp/ctdl."F_XPID_T".%04x",
674                         getpid(),
675                         rand()
676                 );
677                 if (!access(name, F_OK)) {
678                         return;
679                 }
680         }
681 }