]> jfr.im git - irc/evilnet/x3.git/blame - src/tools.c
Fixed incorrect uplink password value name in example config
[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) {
cf945df8
MB
370 if (!stra)
371 return -1;
372 if (!strb)
373 return 1;
d76ed9a9 374 while (*stra && (tolower(*stra) == tolower(*strb)))
375 stra++, strb++;
376 return tolower(*stra) - tolower(*strb);
377}
378
379int
380ircncasecmp(const char *stra, const char *strb, unsigned int len) {
381 len--;
382 while (*stra && (tolower(*stra) == tolower(*strb)) && len)
383 stra++, strb++, len--;
384 return tolower(*stra) - tolower(*strb);
385}
386
387const char *
388irccasestr(const char *haystack, const char *needle) {
389 unsigned int hay_len = strlen(haystack), needle_len = strlen(needle), pos;
390 if (hay_len < needle_len)
391 return NULL;
392 for (pos=0; pos<hay_len+1-needle_len; ++pos) {
393 if ((tolower(haystack[pos]) == tolower(*needle))
394 && !ircncasecmp(haystack+pos, needle, needle_len))
395 return haystack+pos;
396 }
397 return NULL;
398}
399
1136f709 400char *
401ircstrlower(char *str) {
402 size_t ii;
403 for (ii = 0; str[ii] != '\0'; ++ii)
404 str[ii] = tolower(str[ii]);
405 return str;
406}
407
d76ed9a9 408int
409split_line(char *line, int irc_colon, int argv_size, char *argv[])
410{
411 int argc = 0;
412 int n;
413 while (*line && (argc < argv_size)) {
414 while (*line == ' ')
415 *line++ = 0;
416 if (*line == ':' && irc_colon && argc > 0) {
417 /* the rest is a single parameter */
418 argv[argc++] = line + 1;
419 break;
420 }
421 if (!*line)
422 break;
423 argv[argc++] = line;
424 if (argc >= argv_size)
425 break;
426 while (*line != ' ' && *line)
427 line++;
428 }
429#ifdef NDEBUG
430 n = 0;
431#else
432 for (n=argc; n<argv_size; n++)
433 argv[n] = (char*)0xFEEDBEEF;
434#endif
435 return argc;
436}
437
438/* This is ircu's mmatch() function, from match.c. */
439int mmatch(const char *old_mask, const char *new_mask)
440{
441 register const char *m = old_mask;
442 register const char *n = new_mask;
443 const char *ma = m;
444 const char *na = n;
445 int wild = 0;
446 int mq = 0, nq = 0;
447
448 while (1)
449 {
450 if (*m == '*')
451 {
452 while (*m == '*')
453 m++;
454 wild = 1;
455 ma = m;
456 na = n;
457 }
458
459 if (!*m)
460 {
461 if (!*n)
462 return 0;
463 for (m--; (m > old_mask) && (*m == '?'); m--)
464 ;
465 if ((*m == '*') && (m > old_mask) && (m[-1] != '\\'))
466 return 0;
467 if (!wild)
468 return 1;
469 m = ma;
470
471 /* Added to `mmatch' : Because '\?' and '\*' now is one character: */
472 if ((*na == '\\') && ((na[1] == '*') || (na[1] == '?')))
473 ++na;
474
475 n = ++na;
476 }
477 else if (!*n)
478 {
479 while (*m == '*')
480 m++;
481 return (*m != 0);
482 }
483 if ((*m == '\\') && ((m[1] == '*') || (m[1] == '?')))
484 {
485 m++;
486 mq = 1;
487 }
488 else
489 mq = 0;
490
491 /* Added to `mmatch' : Because '\?' and '\*' now is one character: */
492 if ((*n == '\\') && ((n[1] == '*') || (n[1] == '?')))
493 {
494 n++;
495 nq = 1;
496 }
497 else
498 nq = 0;
499
500/*
501 * This `if' has been changed compared to match() to do the following:
502 * Match when:
503 * old (m) new (n) boolean expression
504 * * any (*m == '*' && !mq) ||
505 * ? any except '*' (*m == '?' && !mq && (*n != '*' || nq)) ||
506 * any except * or ? same as m (!((*m == '*' || *m == '?') && !mq) &&
507 * toLower(*m) == toLower(*n) &&
508 * !((mq && !nq) || (!mq && nq)))
509 *
510 * Here `any' also includes \* and \? !
511 *
512 * After reworking the boolean expressions, we get:
513 * (Optimized to use boolean shortcircuits, with most frequently occuring
514 * cases upfront (which took 2 hours!)).
515 */
516 if ((*m == '*' && !mq) ||
517 ((!mq || nq) && tolower(*m) == tolower(*n)) ||
518 (*m == '?' && !mq && (*n != '*' || nq)))
519 {
520 if (*m)
521 m++;
522 if (*n)
523 n++;
524 }
525 else
526 {
527 if (!wild)
528 return 1;
529 m = ma;
530
531 /* Added to `mmatch' : Because '\?' and '\*' now is one character: */
532 if ((*na == '\\') && ((na[1] == '*') || (na[1] == '?')))
533 ++na;
534
535 n = ++na;
536 }
537 }
538}
539
540int
541match_ircglob(const char *text, const char *glob)
542{
dee9951d 543 const char *m = glob, *n = text;
544 const char *m_tmp = glob, *n_tmp = text;
545 int star_p;
546
547 for (;;) switch (*m) {
548 case '\0':
549 if (!*n)
550 return 1;
551 backtrack:
552 if (m_tmp == glob)
553 return 0;
554 m = m_tmp;
555 n = ++n_tmp;
1136f709 556 if (!*n)
557 return 0;
dee9951d 558 break;
559 case '\\':
560 m++;
561 /* allow escaping to force capitalization */
562 if (*m++ != *n++)
2f61d1d7 563 goto backtrack;
dee9951d 564 break;
565 case '*': case '?':
566 for (star_p = 0; ; m++) {
567 if (*m == '*')
568 star_p = 1;
569 else if (*m == '?') {
570 if (!*n++)
571 goto backtrack;
572 } else break;
573 }
574 if (star_p) {
575 if (!*m)
d76ed9a9 576 return 1;
dee9951d 577 else if (*m == '\\') {
578 m_tmp = ++m;
579 if (!*m)
d76ed9a9 580 return 0;
dee9951d 581 for (n_tmp = n; *n && *n != *m; n++) ;
582 } else {
583 m_tmp = m;
584 for (n_tmp = n; *n && tolower(*n) != tolower(*m); n++) ;
585 }
586 }
587 /* and fall through */
588 default:
589 if (!*n)
590 return *m == '\0';
591 if (tolower(*m) != tolower(*n))
592 goto backtrack;
593 m++;
594 n++;
595 break;
d76ed9a9 596 }
597}
598
599extern const char *hidden_host_suffix;
600
d1a65675 601/* Prevent *@* *@** *@*a* type masks, while allowing anything else. This is the best way iv found to detect
602 * a global matching mask; if it matches this string, it'll match almost anything :) */
603int is_overmask(char *mask)
604{
605 return(match_ircglob("abcdefghijklmnopqrstuv!frcmbghilnrtoasde@apdic.yfa.dsfsdaffsdasfdasfd.abcdefghijklmnopqrstuvwxyz.asdfasfdfsdsfdasfda.ydfbe", mask));
606}
607
d76ed9a9 608int
277ad996 609user_matches_glob(struct userNode *user, const char *orig_glob, int flags, int shared)
d76ed9a9 610{
2e9062b9 611 char *tmpglob, *glob, *marker;
634d32a3 612 char exttype = 0;
80ca5a69 613 int extreverse = 0, is_extended = 0, match = 0, banned = 0;
614 unsigned int count, n;
615 struct modeNode *mn;
277ad996 616 struct chanNode *channel;
617 struct banNode *ban;
d76ed9a9 618
619 /* Make a writable copy of the glob */
620 glob = alloca(strlen(orig_glob)+1);
621 strcpy(glob, orig_glob);
634d32a3 622
623 /* Extended bans */
624 tmpglob = alloca(strlen(orig_glob)+1);
625 tmpglob = strdup(orig_glob);
626
627 if (*tmpglob == '~') {
628 tmpglob++; /* get rid of the ~ */
629
630 if (*tmpglob == '!') {
631 extreverse = 1;
632 tmpglob++; /* get rid of the ! */
633 }
634
635 exttype = *tmpglob;
636 tmpglob++; /* get rid of the type */
637
638 if (*tmpglob == ':') {
639 is_extended = 1;
640 tmpglob++; /* get rid of the : */
641 glob = strdup(tmpglob);
642 }
643 }
644
645 if (is_extended) {
646 log_module(MAIN_LOG, LOG_DEBUG, "Extended ban. T (%c) R (%d) M (%s)", exttype, extreverse, glob);
647 switch (exttype) {
80d5d548 648 case 'a': // account
634d32a3 649 if (user->handle_info) {
650 if (extreverse) {
651 if (0 != strcasecmp(glob, user->handle_info->handle))
652 return 1;
653 } else {
654 if (0 == strcasecmp(glob, user->handle_info->handle))
655 return 1;
656 }
657 } else {
658 if (extreverse)
659 return 1;
660 }
661 return match_ircglob(user->hostname, glob);
80d5d548 662 case 'c': // another channel
80ca5a69 663 if (!strstr(glob, "#"))
3b7fa78b 664 return -1;
80ca5a69 665
80ca5a69 666 if (extreverse) {
667 for (n=count=0; n<user->channels.used; n++) {
668 mn = user->channels.list[n];
669 match = 0;
670
2e9062b9 671 if (*glob == '#') {
672 if (0 == strcasecmp(glob, mn->channel->name))
80ca5a69 673 match = 1;
2e9062b9 674 } else {
675 if (0 == strcasecmp(glob+1, mn->channel->name)) {
676 if ((*glob == '@') && (mn->modes & MODE_CHANOP))
677 match = 1;
678 else if ((*glob == '%') && (mn->modes & MODE_HALFOP))
679 match = 1;
680 else if ((*glob == '+') && (mn->modes & MODE_VOICE))
681 match = 1;
682 }
80ca5a69 683 }
684
685 if (match == 0)
686 banned = 1;
687 else {
688 banned = 0;
689 break;
690 }
691 }
692 } else {
693 for (n=count=0; n<user->channels.used; n++) {
694 mn = user->channels.list[n];
695 match = 0;
696
2e9062b9 697 if (*glob == '#') {
698 if (0 == strcasecmp(glob, mn->channel->name))
80ca5a69 699 match = 1;
2e9062b9 700 } else {
701 if (0 == strcasecmp(glob+1, mn->channel->name)) {
702 if ((*glob == '@') && (mn->modes & MODE_CHANOP))
703 match = 1;
704 else if ((*glob == '%') && (mn->modes & MODE_HALFOP))
705 match = 1;
706 else if ((*glob == '+') && (mn->modes & MODE_VOICE))
707 match = 1;
708 }
80ca5a69 709 }
2e9062b9 710
80ca5a69 711 if (match == 1)
712 banned = 1;
713 }
714 }
715
716 if (banned)
717 return 1;
718 else
719 return match_ircglob(user->hostname, glob);
277ad996 720 case 'j':
721 if (shared == 0) {
95b9d0bc 722 if (*glob != '#')
723 return -1;
277ad996 724 if ((channel = GetChannel(glob))) {
725 for (n = 0; n < channel->banlist.used; n++) {
726 ban = channel->banlist.list[n];
727 if (user_matches_glob(user, ban->ban, flags, 1))
728 return 1;
729 }
730 }
731 }
732 return match_ircglob(user->hostname, glob);
80ca5a69 733 case 'n': /* this is handled ircd side */
734 return match_ircglob(user->hostname, glob);
277ad996 735 case 'q': /* this is handled ircd side */
736 return match_ircglob(user->hostname, glob);
80ca5a69 737 case 't': /* this is handled ircd side */
738 return match_ircglob(user->hostname, glob);
69517d70 739 case 'R': /* this is handled ircd side */
740 return match_ircglob(user->hostname, glob);
80d5d548
AS
741 case 'm': // mute by mark
742 if(user->mark && !strcmp(glob, user->mark))
743 return true;
744 else
745 return false;
746
747 case 'M': // mute by mark unless authed
748 return false; // can never match a logged in user
749
634d32a3 750 default:
3b7fa78b 751 return -1;
634d32a3 752 }
753 }
754
d76ed9a9 755 /* Check the nick, if it's present */
2f61d1d7 756 if (flags & MATCH_USENICK) {
d76ed9a9 757 if (!(marker = strchr(glob, '!'))) {
2f61d1d7 758 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 759 return 0;
760 }
761 *marker = 0;
762 if (!match_ircglob(user->nick, glob)) return 0;
763 glob = marker + 1;
764 }
765 /* Check the ident */
766 if (!(marker = strchr(glob, '@'))) {
2f61d1d7 767 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 768 return 0;
769 }
770 *marker = 0;
2f61d1d7 771 if (!match_ircglob(user->ident, glob))
772 return 0;
d76ed9a9 773 glob = marker + 1;
ec1a68c8 774 /* Check for a fakehost match. */
775 if (IsFakeHost(user) && match_ircglob(user->fakehost, glob))
efabf227 776 return 1;
2f61d1d7 777
778 /* Check for a sethost (S:lines) */
779 if (IsSetHost(user) && match_ircglob(user->sethost, glob))
efabf227 780 return 1;
2f61d1d7 781
ec1a68c8 782 /* Check for an account match. */
783 if (hidden_host_suffix && user->handle_info) {
784 char hidden_host[HOSTLEN+1];
785 snprintf(hidden_host, sizeof(hidden_host), "%s.%s", user->handle_info->handle, hidden_host_suffix);
786 if (match_ircglob(hidden_host, glob))
a32da4c7 787 return 1;
d76ed9a9 788 }
f16ad9e7 789
790 /* Match crypt hostname */
791 if (match_ircglob(user->crypthost, glob))
792 return 1;
793
794 /* Match crypt IP */
795 if (match_ircglob(user->cryptip, glob))
796 return 1;
797
2f61d1d7 798 /* If only matching the visible hostnames, bail early. */
799 if ((flags & MATCH_VISIBLE) && IsHiddenHost(user)
b6f98d33
MB
800 && (IsFakeHost(user) || (hidden_host_suffix && user->handle_info) ||
801 user->crypthost || user->cryptip))
2f61d1d7 802 return 0;
803 /* If it might be an IP glob, test that. */
804 if (!glob[strspn(glob, "0123456789./*?")]
805 && match_ircglob(irc_ntoa(&user->ip), glob))
806 return 1;
ec1a68c8 807 /* None of the above; could only be a hostname match. */
808 return match_ircglob(user->hostname, glob);
d76ed9a9 809}
810
811int
812is_ircmask(const char *text)
813{
8062bfc3 814 char *tmptext;
815
816 if (*text == '~') {
817 tmptext = alloca(strlen(text)+1);
818 tmptext = strdup(text);
819
820 tmptext++; /* get rid of the ~ */
821
822 if (*tmptext == '!')
823 tmptext++; /* get rid of the ! if it exists */
824
825 tmptext++; /* get rid of the ext ban type */
826
827 if (*tmptext == ':') {
828 tmptext++; /* get rid of the : */
829 while (*tmptext && !isspace((char)*tmptext))
830 tmptext++; /* get rid of the rest */
831 return !*tmptext;
832 }
833 }
834
d76ed9a9 835 while (*text && (isalnum((char)*text) || strchr("-_[]|\\`^{}?*", *text)))
836 text++;
837 if (*text++ != '!')
838 return 0;
839 while (*text && *text != '@' && !isspace((char)*text))
840 text++;
841 if (*text++ != '@')
842 return 0;
843 while (*text && !isspace((char)*text))
844 text++;
845 return !*text;
846}
847
848int
849is_gline(const char *text)
d914d1cb 850{
851 if (*text == '@')
852 return 0;
853 text += strcspn(text, "@!% \t\r\n");
854 if (*text++ != '@')
855 return 0;
856 if (!*text)
857 return 0;
858 while (*text && (isalnum((char)*text) || strchr(".-?*:", *text)))
859 text++;
860 return !*text;
861}
862
863int
864is_shun(const char *text)
d76ed9a9 865{
866 if (*text == '@')
867 return 0;
868 text += strcspn(text, "@!% \t\r\n");
869 if (*text++ != '@')
870 return 0;
871 if (!*text)
872 return 0;
ec1a68c8 873 while (*text && (isalnum((char)*text) || strchr(".-?*:", *text)))
d76ed9a9 874 text++;
875 return !*text;
876}
877
878int
879split_ircmask(char *text, char **nick, char **ident, char **host)
880{
881 char *start;
882
883 start = text;
884 while (isalnum((char)*text) || strchr("=[]\\`^{}?*", *text))
885 text++;
886 if (*text != '!' || ((text - start) > NICKLEN))
887 return 0;
888 *text = 0;
889 if (nick)
890 *nick = start;
891
892 start = ++text;
893 while (*text && *text != '@' && !isspace((char)*text))
894 text++;
895 if (*text != '@' || ((text - start) > USERLEN))
896 return 0;
897 *text = 0;
898 if (ident)
899 *ident = start;
900
901 start = ++text;
ec1a68c8 902 while (*text && (isalnum((char)*text) || strchr(".-?*:", *text)))
d76ed9a9 903 text++;
904 if (host)
905 *host = start;
906 return !*text && ((text - start) <= HOSTLEN) && nick && ident && host;
907}
908
909char *
910sanitize_ircmask(char *input)
911{
912 unsigned int length, flag;
913 char *mask, *start, *output;
914
915 /* Sanitize everything in place; input *must* be a valid
916 hostmask. */
917 output = input;
918 flag = 0;
919
920 /* The nick is truncated at the end. */
921 length = 0;
922 mask = input;
923 while(*input++ != '!')
924 {
925 length++;
926 }
927 if(length > NICKLEN)
928 {
929 mask += NICKLEN;
930 *mask++ = '!';
931
932 /* This flag is used to indicate following parts should
933 be shifted. */
934 flag = 1;
935 }
936 else
937 {
938 mask = input;
939 }
940
941 /* The ident and host must be truncated at the beginning and
942 replaced with a '*' to be compatible with ircu. */
943 length = 0;
944 start = input;
945 while(*input++ != '@')
946 {
947 length++;
948 }
949 if(length > USERLEN || flag)
950 {
951 if(length > USERLEN)
952 {
953 start = input - USERLEN;
954 *mask++ = '*';
955 }
956 while(*start != '@')
957 {
958 *mask++ = *start++;
959 }
960 *mask++ = '@';
961
962 flag = 1;
963 }
964 else
965 {
966 mask = input;
967 }
968
969 length = 0;
970 start = input;
971 while(*input++)
972 {
973 length++;
974 }
975 if(length > HOSTLEN || flag)
976 {
977 if(length > HOSTLEN)
978 {
979 start = input - HOSTLEN;
980 *mask++ = '*';
981 }
982 while(*start)
983 {
984 *mask++ = *start++;
985 }
986 *mask = '\0';
987 }
988
989 return output;
990}
991
992static long
993TypeLength(char type)
994{
995 switch (type) {
996 case 'y': return 365*24*60*60;
ca22ccd3 997 case 'M': return 30*24*60*60;
d76ed9a9 998 case 'w': return 7*24*60*60;
999 case 'd': return 24*60*60;
1000 case 'h': return 60*60;
1001 case 'm': return 60;
1002 case 's': return 1;
1003 default: return 0;
1004 }
1005}
1006
ca22ccd3 1007/* This function is not entirely accurate as it does not take into account leap units
1008 * or varying months. TODO: use proper dateadd functions to calculate real seconds
1009 * from now for the units (eg 1M should be give us seconds till todays date next month)
1010 */
d76ed9a9 1011unsigned long
1012ParseInterval(const char *interval)
1013{
1014 unsigned long seconds = 0;
1015 int partial = 0;
1016 char c;
1017
1018 /* process the string, resetting the count if we find a unit character */
1019 while ((c = *interval++)) {
1136f709 1020 if (isdigit((int)c)) {
1021 partial = partial*10 + c - '0';
1022 } else if (strchr("yMwdhms", c)) {
1023 seconds += TypeLength(c) * partial;
1024 partial = 0;
1025 } else {
1026 return 0;
1027 }
d76ed9a9 1028 }
1029 /* assume the last chunk is seconds (the normal case) */
1030 return seconds + partial;
1031}
1032
1033static long
1034GetSizeMultiplier(char type)
1035{
1036 switch (type) {
1037 case 'G': case 'g': return 1024*1024*1024;
1038 case 'M': case 'm': return 1024*1024;
1039 case 'K': case 'k': return 1024;
1040 case 'B': case 'b': return 1;
1041 default: return 0;
1042 }
1043}
1044
1045unsigned long
1046ParseVolume(const char *volume)
1047{
1048 unsigned long accum = 0, partial = 0;
1049 char c;
1050 while ((c = *volume++)) {
1051 if (isdigit((int)c)) {
1052 partial = partial*10 + c - '0';
1053 } else {
1054 accum += GetSizeMultiplier(c) * partial;
1055 partial = 0;
1056 }
1057 }
1058 return accum + partial;
1059}
1060
d76ed9a9 1061char *
1062unsplit_string(char *set[], unsigned int max, char *dest)
1063{
1064 static char unsplit_buffer[MAXLEN*2];
1065 unsigned int ii, jj, pos;
1066
1067 if (!dest)
1068 dest = unsplit_buffer;
1069 for (ii=pos=0; ii<max; ii++) {
1070 for (jj=0; set[ii][jj]; jj++)
1071 dest[pos++] = set[ii][jj];
1072 dest[pos++] = ' ';
1073 }
1074 dest[--pos] = 0;
1075 return dest;
1076}
1077
1078char *
1079intervalString(char *output, time_t interval, struct handle_info *hi)
1080{
1081 static const struct {
1082 const char *msg_single;
1083 const char *msg_plural;
1084 long length;
1085 } unit[] = {
1086 { "MSG_YEAR", "MSG_YEARS", 365 * 24 * 60 * 60 },
1087 { "MSG_WEEK", "MSG_WEEKS", 7 * 24 * 60 * 60 },
1088 { "MSG_DAY", "MSG_DAYS", 24 * 60 * 60 },
1089 { "MSG_HOUR", "MSG_HOURS", 60 * 60 },
1090 { "MSG_MINUTE", "MSG_MINUTES", 60 },
1091 { "MSG_SECOND", "MSG_SECONDS", 1 }
1092 };
1093 struct language *lang;
1094 const char *msg;
1095 unsigned int type, words, pos, count;
1096
1097 lang = hi ? hi->language : lang_C;
1098 if(!interval)
1099 {
1100 msg = language_find_message(lang, "MSG_0_SECONDS");
1101 return strcpy(output, msg);
1102 }
1103
1104 for (type = 0, words = pos = 0;
1105 interval && (words < 2) && (type < ArrayLength(unit));
1106 type++) {
1107 if (interval < unit[type].length)
1108 continue;
1109 count = interval / unit[type].length;
1110 interval = interval % unit[type].length;
1111
1112 if (words++ == 1) {
1113 msg = language_find_message(lang, "MSG_AND");
de9510bc 1114 pos += sprintf(output + pos, "%s ", msg);
d76ed9a9 1115 }
1116 if (count == 1)
1117 msg = language_find_message(lang, unit[type].msg_single);
1118 else
1119 msg = language_find_message(lang, unit[type].msg_plural);
de9510bc 1120 pos += sprintf(output + pos, "%d%s", count, msg);
d76ed9a9 1121 }
1122
1123 output[pos] = 0;
1124 return output;
1125}
1126
1127int
1128getipbyname(const char *name, unsigned long *ip)
1129{
1130 struct hostent *he = gethostbyname(name);
1131 if (!he)
1132 return 0;
1133 if (he->h_addrtype != AF_INET)
1134 return 0;
1135 memcpy(ip, he->h_addr_list[0], sizeof(*ip));
1136 return 1;
1137}
1138
1139DEFINE_LIST(string_buffer, char)
1140
1141void
1142string_buffer_append_substring(struct string_buffer *buf, const char *tail, unsigned int len)
1143{
1144 while (buf->used + len >= buf->size) {
1145 if (!buf->size)
1146 buf->size = 16;
1147 else
1148 buf->size <<= 1;
1149 buf->list = realloc(buf->list, buf->size*sizeof(buf->list[0]));
1150 }
1151 memcpy(buf->list + buf->used, tail, len+1);
1152 buf->used += len;
1153}
1154
1155void
1156string_buffer_append_string(struct string_buffer *buf, const char *tail)
1157{
1158 string_buffer_append_substring(buf, tail, strlen(tail));
1159}
1160
1161void
1162string_buffer_append_vprintf(struct string_buffer *buf, const char *fmt, va_list args)
1163{
1164 va_list working;
1165 unsigned int len;
1166 int ret;
1167
1168 VA_COPY(working, args);
1169 len = strlen(fmt);
1170 if (!buf->list || ((buf->used + buf->size) < len)) {
1171 buf->size = buf->used + len;
1172 buf->list = realloc(buf->list, buf->size);
1173 }
1174 ret = vsnprintf(buf->list + buf->used, buf->size - buf->used, fmt, working);
1175 if (ret <= 0) {
1176 /* pre-C99 behavior; double buffer size until it is big enough */
1177 va_end(working);
1178 VA_COPY(working, args);
a32da4c7 1179 while ((ret = vsnprintf(buf->list + buf->used, buf->size - buf->used, fmt, working)) <= 0) {
d76ed9a9 1180 buf->size += len;
1181 buf->list = realloc(buf->list, buf->size);
1182 va_end(working);
1183 VA_COPY(working, args);
1184 }
1185 buf->used += ret;
1186 } else if (buf->used + ret < buf->size) {
1187 /* no need to increase allocation size */
1188 buf->used += ret;
1189 } else {
1190 /* now we know exactly how much space we need */
1191 if (buf->size <= buf->used + ret) {
1192 buf->size = buf->used + ret + 1;
1193 buf->list = realloc(buf->list, buf->size);
1194 }
1195 va_end(working);
1196 VA_COPY(working, args);
1197 buf->used += vsnprintf(buf->list + buf->used, buf->size, fmt, working);
1198 }
1199 va_end(working);
1200 va_end(args);
1201}
1202
1203void string_buffer_append_printf(struct string_buffer *buf, const char *fmt, ...)
1204{
1205 va_list args;
1206 va_start(args, fmt);
1207 string_buffer_append_vprintf(buf, fmt, args);
1208}
1209
1210void
1211string_buffer_replace(struct string_buffer *buf, unsigned int from, unsigned int len, const char *repl)
1212{
1213 unsigned int repl_len = strlen(repl);
1214 if (from > buf->used)
1215 return;
1216 if (len + from > buf->used)
1217 len = buf->used - from;
1218 buf->used = buf->used + repl_len - len;
1219 if (buf->size <= buf->used) {
1220 while (buf->used >= buf->size)
1221 buf->size <<= 1;
1222 buf->list = realloc(buf->list, buf->size*sizeof(buf->list[0]));
1223 }
1224 memmove(buf->list+from+repl_len, buf->list+from+len, strlen(buf->list+from+len));
1225 strcpy(buf->list+from, repl);
1226}
1227
1228struct string_list str_tab;
1229
1230const char *
1231strtab(unsigned int ii) {
1232 if (ii > 65536)
1233 return NULL;
1234 if (ii > str_tab.size) {
1235 unsigned int old_size = str_tab.size;
1236 while (ii >= str_tab.size)
1237 str_tab.size <<= 1;
1238 str_tab.list = realloc(str_tab.list, str_tab.size*sizeof(str_tab.list[0]));
1239 memset(str_tab.list+old_size, 0, (str_tab.size-old_size)*sizeof(str_tab.list[0]));
1240 }
1241 if (!str_tab.list[ii]) {
1242 str_tab.list[ii] = malloc(12);
1243 sprintf(str_tab.list[ii], "%u", ii);
1244 }
1245 return str_tab.list[ii];
1246}
1247
1248void
1249tools_init(void)
1250{
1251 unsigned int upr, lwr;
1252 for (lwr=0; lwr<256; ++lwr)
1253 tolower(lwr) = lwr;
1254 for (upr='A', lwr='a'; lwr <= 'z'; ++upr, ++lwr)
1255 tolower(upr) = lwr;
1256#ifdef WITH_PROTOCOL_P10
1257 for (upr='[', lwr='{'; lwr <= '~'; ++upr, ++lwr)
1258 tolower(upr) = lwr;
1259 for (upr=0xc0, lwr=0xe0; lwr <= 0xf6; ++upr, ++lwr)
1260 tolower(upr) = lwr;
1261 for (upr=0xd8, lwr=0xf8; lwr <= 0xfe; ++upr, ++lwr)
1262 tolower(upr) = lwr;
1263#endif
1264 str_tab.size = 1001;
1265 str_tab.list = calloc(str_tab.size, sizeof(str_tab.list[0]));
1266}
1267
1268void
1269tools_cleanup(void)
1270{
1271 unsigned int ii;
1272 for (ii=0; ii<str_tab.size; ++ii)
1273 free(str_tab.list[ii]);
1274 free(str_tab.list);
1275}
2aef5f4b 1276
1277/* mysep() is my answer to the strtok/strsep
1278 * issue. strsep is nice but doesn't skip
1279 * multiple dilimiters, which can really
1280 * offset tokens and cause huge corruption
1281 * so this function will use strsep but
31f23f13 1282 * act like strtok in that sense.
2aef5f4b 1283 */
1284char *mysep(char **sepstr, char *delim)
1285{
1286 static char *retstr;
1287
1288 if(!*sepstr || !**sepstr)
1289 return(NULL);
1290
1291 do
1292 {
1293 retstr = strsep(sepstr, delim);
1294 }while (retstr && !(*retstr));
1295
1296 return(retstr);
1297}
1298
c9bf23fe 1299/* Mallocing snprintf *
1300 *
1301 * If it overruns size, it will simply be safely truncated.
1302 */
1303char *
1304x3_msnprintf(const int size, const char *format, ...)
1305{
1306 va_list ap;
1307 char* buff = calloc(sizeof(char *), size+1);
1308
1309 va_start(ap, format);
1310 vsnprintf(buff, size, format, ap);
1311 va_end(ap);
1312 buff = realloc(buff, strlen(buff) + 1);
1313 return buff;
1314}
1315
eb5d6b73 1316char *time2str(time_t thetime)
1317{
1318 char *buf, *tmp;
1319
1320 buf = ctime(&thetime);
1321 tmp = (char *)strchr(buf, '\n');
1322 *tmp = '\0';
1323 return(buf);
1324}
1325
63665495 1326char* x3_strtok(char **save, char *str, char *fs)
1327{
1328 char *pos = *save; /* keep last position across calls */
1329 char *tmp;
1330
1331 if (str)
1332 pos = str; /* new string scan */
1333
1334 while (pos && *pos && strchr(fs, *pos) != NULL)
1335 pos++; /* skip leading separators */
1336
1337 if (!pos || !*pos)
1338 return (pos = *save = NULL); /* string contains only sep's */
1339
1340 tmp = pos; /* now, keep position of the token */
1341
1342 while (*pos && strchr(fs, *pos) == NULL)
1343 pos++; /* skip content of the token */
1344
1345 if (*pos)
1346 *pos++ = '\0'; /* remove first sep after the token */
1347 else
1348 pos = NULL; /* end of string */
1349
1350 *save = pos;
1351 return (tmp);
1352}
1353
4c26ef3e 1354int valid_email(const char *email)
1355{
1356 unsigned int i;
1357 for (i=0;i<strlen(email);i++)
1358 {
1359 if(!isalnum(email[i]) &&
1360 email[i] != '.' &&
1361 email[i] != '@' &&
1362 email[i] != '-' &&
1363 email[i] != '+' &&
1364 email[i] != '_' )
1365 return false;
1366 }
1367 if(strchr(email, '@') == NULL)
1368 return false;
1369 return true;
1370}
1371
668dc38e 1372/*
1373 * Create a string of form "foo!bar@fubar" given foo, bar and fubar
1374 * as the parameters. If NULL, they become "*".
1375 */
1376#define NUH_BUFSIZE (NICKLEN + USERLEN + HOSTLEN + 10)
1377static char *make_nick_user_host(char *namebuf, const char *nick,
1378 const char *name, const char *host)
1379{
1380 snprintf(namebuf, NUH_BUFSIZE, "%s!%s@%s", nick, name, host);
1381 return namebuf;
1382}
1383
1384/*
1385 * pretty_mask
1386 *
1387 * by Carlo Wood (Run), 05 Oct 1998.
1388 *
1389 * Canonify a mask.
1390 *
1391 * When the nick is longer then NICKLEN, it is cut off (its an error of course).
1392 * When the user name or host name are too long (USERLEN and HOSTLEN
1393 * respectively) then they are cut off at the start with a '*'.
1394 *
1395 * The following transformations are made:
1396 *
1397 * 1) xxx -> nick!*@*
1398 * 2) xxx.xxx -> *!*@host
1399 * 3) xxx!yyy -> nick!user@*
1400 * 4) xxx@yyy -> *!user@host
1401 * 5) xxx!yyy@zzz -> nick!user@host
1402 */
1403char *pretty_mask(char *mask)
1404{
1405 static char star[2] = { '*', 0 };
1406 static char retmask[NUH_BUFSIZE] = "";
1407 char *last_dot = NULL;
1408 char *ptr = NULL;
1409
1410 /* Case 1: default */
1411 char *nick = mask;
1412 char *user = star;
1413 char *host = star;
1414
1415 /* Do a _single_ pass through the characters of the mask: */
1416 for (ptr = mask; *ptr; ++ptr)
1417 {
1418 if (*ptr == '!')
1419 {
1420 /* Case 3 or 5: Found first '!' (without finding a '@' yet) */
1421 user = ++ptr;
1422 host = star;
1423 }
1424 else if (*ptr == '@')
1425 {
1426 /* Case 4: Found last '@' (without finding a '!' yet) */
1427 nick = star;
1428 user = mask;
1429 host = ++ptr;
1430 }
1431 else if (*ptr == '.')
1432 {
1433 /* Case 2: Found last '.' (without finding a '!' or '@' yet) */
1434 last_dot = ptr;
1435 continue;
1436 }
1437 else
1438 continue;
1439 for (; *ptr; ++ptr)
1440 {
1441 if (*ptr == '@')
1442 {
1443 /* Case 4 or 5: Found last '@' */
1444 host = ptr + 1;
1445 }
1446 }
1447 break;
1448 }
1449 if (user == star && last_dot)
1450 {
1451 /* Case 2: */
1452 nick = star;
1453 user = star;
1454 host = mask;
1455 }
1456 /* Check lengths */
1457 if (nick != star)
1458 {
1459 char *nick_end = (user != star) ? user - 1 : ptr;
1460 if (nick_end - nick > NICKLEN)
1461 nick[NICKLEN] = 0;
1462 *nick_end = 0;
1463 }
1464 if (user != star)
1465 {
1466 char *user_end = (host != star) ? host - 1 : ptr;
1467 if (user_end - user > USERLEN)
1468 {
1469 user = user_end - USERLEN;
1470 *user = '*';
1471 }
1472 *user_end = 0;
1473 }
1474 if (host != star && ptr - host > HOSTLEN)
1475 {
1476 host = ptr - HOSTLEN;
1477 *host = '*';
1478 }
1479 return make_nick_user_host(retmask, nick, user, host);
1480}
c9bf23fe 1481
7a278540 1482int str_is_number(const char *str)
c9bf23fe 1483{
1484 char *ptr;
1485 int ret = false;
7a278540 1486 for(ptr = (char *)str;*ptr;ptr++) {
c9bf23fe 1487 if((*ptr >= '0' && *ptr <= '9') || *ptr == '-')
1488 ret = true;
1489 else
1490 return false;
1491 }
1492 return ret;
1493}