]> jfr.im git - irc/evilnet/x3.git/blob - src/proto-common.c
Added comment about ircu2.10.12 compatability. this needs done up proper
[irc/evilnet/x3.git] / src / proto-common.c
1 /* proto-common.c - common IRC protocol parsing/sending support
2 * Copyright 2000-2004 srvx Development Team
3 *
4 * This file is part of x3.
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 "shun.h"
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
38 unsigned int lines_processed;
39 FILE *replay_file;
40 struct io_fd *socket_io_fd;
41 int force_n2k;
42 const char *hidden_host_suffix;
43
44 static char replay_line[MAXLEN+80];
45 static int ping_freq;
46 static int ping_timeout;
47 static int replay_connected;
48 static unsigned int nicklen = NICKLEN; /* how long do we think servers allow nicks to be? */
49 static struct userList dead_users;
50
51 extern struct cManagerNode cManager;
52 extern unsigned long burst_length;
53 extern struct cManagerNode cManager;
54 extern struct policer_params *oper_policer_params, *luser_policer_params;
55 extern server_link_func_t *slf_list;
56 extern unsigned int slf_size, slf_used;
57 extern new_user_func_t *nuf_list;
58 extern unsigned int nuf_size, nuf_used;
59 extern del_user_func_t *duf_list;
60 extern unsigned int duf_size, duf_used;
61 extern time_t boot_time;
62
63 void received_ping(void);
64
65 static int replay_read(void);
66 static dict_t irc_func_dict;
67
68 typedef void (*foreach_chanfunc) (struct chanNode *chan, void *data);
69 typedef void (*foreach_nonchan) (char *name, void *data);
70 typedef void (*foreach_userfunc) (struct userNode *user, void *data);
71 typedef void (*foreach_nonuser) (char *name, void *data);
72 static void parse_foreach(char *target_list, foreach_chanfunc cf, foreach_nonchan nc, foreach_userfunc uf, foreach_nonuser nu, void *data);
73
74 static void
75 uplink_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
93 void
94 socket_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
104 void 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
121 int
122 create_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
156 void
157 replay_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
201 static int
202 replay_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
229 static void
230 replay_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
246 void putsock(const char *text, ...) PRINTF_LIKE(1, 2);
247
248 void
249 putsock(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
272 void
273 close_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))
284 typedef CMD_FUNC(cmd_func_t);
285
286 static 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). */
290 void
291 timed_send_ping(UNUSED_ARG(void *data))
292 {
293 irc_ping(self->name);
294 timeq_add(now + ping_timeout, timed_ping_timeout, 0);
295 }
296
297 static void
298 timed_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
307 static 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
326 static CMD_FUNC(cmd_dummy)
327 {
328 /* we don't care about these messages */
329 return 1;
330 }
331
332 static 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
347 static 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
370 static 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 }
377 irc_numeric(user, 351, "%s %s %s", PACKAGE_TARNAME, PACKAGE_VERSION, self->name);
378 return 1;
379 }
380
381 static 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
402 static void
403 recalc_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
413 static struct chanmsg_func {
414 chanmsg_func_t func;
415 struct userNode *service;
416 } chanmsg_funcs[256]; /* indexed by trigger character */
417
418 static struct allchanmsg_func {
419 chanmsg_func_t func;
420 struct userNode *service;
421 } allchanmsg_funcs[ALLCHANMSG_FUNCS_MAX];
422
423 struct privmsg_desc {
424 struct userNode *user;
425 char *text;
426 unsigned int is_notice : 1;
427 unsigned int is_qualified : 1;
428 };
429
430 static void
431 privmsg_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 */
444 if (!pd->is_notice && cf->func
445 && ((cn->modes & MODE_REGISTERED) || GetUserMode(cn, cf->service)))
446 cf->func(pd->user, cn, pd->text+1, cf->service);
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
454 cf->func(pd->user, cn, pd->text, cf->service);
455 }
456 }
457
458 static void
459 privmsg_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
468 static void
469 part_helper(struct chanNode *cn, void *data)
470 {
471 DelChannelUser(data, cn, false, 0);
472 }
473
474 void
475 reg_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
483 void
484 reg_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
496 struct userNode *
497 get_chanmsg_bot(unsigned char prefix)
498 {
499 return chanmsg_funcs[prefix].service;
500 }
501
502 static mode_change_func_t *mcf_list;
503 static unsigned int mcf_size = 0, mcf_used = 0;
504
505 void
506 reg_mode_change_func(mode_change_func_t handler)
507 {
508 if (mcf_used == mcf_size) {
509 if (mcf_size) {
510 mcf_size <<= 1;
511 mcf_list = realloc(mcf_list, mcf_size*sizeof(mode_change_func_t));
512 } else {
513 mcf_size = 8;
514 mcf_list = malloc(mcf_size*sizeof(mode_change_func_t));
515 }
516 }
517 mcf_list[mcf_used++] = handler;
518 }
519
520 struct mod_chanmode *
521 mod_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
537 struct mod_chanmode *
538 mod_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
553 void
554 mod_chanmode_apply(struct userNode *who, struct chanNode *channel, struct mod_chanmode *change)
555 {
556 struct banNode *bn;
557 struct exemptNode *en;
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) {
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);
578 jj--;
579 }
580 }
581 bn = calloc(1, sizeof(*bn));
582 safestrncpy(bn->ban, change->args[ii].u.hostmask, sizeof(bn->ban));
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) {
592 bn = channel->banlist.list[jj];
593 if (strcmp(bn->ban, change->args[ii].u.hostmask))
594 continue;
595 free(bn);
596 banList_remove(&channel->banlist, bn);
597 break;
598 }
599 break;
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) {
606 if (match_ircglobs(change->args[ii].u.hostmask, channel->exemptlist.list[jj]->exempt)) {
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));
613 safestrncpy(en->exempt, change->args[ii].u.hostmask, sizeof(en->exempt));
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) {
623 if (strcmp(channel->exemptlist.list[jj]->exempt, change->args[ii].u.hostmask))
624 continue;
625 free(channel->exemptlist.list[jj]);
626 exemptList_remove(&channel->exemptlist, channel->exemptlist.list[jj]);
627 break;
628 }
629 break;
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 **/
634 case MODE_CHANOP:
635 case MODE_HALFOP:
636 case MODE_VOICE:
637 case MODE_VOICE|MODE_CHANOP:
638 case MODE_VOICE|MODE_HALFOP:
639 case MODE_CHANOP|MODE_HALFOP:
640 case MODE_VOICE|MODE_CHANOP|MODE_HALFOP:
641 case MODE_REMOVE|MODE_CHANOP:
642 case MODE_REMOVE|MODE_HALFOP:
643 case MODE_REMOVE|MODE_VOICE:
644 case MODE_REMOVE|MODE_VOICE|MODE_CHANOP:
645 case MODE_REMOVE|MODE_VOICE|MODE_HALFOP:
646 case MODE_REMOVE|MODE_CHANOP|MODE_HALFOP:
647 case MODE_REMOVE|MODE_VOICE|MODE_CHANOP|MODE_HALFOP:
648 if (change->args[ii].mode & MODE_REMOVE)
649 change->args[ii].u.member->modes &= ~change->args[ii].mode;
650 else
651 change->args[ii].u.member->modes |= change->args[ii].mode;
652 break;
653 default:
654 assert(0 && "Invalid mode argument");
655 continue;
656 }
657 }
658 }
659
660 void
661 mod_chanmode_free(struct mod_chanmode *change)
662 {
663 free(change);
664 }
665
666 int
667 mod_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
687 int
688 irc_make_chanmode(struct chanNode *chan, char *out)
689 {
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
698 char *
699 generate_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)
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
719 ident = user->ident;
720 else if (options & GENMASK_ANY_IDENT)
721 ident = "*";
722 else {
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 {
729 ident = alloca(strlen(user->ident)+2);
730 ident[0] = '*';
731 strcpy(ident+1, user->ident + ((*user->ident == '~')?1:0));
732 }
733 }
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);
751 if (options & GENMASK_X3MASK) {
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 }
791 // sethost - reed/apples
792 if (IsSetHost(user))
793 hostname = strchr(user->sethost, '@') + 1;
794
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
808 int
809 IsChannelName(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 }