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