]> jfr.im git - irc/evilnet/x3.git/blob - src/proto-common.c
found out from Kyle what b flag is.
[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 "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
37 unsigned int lines_processed;
38 FILE *replay_file;
39 struct io_fd *socket_io_fd;
40 int force_n2k;
41 const char *hidden_host_suffix;
42
43 static char replay_line[MAXLEN+80];
44 static int ping_freq;
45 static int ping_timeout;
46 static int replay_connected;
47 static unsigned int nicklen = NICKLEN; /* how long do we think servers allow nicks to be? */
48 static struct userList dead_users;
49
50 extern struct cManagerNode cManager;
51 extern unsigned long burst_length;
52 extern struct cManagerNode cManager;
53 extern struct policer_params *oper_policer_params, *luser_policer_params;
54 extern server_link_func_t *slf_list;
55 extern unsigned int slf_size, slf_used;
56 extern new_user_func_t *nuf_list;
57 extern unsigned int nuf_size, nuf_used;
58 extern del_user_func_t *duf_list;
59 extern unsigned int duf_size, duf_used;
60 extern time_t boot_time;
61
62 void received_ping(void);
63
64 static int replay_read(void);
65 static dict_t irc_func_dict;
66
67 typedef void (*foreach_chanfunc) (struct chanNode *chan, void *data);
68 typedef void (*foreach_nonchan) (char *name, void *data);
69 typedef void (*foreach_userfunc) (struct userNode *user, void *data);
70 typedef void (*foreach_nonuser) (char *name, void *data);
71 static void parse_foreach(char *target_list, foreach_chanfunc cf, foreach_nonchan nc, foreach_userfunc uf, foreach_nonuser nu, void *data);
72
73 static void
74 uplink_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
92 void
93 socket_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
103 void 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
120 int
121 create_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
155 void
156 replay_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
200 static int
201 replay_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
228 static void
229 replay_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
245 void putsock(const char *text, ...) PRINTF_LIKE(1, 2);
246
247 void
248 putsock(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
271 void
272 close_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))
283 typedef CMD_FUNC(cmd_func_t);
284
285 static 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). */
289 void
290 timed_send_ping(UNUSED_ARG(void *data))
291 {
292 irc_ping(self->name);
293 timeq_add(now + ping_timeout, timed_ping_timeout, 0);
294 }
295
296 static void
297 timed_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
306 static 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
325 static CMD_FUNC(cmd_dummy)
326 {
327 /* we don't care about these messages */
328 return 1;
329 }
330
331 static 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
346 static 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
369 static 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", PACKAGE_TARNAME, PACKAGE_VERSION, self->name);
377 return 1;
378 }
379
380 static 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
401 static void
402 recalc_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
412 static struct chanmsg_func {
413 chanmsg_func_t func;
414 struct userNode *service;
415 } chanmsg_funcs[256]; /* indexed by trigger character */
416
417 static struct allchanmsg_func {
418 chanmsg_func_t func;
419 struct userNode *service;
420 } allchanmsg_funcs[ALLCHANMSG_FUNCS_MAX];
421
422 struct privmsg_desc {
423 struct userNode *user;
424 char *text;
425 unsigned int is_notice : 1;
426 unsigned int is_qualified : 1;
427 };
428
429 static void
430 privmsg_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
444 && ((cn->modes & MODE_REGISTERED) || GetUserMode(cn, cf->service)))
445 cf->func(pd->user, cn, pd->text+1, cf->service);
446
447 /* This catches *all* text sent to the channel that the services server sees */
448 for (x = 0; x < ALLCHANMSG_FUNCS_MAX; x++) {
449 cf = (struct chanmsg_func *)&allchanmsg_funcs[x];
450 if (!cf->func)
451 break; /* end of list */
452 else
453 cf->func(pd->user, cn, pd->text, cf->service);
454 }
455 }
456
457 static void
458 privmsg_invalid(char *name, void *data)
459 {
460 struct privmsg_desc *pd = data;
461
462 if (*name == '$')
463 return;
464 irc_numeric(pd->user, ERR_NOSUCHNICK, "%s@%s :No such nick", name, self->name);
465 }
466
467 static void
468 part_helper(struct chanNode *cn, void *data)
469 {
470 DelChannelUser(data, cn, false, 0);
471 }
472
473 void
474 reg_chanmsg_func(unsigned char prefix, struct userNode *service, chanmsg_func_t handler)
475 {
476 if (chanmsg_funcs[prefix].func)
477 log_module(MAIN_LOG, LOG_WARNING, "Re-registering new chanmsg handler for character `%c'.", prefix);
478 chanmsg_funcs[prefix].func = handler;
479 chanmsg_funcs[prefix].service = service;
480 }
481
482 void
483 reg_allchanmsg_func(struct userNode *service, chanmsg_func_t handler)
484 {
485 int x;
486 for (x = 0; x < ALLCHANMSG_FUNCS_MAX; x++) {
487 if (allchanmsg_funcs[x].func)
488 continue;
489 allchanmsg_funcs[x].func = handler;
490 allchanmsg_funcs[x].service = service;
491 break;
492 }
493 }
494
495 struct userNode *
496 get_chanmsg_bot(unsigned char prefix)
497 {
498 return chanmsg_funcs[prefix].service;
499 }
500
501 static mode_change_func_t *mcf_list;
502 static unsigned int mcf_size = 0, mcf_used = 0;
503
504 void
505 reg_mode_change_func(mode_change_func_t handler)
506 {
507 if (mcf_used == mcf_size) {
508 if (mcf_size) {
509 mcf_size <<= 1;
510 mcf_list = realloc(mcf_list, mcf_size*sizeof(mode_change_func_t));
511 } else {
512 mcf_size = 8;
513 mcf_list = malloc(mcf_size*sizeof(mode_change_func_t));
514 }
515 }
516 mcf_list[mcf_used++] = handler;
517 }
518
519 struct mod_chanmode *
520 mod_chanmode_alloc(unsigned int argc)
521 {
522 struct mod_chanmode *res;
523 if (argc > 1)
524 res = calloc(1, sizeof(*res) + (argc-1)*sizeof(res->args[0]));
525 else
526 res = calloc(1, sizeof(*res));
527 if (res) {
528 #if !defined(NDEBUG)
529 res->alloc_argc = argc;
530 #endif
531 res->argc = argc;
532 }
533 return res;
534 }
535
536 struct mod_chanmode *
537 mod_chanmode_dup(struct mod_chanmode *orig, unsigned int extra)
538 {
539 struct mod_chanmode *res;
540 res = mod_chanmode_alloc(orig->argc + extra);
541 if (res) {
542 res->modes_set = orig->modes_set;
543 res->modes_clear = orig->modes_clear;
544 res->new_limit = orig->new_limit;
545 memcpy(res->new_key, orig->new_key, sizeof(res->new_key));
546 res->argc = orig->argc;
547 memcpy(res->args, orig->args, orig->argc*sizeof(orig->args[0]));
548 }
549 return res;
550 }
551
552 void
553 mod_chanmode_apply(struct userNode *who, struct chanNode *channel, struct mod_chanmode *change)
554 {
555 struct banNode *bn;
556 struct exemptNode *en;
557 unsigned int ii, jj;
558
559 assert(change->argc <= change->alloc_argc);
560 channel->modes = (channel->modes & ~change->modes_clear) | change->modes_set;
561 if (change->modes_set & MODE_LIMIT)
562 channel->limit = change->new_limit;
563 if (change->modes_set & MODE_KEY)
564 strcpy(channel->key, change->new_key);
565 for (ii = 0; ii < change->argc; ++ii) {
566 switch (change->args[ii].mode) {
567 case MODE_BAN:
568 /* If any existing ban is a subset of the new ban,
569 * silently remove it. The new ban is not allowed
570 * to be more specific than an existing ban.
571 */
572 for (jj=0; jj<channel->banlist.used; ++jj) {
573 bn = channel->banlist.list[jj];
574 if (match_ircglobs(change->args[ii].u.hostmask, bn->ban)) {
575 banList_remove(&channel->banlist, bn);
576 free(bn);
577 jj--;
578 }
579 }
580 bn = calloc(1, sizeof(*bn));
581 safestrncpy(bn->ban, change->args[ii].u.hostmask, sizeof(bn->ban));
582 if (who)
583 safestrncpy(bn->who, who->nick, sizeof(bn->who));
584 else
585 safestrncpy(bn->who, "<unknown>", sizeof(bn->who));
586 bn->set = now;
587 banList_append(&channel->banlist, bn);
588 break;
589 case MODE_REMOVE|MODE_BAN:
590 for (jj=0; jj<channel->banlist.used; ++jj) {
591 bn = channel->banlist.list[jj];
592 if (strcmp(bn->ban, change->args[ii].u.hostmask))
593 continue;
594 free(bn);
595 banList_remove(&channel->banlist, bn);
596 break;
597 }
598 break;
599 case MODE_EXEMPT:
600 /* If any existing exempt is a subset of the new exempt,
601 * silently remove it. The new exempt is not allowed
602 * to be more specific than an existing exempt.
603 */
604 for (jj=0; jj<channel->exemptlist.used; ++jj) {
605 if (match_ircglobs(change->args[ii].u.hostmask, channel->exemptlist.list[jj]->exempt)) {
606 exemptList_remove(&channel->exemptlist, channel->exemptlist.list[jj]);
607 free(channel->exemptlist.list[jj]);
608 jj--;
609 }
610 }
611 en = calloc(1, sizeof(*en));
612 safestrncpy(en->exempt, change->args[ii].u.hostmask, sizeof(en->exempt));
613 if (who)
614 safestrncpy(en->who, who->nick, sizeof(en->who));
615 else
616 safestrncpy(en->who, "<unknown>", sizeof(en->who));
617 en->set = now;
618 exemptList_append(&channel->exemptlist, en);
619 break;
620 case MODE_REMOVE|MODE_EXEMPT:
621 for (jj=0; jj<channel->exemptlist.used; ++jj) {
622 if (strcmp(channel->exemptlist.list[jj]->exempt, change->args[ii].u.hostmask))
623 continue;
624 free(channel->exemptlist.list[jj]);
625 exemptList_remove(&channel->exemptlist, channel->exemptlist.list[jj]);
626 break;
627 }
628 break;
629 /* XXX Hack: this is the stupedest use of switch iv ever seen.
630 * you have to compare for EVERY POSSIBLE COMBINATION of bitmask
631 * because switch does only full comparison. This needs redone as if/else.
632 **/
633 case MODE_CHANOP:
634 case MODE_HALFOP:
635 case MODE_VOICE:
636 case MODE_VOICE|MODE_CHANOP:
637 case MODE_VOICE|MODE_HALFOP:
638 case MODE_CHANOP|MODE_HALFOP:
639 case MODE_VOICE|MODE_CHANOP|MODE_HALFOP:
640 case MODE_REMOVE|MODE_CHANOP:
641 case MODE_REMOVE|MODE_HALFOP:
642 case MODE_REMOVE|MODE_VOICE:
643 case MODE_REMOVE|MODE_VOICE|MODE_CHANOP:
644 case MODE_REMOVE|MODE_VOICE|MODE_HALFOP:
645 case MODE_REMOVE|MODE_CHANOP|MODE_HALFOP:
646 case MODE_REMOVE|MODE_VOICE|MODE_CHANOP|MODE_HALFOP:
647 if (change->args[ii].mode & MODE_REMOVE)
648 change->args[ii].u.member->modes &= ~change->args[ii].mode;
649 else
650 change->args[ii].u.member->modes |= change->args[ii].mode;
651 break;
652 default:
653 assert(0 && "Invalid mode argument");
654 continue;
655 }
656 }
657 }
658
659 void
660 mod_chanmode_free(struct mod_chanmode *change)
661 {
662 free(change);
663 }
664
665 int
666 mod_chanmode(struct userNode *who, struct chanNode *channel, char **modes, unsigned int argc, unsigned int flags)
667 {
668 struct mod_chanmode *change;
669 unsigned int ii;
670
671 if (!modes || !modes[0])
672 return 0;
673 if (!(change = mod_chanmode_parse(channel, modes, argc, flags)))
674 return 0;
675 if (flags & MC_ANNOUNCE)
676 mod_chanmode_announce(who, channel, change);
677 else
678 mod_chanmode_apply(who, channel, change);
679 if (flags & MC_NOTIFY)
680 for (ii = 0; ii < mcf_used; ++ii)
681 mcf_list[ii](channel, who, change);
682 mod_chanmode_free(change);
683 return 1;
684 }
685
686 int
687 irc_make_chanmode(struct chanNode *chan, char *out)
688 {
689 struct mod_chanmode change;
690 mod_chanmode_init(&change);
691 change.modes_set = chan->modes;
692 change.new_limit = chan->limit;
693 safestrncpy(change.new_key, chan->key, sizeof(change.new_key));
694 return strlen(mod_chanmode_format(&change, out));
695 }
696
697 char *
698 generate_hostmask(struct userNode *user, int options)
699 {
700 char *nickname, *ident, *hostname;
701 char *mask;
702 int len, ii;
703
704 /* figure out string parts */
705 if (options & GENMASK_OMITNICK)
706 nickname = NULL;
707 else if (options & GENMASK_USENICK)
708 nickname = user->nick;
709 else
710 nickname = "*";
711 if (options & GENMASK_STRICT_IDENT)
712 // sethost - reed/apples
713 if (IsSetHost(user)) {
714 ident = alloca(strcspn(user->sethost, "@")+2);
715 safestrncpy(ident, user->sethost, strcspn(user->sethost, "@")+1);
716 }
717 else
718 ident = user->ident;
719 else if (options & GENMASK_ANY_IDENT)
720 ident = "*";
721 else {
722 // sethost - reed/apples
723 if (IsSetHost(user)) {
724 ident = alloca(strcspn(user->sethost, "@")+3);
725 ident[0] = '*';
726 safestrncpy(ident+1, user->sethost, strcspn(user->sethost, "@")+1);
727 } else {
728 ident = alloca(strlen(user->ident)+2);
729 ident[0] = '*';
730 strcpy(ident+1, user->ident + ((*user->ident == '~')?1:0));
731 }
732 }
733 hostname = user->hostname;
734 if (IsFakeHost(user) && IsHiddenHost(user) && !(options & GENMASK_NO_HIDING)) {
735 hostname = user->fakehost;
736 } else if (IsHiddenHost(user) && user->handle_info && hidden_host_suffix && !(options & GENMASK_NO_HIDING)) {
737 hostname = alloca(strlen(user->handle_info->handle) + strlen(hidden_host_suffix) + 2);
738 sprintf(hostname, "%s.%s", user->handle_info->handle, hidden_host_suffix);
739 } else if (options & GENMASK_STRICT_HOST) {
740 if (options & GENMASK_BYIP)
741 hostname = inet_ntoa(user->ip);
742 } else if ((options & GENMASK_BYIP) || !hostname[strspn(hostname, "0123456789.")]) {
743 /* Should generate an IP-based hostmask. By popular acclaim, a /16
744 * hostmask is used by default. */
745 unsigned masked_ip, mask, masklen;
746 masklen = 16;
747 mask = ~0 << masklen;
748 masked_ip = ntohl(user->ip.s_addr) & mask;
749 hostname = alloca(32);
750 if (options & GENMASK_X3MASK) {
751 sprintf(hostname, "%d.%d.%d.%d/%d", (masked_ip>>24)&0xFF, (masked_ip>>16)&0xFF, (masked_ip>>8)&0xFF, masked_ip&0xFF, masklen);
752 } else {
753 int ofs = 0;
754 for (ii=0; ii<4; ii++) {
755 if (masklen) {
756 ofs += sprintf(hostname+ofs, "%d.", (masked_ip>>24)&0xFF);
757 masklen -= 8;
758 masked_ip <<= 8;
759 } else {
760 ofs += sprintf(hostname+ofs, "*.");
761 }
762 }
763 /* Truncate the last . */
764 hostname[ofs-1] = 0;
765 }
766 } else {
767 int cnt;
768 /* This heuristic could be made smarter. Is it worth the effort? */
769 for (ii=cnt=0; hostname[ii]; ii++)
770 if (hostname[ii] == '.')
771 cnt++;
772 if (cnt == 1) {
773 /* only a two-level domain name; leave hostname */
774 } else if (cnt == 2) {
775 for (ii=0; user->hostname[ii] != '.'; ii++) ;
776 /* Add 3 to account for the *. and \0. */
777 hostname = alloca(strlen(user->hostname+ii)+3);
778 sprintf(hostname, "*.%s", user->hostname+ii+1);
779 } else {
780 for (cnt=3, ii--; cnt; ii--)
781 if (user->hostname[ii] == '.')
782 cnt--;
783 /* The loop above will overshoot the dot one character;
784 we skip forward two (the one character and the dot)
785 when printing, so we only add one for the \0. */
786 hostname = alloca(strlen(user->hostname+ii)+1);
787 sprintf(hostname, "*.%s", user->hostname+ii+2);
788 }
789 }
790 // sethost - reed/apples
791 if (IsSetHost(user))
792 hostname = strchr(user->sethost, '@') + 1;
793
794 /* Emit hostmask */
795 len = strlen(ident) + strlen(hostname) + 2;
796 if (nickname) {
797 len += strlen(nickname) + 1;
798 mask = malloc(len);
799 sprintf(mask, "%s!%s@%s", nickname, ident, hostname);
800 } else {
801 mask = malloc(len);
802 sprintf(mask, "%s@%s", ident, hostname);
803 }
804 return mask;
805 }
806
807 int
808 IsChannelName(const char *name) {
809 unsigned int ii;
810
811 if (*name !='#')
812 return 0;
813 for (ii=1; name[ii]; ++ii) {
814 if ((name[ii] > 0) && (name[ii] <= 32))
815 return 0;
816 if (name[ii] == ',')
817 return 0;
818 if (name[ii] == '\xa0')
819 return 0;
820 }
821 return 1;
822 }