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