ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/hybrid-ircservices-1/misc.c
Revision: 1209
Committed: Thu Aug 25 19:05:49 2011 UTC (12 years, 7 months ago) by michael
Content type: text/x-csrc
File size: 35268 byte(s)
Log Message:
- run everything thru indent
  "-bli0 -di1 -npcs -nut -cdw -bls -nbbo -bap"

File Contents

# Content
1 /* Miscellaneous routines.
2 *
3 * IRC Services is copyright (c) 1996-2009 Andrew Church.
4 * E-mail: <achurch@achurch.org>
5 * Parts written by Andrew Kempe and others.
6 * This program is free but copyrighted software; see the file GPL.txt for
7 * details.
8 */
9
10 #include "services.h"
11
12 #ifdef CONVERT_DB
13 # undef log
14 # define log(...) fprintf(stderr, __VA_ARGS__)
15 #endif
16
17 /*************************************************************************/
18
19 /* Table used by irc_tolower(). This table follows the RFC 1459
20 * requirement that [ \ ] and { | } be considered equivalent. Protocols
21 * which do not follow this requirement should modify the table
22 * accordingly.
23 */
24
25 unsigned char irc_lowertable[256] = {
26 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
27 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
28 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
29 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
30 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
31 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
32 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
33 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
34
35 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
36 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,
37 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
38 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x5E, 0x5F,
39 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
40 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,
41 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
42 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F,
43
44 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
45 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
46 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
47 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F,
48 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7,
49 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF,
50 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7,
51 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF,
52
53 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7,
54 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF,
55 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7,
56 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF,
57 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7,
58 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF,
59 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7,
60 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF,
61 };
62
63
64 /*************************************************************************/
65
66 /* irc_tolower: Like toupper/tolower, but for nicknames and channel names;
67 * the RFC requires that '[' and '{', '\' and '|', ']' and '}'
68 * be pairwise equivalent in such names. Declared inline for
69 * irc_str[n]icmp()'s benefit.
70 */
71
72 #undef irc_tolower
73 inline unsigned char
74 irc_tolower(char c)
75 {
76 return irc_lowertable[(uint8) c];
77 }
78
79 /*************************************************************************/
80
81 /* irc_str[n]icmp: Like str[n]icmp, but for nicknames and channel names. */
82
83 int
84 irc_stricmp(const char *s1, const char *s2)
85 {
86 register char c1, c2;
87
88 while ((c1 = (char) irc_tolower(*s1)) == (c2 = (char) irc_tolower(*s2)))
89 {
90 if (c1 == 0)
91 return 0;
92 s1++;
93 s2++;
94 }
95 return c1 < c2 ? -1 : 1;
96 }
97
98 int
99 irc_strnicmp(const char *s1, const char *s2, int max)
100 {
101 register char c1, c2;
102
103 if (max <= 0)
104 return 0;
105 while ((c1 = (char) irc_tolower(*s1)) == (c2 = (char) irc_tolower(*s2)))
106 {
107 if (c1 == 0 || --max <= 0)
108 return 0;
109 s1++;
110 s2++;
111 }
112 return c1 < c2 ? -1 : 1;
113 }
114
115 /*************************************************************************/
116
117 /* strscpy: Copy at most len-1 characters from a string to a buffer, and
118 * add a null terminator after the last character copied.
119 * The `strbcpy(d,s)' variant (defined as a macro in extern.h)
120 * assumes `d' is a character array and uses sizeof(d) as the
121 * `len' parameter.
122 */
123
124 char *
125 strscpy(char *d, const char *s, size_t len)
126 {
127 char *d_orig = d;
128
129 if (!len)
130 return d;
131 while (--len && (*d++ = *s++))
132 ;
133 *d = 0;
134 return d_orig;
135 }
136
137 /*************************************************************************/
138
139 /* strmove: Like strcpy(), but handles overlapping regions of memory
140 * properly. The str*() analog of memmove().
141 */
142
143 char *
144 strmove(char *d, const char *s)
145 {
146 memmove(d, s, strlen(s) + 1);
147 return d;
148 }
149
150 /*************************************************************************/
151
152 /* stristr: Search case-insensitively for string s2 within string s1,
153 * returning the first occurrence of s2 or NULL if s2 was not
154 * found.
155 */
156
157 char *
158 stristr(const char *s1, const char *s2)
159 {
160 register const char *s = s1, *d = s2;
161
162 while (*s1)
163 {
164 if (tolower(*s1) == tolower(*d))
165 {
166 s1++;
167 d++;
168 if (*d == 0)
169 return (char *) s;
170 }
171 else
172 {
173 s = ++s1;
174 d = s2;
175 }
176 }
177 return NULL;
178 }
179
180 /*************************************************************************/
181
182 /* strupper, strlower: Convert a string to upper or lower case.
183 */
184
185 char *
186 strupper(char *s)
187 {
188 char *t = s;
189 while (*t)
190 {
191 *t = toupper(*t);
192 t++;
193 }
194 return s;
195 }
196
197 char *
198 strlower(char *s)
199 {
200 char *t = s;
201 while (*t)
202 {
203 *t = tolower(*t);
204 t++;
205 }
206 return s;
207 }
208
209 /*************************************************************************/
210
211 /* strnrepl: Replace occurrences of `old' with `new' in string `s'. Stop
212 * replacing if a replacement would cause the string to exceed
213 * `size' bytes (including the null terminator). Return the
214 * string.
215 */
216
217 char *
218 strnrepl(char *s, int32 size, const char *old, const char *new)
219 {
220 char *ptr = s;
221 int32 left = strlen(s);
222 int32 avail = size - (left + 1);
223 int32 oldlen = strlen(old);
224 int32 newlen = strlen(new);
225 int32 diff = newlen - oldlen;
226
227 if (avail < 0) /* silly parameter */
228 avail = 0;
229 while (left >= oldlen)
230 {
231 if (strncmp(ptr, old, oldlen) != 0)
232 {
233 left--;
234 ptr++;
235 continue;
236 }
237 if (diff > avail)
238 break;
239 if (diff != 0)
240 memmove(ptr + oldlen + diff, ptr + oldlen, left + 1);
241 strncpy(ptr, new, newlen);
242 ptr += newlen;
243 left -= oldlen;
244 }
245 return s;
246 }
247
248 /*************************************************************************/
249
250 /* strtok_remaining: Return the result of strtok(NULL, "") with any
251 * leading or trailing whitespace discarded. If nothing
252 * but whitespace is left, return NULL.
253 */
254
255 char *
256 strtok_remaining(void)
257 {
258 char *s = strtok(NULL, ""), *t;
259 if (s)
260 {
261 while (isspace(*s))
262 s++;
263 t = s + strlen(s) - 1;
264 while (t >= s && isspace(*t))
265 *t-- = 0;
266 if (!*s)
267 return NULL;
268 }
269 return s;
270 }
271
272 /*************************************************************************/
273 /*************************************************************************/
274
275 /* merge_args: Take an argument count and argument vector and merge them
276 * into a single string in which each argument is separated by
277 * a space. The returned string is stored in a static buffer
278 * which will be overwritten on the next call.
279 */
280
281 char *
282 merge_args(int argc, char **argv)
283 {
284 int i;
285 static char s[4096];
286 char *t;
287
288 t = s;
289 for (i = 0; i < argc; i++)
290 {
291 t += snprintf(t, sizeof(s) - (t - s), "%s%s", *argv++,
292 (i < argc - 1) ? " " : "");
293 }
294 return s;
295 }
296
297 /*************************************************************************/
298 /*************************************************************************/
299
300 /* match_wild: Attempt to match a string to a pattern which might contain
301 * '*' or '?' wildcards. Return 1 if the string matches the
302 * pattern, 0 if not.
303 */
304
305 static int
306 do_match_wild(const char *pattern, const char *str, int docase)
307 {
308 char c;
309 const char *s;
310
311 /* Sanity-check pointer parameters */
312
313 if (pattern == NULL)
314 {
315 log("BUG or corrupted database: pattern == NULL in do_match_wild())");
316 return 0;
317 }
318 if (str == NULL)
319 {
320 log("BUG or corrupted database: str == NULL in do_match_wild())");
321 return 0;
322 }
323
324 /* This loop is guaranteed to terminate, either by *pattern == 0 (the
325 * end of the pattern string) or a trailing '*' (or "*???..."). */
326
327 for (;;)
328 {
329 switch (c = *pattern++)
330 {
331 case 0:
332 if (!*str)
333 return 1;
334 return 0;
335 case '?':
336 if (!*str)
337 return 0;
338 str++;
339 break;
340 case '*':
341 while (*pattern == '?')
342 {
343 if (!*str)
344 return 0;
345 str++; /* skip a character for each '?' */
346 pattern++;
347 }
348 if (!*pattern)
349 return 1; /* trailing '*' matches everything else */
350 s = str;
351 while (*s)
352 {
353 if ((docase ? (*s == *pattern) : (tolower(*s) == tolower(*pattern)))
354 && do_match_wild(pattern + 1, s + 1, docase))
355 return 1;
356 s++;
357 }
358 break;
359 default:
360 if (docase ? (*str != c) : (tolower(*str) != tolower(c)))
361 return 0;
362 str++;
363 break;
364 } /* switch */
365 }
366 /* not reached */
367 }
368
369
370 int
371 match_wild(const char *pattern, const char *str)
372 {
373 return do_match_wild(pattern, str, 1);
374 }
375
376 int
377 match_wild_nocase(const char *pattern, const char *str)
378 {
379 return do_match_wild(pattern, str, 0);
380 }
381
382 /*************************************************************************/
383 /*************************************************************************/
384
385 /* Tables used by valid_nick() and valid_chan(). These tables follow the
386 * RFC 1459 definitions; protocols with different definitions should modify
387 * the table accordingly:
388 * Bit 0 (0x01) = valid as a first character
389 * Bit 1 (0x02) = valid as a subsequent character
390 * Bit 2 (0x04) = valid as byte 1 of a 2-byte character
391 * Bit 3 (0x08) = valid as byte 2 of a 2-byte character
392 * valid_{nick,chan}_table[0] should never be modified.
393 *
394 * 2-byte characters are handled by setting bits 2 and 3 on the appropriate
395 * single-byte entries (0x80-0xFF), then setting bits 0 and/or 1 on 2-byte
396 * entries (0x0100-0xFFFF) as needed, where the first byte of the character
397 * is the upper 8 bits of the array index and the second byte is the lower
398 * 8 bits.
399 *
400 * Note: The grammar for nicknames in RFC 1459 excludes '|', but this
401 * contradicts an earlier statement that '|' and '\' are to be considered
402 * equivalent when comparing nicknames. On the other hand, the reference
403 * server code (at least as far back as version 2.6.2, September 1991)
404 * allows anything from 'A' to '}', and allows all specials other than '-'
405 * at the beginning of a nick as well. We consider the RFC to be in error
406 * and go by the reference implementation.
407 */
408
409 unsigned char valid_nick_table[0x10000] = {
410 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
411 0, 0, 0, 0, 0, 0, 0,
412 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2,
413 2, 0, 0, 0, 0, 0, 0,
414 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
415 3, 3, 3, 3, 3, 3, 3,
416 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
417 3, 3, 3, 3, 3, 3, 0,
418 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
419 0, 0, 0, 0, 0, 0, 0,
420 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
421 0, 0, 0, 0, 0, 0, 0,
422 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
423 0, 0, 0, 0, 0, 0, 0,
424 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
425 0, 0, 0, 0, 0, 0, 0,
426 };
427
428 unsigned char valid_chan_table[0x10000] = {
429 0, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
430 2, 2, 2, 2, 2, 2, 2,
431 0, 2, 2, 3, 2, 2, 3, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
432 2, 2, 2, 2, 2, 2, 2,
433 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
434 2, 2, 2, 2, 2, 2, 2,
435 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
436 2, 2, 2, 2, 2, 2, 2,
437 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
438 2, 2, 2, 2, 2, 2, 2,
439 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
440 2, 2, 2, 2, 2, 2, 2,
441 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
442 2, 2, 2, 2, 2, 2, 2,
443 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
444 2, 2, 2, 2, 2, 2, 2,
445 };
446
447 /*************************************************************************/
448
449 /* valid_nick, valid_chan: Check whether the given string is a valid
450 * nickname or channel name, ignoring length.
451 * Returns nonzero if valid, zero if not.
452 */
453
454 static int
455 valid_nickchan(const char *str, const unsigned char *table)
456 {
457 int first = 1; /* First character? */
458 int mbsave = 0; /* First byte of a 2-byte character */
459
460 while (*str)
461 {
462 int ch = *(const unsigned char *) str++;
463 if (mbsave)
464 {
465 if (!(table[ch] & 8))
466 return 0;
467 ch |= mbsave << 8;
468 mbsave = 0;
469 }
470 else if (table[ch] & 4)
471 {
472 mbsave = ch;
473 continue;
474 }
475 if (!(table[ch] & (first ? 1 : 2)))
476 return 0;
477 first = 0;
478 }
479 return 1;
480 }
481
482 int
483 valid_nick(const char *str)
484 {
485 return valid_nickchan(str, valid_nick_table);
486 }
487
488 int
489 valid_chan(const char *str)
490 {
491 return valid_nickchan(str, valid_chan_table);
492 }
493
494 /*************************************************************************/
495
496 /* Check whether the given string is a valid domain name, according to RFC
497 * rules:
498 * - Contains only letters, digits, hyphens, and periods (dots).
499 * - Begins with a letter or digit.
500 * - Has a letter or digit after every dot (except for a trailing dot).
501 * - Is no more than DOMAIN_MAXLEN characters long.
502 * - Has no more than DOMPART_MAXLEN characters between periods.
503 * - Has at least one character and does not end with a dot. (not RFC)
504 */
505
506 #define DOMAIN_MAXLEN 255
507 #define DOMPART_MAXLEN 63
508
509 int
510 valid_domain(const char *str)
511 {
512 const char *s;
513 int i;
514 static const char valid_domain_chars[] =
515 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-.";
516
517 if (!*str)
518 return 0;
519 if (str[strspn(str, valid_domain_chars)] != 0)
520 return 0;
521 s = str;
522 while (s - str < DOMAIN_MAXLEN && *s)
523 {
524 if (*s == '-' || *s == '.')
525 return 0;
526 i = strcspn(s, ".");
527 if (i > DOMPART_MAXLEN)
528 return 0;
529 s += i;
530 if (*s)
531 s++;
532 }
533 if (s - str > DOMAIN_MAXLEN || *s)
534 return 0;
535 if (s[-1] == '.')
536 return 0;
537 return 1;
538 }
539
540 /*************************************************************************/
541
542 /* Check whether the given string is a valid E-mail address. A valid
543 * E-mail address:
544 * - Contains none of the following characters:
545 * + control characters (\000-\037)
546 * + space (\040)
547 * + vertical bar ('|') (because some mailers try to pipe with it)
548 * + RFC-822 specials, except [ ] @ .
549 * - Contains exactly one '@', which may not be the first character.
550 * - Contains a valid domain name or an IP address in square brackets
551 * after the '@'.
552 * - Does not contain [ or ] except when used with an IP address as above.
553 */
554
555 int
556 valid_email(const char *str)
557 {
558 const unsigned char *s;
559 const char *atmark;
560
561 for (s = (const unsigned char *) str; *s; s++)
562 {
563 if (*s <= '\040') // 040 == 0x20 == ' '
564 return 0;
565 if (strchr("|,:;\\\"()<>", *s))
566 return 0;
567 }
568
569 /* Find the @, and abort if there isn't one */
570 atmark = strchr(str, '@');
571 if (!atmark || atmark == str)
572 return 0;
573 atmark++;
574
575 /* Don't allow [] in username */
576 s = (const unsigned char *) strpbrk(str, "[]");
577 if (s && (const char *) s < atmark)
578 return 0;
579
580 /* Check for a [1.2.3.4] type of domain */
581 if (*atmark == '[')
582 {
583 unsigned char ipstr[16];
584 const char *bracket = strchr(atmark + 1, ']');
585 int len = bracket - (atmark + 1);
586 /* Valid IP addresses have no more than 15 characters */
587 if (len <= 15 && !bracket[1])
588 {
589 strncpy((char *) ipstr, atmark + 1, len);
590 ipstr[len] = 0;
591 /* Use pack_ip() to see if it's a valid address */
592 if (pack_ip((const char *) ipstr))
593 return 1;
594 }
595 }
596
597 /* Don't allow [] in domains except for the above case */
598 if (strpbrk(atmark, "[]"))
599 return 0;
600
601 /* Valid domain names cannot contain '@' so we just check valid_domain().
602 * Also prohibit domains without dots. */
603 return strchr(atmark, '.') && valid_domain(atmark);
604 }
605
606 /*************************************************************************/
607
608 /* Check whether the given string is a valid URL. A valid URL:
609 * - Contains neither control characters (\000-\037) nor spaces (\040).
610 * - Contains a series of letters followed by "://" followed by a valid
611 * domain name, possibly followed by a : and a numeric port number in
612 * the range 1-65535, followed either by a slash and possibly more text
613 * or by nothing.
614 */
615
616 int
617 valid_url(const char *str)
618 {
619 const unsigned char *s, *colon, *host;
620 static const char letters[] =
621 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
622 char domainbuf[DOMAIN_MAXLEN + 1];
623
624 for (s = (const unsigned char *) str; *s; s++)
625 {
626 if (*s <= '\040') // 040 == 0x20 == ' '
627 return 0;
628 }
629 s = (const unsigned char *) strstr(str, "://");
630 if (!s)
631 return 0; /* No "://" */
632 if (strspn(str, letters) != s - (const unsigned char *) str)
633 return 0; /* Protocol has non-alphabetic characters */
634 host = s + 3;
635 colon = (const unsigned char *) strchr((const char *) host, ':');
636 /* s will eventually point to the expected end of the host string */
637 s = host + strcspn((const char *) host, "/");
638 /* Make sure the port is valid if present. */
639 if (colon && colon < s)
640 {
641 int port = (int) atolsafe((const char *) colon + 1, 1, 65535);
642 if (port < 1)
643 return 0; /* Invalid port number or non-numeric characters */
644 s = colon;
645 }
646 /* The string from host through s-1 must be a valid domain name.
647 * Check length (must be >=1, <=DOMAIN_MAXLEN), then copy into
648 * temporary buffer and check. Also discard domain names without
649 * dots in them. */
650 if (s - host < 1 || s - host > DOMAIN_MAXLEN)
651 return 0;
652 memcpy(domainbuf, host, s - host);
653 domainbuf[s - host] = 0;
654 return strchr(domainbuf, '.') && valid_domain(domainbuf);
655 }
656
657 /*************************************************************************/
658
659 /* Check whether the given E-mail address is rejected by a RejectEmail
660 * configuration directive.
661 */
662
663 int
664 rejected_email(const char *email)
665 {
666 int i;
667
668 if (!email)
669 {
670 return 0;
671 }
672 ARRAY_FOREACH(i, RejectEmail)
673 {
674 if (match_wild_nocase(RejectEmail[i], email))
675 {
676 return 1;
677 }
678 }
679 return 0;
680 }
681
682 /*************************************************************************/
683 /*************************************************************************/
684
685 /* time_msec: Return the current time to millisecond resolution. */
686
687 uint32
688 time_msec(void)
689 {
690 #if HAVE_GETTIMEOFDAY
691 struct timeval tv;
692 gettimeofday(&tv, NULL);
693 return tv.tv_sec * 1000 + tv.tv_usec / 1000;
694 #else
695 return time(NULL) * 1000;
696 #endif
697 }
698
699 /*************************************************************************/
700
701 /* strtotime: Convert a string into a time_t. Essentially the same as
702 * strtol(), but assumes base 10 and returns a time_t.
703 */
704
705 time_t
706 strtotime(const char *str, char **endptr)
707 {
708 time_t t = 0;
709
710 while (*str >= '0' && *str <= '9')
711 {
712 if (t > MAX_TIME_T / 10 || MAX_TIME_T - t * 10 < *str - '0')
713 {
714 t = MAX_TIME_T;
715 errno = ERANGE;
716 }
717 else
718 {
719 t = t * 10 + *str - '0';
720 }
721 str++;
722 }
723 if (endptr)
724 *endptr = (char *) str;
725 return t;
726 }
727
728 /*************************************************************************/
729
730 /* dotime: Return the number of seconds corresponding to the given time
731 * string. If the given string does not represent a valid time,
732 * return -1.
733 *
734 * A time string is either a plain integer (representing a number
735 * of seconds), an integer followed by one of these characters:
736 * "s" (seconds), "m" (minutes), "h" (hours), or "d" (days), or a
737 * sequence of such integer-character pairs (without separators,
738 * e.g. "1h30m").
739 */
740
741 int
742 dotime(const char *s)
743 {
744 int amount;
745
746 amount = strtol(s, (char **) &s, 10);
747 if (*s)
748 {
749 char c = *s++;
750 int rest = dotime(s);
751 if (rest < 0)
752 return -1;
753 switch (c)
754 {
755 case 's':
756 return rest + amount;
757 case 'm':
758 return rest + amount * 60;
759 case 'h':
760 return rest + amount * 3600;
761 case 'd':
762 return rest + amount * 86400;
763 default:
764 return -1;
765 }
766 }
767 else
768 {
769 return amount;
770 }
771 }
772
773 /*************************************************************************/
774 /*************************************************************************/
775
776 /* Translate an IPv4 dotted-quad address into binary (4 bytes). Returns
777 * NULL if the given string is not in dotted-quad format or `ipaddr' is
778 * NULL. The returned buffer is static and will be overwritten on
779 * subsequent calls.
780 */
781
782 uint8 *
783 pack_ip(const char *ipaddr)
784 {
785 static uint8 ipbuf[4];
786 const char *s, *s2;
787 int i;
788 long tmp;
789
790 if (!ipaddr)
791 return NULL;
792 s = ipaddr;
793 for (i = 0; i < 4; i++)
794 {
795 if (i > 0 && *s++ != '.')
796 return NULL;
797 if (isspace(*s)) /* because strtol() will skip whitespace */
798 return NULL;
799 tmp = strtol(s, (char **) &s2, 10);
800 if (s2 == s || tmp < 0 || tmp > 255)
801 return NULL;
802 ipbuf[i] = (uint8) tmp;
803 s = s2;
804 }
805 if (*s)
806 return NULL;
807 return ipbuf;
808 }
809
810 /*************************************************************************/
811
812 /* Translate a 4-byte binary IPv4 to its dotted-quad (ASCII) representation.
813 * Always succeeds and always returns a null-terminated string <= 15
814 * characters in length, unless `ip' is NULL, in which case the function
815 * returns NULL. The returned buffer is static and will be overwritten on
816 * subsequent calls.
817 */
818
819 char *
820 unpack_ip(const uint8 * ip)
821 {
822 static char ipbuf[16];
823
824 if (!ip)
825 return NULL;
826 snprintf(ipbuf, sizeof(ipbuf), "%u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]);
827 return ipbuf;
828 }
829
830 /*************************************************************************/
831
832 /* Translate an IPv6 ASCII address into binary (16 bytes). Return NULL if
833 * the given string is not in IPv6 format or `ipaddr' is NULL. The
834 * returned buffer is static and will be overwritten on subsequent calls.
835 */
836
837 uint8 *
838 pack_ip6(const char *ipaddr)
839 {
840 static uint8 ipbuf[16];
841 int words[8];
842 int wordnum;
843 const char *s, *t;
844 int i;
845
846 if (!ipaddr)
847 return NULL;
848
849 /* Parse the address into 16-bit words */
850 s = ipaddr;
851 wordnum = 0;
852 if (*s == ':')
853 {
854 words[wordnum++] = 0;
855 s++;
856 }
857 while (*s)
858 {
859 if (wordnum >= 8)
860 {
861 /* too many words, abort */
862 return NULL;
863 }
864 if (*s == ':')
865 {
866 /* mark "::" with a -1 */
867 words[wordnum++] = -1;
868 s++;
869 }
870 else
871 {
872 words[wordnum++] = (int) strtol(s, (char **) &t, 16);
873 if (*t && *t != ':')
874 {
875 /* invalid syntax */
876 return NULL;
877 }
878 if (*t)
879 {
880 t++; /* skip past delimiter */
881 if (!*t)
882 {
883 /* trailing ":", convert to :0000 */
884 if (wordnum >= 8)
885 return NULL;
886 words[wordnum++] = 0;
887 }
888 }
889 s = t;
890 }
891 }
892
893 /* Expand "::" into as many zeros as needed, and make sure there
894 * aren't multiple occurrences of "::" */
895 for (i = 0; i < wordnum; i++)
896 {
897 if (words[i] == -1)
898 break;
899 }
900 if (i < wordnum)
901 { /* found a "::" */
902 int j, offset;
903 for (j = i + 1; j < wordnum; j++)
904 {
905 if (words[j] == -1)
906 {
907 /* multiple "::" */
908 return NULL;
909 }
910 }
911 offset = 8 - wordnum;
912 for (j = 7; j >= i; j--)
913 {
914 if (j - offset > i)
915 words[j] = words[j - offset];
916 else
917 words[j] = 0;
918 }
919 wordnum = 8;
920 }
921
922 /* Make sure we have exactly 8 words */
923 if (wordnum != 8)
924 return NULL;
925
926 /* Convert to binary and return */
927 for (i = 0; i < 8; i++)
928 {
929 ipbuf[i * 2] = words[i] >> 8;
930 ipbuf[i * 2 + 1] = words[i] & 255;
931 }
932 return ipbuf;
933 }
934
935 /*************************************************************************/
936
937 /* Translate a 16-byte binary IPv6 address to its ASCII representation.
938 * Always succeeds and always returns a null-terminated string <= 39
939 * characters in length, unless `ip' is NULL, in which case the function
940 * returns NULL. The returned buffer is static and will be overwritten on
941 * subsequent calls.
942 *
943 * The address string returned by this function will always use 4-digit
944 * hexadecimal numbers for each word in the address, but will omit all-zero
945 * words when possible.
946 */
947
948 char *
949 unpack_ip6(const uint8 * ip)
950 {
951 static char ipbuf[40];
952 char *out, *s;
953 int i;
954
955 if (!ip)
956 return NULL;
957
958 out = ipbuf;
959 for (i = 0; i < 8; i++)
960 {
961 /* Skip 0000 at beginning or end */
962 if ((i != 0 && i != 7) || ip[i * 2] || ip[i * 2 + 1])
963 {
964 out += snprintf(out, sizeof(ipbuf) - (out - ipbuf), "%02X%02X",
965 ip[i * 2], ip[i * 2 + 1]);
966 }
967 if (i != 7)
968 *out++ = ':';
969 }
970 if ((s = strstr(ipbuf, ":0000:")) != NULL)
971 {
972 /* Compress zeros */
973 memmove(s + 1, s + 5, strlen(s + 5) + 1);
974 s++;
975 while (strncmp(s, ":0000:", 6) == 0)
976 memmove(s, s + 5, strlen(s + 5) + 1);
977 }
978 return ipbuf;
979 }
980
981 /*************************************************************************/
982 /*************************************************************************/
983
984 /* Encode buffer `in' of size `insize' to buffer `out' of size `outsize'
985 * using base64, appending a trailing null. Returns the number of bytes
986 * required by the encoded string. (The amount of space needed to store
987 * the encoded string can be found with encode_base64(...,NULL,0).)
988 * Returns -1 if the input or output buffer is NULL (except when the
989 * corresponding size is zero) or if the input or output size is negative.
990 * If `out' is too small to hold the entire encoded string, it will contain
991 * the first `outsize'-1 bytes of the encoded string followed by a null.
992 */
993
994 static const char base64_chars[] =
995 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
996
997 int
998 encode_base64(const void *in, int insize, char *out, int outsize)
999 {
1000 int required = ((insize + 2) / 3 * 4) + 1;
1001 const uint8 *inp = in;
1002 int inpos, outpos;
1003
1004 if (insize < 0 || outsize < 0)
1005 return -1;
1006 if (outsize == 0)
1007 return required;
1008 if (!out)
1009 return -1;
1010 if (insize == 0)
1011 {
1012 /* outsize is at least 1 */
1013 *out = 0;
1014 return 1;
1015 }
1016 if (!in)
1017 return -1;
1018
1019 outsize--; /* leave room for trailing \0 */
1020
1021 /* Actually do the encoding */
1022 outpos = 0;
1023 for (inpos = 0; inpos < insize; inpos += 3)
1024 {
1025 uint8 i0, i1, i2;
1026 char o0, o1, o2, o3;
1027 i0 = inp[inpos];
1028 o0 = base64_chars[i0 >> 2];
1029 if (inpos + 1 < insize)
1030 {
1031 i1 = inp[inpos + 1];
1032 o1 = base64_chars[(i0 & 3) << 4 | i1 >> 4];
1033 if (inpos + 2 < insize)
1034 {
1035 i2 = inp[inpos + 2];
1036 o2 = base64_chars[(i1 & 15) << 2 | i2 >> 6];
1037 o3 = base64_chars[i2 & 63];
1038 }
1039 else
1040 {
1041 o2 = base64_chars[(i1 & 15) << 2];
1042 o3 = '=';
1043 }
1044 }
1045 else
1046 {
1047 o1 = base64_chars[(i0 & 3) << 4];
1048 o2 = '=';
1049 o3 = '=';
1050 }
1051 if (outpos < outsize)
1052 out[outpos++] = o0;
1053 if (outpos < outsize)
1054 out[outpos++] = o1;
1055 if (outpos < outsize)
1056 out[outpos++] = o2;
1057 if (outpos < outsize)
1058 out[outpos++] = o3;
1059 }
1060
1061 /* Terminate the string and return; outpos is constrained above to
1062 * still be within the buffer (remember that outsize was decremented
1063 * before the loop) */
1064 out[outpos] = 0;
1065 return required;
1066 }
1067
1068 /*************************************************************************/
1069
1070 /* Decode base64-encoded string `in' (null-terminated) to buffer `out' of
1071 * size `outsize'. Returns the number of bytes required by the decoded
1072 * data. (The amount of space needed to store the decoded data can be
1073 * found with decode_base64(...,NULL,0).) Returns -1 if the input string
1074 * is NULL, or if the output buffer is NULL (except when the size is zero)
1075 * or the size is negative. If `out' is too small to hold all of the
1076 * decoded data, it will contain the first `outsize' bytes.
1077 */
1078
1079 static const char base64_array[256] = {
1080 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x00 */
1081 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
1082 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
1083 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
1084 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 0x40 */
1085 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
1086 -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
1087 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
1088 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x80 */
1089 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
1090 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
1091 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
1092 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xC0 */
1093 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
1094 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
1095 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
1096 };
1097
1098 int
1099 decode_base64(const char *in, void *out, int outsize)
1100 {
1101 uint8 *outp = out;
1102 int outpos;
1103
1104 if (!in || outsize < 0)
1105 return -1;
1106
1107 outpos = 0;
1108 while (*in)
1109 {
1110 int i0, i1, i2, i3;
1111 i0 = *in++;
1112 if (*in)
1113 {
1114 i1 = *in++;
1115 if (*in)
1116 {
1117 i2 = *in++;
1118 if (*in)
1119 {
1120 i3 = *in++;
1121 }
1122 else
1123 {
1124 i3 = 0;
1125 }
1126 }
1127 else
1128 {
1129 i2 = 0;
1130 i3 = 0;
1131 }
1132 }
1133 else
1134 {
1135 i1 = 0;
1136 i2 = 0;
1137 i3 = 0;
1138 }
1139 i0 = base64_array[(int) i0 & 255];
1140 i1 = base64_array[(int) i1 & 255];
1141 i2 = base64_array[(int) i2 & 255];
1142 i3 = base64_array[(int) i3 & 255];
1143 if (i0 < 0 || i1 < 0)
1144 break;
1145 /* Only store if buffer space is available; increment outpos anyway
1146 * to keep track of total space required (for return value) */
1147 if (outpos < outsize)
1148 outp[outpos] = i0 << 2 | i1 >> 4;
1149 outpos++;
1150 if (i2 < 0)
1151 break;
1152 if (outpos < outsize)
1153 outp[outpos] = (i1 & 15) << 4 | i2 >> 2;
1154 outpos++;
1155 if (i3 < 0)
1156 break;
1157 if (outpos < outsize)
1158 outp[outpos] = (i2 & 3) << 6 | i3;
1159 outpos++;
1160 }
1161
1162 return outpos;
1163 }
1164
1165 /*************************************************************************/
1166 /*************************************************************************/
1167
1168 /* Process a string containing a number/range list in the form
1169 * "n1[-n2][,n3[-n4]]...", calling a caller-specified routine for each
1170 * number in the list. If the callback returns -1, stop immediately.
1171 * Returns the sum of all nonnegative return values from the callback.
1172 * If `count_ret' is non-NULL, *count_ret will be set to the total number
1173 * of times the callback was called.
1174 *
1175 * The number list will never contain duplicates and will always be sorted
1176 * ascendingly. This means the callback routines don't have to worry about
1177 * being called twice for the same index. -TheShadow
1178 * Also, the only values accepted are 0-65536 (inclusive), to avoid someone
1179 * giving us 0-2^31 and causing freezes or out-of-memory. Numbers outside
1180 * this range will be ignored.
1181 *
1182 * The callback should be of type range_callback_t, which is defined as:
1183 * int (*range_callback_t)(User *u, int num, va_list args)
1184 */
1185
1186 int
1187 process_numlist(const char *numstr, int *count_ret,
1188 range_callback_t callback, ...)
1189 {
1190 int n1, n2, min, max, i;
1191 int retval = 0;
1192 int numcount = 0;
1193 va_list args;
1194 static uint8 numflag[65536 / 8 + 1]; /* 1 bit per index 0-65536 inclusive */
1195
1196 memset(numflag, 0, sizeof(numflag));
1197 min = 65536;
1198 max = 0;
1199 va_start(args, callback);
1200
1201 /* This algorithm ignores invalid characters, ignores a dash
1202 * when it precedes a comma, and ignores everything from the
1203 * end of a valid number or range to the next comma or null.
1204 */
1205 while (*numstr)
1206 {
1207 n1 = n2 = strtol(numstr, (char **) &numstr, 10);
1208 numstr += strcspn(numstr, "0123456789,-");
1209 if (*numstr == '-')
1210 {
1211 numstr++;
1212 numstr += strcspn(numstr, "0123456789,");
1213 if (isdigit(*numstr))
1214 {
1215 n2 = strtol(numstr, (char **) &numstr, 10);
1216 numstr += strcspn(numstr, "0123456789,-");
1217 }
1218 }
1219 if (n1 < 0)
1220 n1 = 0;
1221 if (n2 > 65536)
1222 n2 = 65536;
1223 if (n1 < min)
1224 min = n1;
1225 if (n2 > max)
1226 max = n2;
1227 while (n1 <= n2)
1228 {
1229 if ((n1 & 7) == 0 && n1 + 7 <= n2)
1230 {
1231 /* Set a whole byte at once */
1232 numflag[n1 >> 3] = 0xFF;
1233 n1 += 8;
1234 }
1235 else
1236 {
1237 /* Set just a single bit */
1238 numflag[n1 >> 3] |= 1 << (n1 & 7);
1239 n1++;
1240 }
1241 }
1242 numstr += strcspn(numstr, ",");
1243 if (*numstr)
1244 numstr++;
1245 }
1246
1247 /* Now call the callback routine for each index. */
1248 numcount = 0;
1249 for (i = min; i <= max; i++)
1250 {
1251 va_list args_copy;
1252 int res;
1253 if (!(numflag[i >> 3] & (1 << (i & 7))))
1254 continue;
1255 numcount++;
1256 va_copy(args_copy, args);
1257 res = callback(i, args_copy);
1258 va_end(args_copy);
1259 if (res < 0)
1260 break;
1261 retval += res;
1262 }
1263
1264 va_end(args);
1265 if (count_ret)
1266 *count_ret = numcount;
1267 return retval;
1268 }
1269
1270 /*************************************************************************/
1271 /*************************************************************************/
1272
1273 /* atolsafe: Convert a string in base 10 to a long int, ensuring that the
1274 * string contains no invalid characters and that the result is
1275 * within a specified range (>=min && <=max). Returns min-1 on
1276 * error. errno is set to EINVAL if the string is not a valid
1277 * number, ERANGE if the value is outside the range [min..max],
1278 * and left unmodified otherwise.
1279 */
1280
1281 long
1282 atolsafe(const char *s, long min, long max)
1283 {
1284 int errno_save = errno;
1285 long v;
1286
1287 if (!s || !*s)
1288 return min - 1;
1289 errno = 0;
1290 v = strtol(s, (char **) &s, 10);
1291 if (*s)
1292 {
1293 errno = EINVAL;
1294 return min - 1;
1295 }
1296 if (errno == ERANGE || v < min || v > max)
1297 {
1298 errno = ERANGE;
1299 return min - 1;
1300 }
1301 errno = errno_save;
1302 return v;
1303 }
1304
1305 /*************************************************************************/
1306
1307 /*
1308 * Local variables:
1309 * c-file-style: "stroustrup"
1310 * c-file-offsets: ((case-label . *) (statement-case-intro . *))
1311 * indent-tabs-mode: nil
1312 * End:
1313 *
1314 * vim: expandtab shiftwidth=4:
1315 */