1 /* proto-common.c - common IRC protocol parsing/sending support
2 * Copyright 2000-2006 srvx Development Team
4 * This file is part of srvx.
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.
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.
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.
30 #ifdef HAVE_SYS_SOCKET_H
31 #include <sys/socket.h>
33 #ifdef HAVE_NETINET_IN_H
34 #include <netinet/in.h>
36 #ifdef HAVE_ARPA_INET_H
37 #include <arpa/inet.h>
40 unsigned int lines_processed;
42 struct io_fd *socket_io_fd;
44 const char *hidden_host_suffix;
46 static char replay_line[MAXLEN+80];
48 static int ping_timeout;
49 static int replay_connected;
50 static unsigned int nicklen = NICKLEN; /* how long do we think servers allow nicks to be? */
51 static struct userList dead_users;
53 extern struct cManagerNode cManager;
54 extern unsigned long burst_length;
55 extern struct cManagerNode cManager;
56 extern struct policer_params *oper_policer_params, *luser_policer_params;
57 extern server_link_func_t *slf_list;
58 extern unsigned int slf_size, slf_used;
59 extern new_user_func_t *nuf_list;
60 extern unsigned int nuf_size, nuf_used;
61 extern del_user_func_t *duf_list;
62 extern unsigned int duf_size, duf_used;
63 extern unsigned long boot_time;
65 void received_ping(void);
67 static int replay_read(void);
68 static dict_t irc_func_dict;
70 typedef void (*foreach_chanfunc) (struct chanNode *chan, void *data);
71 typedef void (*foreach_nonchan) (char *name, void *data);
72 typedef void (*foreach_userfunc) (struct userNode *user, void *data);
73 typedef void (*foreach_nonuser) (char *name, void *data);
74 static void parse_foreach(char *target_list, foreach_chanfunc cf, foreach_nonchan nc, foreach_userfunc uf, foreach_nonuser nu, void *data);
77 uplink_readable(struct io_fd *fd) {
78 static char buffer[MAXLEN];
82 pos = ioset_line_read(fd, buffer, sizeof(buffer));
87 if ((eol = strpbrk(buffer, "\r\n")))
89 log_replay(MAIN_LOG, false, buffer);
90 if (cManager.uplink->state != DISCONNECTED)
91 parse_line(buffer, 0);
96 socket_destroyed(struct io_fd *fd)
98 if (fd && fd->state != IO_CONNECTED)
99 log_module(MAIN_LOG, LOG_ERROR, "Connection to server lost.");
101 cManager.uplink->state = DISCONNECTED;
103 DelServer(self->uplink, 0, NULL);
106 void replay_event_loop(void)
108 while (!quit_services) {
109 if (!replay_connected) {
110 /* this time fudging is to get some of the logging right */
111 self->link_time = self->boot = now;
112 cManager.uplink->state = AUTHENTICATING;
113 irc_introduce(cManager.uplink->password);
114 replay_connected = 1;
115 } else if (!replay_read()) {
116 log_module(MAIN_LOG, LOG_ERROR, "Connection to server lost.");
124 create_socket_client(struct uplinkNode *target)
126 int port = target->port;
127 const char *addr = target->host;
130 return feof(replay_file) ? 0 : 1;
133 /* Leave the existing socket open, say we failed. */
134 log_module(MAIN_LOG, LOG_ERROR, "Refusing to create second connection to %s:%d.", addr, port);
138 log_module(MAIN_LOG, LOG_INFO, "Connecting to %s:%i...", addr, port);
140 socket_io_fd = ioset_connect(cManager.uplink->bind_addr, cManager.uplink->bind_addr_len, addr, port, 1, 0, NULL);
142 log_module(MAIN_LOG, LOG_ERROR, "Connection to uplink failed: %s (%d)", strerror(errno), errno);
143 target->state = DISCONNECTED;
147 socket_io_fd->readable_cb = uplink_readable;
148 socket_io_fd->destroy_cb = socket_destroyed;
149 socket_io_fd->line_reads = 1;
150 log_module(MAIN_LOG, LOG_INFO, "Connection to server established.");
151 cManager.uplink = target;
152 target->state = AUTHENTICATING;
158 replay_read_line(void)
161 unsigned long new_time;
163 if (replay_line[0]) return;
165 if (!fgets(replay_line, sizeof(replay_line), replay_file)) {
166 if (feof(replay_file)) {
168 memset(replay_line, 0, sizeof(replay_line));
172 if ((replay_line[0] != '[')
173 || (replay_line[3] != ':')
174 || (replay_line[6] != ':')
175 || (replay_line[9] != ' ')
176 || (replay_line[12] != '/')
177 || (replay_line[15] != '/')
178 || (replay_line[20] != ']')
179 || (replay_line[21] != ' ')) {
180 log_module(MAIN_LOG, LOG_ERROR, "Unrecognized timestamp in replay file: %s", replay_line);
183 timestamp.tm_hour = strtoul(replay_line+1, NULL, 10);
184 timestamp.tm_min = strtoul(replay_line+4, NULL, 10);
185 timestamp.tm_sec = strtoul(replay_line+7, NULL, 10);
186 timestamp.tm_mon = strtoul(replay_line+10, NULL, 10) - 1;
187 timestamp.tm_mday = strtoul(replay_line+13, NULL, 10);
188 timestamp.tm_year = strtoul(replay_line+16, NULL, 10) - 1900;
189 timestamp.tm_isdst = 0;
190 new_time = mktime(×tamp);
191 if (new_time == (unsigned long)-1) {
192 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);
197 if (strncmp(replay_line+22, "(info) ", 7))
206 char read_line[MAXLEN];
209 /* if it's a sent line, break out to handle it */
210 if (!strncmp(replay_line+29, " ", 3))
212 if (!strncmp(replay_line+29, "W: ", 3)) {
213 log_module(MAIN_LOG, LOG_ERROR, "Expected response from services: %s", replay_line+32);
219 log_replay(MAIN_LOG, false, replay_line+32);
220 safestrncpy(read_line, replay_line+32, sizeof(read_line));
221 len = strlen(read_line);
222 if (read_line[len-1] == '\n')
223 read_line[--len] = 0;
225 parse_line(read_line, 0);
231 replay_write(char *text)
234 if (strncmp(replay_line+29, "W: ", 3)) {
235 log_module(MAIN_LOG, LOG_ERROR, "Unexpected output during replay: %s", text);
238 if (strcmp(replay_line+32, text)) {
239 log_module(MAIN_LOG, LOG_ERROR, "Incorrect output during replay:\nReceived: %sExpected: %s", text, replay_line+32);
241 log_replay(MAIN_LOG, true, text);
247 void putsock(const char *text, ...) PRINTF_LIKE(1, 2);
250 putsock(const char *text, ...)
256 if (!cManager.uplink || cManager.uplink->state == DISCONNECTED) return;
258 va_start(arg_list, text);
259 pos = vsnprintf(buffer, MAXLEN - 2, text, arg_list);
261 if (pos < 0 || pos > (MAXLEN - 2)) pos = MAXLEN - 2;
264 log_replay(MAIN_LOG, true, buffer);
265 buffer[pos++] = '\n';
267 ioset_write(socket_io_fd, buffer, pos);
269 replay_write(buffer);
277 replay_connected = 0;
278 socket_destroyed(socket_io_fd);
280 ioset_close(socket_io_fd, 3);
285 #define CMD_FUNC(NAME) int NAME(UNUSED_ARG(const char *origin), UNUSED_ARG(unsigned int argc), UNUSED_ARG(char **argv))
286 typedef CMD_FUNC(cmd_func_t);
288 static void timed_ping_timeout(void *data);
290 /* Ping state is kept in the timeq (only one of these two can be in
291 * the queue at any given time). */
293 timed_send_ping(UNUSED_ARG(void *data))
295 irc_ping(self->name);
296 timeq_add(now + ping_timeout, timed_ping_timeout, 0);
300 timed_ping_timeout(UNUSED_ARG(void *data))
302 /* Uplink "health" tracking could be accomplished by counting the
303 number of ping timeouts that happen for each uplink. After the
304 timeouts per time period exceeds some amount, the uplink could
305 be marked as unavalable.*/
306 irc_squit(self, "Ping timeout.", NULL);
309 static CMD_FUNC(cmd_pass)
311 const char *true_pass;
315 true_pass = cManager.uplink->their_password;
316 if (true_pass && strcmp(true_pass, argv[1])) {
317 /* It might be good to mark the uplink as unavailable when
318 this happens, though there should be a way of resetting
320 irc_squit(self, "Incorrect password received.", NULL);
324 cManager.uplink->state = BURSTING;
328 static CMD_FUNC(cmd_dummy)
330 /* we don't care about these messages */
334 static CMD_FUNC(cmd_error)
336 if (argv[1]) log_module(MAIN_LOG, LOG_ERROR, "Error from ircd: %s", argv[1]);
337 log_module(MAIN_LOG, LOG_ERROR, "Error received from uplink, squitting.");
339 if (cManager.uplink->state != CONNECTED) {
340 /* Error messages while connected should be fine. */
341 cManager.uplink->flags |= UPLINK_UNAVAILABLE;
342 log_module(MAIN_LOG, LOG_ERROR, "Disabling uplink.");
349 static CMD_FUNC(cmd_stats)
355 if (!(un = GetUserH(origin)))
357 switch (argv[1][0]) {
359 unsigned long uptime;
360 uptime = now - boot_time;
361 irc_numeric(un, RPL_STATSUPTIME, ":Server Up %d days %d:%02d:%02d",
362 uptime/(24*60*60), (uptime/(60*60))%24, (uptime/60)%60, uptime%60);
363 irc_numeric(un, RPL_MAXCONNECTIONS, ":Highest connection count: %d (%d clients)",
364 self->max_clients+1, self->max_clients);
367 default: /* unrecognized/unhandled stats output */ break;
369 irc_numeric(un, 219, "%s :End of /STATS report", argv[1]);
373 static CMD_FUNC(cmd_version)
375 struct userNode *user;
376 if (!(user = GetUserH(origin))) {
377 log_module(MAIN_LOG, LOG_ERROR, "Could not find VERSION origin user %s", origin);
380 irc_numeric(user, 351, "%s.%s %s :%s", PACKAGE_TARNAME, PACKAGE_VERSION, self->name, CODENAME);
384 static CMD_FUNC(cmd_admin)
386 struct userNode *user;
387 struct string_list *slist;
389 if (!(user = GetUserH(origin))) {
390 log_module(MAIN_LOG, LOG_ERROR, "Could not find ADMIN origin user %s", origin);
393 if ((slist = conf_get_data("server/admin", RECDB_STRING_LIST)) && slist->used) {
396 irc_numeric(user, 256, ":Administrative info about %s", self->name);
397 for (i = 0; i < slist->used && i < 3; i++)
398 irc_numeric(user, 257 + i, ":%s", slist->list[i]);
400 irc_numeric(user, 423, ":No administrative info available");
406 recalc_bursts(struct server *eob_server)
409 eob_server->burst = eob_server->self_burst;
410 if (eob_server->uplink != self)
411 eob_server->burst = eob_server->burst || eob_server->uplink->burst;
412 for (nn=0; nn < eob_server->children.used; nn++)
413 recalc_bursts(eob_server->children.list[nn]);
416 static struct chanmsg_func {
418 struct userNode *service;
419 } chanmsg_funcs[256]; /* indexed by trigger character */
421 static struct allchanmsg_func {
423 struct userNode *service;
424 } allchanmsg_funcs[ALLCHANMSG_FUNCS_MAX];
426 struct privmsg_desc {
427 struct userNode *user;
429 unsigned int is_notice : 1;
430 unsigned int is_qualified : 1;
434 privmsg_chan_helper(struct chanNode *cn, void *data)
436 extern unsigned short offchannel_allowed[256];
437 struct privmsg_desc *pd = data;
439 struct chanmsg_func *cf;
442 /* Don't complain if it can't find the modeNode because the channel might
444 if ((mn = GetUserMode(cn, pd->user)))
445 mn->idle_since = now;
447 /* Never send a NOTICE to a channel to one of the services */
448 cf = &chanmsg_funcs[(unsigned char)pd->text[0]];
449 if (cf->func && !pd->is_notice
450 && (offchannel_allowed[(unsigned char)pd->text[0]]
451 || (GetUserMode(cn, cf->service) && !IsDeaf(cf->service))))
452 cf->func(pd->user, cn, pd->text+1, cf->service, pd->is_notice);
454 spamserv_channel_message(cn, pd->user, pd->text);
456 /* This catches *all* text sent to the channel that the services server sees */
457 for (x = 0; x < ALLCHANMSG_FUNCS_MAX; x++) {
458 cf = (struct chanmsg_func *)&allchanmsg_funcs[x];
460 break; /* end of list */
462 cf->func(pd->user, cn, pd->text, cf->service, pd->is_notice);
467 privmsg_invalid(char *name, void *data)
469 struct privmsg_desc *pd = data;
473 irc_numeric(pd->user, ERR_NOSUCHNICK, "%s@%s :No such nick", name, self->name);
477 struct userNode *user;
482 part_helper(struct chanNode *cn, void *data)
484 struct part_desc *desc = data;
485 DelChannelUser(desc->user, cn, desc->text, false);
486 if (IsOper(desc->user))
487 operpart(cn, desc->user);
490 static CMD_FUNC(cmd_part)
492 struct part_desc desc;
496 desc.user = GetUserH(origin);
499 desc.text = (argc > 2) ? argv[argc - 1] : NULL;
500 parse_foreach(argv[1], part_helper, NULL, NULL, NULL, &desc);
505 reg_chanmsg_func(unsigned char prefix, struct userNode *service, chanmsg_func_t handler)
507 if (chanmsg_funcs[prefix].func)
508 log_module(MAIN_LOG, LOG_WARNING, "Re-registering new chanmsg handler for character `%c'.", prefix);
509 chanmsg_funcs[prefix].func = handler;
510 chanmsg_funcs[prefix].service = service;
514 reg_allchanmsg_func(struct userNode *service, chanmsg_func_t handler)
517 for (x = 0; x < ALLCHANMSG_FUNCS_MAX; x++) {
518 if (allchanmsg_funcs[x].func)
520 allchanmsg_funcs[x].func = handler;
521 allchanmsg_funcs[x].service = service;
527 get_chanmsg_bot(unsigned char prefix)
529 return chanmsg_funcs[prefix].service;
532 static mode_change_func_t *mcf_list;
533 static unsigned int mcf_size = 0, mcf_used = 0;
536 reg_mode_change_func(mode_change_func_t handler)
538 if (mcf_used == mcf_size) {
541 mcf_list = realloc(mcf_list, mcf_size*sizeof(mode_change_func_t));
544 mcf_list = malloc(mcf_size*sizeof(mode_change_func_t));
547 mcf_list[mcf_used++] = handler;
550 static oper_func_t *of_list;
551 static unsigned int of_size = 0, of_used = 0;
554 reg_oper_func(oper_func_t handler)
556 if (of_used == of_size) {
559 of_list = realloc(of_list, of_size*sizeof(oper_func_t));
562 of_list = malloc(of_size*sizeof(oper_func_t));
565 of_list[of_used++] = handler;
569 call_oper_funcs(struct userNode *user)
574 for (n=0; (n<of_used) && !user->dead; n++)
580 static xquery_func_t *xqf_list;
581 static unsigned int xqf_size = 0, xqf_used = 0;
584 reg_xquery_func(xquery_func_t handler)
586 if (xqf_used == xqf_size) {
589 xqf_list = realloc(xqf_list, xqf_size*sizeof(xquery_func_t));
592 xqf_list = malloc(xqf_size*sizeof(xquery_func_t));
595 xqf_list[xqf_used++] = handler;
599 call_xquery_funcs(struct server *source, const char routing[], const char query[])
602 for (n=0; n < xqf_used; n++)
604 xqf_list[n](source, routing, query);
608 struct mod_chanmode *
609 mod_chanmode_alloc(unsigned int argc)
611 struct mod_chanmode *res;
613 res = calloc(1, sizeof(*res) + (argc-1)*sizeof(res->args[0]));
615 res = calloc(1, sizeof(*res));
618 res->alloc_argc = argc;
625 struct mod_chanmode *
626 mod_chanmode_dup(struct mod_chanmode *orig, unsigned int extra)
628 struct mod_chanmode *res;
629 res = mod_chanmode_alloc(orig->argc + extra);
631 res->modes_set = orig->modes_set;
632 res->modes_clear = orig->modes_clear;
633 res->new_limit = orig->new_limit;
634 res->new_access = orig->new_access;
635 memcpy(res->new_altchan, orig->new_altchan, sizeof(res->new_altchan));
636 memcpy(res->new_noflood, orig->new_noflood, sizeof(res->new_noflood));
637 memcpy(res->new_key, orig->new_key, sizeof(res->new_key));
638 memcpy(res->new_upass, orig->new_upass, sizeof(res->new_upass));
639 memcpy(res->new_apass, orig->new_apass, sizeof(res->new_apass));
640 res->argc = orig->argc;
641 memcpy(res->args, orig->args, orig->argc*sizeof(orig->args[0]));
647 mod_chanmode_apply(struct userNode *who, struct chanNode *channel, struct mod_chanmode *change)
652 assert(change->argc <= change->alloc_argc);
653 channel->modes = (channel->modes & ~change->modes_clear) | change->modes_set;
654 if (change->modes_set & MODE_LIMIT)
655 channel->limit = change->new_limit;
656 if (change->modes_set & MODE_ACCESS)
657 channel->access = change->new_access;
658 if (change->modes_set & MODE_KEY)
659 strcpy(channel->key, change->new_key);
660 if (change->modes_set & MODE_ALTCHAN)
661 strcpy(channel->altchan, change->new_altchan);
662 if (change->modes_set & MODE_NOFLOOD)
663 strcpy(channel->noflood, change->new_noflood);
664 if (change->modes_set & MODE_UPASS)
665 strcpy(channel->upass, change->new_upass);
666 if (change->modes_set & MODE_APASS)
667 strcpy(channel->apass, change->new_apass);
668 for (ii = 0; ii < change->argc; ++ii) {
669 switch (change->args[ii].mode) {
671 /* If any existing ban is a subset of the new ban,
672 * silently remove it. The new ban is not allowed
673 * to be more specific than an existing ban.
675 for (jj=0; jj<channel->banlist.used; ++jj) {
676 bn = channel->banlist.list[jj];
677 if (match_ircglobs(change->args[ii].u.hostmask, bn->ban)) {
678 banList_remove(&channel->banlist, bn);
683 bn = calloc(1, sizeof(*bn));
684 safestrncpy(bn->ban, change->args[ii].u.hostmask, sizeof(bn->ban));
686 safestrncpy(bn->who, who->nick, sizeof(bn->who));
688 safestrncpy(bn->who, "<unknown>", sizeof(bn->who));
690 banList_append(&channel->banlist, bn);
692 case MODE_REMOVE|MODE_BAN:
693 for (jj=0; jj<channel->banlist.used; ++jj) {
694 bn = channel->banlist.list[jj];
695 if (strcmp(bn->ban, change->args[ii].u.hostmask))
698 banList_remove(&channel->banlist, bn);
704 case MODE_VOICE|MODE_CHANOP:
705 case MODE_REMOVE|MODE_CHANOP:
706 case MODE_REMOVE|MODE_VOICE:
707 case MODE_REMOVE|MODE_VOICE|MODE_CHANOP:
708 if (change->args[ii].mode & MODE_REMOVE)
709 change->args[ii].u.member->modes &= ~change->args[ii].mode;
711 change->args[ii].u.member->modes |= change->args[ii].mode;
714 assert(0 && "Invalid mode argument");
721 mod_chanmode_free(struct mod_chanmode *change)
727 mod_chanmode(struct userNode *who, struct chanNode *channel, char **modes, unsigned int argc, unsigned int flags)
729 struct modeNode *member;
730 struct mod_chanmode *change;
734 if (!modes || !modes[0])
736 if (who && (member = GetUserMode(channel, who)))
737 base_oplevel = member->oplevel;
739 base_oplevel = MAXOPLEVEL;
740 if (!(change = mod_chanmode_parse(channel, who, modes, argc, flags, base_oplevel)))
742 if (flags & MC_ANNOUNCE)
743 mod_chanmode_announce(who, channel, change);
745 mod_chanmode_apply(who, channel, change);
746 if (flags & MC_NOTIFY)
747 for (ii = 0; ii < mcf_used; ++ii)
748 mcf_list[ii](channel, who, change);
749 mod_chanmode_free(change);
754 irc_make_chanmode(struct chanNode *chan, char *out)
756 struct mod_chanmode change;
757 mod_chanmode_init(&change);
758 change.modes_set = chan->modes;
759 change.new_limit = chan->limit;
760 change.new_access = chan->access;
761 safestrncpy(change.new_altchan, chan->altchan, sizeof(change.new_altchan));
762 safestrncpy(change.new_noflood, chan->noflood, sizeof(change.new_noflood));
763 safestrncpy(change.new_key, chan->key, sizeof(change.new_key));
764 safestrncpy(change.new_upass, chan->upass, sizeof(change.new_upass));
765 safestrncpy(change.new_apass, chan->apass, sizeof(change.new_apass));
766 return strlen(mod_chanmode_format(&change, out));
770 generate_hostmask(struct userNode *user, int options)
773 char *nickname, *ident, *hostname, *mask;
776 /* figure out string parts */
777 if (options & GENMASK_OMITNICK)
779 else if (options & GENMASK_USENICK)
780 nickname = user->nick;
783 if (options & GENMASK_STRICT_IDENT)
785 else if (options & GENMASK_ANY_IDENT)
787 else if (IsFakeIdent(user) && IsHiddenHost(user) && !(options & GENMASK_NO_HIDING))
788 ident = user->fakeident;
790 ident = alloca(strlen(user->ident)+2);
792 strcpy(ident+1, user->ident + ((*user->ident == '~')?1:0));
794 hostname = user->hostname;
795 if (IsFakeHost(user) && IsHiddenHost(user) && !(options & GENMASK_NO_HIDING)) {
796 if(user->fakehost && user->fakehost[0] == '$') {
797 hostname = alloca(strlen(user->handle_info->handle) + strlen(user->fakehost));
798 sprintf(hostname, "%s%s", user->handle_info->handle, user->fakehost+1);
800 hostname = user->fakehost;
802 } else if (IsHiddenHost(user) && user->handle_info && hidden_host_suffix && !(options & GENMASK_NO_HIDING)) {
803 hostname = alloca(strlen(user->handle_info->handle) + strlen(hidden_host_suffix) + 2);
804 sprintf(hostname, "%s.%s", user->handle_info->handle, hidden_host_suffix);
805 } else if (options & GENMASK_STRICT_HOST) {
806 if (options & GENMASK_BYIP)
807 hostname = (char*)irc_ntoa(&user->ip);
808 } else if ((options & GENMASK_BYIP) || irc_pton(&ip, NULL, hostname)) {
809 /* Should generate an IP-based hostmask. */
810 hostname = alloca(IRC_NTOP_MAX_SIZE);
811 hostname[IRC_NTOP_MAX_SIZE-1] = '\0';
812 if (irc_in_addr_is_ipv4(user->ip)) {
813 /* By popular acclaim, a /16 hostmask is used. */
814 sprintf(hostname, "%d.%d.*", user->ip.in6_8[12], user->ip.in6_8[13]);
815 } else if (irc_in_addr_is_ipv6(user->ip)) {
816 /* Who knows what the default mask should be? Use a /48 to start with. */
817 sprintf(hostname, "%x:%x:%x:*", ntohs(user->ip.in6[0]), ntohs(user->ip.in6[1]), ntohs(user->ip.in6[2]));
819 /* Unknown type; just copy IP directly. */
820 irc_ntop(hostname, IRC_NTOP_MAX_SIZE, &user->ip);
824 /* This heuristic could be made smarter. Is it worth the effort? */
825 for (ii=cnt=0; hostname[ii]; ii++)
826 if (hostname[ii] == '.')
828 if (cnt == 0 || cnt == 1) {
829 /* only a one- or two-level domain name; leave hostname */
830 } else if (cnt == 2) {
831 for (ii=0; user->hostname[ii] != '.'; ii++) ;
832 /* Add 3 to account for the *. and \0. */
833 hostname = alloca(strlen(user->hostname+ii)+3);
834 sprintf(hostname, "*.%s", user->hostname+ii+1);
836 for (cnt=3, ii--; cnt; ii--)
837 if (user->hostname[ii] == '.')
839 /* The loop above will overshoot the dot one character;
840 we skip forward two (the one character and the dot)
841 when printing, so we only add one for the \0. */
842 hostname = alloca(strlen(user->hostname+ii)+1);
843 sprintf(hostname, "*.%s", user->hostname+ii+2);
847 len = strlen(ident) + strlen(hostname) + 2;
849 len += strlen(nickname) + 1;
851 sprintf(mask, "%s!%s@%s", nickname, ident, hostname);
854 sprintf(mask, "%s@%s", ident, hostname);
860 IsChannelName(const char *name) {
865 for (ii=1; name[ii]; ++ii) {
866 if ((name[ii] > 0) && (name[ii] <= 32))
870 if (name[ii] == '\xa0')
877 irc_user_modes(const struct userNode *user, char modes[], size_t length)
881 for (ii = jj = 0; (jj < length) && (irc_user_mode_chars[ii] != '\0'); ++ii) {
882 if ((user->modes & (1 << ii)) && (irc_user_mode_chars[ii] != ' '))
883 modes[jj++] = irc_user_mode_chars[ii];