c93758e2bc1388498e895f1f21b2b272bcb81c8c
[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-2017 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  * Determine whether the specified message number is contained within the
466  * specified sequence set.
467  */
468 int is_msg_in_sequence_set(const char *mset, long msgnum) {
469         int num_sets;
470         int s;
471         char setstr[128], lostr[128], histr[128];
472         long lo, hi;
473
474         num_sets = num_tokens(mset, ',');
475         for (s=0; s<num_sets; ++s) {
476                 extract_token(setstr, mset, s, ',', sizeof setstr);
477
478                 extract_token(lostr, setstr, 0, ':', sizeof lostr);
479                 if (num_tokens(setstr, ':') >= 2) {
480                         extract_token(histr, setstr, 1, ':', sizeof histr);
481                         if (!strcmp(histr, "*")) {
482                                 snprintf(histr, sizeof histr, "%ld", LONG_MAX);
483                         }
484                 } 
485                 else {
486                         strcpy(histr, lostr);
487                 }
488                 lo = atol(lostr);
489                 hi = atol(histr);
490
491                 if ((msgnum >= lo) && (msgnum <= hi)) return(1);
492         }
493
494         return(0);
495 }
496
497 /** 
498  * \brief Utility function to "readline" from memory
499  * \param start Location in memory from which we are reading.
500  * \param buf the buffer to place the string in.
501  * \param maxlen Size of string buffer
502  * \return Pointer to the source memory right after we stopped reading.
503  */
504 char *memreadline(char *start, char *buf, int maxlen)
505 {
506         char ch;
507         char *ptr;
508         int len = 0;            /**< tally our own length to avoid strlen() delays */
509
510         ptr = start;
511
512         while (1) {
513                 ch = *ptr++;
514                 if ((len + 1 < (maxlen)) && (ch != 13) && (ch != 10)) {
515                         buf[len++] = ch;
516                 }
517                 if ((ch == 10) || (ch == 0)) {
518                         buf[len] = 0;
519                         return ptr;
520                 }
521         }
522 }
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  * \param retlen the length of the returned string
531  * \return Pointer to the source memory right after we stopped reading.
532  */
533 char *memreadlinelen(char *start, char *buf, int maxlen, int *retlen)
534 {
535         char ch;
536         char *ptr;
537         int len = 0;            /**< tally our own length to avoid strlen() delays */
538
539         ptr = start;
540
541         while (1) {
542                 ch = *ptr++;
543                 if ((len + 1 < (maxlen)) && (ch != 13) && (ch != 10)) {
544                         buf[len++] = ch;
545                 }
546                 if ((ch == 10) || (ch == 0)) {
547                         buf[len] = 0;
548                         *retlen = len;
549                         return ptr;
550                 }
551         }
552 }
553
554
555 /** 
556  * \brief Utility function to "readline" from memory
557  * \param start Location in memory from which we are reading.
558  * \param buf the buffer to place the string in.
559  * \param maxlen Size of string buffer
560  * \return Pointer to the source memory right after we stopped reading.
561  */
562 const char *cmemreadline(const char *start, char *buf, int maxlen)
563 {
564         char ch;
565         const char *ptr;
566         int len = 0;            /**< tally our own length to avoid strlen() delays */
567
568         ptr = start;
569
570         while (1) {
571                 ch = *ptr++;
572                 if ((len + 1 < (maxlen)) && (ch != 13) && (ch != 10)) {
573                         buf[len++] = ch;
574                 }
575                 if ((ch == 10) || (ch == 0)) {
576                         buf[len] = 0;
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  * \param retlen the length of the returned string
589  * \return Pointer to the source memory right after we stopped reading.
590  */
591 const char *cmemreadlinelen(const char *start, char *buf, int maxlen, int *retlen)
592 {
593         char ch;
594         const char *ptr;
595         int len = 0;            /**< tally our own length to avoid strlen() delays */
596
597         ptr = start;
598
599         while (1) {
600                 ch = *ptr++;
601                 if ((len + 1 < (maxlen)) && (ch != 13) && (ch != 10)) {
602                         buf[len++] = ch;
603                 }
604                 if ((ch == 10) || (ch == 0)) {
605                         buf[len] = 0;
606                         *retlen = len;
607                         return ptr;
608                 }
609         }
610 }
611
612
613
614
615 /*
616  * Strip a boundarized substring out of a string (for example, remove
617  * parentheses and anything inside them).
618  */
619 int stripout(char *str, char leftboundary, char rightboundary) {
620         int a;
621         int lb = (-1);
622         int rb = (-1);
623
624         for (a = 0; a < strlen(str); ++a) {
625                 if (str[a] == leftboundary) lb = a;
626                 if (str[a] == rightboundary) rb = a;
627         }
628
629         if ( (lb > 0) && (rb > lb) ) {
630                 strcpy(&str[lb - 1], &str[rb + 1]);
631                 return 1;
632         }
633
634         else if ( (lb == 0) && (rb > lb) ) {
635                 strcpy(str, &str[rb + 1]);
636                 return 1;
637         }
638         return 0;
639 }
640
641
642 /*
643  * Reduce a string down to a boundarized substring (for example, remove
644  * parentheses and anything outside them).
645  */
646 long stripallbut(char *str, char leftboundary, char rightboundary) {
647         long len = 0;
648
649         char *lb = NULL;
650         char *rb = NULL;
651
652         lb = strrchr(str, leftboundary);
653         if (lb != NULL) {
654                 ++lb;
655                 rb = strchr(str, rightboundary);
656                 if ((rb != NULL) && (rb >= lb))  {
657                         *rb = 0;
658                         fflush(stderr);
659                         len = (long)rb - (long)lb;
660                         memmove(str, lb, len);
661                         str[len] = 0;
662                         return(len);
663                 }
664         }
665
666         return (long)strlen(str);
667 }
668
669
670 char *myfgets(char *s, int size, FILE *stream) {
671         char *ret = fgets(s, size, stream);
672         char *nl;
673
674         if (ret != NULL) {
675                 nl = strchr(s, '\n');
676
677                 if (nl != NULL)
678                         *nl = 0;
679         }
680
681         return ret;
682 }
683
684 /** 
685  * \brief Escape a string for feeding out as a URL.
686  * \param outbuf the output buffer
687  * \param oblen the size of outbuf to sanitize
688  * \param strbuf the input buffer
689  */
690 void urlesc(char *outbuf, size_t oblen, char *strbuf)
691 {
692         int a, b, c, len, eclen, olen;
693         char *ec = " +#&;`'|*?-~<>^()[]{}/$\"\\";
694
695         *outbuf = '\0';
696         len = strlen(strbuf);
697         eclen = strlen(ec);
698         olen = 0;
699         for (a = 0; a < len; ++a) {
700                 c = 0;
701                 for (b = 0; b < eclen; ++b) {
702                         if (strbuf[a] == ec[b])
703                                 c = 1;
704                 }
705                 if (c == 1) {
706                         snprintf(&outbuf[olen], oblen - olen, "%%%02x", strbuf[a]);
707                         olen += 3;
708                 }
709                 else 
710                         outbuf[olen ++] = strbuf[a];
711         }
712         outbuf[olen] = '\0';
713 }
714
715
716
717 /*
718  * In our world, we want strcpy() to be able to work with overlapping strings.
719  */
720 #ifdef strcpy
721 #undef strcpy
722 #endif
723 char *strcpy(char *dest, const char *src) {
724         memmove(dest, src, (strlen(src) + 1) );
725         return(dest);
726 }
727
728
729 /*
730  * Generate a new, globally unique UID parameter for a calendar etc. object
731  */
732 void generate_uuid(char *buf) {
733         static int seq = (-1);
734         static int no_kernel_uuid = 0;
735
736         /* If we are running on Linux then we have a kernelspace uuid generator available */
737
738         if (no_kernel_uuid == 0) {
739                 FILE *fp;
740                 fp = fopen("/proc/sys/kernel/random/uuid", "rb");
741                 if (fp) {
742                         int rv;
743                         rv = fread(buf, 36, 1, fp);
744                         fclose(fp);
745                         if (rv == 1) {
746                                 buf[36] = 0;
747                                 return;
748                         }
749                 }
750         }
751
752         /* If the kernel didn't provide us with a uuid, we generate a pseudo-random one */
753
754         no_kernel_uuid = 1;
755
756         if (seq == (-1)) {
757                 seq = (int)rand();
758         }
759         ++seq;
760         seq = (seq % 0x0FFF) ;
761
762         sprintf(buf, "%08lx-%04lx-4%03x-a%03x-%012lx",
763                 (long)time(NULL),
764                 (long)getpid(),
765                 seq,
766                 seq,
767                 (long)rand()
768         );
769 }
770
771 /*
772  * bmstrcasestr() -- case-insensitive substring search
773  *
774  * This uses the Boyer-Moore search algorithm and is therefore quite fast.
775  * The code is roughly based on the strstr() replacement from 'tin' written
776  * by Urs Jannsen.
777  */
778 inline static char *_bmstrcasestr_len(char *text, size_t textlen, const char *pattern, size_t patlen) {
779
780         register unsigned char *p, *t;
781         register int i, j, *delta;
782         register size_t p1;
783         int deltaspace[256];
784
785         if (!text) return(NULL);
786         if (!pattern) return(NULL);
787
788         /* algorithm fails if pattern is empty */
789         if ((p1 = patlen) == 0)
790                 return (text);
791
792         /* code below fails (whenever i is unsigned) if pattern too long */
793         if (p1 > textlen)
794                 return (NULL);
795
796         /* set up deltas */
797         delta = deltaspace;
798         for (i = 0; i <= 255; i++)
799                 delta[i] = p1;
800         for (p = (unsigned char *) pattern, i = p1; --i > 0;)
801                 delta[tolower(*p++)] = i;
802
803         /*
804          * From now on, we want patlen - 1.
805          * In the loop below, p points to the end of the pattern,
806          * t points to the end of the text to be tested against the
807          * pattern, and i counts the amount of text remaining, not
808          * including the part to be tested.
809          */
810         p1--;
811         p = (unsigned char *) pattern + p1;
812         t = (unsigned char *) text + p1;
813         i = textlen - patlen;
814         while(1) {
815                 if (tolower(p[0]) == tolower(t[0])) {
816                         if (strncasecmp ((const char *)(p - p1), (const char *)(t - p1), p1) == 0) {
817                                 return ((char *)t - p1);
818                         }
819                 }
820                 j = delta[tolower(t[0])];
821                 if (i < j)
822                         break;
823                 i -= j;
824                 t += j;
825         }
826         return (NULL);
827 }
828
829 /*
830  * bmstrcasestr() -- case-insensitive substring search
831  *
832  * This uses the Boyer-Moore search algorithm and is therefore quite fast.
833  * The code is roughly based on the strstr() replacement from 'tin' written
834  * by Urs Jannsen.
835  */
836 char *bmstrcasestr(char *text, const char *pattern) {
837         size_t textlen;
838         size_t patlen;
839
840         if (!text) return(NULL);
841         if (!pattern) return(NULL);
842
843         textlen = strlen (text);
844         patlen = strlen (pattern);
845
846         return _bmstrcasestr_len(text, textlen, pattern, patlen);
847 }
848
849 char *bmstrcasestr_len(char *text, size_t textlen, const char *pattern, size_t patlen) {
850         return _bmstrcasestr_len(text, textlen, pattern, patlen);
851 }
852
853
854
855
856 /*
857  * bmstrcasestr() -- case-insensitive substring search
858  *
859  * This uses the Boyer-Moore search algorithm and is therefore quite fast.
860  * The code is roughly based on the strstr() replacement from 'tin' written
861  * by Urs Jannsen.
862  */
863 inline static const char *_cbmstrcasestr_len(const char *text, size_t textlen, const char *pattern, size_t patlen) {
864
865         register unsigned char *p, *t;
866         register int i, j, *delta;
867         register size_t p1;
868         int deltaspace[256];
869
870         if (!text) return(NULL);
871         if (!pattern) return(NULL);
872
873         /* algorithm fails if pattern is empty */
874         if ((p1 = patlen) == 0)
875                 return (text);
876
877         /* code below fails (whenever i is unsigned) if pattern too long */
878         if (p1 > textlen)
879                 return (NULL);
880
881         /* set up deltas */
882         delta = deltaspace;
883         for (i = 0; i <= 255; i++)
884                 delta[i] = p1;
885         for (p = (unsigned char *) pattern, i = p1; --i > 0;)
886                 delta[tolower(*p++)] = i;
887
888         /*
889          * From now on, we want patlen - 1.
890          * In the loop below, p points to the end of the pattern,
891          * t points to the end of the text to be tested against the
892          * pattern, and i counts the amount of text remaining, not
893          * including the part to be tested.
894          */
895         p1--;
896         p = (unsigned char *) pattern + p1;
897         t = (unsigned char *) text + p1;
898         i = textlen - patlen;
899         while(1) {
900                 if (tolower(p[0]) == tolower(t[0])) {
901                         if (strncasecmp ((const char *)(p - p1), (const char *)(t - p1), p1) == 0) {
902                                 return ((char *)t - p1);
903                         }
904                 }
905                 j = delta[tolower(t[0])];
906                 if (i < j)
907                         break;
908                 i -= j;
909                 t += j;
910         }
911         return (NULL);
912 }
913
914 /*
915  * bmstrcasestr() -- case-insensitive substring search
916  *
917  * This uses the Boyer-Moore search algorithm and is therefore quite fast.
918  * The code is roughly based on the strstr() replacement from 'tin' written
919  * by Urs Jannsen.
920  */
921 const char *cbmstrcasestr(const char *text, const char *pattern) {
922         size_t textlen;
923         size_t patlen;
924
925         if (!text) return(NULL);
926         if (!pattern) return(NULL);
927
928         textlen = strlen (text);
929         patlen = strlen (pattern);
930
931         return _cbmstrcasestr_len(text, textlen, pattern, patlen);
932 }
933
934 const char *cbmstrcasestr_len(const char *text, size_t textlen, const char *pattern, size_t patlen) {
935         return _cbmstrcasestr_len(text, textlen, pattern, patlen);
936 }
937
938 /*
939  * Local replacement for controversial C library function that generates
940  * names for temporary files.  Included to shut up compiler warnings.
941  */
942 void CtdlMakeTempFileName(char *name, int len) {
943         int i = 0;
944
945         while (i++, i < 100) {
946                 snprintf(name, len, "/tmp/ctdl.%04lx.%04x",
947                         (long)getpid(),
948                         rand()
949                 );
950                 if (!access(name, F_OK)) {
951                         return;
952                 }
953         }
954 }
955
956
957
958 /*
959  * Determine whether the specified message number is contained within the specified set.
960  * Returns nonzero if the specified message number is in the specified message set string.
961  */
962 int is_msg_in_mset(const char *mset, long msgnum) {
963         int num_sets;
964         int s;
965         char setstr[SIZ], lostr[SIZ], histr[SIZ];       /* was 1024 */
966         long lo, hi;
967
968         /*
969          * Now set it for all specified messages.
970          */
971         num_sets = num_tokens(mset, ',');
972         for (s=0; s<num_sets; ++s) {
973                 extract_token(setstr, mset, s, ',', sizeof setstr);
974
975                 extract_token(lostr, setstr, 0, ':', sizeof lostr);
976                 if (num_tokens(setstr, ':') >= 2) {
977                         extract_token(histr, setstr, 1, ':', sizeof histr);
978                         if (!strcmp(histr, "*")) {
979                                 snprintf(histr, sizeof histr, "%ld", LONG_MAX);
980                         }
981                 }
982                 else {
983                         strcpy(histr, lostr);
984                 }
985                 lo = atol(lostr);
986                 hi = atol(histr);
987
988                 if ((msgnum >= lo) && (msgnum <= hi)) return(1);
989         }
990
991         return(0);
992 }
993
994
995 /*
996  * searches for a pattern within a search string
997  * returns position in string
998  */
999 int pattern2(char *search, char *patn)
1000 {
1001         int a;
1002         int len, plen;
1003         len = strlen (search);
1004         plen = strlen (patn);
1005         for (a = 0; a < len; ++a) {
1006                 if (!strncasecmp(&search[a], patn, plen))
1007                         return (a);
1008         }
1009         return (-1);
1010 }
1011
1012
1013 /*
1014  * Strip leading and trailing spaces from a string; with premeasured and adjusted length.
1015  * buf - the string to modify
1016  * len - length of the string. 
1017  */
1018 void stripltlen(char *buf, int *len)
1019 {
1020         int delta = 0;
1021         if (*len == 0) return;
1022         while ((*len > delta) && (isspace(buf[delta]))){
1023                 delta ++;
1024         }
1025         memmove (buf, &buf[delta], *len - delta + 1);
1026         (*len) -=delta;
1027
1028         if (*len == 0) return;
1029         while (isspace(buf[(*len) - 1])){
1030                 buf[--(*len)] = '\0';
1031         }
1032 }
1033
1034
1035 /*
1036  * Convert all whitespace characters in a supplied string to underscores
1037  */
1038 void convert_spaces_to_underscores(char *str)
1039 {
1040         int len;
1041         int i;
1042
1043         if (!str) return;
1044
1045         len = strlen(str);
1046         for (i=0; i<len; ++i) {
1047                 if (isspace(str[i])) {
1048                         str[i] = '_';
1049                 }
1050         }
1051 }
1052
1053
1054 /*
1055  * check whether the provided string needs to be qp encoded or not
1056  */
1057 int CheckEncode(const char *pch, long len, const char *pche)
1058 {
1059         if (pche == NULL)
1060                 pche = pch + len;
1061         while (pch < pche) {
1062                 if (((unsigned char) *pch < 32) || 
1063                     ((unsigned char) *pch > 126)) {
1064                         return 1;
1065                 }
1066                 pch++;
1067         }
1068         return 0;
1069 }
1070