fmt_date() use ISO 8601 date format
[citadel.git] / libcitadel / lib / tools.c
1 /*
2  * A basic toolset containing miscellaneous functions for string manipluation,
3  * encoding/decoding, and a bunch of other stuff.
4  *
5  * Copyright (c) 1987-2011 by the citadel.org team
6  *
7  * This program is open source software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20  */
21
22
23 #include <stdlib.h>
24 #include <unistd.h>
25 #include <stdio.h>
26 #include <signal.h>
27 #include <sys/types.h>
28 #include <ctype.h>
29 #include <string.h>
30 #include <sys/stat.h>
31 #include <errno.h>
32 #include <limits.h>
33
34 #if TIME_WITH_SYS_TIME
35 # include <sys/time.h>
36 # include <time.h>
37 #else
38 # if HAVE_SYS_TIME_H
39 #  include <sys/time.h>
40 # else
41 #  include <time.h>
42 # endif
43 #endif
44
45 #include "libcitadel.h"
46
47
48 #define TRUE  1
49 #define FALSE 0
50
51 typedef unsigned char byte;           /* Byte type */
52
53 /* Base64 encoding table */
54 const byte etable[256] = {
55         65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81,
56         82, 83, 84, 85, 86, 87, 88, 89, 90, 97, 98, 99, 100, 101, 102, 103,
57         104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
58         118, 119, 120, 121, 122, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 43,
59         47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
60         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
61         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
62         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
63         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
64         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
65         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
66         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
67         0, 0, 0, 0, 0, 0, 0, 0, 0
68 };
69
70 /* Base64 decoding table */
71 const byte dtable[256] = {
72         128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
73         128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
74         128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
75         128, 62, 128, 128, 128, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,
76         128, 128, 128, 0, 128, 128, 128, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
77         12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 128, 128, 128,
78         128, 128, 128, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
79         40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 128, 128, 128, 128,
80         128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
81         128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
82         128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
83         128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
84         128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
85         128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
86         128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
87         128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
88         128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
89         128, 128, 0
90 };
91
92 /*
93  * copy a string into a buffer of a known size. abort if we exceed the limits
94  *
95  * dest the targetbuffer
96  * src  the source string
97  * n    the size od dest
98  *
99  * returns the number of characters copied if dest is big enough, -n if not.
100  */
101 int safestrncpy(char *dest, const char *src, size_t n)
102 {
103         int i = 0;
104
105         if (dest == NULL || src == NULL) {
106                 fprintf(stderr, "safestrncpy: NULL argument\n");
107                 abort();
108         }
109
110         do {
111                 dest[i] = src[i];
112                 if (dest[i] == 0) return i;
113                 ++i;
114         } while (i<n);
115         dest[n - 1] = 0;
116         return -i;
117 }
118
119
120
121 /*
122  * num_tokens()  -  discover number of parameters/tokens in a string
123  */
124 int num_tokens(const char *source, char tok)
125 {
126         int count = 1;
127         const char *ptr = source;
128
129         if (source == NULL) {
130                 return (0);
131         }
132
133         while (*ptr != '\0') {
134                 if (*ptr++ == tok) {
135                         ++count;
136                 }
137         }
138         
139         return (count);
140 }
141
142 //extern void cit_backtrace(void);
143
144
145 /*
146  * extract_token() - a string tokenizer
147  * returns -1 if not found, or length of token.
148  */
149 long extract_token(char *dest, const char *source, int parmnum, char separator, int maxlen)
150 {
151         const char *s;                  //* source * /
152         int len = 0;                    //* running total length of extracted string * /
153         int current_token = 0;          //* token currently being processed * /
154
155         s = source;
156
157         if (dest == NULL) {
158                 return(-1);
159         }
160
161         //cit_backtrace();
162         //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
163         dest[0] = 0;
164
165         if (s == NULL) {
166                 return(-1);
167         }
168         
169         maxlen--;
170
171         while (*s) {
172                 if (*s == separator) {
173                         ++current_token;
174                 }
175                 if ( (current_token == parmnum) && 
176                      (*s != separator) && 
177                      (len < maxlen) ) {
178                         dest[len] = *s;
179                         ++len;
180                 }
181                 else if ((current_token > parmnum) || (len >= maxlen)) {
182                         break;
183                 }
184                 ++s;
185         }
186
187         dest[len] = '\0';
188         if (current_token < parmnum) {
189                 //lprintf (CTDL_DEBUG,"test <!: %s\n", dest);
190                 return(-1);
191         }
192         //lprintf (CTDL_DEBUG,"test <: %d; %s\n", len, dest);
193         return(len);
194 }
195 //*/
196
197
198 /*
199  * extract_token() - a string tokenizer
200  * /
201 long extract_token(char *dest, const char *source, int parmnum, char separator, int maxlen)
202 {
203         char *d;                // dest
204         const char *s;          // source
205         int count = 0;
206         int len = 0;
207
208         
209         //cit_backtrace();
210         //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
211         strcpy(dest, "");
212
213         //  Locate desired parameter 
214         s = source;
215         while (count < parmnum) {
216                 //  End of string, bail!
217                 if (!*s) {
218                         s = NULL;
219                         break;
220                 }
221                 if (*s == separator) {
222                         count++;
223                 }
224                 s++;
225         }
226         if (!s) {
227                 //lprintf (CTDL_DEBUG,"test <!: %s\n", dest);
228                 return -1;              // Parameter not found
229         }
230         
231         for (d = dest; *s && *s != separator && ++len<maxlen; s++, d++) {
232                 *d = *s;
233         }
234         *d = 0;
235         //lprintf (CTDL_DEBUG,"test <: %d; %s\n", len, dest);
236         return 0;
237 }
238 */
239
240
241 /*
242  * remove_token() - a tokenizer that kills, maims, and destroys
243  */
244 void remove_token(char *source, int parmnum, char separator)
245 {
246         char *d, *s;            /* dest, source */
247         int count = 0;
248
249         /* Find desired parameter */
250         d = source;
251         while (count < parmnum) {
252                 /* End of string, bail! */
253                 if (!*d) {
254                         d = NULL;
255                         break;
256                 }
257                 if (*d == separator) {
258                         count++;
259                 }
260                 d++;
261         }
262         if (!d) return;         /* Parameter not found */
263
264         /* Find next parameter */
265         s = d;
266         while (*s && *s != separator) {
267                 s++;
268         }
269
270         /* Hack and slash */
271         if (*s)
272                 strcpy(d, ++s);
273         else if (d == source)
274                 *d = 0;
275         else
276                 *--d = 0;
277         /*
278         while (*s) {
279                 *d++ = *s++;
280         }
281         *d = 0;
282         */
283 }
284
285
286 /*
287  * extract_int()  -  extract an int parm w/o supplying a buffer
288  */
289 int extract_int(const char *source, int parmnum)
290 {
291         char buf[32];
292         
293         if (extract_token(buf, source, parmnum, '|', sizeof buf) > 0)
294                 return(atoi(buf));
295         else
296                 return 0;
297 }
298
299 /*
300  * extract_long()  -  extract an long parm w/o supplying a buffer
301  */
302 long extract_long(const char *source, int parmnum)
303 {
304         char buf[32];
305         
306         if (extract_token(buf, source, parmnum, '|', sizeof buf) > 0)
307                 return(atol(buf));
308         else
309                 return 0;
310 }
311
312
313 /*
314  * extract_unsigned_long() - extract an unsigned long parm
315  */
316 unsigned long extract_unsigned_long(const char *source, int parmnum)
317 {
318         char buf[32];
319
320         if (extract_token(buf, source, parmnum, '|', sizeof buf) > 0)
321                 return strtoul(buf, NULL, 10);
322         else 
323                 return 0;
324 }
325
326
327 /*
328  * CtdlDecodeBase64() and CtdlEncodeBase64() are adaptations of code by John Walker.
329  */
330
331 size_t CtdlEncodeBase64(char *dest, const char *source, size_t sourcelen, int linebreaks)
332 {
333         int i, hiteof = FALSE;
334         int spos = 0;
335         int dpos = 0;
336         int thisline = 0;
337
338         while (!hiteof) {
339                 byte igroup[3], ogroup[4];
340                 int c, n;
341
342                 igroup[0] = igroup[1] = igroup[2] = 0;
343                 for (n = 0; n < 3; n++) {
344                         if (spos >= sourcelen) {
345                                 hiteof = TRUE;
346                                 break;
347                         }
348                         c = source[spos++];
349                         igroup[n] = (byte) c;
350                 }
351                 if (n > 0) {
352                         ogroup[0] = etable[igroup[0] >> 2];
353                         ogroup[1] =
354                             etable[((igroup[0] & 3) << 4) |
355                                    (igroup[1] >> 4)];
356                         ogroup[2] =
357                             etable[((igroup[1] & 0xF) << 2) |
358                                    (igroup[2] >> 6)];
359                         ogroup[3] = etable[igroup[2] & 0x3F];
360
361                         /*
362                          * Replace characters in output stream with "=" pad
363                          * characters if fewer than three characters were
364                          * read from the end of the input stream. 
365                          */
366
367                         if (n < 3) {
368                                 ogroup[3] = '=';
369                                 if (n < 2) {
370                                         ogroup[2] = '=';
371                                 }
372                         }
373                         for (i = 0; i < 4; i++) {
374                                 dest[dpos++] = ogroup[i];
375                                 dest[dpos] = 0;
376                         }
377                         thisline += 4;
378                         if ( (linebreaks) && (thisline > 70) ) {
379                                 dest[dpos++] = '\r';
380                                 dest[dpos++] = '\n';
381                                 dest[dpos] = 0;
382                                 thisline = 0;
383                         }
384                 }
385         }
386         if ( (linebreaks) && (thisline > 70) ) {
387                 dest[dpos++] = '\r';
388                 dest[dpos++] = '\n';
389                 dest[dpos] = 0;
390         }
391
392         return(dpos);
393 }
394
395
396
397 /* 
398  * Convert base64-encoded to binary.  Returns the length of the decoded data.
399  * It will stop after reading 'length' bytes.
400  */
401 int CtdlDecodeBase64(char *dest, const char *source, size_t length)
402 {
403     int i, c;
404     int dpos = 0;
405     int spos = 0;
406
407     while (TRUE) {
408         byte a[4], b[4], o[3];
409
410         for (i = 0; i < 4; i++) {
411             if (spos >= length) {
412                 return(dpos);
413             }
414             c = source[spos++];
415
416             if (c == 0) {
417                 if (i > 0) {
418                     return(dpos);
419                 }
420                 return(dpos);
421             }
422             if (dtable[c] & 0x80) {
423                 /* Ignoring errors: discard invalid character. */
424                 i--;
425                 continue;
426             }
427             a[i] = (byte) c;
428             b[i] = (byte) dtable[c];
429         }
430         o[0] = (b[0] << 2) | (b[1] >> 4);
431         o[1] = (b[1] << 4) | (b[2] >> 2);
432         o[2] = (b[2] << 6) | b[3];
433         i = a[2] == '=' ? 1 : (a[3] == '=' ? 2 : 3);
434         if (i>=1) dest[dpos++] = o[0];
435         if (i>=2) dest[dpos++] = o[1];
436         if (i>=3) dest[dpos++] = o[2];
437         dest[dpos] = 0;
438         if (i < 3) {
439             return(dpos);
440         }
441     }
442 }
443
444
445 /*
446  * if we send out non ascii subjects, we encode it this way.
447  */
448 char *rfc2047encode(const char *line, long length)
449 {
450         const char *AlreadyEncoded;
451         char *result;
452         long end;
453 #define UTF8_HEADER "=?UTF-8?B?"
454
455         /* check if we're already done */
456         AlreadyEncoded = strstr(line, "=?");
457         if ((AlreadyEncoded != NULL) &&
458             ((strstr(AlreadyEncoded, "?B?") != NULL)||
459              (strstr(AlreadyEncoded, "?Q?") != NULL)))
460         {
461                 return strdup(line);
462         }
463
464         result = (char*) malloc(sizeof(UTF8_HEADER) + 4 + length * 2);
465         strncpy (result, UTF8_HEADER, strlen (UTF8_HEADER));
466         CtdlEncodeBase64(result + strlen(UTF8_HEADER), line, length, 0);
467         end = strlen (result);
468         result[end]='?';
469         result[end+1]='=';
470         result[end+2]='\0';
471         return result;
472 }
473
474 /*
475  * removes double slashes from pathnames
476  * allows / disallows trailing slashes
477  */
478 void StripSlashes(char *Dir, int TrailingSlash)
479 {
480         char *a, *b;
481
482         a = b = Dir;
483
484         while (!IsEmptyStr(a)) {
485                 if (*a == '/') {
486                         while (*a == '/')
487                                 a++;
488                         *b = '/';
489                         b++;
490                 }
491                 else {
492                         *b = *a;
493                         b++; a++;
494                 }
495         }
496         if ((TrailingSlash) && (*(b - 1) != '/')){
497                 *b = '/';
498                 b++;
499         }
500         *b = '\0';
501
502 }
503
504 /*
505  * Strip leading and trailing spaces from a string
506  */
507 size_t striplt(char *buf) {
508         char *first_nonspace = NULL;
509         char *last_nonspace = NULL;
510         char *ptr;
511         size_t new_len = 0;
512
513         if ((buf == NULL) || (*buf == '\0')) {
514                 return 0;
515         }
516
517         for (ptr=buf; *ptr!=0; ++ptr) {
518                 if (!isspace(*ptr)) {
519                         if (!first_nonspace) {
520                                 first_nonspace = ptr;
521                         }
522                         last_nonspace = ptr;
523                 }
524         }
525
526         if ((!first_nonspace) || (!last_nonspace)) {
527                 buf[0] = 0;
528                 return 0;
529         }
530
531         new_len = last_nonspace - first_nonspace + 1;
532         memmove(buf, first_nonspace, new_len);
533         buf[new_len] = 0;
534         return new_len;
535 }
536
537
538 /**
539  * \brief check for the presence of a character within a string (returns count)
540  * \param st the string to examine
541  * \param ch the char to search
542  * \return the number of times ch appears in st
543  */
544 int haschar(const char *st, int ch)
545 {
546         const char *ptr;
547         int b;
548         b = 0;
549         ptr = st;
550         while (!IsEmptyStr(ptr))
551         {
552                 if (*ptr == ch)
553                         ++b;
554                 ptr ++;
555         }
556         return (b);
557 }
558
559
560
561
562
563 /*
564  * Format a date/time stamp for output 
565  * seconds is whether to print the seconds
566  */
567 void fmt_date(char *buf, size_t n, time_t thetime, int seconds) {
568         struct tm tm;
569         char *teh_format = NULL;
570
571         *buf = '\0';
572         localtime_r(&thetime, &tm);
573
574         if (seconds) {
575                 teh_format = "%F %R:%S";
576         }
577         else {
578                 teh_format = "%F %R";
579         }
580
581         strftime(buf, n, teh_format, &tm);
582 }
583
584
585
586 /*
587  * Determine whether the specified message number is contained within the
588  * specified sequence set.
589  */
590 int is_msg_in_sequence_set(const char *mset, long msgnum) {
591         int num_sets;
592         int s;
593         char setstr[128], lostr[128], histr[128];
594         long lo, hi;
595
596         num_sets = num_tokens(mset, ',');
597         for (s=0; s<num_sets; ++s) {
598                 extract_token(setstr, mset, s, ',', sizeof setstr);
599
600                 extract_token(lostr, setstr, 0, ':', sizeof lostr);
601                 if (num_tokens(setstr, ':') >= 2) {
602                         extract_token(histr, setstr, 1, ':', sizeof histr);
603                         if (!strcmp(histr, "*")) {
604                                 snprintf(histr, sizeof histr, "%ld", LONG_MAX);
605                         }
606                 } 
607                 else {
608                         strcpy(histr, lostr);
609                 }
610                 lo = atol(lostr);
611                 hi = atol(histr);
612
613                 if ((msgnum >= lo) && (msgnum <= hi)) return(1);
614         }
615
616         return(0);
617 }
618
619 /** 
620  * \brief Utility function to "readline" from memory
621  * \param start Location in memory from which we are reading.
622  * \param buf the buffer to place the string in.
623  * \param maxlen Size of string buffer
624  * \return Pointer to the source memory right after we stopped reading.
625  */
626 char *memreadline(char *start, char *buf, int maxlen)
627 {
628         char ch;
629         char *ptr;
630         int len = 0;            /**< tally our own length to avoid strlen() delays */
631
632         ptr = start;
633
634         while (1) {
635                 ch = *ptr++;
636                 if ((len + 1 < (maxlen)) && (ch != 13) && (ch != 10)) {
637                         buf[len++] = ch;
638                 }
639                 if ((ch == 10) || (ch == 0)) {
640                         buf[len] = 0;
641                         return ptr;
642                 }
643         }
644 }
645
646
647 /** 
648  * \brief Utility function to "readline" from memory
649  * \param start Location in memory from which we are reading.
650  * \param buf the buffer to place the string in.
651  * \param maxlen Size of string buffer
652  * \param retlen the length of the returned string
653  * \return Pointer to the source memory right after we stopped reading.
654  */
655 char *memreadlinelen(char *start, char *buf, int maxlen, int *retlen)
656 {
657         char ch;
658         char *ptr;
659         int len = 0;            /**< tally our own length to avoid strlen() delays */
660
661         ptr = start;
662
663         while (1) {
664                 ch = *ptr++;
665                 if ((len + 1 < (maxlen)) && (ch != 13) && (ch != 10)) {
666                         buf[len++] = ch;
667                 }
668                 if ((ch == 10) || (ch == 0)) {
669                         buf[len] = 0;
670                         *retlen = len;
671                         return ptr;
672                 }
673         }
674 }
675
676
677 /** 
678  * \brief Utility function to "readline" from memory
679  * \param start Location in memory from which we are reading.
680  * \param buf the buffer to place the string in.
681  * \param maxlen Size of string buffer
682  * \return Pointer to the source memory right after we stopped reading.
683  */
684 const char *cmemreadline(const char *start, char *buf, int maxlen)
685 {
686         char ch;
687         const char *ptr;
688         int len = 0;            /**< tally our own length to avoid strlen() delays */
689
690         ptr = start;
691
692         while (1) {
693                 ch = *ptr++;
694                 if ((len + 1 < (maxlen)) && (ch != 13) && (ch != 10)) {
695                         buf[len++] = ch;
696                 }
697                 if ((ch == 10) || (ch == 0)) {
698                         buf[len] = 0;
699                         return ptr;
700                 }
701         }
702 }
703
704
705 /** 
706  * \brief Utility function to "readline" from memory
707  * \param start Location in memory from which we are reading.
708  * \param buf the buffer to place the string in.
709  * \param maxlen Size of string buffer
710  * \param retlen the length of the returned string
711  * \return Pointer to the source memory right after we stopped reading.
712  */
713 const char *cmemreadlinelen(const char *start, char *buf, int maxlen, int *retlen)
714 {
715         char ch;
716         const char *ptr;
717         int len = 0;            /**< tally our own length to avoid strlen() delays */
718
719         ptr = start;
720
721         while (1) {
722                 ch = *ptr++;
723                 if ((len + 1 < (maxlen)) && (ch != 13) && (ch != 10)) {
724                         buf[len++] = ch;
725                 }
726                 if ((ch == 10) || (ch == 0)) {
727                         buf[len] = 0;
728                         *retlen = len;
729                         return ptr;
730                 }
731         }
732 }
733
734
735
736
737 /*
738  * Strip a boundarized substring out of a string (for example, remove
739  * parentheses and anything inside them).
740  */
741 int stripout(char *str, char leftboundary, char rightboundary) {
742         int a;
743         int lb = (-1);
744         int rb = (-1);
745
746         for (a = 0; a < strlen(str); ++a) {
747                 if (str[a] == leftboundary) lb = a;
748                 if (str[a] == rightboundary) rb = a;
749         }
750
751         if ( (lb > 0) && (rb > lb) ) {
752                 strcpy(&str[lb - 1], &str[rb + 1]);
753                 return 1;
754         }
755
756         else if ( (lb == 0) && (rb > lb) ) {
757                 strcpy(str, &str[rb + 1]);
758                 return 1;
759         }
760         return 0;
761 }
762
763
764 /*
765  * Reduce a string down to a boundarized substring (for example, remove
766  * parentheses and anything outside them).
767  */
768 long stripallbut(char *str, char leftboundary, char rightboundary) {
769         long len = 0;
770
771         char *lb = NULL;
772         char *rb = NULL;
773
774         lb = strrchr(str, leftboundary);
775         if (lb != NULL) {
776                 ++lb;
777                 rb = strchr(str, rightboundary);
778                 if ((rb != NULL) && (rb >= lb))  {
779                         *rb = 0;
780                         fflush(stderr);
781                         len = (long)rb - (long)lb;
782                         memmove(str, lb, len);
783                         str[len] = 0;
784                         return(len);
785                 }
786         }
787
788         return (long)strlen(str);
789 }
790
791
792 char *myfgets(char *s, int size, FILE *stream) {
793         char *ret = fgets(s, size, stream);
794         char *nl;
795
796         if (ret != NULL) {
797                 nl = strchr(s, '\n');
798
799                 if (nl != NULL)
800                         *nl = 0;
801         }
802
803         return ret;
804 }
805
806 /** 
807  * \brief Escape a string for feeding out as a URL.
808  * \param outbuf the output buffer
809  * \param oblen the size of outbuf to sanitize
810  * \param strbuf the input buffer
811  */
812 void urlesc(char *outbuf, size_t oblen, char *strbuf)
813 {
814         int a, b, c, len, eclen, olen;
815         char *ec = " +#&;`'|*?-~<>^()[]{}/$\"\\";
816
817         *outbuf = '\0';
818         len = strlen(strbuf);
819         eclen = strlen(ec);
820         olen = 0;
821         for (a = 0; a < len; ++a) {
822                 c = 0;
823                 for (b = 0; b < eclen; ++b) {
824                         if (strbuf[a] == ec[b])
825                                 c = 1;
826                 }
827                 if (c == 1) {
828                         snprintf(&outbuf[olen], oblen - olen, "%%%02x", strbuf[a]);
829                         olen += 3;
830                 }
831                 else 
832                         outbuf[olen ++] = strbuf[a];
833         }
834         outbuf[olen] = '\0';
835 }
836
837
838
839 /*
840  * In our world, we want strcpy() to be able to work with overlapping strings.
841  */
842 #ifdef strcpy
843 #undef strcpy
844 #endif
845 char *strcpy(char *dest, const char *src) {
846         memmove(dest, src, (strlen(src) + 1) );
847         return(dest);
848 }
849
850
851 /*
852  * Generate a new, globally unique UID parameter for a calendar etc. object
853  */
854 void generate_uuid(char *buf) {
855         static int seq = (-1);
856         static int no_kernel_uuid = 0;
857
858         /* If we are running on Linux then we have a kernelspace uuid generator available */
859
860         if (no_kernel_uuid == 0) {
861                 FILE *fp;
862                 fp = fopen("/proc/sys/kernel/random/uuid", "rb");
863                 if (fp) {
864                         int rv;
865                         rv = fread(buf, 36, 1, fp);
866                         fclose(fp);
867                         if (rv == 1) {
868                                 buf[36] = 0;
869                                 return;
870                         }
871                 }
872         }
873
874         /* If the kernel didn't provide us with a uuid, we generate a pseudo-random one */
875
876         no_kernel_uuid = 1;
877
878         if (seq == (-1)) {
879                 seq = (int)rand();
880         }
881         ++seq;
882         seq = (seq % 0x0FFF) ;
883
884         sprintf(buf, "%08lx-%04lx-4%03x-a%03x-%012lx",
885                 (long)time(NULL),
886                 (long)getpid(),
887                 seq,
888                 seq,
889                 (long)rand()
890         );
891 }
892
893 /*
894  * bmstrcasestr() -- case-insensitive substring search
895  *
896  * This uses the Boyer-Moore search algorithm and is therefore quite fast.
897  * The code is roughly based on the strstr() replacement from 'tin' written
898  * by Urs Jannsen.
899  */
900 inline static char *_bmstrcasestr_len(char *text, size_t textlen, const char *pattern, size_t patlen) {
901
902         register unsigned char *p, *t;
903         register int i, j, *delta;
904         register size_t p1;
905         int deltaspace[256];
906
907         if (!text) return(NULL);
908         if (!pattern) return(NULL);
909
910         /* algorithm fails if pattern is empty */
911         if ((p1 = patlen) == 0)
912                 return (text);
913
914         /* code below fails (whenever i is unsigned) if pattern too long */
915         if (p1 > textlen)
916                 return (NULL);
917
918         /* set up deltas */
919         delta = deltaspace;
920         for (i = 0; i <= 255; i++)
921                 delta[i] = p1;
922         for (p = (unsigned char *) pattern, i = p1; --i > 0;)
923                 delta[tolower(*p++)] = i;
924
925         /*
926          * From now on, we want patlen - 1.
927          * In the loop below, p points to the end of the pattern,
928          * t points to the end of the text to be tested against the
929          * pattern, and i counts the amount of text remaining, not
930          * including the part to be tested.
931          */
932         p1--;
933         p = (unsigned char *) pattern + p1;
934         t = (unsigned char *) text + p1;
935         i = textlen - patlen;
936         while(1) {
937                 if (tolower(p[0]) == tolower(t[0])) {
938                         if (strncasecmp ((const char *)(p - p1), (const char *)(t - p1), p1) == 0) {
939                                 return ((char *)t - p1);
940                         }
941                 }
942                 j = delta[tolower(t[0])];
943                 if (i < j)
944                         break;
945                 i -= j;
946                 t += j;
947         }
948         return (NULL);
949 }
950
951 /*
952  * bmstrcasestr() -- case-insensitive substring search
953  *
954  * This uses the Boyer-Moore search algorithm and is therefore quite fast.
955  * The code is roughly based on the strstr() replacement from 'tin' written
956  * by Urs Jannsen.
957  */
958 char *bmstrcasestr(char *text, const char *pattern) {
959         size_t textlen;
960         size_t patlen;
961
962         if (!text) return(NULL);
963         if (!pattern) return(NULL);
964
965         textlen = strlen (text);
966         patlen = strlen (pattern);
967
968         return _bmstrcasestr_len(text, textlen, pattern, patlen);
969 }
970
971 char *bmstrcasestr_len(char *text, size_t textlen, const char *pattern, size_t patlen) {
972         return _bmstrcasestr_len(text, textlen, pattern, patlen);
973 }
974
975
976
977
978 /*
979  * bmstrcasestr() -- case-insensitive substring search
980  *
981  * This uses the Boyer-Moore search algorithm and is therefore quite fast.
982  * The code is roughly based on the strstr() replacement from 'tin' written
983  * by Urs Jannsen.
984  */
985 inline static const char *_cbmstrcasestr_len(const char *text, size_t textlen, const char *pattern, size_t patlen) {
986
987         register unsigned char *p, *t;
988         register int i, j, *delta;
989         register size_t p1;
990         int deltaspace[256];
991
992         if (!text) return(NULL);
993         if (!pattern) return(NULL);
994
995         /* algorithm fails if pattern is empty */
996         if ((p1 = patlen) == 0)
997                 return (text);
998
999         /* code below fails (whenever i is unsigned) if pattern too long */
1000         if (p1 > textlen)
1001                 return (NULL);
1002
1003         /* set up deltas */
1004         delta = deltaspace;
1005         for (i = 0; i <= 255; i++)
1006                 delta[i] = p1;
1007         for (p = (unsigned char *) pattern, i = p1; --i > 0;)
1008                 delta[tolower(*p++)] = i;
1009
1010         /*
1011          * From now on, we want patlen - 1.
1012          * In the loop below, p points to the end of the pattern,
1013          * t points to the end of the text to be tested against the
1014          * pattern, and i counts the amount of text remaining, not
1015          * including the part to be tested.
1016          */
1017         p1--;
1018         p = (unsigned char *) pattern + p1;
1019         t = (unsigned char *) text + p1;
1020         i = textlen - patlen;
1021         while(1) {
1022                 if (tolower(p[0]) == tolower(t[0])) {
1023                         if (strncasecmp ((const char *)(p - p1), (const char *)(t - p1), p1) == 0) {
1024                                 return ((char *)t - p1);
1025                         }
1026                 }
1027                 j = delta[tolower(t[0])];
1028                 if (i < j)
1029                         break;
1030                 i -= j;
1031                 t += j;
1032         }
1033         return (NULL);
1034 }
1035
1036 /*
1037  * bmstrcasestr() -- case-insensitive substring search
1038  *
1039  * This uses the Boyer-Moore search algorithm and is therefore quite fast.
1040  * The code is roughly based on the strstr() replacement from 'tin' written
1041  * by Urs Jannsen.
1042  */
1043 const char *cbmstrcasestr(const char *text, const char *pattern) {
1044         size_t textlen;
1045         size_t patlen;
1046
1047         if (!text) return(NULL);
1048         if (!pattern) return(NULL);
1049
1050         textlen = strlen (text);
1051         patlen = strlen (pattern);
1052
1053         return _cbmstrcasestr_len(text, textlen, pattern, patlen);
1054 }
1055
1056 const char *cbmstrcasestr_len(const char *text, size_t textlen, const char *pattern, size_t patlen) {
1057         return _cbmstrcasestr_len(text, textlen, pattern, patlen);
1058 }
1059
1060 /*
1061  * Local replacement for controversial C library function that generates
1062  * names for temporary files.  Included to shut up compiler warnings.
1063  */
1064 void CtdlMakeTempFileName(char *name, int len) {
1065         int i = 0;
1066
1067         while (i++, i < 100) {
1068                 snprintf(name, len, "/tmp/ctdl.%04lx.%04x",
1069                         (long)getpid(),
1070                         rand()
1071                 );
1072                 if (!access(name, F_OK)) {
1073                         return;
1074                 }
1075         }
1076 }
1077
1078
1079
1080 /*
1081  * Determine whether the specified message number is contained within the specified set.
1082  * Returns nonzero if the specified message number is in the specified message set string.
1083  */
1084 int is_msg_in_mset(const char *mset, long msgnum) {
1085         int num_sets;
1086         int s;
1087         char setstr[SIZ], lostr[SIZ], histr[SIZ];       /* was 1024 */
1088         long lo, hi;
1089
1090         /*
1091          * Now set it for all specified messages.
1092          */
1093         num_sets = num_tokens(mset, ',');
1094         for (s=0; s<num_sets; ++s) {
1095                 extract_token(setstr, mset, s, ',', sizeof setstr);
1096
1097                 extract_token(lostr, setstr, 0, ':', sizeof lostr);
1098                 if (num_tokens(setstr, ':') >= 2) {
1099                         extract_token(histr, setstr, 1, ':', sizeof histr);
1100                         if (!strcmp(histr, "*")) {
1101                                 snprintf(histr, sizeof histr, "%ld", LONG_MAX);
1102                         }
1103                 }
1104                 else {
1105                         strcpy(histr, lostr);
1106                 }
1107                 lo = atol(lostr);
1108                 hi = atol(histr);
1109
1110                 if ((msgnum >= lo) && (msgnum <= hi)) return(1);
1111         }
1112
1113         return(0);
1114 }
1115
1116
1117 /*
1118  * searches for a pattern within a search string
1119  * returns position in string
1120  */
1121 int pattern2(char *search, char *patn)
1122 {
1123         int a;
1124         int len, plen;
1125         len = strlen (search);
1126         plen = strlen (patn);
1127         for (a = 0; a < len; ++a) {
1128                 if (!strncasecmp(&search[a], patn, plen))
1129                         return (a);
1130         }
1131         return (-1);
1132 }
1133
1134
1135 /*
1136  * Strip leading and trailing spaces from a string; with premeasured and adjusted length.
1137  * buf - the string to modify
1138  * len - length of the string. 
1139  */
1140 void stripltlen(char *buf, int *len)
1141 {
1142         int delta = 0;
1143         if (*len == 0) return;
1144         while ((*len > delta) && (isspace(buf[delta]))){
1145                 delta ++;
1146         }
1147         memmove (buf, &buf[delta], *len - delta + 1);
1148         (*len) -=delta;
1149
1150         if (*len == 0) return;
1151         while (isspace(buf[(*len) - 1])){
1152                 buf[--(*len)] = '\0';
1153         }
1154 }
1155
1156
1157 /*
1158  * Convert all whitespace characters in a supplied string to underscores
1159  */
1160 void convert_spaces_to_underscores(char *str)
1161 {
1162         int len;
1163         int i;
1164
1165         if (!str) return;
1166
1167         len = strlen(str);
1168         for (i=0; i<len; ++i) {
1169                 if (isspace(str[i])) {
1170                         str[i] = '_';
1171                 }
1172         }
1173 }
1174
1175
1176 /*
1177  * check whether the provided string needs to be qp encoded or not
1178  */
1179 int CheckEncode(const char *pch, long len, const char *pche)
1180 {
1181         if (pche == NULL)
1182                 pche = pch + len;
1183         while (pch < pche) {
1184                 if (((unsigned char) *pch < 32) || 
1185                     ((unsigned char) *pch > 126)) {
1186                         return 1;
1187                 }
1188                 pch++;
1189         }
1190         return 0;
1191 }
1192