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