]> jfr.im git - irc/evilnet/x3.git/blame - src/tools.c
Couple of srvx updates.
[irc/evilnet/x3.git] / src / tools.c
CommitLineData
d76ed9a9 1/* tools.c - miscellaneous utility functions
2 * Copyright 2000-2004 srvx Development Team
3 *
83ff05c3 4 * This file is part of x3.
d76ed9a9 5 *
d0f04f71 6 * x3 is free software; you can redistribute it and/or modify
d76ed9a9 7 * it under the terms of the GNU General Public License as published by
348683aa 8 * the Free Software Foundation; either version 3 of the License, or
d76ed9a9 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 srvx; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
19 */
20
21#include "helpfile.h"
22#include "log.h"
23#include "nickserv.h"
24#include "recdb.h"
25
26#ifdef HAVE_NETDB_H
27#include <netdb.h>
28#endif
29#ifdef HAVE_SYS_SOCKET_H
30#include <sys/socket.h>
31#endif
32#ifdef HAVE_ARPA_INET_H
33#include <arpa/inet.h>
34#endif
35
36#define NUMNICKLOG 6
37#define NUMNICKBASE (1 << NUMNICKLOG)
38#define NUMNICKMASK (NUMNICKBASE - 1)
39
40/* Yes, P10's encoding here is almost-but-not-quite MIME Base64. Yay
41 * for gratuitous incompatibilities. */
42static const char convert2y[256] = {
43 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
44 'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
45 'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
46 'w','x','y','z','0','1','2','3','4','5','6','7','8','9','[',']'
47};
48
49static const unsigned char convert2n[256] = {
50 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
51 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
52 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2f61d1d7 53 52,53,54,55,56,57,58,59,60,61, 0, 0, 0, 0, 0, 0,
d76ed9a9 54 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,
55 15,16,17,18,19,20,21,22,23,24,25,62, 0,63, 0, 0,
56 0,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,
57 41,42,43,44,45,46,47,48,49,50,51, 0, 0, 0, 0, 0
58};
59
2f61d1d7 60static const unsigned char ctype[256] = {
61 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
62 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
63 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
64 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0,
65 0,10,11,12,13,14,15, 0, 0, 0, 0, 0, 0, 0, 0, 0,
66 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
67 0,10,11,12,13,14,15, 0, 0, 0, 0, 0, 0, 0, 0, 0,
68};
69
d76ed9a9 70unsigned long int
71base64toint(const char* s, int count)
72{
73 unsigned int i = 0;
74 while (*s && count) {
75 i = (i << NUMNICKLOG) + convert2n[(unsigned char)*s++];
76 count--;
77 }
78 return i;
79}
80
81const char* inttobase64(char* buf, unsigned int v, unsigned int count)
82{
83 buf[count] = '\0';
84 while (count > 0) {
85 buf[--count] = convert2y[(unsigned char)(v & NUMNICKMASK)];
86 v >>= NUMNICKLOG;
87 }
88 return buf;
89}
90
2f61d1d7 91unsigned int
92irc_ntop(char *output, unsigned int out_size, const irc_in_addr_t *addr)
93{
94 static const char hexdigits[] = "0123456789abcdef";
95 unsigned int pos;
96
97 assert(output);
98 assert(addr);
99
100 if (irc_in_addr_is_ipv4(*addr)) {
101 unsigned int ip4;
102
103 ip4 = (ntohs(addr->in6[6]) << 16) | ntohs(addr->in6[7]);
104 pos = snprintf(output, out_size, "%u.%u.%u.%u", (ip4 >> 24), (ip4 >> 16) & 255, (ip4 >> 8) & 255, ip4 & 255);
105 } else {
106 unsigned int part, max_start, max_zeros, curr_zeros, ii;
107
108 /* Find longest run of zeros. */
109 for (max_start = max_zeros = curr_zeros = ii = 0; ii < 8; ++ii) {
110 if (!addr->in6[ii])
111 curr_zeros++;
112 else if (curr_zeros > max_zeros) {
113 max_start = ii - curr_zeros;
114 max_zeros = curr_zeros;
115 curr_zeros = 0;
116 }
117 }
118 if (curr_zeros > max_zeros) {
119 max_start = ii - curr_zeros;
120 max_zeros = curr_zeros;
121 }
122
123 /* Print out address. */
124#define APPEND(CH) do { if (pos < out_size) output[pos] = (CH); pos++; } while (0)
125 for (pos = 0, ii = 0; ii < 8; ++ii) {
126 if ((max_zeros > 0) && (ii == max_start)) {
127 if (ii == 0)
128 APPEND(':');
129 APPEND(':');
130 ii += max_zeros - 1;
131 continue;
132 }
133 part = ntohs(addr->in6[ii]);
134 if (part >= 0x1000)
135 APPEND(hexdigits[part >> 12]);
136 if (part >= 0x100)
137 APPEND(hexdigits[(part >> 8) & 15]);
138 if (part >= 0x10)
139 APPEND(hexdigits[(part >> 4) & 15]);
140 APPEND(hexdigits[part & 15]);
141 if (ii < 7)
142 APPEND(':');
143 }
144#undef APPEND
145 output[pos < out_size ? pos : out_size - 1] = '\0';
146 }
147
148 return pos;
149}
150
151unsigned int
152irc_ntop_mask(char *output, unsigned int out_size, const irc_in_addr_t *addr, unsigned char bits)
153{
154 char base_addr[IRC_NTOP_MAX_SIZE];
155 int len;
156
157 if (bits >= 128)
158 return irc_ntop(output, out_size, addr);
159 if (!irc_ntop(base_addr, sizeof(base_addr), addr))
160 return 0;
161 len = snprintf(output, out_size, "%s/%d", base_addr, bits);
162 if ((unsigned int)len >= out_size)
163 return 0;
164 return len;
165}
166
167static unsigned int
168irc_pton_ip4(const char *input, unsigned char *pbits, uint32_t *output)
169{
170 unsigned int dots = 0, pos = 0, part = 0, ip = 0, bits = 32;
171
172 /* Intentionally no support for bizarre IPv4 formats (plain
173 * integers, octal or hex components) -- only vanilla dotted
174 * decimal quads, optionally with trailing /nn.
175 */
176 if (input[0] == '.')
177 return 0;
178 while (1) switch (input[pos]) {
179 default:
180 if (dots < 3)
181 return 0;
182 out:
183 ip |= part << (24 - 8 * dots++);
184 *output = htonl(ip);
185 if (pbits)
186 *pbits = bits;
187 return pos;
188 case '.':
189 if (input[++pos] == '.')
190 return 0;
191 ip |= part << (24 - 8 * dots++);
192 part = 0;
193 if (input[pos] == '*') {
194 while (input[++pos] == '*') ;
195 if (input[pos] != '\0')
196 return 0;
197 if (pbits)
198 *pbits = dots * 8;
199 *output = htonl(ip);
200 return pos;
201 }
202 break;
203 case '/':
204 if (!pbits || !isdigit(input[pos + 1]))
205 return 0;
206 for (bits = 0; isdigit(input[++pos]); )
207 bits = bits * 10 + input[pos] - '0';
208 if (bits > 32)
209 return 0;
210 goto out;
211 case '0': case '1': case '2': case '3': case '4':
212 case '5': case '6': case '7': case '8': case '9':
213 part = part * 10 + input[pos++] - '0';
214 if (part > 255)
215 return 0;
216 break;
217 }
218}
219
220unsigned int
221irc_pton(irc_in_addr_t *addr, unsigned char *bits, const char *input)
222{
223 const char *part_start = NULL;
224 char *colon;
225 char *dot;
226 unsigned int part = 0, pos = 0, ii = 0, cpos = 8;
227
228 assert(input);
229 memset(addr, 0, sizeof(*addr));
230 colon = strchr(input, ':');
231 dot = strchr(input, '.');
232
233 if (colon && (!dot || (dot > colon))) {
234 /* Parse IPv6, possibly like ::127.0.0.1.
235 * This is pretty straightforward; the only trick is borrowed
236 * from Paul Vixie (BIND): when it sees a "::" continue as if
237 * it were a single ":", but note where it happened, and fill
238 * with zeros afterwards.
239 */
240 if (input[pos] == ':') {
241 if ((input[pos+1] != ':') || (input[pos+2] == ':'))
242 return 0;
243 cpos = 0;
244 pos += 2;
245 part_start = input + pos;
246 }
247 while (ii < 8) switch (input[pos]) {
248 case '0': case '1': case '2': case '3': case '4':
249 case '5': case '6': case '7': case '8': case '9':
250 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
251 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
252 part = (part << 4) | (ctype[(unsigned char)input[pos++]] & 15);
253 if (part > 0xffff)
254 return 0;
255 break;
256 case ':':
257 part_start = input + ++pos;
258 if (input[pos] == '.')
259 return 0;
260 addr->in6[ii++] = htons(part);
261 part = 0;
262 if (input[pos] == ':') {
263 if (cpos < 8)
264 return 0;
265 cpos = ii;
266 }
267 break;
268 case '.': {
269 uint32_t ip4;
270 unsigned int len;
271 len = irc_pton_ip4(part_start, bits, &ip4);
272 if (!len || (ii > 6))
273 return 0;
274 memcpy(addr->in6 + ii, &ip4, sizeof(ip4));
275 if (bits)
276 *bits += 96;
277 ii += 2;
278 pos = part_start + len - input;
279 goto finish;
280 }
281 case '/':
282 if (!bits || !isdigit(input[pos + 1]))
283 return 0;
284 addr->in6[ii++] = htons(part);
285 for (part = 0; isdigit(input[++pos]); )
286 part = part * 10 + input[pos] - '0';
287 if (part > 128)
288 return 0;
289 *bits = part;
290 goto finish;
291 case '*':
292 while (input[++pos] == '*') ;
293 if (input[pos] != '\0' || cpos < 8)
294 return 0;
295 if (bits)
296 *bits = ii * 16;
297 return pos;
298 default:
299 addr->in6[ii++] = htons(part);
300 if (cpos == 8 && ii < 8)
301 return 0;
302 if (bits)
303 *bits = 128;
304 goto finish;
305 }
306 finish:
307 /* Shift stuff after "::" up and fill middle with zeros. */
308 if (cpos < 8) {
309 unsigned int jj;
310 for (jj = 0; jj < ii - cpos; jj++)
311 addr->in6[7 - jj] = addr->in6[ii - jj - 1];
312 for (jj = 0; jj < 8 - ii; jj++)
313 addr->in6[cpos + jj] = 0;
314 }
315 } else if (dot) {
1136f709 316 uint32_t ip4;
2f61d1d7 317 pos = irc_pton_ip4(input, bits, &ip4);
318 if (pos) {
319 addr->in6[5] = htons(65535);
320 addr->in6[6] = htons(ntohl(ip4) >> 16);
321 addr->in6[7] = htons(ntohl(ip4) & 65535);
322 if (bits)
323 *bits += 96;
324 }
325 } else if (input[0] == '*') {
326 while (input[++pos] == '*') ;
327 if (input[pos] != '\0')
328 return 0;
329 if (bits)
330 *bits = 0;
331 }
332 return pos;
333}
334
335const char *irc_ntoa(const irc_in_addr_t *addr)
336{
337 static char ntoa[IRC_NTOP_MAX_SIZE];
338 irc_ntop(ntoa, sizeof(ntoa), addr);
339 return ntoa;
340}
341
342unsigned int
343irc_check_mask(const irc_in_addr_t *check, const irc_in_addr_t *mask, unsigned char bits)
344{
345 unsigned int ii;
346
347 for (ii = 0; (ii < 8) && (bits > 16); bits -= 16, ++ii)
348 if (check->in6[ii] != mask->in6[ii])
349 return 0;
350 if (ii < 8 && bits > 0
351 && (ntohs(check->in6[ii] ^ mask->in6[ii]) >> (16 - bits)))
352 return 0;
353 return 1;
354}
355
d76ed9a9 356static char irc_tolower[256];
357#undef tolower
358#define tolower(X) irc_tolower[(unsigned char)(X)]
359
d6b0769f 360void
361irc_strtolower(char *str) {
362 char *p;
363 for(p = str;*p;p++) {
364 *p = tolower(*p);
365 }
366}
367
d76ed9a9 368int
369irccasecmp(const char *stra, const char *strb) {
370 while (*stra && (tolower(*stra) == tolower(*strb)))
371 stra++, strb++;
372 return tolower(*stra) - tolower(*strb);
373}
374
375int
376ircncasecmp(const char *stra, const char *strb, unsigned int len) {
377 len--;
378 while (*stra && (tolower(*stra) == tolower(*strb)) && len)
379 stra++, strb++, len--;
380 return tolower(*stra) - tolower(*strb);
381}
382
383const char *
384irccasestr(const char *haystack, const char *needle) {
385 unsigned int hay_len = strlen(haystack), needle_len = strlen(needle), pos;
386 if (hay_len < needle_len)
387 return NULL;
388 for (pos=0; pos<hay_len+1-needle_len; ++pos) {
389 if ((tolower(haystack[pos]) == tolower(*needle))
390 && !ircncasecmp(haystack+pos, needle, needle_len))
391 return haystack+pos;
392 }
393 return NULL;
394}
395
1136f709 396char *
397ircstrlower(char *str) {
398 size_t ii;
399 for (ii = 0; str[ii] != '\0'; ++ii)
400 str[ii] = tolower(str[ii]);
401 return str;
402}
403
d76ed9a9 404int
405split_line(char *line, int irc_colon, int argv_size, char *argv[])
406{
407 int argc = 0;
408 int n;
409 while (*line && (argc < argv_size)) {
410 while (*line == ' ')
411 *line++ = 0;
412 if (*line == ':' && irc_colon && argc > 0) {
413 /* the rest is a single parameter */
414 argv[argc++] = line + 1;
415 break;
416 }
417 if (!*line)
418 break;
419 argv[argc++] = line;
420 if (argc >= argv_size)
421 break;
422 while (*line != ' ' && *line)
423 line++;
424 }
425#ifdef NDEBUG
426 n = 0;
427#else
428 for (n=argc; n<argv_size; n++)
429 argv[n] = (char*)0xFEEDBEEF;
430#endif
431 return argc;
432}
433
434/* This is ircu's mmatch() function, from match.c. */
435int mmatch(const char *old_mask, const char *new_mask)
436{
437 register const char *m = old_mask;
438 register const char *n = new_mask;
439 const char *ma = m;
440 const char *na = n;
441 int wild = 0;
442 int mq = 0, nq = 0;
443
444 while (1)
445 {
446 if (*m == '*')
447 {
448 while (*m == '*')
449 m++;
450 wild = 1;
451 ma = m;
452 na = n;
453 }
454
455 if (!*m)
456 {
457 if (!*n)
458 return 0;
459 for (m--; (m > old_mask) && (*m == '?'); m--)
460 ;
461 if ((*m == '*') && (m > old_mask) && (m[-1] != '\\'))
462 return 0;
463 if (!wild)
464 return 1;
465 m = ma;
466
467 /* Added to `mmatch' : Because '\?' and '\*' now is one character: */
468 if ((*na == '\\') && ((na[1] == '*') || (na[1] == '?')))
469 ++na;
470
471 n = ++na;
472 }
473 else if (!*n)
474 {
475 while (*m == '*')
476 m++;
477 return (*m != 0);
478 }
479 if ((*m == '\\') && ((m[1] == '*') || (m[1] == '?')))
480 {
481 m++;
482 mq = 1;
483 }
484 else
485 mq = 0;
486
487 /* Added to `mmatch' : Because '\?' and '\*' now is one character: */
488 if ((*n == '\\') && ((n[1] == '*') || (n[1] == '?')))
489 {
490 n++;
491 nq = 1;
492 }
493 else
494 nq = 0;
495
496/*
497 * This `if' has been changed compared to match() to do the following:
498 * Match when:
499 * old (m) new (n) boolean expression
500 * * any (*m == '*' && !mq) ||
501 * ? any except '*' (*m == '?' && !mq && (*n != '*' || nq)) ||
502 * any except * or ? same as m (!((*m == '*' || *m == '?') && !mq) &&
503 * toLower(*m) == toLower(*n) &&
504 * !((mq && !nq) || (!mq && nq)))
505 *
506 * Here `any' also includes \* and \? !
507 *
508 * After reworking the boolean expressions, we get:
509 * (Optimized to use boolean shortcircuits, with most frequently occuring
510 * cases upfront (which took 2 hours!)).
511 */
512 if ((*m == '*' && !mq) ||
513 ((!mq || nq) && tolower(*m) == tolower(*n)) ||
514 (*m == '?' && !mq && (*n != '*' || nq)))
515 {
516 if (*m)
517 m++;
518 if (*n)
519 n++;
520 }
521 else
522 {
523 if (!wild)
524 return 1;
525 m = ma;
526
527 /* Added to `mmatch' : Because '\?' and '\*' now is one character: */
528 if ((*na == '\\') && ((na[1] == '*') || (na[1] == '?')))
529 ++na;
530
531 n = ++na;
532 }
533 }
534}
535
536int
537match_ircglob(const char *text, const char *glob)
538{
dee9951d 539 const char *m = glob, *n = text;
540 const char *m_tmp = glob, *n_tmp = text;
541 int star_p;
542
543 for (;;) switch (*m) {
544 case '\0':
545 if (!*n)
546 return 1;
547 backtrack:
548 if (m_tmp == glob)
549 return 0;
550 m = m_tmp;
551 n = ++n_tmp;
1136f709 552 if (!*n)
553 return 0;
dee9951d 554 break;
555 case '\\':
556 m++;
557 /* allow escaping to force capitalization */
558 if (*m++ != *n++)
2f61d1d7 559 goto backtrack;
dee9951d 560 break;
561 case '*': case '?':
562 for (star_p = 0; ; m++) {
563 if (*m == '*')
564 star_p = 1;
565 else if (*m == '?') {
566 if (!*n++)
567 goto backtrack;
568 } else break;
569 }
570 if (star_p) {
571 if (!*m)
d76ed9a9 572 return 1;
dee9951d 573 else if (*m == '\\') {
574 m_tmp = ++m;
575 if (!*m)
d76ed9a9 576 return 0;
dee9951d 577 for (n_tmp = n; *n && *n != *m; n++) ;
578 } else {
579 m_tmp = m;
580 for (n_tmp = n; *n && tolower(*n) != tolower(*m); n++) ;
581 }
582 }
583 /* and fall through */
584 default:
585 if (!*n)
586 return *m == '\0';
587 if (tolower(*m) != tolower(*n))
588 goto backtrack;
589 m++;
590 n++;
591 break;
d76ed9a9 592 }
593}
594
595extern const char *hidden_host_suffix;
596
d1a65675 597/* Prevent *@* *@** *@*a* type masks, while allowing anything else. This is the best way iv found to detect
598 * a global matching mask; if it matches this string, it'll match almost anything :) */
599int is_overmask(char *mask)
600{
601 return(match_ircglob("abcdefghijklmnopqrstuv!frcmbghilnrtoasde@apdic.yfa.dsfsdaffsdasfdasfd.abcdefghijklmnopqrstuvwxyz.asdfasfdfsdsfdasfda.ydfbe", mask));
602}
603
d76ed9a9 604int
277ad996 605user_matches_glob(struct userNode *user, const char *orig_glob, int flags, int shared)
d76ed9a9 606{
2e9062b9 607 char *tmpglob, *glob, *marker;
634d32a3 608 char exttype = 0;
80ca5a69 609 int extreverse = 0, is_extended = 0, match = 0, banned = 0;
610 unsigned int count, n;
611 struct modeNode *mn;
277ad996 612 struct chanNode *channel;
613 struct banNode *ban;
d76ed9a9 614
615 /* Make a writable copy of the glob */
616 glob = alloca(strlen(orig_glob)+1);
617 strcpy(glob, orig_glob);
634d32a3 618
619 /* Extended bans */
620 tmpglob = alloca(strlen(orig_glob)+1);
621 tmpglob = strdup(orig_glob);
622
623 if (*tmpglob == '~') {
624 tmpglob++; /* get rid of the ~ */
625
626 if (*tmpglob == '!') {
627 extreverse = 1;
628 tmpglob++; /* get rid of the ! */
629 }
630
631 exttype = *tmpglob;
632 tmpglob++; /* get rid of the type */
633
634 if (*tmpglob == ':') {
635 is_extended = 1;
636 tmpglob++; /* get rid of the : */
637 glob = strdup(tmpglob);
638 }
639 }
640
641 if (is_extended) {
642 log_module(MAIN_LOG, LOG_DEBUG, "Extended ban. T (%c) R (%d) M (%s)", exttype, extreverse, glob);
643 switch (exttype) {
644 case 'a':
645 if (user->handle_info) {
646 if (extreverse) {
647 if (0 != strcasecmp(glob, user->handle_info->handle))
648 return 1;
649 } else {
650 if (0 == strcasecmp(glob, user->handle_info->handle))
651 return 1;
652 }
653 } else {
654 if (extreverse)
655 return 1;
656 }
657 return match_ircglob(user->hostname, glob);
80ca5a69 658 case 'c':
659 if (!strstr(glob, "#"))
3b7fa78b 660 return -1;
80ca5a69 661
80ca5a69 662 if (extreverse) {
663 for (n=count=0; n<user->channels.used; n++) {
664 mn = user->channels.list[n];
665 match = 0;
666
2e9062b9 667 if (*glob == '#') {
668 if (0 == strcasecmp(glob, mn->channel->name))
80ca5a69 669 match = 1;
2e9062b9 670 } else {
671 if (0 == strcasecmp(glob+1, mn->channel->name)) {
672 if ((*glob == '@') && (mn->modes & MODE_CHANOP))
673 match = 1;
674 else if ((*glob == '%') && (mn->modes & MODE_HALFOP))
675 match = 1;
676 else if ((*glob == '+') && (mn->modes & MODE_VOICE))
677 match = 1;
678 }
80ca5a69 679 }
680
681 if (match == 0)
682 banned = 1;
683 else {
684 banned = 0;
685 break;
686 }
687 }
688 } else {
689 for (n=count=0; n<user->channels.used; n++) {
690 mn = user->channels.list[n];
691 match = 0;
692
2e9062b9 693 if (*glob == '#') {
694 if (0 == strcasecmp(glob, mn->channel->name))
80ca5a69 695 match = 1;
2e9062b9 696 } else {
697 if (0 == strcasecmp(glob+1, mn->channel->name)) {
698 if ((*glob == '@') && (mn->modes & MODE_CHANOP))
699 match = 1;
700 else if ((*glob == '%') && (mn->modes & MODE_HALFOP))
701 match = 1;
702 else if ((*glob == '+') && (mn->modes & MODE_VOICE))
703 match = 1;
704 }
80ca5a69 705 }
2e9062b9 706
80ca5a69 707 if (match == 1)
708 banned = 1;
709 }
710 }
711
712 if (banned)
713 return 1;
714 else
715 return match_ircglob(user->hostname, glob);
277ad996 716 case 'j':
717 if (shared == 0) {
95b9d0bc 718 if (*glob != '#')
719 return -1;
277ad996 720 if ((channel = GetChannel(glob))) {
721 for (n = 0; n < channel->banlist.used; n++) {
722 ban = channel->banlist.list[n];
723 if (user_matches_glob(user, ban->ban, flags, 1))
724 return 1;
725 }
726 }
727 }
728 return match_ircglob(user->hostname, glob);
80ca5a69 729 case 'n': /* this is handled ircd side */
730 return match_ircglob(user->hostname, glob);
277ad996 731 case 'q': /* this is handled ircd side */
732 return match_ircglob(user->hostname, glob);
80ca5a69 733 case 't': /* this is handled ircd side */
734 return match_ircglob(user->hostname, glob);
69517d70 735 case 'R': /* this is handled ircd side */
736 return match_ircglob(user->hostname, glob);
634d32a3 737 default:
3b7fa78b 738 return -1;
634d32a3 739 }
740 }
741
d76ed9a9 742 /* Check the nick, if it's present */
2f61d1d7 743 if (flags & MATCH_USENICK) {
d76ed9a9 744 if (!(marker = strchr(glob, '!'))) {
2f61d1d7 745 log_module(MAIN_LOG, LOG_ERROR, "user_matches_glob(\"%s\", \"%s\", %d) called, and glob doesn't include a '!'", user->nick, orig_glob, flags);
d76ed9a9 746 return 0;
747 }
748 *marker = 0;
749 if (!match_ircglob(user->nick, glob)) return 0;
750 glob = marker + 1;
751 }
752 /* Check the ident */
753 if (!(marker = strchr(glob, '@'))) {
2f61d1d7 754 log_module(MAIN_LOG, LOG_ERROR, "user_matches_glob(\"%s\", \"%s\", %d) called, and glob doesn't include an '@'", user->nick, orig_glob, flags);
d76ed9a9 755 return 0;
756 }
757 *marker = 0;
2f61d1d7 758 if (!match_ircglob(user->ident, glob))
759 return 0;
d76ed9a9 760 glob = marker + 1;
ec1a68c8 761 /* Check for a fakehost match. */
762 if (IsFakeHost(user) && match_ircglob(user->fakehost, glob))
efabf227 763 return 1;
2f61d1d7 764
765 /* Check for a sethost (S:lines) */
766 if (IsSetHost(user) && match_ircglob(user->sethost, glob))
efabf227 767 return 1;
2f61d1d7 768
ec1a68c8 769 /* Check for an account match. */
770 if (hidden_host_suffix && user->handle_info) {
771 char hidden_host[HOSTLEN+1];
772 snprintf(hidden_host, sizeof(hidden_host), "%s.%s", user->handle_info->handle, hidden_host_suffix);
773 if (match_ircglob(hidden_host, glob))
a32da4c7 774 return 1;
d76ed9a9 775 }
f16ad9e7 776
777 /* Match crypt hostname */
778 if (match_ircglob(user->crypthost, glob))
779 return 1;
780
781 /* Match crypt IP */
782 if (match_ircglob(user->cryptip, glob))
783 return 1;
784
2f61d1d7 785 /* If only matching the visible hostnames, bail early. */
786 if ((flags & MATCH_VISIBLE) && IsHiddenHost(user)
787 && (IsFakeHost(user) || (hidden_host_suffix && user->handle_info)))
788 return 0;
789 /* If it might be an IP glob, test that. */
790 if (!glob[strspn(glob, "0123456789./*?")]
791 && match_ircglob(irc_ntoa(&user->ip), glob))
792 return 1;
ec1a68c8 793 /* None of the above; could only be a hostname match. */
794 return match_ircglob(user->hostname, glob);
d76ed9a9 795}
796
797int
798is_ircmask(const char *text)
799{
8062bfc3 800 char *tmptext;
801
802 if (*text == '~') {
803 tmptext = alloca(strlen(text)+1);
804 tmptext = strdup(text);
805
806 tmptext++; /* get rid of the ~ */
807
808 if (*tmptext == '!')
809 tmptext++; /* get rid of the ! if it exists */
810
811 tmptext++; /* get rid of the ext ban type */
812
813 if (*tmptext == ':') {
814 tmptext++; /* get rid of the : */
815 while (*tmptext && !isspace((char)*tmptext))
816 tmptext++; /* get rid of the rest */
817 return !*tmptext;
818 }
819 }
820
d76ed9a9 821 while (*text && (isalnum((char)*text) || strchr("-_[]|\\`^{}?*", *text)))
822 text++;
823 if (*text++ != '!')
824 return 0;
825 while (*text && *text != '@' && !isspace((char)*text))
826 text++;
827 if (*text++ != '@')
828 return 0;
829 while (*text && !isspace((char)*text))
830 text++;
831 return !*text;
832}
833
834int
835is_gline(const char *text)
d914d1cb 836{
837 if (*text == '@')
838 return 0;
839 text += strcspn(text, "@!% \t\r\n");
840 if (*text++ != '@')
841 return 0;
842 if (!*text)
843 return 0;
844 while (*text && (isalnum((char)*text) || strchr(".-?*:", *text)))
845 text++;
846 return !*text;
847}
848
849int
850is_shun(const char *text)
d76ed9a9 851{
852 if (*text == '@')
853 return 0;
854 text += strcspn(text, "@!% \t\r\n");
855 if (*text++ != '@')
856 return 0;
857 if (!*text)
858 return 0;
ec1a68c8 859 while (*text && (isalnum((char)*text) || strchr(".-?*:", *text)))
d76ed9a9 860 text++;
861 return !*text;
862}
863
864int
865split_ircmask(char *text, char **nick, char **ident, char **host)
866{
867 char *start;
868
869 start = text;
870 while (isalnum((char)*text) || strchr("=[]\\`^{}?*", *text))
871 text++;
872 if (*text != '!' || ((text - start) > NICKLEN))
873 return 0;
874 *text = 0;
875 if (nick)
876 *nick = start;
877
878 start = ++text;
879 while (*text && *text != '@' && !isspace((char)*text))
880 text++;
881 if (*text != '@' || ((text - start) > USERLEN))
882 return 0;
883 *text = 0;
884 if (ident)
885 *ident = start;
886
887 start = ++text;
ec1a68c8 888 while (*text && (isalnum((char)*text) || strchr(".-?*:", *text)))
d76ed9a9 889 text++;
890 if (host)
891 *host = start;
892 return !*text && ((text - start) <= HOSTLEN) && nick && ident && host;
893}
894
895char *
896sanitize_ircmask(char *input)
897{
898 unsigned int length, flag;
899 char *mask, *start, *output;
900
901 /* Sanitize everything in place; input *must* be a valid
902 hostmask. */
903 output = input;
904 flag = 0;
905
906 /* The nick is truncated at the end. */
907 length = 0;
908 mask = input;
909 while(*input++ != '!')
910 {
911 length++;
912 }
913 if(length > NICKLEN)
914 {
915 mask += NICKLEN;
916 *mask++ = '!';
917
918 /* This flag is used to indicate following parts should
919 be shifted. */
920 flag = 1;
921 }
922 else
923 {
924 mask = input;
925 }
926
927 /* The ident and host must be truncated at the beginning and
928 replaced with a '*' to be compatible with ircu. */
929 length = 0;
930 start = input;
931 while(*input++ != '@')
932 {
933 length++;
934 }
935 if(length > USERLEN || flag)
936 {
937 if(length > USERLEN)
938 {
939 start = input - USERLEN;
940 *mask++ = '*';
941 }
942 while(*start != '@')
943 {
944 *mask++ = *start++;
945 }
946 *mask++ = '@';
947
948 flag = 1;
949 }
950 else
951 {
952 mask = input;
953 }
954
955 length = 0;
956 start = input;
957 while(*input++)
958 {
959 length++;
960 }
961 if(length > HOSTLEN || flag)
962 {
963 if(length > HOSTLEN)
964 {
965 start = input - HOSTLEN;
966 *mask++ = '*';
967 }
968 while(*start)
969 {
970 *mask++ = *start++;
971 }
972 *mask = '\0';
973 }
974
975 return output;
976}
977
978static long
979TypeLength(char type)
980{
981 switch (type) {
982 case 'y': return 365*24*60*60;
ca22ccd3 983 case 'M': return 30*24*60*60;
d76ed9a9 984 case 'w': return 7*24*60*60;
985 case 'd': return 24*60*60;
986 case 'h': return 60*60;
987 case 'm': return 60;
988 case 's': return 1;
989 default: return 0;
990 }
991}
992
ca22ccd3 993/* This function is not entirely accurate as it does not take into account leap units
994 * or varying months. TODO: use proper dateadd functions to calculate real seconds
995 * from now for the units (eg 1M should be give us seconds till todays date next month)
996 */
d76ed9a9 997unsigned long
998ParseInterval(const char *interval)
999{
1000 unsigned long seconds = 0;
1001 int partial = 0;
1002 char c;
1003
1004 /* process the string, resetting the count if we find a unit character */
1005 while ((c = *interval++)) {
1136f709 1006 if (isdigit((int)c)) {
1007 partial = partial*10 + c - '0';
1008 } else if (strchr("yMwdhms", c)) {
1009 seconds += TypeLength(c) * partial;
1010 partial = 0;
1011 } else {
1012 return 0;
1013 }
d76ed9a9 1014 }
1015 /* assume the last chunk is seconds (the normal case) */
1016 return seconds + partial;
1017}
1018
1019static long
1020GetSizeMultiplier(char type)
1021{
1022 switch (type) {
1023 case 'G': case 'g': return 1024*1024*1024;
1024 case 'M': case 'm': return 1024*1024;
1025 case 'K': case 'k': return 1024;
1026 case 'B': case 'b': return 1;
1027 default: return 0;
1028 }
1029}
1030
1031unsigned long
1032ParseVolume(const char *volume)
1033{
1034 unsigned long accum = 0, partial = 0;
1035 char c;
1036 while ((c = *volume++)) {
1037 if (isdigit((int)c)) {
1038 partial = partial*10 + c - '0';
1039 } else {
1040 accum += GetSizeMultiplier(c) * partial;
1041 partial = 0;
1042 }
1043 }
1044 return accum + partial;
1045}
1046
d76ed9a9 1047char *
1048unsplit_string(char *set[], unsigned int max, char *dest)
1049{
1050 static char unsplit_buffer[MAXLEN*2];
1051 unsigned int ii, jj, pos;
1052
1053 if (!dest)
1054 dest = unsplit_buffer;
1055 for (ii=pos=0; ii<max; ii++) {
1056 for (jj=0; set[ii][jj]; jj++)
1057 dest[pos++] = set[ii][jj];
1058 dest[pos++] = ' ';
1059 }
1060 dest[--pos] = 0;
1061 return dest;
1062}
1063
1064char *
1065intervalString(char *output, time_t interval, struct handle_info *hi)
1066{
1067 static const struct {
1068 const char *msg_single;
1069 const char *msg_plural;
1070 long length;
1071 } unit[] = {
1072 { "MSG_YEAR", "MSG_YEARS", 365 * 24 * 60 * 60 },
1073 { "MSG_WEEK", "MSG_WEEKS", 7 * 24 * 60 * 60 },
1074 { "MSG_DAY", "MSG_DAYS", 24 * 60 * 60 },
1075 { "MSG_HOUR", "MSG_HOURS", 60 * 60 },
1076 { "MSG_MINUTE", "MSG_MINUTES", 60 },
1077 { "MSG_SECOND", "MSG_SECONDS", 1 }
1078 };
1079 struct language *lang;
1080 const char *msg;
1081 unsigned int type, words, pos, count;
1082
1083 lang = hi ? hi->language : lang_C;
1084 if(!interval)
1085 {
1086 msg = language_find_message(lang, "MSG_0_SECONDS");
1087 return strcpy(output, msg);
1088 }
1089
1090 for (type = 0, words = pos = 0;
1091 interval && (words < 2) && (type < ArrayLength(unit));
1092 type++) {
1093 if (interval < unit[type].length)
1094 continue;
1095 count = interval / unit[type].length;
1096 interval = interval % unit[type].length;
1097
1098 if (words++ == 1) {
1099 msg = language_find_message(lang, "MSG_AND");
de9510bc 1100 pos += sprintf(output + pos, "%s ", msg);
d76ed9a9 1101 }
1102 if (count == 1)
1103 msg = language_find_message(lang, unit[type].msg_single);
1104 else
1105 msg = language_find_message(lang, unit[type].msg_plural);
de9510bc 1106 pos += sprintf(output + pos, "%d%s", count, msg);
d76ed9a9 1107 }
1108
1109 output[pos] = 0;
1110 return output;
1111}
1112
1113int
1114getipbyname(const char *name, unsigned long *ip)
1115{
1116 struct hostent *he = gethostbyname(name);
1117 if (!he)
1118 return 0;
1119 if (he->h_addrtype != AF_INET)
1120 return 0;
1121 memcpy(ip, he->h_addr_list[0], sizeof(*ip));
1122 return 1;
1123}
1124
1125DEFINE_LIST(string_buffer, char)
1126
1127void
1128string_buffer_append_substring(struct string_buffer *buf, const char *tail, unsigned int len)
1129{
1130 while (buf->used + len >= buf->size) {
1131 if (!buf->size)
1132 buf->size = 16;
1133 else
1134 buf->size <<= 1;
1135 buf->list = realloc(buf->list, buf->size*sizeof(buf->list[0]));
1136 }
1137 memcpy(buf->list + buf->used, tail, len+1);
1138 buf->used += len;
1139}
1140
1141void
1142string_buffer_append_string(struct string_buffer *buf, const char *tail)
1143{
1144 string_buffer_append_substring(buf, tail, strlen(tail));
1145}
1146
1147void
1148string_buffer_append_vprintf(struct string_buffer *buf, const char *fmt, va_list args)
1149{
1150 va_list working;
1151 unsigned int len;
1152 int ret;
1153
1154 VA_COPY(working, args);
1155 len = strlen(fmt);
1156 if (!buf->list || ((buf->used + buf->size) < len)) {
1157 buf->size = buf->used + len;
1158 buf->list = realloc(buf->list, buf->size);
1159 }
1160 ret = vsnprintf(buf->list + buf->used, buf->size - buf->used, fmt, working);
1161 if (ret <= 0) {
1162 /* pre-C99 behavior; double buffer size until it is big enough */
1163 va_end(working);
1164 VA_COPY(working, args);
a32da4c7 1165 while ((ret = vsnprintf(buf->list + buf->used, buf->size - buf->used, fmt, working)) <= 0) {
d76ed9a9 1166 buf->size += len;
1167 buf->list = realloc(buf->list, buf->size);
1168 va_end(working);
1169 VA_COPY(working, args);
1170 }
1171 buf->used += ret;
1172 } else if (buf->used + ret < buf->size) {
1173 /* no need to increase allocation size */
1174 buf->used += ret;
1175 } else {
1176 /* now we know exactly how much space we need */
1177 if (buf->size <= buf->used + ret) {
1178 buf->size = buf->used + ret + 1;
1179 buf->list = realloc(buf->list, buf->size);
1180 }
1181 va_end(working);
1182 VA_COPY(working, args);
1183 buf->used += vsnprintf(buf->list + buf->used, buf->size, fmt, working);
1184 }
1185 va_end(working);
1186 va_end(args);
1187}
1188
1189void string_buffer_append_printf(struct string_buffer *buf, const char *fmt, ...)
1190{
1191 va_list args;
1192 va_start(args, fmt);
1193 string_buffer_append_vprintf(buf, fmt, args);
1194}
1195
1196void
1197string_buffer_replace(struct string_buffer *buf, unsigned int from, unsigned int len, const char *repl)
1198{
1199 unsigned int repl_len = strlen(repl);
1200 if (from > buf->used)
1201 return;
1202 if (len + from > buf->used)
1203 len = buf->used - from;
1204 buf->used = buf->used + repl_len - len;
1205 if (buf->size <= buf->used) {
1206 while (buf->used >= buf->size)
1207 buf->size <<= 1;
1208 buf->list = realloc(buf->list, buf->size*sizeof(buf->list[0]));
1209 }
1210 memmove(buf->list+from+repl_len, buf->list+from+len, strlen(buf->list+from+len));
1211 strcpy(buf->list+from, repl);
1212}
1213
1214struct string_list str_tab;
1215
1216const char *
1217strtab(unsigned int ii) {
1218 if (ii > 65536)
1219 return NULL;
1220 if (ii > str_tab.size) {
1221 unsigned int old_size = str_tab.size;
1222 while (ii >= str_tab.size)
1223 str_tab.size <<= 1;
1224 str_tab.list = realloc(str_tab.list, str_tab.size*sizeof(str_tab.list[0]));
1225 memset(str_tab.list+old_size, 0, (str_tab.size-old_size)*sizeof(str_tab.list[0]));
1226 }
1227 if (!str_tab.list[ii]) {
1228 str_tab.list[ii] = malloc(12);
1229 sprintf(str_tab.list[ii], "%u", ii);
1230 }
1231 return str_tab.list[ii];
1232}
1233
1234void
1235tools_init(void)
1236{
1237 unsigned int upr, lwr;
1238 for (lwr=0; lwr<256; ++lwr)
1239 tolower(lwr) = lwr;
1240 for (upr='A', lwr='a'; lwr <= 'z'; ++upr, ++lwr)
1241 tolower(upr) = lwr;
1242#ifdef WITH_PROTOCOL_P10
1243 for (upr='[', lwr='{'; lwr <= '~'; ++upr, ++lwr)
1244 tolower(upr) = lwr;
1245 for (upr=0xc0, lwr=0xe0; lwr <= 0xf6; ++upr, ++lwr)
1246 tolower(upr) = lwr;
1247 for (upr=0xd8, lwr=0xf8; lwr <= 0xfe; ++upr, ++lwr)
1248 tolower(upr) = lwr;
1249#endif
1250 str_tab.size = 1001;
1251 str_tab.list = calloc(str_tab.size, sizeof(str_tab.list[0]));
1252}
1253
1254void
1255tools_cleanup(void)
1256{
1257 unsigned int ii;
1258 for (ii=0; ii<str_tab.size; ++ii)
1259 free(str_tab.list[ii]);
1260 free(str_tab.list);
1261}
2aef5f4b 1262
1263/* mysep() is my answer to the strtok/strsep
1264 * issue. strsep is nice but doesn't skip
1265 * multiple dilimiters, which can really
1266 * offset tokens and cause huge corruption
1267 * so this function will use strsep but
31f23f13 1268 * act like strtok in that sense.
2aef5f4b 1269 */
1270char *mysep(char **sepstr, char *delim)
1271{
1272 static char *retstr;
1273
1274 if(!*sepstr || !**sepstr)
1275 return(NULL);
1276
1277 do
1278 {
1279 retstr = strsep(sepstr, delim);
1280 }while (retstr && !(*retstr));
1281
1282 return(retstr);
1283}
1284
c9bf23fe 1285/* Mallocing snprintf *
1286 *
1287 * If it overruns size, it will simply be safely truncated.
1288 */
1289char *
1290x3_msnprintf(const int size, const char *format, ...)
1291{
1292 va_list ap;
1293 char* buff = calloc(sizeof(char *), size+1);
1294
1295 va_start(ap, format);
1296 vsnprintf(buff, size, format, ap);
1297 va_end(ap);
1298 buff = realloc(buff, strlen(buff) + 1);
1299 return buff;
1300}
1301
eb5d6b73 1302char *time2str(time_t thetime)
1303{
1304 char *buf, *tmp;
1305
1306 buf = ctime(&thetime);
1307 tmp = (char *)strchr(buf, '\n');
1308 *tmp = '\0';
1309 return(buf);
1310}
1311
63665495 1312char* x3_strtok(char **save, char *str, char *fs)
1313{
1314 char *pos = *save; /* keep last position across calls */
1315 char *tmp;
1316
1317 if (str)
1318 pos = str; /* new string scan */
1319
1320 while (pos && *pos && strchr(fs, *pos) != NULL)
1321 pos++; /* skip leading separators */
1322
1323 if (!pos || !*pos)
1324 return (pos = *save = NULL); /* string contains only sep's */
1325
1326 tmp = pos; /* now, keep position of the token */
1327
1328 while (*pos && strchr(fs, *pos) == NULL)
1329 pos++; /* skip content of the token */
1330
1331 if (*pos)
1332 *pos++ = '\0'; /* remove first sep after the token */
1333 else
1334 pos = NULL; /* end of string */
1335
1336 *save = pos;
1337 return (tmp);
1338}
1339
4c26ef3e 1340int valid_email(const char *email)
1341{
1342 unsigned int i;
1343 for (i=0;i<strlen(email);i++)
1344 {
1345 if(!isalnum(email[i]) &&
1346 email[i] != '.' &&
1347 email[i] != '@' &&
1348 email[i] != '-' &&
1349 email[i] != '+' &&
1350 email[i] != '_' )
1351 return false;
1352 }
1353 if(strchr(email, '@') == NULL)
1354 return false;
1355 return true;
1356}
1357
668dc38e 1358/*
1359 * Create a string of form "foo!bar@fubar" given foo, bar and fubar
1360 * as the parameters. If NULL, they become "*".
1361 */
1362#define NUH_BUFSIZE (NICKLEN + USERLEN + HOSTLEN + 10)
1363static char *make_nick_user_host(char *namebuf, const char *nick,
1364 const char *name, const char *host)
1365{
1366 snprintf(namebuf, NUH_BUFSIZE, "%s!%s@%s", nick, name, host);
1367 return namebuf;
1368}
1369
1370/*
1371 * pretty_mask
1372 *
1373 * by Carlo Wood (Run), 05 Oct 1998.
1374 *
1375 * Canonify a mask.
1376 *
1377 * When the nick is longer then NICKLEN, it is cut off (its an error of course).
1378 * When the user name or host name are too long (USERLEN and HOSTLEN
1379 * respectively) then they are cut off at the start with a '*'.
1380 *
1381 * The following transformations are made:
1382 *
1383 * 1) xxx -> nick!*@*
1384 * 2) xxx.xxx -> *!*@host
1385 * 3) xxx!yyy -> nick!user@*
1386 * 4) xxx@yyy -> *!user@host
1387 * 5) xxx!yyy@zzz -> nick!user@host
1388 */
1389char *pretty_mask(char *mask)
1390{
1391 static char star[2] = { '*', 0 };
1392 static char retmask[NUH_BUFSIZE] = "";
1393 char *last_dot = NULL;
1394 char *ptr = NULL;
1395
1396 /* Case 1: default */
1397 char *nick = mask;
1398 char *user = star;
1399 char *host = star;
1400
1401 /* Do a _single_ pass through the characters of the mask: */
1402 for (ptr = mask; *ptr; ++ptr)
1403 {
1404 if (*ptr == '!')
1405 {
1406 /* Case 3 or 5: Found first '!' (without finding a '@' yet) */
1407 user = ++ptr;
1408 host = star;
1409 }
1410 else if (*ptr == '@')
1411 {
1412 /* Case 4: Found last '@' (without finding a '!' yet) */
1413 nick = star;
1414 user = mask;
1415 host = ++ptr;
1416 }
1417 else if (*ptr == '.')
1418 {
1419 /* Case 2: Found last '.' (without finding a '!' or '@' yet) */
1420 last_dot = ptr;
1421 continue;
1422 }
1423 else
1424 continue;
1425 for (; *ptr; ++ptr)
1426 {
1427 if (*ptr == '@')
1428 {
1429 /* Case 4 or 5: Found last '@' */
1430 host = ptr + 1;
1431 }
1432 }
1433 break;
1434 }
1435 if (user == star && last_dot)
1436 {
1437 /* Case 2: */
1438 nick = star;
1439 user = star;
1440 host = mask;
1441 }
1442 /* Check lengths */
1443 if (nick != star)
1444 {
1445 char *nick_end = (user != star) ? user - 1 : ptr;
1446 if (nick_end - nick > NICKLEN)
1447 nick[NICKLEN] = 0;
1448 *nick_end = 0;
1449 }
1450 if (user != star)
1451 {
1452 char *user_end = (host != star) ? host - 1 : ptr;
1453 if (user_end - user > USERLEN)
1454 {
1455 user = user_end - USERLEN;
1456 *user = '*';
1457 }
1458 *user_end = 0;
1459 }
1460 if (host != star && ptr - host > HOSTLEN)
1461 {
1462 host = ptr - HOSTLEN;
1463 *host = '*';
1464 }
1465 return make_nick_user_host(retmask, nick, user, host);
1466}
c9bf23fe 1467
7a278540 1468int str_is_number(const char *str)
c9bf23fe 1469{
1470 char *ptr;
1471 int ret = false;
7a278540 1472 for(ptr = (char *)str;*ptr;ptr++) {
c9bf23fe 1473 if((*ptr >= '0' && *ptr <= '9') || *ptr == '-')
1474 ret = true;
1475 else
1476 return false;
1477 }
1478 return ret;
1479}