]> jfr.im git - irc/evilnet/x3.git/blame - src/tools.c
changing how libtre is dealt with
[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 }
2f61d1d7 628 /* If only matching the visible hostnames, bail early. */
629 if ((flags & MATCH_VISIBLE) && IsHiddenHost(user)
630 && (IsFakeHost(user) || (hidden_host_suffix && user->handle_info)))
631 return 0;
632 /* If it might be an IP glob, test that. */
633 if (!glob[strspn(glob, "0123456789./*?")]
634 && match_ircglob(irc_ntoa(&user->ip), glob))
635 return 1;
ec1a68c8 636 /* None of the above; could only be a hostname match. */
637 return match_ircglob(user->hostname, glob);
d76ed9a9
AS
638}
639
640int
641is_ircmask(const char *text)
642{
643 while (*text && (isalnum((char)*text) || strchr("-_[]|\\`^{}?*", *text)))
644 text++;
645 if (*text++ != '!')
646 return 0;
647 while (*text && *text != '@' && !isspace((char)*text))
648 text++;
649 if (*text++ != '@')
650 return 0;
651 while (*text && !isspace((char)*text))
652 text++;
653 return !*text;
654}
655
656int
657is_gline(const char *text)
d914d1cb 658{
659 if (*text == '@')
660 return 0;
661 text += strcspn(text, "@!% \t\r\n");
662 if (*text++ != '@')
663 return 0;
664 if (!*text)
665 return 0;
666 while (*text && (isalnum((char)*text) || strchr(".-?*:", *text)))
667 text++;
668 return !*text;
669}
670
671int
672is_shun(const char *text)
d76ed9a9
AS
673{
674 if (*text == '@')
675 return 0;
676 text += strcspn(text, "@!% \t\r\n");
677 if (*text++ != '@')
678 return 0;
679 if (!*text)
680 return 0;
ec1a68c8 681 while (*text && (isalnum((char)*text) || strchr(".-?*:", *text)))
d76ed9a9
AS
682 text++;
683 return !*text;
684}
685
686int
687split_ircmask(char *text, char **nick, char **ident, char **host)
688{
689 char *start;
690
691 start = text;
692 while (isalnum((char)*text) || strchr("=[]\\`^{}?*", *text))
693 text++;
694 if (*text != '!' || ((text - start) > NICKLEN))
695 return 0;
696 *text = 0;
697 if (nick)
698 *nick = start;
699
700 start = ++text;
701 while (*text && *text != '@' && !isspace((char)*text))
702 text++;
703 if (*text != '@' || ((text - start) > USERLEN))
704 return 0;
705 *text = 0;
706 if (ident)
707 *ident = start;
708
709 start = ++text;
ec1a68c8 710 while (*text && (isalnum((char)*text) || strchr(".-?*:", *text)))
d76ed9a9
AS
711 text++;
712 if (host)
713 *host = start;
714 return !*text && ((text - start) <= HOSTLEN) && nick && ident && host;
715}
716
717char *
718sanitize_ircmask(char *input)
719{
720 unsigned int length, flag;
721 char *mask, *start, *output;
722
723 /* Sanitize everything in place; input *must* be a valid
724 hostmask. */
725 output = input;
726 flag = 0;
727
728 /* The nick is truncated at the end. */
729 length = 0;
730 mask = input;
731 while(*input++ != '!')
732 {
733 length++;
734 }
735 if(length > NICKLEN)
736 {
737 mask += NICKLEN;
738 *mask++ = '!';
739
740 /* This flag is used to indicate following parts should
741 be shifted. */
742 flag = 1;
743 }
744 else
745 {
746 mask = input;
747 }
748
749 /* The ident and host must be truncated at the beginning and
750 replaced with a '*' to be compatible with ircu. */
751 length = 0;
752 start = input;
753 while(*input++ != '@')
754 {
755 length++;
756 }
757 if(length > USERLEN || flag)
758 {
759 if(length > USERLEN)
760 {
761 start = input - USERLEN;
762 *mask++ = '*';
763 }
764 while(*start != '@')
765 {
766 *mask++ = *start++;
767 }
768 *mask++ = '@';
769
770 flag = 1;
771 }
772 else
773 {
774 mask = input;
775 }
776
777 length = 0;
778 start = input;
779 while(*input++)
780 {
781 length++;
782 }
783 if(length > HOSTLEN || flag)
784 {
785 if(length > HOSTLEN)
786 {
787 start = input - HOSTLEN;
788 *mask++ = '*';
789 }
790 while(*start)
791 {
792 *mask++ = *start++;
793 }
794 *mask = '\0';
795 }
796
797 return output;
798}
799
800static long
801TypeLength(char type)
802{
803 switch (type) {
804 case 'y': return 365*24*60*60;
ca22ccd3 805 case 'M': return 30*24*60*60;
d76ed9a9
AS
806 case 'w': return 7*24*60*60;
807 case 'd': return 24*60*60;
808 case 'h': return 60*60;
809 case 'm': return 60;
810 case 's': return 1;
811 default: return 0;
812 }
813}
814
ca22ccd3
AS
815/* This function is not entirely accurate as it does not take into account leap units
816 * or varying months. TODO: use proper dateadd functions to calculate real seconds
817 * from now for the units (eg 1M should be give us seconds till todays date next month)
818 */
d76ed9a9
AS
819unsigned long
820ParseInterval(const char *interval)
821{
822 unsigned long seconds = 0;
823 int partial = 0;
824 char c;
825
826 /* process the string, resetting the count if we find a unit character */
827 while ((c = *interval++)) {
828 if (isdigit((int)c)) {
829 partial = partial*10 + c - '0';
830 } else {
831 seconds += TypeLength(c) * partial;
832 partial = 0;
833 }
834 }
835 /* assume the last chunk is seconds (the normal case) */
836 return seconds + partial;
837}
838
839static long
840GetSizeMultiplier(char type)
841{
842 switch (type) {
843 case 'G': case 'g': return 1024*1024*1024;
844 case 'M': case 'm': return 1024*1024;
845 case 'K': case 'k': return 1024;
846 case 'B': case 'b': return 1;
847 default: return 0;
848 }
849}
850
851unsigned long
852ParseVolume(const char *volume)
853{
854 unsigned long accum = 0, partial = 0;
855 char c;
856 while ((c = *volume++)) {
857 if (isdigit((int)c)) {
858 partial = partial*10 + c - '0';
859 } else {
860 accum += GetSizeMultiplier(c) * partial;
861 partial = 0;
862 }
863 }
864 return accum + partial;
865}
866
d76ed9a9
AS
867char *
868unsplit_string(char *set[], unsigned int max, char *dest)
869{
870 static char unsplit_buffer[MAXLEN*2];
871 unsigned int ii, jj, pos;
872
873 if (!dest)
874 dest = unsplit_buffer;
875 for (ii=pos=0; ii<max; ii++) {
876 for (jj=0; set[ii][jj]; jj++)
877 dest[pos++] = set[ii][jj];
878 dest[pos++] = ' ';
879 }
880 dest[--pos] = 0;
881 return dest;
882}
883
884char *
885intervalString(char *output, time_t interval, struct handle_info *hi)
886{
887 static const struct {
888 const char *msg_single;
889 const char *msg_plural;
890 long length;
891 } unit[] = {
892 { "MSG_YEAR", "MSG_YEARS", 365 * 24 * 60 * 60 },
893 { "MSG_WEEK", "MSG_WEEKS", 7 * 24 * 60 * 60 },
894 { "MSG_DAY", "MSG_DAYS", 24 * 60 * 60 },
895 { "MSG_HOUR", "MSG_HOURS", 60 * 60 },
896 { "MSG_MINUTE", "MSG_MINUTES", 60 },
897 { "MSG_SECOND", "MSG_SECONDS", 1 }
898 };
899 struct language *lang;
900 const char *msg;
901 unsigned int type, words, pos, count;
902
903 lang = hi ? hi->language : lang_C;
904 if(!interval)
905 {
906 msg = language_find_message(lang, "MSG_0_SECONDS");
907 return strcpy(output, msg);
908 }
909
910 for (type = 0, words = pos = 0;
911 interval && (words < 2) && (type < ArrayLength(unit));
912 type++) {
913 if (interval < unit[type].length)
914 continue;
915 count = interval / unit[type].length;
916 interval = interval % unit[type].length;
917
918 if (words++ == 1) {
919 msg = language_find_message(lang, "MSG_AND");
de9510bc 920 pos += sprintf(output + pos, "%s ", msg);
d76ed9a9
AS
921 }
922 if (count == 1)
923 msg = language_find_message(lang, unit[type].msg_single);
924 else
925 msg = language_find_message(lang, unit[type].msg_plural);
de9510bc 926 pos += sprintf(output + pos, "%d%s", count, msg);
d76ed9a9
AS
927 }
928
929 output[pos] = 0;
930 return output;
931}
932
933int
934getipbyname(const char *name, unsigned long *ip)
935{
936 struct hostent *he = gethostbyname(name);
937 if (!he)
938 return 0;
939 if (he->h_addrtype != AF_INET)
940 return 0;
941 memcpy(ip, he->h_addr_list[0], sizeof(*ip));
942 return 1;
943}
944
945DEFINE_LIST(string_buffer, char)
946
947void
948string_buffer_append_substring(struct string_buffer *buf, const char *tail, unsigned int len)
949{
950 while (buf->used + len >= buf->size) {
951 if (!buf->size)
952 buf->size = 16;
953 else
954 buf->size <<= 1;
955 buf->list = realloc(buf->list, buf->size*sizeof(buf->list[0]));
956 }
957 memcpy(buf->list + buf->used, tail, len+1);
958 buf->used += len;
959}
960
961void
962string_buffer_append_string(struct string_buffer *buf, const char *tail)
963{
964 string_buffer_append_substring(buf, tail, strlen(tail));
965}
966
967void
968string_buffer_append_vprintf(struct string_buffer *buf, const char *fmt, va_list args)
969{
970 va_list working;
971 unsigned int len;
972 int ret;
973
974 VA_COPY(working, args);
975 len = strlen(fmt);
976 if (!buf->list || ((buf->used + buf->size) < len)) {
977 buf->size = buf->used + len;
978 buf->list = realloc(buf->list, buf->size);
979 }
980 ret = vsnprintf(buf->list + buf->used, buf->size - buf->used, fmt, working);
981 if (ret <= 0) {
982 /* pre-C99 behavior; double buffer size until it is big enough */
983 va_end(working);
984 VA_COPY(working, args);
a32da4c7 985 while ((ret = vsnprintf(buf->list + buf->used, buf->size - buf->used, fmt, working)) <= 0) {
d76ed9a9
AS
986 buf->size += len;
987 buf->list = realloc(buf->list, buf->size);
988 va_end(working);
989 VA_COPY(working, args);
990 }
991 buf->used += ret;
992 } else if (buf->used + ret < buf->size) {
993 /* no need to increase allocation size */
994 buf->used += ret;
995 } else {
996 /* now we know exactly how much space we need */
997 if (buf->size <= buf->used + ret) {
998 buf->size = buf->used + ret + 1;
999 buf->list = realloc(buf->list, buf->size);
1000 }
1001 va_end(working);
1002 VA_COPY(working, args);
1003 buf->used += vsnprintf(buf->list + buf->used, buf->size, fmt, working);
1004 }
1005 va_end(working);
1006 va_end(args);
1007}
1008
1009void string_buffer_append_printf(struct string_buffer *buf, const char *fmt, ...)
1010{
1011 va_list args;
1012 va_start(args, fmt);
1013 string_buffer_append_vprintf(buf, fmt, args);
1014}
1015
1016void
1017string_buffer_replace(struct string_buffer *buf, unsigned int from, unsigned int len, const char *repl)
1018{
1019 unsigned int repl_len = strlen(repl);
1020 if (from > buf->used)
1021 return;
1022 if (len + from > buf->used)
1023 len = buf->used - from;
1024 buf->used = buf->used + repl_len - len;
1025 if (buf->size <= buf->used) {
1026 while (buf->used >= buf->size)
1027 buf->size <<= 1;
1028 buf->list = realloc(buf->list, buf->size*sizeof(buf->list[0]));
1029 }
1030 memmove(buf->list+from+repl_len, buf->list+from+len, strlen(buf->list+from+len));
1031 strcpy(buf->list+from, repl);
1032}
1033
1034struct string_list str_tab;
1035
1036const char *
1037strtab(unsigned int ii) {
1038 if (ii > 65536)
1039 return NULL;
1040 if (ii > str_tab.size) {
1041 unsigned int old_size = str_tab.size;
1042 while (ii >= str_tab.size)
1043 str_tab.size <<= 1;
1044 str_tab.list = realloc(str_tab.list, str_tab.size*sizeof(str_tab.list[0]));
1045 memset(str_tab.list+old_size, 0, (str_tab.size-old_size)*sizeof(str_tab.list[0]));
1046 }
1047 if (!str_tab.list[ii]) {
1048 str_tab.list[ii] = malloc(12);
1049 sprintf(str_tab.list[ii], "%u", ii);
1050 }
1051 return str_tab.list[ii];
1052}
1053
1054void
1055tools_init(void)
1056{
1057 unsigned int upr, lwr;
1058 for (lwr=0; lwr<256; ++lwr)
1059 tolower(lwr) = lwr;
1060 for (upr='A', lwr='a'; lwr <= 'z'; ++upr, ++lwr)
1061 tolower(upr) = lwr;
1062#ifdef WITH_PROTOCOL_P10
1063 for (upr='[', lwr='{'; lwr <= '~'; ++upr, ++lwr)
1064 tolower(upr) = lwr;
1065 for (upr=0xc0, lwr=0xe0; lwr <= 0xf6; ++upr, ++lwr)
1066 tolower(upr) = lwr;
1067 for (upr=0xd8, lwr=0xf8; lwr <= 0xfe; ++upr, ++lwr)
1068 tolower(upr) = lwr;
1069#endif
1070 str_tab.size = 1001;
1071 str_tab.list = calloc(str_tab.size, sizeof(str_tab.list[0]));
1072}
1073
1074void
1075tools_cleanup(void)
1076{
1077 unsigned int ii;
1078 for (ii=0; ii<str_tab.size; ++ii)
1079 free(str_tab.list[ii]);
1080 free(str_tab.list);
1081}
2aef5f4b 1082
1083/* mysep() is my answer to the strtok/strsep
1084 * issue. strsep is nice but doesn't skip
1085 * multiple dilimiters, which can really
1086 * offset tokens and cause huge corruption
1087 * so this function will use strsep but
31f23f13 1088 * act like strtok in that sense.
2aef5f4b 1089 */
1090char *mysep(char **sepstr, char *delim)
1091{
1092 static char *retstr;
1093
1094 if(!*sepstr || !**sepstr)
1095 return(NULL);
1096
1097 do
1098 {
1099 retstr = strsep(sepstr, delim);
1100 }while (retstr && !(*retstr));
1101
1102 return(retstr);
1103}
1104
eb5d6b73 1105char *time2str(time_t thetime)
1106{
1107 char *buf, *tmp;
1108
1109 buf = ctime(&thetime);
1110 tmp = (char *)strchr(buf, '\n');
1111 *tmp = '\0';
1112 return(buf);
1113}
1114
63665495 1115char* x3_strtok(char **save, char *str, char *fs)
1116{
1117 char *pos = *save; /* keep last position across calls */
1118 char *tmp;
1119
1120 if (str)
1121 pos = str; /* new string scan */
1122
1123 while (pos && *pos && strchr(fs, *pos) != NULL)
1124 pos++; /* skip leading separators */
1125
1126 if (!pos || !*pos)
1127 return (pos = *save = NULL); /* string contains only sep's */
1128
1129 tmp = pos; /* now, keep position of the token */
1130
1131 while (*pos && strchr(fs, *pos) == NULL)
1132 pos++; /* skip content of the token */
1133
1134 if (*pos)
1135 *pos++ = '\0'; /* remove first sep after the token */
1136 else
1137 pos = NULL; /* end of string */
1138
1139 *save = pos;
1140 return (tmp);
1141}
1142
4c26ef3e
AS
1143int valid_email(const char *email)
1144{
1145 unsigned int i;
1146 for (i=0;i<strlen(email);i++)
1147 {
1148 if(!isalnum(email[i]) &&
1149 email[i] != '.' &&
1150 email[i] != '@' &&
1151 email[i] != '-' &&
1152 email[i] != '+' &&
1153 email[i] != '_' )
1154 return false;
1155 }
1156 if(strchr(email, '@') == NULL)
1157 return false;
1158 return true;
1159}
1160
668dc38e 1161/*
1162 * Create a string of form "foo!bar@fubar" given foo, bar and fubar
1163 * as the parameters. If NULL, they become "*".
1164 */
1165#define NUH_BUFSIZE (NICKLEN + USERLEN + HOSTLEN + 10)
1166static char *make_nick_user_host(char *namebuf, const char *nick,
1167 const char *name, const char *host)
1168{
1169 snprintf(namebuf, NUH_BUFSIZE, "%s!%s@%s", nick, name, host);
1170 return namebuf;
1171}
1172
1173/*
1174 * pretty_mask
1175 *
1176 * by Carlo Wood (Run), 05 Oct 1998.
1177 *
1178 * Canonify a mask.
1179 *
1180 * When the nick is longer then NICKLEN, it is cut off (its an error of course).
1181 * When the user name or host name are too long (USERLEN and HOSTLEN
1182 * respectively) then they are cut off at the start with a '*'.
1183 *
1184 * The following transformations are made:
1185 *
1186 * 1) xxx -> nick!*@*
1187 * 2) xxx.xxx -> *!*@host
1188 * 3) xxx!yyy -> nick!user@*
1189 * 4) xxx@yyy -> *!user@host
1190 * 5) xxx!yyy@zzz -> nick!user@host
1191 */
1192char *pretty_mask(char *mask)
1193{
1194 static char star[2] = { '*', 0 };
1195 static char retmask[NUH_BUFSIZE] = "";
1196 char *last_dot = NULL;
1197 char *ptr = NULL;
1198
1199 /* Case 1: default */
1200 char *nick = mask;
1201 char *user = star;
1202 char *host = star;
1203
1204 /* Do a _single_ pass through the characters of the mask: */
1205 for (ptr = mask; *ptr; ++ptr)
1206 {
1207 if (*ptr == '!')
1208 {
1209 /* Case 3 or 5: Found first '!' (without finding a '@' yet) */
1210 user = ++ptr;
1211 host = star;
1212 }
1213 else if (*ptr == '@')
1214 {
1215 /* Case 4: Found last '@' (without finding a '!' yet) */
1216 nick = star;
1217 user = mask;
1218 host = ++ptr;
1219 }
1220 else if (*ptr == '.')
1221 {
1222 /* Case 2: Found last '.' (without finding a '!' or '@' yet) */
1223 last_dot = ptr;
1224 continue;
1225 }
1226 else
1227 continue;
1228 for (; *ptr; ++ptr)
1229 {
1230 if (*ptr == '@')
1231 {
1232 /* Case 4 or 5: Found last '@' */
1233 host = ptr + 1;
1234 }
1235 }
1236 break;
1237 }
1238 if (user == star && last_dot)
1239 {
1240 /* Case 2: */
1241 nick = star;
1242 user = star;
1243 host = mask;
1244 }
1245 /* Check lengths */
1246 if (nick != star)
1247 {
1248 char *nick_end = (user != star) ? user - 1 : ptr;
1249 if (nick_end - nick > NICKLEN)
1250 nick[NICKLEN] = 0;
1251 *nick_end = 0;
1252 }
1253 if (user != star)
1254 {
1255 char *user_end = (host != star) ? host - 1 : ptr;
1256 if (user_end - user > USERLEN)
1257 {
1258 user = user_end - USERLEN;
1259 *user = '*';
1260 }
1261 *user_end = 0;
1262 }
1263 if (host != star && ptr - host > HOSTLEN)
1264 {
1265 host = ptr - HOSTLEN;
1266 *host = '*';
1267 }
1268 return make_nick_user_host(retmask, nick, user, host);
1269}