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