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