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