]> jfr.im git - irc/evilnet/x3.git/blame - src/proto-common.c
Synchronization with srvx--devo--1.3--patch-70.
[irc/evilnet/x3.git] / src / proto-common.c
CommitLineData
d76ed9a9 1/* proto-common.c - common IRC protocol parsing/sending support
2 * Copyright 2000-2004 srvx Development Team
3 *
4 * This file is part of srvx.
5 *
6 * srvx is free software; you can redistribute it and/or modify
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 "conf.h"
22#include "gline.h"
23#include "ioset.h"
24#include "log.h"
25#include "nickserv.h"
26#include "timeq.h"
27#ifdef HAVE_SYS_SOCKET_H
28#include <sys/socket.h>
29#endif
30#ifdef HAVE_NETINET_IN_H
31#include <netinet/in.h>
32#endif
33#ifdef HAVE_ARPA_INET_H
34#include <arpa/inet.h>
35#endif
36
37unsigned int lines_processed;
38FILE *replay_file;
39struct io_fd *socket_io_fd;
40int force_n2k;
41const char *hidden_host_suffix;
42
43static char replay_line[MAXLEN+80];
44static int ping_freq;
45static int ping_timeout;
46static int replay_connected;
47static unsigned int nicklen = NICKLEN; /* how long do we think servers allow nicks to be? */
48static struct userList dead_users;
49
50extern struct cManagerNode cManager;
51extern unsigned long burst_length;
52extern struct cManagerNode cManager;
53extern struct policer_params *oper_policer_params, *luser_policer_params;
54extern server_link_func_t *slf_list;
55extern unsigned int slf_size, slf_used;
56extern new_user_func_t *nuf_list;
57extern unsigned int nuf_size, nuf_used;
58extern del_user_func_t *duf_list;
59extern unsigned int duf_size, duf_used;
60extern time_t boot_time;
61
62void received_ping(void);
63
64static int replay_read(void);
65static dict_t irc_func_dict;
66
67typedef void (*foreach_chanfunc) (struct chanNode *chan, void *data);
68typedef void (*foreach_nonchan) (char *name, void *data);
69typedef void (*foreach_userfunc) (struct userNode *user, void *data);
70typedef void (*foreach_nonuser) (char *name, void *data);
71static void parse_foreach(char *target_list, foreach_chanfunc cf, foreach_nonchan nc, foreach_userfunc uf, foreach_nonuser nu, void *data);
72
73static void
74uplink_readable(struct io_fd *fd) {
75 static char buffer[MAXLEN];
76 char *eol;
77 int pos;
78
79 pos = ioset_line_read(fd, buffer, sizeof(buffer));
80 if (pos <= 0) {
81 close_socket();
82 return;
83 }
84 if ((eol = strpbrk(buffer, "\r\n")))
85 *eol = 0;
86 log_replay(MAIN_LOG, false, buffer);
87 if (cManager.uplink->state != DISCONNECTED)
88 parse_line(buffer, 0);
89 lines_processed++;
90}
91
92void
93socket_destroyed(struct io_fd *fd)
94{
95 if (fd && fd->eof)
96 log_module(MAIN_LOG, LOG_ERROR, "Connection to server lost.");
97 socket_io_fd = NULL;
98 cManager.uplink->state = DISCONNECTED;
99 if (self->uplink)
100 DelServer(self->uplink, 0, NULL);
101}
102
103void replay_event_loop(void)
104{
105 while (!quit_services) {
106 if (!replay_connected) {
107 /* this time fudging is to get some of the logging right */
108 self->link = self->boot = now;
109 cManager.uplink->state = AUTHENTICATING;
110 irc_introduce(cManager.uplink->password);
111 replay_connected = 1;
112 } else if (!replay_read()) {
113 log_module(MAIN_LOG, LOG_ERROR, "Connection to server lost.");
114 close_socket();
115 }
116 timeq_run();
117 }
118}
119
120int
121create_socket_client(struct uplinkNode *target)
122{
123 int port = target->port;
124 const char *addr = target->host;
125
126 if (replay_file)
127 return feof(replay_file) ? 0 : 1;
128
129 if (socket_io_fd) {
130 /* Leave the existing socket open, say we failed. */
131 log_module(MAIN_LOG, LOG_ERROR, "Refusing to create second connection to %s:%d.", addr, port);
132 return 0;
133 }
134
135 log_module(MAIN_LOG, LOG_INFO, "Connecting to %s:%i...", addr, port);
136
137 socket_io_fd = ioset_connect((struct sockaddr*)cManager.uplink->bind_addr, sizeof(struct sockaddr), addr, port, 1, 0, NULL);
138 if (!socket_io_fd) {
139 log_module(MAIN_LOG, LOG_ERROR, "Connection to uplink failed: %s (%d)", strerror(errno), errno);
140 target->state = DISCONNECTED;
141 target->tries++;
142 return 0;
143 }
144 socket_io_fd->readable_cb = uplink_readable;
145 socket_io_fd->destroy_cb = socket_destroyed;
146 socket_io_fd->line_reads = 1;
147 socket_io_fd->wants_reads = 1;
148 log_module(MAIN_LOG, LOG_INFO, "Connection to server established.");
149 cManager.uplink = target;
150 target->state = AUTHENTICATING;
151 target->tries = 0;
152 return 1;
153}
154
155void
156replay_read_line(void)
157{
158 struct tm timestamp;
159 time_t new_time;
160
161 if (replay_line[0]) return;
162 read_line:
163 if (!fgets(replay_line, sizeof(replay_line), replay_file)) {
164 if (feof(replay_file)) {
165 quit_services = 1;
166 memset(replay_line, 0, sizeof(replay_line));
167 return;
168 }
169 }
170 if ((replay_line[0] != '[')
171 || (replay_line[3] != ':')
172 || (replay_line[6] != ':')
173 || (replay_line[9] != ' ')
174 || (replay_line[12] != '/')
175 || (replay_line[15] != '/')
176 || (replay_line[20] != ']')
177 || (replay_line[21] != ' ')) {
178 log_module(MAIN_LOG, LOG_ERROR, "Unrecognized timestamp in replay file: %s", replay_line);
179 goto read_line;
180 }
181 timestamp.tm_hour = strtoul(replay_line+1, NULL, 10);
182 timestamp.tm_min = strtoul(replay_line+4, NULL, 10);
183 timestamp.tm_sec = strtoul(replay_line+7, NULL, 10);
184 timestamp.tm_mon = strtoul(replay_line+10, NULL, 10) - 1;
185 timestamp.tm_mday = strtoul(replay_line+13, NULL, 10);
186 timestamp.tm_year = strtoul(replay_line+16, NULL, 10) - 1900;
187 timestamp.tm_isdst = 0;
188 new_time = mktime(&timestamp);
189 if (new_time == -1) {
190 log_module(MAIN_LOG, LOG_ERROR, "Unable to parse time struct tm_sec=%d tm_min=%d tm_hour=%d tm_mday=%d tm_mon=%d tm_year=%d", timestamp.tm_sec, timestamp.tm_min, timestamp.tm_hour, timestamp.tm_mday, timestamp.tm_mon, timestamp.tm_year);
191 } else {
192 now = new_time;
193 }
194
195 if (strncmp(replay_line+22, "(info) ", 7))
196 goto read_line;
197 return;
198}
199
200static int
201replay_read(void)
202{
203 size_t len;
204 char read_line[MAXLEN];
205 while (1) {
206 replay_read_line();
207 /* if it's a sent line, break out to handle it */
208 if (!strncmp(replay_line+29, " ", 3))
209 break;
210 if (!strncmp(replay_line+29, "W: ", 3)) {
211 log_module(MAIN_LOG, LOG_ERROR, "Expected response from services: %s", replay_line+32);
212 replay_line[0] = 0;
213 } else {
214 return 0;
215 }
216 }
217 log_replay(MAIN_LOG, false, replay_line+32);
218 safestrncpy(read_line, replay_line+32, sizeof(read_line));
219 len = strlen(read_line);
220 if (read_line[len-1] == '\n')
221 read_line[--len] = 0;
222 replay_line[0] = 0;
223 parse_line(read_line, 0);
224 lines_processed++;
225 return 1;
226}
227
228static void
229replay_write(char *text)
230{
231 replay_read_line();
232 if (strncmp(replay_line+29, "W: ", 3)) {
233 log_module(MAIN_LOG, LOG_ERROR, "Unexpected output during replay: %s", text);
234 return;
235 } else {
236 if (strcmp(replay_line+32, text)) {
237 log_module(MAIN_LOG, LOG_ERROR, "Incorrect output during replay:\nReceived: %sExpected: %s", text, replay_line+32);
238 } else {
239 log_replay(MAIN_LOG, true, text);
240 }
241 replay_line[0] = 0;
242 }
243}
244
245void putsock(const char *text, ...) PRINTF_LIKE(1, 2);
246
247void
248putsock(const char *text, ...)
249{
250 va_list arg_list;
251 char buffer[MAXLEN];
252 int pos;
253
254 if (!cManager.uplink || cManager.uplink->state == DISCONNECTED) return;
255 buffer[0] = '\0';
256 va_start(arg_list, text);
257 pos = vsnprintf(buffer, MAXLEN - 2, text, arg_list);
258 va_end(arg_list);
259 if (pos < 0 || pos > (MAXLEN - 2)) pos = MAXLEN - 2;
260 buffer[pos] = 0;
261 if (!replay_file) {
262 log_replay(MAIN_LOG, true, buffer);
263 buffer[pos++] = '\n';
264 buffer[pos] = 0;
265 ioset_write(socket_io_fd, buffer, pos);
266 } else {
267 replay_write(buffer);
268 }
269}
270
271void
272close_socket(void)
273{
274 if (replay_file) {
275 replay_connected = 0;
276 socket_destroyed(socket_io_fd);
277 } else {
278 ioset_close(socket_io_fd->fd, 1);
279 }
280}
281
282#define CMD_FUNC(NAME) int NAME(UNUSED_ARG(const char *origin), UNUSED_ARG(unsigned int argc), UNUSED_ARG(char **argv))
283typedef CMD_FUNC(cmd_func_t);
284
285static void timed_ping_timeout(void *data);
286
287/* Ping state is kept in the timeq (only one of these two can be in
288 * the queue at any given time). */
289void
290timed_send_ping(UNUSED_ARG(void *data))
291{
292 irc_ping(self->name);
293 timeq_add(now + ping_timeout, timed_ping_timeout, 0);
294}
295
296static void
297timed_ping_timeout(UNUSED_ARG(void *data))
298{
299 /* Uplink "health" tracking could be accomplished by counting the
300 number of ping timeouts that happen for each uplink. After the
301 timeouts per time period exceeds some amount, the uplink could
302 be marked as unavalable.*/
303 irc_squit(self, "Ping timeout.", NULL);
304}
305
306static CMD_FUNC(cmd_pass)
307{
308 const char *true_pass;
309
310 if (argc < 2)
311 return 0;
312 true_pass = cManager.uplink->their_password;
313 if (true_pass && strcmp(true_pass, argv[1])) {
314 /* It might be good to mark the uplink as unavailable when
315 this happens, though there should be a way of resetting
316 the flag. */
317 irc_squit(self, "Incorrect password received.", NULL);
318 return 1;
319 }
320
321 cManager.uplink->state = BURSTING;
322 return 1;
323}
324
325static CMD_FUNC(cmd_dummy)
326{
327 /* we don't care about these messages */
328 return 1;
329}
330
331static CMD_FUNC(cmd_error)
332{
333 if (argv[1]) log_module(MAIN_LOG, LOG_ERROR, "Error: %s", argv[1]);
334 log_module(MAIN_LOG, LOG_ERROR, "Error received from uplink, squitting.");
335
336 if (cManager.uplink->state != CONNECTED) {
337 /* Error messages while connected should be fine. */
338 cManager.uplink->flags |= UPLINK_UNAVAILABLE;
339 log_module(MAIN_LOG, LOG_ERROR, "Disabling uplink.");
340 }
341
342 close_socket();
343 return 1;
344}
345
346static CMD_FUNC(cmd_stats)
347{
348 struct userNode *un;
349
350 if (argc < 2)
351 return 0;
352 if (!(un = GetUserH(origin)))
353 return 0;
354 switch (argv[1][0]) {
355 case 'u': {
356 unsigned int uptime = now - boot_time;
357 irc_numeric(un, RPL_STATSUPTIME, ":Server Up %d days %d:%02d:%02d",
358 uptime/(24*60*60), (uptime/(60*60))%24, (uptime/60)%60, uptime%60);
359 irc_numeric(un, RPL_MAXCONNECTIONS, ":Highest connection count: %d (%d clients)",
360 self->max_clients+1, self->max_clients);
361 break;
362 }
363 default: /* unrecognized/unhandled stats output */ break;
364 }
365 irc_numeric(un, 219, "%s :End of /STATS report", argv[1]);
366 return 1;
367}
368
369static CMD_FUNC(cmd_version)
370{
371 struct userNode *user;
372 if (!(user = GetUserH(origin))) {
373 log_module(MAIN_LOG, LOG_ERROR, "Could not find VERSION origin user %s", origin);
374 return 0;
375 }
376 irc_numeric(user, 351, "%s.%s %s :%s", PACKAGE_TARNAME, PACKAGE_VERSION, self->name, CODENAME);
377 return 1;
378}
379
380static CMD_FUNC(cmd_admin)
381{
382 struct userNode *user;
383 struct string_list *slist;
384
385 if (!(user = GetUserH(origin))) {
386 log_module(MAIN_LOG, LOG_ERROR, "Could not find ADMIN origin user %s", origin);
387 return 0;
388 }
389 if ((slist = conf_get_data("server/admin", RECDB_STRING_LIST)) && slist->used) {
390 unsigned int i;
391
392 irc_numeric(user, 256, ":Administrative info about %s", self->name);
393 for (i = 0; i < slist->used && i < 3; i++)
394 irc_numeric(user, 257 + i, ":%s", slist->list[i]);
395 } else {
396 irc_numeric(user, 423, ":No administrative info available");
397 }
398 return 1;
399}
400
401static void
402recalc_bursts(struct server *eob_server)
403{
404 unsigned int nn;
405 eob_server->burst = eob_server->self_burst;
406 if (eob_server->uplink != self)
407 eob_server->burst = eob_server->burst || eob_server->uplink->burst;
408 for (nn=0; nn < eob_server->children.used; nn++)
409 recalc_bursts(eob_server->children.list[nn]);
410}
411
412static struct chanmsg_func {
413 chanmsg_func_t func;
414 struct userNode *service;
415} chanmsg_funcs[256]; /* indexed by trigger character */
416
417static struct allchanmsg_func {
418 chanmsg_func_t func;
419 struct userNode *service;
420} allchanmsg_funcs[ALLCHANMSG_FUNCS_MAX];
421
422struct privmsg_desc {
423 struct userNode *user;
424 char *text;
425 unsigned int is_notice : 1;
426 unsigned int is_qualified : 1;
427};
428
429static void
430privmsg_chan_helper(struct chanNode *cn, void *data)
431{
432 struct privmsg_desc *pd = data;
433 struct modeNode *mn;
434 struct chanmsg_func *cf = &chanmsg_funcs[(unsigned char)pd->text[0]];
435 int x;
436
437 /* Don't complain if it can't find the modeNode because the channel might
438 * be -n */
439 if ((mn = GetUserMode(cn, pd->user)))
440 mn->idle_since = now;
441
442 /* Never send a NOTICE to a channel to one of the services */
443 if (!pd->is_notice && cf->func && GetUserMode(cn, cf->service))
444 cf->func(pd->user, cn, pd->text+1, cf->service);
445
446 /* This catches *all* text sent to the channel that the services server sees */
447 for (x = 0; x < ALLCHANMSG_FUNCS_MAX; x++) {
448 cf = (struct chanmsg_func *)&allchanmsg_funcs[x];
449 if (!cf->func)
450 break; /* end of list */
451 else
452 cf->func(pd->user, cn, pd->text, cf->service);
453 }
454}
455
456static void
457privmsg_invalid(char *name, void *data)
458{
459 struct privmsg_desc *pd = data;
460
461 if (*name == '$')
462 return;
463 irc_numeric(pd->user, ERR_NOSUCHNICK, "%s@%s :No such nick", name, self->name);
464}
465
466static void
467part_helper(struct chanNode *cn, void *data)
468{
469 DelChannelUser(data, cn, false, 0);
470}
471
472void
473reg_chanmsg_func(unsigned char prefix, struct userNode *service, chanmsg_func_t handler)
474{
475 if (chanmsg_funcs[prefix].func)
476 log_module(MAIN_LOG, LOG_WARNING, "Re-registering new chanmsg handler for character `%c'.", prefix);
477 chanmsg_funcs[prefix].func = handler;
478 chanmsg_funcs[prefix].service = service;
479}
480
481void
482reg_allchanmsg_func(struct userNode *service, chanmsg_func_t handler)
483{
484 int x;
485 for (x = 0; x < ALLCHANMSG_FUNCS_MAX; x++) {
486 if (allchanmsg_funcs[x].func)
487 continue;
488 allchanmsg_funcs[x].func = handler;
489 allchanmsg_funcs[x].service = service;
490 break;
491 }
492}
493
494struct userNode *
495get_chanmsg_bot(unsigned char prefix)
496{
497 return chanmsg_funcs[prefix].service;
498}
499
500static mode_change_func_t *mcf_list;
501static unsigned int mcf_size = 0, mcf_used = 0;
502
503void
504reg_mode_change_func(mode_change_func_t handler)
505{
506 if (mcf_used == mcf_size) {
507 if (mcf_size) {
508 mcf_size <<= 1;
509 mcf_list = realloc(mcf_list, mcf_size*sizeof(mode_change_func_t));
510 } else {
511 mcf_size = 8;
512 mcf_list = malloc(mcf_size*sizeof(mode_change_func_t));
513 }
514 }
515 mcf_list[mcf_used++] = handler;
516}
517
518struct mod_chanmode *
519mod_chanmode_alloc(unsigned int argc)
520{
521 struct mod_chanmode *res;
522 if (argc > 1)
523 res = calloc(1, sizeof(*res) + (argc-1)*sizeof(res->args[0]));
524 else
525 res = calloc(1, sizeof(*res));
526 if (res) {
527#if !defined(NDEBUG)
528 res->alloc_argc = argc;
529#endif
530 res->argc = argc;
531 }
532 return res;
533}
534
535struct mod_chanmode *
536mod_chanmode_dup(struct mod_chanmode *orig, unsigned int extra)
537{
538 struct mod_chanmode *res;
539 res = mod_chanmode_alloc(orig->argc + extra);
540 if (res) {
541 res->modes_set = orig->modes_set;
542 res->modes_clear = orig->modes_clear;
543 res->new_limit = orig->new_limit;
544 memcpy(res->new_key, orig->new_key, sizeof(res->new_key));
545 res->argc = orig->argc;
546 memcpy(res->args, orig->args, orig->argc*sizeof(orig->args[0]));
547 }
548 return res;
549}
550
551void
552mod_chanmode_apply(struct userNode *who, struct chanNode *channel, struct mod_chanmode *change)
553{
554 struct banNode *bn;
2aef5f4b 555 struct exemptNode *en;
d76ed9a9 556 unsigned int ii, jj;
557
558 assert(change->argc <= change->alloc_argc);
559 channel->modes = (channel->modes & ~change->modes_clear) | change->modes_set;
560 if (change->modes_set & MODE_LIMIT)
561 channel->limit = change->new_limit;
562 if (change->modes_set & MODE_KEY)
563 strcpy(channel->key, change->new_key);
564 for (ii = 0; ii < change->argc; ++ii) {
565 switch (change->args[ii].mode) {
566 case MODE_BAN:
567 /* If any existing ban is a subset of the new ban,
568 * silently remove it. The new ban is not allowed
569 * to be more specific than an existing ban.
570 */
571 for (jj=0; jj<channel->banlist.used; ++jj) {
a32da4c7 572 if (match_ircglobs(change->args[ii].u.hostmask, channel->banlist.list[jj]->ban)) {
d76ed9a9 573 banList_remove(&channel->banlist, channel->banlist.list[jj]);
574 free(channel->banlist.list[jj]);
575 jj--;
576 }
577 }
578 bn = calloc(1, sizeof(*bn));
a32da4c7 579 safestrncpy(bn->ban, change->args[ii].u.hostmask, sizeof(bn->ban));
d76ed9a9 580 if (who)
581 safestrncpy(bn->who, who->nick, sizeof(bn->who));
582 else
583 safestrncpy(bn->who, "<unknown>", sizeof(bn->who));
584 bn->set = now;
585 banList_append(&channel->banlist, bn);
586 break;
587 case MODE_REMOVE|MODE_BAN:
588 for (jj=0; jj<channel->banlist.used; ++jj) {
a32da4c7 589 if (strcmp(channel->banlist.list[jj]->ban, change->args[ii].u.hostmask))
d76ed9a9 590 continue;
591 free(channel->banlist.list[jj]);
592 banList_remove(&channel->banlist, channel->banlist.list[jj]);
593 break;
594 }
595 break;
2aef5f4b 596 case MODE_EXEMPT:
597 /* If any existing exempt is a subset of the new exempt,
598 * silently remove it. The new exempt is not allowed
599 * to be more specific than an existing exempt.
600 */
601 for (jj=0; jj<channel->exemptlist.used; ++jj) {
a32da4c7 602 if (match_ircglobs(change->args[ii].u.hostmask, channel->exemptlist.list[jj]->exempt)) {
2aef5f4b 603 exemptList_remove(&channel->exemptlist, channel->exemptlist.list[jj]);
604 free(channel->exemptlist.list[jj]);
605 jj--;
606 }
607 }
608 en = calloc(1, sizeof(*en));
a32da4c7 609 safestrncpy(en->exempt, change->args[ii].u.hostmask, sizeof(en->exempt));
2aef5f4b 610 if (who)
611 safestrncpy(en->who, who->nick, sizeof(en->who));
612 else
613 safestrncpy(en->who, "<unknown>", sizeof(en->who));
614 en->set = now;
615 exemptList_append(&channel->exemptlist, en);
616 break;
617 case MODE_REMOVE|MODE_EXEMPT:
618 for (jj=0; jj<channel->exemptlist.used; ++jj) {
a32da4c7 619 if (strcmp(channel->exemptlist.list[jj]->exempt, change->args[ii].u.hostmask))
2aef5f4b 620 continue;
621 free(channel->exemptlist.list[jj]);
622 exemptList_remove(&channel->exemptlist, channel->exemptlist.list[jj]);
623 break;
624 }
625 break;
d76ed9a9 626 case MODE_CHANOP:
55342ce8 627 case MODE_HALFOP:
d76ed9a9 628 case MODE_VOICE:
55342ce8 629 case MODE_VOICE|MODE_CHANOP|MODE_HALFOP:
d76ed9a9 630 case MODE_REMOVE|MODE_CHANOP:
55342ce8 631 case MODE_REMOVE|MODE_HALFOP:
d76ed9a9 632 case MODE_REMOVE|MODE_VOICE:
55342ce8 633 case MODE_REMOVE|MODE_VOICE|MODE_CHANOP|MODE_HALFOP:
d76ed9a9 634 if (change->args[ii].mode & MODE_REMOVE)
a32da4c7 635 change->args[ii].u.member->modes &= ~change->args[ii].mode;
d76ed9a9 636 else
a32da4c7 637 change->args[ii].u.member->modes |= change->args[ii].mode;
d76ed9a9 638 break;
639 default:
640 assert(0 && "Invalid mode argument");
641 continue;
642 }
643 }
644}
645
646void
647mod_chanmode_free(struct mod_chanmode *change)
648{
649 free(change);
650}
651
652int
653mod_chanmode(struct userNode *who, struct chanNode *channel, char **modes, unsigned int argc, unsigned int flags)
654{
655 struct mod_chanmode *change;
656 unsigned int ii;
657
658 if (!modes || !modes[0])
659 return 0;
660 if (!(change = mod_chanmode_parse(channel, modes, argc, flags)))
661 return 0;
662 if (flags & MC_ANNOUNCE)
663 mod_chanmode_announce(who, channel, change);
664 else
665 mod_chanmode_apply(who, channel, change);
666 if (flags & MC_NOTIFY)
667 for (ii = 0; ii < mcf_used; ++ii)
668 mcf_list[ii](channel, who, change);
669 mod_chanmode_free(change);
670 return 1;
671}
672
673int
a32da4c7 674irc_make_chanmode(struct chanNode *chan, char *out)
675{
d76ed9a9 676 struct mod_chanmode change;
677 mod_chanmode_init(&change);
678 change.modes_set = chan->modes;
679 change.new_limit = chan->limit;
680 safestrncpy(change.new_key, chan->key, sizeof(change.new_key));
681 return strlen(mod_chanmode_format(&change, out));
682}
683
684char *
685generate_hostmask(struct userNode *user, int options)
686{
687 char *nickname, *ident, *hostname;
688 char *mask;
689 int len, ii;
690
691 /* figure out string parts */
692 if (options & GENMASK_OMITNICK)
693 nickname = NULL;
694 else if (options & GENMASK_USENICK)
695 nickname = user->nick;
696 else
697 nickname = "*";
698 if (options & GENMASK_STRICT_IDENT)
182dd032 699 // sethost - reed/apples
700 if (IsSetHost(user)) {
701 ident = alloca(strcspn(user->sethost, "@")+2);
702 safestrncpy(ident, user->sethost, strcspn(user->sethost, "@")+1);
703 }
704 else
d76ed9a9 705 ident = user->ident;
706 else if (options & GENMASK_ANY_IDENT)
707 ident = "*";
708 else {
182dd032 709 // sethost - reed/apples
710 if (IsSetHost(user)) {
711 ident = alloca(strcspn(user->sethost, "@")+3);
712 ident[0] = '*';
713 safestrncpy(ident+1, user->sethost, strcspn(user->sethost, "@")+1);
714 } else {
d76ed9a9 715 ident = alloca(strlen(user->ident)+2);
716 ident[0] = '*';
717 strcpy(ident+1, user->ident + ((*user->ident == '~')?1:0));
718 }
182dd032 719 }
d76ed9a9 720 hostname = user->hostname;
721 if (IsFakeHost(user) && IsHiddenHost(user) && !(options & GENMASK_NO_HIDING)) {
722 hostname = user->fakehost;
723 } else if (IsHiddenHost(user) && user->handle_info && hidden_host_suffix && !(options & GENMASK_NO_HIDING)) {
724 hostname = alloca(strlen(user->handle_info->handle) + strlen(hidden_host_suffix) + 2);
725 sprintf(hostname, "%s.%s", user->handle_info->handle, hidden_host_suffix);
726 } else if (options & GENMASK_STRICT_HOST) {
727 if (options & GENMASK_BYIP)
728 hostname = inet_ntoa(user->ip);
729 } else if ((options & GENMASK_BYIP) || !hostname[strspn(hostname, "0123456789.")]) {
730 /* Should generate an IP-based hostmask. By popular acclaim, a /16
731 * hostmask is used by default. */
732 unsigned masked_ip, mask, masklen;
733 masklen = 16;
734 mask = ~0 << masklen;
735 masked_ip = ntohl(user->ip.s_addr) & mask;
736 hostname = alloca(32);
737 if (options & GENMASK_SRVXMASK) {
738 sprintf(hostname, "%d.%d.%d.%d/%d", (masked_ip>>24)&0xFF, (masked_ip>>16)&0xFF, (masked_ip>>8)&0xFF, masked_ip&0xFF, masklen);
739 } else {
740 int ofs = 0;
741 for (ii=0; ii<4; ii++) {
742 if (masklen) {
743 ofs += sprintf(hostname+ofs, "%d.", (masked_ip>>24)&0xFF);
744 masklen -= 8;
745 masked_ip <<= 8;
746 } else {
747 ofs += sprintf(hostname+ofs, "*.");
748 }
749 }
750 /* Truncate the last . */
751 hostname[ofs-1] = 0;
752 }
753 } else {
754 int cnt;
755 /* This heuristic could be made smarter. Is it worth the effort? */
756 for (ii=cnt=0; hostname[ii]; ii++)
757 if (hostname[ii] == '.')
758 cnt++;
759 if (cnt == 1) {
760 /* only a two-level domain name; leave hostname */
761 } else if (cnt == 2) {
762 for (ii=0; user->hostname[ii] != '.'; ii++) ;
763 /* Add 3 to account for the *. and \0. */
764 hostname = alloca(strlen(user->hostname+ii)+3);
765 sprintf(hostname, "*.%s", user->hostname+ii+1);
766 } else {
767 for (cnt=3, ii--; cnt; ii--)
768 if (user->hostname[ii] == '.')
769 cnt--;
770 /* The loop above will overshoot the dot one character;
771 we skip forward two (the one character and the dot)
772 when printing, so we only add one for the \0. */
773 hostname = alloca(strlen(user->hostname+ii)+1);
774 sprintf(hostname, "*.%s", user->hostname+ii+2);
775 }
776 }
182dd032 777 // sethost - reed/apples
778 if (IsSetHost(user))
779 hostname = strchr(user->sethost, '@') + 1;
780
d76ed9a9 781 /* Emit hostmask */
782 len = strlen(ident) + strlen(hostname) + 2;
783 if (nickname) {
784 len += strlen(nickname) + 1;
785 mask = malloc(len);
786 sprintf(mask, "%s!%s@%s", nickname, ident, hostname);
787 } else {
788 mask = malloc(len);
789 sprintf(mask, "%s@%s", ident, hostname);
790 }
791 return mask;
792}
793
794int
795IsChannelName(const char *name) {
796 unsigned int ii;
797
798 if (*name !='#')
799 return 0;
800 for (ii=1; name[ii]; ++ii) {
801 if ((name[ii] > 0) && (name[ii] <= 32))
802 return 0;
803 if (name[ii] == ',')
804 return 0;
805 if (name[ii] == '\xa0')
806 return 0;
807 }
808 return 1;
809}