]> jfr.im git - irc/evilnet/x3.git/blame - src/tools.c
roll version to 1.8.1 for conversion to git repository
[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) {
648 case 'a':
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);
80ca5a69 662 case 'c':
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);
634d32a3 741 default:
3b7fa78b 742 return -1;
634d32a3 743 }
744 }
745
d76ed9a9 746 /* Check the nick, if it's present */
2f61d1d7 747 if (flags & MATCH_USENICK) {
d76ed9a9 748 if (!(marker = strchr(glob, '!'))) {
2f61d1d7 749 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 750 return 0;
751 }
752 *marker = 0;
753 if (!match_ircglob(user->nick, glob)) return 0;
754 glob = marker + 1;
755 }
756 /* Check the ident */
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 an '@'", user->nick, orig_glob, flags);
d76ed9a9 759 return 0;
760 }
761 *marker = 0;
2f61d1d7 762 if (!match_ircglob(user->ident, glob))
763 return 0;
d76ed9a9 764 glob = marker + 1;
ec1a68c8 765 /* Check for a fakehost match. */
766 if (IsFakeHost(user) && match_ircglob(user->fakehost, glob))
efabf227 767 return 1;
2f61d1d7 768
769 /* Check for a sethost (S:lines) */
770 if (IsSetHost(user) && match_ircglob(user->sethost, glob))
efabf227 771 return 1;
2f61d1d7 772
ec1a68c8 773 /* Check for an account match. */
774 if (hidden_host_suffix && user->handle_info) {
775 char hidden_host[HOSTLEN+1];
776 snprintf(hidden_host, sizeof(hidden_host), "%s.%s", user->handle_info->handle, hidden_host_suffix);
777 if (match_ircglob(hidden_host, glob))
a32da4c7 778 return 1;
d76ed9a9 779 }
f16ad9e7 780
781 /* Match crypt hostname */
782 if (match_ircglob(user->crypthost, glob))
783 return 1;
784
785 /* Match crypt IP */
786 if (match_ircglob(user->cryptip, glob))
787 return 1;
788
2f61d1d7 789 /* If only matching the visible hostnames, bail early. */
790 if ((flags & MATCH_VISIBLE) && IsHiddenHost(user)
791 && (IsFakeHost(user) || (hidden_host_suffix && user->handle_info)))
792 return 0;
793 /* If it might be an IP glob, test that. */
794 if (!glob[strspn(glob, "0123456789./*?")]
795 && match_ircglob(irc_ntoa(&user->ip), glob))
796 return 1;
ec1a68c8 797 /* None of the above; could only be a hostname match. */
798 return match_ircglob(user->hostname, glob);
d76ed9a9 799}
800
801int
802is_ircmask(const char *text)
803{
8062bfc3 804 char *tmptext;
805
806 if (*text == '~') {
807 tmptext = alloca(strlen(text)+1);
808 tmptext = strdup(text);
809
810 tmptext++; /* get rid of the ~ */
811
812 if (*tmptext == '!')
813 tmptext++; /* get rid of the ! if it exists */
814
815 tmptext++; /* get rid of the ext ban type */
816
817 if (*tmptext == ':') {
818 tmptext++; /* get rid of the : */
819 while (*tmptext && !isspace((char)*tmptext))
820 tmptext++; /* get rid of the rest */
821 return !*tmptext;
822 }
823 }
824
d76ed9a9 825 while (*text && (isalnum((char)*text) || strchr("-_[]|\\`^{}?*", *text)))
826 text++;
827 if (*text++ != '!')
828 return 0;
829 while (*text && *text != '@' && !isspace((char)*text))
830 text++;
831 if (*text++ != '@')
832 return 0;
833 while (*text && !isspace((char)*text))
834 text++;
835 return !*text;
836}
837
838int
839is_gline(const char *text)
d914d1cb 840{
841 if (*text == '@')
842 return 0;
843 text += strcspn(text, "@!% \t\r\n");
844 if (*text++ != '@')
845 return 0;
846 if (!*text)
847 return 0;
848 while (*text && (isalnum((char)*text) || strchr(".-?*:", *text)))
849 text++;
850 return !*text;
851}
852
853int
854is_shun(const char *text)
d76ed9a9 855{
856 if (*text == '@')
857 return 0;
858 text += strcspn(text, "@!% \t\r\n");
859 if (*text++ != '@')
860 return 0;
861 if (!*text)
862 return 0;
ec1a68c8 863 while (*text && (isalnum((char)*text) || strchr(".-?*:", *text)))
d76ed9a9 864 text++;
865 return !*text;
866}
867
868int
869split_ircmask(char *text, char **nick, char **ident, char **host)
870{
871 char *start;
872
873 start = text;
874 while (isalnum((char)*text) || strchr("=[]\\`^{}?*", *text))
875 text++;
876 if (*text != '!' || ((text - start) > NICKLEN))
877 return 0;
878 *text = 0;
879 if (nick)
880 *nick = start;
881
882 start = ++text;
883 while (*text && *text != '@' && !isspace((char)*text))
884 text++;
885 if (*text != '@' || ((text - start) > USERLEN))
886 return 0;
887 *text = 0;
888 if (ident)
889 *ident = start;
890
891 start = ++text;
ec1a68c8 892 while (*text && (isalnum((char)*text) || strchr(".-?*:", *text)))
d76ed9a9 893 text++;
894 if (host)
895 *host = start;
896 return !*text && ((text - start) <= HOSTLEN) && nick && ident && host;
897}
898
899char *
900sanitize_ircmask(char *input)
901{
902 unsigned int length, flag;
903 char *mask, *start, *output;
904
905 /* Sanitize everything in place; input *must* be a valid
906 hostmask. */
907 output = input;
908 flag = 0;
909
910 /* The nick is truncated at the end. */
911 length = 0;
912 mask = input;
913 while(*input++ != '!')
914 {
915 length++;
916 }
917 if(length > NICKLEN)
918 {
919 mask += NICKLEN;
920 *mask++ = '!';
921
922 /* This flag is used to indicate following parts should
923 be shifted. */
924 flag = 1;
925 }
926 else
927 {
928 mask = input;
929 }
930
931 /* The ident and host must be truncated at the beginning and
932 replaced with a '*' to be compatible with ircu. */
933 length = 0;
934 start = input;
935 while(*input++ != '@')
936 {
937 length++;
938 }
939 if(length > USERLEN || flag)
940 {
941 if(length > USERLEN)
942 {
943 start = input - USERLEN;
944 *mask++ = '*';
945 }
946 while(*start != '@')
947 {
948 *mask++ = *start++;
949 }
950 *mask++ = '@';
951
952 flag = 1;
953 }
954 else
955 {
956 mask = input;
957 }
958
959 length = 0;
960 start = input;
961 while(*input++)
962 {
963 length++;
964 }
965 if(length > HOSTLEN || flag)
966 {
967 if(length > HOSTLEN)
968 {
969 start = input - HOSTLEN;
970 *mask++ = '*';
971 }
972 while(*start)
973 {
974 *mask++ = *start++;
975 }
976 *mask = '\0';
977 }
978
979 return output;
980}
981
982static long
983TypeLength(char type)
984{
985 switch (type) {
986 case 'y': return 365*24*60*60;
ca22ccd3 987 case 'M': return 30*24*60*60;
d76ed9a9 988 case 'w': return 7*24*60*60;
989 case 'd': return 24*60*60;
990 case 'h': return 60*60;
991 case 'm': return 60;
992 case 's': return 1;
993 default: return 0;
994 }
995}
996
ca22ccd3 997/* This function is not entirely accurate as it does not take into account leap units
998 * or varying months. TODO: use proper dateadd functions to calculate real seconds
999 * from now for the units (eg 1M should be give us seconds till todays date next month)
1000 */
d76ed9a9 1001unsigned long
1002ParseInterval(const char *interval)
1003{
1004 unsigned long seconds = 0;
1005 int partial = 0;
1006 char c;
1007
1008 /* process the string, resetting the count if we find a unit character */
1009 while ((c = *interval++)) {
1136f709 1010 if (isdigit((int)c)) {
1011 partial = partial*10 + c - '0';
1012 } else if (strchr("yMwdhms", c)) {
1013 seconds += TypeLength(c) * partial;
1014 partial = 0;
1015 } else {
1016 return 0;
1017 }
d76ed9a9 1018 }
1019 /* assume the last chunk is seconds (the normal case) */
1020 return seconds + partial;
1021}
1022
1023static long
1024GetSizeMultiplier(char type)
1025{
1026 switch (type) {
1027 case 'G': case 'g': return 1024*1024*1024;
1028 case 'M': case 'm': return 1024*1024;
1029 case 'K': case 'k': return 1024;
1030 case 'B': case 'b': return 1;
1031 default: return 0;
1032 }
1033}
1034
1035unsigned long
1036ParseVolume(const char *volume)
1037{
1038 unsigned long accum = 0, partial = 0;
1039 char c;
1040 while ((c = *volume++)) {
1041 if (isdigit((int)c)) {
1042 partial = partial*10 + c - '0';
1043 } else {
1044 accum += GetSizeMultiplier(c) * partial;
1045 partial = 0;
1046 }
1047 }
1048 return accum + partial;
1049}
1050
d76ed9a9 1051char *
1052unsplit_string(char *set[], unsigned int max, char *dest)
1053{
1054 static char unsplit_buffer[MAXLEN*2];
1055 unsigned int ii, jj, pos;
1056
1057 if (!dest)
1058 dest = unsplit_buffer;
1059 for (ii=pos=0; ii<max; ii++) {
1060 for (jj=0; set[ii][jj]; jj++)
1061 dest[pos++] = set[ii][jj];
1062 dest[pos++] = ' ';
1063 }
1064 dest[--pos] = 0;
1065 return dest;
1066}
1067
1068char *
1069intervalString(char *output, time_t interval, struct handle_info *hi)
1070{
1071 static const struct {
1072 const char *msg_single;
1073 const char *msg_plural;
1074 long length;
1075 } unit[] = {
1076 { "MSG_YEAR", "MSG_YEARS", 365 * 24 * 60 * 60 },
1077 { "MSG_WEEK", "MSG_WEEKS", 7 * 24 * 60 * 60 },
1078 { "MSG_DAY", "MSG_DAYS", 24 * 60 * 60 },
1079 { "MSG_HOUR", "MSG_HOURS", 60 * 60 },
1080 { "MSG_MINUTE", "MSG_MINUTES", 60 },
1081 { "MSG_SECOND", "MSG_SECONDS", 1 }
1082 };
1083 struct language *lang;
1084 const char *msg;
1085 unsigned int type, words, pos, count;
1086
1087 lang = hi ? hi->language : lang_C;
1088 if(!interval)
1089 {
1090 msg = language_find_message(lang, "MSG_0_SECONDS");
1091 return strcpy(output, msg);
1092 }
1093
1094 for (type = 0, words = pos = 0;
1095 interval && (words < 2) && (type < ArrayLength(unit));
1096 type++) {
1097 if (interval < unit[type].length)
1098 continue;
1099 count = interval / unit[type].length;
1100 interval = interval % unit[type].length;
1101
1102 if (words++ == 1) {
1103 msg = language_find_message(lang, "MSG_AND");
de9510bc 1104 pos += sprintf(output + pos, "%s ", msg);
d76ed9a9 1105 }
1106 if (count == 1)
1107 msg = language_find_message(lang, unit[type].msg_single);
1108 else
1109 msg = language_find_message(lang, unit[type].msg_plural);
de9510bc 1110 pos += sprintf(output + pos, "%d%s", count, msg);
d76ed9a9 1111 }
1112
1113 output[pos] = 0;
1114 return output;
1115}
1116
1117int
1118getipbyname(const char *name, unsigned long *ip)
1119{
1120 struct hostent *he = gethostbyname(name);
1121 if (!he)
1122 return 0;
1123 if (he->h_addrtype != AF_INET)
1124 return 0;
1125 memcpy(ip, he->h_addr_list[0], sizeof(*ip));
1126 return 1;
1127}
1128
1129DEFINE_LIST(string_buffer, char)
1130
1131void
1132string_buffer_append_substring(struct string_buffer *buf, const char *tail, unsigned int len)
1133{
1134 while (buf->used + len >= buf->size) {
1135 if (!buf->size)
1136 buf->size = 16;
1137 else
1138 buf->size <<= 1;
1139 buf->list = realloc(buf->list, buf->size*sizeof(buf->list[0]));
1140 }
1141 memcpy(buf->list + buf->used, tail, len+1);
1142 buf->used += len;
1143}
1144
1145void
1146string_buffer_append_string(struct string_buffer *buf, const char *tail)
1147{
1148 string_buffer_append_substring(buf, tail, strlen(tail));
1149}
1150
1151void
1152string_buffer_append_vprintf(struct string_buffer *buf, const char *fmt, va_list args)
1153{
1154 va_list working;
1155 unsigned int len;
1156 int ret;
1157
1158 VA_COPY(working, args);
1159 len = strlen(fmt);
1160 if (!buf->list || ((buf->used + buf->size) < len)) {
1161 buf->size = buf->used + len;
1162 buf->list = realloc(buf->list, buf->size);
1163 }
1164 ret = vsnprintf(buf->list + buf->used, buf->size - buf->used, fmt, working);
1165 if (ret <= 0) {
1166 /* pre-C99 behavior; double buffer size until it is big enough */
1167 va_end(working);
1168 VA_COPY(working, args);
a32da4c7 1169 while ((ret = vsnprintf(buf->list + buf->used, buf->size - buf->used, fmt, working)) <= 0) {
d76ed9a9 1170 buf->size += len;
1171 buf->list = realloc(buf->list, buf->size);
1172 va_end(working);
1173 VA_COPY(working, args);
1174 }
1175 buf->used += ret;
1176 } else if (buf->used + ret < buf->size) {
1177 /* no need to increase allocation size */
1178 buf->used += ret;
1179 } else {
1180 /* now we know exactly how much space we need */
1181 if (buf->size <= buf->used + ret) {
1182 buf->size = buf->used + ret + 1;
1183 buf->list = realloc(buf->list, buf->size);
1184 }
1185 va_end(working);
1186 VA_COPY(working, args);
1187 buf->used += vsnprintf(buf->list + buf->used, buf->size, fmt, working);
1188 }
1189 va_end(working);
1190 va_end(args);
1191}
1192
1193void string_buffer_append_printf(struct string_buffer *buf, const char *fmt, ...)
1194{
1195 va_list args;
1196 va_start(args, fmt);
1197 string_buffer_append_vprintf(buf, fmt, args);
1198}
1199
1200void
1201string_buffer_replace(struct string_buffer *buf, unsigned int from, unsigned int len, const char *repl)
1202{
1203 unsigned int repl_len = strlen(repl);
1204 if (from > buf->used)
1205 return;
1206 if (len + from > buf->used)
1207 len = buf->used - from;
1208 buf->used = buf->used + repl_len - len;
1209 if (buf->size <= buf->used) {
1210 while (buf->used >= buf->size)
1211 buf->size <<= 1;
1212 buf->list = realloc(buf->list, buf->size*sizeof(buf->list[0]));
1213 }
1214 memmove(buf->list+from+repl_len, buf->list+from+len, strlen(buf->list+from+len));
1215 strcpy(buf->list+from, repl);
1216}
1217
1218struct string_list str_tab;
1219
1220const char *
1221strtab(unsigned int ii) {
1222 if (ii > 65536)
1223 return NULL;
1224 if (ii > str_tab.size) {
1225 unsigned int old_size = str_tab.size;
1226 while (ii >= str_tab.size)
1227 str_tab.size <<= 1;
1228 str_tab.list = realloc(str_tab.list, str_tab.size*sizeof(str_tab.list[0]));
1229 memset(str_tab.list+old_size, 0, (str_tab.size-old_size)*sizeof(str_tab.list[0]));
1230 }
1231 if (!str_tab.list[ii]) {
1232 str_tab.list[ii] = malloc(12);
1233 sprintf(str_tab.list[ii], "%u", ii);
1234 }
1235 return str_tab.list[ii];
1236}
1237
1238void
1239tools_init(void)
1240{
1241 unsigned int upr, lwr;
1242 for (lwr=0; lwr<256; ++lwr)
1243 tolower(lwr) = lwr;
1244 for (upr='A', lwr='a'; lwr <= 'z'; ++upr, ++lwr)
1245 tolower(upr) = lwr;
1246#ifdef WITH_PROTOCOL_P10
1247 for (upr='[', lwr='{'; lwr <= '~'; ++upr, ++lwr)
1248 tolower(upr) = lwr;
1249 for (upr=0xc0, lwr=0xe0; lwr <= 0xf6; ++upr, ++lwr)
1250 tolower(upr) = lwr;
1251 for (upr=0xd8, lwr=0xf8; lwr <= 0xfe; ++upr, ++lwr)
1252 tolower(upr) = lwr;
1253#endif
1254 str_tab.size = 1001;
1255 str_tab.list = calloc(str_tab.size, sizeof(str_tab.list[0]));
1256}
1257
1258void
1259tools_cleanup(void)
1260{
1261 unsigned int ii;
1262 for (ii=0; ii<str_tab.size; ++ii)
1263 free(str_tab.list[ii]);
1264 free(str_tab.list);
1265}
2aef5f4b 1266
1267/* mysep() is my answer to the strtok/strsep
1268 * issue. strsep is nice but doesn't skip
1269 * multiple dilimiters, which can really
1270 * offset tokens and cause huge corruption
1271 * so this function will use strsep but
31f23f13 1272 * act like strtok in that sense.
2aef5f4b 1273 */
1274char *mysep(char **sepstr, char *delim)
1275{
1276 static char *retstr;
1277
1278 if(!*sepstr || !**sepstr)
1279 return(NULL);
1280
1281 do
1282 {
1283 retstr = strsep(sepstr, delim);
1284 }while (retstr && !(*retstr));
1285
1286 return(retstr);
1287}
1288
c9bf23fe 1289/* Mallocing snprintf *
1290 *
1291 * If it overruns size, it will simply be safely truncated.
1292 */
1293char *
1294x3_msnprintf(const int size, const char *format, ...)
1295{
1296 va_list ap;
1297 char* buff = calloc(sizeof(char *), size+1);
1298
1299 va_start(ap, format);
1300 vsnprintf(buff, size, format, ap);
1301 va_end(ap);
1302 buff = realloc(buff, strlen(buff) + 1);
1303 return buff;
1304}
1305
eb5d6b73 1306char *time2str(time_t thetime)
1307{
1308 char *buf, *tmp;
1309
1310 buf = ctime(&thetime);
1311 tmp = (char *)strchr(buf, '\n');
1312 *tmp = '\0';
1313 return(buf);
1314}
1315
63665495 1316char* x3_strtok(char **save, char *str, char *fs)
1317{
1318 char *pos = *save; /* keep last position across calls */
1319 char *tmp;
1320
1321 if (str)
1322 pos = str; /* new string scan */
1323
1324 while (pos && *pos && strchr(fs, *pos) != NULL)
1325 pos++; /* skip leading separators */
1326
1327 if (!pos || !*pos)
1328 return (pos = *save = NULL); /* string contains only sep's */
1329
1330 tmp = pos; /* now, keep position of the token */
1331
1332 while (*pos && strchr(fs, *pos) == NULL)
1333 pos++; /* skip content of the token */
1334
1335 if (*pos)
1336 *pos++ = '\0'; /* remove first sep after the token */
1337 else
1338 pos = NULL; /* end of string */
1339
1340 *save = pos;
1341 return (tmp);
1342}
1343
4c26ef3e 1344int valid_email(const char *email)
1345{
1346 unsigned int i;
1347 for (i=0;i<strlen(email);i++)
1348 {
1349 if(!isalnum(email[i]) &&
1350 email[i] != '.' &&
1351 email[i] != '@' &&
1352 email[i] != '-' &&
1353 email[i] != '+' &&
1354 email[i] != '_' )
1355 return false;
1356 }
1357 if(strchr(email, '@') == NULL)
1358 return false;
1359 return true;
1360}
1361
668dc38e 1362/*
1363 * Create a string of form "foo!bar@fubar" given foo, bar and fubar
1364 * as the parameters. If NULL, they become "*".
1365 */
1366#define NUH_BUFSIZE (NICKLEN + USERLEN + HOSTLEN + 10)
1367static char *make_nick_user_host(char *namebuf, const char *nick,
1368 const char *name, const char *host)
1369{
1370 snprintf(namebuf, NUH_BUFSIZE, "%s!%s@%s", nick, name, host);
1371 return namebuf;
1372}
1373
1374/*
1375 * pretty_mask
1376 *
1377 * by Carlo Wood (Run), 05 Oct 1998.
1378 *
1379 * Canonify a mask.
1380 *
1381 * When the nick is longer then NICKLEN, it is cut off (its an error of course).
1382 * When the user name or host name are too long (USERLEN and HOSTLEN
1383 * respectively) then they are cut off at the start with a '*'.
1384 *
1385 * The following transformations are made:
1386 *
1387 * 1) xxx -> nick!*@*
1388 * 2) xxx.xxx -> *!*@host
1389 * 3) xxx!yyy -> nick!user@*
1390 * 4) xxx@yyy -> *!user@host
1391 * 5) xxx!yyy@zzz -> nick!user@host
1392 */
1393char *pretty_mask(char *mask)
1394{
1395 static char star[2] = { '*', 0 };
1396 static char retmask[NUH_BUFSIZE] = "";
1397 char *last_dot = NULL;
1398 char *ptr = NULL;
1399
1400 /* Case 1: default */
1401 char *nick = mask;
1402 char *user = star;
1403 char *host = star;
1404
1405 /* Do a _single_ pass through the characters of the mask: */
1406 for (ptr = mask; *ptr; ++ptr)
1407 {
1408 if (*ptr == '!')
1409 {
1410 /* Case 3 or 5: Found first '!' (without finding a '@' yet) */
1411 user = ++ptr;
1412 host = star;
1413 }
1414 else if (*ptr == '@')
1415 {
1416 /* Case 4: Found last '@' (without finding a '!' yet) */
1417 nick = star;
1418 user = mask;
1419 host = ++ptr;
1420 }
1421 else if (*ptr == '.')
1422 {
1423 /* Case 2: Found last '.' (without finding a '!' or '@' yet) */
1424 last_dot = ptr;
1425 continue;
1426 }
1427 else
1428 continue;
1429 for (; *ptr; ++ptr)
1430 {
1431 if (*ptr == '@')
1432 {
1433 /* Case 4 or 5: Found last '@' */
1434 host = ptr + 1;
1435 }
1436 }
1437 break;
1438 }
1439 if (user == star && last_dot)
1440 {
1441 /* Case 2: */
1442 nick = star;
1443 user = star;
1444 host = mask;
1445 }
1446 /* Check lengths */
1447 if (nick != star)
1448 {
1449 char *nick_end = (user != star) ? user - 1 : ptr;
1450 if (nick_end - nick > NICKLEN)
1451 nick[NICKLEN] = 0;
1452 *nick_end = 0;
1453 }
1454 if (user != star)
1455 {
1456 char *user_end = (host != star) ? host - 1 : ptr;
1457 if (user_end - user > USERLEN)
1458 {
1459 user = user_end - USERLEN;
1460 *user = '*';
1461 }
1462 *user_end = 0;
1463 }
1464 if (host != star && ptr - host > HOSTLEN)
1465 {
1466 host = ptr - HOSTLEN;
1467 *host = '*';
1468 }
1469 return make_nick_user_host(retmask, nick, user, host);
1470}
c9bf23fe 1471
7a278540 1472int str_is_number(const char *str)
c9bf23fe 1473{
1474 char *ptr;
1475 int ret = false;
7a278540 1476 for(ptr = (char *)str;*ptr;ptr++) {
c9bf23fe 1477 if((*ptr >= '0' && *ptr <= '9') || *ptr == '-')
1478 ret = true;
1479 else
1480 return false;
1481 }
1482 return ret;
1483}