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