X-Git-Url: http://git.pk910.de/?a=blobdiff_plain;f=src%2Fproto-bahamut.c;h=a0c1d98ef102d5f80004c0a05aa3cb8dc21a2f79;hb=HEAD;hp=4b82d7fccd35a487dea84cc2cc493b920a71f9a1;hpb=aade06878c014cae4f34d5ab1e63789faf51c6a2;p=srvx.git diff --git a/src/proto-bahamut.c b/src/proto-bahamut.c index 4b82d7f..a0c1d98 100644 --- a/src/proto-bahamut.c +++ b/src/proto-bahamut.c @@ -1,11 +1,12 @@ /* proto-bahamut.c - IRC protocol output - * Copyright 2000-2004 srvx Development Team + * Copyright 2000-2006 srvx Development Team * - * This program is free software; you can redistribute it and/or modify + * This file is part of srvx. + * + * srvx is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. Important limitations are - * listed in the COPYING file that accompanies this software. + * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -13,7 +14,8 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, email srvx-maintainers@srvx.net. + * along with srvx; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ #include "proto-common.c" @@ -36,10 +38,13 @@ static dict_t service_msginfo_dict; /* holds service_message_info structs */ static int uplink_capab; static void privmsg_user_helper(struct userNode *un, void *data); +/* These correspond to 1 << X: 012345678901234567 */ +const char irc_user_mode_chars[] = "o iw dkg r "; + void irc_svsmode(struct userNode *target, char *modes, unsigned long stamp); struct server * -AddServer(struct server *uplink, const char *name, int hops, time_t boot, time_t link, UNUSED_ARG(const char *numeric), const char *description) { +AddServer(struct server *uplink, const char *name, int hops, unsigned long boot, unsigned long link_time, UNUSED_ARG(const char *numeric), const char *description) { struct server* sNode; sNode = calloc(1, sizeof(*sNode)); @@ -47,7 +52,7 @@ AddServer(struct server *uplink, const char *name, int hops, time_t boot, time_t safestrncpy(sNode->name, name, sizeof(sNode->name)); sNode->hops = hops; sNode->boot = boot; - sNode->link = link; + sNode->link_time = link_time; sNode->users = dict_new(); safestrncpy(sNode->description, description, sizeof(sNode->description)); serverList_init(&sNode->children); @@ -110,16 +115,19 @@ is_valid_nick(const char *nick) { } struct userNode * -AddUser(struct server* uplink, const char *nick, const char *ident, const char *hostname, const char *modes, const char *userinfo, time_t timestamp, struct in_addr realip, const char *stamp) { +AddUser(struct server* uplink, const char *nick, const char *ident, const char *hostname, const char *modes, const char *userinfo, unsigned long timestamp, irc_in_addr_t realip, unsigned long stamp) { struct userNode *uNode, *oldUser; - unsigned int nn; + unsigned int nn, dummy; if (!uplink) { log_module(MAIN_LOG, LOG_WARNING, "AddUser(%p, %s, ...): server does not exist!", uplink, nick); return NULL; } - if (!is_valid_nick(nick)) { + dummy = modes && modes[0] == '*'; + if (dummy) { + ++modes; + } else if (!is_valid_nick(nick)) { log_module(MAIN_LOG, LOG_WARNING, "AddUser(%p, %s, ...): invalid nickname detected.", uplink, nick); return NULL; } @@ -160,38 +168,31 @@ AddUser(struct server* uplink, const char *nick, const char *ident, const char * } mod_usermode(uNode, modes); - if (stamp) call_account_func(uNode, stamp); + if (dummy) uNode->modes |= FLAGS_DUMMY; + if (stamp) call_account_func(uNode, NULL, 0, stamp); if (IsLocal(uNode)) irc_user(uNode); - for (nn=0; nndead; nn++) + nuf_list[nn](uNode); return uNode; } struct userNode * -AddService(const char *nick, const char *desc) { - time_t timestamp = now; +AddLocalUser(const char *nick, const char *ident, const char *hostname, const char *desc, const char *modes) +{ + unsigned long timestamp = now; struct userNode *old_user = GetUserH(nick); - struct in_addr ipaddr = { INADDR_LOOPBACK }; - if (old_user) { - if (IsLocal(old_user)) - return old_user; - timestamp = old_user->timestamp - 1; - } - return AddUser(self, nick, nick, self->name, "+oikr", desc, timestamp, ipaddr, 0); -} + static const irc_in_addr_t ipaddr; -struct userNode * -AddClone(const char *nick, const char *ident, const char *hostname, const char *desc) { - time_t timestamp = now; - struct userNode *old_user = GetUserH(nick); - struct in_addr ipaddr = { INADDR_LOOPBACK }; + if (!modes) + modes = "+oikr"; if (old_user) { if (IsLocal(old_user)) return old_user; timestamp = old_user->timestamp - 1; } - return AddUser(self, nick, ident, hostname, "+ir", desc, timestamp, ipaddr, 0); + if (!hostname) + hostname = self->name; + return AddUser(self, nick, ident, hostname, modes, desc, timestamp, ipaddr, 0); } void @@ -206,7 +207,7 @@ DelUser(struct userNode* user, struct userNode *killer, int announce, const char unsigned int nn; for (nn=user->channels.used; nn>0;) { - DelChannelUser(user, user->channels.list[--nn]->channel, false, 0); + DelChannelUser(user, user->channels.list[--nn]->channel, NULL, false); } for (nn=duf_used; nn>0; ) duf_list[--nn](user, killer, why); user->uplink->clients--; @@ -221,6 +222,7 @@ DelUser(struct userNode* user, struct userNode *killer, int announce, const char irc_kill(killer, user, why); } } + dict_remove(service_msginfo_dict, user->nick); modeList_clean(&user->channels); user->dead = 1; if (dead_users.size) { @@ -242,31 +244,30 @@ irc_server(struct server *srv) { void irc_user(struct userNode *user) { char modes[32]; - int modelen = 0; - if (IsOper(user)) modes[modelen++] = 'o'; - if (IsInvisible(user)) modes[modelen++] = 'i'; - if (IsWallOp(user)) modes[modelen++] = 'w'; - if (IsService(user)) modes[modelen++] = 'k'; - if (IsServNotice(user)) modes[modelen++] = 's'; - if (IsDeaf(user)) modes[modelen++] = 'd'; - if (IsReggedNick(user)) modes[modelen++] = 'r'; - if (IsGlobal(user)) modes[modelen++] = 'g'; - modes[modelen] = 0; - putsock("NICK %s %d "FMT_TIME_T" +%s %s %s %s %d %u :%s", - user->nick, user->uplink->hops+2, user->timestamp, modes, - user->ident, user->hostname, user->uplink->name, 0, ntohl(user->ip.s_addr), user->info); + if (!user || user->nick[0] != ' ') return; + irc_user_modes(user, modes, sizeof(modes)); + putsock("NICK %s %d %lu +%s %s %s %s %d %u :%s", + user->nick, user->uplink->hops+2, (unsigned long)user->timestamp, + modes, user->ident, user->hostname, user->uplink->name, 0, + ntohl(user->ip.in6_32[3]), user->info); } void -irc_account(struct userNode *user, const char *stamp) +irc_account(struct userNode *user, UNUSED_ARG(const char *stamp), UNUSED_ARG(unsigned long timestamp), unsigned long serial) { if (IsReggedNick(user)) { - irc_svsmode(user, "+rd", base64toint(stamp, IDLEN)); + irc_svsmode(user, "+rd", serial); } else { - irc_svsmode(user, "+d", base64toint(stamp, IDLEN)); + irc_svsmode(user, "+d", serial); } } +void +irc_fakehost(UNUSED_ARG(struct userNode *user), UNUSED_ARG(const char *host), UNUSED_ARG(const char *ident), UNUSED_ARG(int force)) +{ + /* not supported in bahamut */ +} + void irc_regnick(struct userNode *user) { @@ -287,7 +288,7 @@ irc_nick(struct userNode *user, const char *old_nick) { dict_insert(service_msginfo_dict, user->nick, smi); } } - putsock(":%s NICK %s :"FMT_TIME_T, old_nick, user->nick, user->timestamp); + putsock(":%s NICK %s :%lu", old_nick, user->nick, (unsigned long)user->timestamp); } void @@ -302,17 +303,12 @@ irc_capab() { void irc_svinfo() { - putsock("SVINFO 3 3 0 :"FMT_TIME_T, now); -} - -void -irc_burst() { - putsock("BURST"); + putsock("SVINFO 3 3 0 :%lu", (unsigned long)now); } void irc_introduce(const char *passwd) { - extern time_t burst_begin; + extern unsigned long burst_begin; irc_pass(passwd); irc_capab(); @@ -357,14 +353,49 @@ irc_squit(struct server *srv, const char *message, const char *service_message) } } +static int +deliver_to_dummy(struct userNode *source, struct userNode *dest, const char *message, int type) +{ + struct service_message_info *smi; + + if (!dest || !IsDummy(dest) || !IsLocal(dest)) + return 0; + smi = dict_find(service_msginfo_dict, dest->nick, NULL); + switch (type) { + default: + if (smi && smi->on_privmsg) + { + smi->on_privmsg(source, dest, message, 0); + return 1; + } + break; + case 1: + if (smi && smi->on_notice) + { + smi->on_notice(source, dest, message, 0); + return 1; + } + break; + } + return 0; +} + void irc_privmsg(struct userNode *from, const char *to, const char *message) { - putsock(":%s PRIVMSG %s :%s", from->nick, to, message); + if (!deliver_to_dummy(from, GetUserH(to), message, 1)) + putsock(":%s PRIVMSG %s :%s", from->nick, to, message); } void irc_notice(struct userNode *from, const char *to, const char *message) { - putsock(":%s NOTICE %s :%s", from->nick, to, message); + if (!deliver_to_dummy(from, GetUserH(to), message, 0)) + putsock(":%s NOTICE %s :%s", from->nick, to, message); +} + +void +irc_notice_user(struct userNode *from, struct userNode *to, const char *message) { + if (!deliver_to_dummy(from, to, message, 0)) + putsock(":%s NOTICE %s :%s", from->nick, to->nick, message); } void @@ -374,9 +405,9 @@ irc_wallchops(UNUSED_ARG(struct userNode *from), UNUSED_ARG(const char *to), UNU void irc_join(struct userNode *who, struct chanNode *what) { if (what->members.used == 1) { - putsock(":%s SJOIN "FMT_TIME_T" %s + :@%s", self->name, what->timestamp, what->name, who->nick); + putsock(":%s SJOIN %lu %s + :@%s", self->name, (unsigned long)what->timestamp, what->name, who->nick); } else { - putsock(":%s SJOIN "FMT_TIME_T" %s", who->nick, what->timestamp, what->name); + putsock(":%s SJOIN %lu %s", who->nick, (unsigned long)what->timestamp, what->name); } } @@ -387,16 +418,16 @@ irc_invite(struct userNode *from, struct userNode *who, struct chanNode *to) { void irc_mode(struct userNode *who, struct chanNode *target, const char *modes) { - putsock(":%s MODE %s "FMT_TIME_T" %s", who->nick, target->name, target->timestamp, modes); + putsock(":%s MODE %s %lu %s", who->nick, target->name, (unsigned long)target->timestamp, modes); } void irc_svsmode(struct userNode *target, char *modes, unsigned long stamp) { extern struct userNode *nickserv; if (stamp) { - putsock(":%s SVSMODE %s "FMT_TIME_T" %s %lu", nickserv->nick, target->nick, target->timestamp, modes, stamp); + putsock(":%s SVSMODE %s %lu %s %lu", nickserv->nick, target->nick, (unsigned long)target->timestamp, modes, stamp); } else { - putsock(":%s SVSMODE %s "FMT_TIME_T" %s", nickserv->nick, target->nick, target->timestamp, modes); + putsock(":%s SVSMODE %s %lu %s", nickserv->nick, target->nick, (unsigned long)target->timestamp, modes); } } @@ -442,11 +473,11 @@ irc_gline(struct server *srv, struct gline *gline) { if (len > ArrayLength(ident)) len = ArrayLength(ident); safestrncpy(ident, gline->target, len); safestrncpy(host, sep+1, ArrayLength(host)); - putsock(":%s AKILL %s %s "FMT_TIME_T" %s "FMT_TIME_T" :%s", self->name, host, ident, gline->expires-gline->issued, gline->issuer, gline->issued, gline->reason); + putsock(":%s AKILL %s %s %lu %s %lu :%s", self->name, host, ident, (unsigned long)(gline->expires-gline->issued), gline->issuer, (unsigned long)gline->issued, gline->reason); } void -irc_settime(UNUSED_ARG(const char *srv_name_mask), UNUSED_ARG(time_t new_time)) +irc_settime(UNUSED_ARG(const char *srv_name_mask), UNUSED_ARG(unsigned long new_time)) { /* Bahamut has nothing like this, so ignore it. */ } @@ -497,7 +528,7 @@ irc_stats(struct userNode *from, struct server *target, char type) { void irc_svsnick(struct userNode *from, struct userNode *target, const char *newnick) { - putsock(":%s SVSNICK %s %s :"FMT_TIME_T, from->nick, target->nick, newnick, now); + putsock(":%s SVSNICK %s %s :%lu", from->nick, target->nick, newnick, (unsigned long)now); } void @@ -514,47 +545,47 @@ static void parse_foreach(char *target_list, foreach_chanfunc cf, foreach_nonchan nc, foreach_userfunc uf, foreach_nonuser nu, void *data) { char *j, old; do { - j = target_list; - while (*j != 0 && *j != ',') j++; - old = *j; + j = target_list; + while (*j != 0 && *j != ',') j++; + old = *j; *j = 0; - if (IsChannelName(target_list)) { - struct chanNode *chan = GetChannel(target_list); + if (IsChannelName(target_list)) { + struct chanNode *chan = GetChannel(target_list); if (chan) { if (cf) cf(chan, data); } else { if (nc) nc(target_list, data); } - } else { - struct userNode *user; + } else { + struct userNode *user; struct privmsg_desc *pd = data; pd->is_qualified = 0; if (*target_list == '@') { user = NULL; } else if (strchr(target_list, '@')) { - struct server *server; + struct server *server; pd->is_qualified = 1; - user = GetUserH(strtok(target_list, "@")); - server = GetServerH(strtok(NULL, "@")); - - if (user && (user->uplink != server)) { - /* Don't attempt to index into any arrays - using a user's numeric on another server. */ - user = NULL; - } - } else { - user = GetUserH(target_list); - } + user = GetUserH(strtok(target_list, "@")); + server = GetServerH(strtok(NULL, "@")); + + if (user && (user->uplink != server)) { + /* Don't attempt to index into any arrays + using a user's numeric on another server. */ + user = NULL; + } + } else { + user = GetUserH(target_list); + } if (user) { if (uf) uf(user, data); } else { if (nu) nu(target_list, data); } - } - target_list = j+1; + } + target_list = j+1; } while (old == ','); } @@ -580,6 +611,39 @@ static CMD_FUNC(cmd_privmsg) { return 1; } +static CMD_FUNC(cmd_whois) { + struct userNode *from; + struct userNode *who; + + if (argc < 3) + return 0; + if (!(from = GetUserH(origin))) { + log_module(MAIN_LOG, LOG_ERROR, "Could not find WHOIS origin user %s", origin); + return 0; + } + if(!(who = GetUserH(argv[2]))) { + irc_numeric(from, ERR_NOSUCHNICK, "%s@%s :No such nick", argv[2], self->name); + return 1; + } + if (IsHiddenHost(who) && !IsOper(from)) { + /* Just stay quiet. */ + return 1; + } + irc_numeric(from, RPL_WHOISUSER, "%s %s %s * :%s", who->nick, who->ident, who->hostname, who->info); +#ifdef WITH_PROTOCOL_P10 + if (his_servername && his_servercomment) + irc_numeric(from, RPL_WHOISSERVER, "%s %s :%s", who->nick, his_servername, his_servercomment); + else +#endif + irc_numeric(from, RPL_WHOISSERVER, "%s %s :%s", who->nick, who->uplink->name, who->uplink->description); + + if (IsOper(who)) { + irc_numeric(from, RPL_WHOISOPERATOR, "%s :is a megalomaniacal power hungry tyrant", who->nick); + } + irc_numeric(from, RPL_ENDOFWHOIS, "%s :End of /WHOIS list", who->nick); + return 1; +} + static CMD_FUNC(cmd_capab) { static const struct { const char *name; @@ -616,7 +680,7 @@ static void burst_channel(struct chanNode *chan) { if (!chan->members.used) return; /* send list of users in the channel.. */ - base_len = sprintf(line, ":%s SJOIN "FMT_TIME_T" %s ", self->name, chan->timestamp, chan->name); + base_len = sprintf(line, ":%s SJOIN %lu %s ", self->name, (unsigned long)chan->timestamp, chan->name); len = irc_make_chanmode(chan, line+base_len); pos = base_len + len; line[pos++] = ' '; @@ -642,7 +706,7 @@ static void burst_channel(struct chanNode *chan) { line[pos] = 0; putsock("%s", line); /* now send the bans.. */ - base_len = sprintf(line, ":%s MODE "FMT_TIME_T" %s +", self->name, chan->timestamp, chan->name); + base_len = sprintf(line, ":%s MODE %lu %s +", self->name, (unsigned long)chan->timestamp, chan->name); pos = sizeof(line)-1; line[pos] = 0; for (nn=queued=0; nnbanlist.used; nn++) { @@ -693,7 +757,6 @@ static CMD_FUNC(cmd_server) { AddServer(GetServerH(origin), argv[1], atoi(argv[2]), 0, now, 0, argv[3]); } else { self->uplink = AddServer(self, argv[1], atoi(argv[2]), 0, now, 0, argv[3]); - send_burst(); } return 1; } @@ -702,6 +765,7 @@ static CMD_FUNC(cmd_svinfo) { if (argc < 5) return 0; if ((atoi(argv[1]) < 3) || (atoi(argv[2]) > 3)) return 0; /* TODO: something with the timestamp we get from the other guy */ + send_burst(); return 1; } @@ -735,15 +799,14 @@ static CMD_FUNC(cmd_nick) { NickChange(un, argv[1], 1); } else { /* new nick from a server */ - char id[8]; unsigned long stamp; - struct in_addr ip; + irc_in_addr_t ip; if (argc < 10) return 0; stamp = strtoul(argv[8], NULL, 0); - if (stamp) inttobase64(id, stamp, IDLEN); - ip.s_addr = (argc > 10) ? atoi(argv[9]) : 0; - un = AddUser(GetServerH(argv[7]), argv[1], argv[5], argv[6], argv[4], argv[argc-1], atoi(argv[3]), ip, (stamp ? id : 0)); + if (argc > 10) + ip.in6_32[3] = htonl(atoi(argv[9])); + un = AddUser(GetServerH(argv[7]), argv[1], argv[5], argv[6], argv[4], argv[argc-1], atoi(argv[3]), ip, stamp); } return 1; } @@ -848,12 +911,6 @@ static CMD_FUNC(cmd_topic) { return 1; } -static CMD_FUNC(cmd_part) { - if (argc < 2) return 0; - parse_foreach(argv[1], part_helper, NULL, NULL, NULL, GetUserH(origin)); - return 1; -} - static CMD_FUNC(cmd_away) { struct userNode *un; @@ -894,10 +951,10 @@ static CMD_FUNC(cmd_pong) { if (argc < 3) return 0; if (!strcmp(argv[2], self->name)) { - timeq_del(0, timed_send_ping, 0, TIMEQ_IGNORE_WHEN|TIMEQ_IGNORE_DATA); - timeq_del(0, timed_ping_timeout, 0, TIMEQ_IGNORE_WHEN|TIMEQ_IGNORE_DATA); - timeq_add(now + ping_freq, timed_send_ping, 0); - received_ping(); + timeq_del(0, timed_send_ping, 0, TIMEQ_IGNORE_WHEN|TIMEQ_IGNORE_DATA); + timeq_del(0, timed_ping_timeout, 0, TIMEQ_IGNORE_WHEN|TIMEQ_IGNORE_DATA); + timeq_add(now + ping_freq, timed_send_ping, 0); + received_ping(); } return 1; } @@ -980,12 +1037,9 @@ static CMD_FUNC(cmd_svsnick) return 1; } -static oper_func_t *of_list; -static unsigned int of_size = 0, of_used = 0; - void parse_cleanup(void) { unsigned int nn; - if (of_list) free(of_list); + free(of_list); dict_delete(irc_func_dict); dict_delete(service_msginfo_dict); free(mcf_list); @@ -1051,6 +1105,7 @@ void init_parse(void) { userList_init(&dead_users); reg_exit_func(parse_cleanup); + (void)call_xquery_funcs; } int parse_line(char *line, int recursive) { @@ -1074,7 +1129,7 @@ int parse_line(char *line, int recursive) { res = 0; } if (!res) { - log_module(MAIN_LOG, LOG_ERROR, "PARSE ERROR on line: %s", unsplit_string(argv, argc, NULL)); + log_module(MAIN_LOG, LOG_ERROR, "PARSE ERROR on line: %s", unsplit_string(argv, argc, NULL)); } else if (!recursive) { unsigned int i; for (i=0; ion_privmsg = handler; } +void +unreg_privmsg_func(struct userNode *user) { + struct service_message_info *info; + info = dict_find(service_msginfo_dict, user->nick, NULL); + if (info) { + info->on_privmsg = NULL; + if (info->on_notice == NULL) { + dict_remove(service_msginfo_dict, user->nick); + } + } +} + void reg_notice_func(struct userNode *user, privmsg_func_t handler) { struct service_message_info *info = dict_find(service_msginfo_dict, user->nick, NULL); @@ -1120,28 +1187,14 @@ reg_notice_func(struct userNode *user, privmsg_func_t handler) { } void -reg_oper_func(oper_func_t handler) -{ - if (of_used == of_size) { - if (of_size) { - of_size <<= 1; - of_list = realloc(of_list, of_size*sizeof(oper_func_t)); - } else { - of_size = 8; - of_list = malloc(of_size*sizeof(oper_func_t)); - } - } - of_list[of_used++] = handler; -} - -static void -call_oper_funcs(struct userNode *user) -{ - unsigned int n; - if (IsLocal(user)) return; - for (n=0; nnick, NULL); + if (info) { + info->on_notice = NULL; + if (info->on_privmsg == NULL) { + dict_remove(service_msginfo_dict, user->nick); + } } } @@ -1151,37 +1204,34 @@ void mod_usermode(struct userNode *user, const char *mode_change) { if (!user || !mode_change) return; while (1) { #define do_user_mode(FLAG) do { if (add) user->modes |= FLAG; else user->modes &= ~FLAG; } while (0) - switch (*mode_change++) { - case 0: return; - case '+': add = 1; break; - case '-': add = 0; break; - case 'o': - do_user_mode(FLAGS_OPER); - if (add) { - userList_append(&curr_opers, user); - call_oper_funcs(user); - } else { - userList_remove(&curr_opers, user); - } - break; - case 'O': do_user_mode(FLAGS_LOCOP); break; - case 'i': do_user_mode(FLAGS_INVISIBLE); - if (add) invis_clients++; else invis_clients--; - break; - case 'w': do_user_mode(FLAGS_WALLOP); break; - case 's': do_user_mode(FLAGS_SERVNOTICE); break; - case 'd': do_user_mode(FLAGS_DEAF); break; - case 'r': do_user_mode(FLAGS_REGNICK); break; - case 'k': do_user_mode(FLAGS_SERVICE); break; - case 'g': do_user_mode(FLAGS_GLOBAL); break; - case 'h': do_user_mode(FLAGS_HELPER); break; - } + switch (*mode_change++) { + case 0: return; + case '+': add = 1; break; + case '-': add = 0; break; + case 'o': + do_user_mode(FLAGS_OPER); + if (add) { + userList_append(&curr_opers, user); + call_oper_funcs(user); + } else { + userList_remove(&curr_opers, user); + } + break; + case 'i': do_user_mode(FLAGS_INVISIBLE); + if (add) invis_clients++; else invis_clients--; + break; + case 'w': do_user_mode(FLAGS_WALLOP); break; + case 'd': do_user_mode(FLAGS_DEAF); break; + case 'r': do_user_mode(FLAGS_REGNICK); break; + case 'k': do_user_mode(FLAGS_SERVICE); break; + case 'g': do_user_mode(FLAGS_GLOBAL); break; + } #undef do_user_mode } } struct mod_chanmode * -mod_chanmode_parse(struct chanNode *channel, char **modes, unsigned int argc, unsigned int flags) +mod_chanmode_parse(struct chanNode *channel, struct userNode *user, char **modes, unsigned int argc, unsigned int flags, short base_oplevel) { struct mod_chanmode *change; unsigned int ii, in_arg, ch_arg, add; @@ -1211,6 +1261,14 @@ mod_chanmode_parse(struct chanNode *channel, char **modes, unsigned int argc, un case 'p': do_chan_mode(MODE_PRIVATE); break; case 's': do_chan_mode(MODE_SECRET); break; case 't': do_chan_mode(MODE_TOPICLIMIT); break; + case 'r': + if (!(flags & MCP_REGISTERED)) { + do_chan_mode(MODE_REGISTERED); + } else { + mod_chanmode_free(change); + return NULL; + } + break; #undef do_chan_mode case 'l': if (add) { @@ -1245,7 +1303,7 @@ mod_chanmode_parse(struct chanNode *channel, char **modes, unsigned int argc, un change->args[ch_arg].mode = MODE_BAN; if (!add) change->args[ch_arg].mode |= MODE_REMOVE; - change->args[ch_arg++].hostmask = modes[in_arg++]; + change->args[ch_arg++].u.hostmask = modes[in_arg++]; break; case 'o': case 'v': { @@ -1260,13 +1318,17 @@ mod_chanmode_parse(struct chanNode *channel, char **modes, unsigned int argc, un victim = GetUserH(modes[in_arg++]); if (!victim) continue; - if ((change->args[ch_arg].member = GetUserMode(channel, victim))) + if ((change->args[ch_arg].u.member = GetUserMode(channel, victim))) ch_arg++; break; } + default: + if (!(flags & MCP_FROM_SERVER)) + goto error; + break; } } - change->argc = argc; /* in case any turned out to be ignored */ + change->argc = ch_arg; /* in case any turned out to be ignored */ if (change->modes_set & MODE_SECRET) { change->modes_set &= ~(MODE_PRIVATE); change->modes_clear |= MODE_PRIVATE; @@ -1278,6 +1340,7 @@ mod_chanmode_parse(struct chanNode *channel, char **modes, unsigned int argc, un error: mod_chanmode_free(change); return NULL; + (void)base_oplevel; } struct chanmode_buffer { @@ -1337,6 +1400,7 @@ mod_chanmode_announce(struct userNode *who, struct chanNode *channel, struct mod DO_MODE_CHAR(DELAYJOINS, 'D'); DO_MODE_CHAR(REGONLY, 'R'); DO_MODE_CHAR(NOCOLORS, 'c'); + DO_MODE_CHAR(REGISTERED, 'r'); #undef DO_MODE_CHAR if (change->modes_clear & channel->modes & MODE_KEY) mod_chanmode_append(&chbuf, 'k', channel->key); @@ -1346,13 +1410,13 @@ mod_chanmode_announce(struct userNode *who, struct chanNode *channel, struct mod continue; switch (change->args[arg].mode & ~MODE_REMOVE) { case MODE_BAN: - mod_chanmode_append(&chbuf, 'b', change->args[arg].hostmask); + mod_chanmode_append(&chbuf, 'b', change->args[arg].u.hostmask); break; default: if (change->args[arg].mode & MODE_CHANOP) - mod_chanmode_append(&chbuf, 'o', change->args[arg].member->user->nick); + mod_chanmode_append(&chbuf, 'o', change->args[arg].u.member->user->nick); if (change->args[arg].mode & MODE_VOICE) - mod_chanmode_append(&chbuf, 'v', change->args[arg].member->user->nick); + mod_chanmode_append(&chbuf, 'v', change->args[arg].u.member->user->nick); break; } } @@ -1371,6 +1435,7 @@ mod_chanmode_announce(struct userNode *who, struct chanNode *channel, struct mod DO_MODE_CHAR(DELAYJOINS, 'D'); DO_MODE_CHAR(REGONLY, 'R'); DO_MODE_CHAR(NOCOLORS, 'c'); + DO_MODE_CHAR(REGISTERED, 'r'); #undef DO_MODE_CHAR if (change->modes_set & MODE_KEY) mod_chanmode_append(&chbuf, 'k', change->new_key); @@ -1385,13 +1450,13 @@ mod_chanmode_announce(struct userNode *who, struct chanNode *channel, struct mod continue; switch (change->args[arg].mode) { case MODE_BAN: - mod_chanmode_append(&chbuf, 'b', change->args[arg].hostmask); + mod_chanmode_append(&chbuf, 'b', change->args[arg].u.hostmask); break; default: if (change->args[arg].mode & MODE_CHANOP) - mod_chanmode_append(&chbuf, 'o', change->args[arg].member->user->nick); + mod_chanmode_append(&chbuf, 'o', change->args[arg].u.member->user->nick); if (change->args[arg].mode & MODE_VOICE) - mod_chanmode_append(&chbuf, 'v', change->args[arg].member->user->nick); + mod_chanmode_append(&chbuf, 'v', change->args[arg].u.member->user->nick); break; } } @@ -1409,6 +1474,9 @@ char * mod_chanmode_format(struct mod_chanmode *change, char *outbuff) { unsigned int used = 0; + unsigned int args_used = 0; + char args[MAXLEN]; + assert(change->argc <= change->alloc_argc); if (change->modes_clear) { outbuff[used++] = '-'; @@ -1422,8 +1490,9 @@ mod_chanmode_format(struct mod_chanmode *change, char *outbuff) DO_MODE_CHAR(LIMIT, 'l'); DO_MODE_CHAR(KEY, 'k'); DO_MODE_CHAR(DELAYJOINS, 'D'); - DO_MODE_CHAR(REGONLY, '$'); + DO_MODE_CHAR(REGONLY, 'R'); DO_MODE_CHAR(NOCOLORS, 'c'); + DO_MODE_CHAR(REGISTERED, 'r'); #undef DO_MODE_CHAR } if (change->modes_set) { @@ -1438,19 +1507,12 @@ mod_chanmode_format(struct mod_chanmode *change, char *outbuff) DO_MODE_CHAR(DELAYJOINS, 'D'); DO_MODE_CHAR(REGONLY, 'R'); DO_MODE_CHAR(NOCOLORS, 'c'); + DO_MODE_CHAR(REGISTERED, 'r'); + DO_MODE_CHAR(LIMIT, 'l'), args_used += sprintf(args + args_used, " %d", change->new_limit); + DO_MODE_CHAR(KEY, 'k'), args_used += sprintf(args + args_used, " %s", change->new_key); #undef DO_MODE_CHAR - switch (change->modes_set & (MODE_KEY|MODE_LIMIT)) { - case MODE_KEY|MODE_LIMIT: - used += sprintf(outbuff+used, "lk %d %s", change->new_limit, change->new_key); - break; - case MODE_KEY: - used += sprintf(outbuff+used, "k %s", change->new_key); - break; - case MODE_LIMIT: - used += sprintf(outbuff+used, "l %d", change->new_limit); - break; - } } - outbuff[used] = 0; + args[args_used] = '\0'; + strcpy(outbuff + used, args); return outbuff; }