1 /*
2 * atheme-services: A collection of minimalist IRC services
3 * match.c: Casemapping and matching functions.
4 *
5 * Copyright (c) 2005-2007 Atheme Project (http://www.atheme.org)
6 *
7 * Permission to use, copy, modify, and/or distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
12 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
13 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
14 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
15 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
16 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
17 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
18 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
19 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
20 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
21 * POSSIBILITY OF SUCH DAMAGE.
22 */
23
24 #include "atheme.h"
25
26 #include <regex.h>
27 #ifdef HAVE_PCRE
28 #include <pcre.h>
29 #endif
30
31 #define BadPtr(x) (!(x) || (*(x) == '\0'))
32
33 int match_mapping = MATCH_RFC1459;
34
35 const unsigned char ToLowerTab[] = {
36 0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa,
37 0xb, 0xc, 0xd, 0xe, 0xf, 0x10, 0x11, 0x12, 0x13, 0x14,
38 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d,
39 0x1e, 0x1f,
40 ' ', '!', '"', '#', '$', '%', '&', 0x27, '(', ')',
41 '*', '+', ',', '-', '.', '/',
42 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
43 ':', ';', '<', '=', '>', '?',
44 '@', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i',
45 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
46 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~',
47 '_',
48 '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i',
49 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
50 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~',
51 0x7f,
52 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
53 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
54 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
55 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
56 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9,
57 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
58 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9,
59 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
60 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9,
61 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
62 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9,
63 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
64 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9,
65 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
66 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9,
67 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
68 };
69
70 const unsigned char ToUpperTab[] = {
71 0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa,
72 0xb, 0xc, 0xd, 0xe, 0xf, 0x10, 0x11, 0x12, 0x13, 0x14,
73 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d,
74 0x1e, 0x1f,
75 ' ', '!', '"', '#', '$', '%', '&', 0x27, '(', ')',
76 '*', '+', ',', '-', '.', '/',
77 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
78 ':', ';', '<', '=', '>', '?',
79 '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I',
80 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
81 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^',
82 0x5f,
83 '`', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I',
84 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
85 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^',
86 0x7f,
87 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
88 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
89 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
90 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
91 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9,
92 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
93 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9,
94 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
95 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9,
96 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
97 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9,
98 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
99 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9,
100 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
101 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9,
102 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
103 };
104
ToLower(int c)105 int ToLower(int c)
106 {
107 if (match_mapping == MATCH_ASCII)
108 return tolower((unsigned char)c);
109 /* else */
110 return (ToLowerTab[(unsigned char)(c)]);
111 }
112
ToUpper(int c)113 int ToUpper(int c)
114 {
115 if (match_mapping == MATCH_ASCII)
116 return toupper((unsigned char)c);
117 /* else */
118 return (ToUpperTab[(unsigned char)(c)]);
119 }
120
set_match_mapping(int type)121 void set_match_mapping(int type)
122 {
123 match_mapping = type;
124 }
125
126 #define MAX_ITERATIONS 512
127 /*
128 ** Compare if a given string (name) matches the given
129 ** mask (which can contain wild cards: '*' - match any
130 ** number of chars, '?' - match any single character.
131 **
132 ** return 0, if match
133 ** 1, if no match
134 */
135
136 /*
137 ** match()
138 ** Iterative matching function, rather than recursive.
139 ** Written by Douglas A Lewis (dalewis@acsu.buffalo.edu)
140 */
141
match(const char * mask,const char * name)142 int match(const char *mask, const char *name)
143 {
144 const unsigned char *m = (const unsigned char *)mask, *n = (const unsigned char *)name;
145 const char *ma = mask, *na = name;
146 int wild = 0, q = 0, calls = 0;
147
148 if (!mask || !name)
149 return 1;
150
151 /* if the mask is "*", it matches everything */
152 if ((*m == '*') && (*(m + 1) == '\0'))
153 return 0;
154
155 while (1)
156 {
157 #ifdef MAX_ITERATIONS
158 if (calls++ > MAX_ITERATIONS)
159 break;
160 #endif
161
162 if (*m == '*')
163 {
164 while (*m == '*')
165 m++;
166 wild = 1;
167 ma = (const char *)m;
168 na = (const char *)n;
169 }
170
171 if (!*m)
172 {
173 if (!*n)
174 return 0;
175 for (m--; (m > (const unsigned char *) mask) && (*m == '?' || *m == '&' || *m == '#'); m--)
176 ;
177 if ((m > (const unsigned char *) mask) && (*m == '*') && (m[-1] != '\\'))
178 return 0;
179 if (!wild)
180 return 1;
181 m = (const unsigned char *) ma;
182 n = (const unsigned char *)++ na;
183 }
184 else if (!*n)
185 return 1;
186 if ((*m == '\\') && ((m[1] == '*') || (m[1] == '?') || (m[1] == '&') || (m[1] == '#') || (m[1] == '%')))
187 {
188 m++;
189 q = 1;
190 }
191 else
192 q = 0;
193
194 if ((ToLower(*m) != ToLower(*n)) && (((*m != '?') && !(*m == '&' && IsAlpha(*n)) && !(*m == '#' && IsDigit(*n)) && !(*m == '%' && IsNon(*n))) || q))
195 {
196 if (!wild)
197 return 1;
198 m = (const unsigned char *) ma;
199 n = (const unsigned char *)++ na;
200 }
201 else
202 {
203 if (*m)
204 m++;
205 if (*n)
206 n++;
207 }
208 }
209
210 return 1;
211 }
212
213
214 /*
215 ** collapse a pattern string into minimal components.
216 ** This particular version is "in place", so that it changes the pattern
217 ** which is to be reduced to a "minimal" size.
218 */
collapse(char * pattern)219 char *collapse(char *pattern)
220 {
221 char *s = pattern, *s1, *t;
222
223 if (BadPtr(pattern))
224 return pattern;
225 /*
226 * Collapse all \** into \*, \*[?]+\** into \*[?]+
227 */
228 for (; *s; s++)
229 if (*s == '\\')
230 if (!*(s + 1))
231 break;
232 else
233 s++;
234 else if (*s == '*')
235 {
236 if (*(t = s1 = s + 1) == '*')
237 while (*t == '*')
238 t++;
239 else if (*t == '?')
240 for (t++, s1++; *t == '*' || *t == '?'; t++)
241 if (*t == '?')
242 *s1++ = *t;
243 while ((*s1++ = *t++))
244 ;
245 }
246 return pattern;
247 }
248
249 /*
250 ** Case insensitive comparison of two null terminated strings.
251 **
252 ** returns 0, if s1 equal to s2
253 ** <0, if s1 lexicographically less than s2
254 ** >0, if s1 lexicographically greater than s2
255 */
irccasecmp(const char * s1,const char * s2)256 int irccasecmp(const char *s1, const char *s2)
257 {
258 const unsigned char *str1 = (const unsigned char *)s1;
259 const unsigned char *str2 = (const unsigned char *)s2;
260 int res;
261
262 if (!s1 || !s2)
263 return -1;
264
265 if (match_mapping == MATCH_ASCII)
266 return strcasecmp(s1, s2);
267
268 while ((res = ToUpper(*str1) - ToUpper(*str2)) == 0)
269 {
270 if (*str1 == '\0')
271 return 0;
272 str1++;
273 str2++;
274 }
275 return (res);
276 }
277
ircncasecmp(const char * str1,const char * str2,size_t n)278 int ircncasecmp(const char *str1, const char *str2, size_t n)
279 {
280 const unsigned char *s1 = (const unsigned char *)str1;
281 const unsigned char *s2 = (const unsigned char *)str2;
282 int res;
283
284 if (match_mapping == MATCH_ASCII)
285 return strncasecmp(str1, str2, n);
286
287 while ((res = ToUpper(*s1) - ToUpper(*s2)) == 0)
288 {
289 s1++;
290 s2++;
291 n--;
292 if (n == 0 || (*s1 == '\0' && *s2 == '\0'))
293 return 0;
294 }
295 return (res);
296 }
297
irccasecanon(char * str)298 void irccasecanon(char *str)
299 {
300 while (*str)
301 {
302 *str = ToUpper(*str);
303 str++;
304 }
305 return;
306 }
307
strcasecanon(char * str)308 void strcasecanon(char *str)
309 {
310 while (*str)
311 {
312 *str = toupper((unsigned char)*str);
313 str++;
314 }
315 return;
316 }
317
noopcanon(char * str)318 void noopcanon(char *str)
319 {
320 return;
321 }
322
323 const unsigned int charattrs[] = {
324 /* 0 */ 0,
325 /* 1 */ 0,
326 /* 2 */ 0,
327 /* 3 */ 0,
328 /* 4 */ 0,
329 /* 5 */ 0,
330 /* 6 */ 0,
331 /* 7 BEL */ 0,
332 /* 8 \b */ 0,
333 /* 9 \t */ 0,
334 /* 10 \n */ 0,
335 /* 11 \v */ 0,
336 /* 12 \f */ 0,
337 /* 13 \r */ 0,
338 /* 14 */ 0,
339 /* 15 */ 0,
340 /* 16 */ 0,
341 /* 17 */ 0,
342 /* 18 */ 0,
343 /* 19 */ 0,
344 /* 20 */ 0,
345 /* 21 */ 0,
346 /* 22 */ 0,
347 /* 23 */ 0,
348 /* 24 */ 0,
349 /* 25 */ 0,
350 /* 26 */ 0,
351 /* 27 */ 0,
352 /* 28 */ 0,
353 /* 29 */ 0,
354 /* 30 */ 0,
355 /* 31 */ 0,
356 /* SP */ 0,
357 /* ! */ 0,
358 /* " */ 0,
359 /* # */ 0,
360 /* $ */ 0,
361 /* % */ 0,
362 /* & */ 0,
363 /* ' */ 0,
364 /* ( */ 0,
365 /* ) */ 0,
366 /* * */ 0,
367 /* + */ 0,
368 /* , */ 0,
369 /* - */ C_NICK | C_USER,
370 /* . */ C_USER,
371 /* / */ 0,
372 /* 0 */ C_DIGIT | C_NICK | C_USER,
373 /* 1 */ C_DIGIT | C_NICK | C_USER,
374 /* 2 */ C_DIGIT | C_NICK | C_USER,
375 /* 3 */ C_DIGIT | C_NICK | C_USER,
376 /* 4 */ C_DIGIT | C_NICK | C_USER,
377 /* 5 */ C_DIGIT | C_NICK | C_USER,
378 /* 6 */ C_DIGIT | C_NICK | C_USER,
379 /* 7 */ C_DIGIT | C_NICK | C_USER,
380 /* 8 */ C_DIGIT | C_NICK | C_USER,
381 /* 9 */ C_DIGIT | C_NICK | C_USER,
382 /* : */ 0,
383 /* ; */ 0,
384 /* < */ 0,
385 /* = */ 0,
386 /* > */ 0,
387 /* ? */ 0,
388 /* @ */ 0,
389 /* A */ C_ALPHA | C_NICK | C_USER,
390 /* B */ C_ALPHA | C_NICK | C_USER,
391 /* C */ C_ALPHA | C_NICK | C_USER,
392 /* D */ C_ALPHA | C_NICK | C_USER,
393 /* E */ C_ALPHA | C_NICK | C_USER,
394 /* F */ C_ALPHA | C_NICK | C_USER,
395 /* G */ C_ALPHA | C_NICK | C_USER,
396 /* H */ C_ALPHA | C_NICK | C_USER,
397 /* I */ C_ALPHA | C_NICK | C_USER,
398 /* J */ C_ALPHA | C_NICK | C_USER,
399 /* K */ C_ALPHA | C_NICK | C_USER,
400 /* L */ C_ALPHA | C_NICK | C_USER,
401 /* M */ C_ALPHA | C_NICK | C_USER,
402 /* N */ C_ALPHA | C_NICK | C_USER,
403 /* O */ C_ALPHA | C_NICK | C_USER,
404 /* P */ C_ALPHA | C_NICK | C_USER,
405 /* Q */ C_ALPHA | C_NICK | C_USER,
406 /* R */ C_ALPHA | C_NICK | C_USER,
407 /* S */ C_ALPHA | C_NICK | C_USER,
408 /* T */ C_ALPHA | C_NICK | C_USER,
409 /* U */ C_ALPHA | C_NICK | C_USER,
410 /* V */ C_ALPHA | C_NICK | C_USER,
411 /* W */ C_ALPHA | C_NICK | C_USER,
412 /* X */ C_ALPHA | C_NICK | C_USER,
413 /* Y */ C_ALPHA | C_NICK | C_USER,
414 /* Z */ C_ALPHA | C_NICK | C_USER,
415 /* [ */ C_NICK | C_USER,
416 /* \ */ C_NICK | C_USER,
417 /* ] */ C_NICK | C_USER,
418 /* ^ */ C_NICK | C_USER,
419 /* _ */ C_NICK | C_USER,
420 /* ` */ C_NICK | C_USER,
421 /* a */ C_ALPHA | C_NICK | C_USER,
422 /* b */ C_ALPHA | C_NICK | C_USER,
423 /* c */ C_ALPHA | C_NICK | C_USER,
424 /* d */ C_ALPHA | C_NICK | C_USER,
425 /* e */ C_ALPHA | C_NICK | C_USER,
426 /* f */ C_ALPHA | C_NICK | C_USER,
427 /* g */ C_ALPHA | C_NICK | C_USER,
428 /* h */ C_ALPHA | C_NICK | C_USER,
429 /* i */ C_ALPHA | C_NICK | C_USER,
430 /* j */ C_ALPHA | C_NICK | C_USER,
431 /* k */ C_ALPHA | C_NICK | C_USER,
432 /* l */ C_ALPHA | C_NICK | C_USER,
433 /* m */ C_ALPHA | C_NICK | C_USER,
434 /* n */ C_ALPHA | C_NICK | C_USER,
435 /* o */ C_ALPHA | C_NICK | C_USER,
436 /* p */ C_ALPHA | C_NICK | C_USER,
437 /* q */ C_ALPHA | C_NICK | C_USER,
438 /* r */ C_ALPHA | C_NICK | C_USER,
439 /* s */ C_ALPHA | C_NICK | C_USER,
440 /* t */ C_ALPHA | C_NICK | C_USER,
441 /* u */ C_ALPHA | C_NICK | C_USER,
442 /* v */ C_ALPHA | C_NICK | C_USER,
443 /* w */ C_ALPHA | C_NICK | C_USER,
444 /* x */ C_ALPHA | C_NICK | C_USER,
445 /* y */ C_ALPHA | C_NICK | C_USER,
446 /* z */ C_ALPHA | C_NICK | C_USER,
447 /* { */ C_NICK | C_USER,
448 /* | */ C_NICK | C_USER,
449 /* } */ C_NICK | C_USER,
450 /* ~ */ C_USER,
451 /* del */ 0,
452 /* 0x80 */ 0,
453 /* 0x81 */ 0,
454 /* 0x82 */ 0,
455 /* 0x83 */ 0,
456 /* 0x84 */ 0,
457 /* 0x85 */ 0,
458 /* 0x86 */ 0,
459 /* 0x87 */ 0,
460 /* 0x88 */ 0,
461 /* 0x89 */ 0,
462 /* 0x8A */ 0,
463 /* 0x8B */ 0,
464 /* 0x8C */ 0,
465 /* 0x8D */ 0,
466 /* 0x8E */ 0,
467 /* 0x8F */ 0,
468 /* 0x90 */ 0,
469 /* 0x91 */ 0,
470 /* 0x92 */ 0,
471 /* 0x93 */ 0,
472 /* 0x94 */ 0,
473 /* 0x95 */ 0,
474 /* 0x96 */ 0,
475 /* 0x97 */ 0,
476 /* 0x98 */ 0,
477 /* 0x99 */ 0,
478 /* 0x9A */ 0,
479 /* 0x9B */ 0,
480 /* 0x9C */ 0,
481 /* 0x9D */ 0,
482 /* 0x9E */ 0,
483 /* 0x9F */ 0,
484 /* 0xA0 */ 0,
485 /* 0xA1 */ 0,
486 /* 0xA2 */ 0,
487 /* 0xA3 */ 0,
488 /* 0xA4 */ 0,
489 /* 0xA5 */ 0,
490 /* 0xA6 */ 0,
491 /* 0xA7 */ 0,
492 /* 0xA8 */ 0,
493 /* 0xA9 */ 0,
494 /* 0xAA */ 0,
495 /* 0xAB */ 0,
496 /* 0xAC */ 0,
497 /* 0xAD */ 0,
498 /* 0xAE */ 0,
499 /* 0xAF */ 0,
500 /* 0xB0 */ 0,
501 /* 0xB1 */ 0,
502 /* 0xB2 */ 0,
503 /* 0xB3 */ 0,
504 /* 0xB4 */ 0,
505 /* 0xB5 */ 0,
506 /* 0xB6 */ 0,
507 /* 0xB7 */ 0,
508 /* 0xB8 */ 0,
509 /* 0xB9 */ 0,
510 /* 0xBA */ 0,
511 /* 0xBB */ 0,
512 /* 0xBC */ 0,
513 /* 0xBD */ 0,
514 /* 0xBE */ 0,
515 /* 0xBF */ 0,
516 /* 0xC0 */ 0,
517 /* 0xC1 */ 0,
518 /* 0xC2 */ 0,
519 /* 0xC3 */ 0,
520 /* 0xC4 */ 0,
521 /* 0xC5 */ 0,
522 /* 0xC6 */ 0,
523 /* 0xC7 */ 0,
524 /* 0xC8 */ 0,
525 /* 0xC9 */ 0,
526 /* 0xCA */ 0,
527 /* 0xCB */ 0,
528 /* 0xCC */ 0,
529 /* 0xCD */ 0,
530 /* 0xCE */ 0,
531 /* 0xCF */ 0,
532 /* 0xD0 */ 0,
533 /* 0xD1 */ 0,
534 /* 0xD2 */ 0,
535 /* 0xD3 */ 0,
536 /* 0xD4 */ 0,
537 /* 0xD5 */ 0,
538 /* 0xD6 */ 0,
539 /* 0xD7 */ 0,
540 /* 0xD8 */ 0,
541 /* 0xD9 */ 0,
542 /* 0xDA */ 0,
543 /* 0xDB */ 0,
544 /* 0xDC */ 0,
545 /* 0xDD */ 0,
546 /* 0xDE */ 0,
547 /* 0xDF */ 0,
548 /* 0xE0 */ 0,
549 /* 0xE1 */ 0,
550 /* 0xE2 */ 0,
551 /* 0xE3 */ 0,
552 /* 0xE4 */ 0,
553 /* 0xE5 */ 0,
554 /* 0xE6 */ 0,
555 /* 0xE7 */ 0,
556 /* 0xE8 */ 0,
557 /* 0xE9 */ 0,
558 /* 0xEA */ 0,
559 /* 0xEB */ 0,
560 /* 0xEC */ 0,
561 /* 0xED */ 0,
562 /* 0xEE */ 0,
563 /* 0xEF */ 0,
564 /* 0xF0 */ 0,
565 /* 0xF1 */ 0,
566 /* 0xF2 */ 0,
567 /* 0xF3 */ 0,
568 /* 0xF4 */ 0,
569 /* 0xF5 */ 0,
570 /* 0xF6 */ 0,
571 /* 0xF7 */ 0,
572 /* 0xF8 */ 0,
573 /* 0xF9 */ 0,
574 /* 0xFA */ 0,
575 /* 0xFB */ 0,
576 /* 0xFC */ 0,
577 /* 0xFD */ 0,
578 /* 0xFE */ 0,
579 /* 0xFF */ 0,
580 };
581
582 enum atheme_regex_type
583 {
584 at_posix = 1,
585 at_pcre = 2
586 };
587
588 struct atheme_regex_
589 {
590 enum atheme_regex_type type;
591 union
592 {
593 regex_t posix;
594 #ifdef HAVE_PCRE
595 pcre *pcre;
596 #endif
597 } un;
598 };
599
600 /*
601 * regex_compile()
602 * Compile a regex of `pattern' and return it.
603 */
regex_create(char * pattern,int flags)604 atheme_regex_t *regex_create(char *pattern, int flags)
605 {
606 static char errmsg[BUFSIZE];
607 int errnum;
608 atheme_regex_t *preg;
609
610 if (pattern == NULL)
611 {
612 return NULL;
613 }
614
615 preg = smalloc(sizeof(atheme_regex_t));
616 if (flags & AREGEX_PCRE)
617 {
618 #ifdef HAVE_PCRE
619 const char *errptr;
620 int erroffset;
621
622 preg->un.pcre = pcre_compile(pattern, (flags & AREGEX_ICASE ? PCRE_CASELESS : 0) | PCRE_NO_AUTO_CAPTURE, &errptr, &erroffset, NULL);
623 if (preg->un.pcre == NULL)
624 {
625 slog(LG_ERROR, "regex_match(): %s at offset %d in %s",
626 errptr, erroffset, pattern);
627 free(preg);
628 return NULL;
629 }
630 preg->type = at_pcre;
631 #else
632 slog(LG_ERROR, "regex_match(): PCRE support is not compiled in");
633 free(preg);
634 return NULL;
635 #endif
636 }
637 else
638 {
639 errnum = regcomp(&preg->un.posix, pattern, (flags & AREGEX_ICASE ? REG_ICASE : 0) | REG_EXTENDED);
640
641 if (errnum != 0)
642 {
643 regerror(errnum, &preg->un.posix, errmsg, BUFSIZE);
644 slog(LG_ERROR, "regex_match(): %s in %s",
645 errmsg, pattern);
646 regfree(&preg->un.posix);
647 free(preg);
648 return NULL;
649 }
650 preg->type = at_posix;
651 }
652
653 return preg;
654 }
655
regex_extract(char * pattern,char ** pend,int * pflags)656 char *regex_extract(char *pattern, char **pend, int *pflags)
657 {
658 char c, *p, *p2;
659 bool backslash = false;
660
661 c = *pattern;
662 if (isalnum((unsigned char)c) || isspace((unsigned char)c) || c == '\\')
663 return NULL;
664 p = pattern + 1;
665 while (*p != c || backslash)
666 {
667 if (*p == '\0')
668 return NULL;
669 if (backslash || *p == '\\')
670 backslash = !backslash;
671 p++;
672 }
673 p2 = p;
674 p++;
675 *pflags = 0;
676 while (*p != '\0' && *p != ' ')
677 {
678 if (*p == 'i')
679 *pflags |= AREGEX_ICASE;
680 else if (*p == 'p')
681 *pflags |= AREGEX_PCRE;
682 else if (*p == 'K')
683 *pflags |= AREGEX_KLINE;
684 else if (!isalnum((unsigned char)*p))
685 return NULL;
686 p++;
687 }
688 *pend = p;
689 *p2 = '\0';
690 return pattern + 1;
691 }
692
693 /*
694 * regex_match()
695 * Internal wrapper API for regex matching.
696 * `preg' is the regex to check with, `string' needs to be checked against.
697 * Returns `true' on match, `false' else.
698 */
regex_match(atheme_regex_t * preg,char * string)699 bool regex_match(atheme_regex_t *preg, char *string)
700 {
701 if (preg == NULL || string == NULL)
702 {
703 slog(LG_ERROR, "regex_match(): we were given NULL string or pattern, bad!");
704 return false;
705 }
706
707 switch (preg->type)
708 {
709 case at_posix:
710 return regexec(&preg->un.posix, string, 0, NULL, 0) == 0;
711 #ifdef HAVE_PCRE
712 case at_pcre:
713 return pcre_exec(preg->un.pcre, NULL, string, strlen(string), 0, 0, NULL, 0) >= 0;
714 #endif
715 default:
716 slog(LG_ERROR, "regex_match(): we were given a pattern of unknown type %d, bad!", preg->type);
717 return false;
718 }
719 }
720
721 /*
722 * regex_destroy()
723 * Perform cleanup with regex `preg', free associated memory.
724 */
regex_destroy(atheme_regex_t * preg)725 bool regex_destroy(atheme_regex_t *preg)
726 {
727 switch (preg->type)
728 {
729 case at_posix:
730 regfree(&preg->un.posix);
731 break;
732 #ifdef HAVE_PCRE
733 case at_pcre:
734 pcre_free(preg->un.pcre);
735 break;
736 #endif
737 default:
738 slog(LG_ERROR, "regex_destroy(): we were given a pattern of unknown type %d, bad!", preg->type);
739 break;
740 }
741 free(preg);
742 return true;
743 }
744
745 /* vim:cinoptions=>s,e0,n0,f0,{0,}0,^0,=s,ps,t0,c3,+s,(2s,us,)20,*30,gs,hs
746 * vim:ts=8
747 * vim:sw=8
748 * vim:noexpandtab
749 */
750