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