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