2eafddaaaa4e602d865c1052581f23eb299a0f61
[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         int hour;
570
571         /* Month strings for date conversions ... this needs to be localized eventually */
572         char *fmt_date_months[12] = {
573                 "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
574         };
575
576         *buf = '\0';
577         localtime_r(&thetime, &tm);
578
579         hour = tm.tm_hour;
580         if (hour == 0)  hour = 12;
581         else if (hour > 12) hour = hour - 12;
582
583         if (seconds) {
584                 snprintf(buf, n, "%s %d %4d %d:%02d:%02d%s",
585                         fmt_date_months[tm.tm_mon],
586                         tm.tm_mday,
587                         tm.tm_year + 1900,
588                         hour,
589                         tm.tm_min,
590                         tm.tm_sec,
591                         ( (tm.tm_hour >= 12) ? "pm" : "am" )
592                 );
593         } else {
594                 snprintf(buf, n, "%s %d %4d %d:%02d%s",
595                         fmt_date_months[tm.tm_mon],
596                         tm.tm_mday,
597                         tm.tm_year + 1900,
598                         hour,
599                         tm.tm_min,
600                         ( (tm.tm_hour >= 12) ? "pm" : "am" )
601                 );
602         }
603 }
604
605
606
607 /*
608  * Determine whether the specified message number is contained within the
609  * specified sequence set.
610  */
611 int is_msg_in_sequence_set(const char *mset, long msgnum) {
612         int num_sets;
613         int s;
614         char setstr[128], lostr[128], histr[128];
615         long lo, hi;
616
617         num_sets = num_tokens(mset, ',');
618         for (s=0; s<num_sets; ++s) {
619                 extract_token(setstr, mset, s, ',', sizeof setstr);
620
621                 extract_token(lostr, setstr, 0, ':', sizeof lostr);
622                 if (num_tokens(setstr, ':') >= 2) {
623                         extract_token(histr, setstr, 1, ':', sizeof histr);
624                         if (!strcmp(histr, "*")) {
625                                 snprintf(histr, sizeof histr, "%ld", LONG_MAX);
626                         }
627                 } 
628                 else {
629                         strcpy(histr, lostr);
630                 }
631                 lo = atol(lostr);
632                 hi = atol(histr);
633
634                 if ((msgnum >= lo) && (msgnum <= hi)) return(1);
635         }
636
637         return(0);
638 }
639
640 /** 
641  * \brief Utility function to "readline" from memory
642  * \param start Location in memory from which we are reading.
643  * \param buf the buffer to place the string in.
644  * \param maxlen Size of string buffer
645  * \return Pointer to the source memory right after we stopped reading.
646  */
647 char *memreadline(char *start, char *buf, int maxlen)
648 {
649         char ch;
650         char *ptr;
651         int len = 0;            /**< tally our own length to avoid strlen() delays */
652
653         ptr = start;
654
655         while (1) {
656                 ch = *ptr++;
657                 if ((len + 1 < (maxlen)) && (ch != 13) && (ch != 10)) {
658                         buf[len++] = ch;
659                 }
660                 if ((ch == 10) || (ch == 0)) {
661                         buf[len] = 0;
662                         return ptr;
663                 }
664         }
665 }
666
667
668 /** 
669  * \brief Utility function to "readline" from memory
670  * \param start Location in memory from which we are reading.
671  * \param buf the buffer to place the string in.
672  * \param maxlen Size of string buffer
673  * \param retlen the length of the returned string
674  * \return Pointer to the source memory right after we stopped reading.
675  */
676 char *memreadlinelen(char *start, char *buf, int maxlen, int *retlen)
677 {
678         char ch;
679         char *ptr;
680         int len = 0;            /**< tally our own length to avoid strlen() delays */
681
682         ptr = start;
683
684         while (1) {
685                 ch = *ptr++;
686                 if ((len + 1 < (maxlen)) && (ch != 13) && (ch != 10)) {
687                         buf[len++] = ch;
688                 }
689                 if ((ch == 10) || (ch == 0)) {
690                         buf[len] = 0;
691                         *retlen = len;
692                         return ptr;
693                 }
694         }
695 }
696
697
698 /** 
699  * \brief Utility function to "readline" from memory
700  * \param start Location in memory from which we are reading.
701  * \param buf the buffer to place the string in.
702  * \param maxlen Size of string buffer
703  * \return Pointer to the source memory right after we stopped reading.
704  */
705 const char *cmemreadline(const char *start, char *buf, int maxlen)
706 {
707         char ch;
708         const char *ptr;
709         int len = 0;            /**< tally our own length to avoid strlen() delays */
710
711         ptr = start;
712
713         while (1) {
714                 ch = *ptr++;
715                 if ((len + 1 < (maxlen)) && (ch != 13) && (ch != 10)) {
716                         buf[len++] = ch;
717                 }
718                 if ((ch == 10) || (ch == 0)) {
719                         buf[len] = 0;
720                         return ptr;
721                 }
722         }
723 }
724
725
726 /** 
727  * \brief Utility function to "readline" from memory
728  * \param start Location in memory from which we are reading.
729  * \param buf the buffer to place the string in.
730  * \param maxlen Size of string buffer
731  * \param retlen the length of the returned string
732  * \return Pointer to the source memory right after we stopped reading.
733  */
734 const char *cmemreadlinelen(const char *start, char *buf, int maxlen, int *retlen)
735 {
736         char ch;
737         const char *ptr;
738         int len = 0;            /**< tally our own length to avoid strlen() delays */
739
740         ptr = start;
741
742         while (1) {
743                 ch = *ptr++;
744                 if ((len + 1 < (maxlen)) && (ch != 13) && (ch != 10)) {
745                         buf[len++] = ch;
746                 }
747                 if ((ch == 10) || (ch == 0)) {
748                         buf[len] = 0;
749                         *retlen = len;
750                         return ptr;
751                 }
752         }
753 }
754
755
756
757
758 /*
759  * Strip a boundarized substring out of a string (for example, remove
760  * parentheses and anything inside them).
761  */
762 int stripout(char *str, char leftboundary, char rightboundary) {
763         int a;
764         int lb = (-1);
765         int rb = (-1);
766
767         for (a = 0; a < strlen(str); ++a) {
768                 if (str[a] == leftboundary) lb = a;
769                 if (str[a] == rightboundary) rb = a;
770         }
771
772         if ( (lb > 0) && (rb > lb) ) {
773                 strcpy(&str[lb - 1], &str[rb + 1]);
774                 return 1;
775         }
776
777         else if ( (lb == 0) && (rb > lb) ) {
778                 strcpy(str, &str[rb + 1]);
779                 return 1;
780         }
781         return 0;
782 }
783
784
785 /*
786  * Reduce a string down to a boundarized substring (for example, remove
787  * parentheses and anything outside them).
788  */
789 long stripallbut(char *str, char leftboundary, char rightboundary) {
790         long len = 0;
791
792         char *lb = NULL;
793         char *rb = NULL;
794
795         lb = strrchr(str, leftboundary);
796         if (lb != NULL) {
797                 ++lb;
798                 rb = strchr(str, rightboundary);
799                 if ((rb != NULL) && (rb >= lb))  {
800                         *rb = 0;
801                         fflush(stderr);
802                         len = (long)rb - (long)lb;
803                         memmove(str, lb, len);
804                         str[len] = 0;
805                         return(len);
806                 }
807         }
808
809         return (long)strlen(str);
810 }
811
812
813 char *myfgets(char *s, int size, FILE *stream) {
814         char *ret = fgets(s, size, stream);
815         char *nl;
816
817         if (ret != NULL) {
818                 nl = strchr(s, '\n');
819
820                 if (nl != NULL)
821                         *nl = 0;
822         }
823
824         return ret;
825 }
826
827 /** 
828  * \brief Escape a string for feeding out as a URL.
829  * \param outbuf the output buffer
830  * \param oblen the size of outbuf to sanitize
831  * \param strbuf the input buffer
832  */
833 void urlesc(char *outbuf, size_t oblen, char *strbuf)
834 {
835         int a, b, c, len, eclen, olen;
836         char *ec = " +#&;`'|*?-~<>^()[]{}/$\"\\";
837
838         *outbuf = '\0';
839         len = strlen(strbuf);
840         eclen = strlen(ec);
841         olen = 0;
842         for (a = 0; a < len; ++a) {
843                 c = 0;
844                 for (b = 0; b < eclen; ++b) {
845                         if (strbuf[a] == ec[b])
846                                 c = 1;
847                 }
848                 if (c == 1) {
849                         snprintf(&outbuf[olen], oblen - olen, "%%%02x", strbuf[a]);
850                         olen += 3;
851                 }
852                 else 
853                         outbuf[olen ++] = strbuf[a];
854         }
855         outbuf[olen] = '\0';
856 }
857
858
859
860 /*
861  * In our world, we want strcpy() to be able to work with overlapping strings.
862  */
863 #ifdef strcpy
864 #undef strcpy
865 #endif
866 char *strcpy(char *dest, const char *src) {
867         memmove(dest, src, (strlen(src) + 1) );
868         return(dest);
869 }
870
871
872 /*
873  * Generate a new, globally unique UID parameter for a calendar etc. object
874  */
875 void generate_uuid(char *buf) {
876         static int seq = (-1);
877         static int no_kernel_uuid = 0;
878
879         /* If we are running on Linux then we have a kernelspace uuid generator available */
880
881         if (no_kernel_uuid == 0) {
882                 FILE *fp;
883                 fp = fopen("/proc/sys/kernel/random/uuid", "rb");
884                 if (fp) {
885                         int rv;
886                         rv = fread(buf, 36, 1, fp);
887                         fclose(fp);
888                         if (rv == 1) {
889                                 buf[36] = 0;
890                                 return;
891                         }
892                 }
893         }
894
895         /* If the kernel didn't provide us with a uuid, we generate a pseudo-random one */
896
897         no_kernel_uuid = 1;
898
899         if (seq == (-1)) {
900                 seq = (int)rand();
901         }
902         ++seq;
903         seq = (seq % 0x0FFF) ;
904
905         sprintf(buf, "%08lx-%04lx-4%03x-a%03x-%012lx",
906                 (long)time(NULL),
907                 (long)getpid(),
908                 seq,
909                 seq,
910                 (long)rand()
911         );
912 }
913
914 /*
915  * bmstrcasestr() -- case-insensitive substring search
916  *
917  * This uses the Boyer-Moore search algorithm and is therefore quite fast.
918  * The code is roughly based on the strstr() replacement from 'tin' written
919  * by Urs Jannsen.
920  */
921 inline static char *_bmstrcasestr_len(char *text, size_t textlen, const char *pattern, size_t patlen) {
922
923         register unsigned char *p, *t;
924         register int i, j, *delta;
925         register size_t p1;
926         int deltaspace[256];
927
928         if (!text) return(NULL);
929         if (!pattern) return(NULL);
930
931         /* algorithm fails if pattern is empty */
932         if ((p1 = patlen) == 0)
933                 return (text);
934
935         /* code below fails (whenever i is unsigned) if pattern too long */
936         if (p1 > textlen)
937                 return (NULL);
938
939         /* set up deltas */
940         delta = deltaspace;
941         for (i = 0; i <= 255; i++)
942                 delta[i] = p1;
943         for (p = (unsigned char *) pattern, i = p1; --i > 0;)
944                 delta[tolower(*p++)] = i;
945
946         /*
947          * From now on, we want patlen - 1.
948          * In the loop below, p points to the end of the pattern,
949          * t points to the end of the text to be tested against the
950          * pattern, and i counts the amount of text remaining, not
951          * including the part to be tested.
952          */
953         p1--;
954         p = (unsigned char *) pattern + p1;
955         t = (unsigned char *) text + p1;
956         i = textlen - patlen;
957         while(1) {
958                 if (tolower(p[0]) == tolower(t[0])) {
959                         if (strncasecmp ((const char *)(p - p1), (const char *)(t - p1), p1) == 0) {
960                                 return ((char *)t - p1);
961                         }
962                 }
963                 j = delta[tolower(t[0])];
964                 if (i < j)
965                         break;
966                 i -= j;
967                 t += j;
968         }
969         return (NULL);
970 }
971
972 /*
973  * bmstrcasestr() -- case-insensitive substring search
974  *
975  * This uses the Boyer-Moore search algorithm and is therefore quite fast.
976  * The code is roughly based on the strstr() replacement from 'tin' written
977  * by Urs Jannsen.
978  */
979 char *bmstrcasestr(char *text, const char *pattern) {
980         size_t textlen;
981         size_t patlen;
982
983         if (!text) return(NULL);
984         if (!pattern) return(NULL);
985
986         textlen = strlen (text);
987         patlen = strlen (pattern);
988
989         return _bmstrcasestr_len(text, textlen, pattern, patlen);
990 }
991
992 char *bmstrcasestr_len(char *text, size_t textlen, const char *pattern, size_t patlen) {
993         return _bmstrcasestr_len(text, textlen, pattern, patlen);
994 }
995
996
997
998
999 /*
1000  * bmstrcasestr() -- case-insensitive substring search
1001  *
1002  * This uses the Boyer-Moore search algorithm and is therefore quite fast.
1003  * The code is roughly based on the strstr() replacement from 'tin' written
1004  * by Urs Jannsen.
1005  */
1006 inline static const char *_cbmstrcasestr_len(const char *text, size_t textlen, const char *pattern, size_t patlen) {
1007
1008         register unsigned char *p, *t;
1009         register int i, j, *delta;
1010         register size_t p1;
1011         int deltaspace[256];
1012
1013         if (!text) return(NULL);
1014         if (!pattern) return(NULL);
1015
1016         /* algorithm fails if pattern is empty */
1017         if ((p1 = patlen) == 0)
1018                 return (text);
1019
1020         /* code below fails (whenever i is unsigned) if pattern too long */
1021         if (p1 > textlen)
1022                 return (NULL);
1023
1024         /* set up deltas */
1025         delta = deltaspace;
1026         for (i = 0; i <= 255; i++)
1027                 delta[i] = p1;
1028         for (p = (unsigned char *) pattern, i = p1; --i > 0;)
1029                 delta[tolower(*p++)] = i;
1030
1031         /*
1032          * From now on, we want patlen - 1.
1033          * In the loop below, p points to the end of the pattern,
1034          * t points to the end of the text to be tested against the
1035          * pattern, and i counts the amount of text remaining, not
1036          * including the part to be tested.
1037          */
1038         p1--;
1039         p = (unsigned char *) pattern + p1;
1040         t = (unsigned char *) text + p1;
1041         i = textlen - patlen;
1042         while(1) {
1043                 if (tolower(p[0]) == tolower(t[0])) {
1044                         if (strncasecmp ((const char *)(p - p1), (const char *)(t - p1), p1) == 0) {
1045                                 return ((char *)t - p1);
1046                         }
1047                 }
1048                 j = delta[tolower(t[0])];
1049                 if (i < j)
1050                         break;
1051                 i -= j;
1052                 t += j;
1053         }
1054         return (NULL);
1055 }
1056
1057 /*
1058  * bmstrcasestr() -- case-insensitive substring search
1059  *
1060  * This uses the Boyer-Moore search algorithm and is therefore quite fast.
1061  * The code is roughly based on the strstr() replacement from 'tin' written
1062  * by Urs Jannsen.
1063  */
1064 const char *cbmstrcasestr(const char *text, const char *pattern) {
1065         size_t textlen;
1066         size_t patlen;
1067
1068         if (!text) return(NULL);
1069         if (!pattern) return(NULL);
1070
1071         textlen = strlen (text);
1072         patlen = strlen (pattern);
1073
1074         return _cbmstrcasestr_len(text, textlen, pattern, patlen);
1075 }
1076
1077 const char *cbmstrcasestr_len(const char *text, size_t textlen, const char *pattern, size_t patlen) {
1078         return _cbmstrcasestr_len(text, textlen, pattern, patlen);
1079 }
1080
1081 /*
1082  * Local replacement for controversial C library function that generates
1083  * names for temporary files.  Included to shut up compiler warnings.
1084  */
1085 void CtdlMakeTempFileName(char *name, int len) {
1086         int i = 0;
1087
1088         while (i++, i < 100) {
1089                 snprintf(name, len, "/tmp/ctdl.%04lx.%04x",
1090                         (long)getpid(),
1091                         rand()
1092                 );
1093                 if (!access(name, F_OK)) {
1094                         return;
1095                 }
1096         }
1097 }
1098
1099
1100
1101 /*
1102  * Determine whether the specified message number is contained within the specified set.
1103  * Returns nonzero if the specified message number is in the specified message set string.
1104  */
1105 int is_msg_in_mset(const char *mset, long msgnum) {
1106         int num_sets;
1107         int s;
1108         char setstr[SIZ], lostr[SIZ], histr[SIZ];       /* was 1024 */
1109         long lo, hi;
1110
1111         /*
1112          * Now set it for all specified messages.
1113          */
1114         num_sets = num_tokens(mset, ',');
1115         for (s=0; s<num_sets; ++s) {
1116                 extract_token(setstr, mset, s, ',', sizeof setstr);
1117
1118                 extract_token(lostr, setstr, 0, ':', sizeof lostr);
1119                 if (num_tokens(setstr, ':') >= 2) {
1120                         extract_token(histr, setstr, 1, ':', sizeof histr);
1121                         if (!strcmp(histr, "*")) {
1122                                 snprintf(histr, sizeof histr, "%ld", LONG_MAX);
1123                         }
1124                 }
1125                 else {
1126                         strcpy(histr, lostr);
1127                 }
1128                 lo = atol(lostr);
1129                 hi = atol(histr);
1130
1131                 if ((msgnum >= lo) && (msgnum <= hi)) return(1);
1132         }
1133
1134         return(0);
1135 }
1136
1137
1138 /*
1139  * searches for a pattern within a search string
1140  * returns position in string
1141  */
1142 int pattern2(char *search, char *patn)
1143 {
1144         int a;
1145         int len, plen;
1146         len = strlen (search);
1147         plen = strlen (patn);
1148         for (a = 0; a < len; ++a) {
1149                 if (!strncasecmp(&search[a], patn, plen))
1150                         return (a);
1151         }
1152         return (-1);
1153 }
1154
1155
1156 /*
1157  * Strip leading and trailing spaces from a string; with premeasured and adjusted length.
1158  * buf - the string to modify
1159  * len - length of the string. 
1160  */
1161 void stripltlen(char *buf, int *len)
1162 {
1163         int delta = 0;
1164         if (*len == 0) return;
1165         while ((*len > delta) && (isspace(buf[delta]))){
1166                 delta ++;
1167         }
1168         memmove (buf, &buf[delta], *len - delta + 1);
1169         (*len) -=delta;
1170
1171         if (*len == 0) return;
1172         while (isspace(buf[(*len) - 1])){
1173                 buf[--(*len)] = '\0';
1174         }
1175 }
1176
1177
1178 /*
1179  * Convert all whitespace characters in a supplied string to underscores
1180  */
1181 void convert_spaces_to_underscores(char *str)
1182 {
1183         int len;
1184         int i;
1185
1186         if (!str) return;
1187
1188         len = strlen(str);
1189         for (i=0; i<len; ++i) {
1190                 if (isspace(str[i])) {
1191                         str[i] = '_';
1192                 }
1193         }
1194 }
1195
1196
1197 /*
1198  * check whether the provided string needs to be qp encoded or not
1199  */
1200 int CheckEncode(const char *pch, long len, const char *pche)
1201 {
1202         if (pche == NULL)
1203                 pche = pch + len;
1204         while (pch < pche) {
1205                 if (((unsigned char) *pch < 32) || 
1206                     ((unsigned char) *pch > 126)) {
1207                         return 1;
1208                 }
1209                 pch++;
1210         }
1211         return 0;
1212 }
1213