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