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