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