X-Git-Url: http://git.pk910.de/?a=blobdiff_plain;f=src%2Fproto-p10.c;h=77115601b6d62d463f8a84dd68edb4213352e07c;hb=4e957e3b845963aa6cc80ebedb96f131695a34ab;hp=aacaa59979d356f373cd0d168b3a9e5a2e45b5ff;hpb=5bd4f4499d0fa3e2163fdba2ac20b487af87b158;p=srvx.git diff --git a/src/proto-p10.c b/src/proto-p10.c index aacaa59..7711560 100644 --- a/src/proto-p10.c +++ b/src/proto-p10.c @@ -1,5 +1,5 @@ /* proto-p10.c - IRC protocol output - * Copyright 2000-2004 srvx Development Team + * Copyright 2000-2006 srvx Development Team * * This file is part of srvx. * @@ -21,9 +21,9 @@ #include "proto-common.c" /* Full commands. */ -#define CMD_ACCOUNT "ACCOUNT" +#define CMD_ACCOUNT "ACCOUNT" #define CMD_ADMIN "ADMIN" -#define CMD_ASLL "ASLL" +#define CMD_ASLL "ASLL" #define CMD_AWAY "AWAY" #define CMD_BURST "BURST" #define CMD_CLEARMODE "CLEARMODE" @@ -39,7 +39,8 @@ #define CMD_EOB "END_OF_BURST" #define CMD_EOB_ACK "EOB_ACK" #define CMD_ERROR "ERROR" -#define CMD_GET "GET" +#define CMD_FAKEHOST "FAKE" +#define CMD_GET "GET" #define CMD_GLINE "GLINE" #define CMD_HASH "HASH" #define CMD_HELP "HELP" @@ -67,18 +68,18 @@ #define CMD_PONG "PONG" #define CMD_POST "POST" #define CMD_PRIVMSG "PRIVMSG" -#define CMD_PRIVS "PRIVS" +#define CMD_PRIVS "PRIVS" #define CMD_PROTO "PROTO" #define CMD_QUIT "QUIT" #define CMD_REHASH "REHASH" -#define CMD_RESET "RESET" +#define CMD_RESET "RESET" #define CMD_RESTART "RESTART" #define CMD_RPING "RPING" #define CMD_RPONG "RPONG" #define CMD_SERVER "SERVER" #define CMD_SERVLIST "SERVLIST" #define CMD_SERVSET "SERVSET" -#define CMD_SET "SET" +#define CMD_SET "SET" #define CMD_SETTIME "SETTIME" #define CMD_SILENCE "SILENCE" #define CMD_SQUERY "SQUERY" @@ -102,9 +103,9 @@ #define CMD_WHOWAS "WHOWAS" /* Tokenized commands. */ -#define TOK_ACCOUNT "AC" +#define TOK_ACCOUNT "AC" #define TOK_ADMIN "AD" -#define TOK_ASLL "LL" +#define TOK_ASLL "LL" #define TOK_AWAY "A" #define TOK_BURST "B" #define TOK_CLEARMODE "CM" @@ -120,7 +121,8 @@ #define TOK_EOB "EB" #define TOK_EOB_ACK "EA" #define TOK_ERROR "Y" -#define TOK_GET "GET" +#define TOK_FAKEHOST "FA" +#define TOK_GET "GET" #define TOK_GLINE "GL" #define TOK_HASH "HASH" #define TOK_HELP "HELP" @@ -148,18 +150,18 @@ #define TOK_PONG "Z" #define TOK_POST "POST" #define TOK_PRIVMSG "P" -#define TOK_PRIVS "PRIVS" +#define TOK_PRIVS "PRIVS" #define TOK_PROTO "PROTO" #define TOK_QUIT "Q" #define TOK_REHASH "REHASH" -#define TOK_RESET "RESET" +#define TOK_RESET "RESET" #define TOK_RESTART "RESTART" #define TOK_RPING "RI" #define TOK_RPONG "RO" #define TOK_SERVER "S" #define TOK_SERVLIST "SERVSET" #define TOK_SERVSET "SERVSET" -#define TOK_SET "SET" +#define TOK_SET "SET" #define TOK_SETTIME "SE" #define TOK_SILENCE "U" #define TOK_SQUERY "SQUERY" @@ -194,9 +196,9 @@ #define TYPE(NAME) CMD_ ## NAME #endif /* ENABLE_TOKENS */ -#define P10_ACCOUNT TYPE(ACCOUNT) +#define P10_ACCOUNT TYPE(ACCOUNT) #define P10_ADMIN TYPE(ADMIN) -#define P10_ASLL TYPE(ASLL) +#define P10_ASLL TYPE(ASLL) #define P10_AWAY TYPE(AWAY) #define P10_BURST TYPE(BURST) #define P10_CLEARMODE TYPE(CLEARMODE) @@ -212,7 +214,8 @@ #define P10_EOB TYPE(EOB) #define P10_EOB_ACK TYPE(EOB_ACK) #define P10_ERROR TYPE(ERROR) -#define P10_GET TYPE(GET) +#define P10_FAKEHOST TYPE(FAKEHOST) +#define P10_GET TYPE(GET) #define P10_GLINE TYPE(GLINE) #define P10_HASH TYPE(HASH) #define P10_HELP TYPE(HELP) @@ -240,18 +243,18 @@ #define P10_PONG TYPE(PONG) #define P10_POST TYPE(POST) #define P10_PRIVMSG TYPE(PRIVMSG) -#define P10_PRIVS TYPE(PRIVS) +#define P10_PRIVS TYPE(PRIVS) #define P10_PROTO TYPE(PROTO) #define P10_QUIT TYPE(QUIT) #define P10_REHASH TYPE(REHASH) -#define P10_RESET TYPE(RESET) +#define P10_RESET TYPE(RESET) #define P10_RESTART TYPE(RESTART) #define P10_RPING TYPE(RPING) #define P10_RPONG TYPE(RPONG) #define P10_SERVER CMD_SERVER #define P10_SERVLIST TYPE(SERVLIST) #define P10_SERVSET TYPE(SERVSET) -#define P10_SET TYPE(SET) +#define P10_SET TYPE(SET) #define P10_SETTIME TYPE(SETTIME) #define P10_SILENCE TYPE(SILENCE) #define P10_SQUERY TYPE(SQUERY) @@ -286,11 +289,14 @@ static unsigned int num_privmsg_funcs; static privmsg_func_t *notice_funcs; static unsigned int num_notice_funcs; static struct dict *unbursted_channels; +static char *his_servername; +static char *his_servercomment; -char *his_servername; -char *his_servercomment; +static struct userNode *AddUser(struct server* uplink, const char *nick, const char *ident, const char *hostname, const char *modes, const char *numeric, const char *userinfo, unsigned long timestamp, const char *realip); -static struct userNode *AddUser(struct server* uplink, const char *nick, const char *ident, const char *hostname, const char *modes, const char *numeric, const char *userinfo, time_t timestamp, const char *realip); +extern int off_channel; + +static int parse_oplevel(char *str); /* Numerics can be XYY, XYYY, or XXYYY; with X's identifying the * server and Y's indentifying the client on that server. */ @@ -362,21 +368,96 @@ irc_server(struct server *srv) inttobase64(extranum, srv->num_mask, (srv->numeric[1] || (srv->num_mask >= 64*64)) ? 3 : 2); if (srv == self) { /* The +s, ignored by Run's ircu, means "service" to Undernet's ircu */ - putsock(P10_SERVER " %s %d %li %li J10 %s%s +s :%s", + putsock(P10_SERVER " %s %d %lu %lu J10 %s%s +s6 :%s", srv->name, srv->hops+1, srv->boot, srv->link, srv->numeric, extranum, srv->description); } else { - putsock("%s " P10_SERVER " %s %d %li %li %c10 %s%s +s :%s", + putsock("%s " P10_SERVER " %s %d %lu %lu %c10 %s%s +s6 :%s", self->numeric, srv->name, srv->hops+1, srv->boot, srv->link, (srv->self_burst ? 'J' : 'P'), srv->numeric, extranum, srv->description); } } +static void +irc_p10_pton(irc_in_addr_t *ip, const char *input) +{ + if (strlen(input) == 6) { + unsigned int value; + memset(ip, 0, 6 * sizeof(ip->in6[0])); + value = base64toint(input, 6); + if (value) + ip->in6[5] = htons(65535); + ip->in6[6] = htons(value >> 16); + ip->in6[7] = htons(value & 65535); + } else { + unsigned int pos = 0; + do { + if (*input == '_') { + unsigned int left; + for (left = (25 - strlen(input)) / 3; left; left--) + ip->in6[pos++] = 0; + input++; + } else { + ip->in6[pos++] = ntohs(base64toint(input, 3)); + input += 3; + } + } while (pos < 8); + } +} + +static void +irc_p10_ntop(char *output, const irc_in_addr_t *ip) +{ + if (!irc_in_addr_is_valid(*ip)) { + strcpy(output, "AAAAAA"); + } else if (irc_in_addr_is_ipv4(*ip)) { + unsigned int in4; + in4 = (ntohs(ip->in6[6]) << 16) | ntohs(ip->in6[7]); + inttobase64(output, in4, 6); + output[6] = '\0'; + } else if (irc_in_addr_is_ipv6(*ip)) { + unsigned int max_start, max_zeros, curr_zeros, zero, ii; + /* Can start by printing out the leading non-zero parts. */ + for (ii = 0; (ip->in6[ii]) && (ii < 8); ++ii) { + inttobase64(output, ntohs(ip->in6[ii]), 3); + output += 3; + } + /* Find the longest run of zeros. */ + for (max_start = zero = ii, max_zeros = curr_zeros = 0; ii < 8; ++ii) { + if (!ip->in6[ii]) + curr_zeros++; + else if (curr_zeros > max_zeros) { + max_start = ii - curr_zeros; + max_zeros = curr_zeros; + curr_zeros = 0; + } + } + if (curr_zeros > max_zeros) { + max_start = ii - curr_zeros; + max_zeros = curr_zeros; + curr_zeros = 0; + } + /* Print the rest of the address */ + for (ii = zero; ii < 8; ) { + if ((ii == max_start) && max_zeros) { + *output++ = '_'; + ii += max_zeros; + } else { + inttobase64(output, ntohs(ip->in6[ii]), 3); + output += 3; + } + } + *output = '\0'; + } else { + strcpy(output, "???"); + } +} + void irc_user(struct userNode *user) { - char b64ip[7]; - if (!user) + char b64ip[25]; + if (!user || IsDummy(user)) return; - inttobase64(b64ip, ntohl(user->ip.s_addr), 6); + irc_p10_ntop(b64ip, &user->ip); if (user->modes) { int modelen; char modes[32]; @@ -390,24 +471,24 @@ irc_user(struct userNode *user) modes[modelen++] = 'w'; if (IsService(user)) modes[modelen++] = 'k'; - if (IsServNotice(user)) - modes[modelen++] = 's'; if (IsDeaf(user)) modes[modelen++] = 'd'; if (IsGlobal(user)) modes[modelen++] = 'g'; - if (IsHelperIrcu(user)) - modes[modelen++] = 'h'; + if (IsNoChan(user)) + modes[modelen++] = 'n'; if (IsHiddenHost(user)) modes[modelen++] = 'x'; + if (IsNoIdle(user)) + modes[modelen++] = 'I'; modes[modelen] = 0; /* we don't need to put the + in modes because it's in the format string. */ - putsock("%s " P10_NICK " %s %d %li %s %s +%s %s %s :%s", - user->uplink->numeric, user->nick, user->uplink->hops+1, user->timestamp, user->ident, user->hostname, modes, b64ip, user->numeric, user->info); + putsock("%s " P10_NICK " %s %d %lu %s %s +%s %s %s :%s", + user->uplink->numeric, user->nick, user->uplink->hops+1, (unsigned long)user->timestamp, user->ident, user->hostname, modes, b64ip, user->numeric, user->info); } else { - putsock("%s " P10_NICK " %s %d %li %s %s %s %s :%s", - user->uplink->numeric, user->nick, user->uplink->hops+1, user->timestamp, user->ident, user->hostname, b64ip, user->numeric, user->info); + putsock("%s " P10_NICK " %s %d %lu %s %s %s %s :%s", + user->uplink->numeric, user->nick, user->uplink->hops+1, (unsigned long)user->timestamp, user->ident, user->hostname, b64ip, user->numeric, user->info); } } @@ -417,6 +498,12 @@ irc_account(struct userNode *user, const char *stamp) putsock("%s " P10_ACCOUNT " %s %s", self->numeric, user->numeric, stamp); } +void +irc_fakehost(struct userNode *user, const char *host) +{ + putsock("%s " P10_FAKEHOST " %s %s", self->numeric, user->numeric, host); +} + void irc_regnick(UNUSED_ARG(struct userNode *user)) { @@ -426,7 +513,7 @@ irc_regnick(UNUSED_ARG(struct userNode *user)) void irc_nick(struct userNode *user, UNUSED_ARG(const char *old_nick)) { - putsock("%s " P10_NICK " %s "FMT_TIME_T, user->numeric, user->nick, now); + putsock("%s " P10_NICK " %s %lu", user->numeric, user->nick, (unsigned long)now); } void @@ -471,22 +558,48 @@ irc_wallchops(struct userNode *from, const char *to, const char *message) putsock("%s " P10_WALLCHOPS " %s :%s", from->numeric, to, message); } +static int +deliver_to_dummy(struct userNode *source, struct userNode *dest, const char *message, int type) +{ + unsigned int num; + + if (!dest || !IsDummy(dest) || !IsLocal(dest)) + return 0; + num = dest->num_local; + switch (type) { + default: + if ((num < num_notice_funcs) && notice_funcs[num]) + notice_funcs[num](source, dest, message, 0); + break; + case 1: + if ((num < num_privmsg_funcs) && privmsg_funcs[num]) + privmsg_funcs[num](source, dest, message, 0); + break; + } + return 1; +} + void irc_notice(struct userNode *from, const char *to, const char *message) { - putsock("%s " P10_NOTICE " %s :%s", from->numeric, to, message); + if (to[0] == '#' || to[0] == '$' + || !deliver_to_dummy(from, GetUserN(to), message, 0)) + putsock("%s " P10_NOTICE " %s :%s", from->numeric, to, message); } void irc_notice_user(struct userNode *from, struct userNode *to, const char *message) { - putsock("%s " P10_NOTICE " %s :%s", from->numeric, to->numeric, message); + if (!deliver_to_dummy(from, to, message, 0)) + putsock("%s " P10_NOTICE " %s :%s", from->numeric, to->numeric, message); } void irc_privmsg(struct userNode *from, const char *to, const char *message) { - putsock("%s " P10_PRIVMSG " %s :%s", from->numeric, to, message); + if (to[0] == '#' || to[0] == '$' + || !deliver_to_dummy(from, GetUserN(to), message, 1)) + putsock("%s " P10_PRIVMSG " %s :%s", from->numeric, to, message); } void @@ -513,6 +626,21 @@ irc_pong(const char *who, const char *data) putsock("%s " P10_PONG " %s :%s", self->numeric, who, data); } +void +irc_pong_asll(const char *who, const char *orig_ts) +{ + char *delim; + struct timeval orig; + struct timeval now; + int diff; + + orig.tv_sec = strtoul(orig_ts, &delim, 10); + orig.tv_usec = (*delim == '.') ? strtoul(delim + 1, NULL, 10) : 0; + gettimeofday(&now, NULL); + diff = (now.tv_sec - orig.tv_sec) * 1000 + (now.tv_usec - orig.tv_usec) / 1000; + putsock("%s " P10_PONG " %s %s %d %lu.%06lu", self->numeric, who, orig_ts, diff, (unsigned long)now.tv_sec, (unsigned long)now.tv_usec); +} + void irc_pass(const char *passwd) { @@ -534,17 +662,21 @@ irc_introduce(const char *passwd) void irc_gline(struct server *srv, struct gline *gline) { - putsock("%s " P10_GLINE " %s +%s %ld :%s", - self->numeric, (srv ? srv->numeric : "*"), gline->target, gline->expires-now, gline->reason); + if (gline->lastmod) + putsock("%s " P10_GLINE " %s +%s %lu %lu :%s", + self->numeric, (srv ? srv->numeric : "*"), gline->target, (unsigned long)(gline->expires-now), (unsigned long)gline->lastmod, gline->reason); + else + putsock("%s " P10_GLINE " %s +%s %lu :%s", + self->numeric, (srv ? srv->numeric : "*"), gline->target, (unsigned long)(gline->expires-now), gline->reason); } void -irc_settime(const char *srv_name_mask, time_t new_time) +irc_settime(const char *srv_name_mask, unsigned long new_time) { ioset_set_time(new_time); if (!strcmp(srv_name_mask, "*")) srv_name_mask = ""; - putsock("%s " P10_SETTIME " " FMT_TIME_T " %s", self->numeric, new_time, srv_name_mask); + putsock("%s " P10_SETTIME " %lu %s", self->numeric, new_time, srv_name_mask); } void @@ -561,13 +693,15 @@ irc_burst(struct chanNode *chan) struct modeNode *mn; struct banNode *bn; long last_mode=-1; + unsigned int first_ban; unsigned int n; - base_len = sprintf(burst_line, "%s " P10_BURST " %s " FMT_TIME_T " ", - self->numeric, chan->name, chan->timestamp); + base_len = sprintf(burst_line, "%s " P10_BURST " %s %lu ", + self->numeric, chan->name, + (unsigned long)chan->timestamp); len = irc_make_chanmode(chan, burst_line+base_len); pos = base_len + len; - if (len) + if (len > 0 && chan->members.used > 0) burst_line[pos++] = ' '; /* dump the users */ @@ -592,32 +726,36 @@ irc_burst(struct chanNode *chan) if ((n+1)members.used) burst_line[pos++] = ','; } + + /* dump the bans */ if (chan->banlist.used) { - /* dump the bans */ - if (pos+2+strlen(chan->banlist.list[0]->ban) > 505) { - burst_line[pos-1] = 0; - putsock("%s", burst_line); - pos = base_len; - } else { + first_ban = 1; + + if (chan->members.used > 0) burst_line[pos++] = ' '; - } - burst_line[pos++] = ':'; - burst_line[pos++] = '%'; - base_len = pos; - for (n=0; nbanlist.used; n++) { + for (n=0; nbanlist.used; ) { + if (first_ban && (pos < 500)) { + burst_line[pos++] = ':'; + burst_line[pos++] = '%'; + } bn = chan->banlist.list[n]; len = strlen(bn->ban); - if (pos+len+1 > 510) { - burst_line[pos-1] = 0; /* -1 to back up over the space or comma */ + if (pos + 2 + len < 505) { + memcpy(burst_line + pos, bn->ban, len); + pos += len; + burst_line[pos++] = ' '; + first_ban = 0; + n++; + } else { + burst_line[pos-1] = 0; putsock("%s", burst_line); pos = base_len; + first_ban = 1; } - memcpy(burst_line+pos, bn->ban, len); - pos += len; - burst_line[pos++] = ' '; } } + /* print the last line */ burst_line[pos] = 0; putsock("%s", burst_line); @@ -654,9 +792,9 @@ irc_kill(struct userNode *from, struct userNode *target, const char *message) void irc_mode(struct userNode *from, struct chanNode *target, const char *modes) { - putsock("%s " P10_MODE " %s %s "FMT_TIME_T, + putsock("%s " P10_MODE " %s %s %lu", (from ? from->numeric : self->numeric), - target->name, modes, target->timestamp); + target->name, modes, (unsigned long)target->timestamp); } void @@ -670,9 +808,10 @@ irc_join(struct userNode *who, struct chanNode *what) { if (what->members.used == 1) { putsock("%s " P10_CREATE " %s %lu", - who->numeric, what->name, what->timestamp); + who->numeric, what->name, (unsigned long)what->timestamp); } else { - putsock("%s " P10_JOIN " %s %lu", who->numeric, what->name, what->timestamp); + putsock("%s " P10_JOIN " %s %lu", who->numeric, what->name, + (unsigned long)what->timestamp); } } @@ -681,7 +820,7 @@ irc_kick(struct userNode *who, struct userNode *target, struct chanNode *channel { const char *numeric; struct modeNode *mn = GetUserMode(channel, who); - numeric = ((!mn && off_channel) || (mn->modes & MODE_CHANOP)) ? who->numeric : self->numeric; + numeric = ((mn && (mn->modes & MODE_CHANOP)) || off_channel) ? who->numeric : self->numeric; putsock("%s " P10_KICK " %s %s :%s", numeric, channel->name, target->numeric, msg); } @@ -695,7 +834,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 " P10_SVSNICK " %s %s "FMT_TIME_T, from->uplink->numeric, target->numeric, newnick, now); + putsock("%s " P10_SVSNICK " %s %s %lu", from->uplink->numeric, target->numeric, newnick, (unsigned long)now); } void @@ -751,6 +890,90 @@ change_nicklen(int new_nicklen) } } +static CMD_FUNC(cmd_whois) +{ + struct userNode *from; + struct userNode *who; + char buf[MAXLEN]; + unsigned int i, mlen, len; + + 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 (IsFakeHost(who) && IsHiddenHost(who)) + irc_numeric(from, RPL_WHOISUSER, "%s %s %s * :%s", who->nick, who->ident, who->fakehost, who->info); + else if (IsHiddenHost(who) && who->handle_info && hidden_host_suffix) + irc_numeric(from, RPL_WHOISUSER, "%s %s %s.%s * :%s", who->nick, who->ident, who->handle_info->handle, hidden_host_suffix, who->info); + else + irc_numeric(from, RPL_WHOISUSER, "%s %s %s * :%s", who->nick, who->ident, who->hostname, who->info); + + if ((!IsService(who) && !IsNoChan(who)) || (from == who)) { + struct modeNode *mn; + mlen = strlen(self->name) + strlen(from->nick) + 12 + strlen(who->nick); + len = 0; + *buf = '\0'; + for (i = who->channels.used; i > 0; ) + { + mn = who->channels.list[--i]; + + if (!IsOper(from) && (mn->channel->modes & (MODE_PRIVATE | MODE_SECRET)) && !GetUserMode(mn->channel, from)) + continue; + + if (len + strlen(mn->channel->name) + mlen > MAXLEN - 5) + { + irc_numeric(from, RPL_WHOISCHANNELS, "%s :%s", who->nick, buf); + *buf = '\0'; + len = 0; + } + + if (IsDeaf(who)) + *(buf + len++) = '-'; + if ((mn->channel->modes & (MODE_PRIVATE | MODE_SECRET)) && !GetUserMode(mn->channel, from)) + *(buf + len++) = '*'; + if (mn->modes & MODE_CHANOP) + *(buf + len++) = '@'; + else if (mn->modes & MODE_VOICE) + *(buf + len++) = '+'; + + if (len) + *(buf + len) = '\0'; + strcpy(buf + len, mn->channel->name); + len += strlen(mn->channel->name); + strcat(buf + len, " "); + len++; + } + if (buf[0] != '\0') + irc_numeric(from, RPL_WHOISCHANNELS, "%s :%s", who->nick, buf); + } + + if (his_servername && his_servercomment && !IsOper(from) && from != who) + irc_numeric(from, RPL_WHOISSERVER, "%s %s :%s", who->nick, his_servername, his_servercomment); + else + irc_numeric(from, RPL_WHOISSERVER, "%s %s :%s", who->nick, who->uplink->name, who->uplink->description); + + if (IsAway(who)) + irc_numeric(from, RPL_AWAY, "%s :Away", who->nick); + if (IsOper(who)) + irc_numeric(from, RPL_WHOISOPERATOR, "%s :%s", who->nick, IsLocal(who) ? "is a megalomaniacal power hungry tyrant" : "is an IRC Operator"); + if (who->handle_info) + irc_numeric(from, RPL_WHOISACCOUNT, "%s %s :is logged in as", who->nick, who->handle_info->handle); + if (IsHiddenHost(who) && who->handle_info && (IsOper(from) || from == who)) + irc_numeric(from, RPL_WHOISACTUALLY, "%s %s@%s %s :Actual user@host, Actual IP", who->nick, who->ident, who->hostname, irc_ntoa(&who->ip)); + if (IsLocal(who) && !IsService(who) && (!IsNoIdle(who) || IsOper(from) || from == who)) + irc_numeric(from, RPL_WHOISIDLE, "%s %ld %ld :seconds idle, signon time", who->nick, now - who->idle_since, who->timestamp); + + irc_numeric(from, RPL_ENDOFWHOIS, "%s :End of /WHOIS list", who->nick); + return 1; +} + static CMD_FUNC(cmd_server) { struct server *srv; @@ -758,7 +981,7 @@ static CMD_FUNC(cmd_server) if (argc < 8) return 0; - if (origin) { + if (self->uplink) { /* another server introduced us */ srv = AddServer(GetServerH(origin), argv[1], atoi(argv[2]), atoi(argv[3]), atoi(argv[4]), argv[6], argv[argc-1]); if (!srv) @@ -782,7 +1005,7 @@ static CMD_FUNC(cmd_server) if (srv->boot <= PREHISTORY) { /* Server from the mists of time.. */ if (srv->hops == 1) { - log_module(MAIN_LOG, LOG_ERROR, "Server %s claims to have booted at time "FMT_TIME_T". This is absurd.", srv->name, srv->boot); + log_module(MAIN_LOG, LOG_ERROR, "Server %s claims to have booted at time %lu. This is absurd.", srv->name, (unsigned long)srv->boot); } } else if ((str = conf_get_data("server/reliable_clock", RECDB_QSTRING)) && enabled_string(str)) { @@ -797,7 +1020,7 @@ static CMD_FUNC(cmd_server) } } if (srv == self->uplink) { - extern time_t burst_begin; + extern unsigned long burst_begin; burst_begin = now; } return 1; @@ -829,7 +1052,7 @@ static CMD_FUNC(cmd_eob) static CMD_FUNC(cmd_eob_ack) { - extern time_t burst_begin; + extern unsigned long burst_begin; if (GetServerH(origin) == self->uplink) { burst_length = now - burst_begin; @@ -844,8 +1067,8 @@ static CMD_FUNC(cmd_ping) struct server *srv; struct userNode *un; - if(argc > 3) - irc_pong(argv[2], argv[1]); + if (argc > 3) + irc_pong_asll(argv[2], argv[3]); else if ((srv = GetServerH(origin))) irc_pong(self->name, srv->numeric); else if ((un = GetUserH(origin))) @@ -870,7 +1093,7 @@ static CMD_FUNC(cmd_error_nick) struct create_desc { struct userNode *user; - time_t when; + unsigned long when; }; static void @@ -973,6 +1196,18 @@ static CMD_FUNC(cmd_account) return 1; } +static CMD_FUNC(cmd_fakehost) +{ + struct userNode *user; + + if ((argc < 3) || !origin || !GetServerH(origin)) + return 0; + if (!(user = GetUserN(argv[1]))) + return 1; + assign_fakehost(user, argv[2], 0); + return 1; +} + static CMD_FUNC(cmd_burst) { extern int rel_age; @@ -982,8 +1217,9 @@ static CMD_FUNC(cmd_burst) struct userNode *un; struct modeNode *mNode; long mode; + int oplevel = -1; char *user, *end, sep; - time_t in_timestamp; + unsigned long in_timestamp; if (argc < 3) return 0; @@ -994,8 +1230,11 @@ static CMD_FUNC(cmd_burst) const char *pos; int n_modes; for (pos=argv[next], n_modes = 1; *pos; pos++) - if ((*pos == 'k') || (*pos == 'l')) + if ((*pos == 'k') || (*pos == 'l') || (*pos == 'A') + || (*pos == 'U')) n_modes++; + if (next + n_modes > argc) + n_modes = argc - next; unsplit_string(argv+next, n_modes, modes); next += n_modes; break; @@ -1020,11 +1259,20 @@ static CMD_FUNC(cmd_burst) if (sep == ':') { mode = 0; while ((sep = *end++)) { - if (sep == 'o') + if (sep == 'o') { mode |= MODE_CHANOP; - else if (sep == 'v') + oplevel = -1; + } else if (sep == 'v') { mode |= MODE_VOICE; - else + oplevel = -1; + } else if (isdigit(sep)) { + mode |= MODE_CHANOP; + if (oplevel >= 0) + oplevel += parse_oplevel(end); + else + oplevel = parse_oplevel(end); + while (isdigit(*end)) end++; + } else break; } if (rel_age < 0) @@ -1034,8 +1282,10 @@ static CMD_FUNC(cmd_burst) res = 0; continue; } - if ((mNode = AddChannelUser(un, cNode))) + if ((mNode = AddChannelUser(un, cNode))) { mNode->modes = mode; + mNode->oplevel = oplevel; + } } return res; @@ -1128,7 +1378,7 @@ static CMD_FUNC(cmd_clearmode) static CMD_FUNC(cmd_topic) { struct chanNode *cn; - time_t chan_ts, topic_ts; + unsigned long chan_ts, topic_ts; if (argc < 3) return 0; @@ -1151,7 +1401,7 @@ static CMD_FUNC(cmd_topic) static CMD_FUNC(cmd_num_topic) { - static struct chanNode *cn; + struct chanNode *cn; if (!argv[0]) return 0; /* huh? */ @@ -1187,9 +1437,11 @@ static CMD_FUNC(cmd_num_topic) static CMD_FUNC(cmd_num_gline) { + unsigned long lastmod; if (argc < 6) return 0; - gline_add(origin, argv[3], atoi(argv[4])-now, argv[5], now, 0); + lastmod = (argc > 5) ? strtoul(argv[5], NULL, 0) : 0; + gline_add(origin, argv[3], atoi(argv[4])-now, argv[argc - 1], now, lastmod, 0); return 1; } @@ -1231,19 +1483,6 @@ static CMD_FUNC(cmd_kill) return 1; } -static CMD_FUNC(cmd_part) -{ - struct userNode *user; - - if (argc < 2) - return 0; - user = GetUserH(origin); - if (!user) - return 0; - parse_foreach(argv[1], part_helper, NULL, NULL, NULL, user); - return 1; -} - static CMD_FUNC(cmd_kick) { if (argc < 3) @@ -1276,13 +1515,13 @@ static CMD_FUNC(cmd_squit) static CMD_FUNC(cmd_privmsg) { struct privmsg_desc pd; - if (argc != 3) + if (argc < 3) return 0; pd.user = GetUserH(origin); if (!pd.user || (IsGagged(pd.user) && !IsOper(pd.user))) return 1; pd.is_notice = 0; - pd.text = argv[2]; + pd.text = argv[argc - 1]; parse_foreach(argv[1], privmsg_chan_helper, NULL, privmsg_user_helper, privmsg_invalid, &pd); return 1; } @@ -1290,13 +1529,13 @@ static CMD_FUNC(cmd_privmsg) static CMD_FUNC(cmd_notice) { struct privmsg_desc pd; - if (argc != 3) + if (argc < 3) return 0; pd.user = GetUserH(origin); if (!pd.user || (IsGagged(pd.user) && !IsOper(pd.user))) return 1; pd.is_notice = 1; - pd.text = argv[2]; + pd.text = argv[argc - 1]; parse_foreach(argv[1], privmsg_chan_helper, NULL, privmsg_user_helper, privmsg_invalid, &pd); return 1; } @@ -1317,12 +1556,15 @@ static CMD_FUNC(cmd_away) static CMD_FUNC(cmd_gline) { + unsigned long lastmod; + if (argc < 3) return 0; if (argv[2][0] == '+') { if (argc < 5) return 0; - gline_add(origin, argv[2]+1, strtoul(argv[3], NULL, 0), argv[argc-1], now, 0); + lastmod = (argc > 5) ? strtoul(argv[5], NULL, 0) : 0; + gline_add(origin, argv[2]+1, strtoul(argv[3], NULL, 0), argv[argc-1], now, lastmod, 0); return 1; } else if (argv[2][0] == '-') { gline_remove(argv[2]+1, 0); @@ -1359,12 +1601,16 @@ parse_cleanup(void) unsigned int nn; free(of_list); free(privmsg_funcs); + num_privmsg_funcs = 0; free(notice_funcs); + num_notice_funcs = 0; free(mcf_list); dict_delete(irc_func_dict); for (nn=0; nnname; make_numeric(self, local_num, numeric); - return AddUser(self, nick, nick, hostname, "+oik", numeric, desc, now, "AAAAAA"); + return AddUser(self, nick, ident, hostname, modes, numeric, desc, now, "AAAAAA"); } struct userNode * @@ -1773,7 +2026,7 @@ AddClone(const char *nick, const char *ident, const char *hostname, const char * { char numeric[COMBO_NUMERIC_LEN+1]; int local_num = get_local_numeric(); - time_t timestamp = now; + unsigned long timestamp = now; struct userNode *old_user = GetUserH(nick); if (old_user) { @@ -1791,12 +2044,13 @@ AddClone(const char *nick, const char *ident, const char *hostname, const char * int is_valid_nick(const char *nick) { + unsigned int ii; /* IRC has some of The Most Fucked-Up ideas about character sets * in the world.. */ if (!isalpha(*nick) && !strchr("{|}~[\\]^_`", *nick)) return 0; - for (++nick; *nick; ++nick) - if (!isalnum(*nick) && !strchr("{|}~[\\]^-_`", *nick)) + for (ii = 0; nick[ii]; ++ii) + if (!isalnum(nick[ii]) && !strchr("{|}~[\\]^-_`", nick[ii])) return 0; if (strlen(nick) > nicklen) return 0; @@ -1804,40 +2058,49 @@ is_valid_nick(const char *nick) { } static struct userNode* -AddUser(struct server* uplink, const char *nick, const char *ident, const char *hostname, const char *modes, const char *numeric, const char *userinfo, time_t timestamp, const char *realip) +AddUser(struct server* uplink, const char *nick, const char *ident, const char *hostname, const char *modes, const char *numeric, const char *userinfo, unsigned long timestamp, const char *realip) { struct userNode *oldUser, *uNode; - unsigned int n, ignore_user; + unsigned int n, ignore_user, dummy; if ((strlen(numeric) < 3) || (strlen(numeric) > 5)) { - log_module(MAIN_LOG, LOG_WARNING, "AddUser(%p, %s, ...): numeric %s wrong length!", uplink, nick, numeric); + log_module(MAIN_LOG, LOG_WARNING, "AddUser(%p, %s, ...): numeric %s wrong length!", (void*)uplink, nick, numeric); return NULL; } if (!uplink) { - log_module(MAIN_LOG, LOG_WARNING, "AddUser(%p, %s, ...): server for numeric %s doesn't exist!", uplink, nick, numeric); + log_module(MAIN_LOG, LOG_WARNING, "AddUser(%p, %s, ...): server for numeric %s doesn't exist!", (void*)uplink, nick, numeric); return NULL; } if (uplink != GetServerN(numeric)) { - log_module(MAIN_LOG, LOG_WARNING, "AddUser(%p, %s, ...): server for numeric %s differs from nominal uplink %s.", uplink, nick, numeric, uplink->name); + log_module(MAIN_LOG, LOG_WARNING, "AddUser(%p, %s, ...): server for numeric %s differs from nominal uplink %s.", (void*)uplink, nick, numeric, uplink->name); return NULL; } - if (!is_valid_nick(nick)) { - log_module(MAIN_LOG, LOG_WARNING, "AddUser(%p, %s, ...): invalid nickname detected.", uplink, 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.", (void*)uplink, nick); return NULL; } ignore_user = 0; if ((oldUser = GetUserH(nick))) { - if (IsLocal(oldUser) && (IsService(oldUser) || IsPersistent(oldUser))) { - /* The service should collide the new user off. */ + if (IsLocal(oldUser) + && (IsService(oldUser) || IsPersistent(oldUser)) + && !uplink->burst) { + /* The service should collide the new user off - but not + * if the new user is coming in during a burst. (During a + * burst, the bursting server will kill either our user -- + * triggering a ReintroduceUser() -- or its own.) + */ oldUser->timestamp = timestamp - 1; irc_user(oldUser); } if (oldUser->timestamp > timestamp) { - /* "Old" user is really newer; remove them */ + /* "Old" user is really newer; remove them. */ DelUser(oldUser, 0, 1, "Overruled by older nick"); } else { /* User being added is too new; do not add them to @@ -1854,8 +2117,9 @@ AddUser(struct server* uplink, const char *nick, const char *ident, const char * safestrncpy(uNode->info, userinfo, sizeof(uNode->info)); safestrncpy(uNode->hostname, hostname, sizeof(uNode->hostname)); safestrncpy(uNode->numeric, numeric, sizeof(uNode->numeric)); - uNode->ip.s_addr = htonl(base64toint(realip, 6)); + irc_p10_pton(&uNode->ip, realip); uNode->timestamp = timestamp; + uNode->idle_since = timestamp; modeList_init(&uNode->channels); uNode->uplink = uplink; if (++uNode->uplink->clients > uNode->uplink->max_clients) { @@ -1864,6 +2128,8 @@ AddUser(struct server* uplink, const char *nick, const char *ident, const char * uNode->num_local = base64toint(numeric+strlen(uNode->uplink->numeric), 3) & uNode->uplink->num_mask; uNode->uplink->users[uNode->num_local] = uNode; mod_usermode(uNode, modes); + if (dummy) + uNode->modes |= FLAGS_DUMMY; if (ignore_user) return uNode; @@ -1886,12 +2152,14 @@ DelUser(struct userNode* user, struct userNode *killer, int announce, const char { unsigned int n; + verify(user); + /* mark them as dead, in case anybody cares */ user->dead = 1; /* remove user from all channels */ while (user->channels.used > 0) - DelChannelUser(user, user->channels.list[user->channels.used-1]->channel, false, 0); + DelChannelUser(user, user->channels.list[user->channels.used-1]->channel, NULL, false); /* Call these in reverse order so ChanServ can update presence information before NickServ nukes the handle_info. */ @@ -1916,6 +2184,14 @@ DelUser(struct userNode* user, struct userNode *killer, int announce, const char irc_kill(killer, user, why); } + if (IsLocal(user)) { + unsigned int num = user->num_local; + if (num < num_privmsg_funcs) + privmsg_funcs[num] = NULL; + if (num < num_notice_funcs) + notice_funcs[num] = NULL; + } + modeList_clean(&user->channels); /* We don't free them, in case we try to privmsg them or something * (like when a stupid oper kills themself). We just put them onto @@ -1927,43 +2203,43 @@ DelUser(struct userNode* user, struct userNode *killer, int announce, const char free_user(user); } +static void call_oper_funcs(struct userNode *user); + void mod_usermode(struct userNode *user, const char *mode_change) { - static void call_oper_funcs(struct userNode *user); int add = 1; const char *word = mode_change; if (!user || !mode_change) return; - while (*word != ' ' && *word) word++;\ - while (*word == ' ') word++; \ + while (*word != ' ' && *word) word++; + while (*word == ' ') word++; while (1) { #define do_user_mode(FLAG) do { if (add) user->modes |= FLAG; else user->modes &= ~FLAG; } while (0) - switch (*mode_change++) { - case 0: case ' ': 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) + switch (*mode_change++) { + case 0: case ' ': return; + case '+': add = 1; break; + case '-': add = 0; break; + case 'o': + do_user_mode(FLAGS_OPER); + if (!add) { + userList_remove(&curr_opers, user); + } else if (!userList_contains(&curr_opers, user)) { + userList_append(&curr_opers, user); + call_oper_funcs(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 's': do_user_mode(FLAGS_SERVNOTICE); break; - case 'd': do_user_mode(FLAGS_DEAF); 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; + break; + case 'w': do_user_mode(FLAGS_WALLOP); break; + case 'd': do_user_mode(FLAGS_DEAF); break; + case 'k': do_user_mode(FLAGS_SERVICE); break; + case 'g': do_user_mode(FLAGS_GLOBAL); break; + case 'n': do_user_mode(FLAGS_NOCHAN); break; + case 'I': do_user_mode(FLAGS_NOIDLE); break; case 'x': do_user_mode(FLAGS_HIDDEN_HOST); break; case 'r': if (*word) { @@ -1977,13 +2253,25 @@ void mod_usermode(struct userNode *user, const char *mode_change) { call_account_func(user, tag); } break; - } + case 'f': + if (*word) { + char host[MAXLEN]; + unsigned int ii; + for (ii=0; (*word != ' ') && (*word != '\0'); ) + host[ii++] = *word++; + host[ii] = 0; + while (*word == ' ') + word++; + assign_fakehost(user, host, 0); + } + 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, char **modes, unsigned int argc, unsigned int flags, short base_oplevel) { struct mod_chanmode *change; unsigned int ii, in_arg, ch_arg, add; @@ -2014,6 +2302,14 @@ mod_chanmode_parse(struct chanNode *channel, char **modes, unsigned int argc, un case 'r': do_chan_mode(MODE_REGONLY); break; case 's': do_chan_mode(MODE_SECRET); break; case 't': do_chan_mode(MODE_TOPICLIMIT); break; + case 'z': + 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) { @@ -2041,6 +2337,37 @@ mod_chanmode_parse(struct chanNode *channel, char **modes, unsigned int argc, un } } break; + case 'U': + if (add) + { + if (in_arg >= argc) + goto error; + change->modes_set |= MODE_UPASS; + safestrncpy(change->new_upass, modes[in_arg++], sizeof(change->new_upass)); + } else { + change->modes_clear |= MODE_UPASS; + if (!(flags & MCP_UPASS_FREE)) { + if (in_arg >= argc) + goto error; + in_arg++; + } + } + break; + case 'A': + if (add) { + if (in_arg >= argc) + goto error; + change->modes_set |= MODE_APASS; + safestrncpy(change->new_apass, modes[in_arg++], sizeof(change->new_apass)); + } else { + change->modes_clear |= MODE_APASS; + if (!(flags & MCP_APASS_FREE)) { + if (in_arg >= argc) + goto error; + in_arg++; + } + } + break; case 'b': if (!(flags & MCP_ALLOW_OVB)) goto error; @@ -2049,11 +2376,34 @@ 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': { struct userNode *victim; + char *oplevel_str; + int oplevel; + + if (in_arg >= argc) + goto error; + oplevel_str = strchr(modes[in_arg], ':'); + if (oplevel_str) + { + /* XXYYY M #channel +o XXYYY: */ + *oplevel_str++ = '\0'; + oplevel = parse_oplevel(oplevel_str); + if (oplevel <= base_oplevel && !(flags & MCP_FROM_SERVER)) + oplevel = base_oplevel + 1; + } + else if (channel->modes & MODE_UPASS) + oplevel = base_oplevel + 1; + else + oplevel = -1; + + /* Check that oplevel is within bounds. */ + if (oplevel > MAXOPLEVEL) + oplevel = MAXOPLEVEL; + if (!(flags & MCP_ALLOW_OVB)) goto error; if (in_arg >= argc) @@ -2067,8 +2417,12 @@ 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))) + { + /* Apply the oplevel change */ + change->args[ch_arg].u.member->oplevel = oplevel; ch_arg++; + } break; } default: @@ -2137,7 +2491,7 @@ mod_chanmode_announce(struct userNode *who, struct chanNode *channel, struct mod chbuf.chname_len = strlen(channel->name); mn = GetUserMode(channel, who); - if ((!mn && off_channel) || (mn->modes & MODE_CHANOP)) + if ((mn && (mn->modes & MODE_CHANOP)) || off_channel) chbuf.is_chanop = 1; /* First remove modes */ @@ -2157,9 +2511,14 @@ mod_chanmode_announce(struct userNode *who, struct chanNode *channel, struct mod DO_MODE_CHAR(REGONLY, 'r'); DO_MODE_CHAR(NOCOLORS, 'c'); DO_MODE_CHAR(NOCTCPS, 'C'); + DO_MODE_CHAR(REGISTERED, 'z'); #undef DO_MODE_CHAR if (change->modes_clear & channel->modes & MODE_KEY) mod_chanmode_append(&chbuf, 'k', channel->key); + if (change->modes_clear & channel->modes & MODE_UPASS) + mod_chanmode_append(&chbuf, 'U', channel->upass); + if (change->modes_clear & channel->modes & MODE_APASS) + mod_chanmode_append(&chbuf, 'A', channel->apass); } for (arg = 0; arg < change->argc; ++arg) { if (!(change->args[arg].mode & MODE_REMOVE)) @@ -2168,13 +2527,13 @@ mod_chanmode_announce(struct userNode *who, struct chanNode *channel, struct mod chbuf.modes[chbuf.modes_used++] = mode = '-'; 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->numeric); + mod_chanmode_append(&chbuf, 'o', change->args[arg].u.member->user->numeric); if (change->args[arg].mode & MODE_VOICE) - mod_chanmode_append(&chbuf, 'v', change->args[arg].member->user->numeric); + mod_chanmode_append(&chbuf, 'v', change->args[arg].u.member->user->numeric); break; } } @@ -2195,9 +2554,14 @@ mod_chanmode_announce(struct userNode *who, struct chanNode *channel, struct mod DO_MODE_CHAR(REGONLY, 'r'); DO_MODE_CHAR(NOCOLORS, 'c'); DO_MODE_CHAR(NOCTCPS, 'C'); + DO_MODE_CHAR(REGISTERED, 'z'); #undef DO_MODE_CHAR if(change->modes_set & MODE_KEY) mod_chanmode_append(&chbuf, 'k', change->new_key); + if (change->modes_set & MODE_UPASS) + mod_chanmode_append(&chbuf, 'U', change->new_upass); + if (change->modes_set & MODE_APASS) + mod_chanmode_append(&chbuf, 'A', change->new_apass); if(change->modes_set & MODE_LIMIT) { sprintf(int_buff, "%d", change->new_limit); mod_chanmode_append(&chbuf, 'l', int_buff); @@ -2210,13 +2574,13 @@ mod_chanmode_announce(struct userNode *who, struct chanNode *channel, struct mod chbuf.modes[chbuf.modes_used++] = mode = '+'; 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->numeric); + mod_chanmode_append(&chbuf, 'o', change->args[arg].u.member->user->numeric); if (change->args[arg].mode & MODE_VOICE) - mod_chanmode_append(&chbuf, 'v', change->args[arg].member->user->numeric); + mod_chanmode_append(&chbuf, 'v', change->args[arg].u.member->user->numeric); break; } } @@ -2246,10 +2610,13 @@ mod_chanmode_format(struct mod_chanmode *change, char *outbuff) DO_MODE_CHAR(NOPRIVMSGS, 'n'); DO_MODE_CHAR(LIMIT, 'l'); DO_MODE_CHAR(KEY, 'k'); + DO_MODE_CHAR(UPASS, 'U'); + DO_MODE_CHAR(APASS, 'A'); DO_MODE_CHAR(DELAYJOINS, 'D'); DO_MODE_CHAR(REGONLY, 'r'); DO_MODE_CHAR(NOCOLORS, 'c'); DO_MODE_CHAR(NOCTCPS, 'C'); + DO_MODE_CHAR(REGISTERED, 'z'); #undef DO_MODE_CHAR } if (change->modes_set) { @@ -2265,11 +2632,53 @@ mod_chanmode_format(struct mod_chanmode *change, char *outbuff) DO_MODE_CHAR(REGONLY, 'r'); DO_MODE_CHAR(NOCOLORS, 'c'); DO_MODE_CHAR(NOCTCPS, 'C'); + DO_MODE_CHAR(REGISTERED, 'z'); #undef DO_MODE_CHAR - switch (change->modes_set & (MODE_KEY|MODE_LIMIT)) { + switch (change->modes_set & (MODE_KEY|MODE_LIMIT|MODE_APASS|MODE_UPASS)) { + /* Doing this implementation has been a pain in the arse, I hope I didn't forget a possible combination */ + case MODE_KEY|MODE_LIMIT|MODE_APASS|MODE_UPASS: + used += sprintf(outbuff+used, "lkAU %d %s %s %s", change->new_limit, change->new_key, change->new_apass, change->new_upass); + break; + + case MODE_KEY|MODE_LIMIT|MODE_APASS: + used += sprintf(outbuff+used, "lkA %d %s %s", change->new_limit, change->new_key, change->new_apass); + break; + case MODE_KEY|MODE_LIMIT|MODE_UPASS: + used += sprintf(outbuff+used, "lkU %d %s %s", change->new_limit, change->new_key, change->new_upass); + break; + case MODE_KEY|MODE_APASS|MODE_UPASS: + used += sprintf(outbuff+used, "kAU %s %s %s", change->new_key, change->new_apass, change->new_upass); + break; + + case MODE_KEY|MODE_APASS: + used += sprintf(outbuff+used, "kA %s %s", change->new_key, change->new_apass); + break; + case MODE_KEY|MODE_UPASS: + used += sprintf(outbuff+used, "kU %s %s", change->new_key, change->new_upass); + break; case MODE_KEY|MODE_LIMIT: used += sprintf(outbuff+used, "lk %d %s", change->new_limit, change->new_key); break; + case MODE_LIMIT|MODE_UPASS: + used += sprintf(outbuff+used, "lU %d %s", change->new_limit, change->new_upass); + break; + case MODE_LIMIT|MODE_APASS: + used += sprintf(outbuff+used, "lA %d %s", change->new_limit, change->new_apass); + break; + case MODE_APASS|MODE_UPASS: + used += sprintf(outbuff+used, "AU %s %s", change->new_apass, change->new_upass); + break; + + case MODE_LIMIT|MODE_APASS|MODE_UPASS: + used += sprintf(outbuff+used, "lAU %d %s %s", change->new_limit, change->new_apass, change->new_upass); + break; + + case MODE_APASS: + used += sprintf(outbuff+used, "A %s", change->new_apass); + break; + case MODE_UPASS: + used += sprintf(outbuff+used, "U %s", change->new_upass); + break; case MODE_KEY: used += sprintf(outbuff+used, "k %s", change->new_key); break; @@ -2301,6 +2710,14 @@ clear_chanmode(struct chanNode *channel, const char *modes) remove |= MODE_KEY; channel->key[0] = '\0'; break; + case 'A': + remove |= MODE_APASS; + channel->apass[0] = '\0'; + break; + case 'U': + remove |= MODE_UPASS; + channel->upass[0] = '\0'; + break; case 'l': remove |= MODE_LIMIT; channel->limit = 0; @@ -2308,8 +2725,9 @@ clear_chanmode(struct chanNode *channel, const char *modes) case 'b': remove |= MODE_BAN; break; case 'D': remove |= MODE_DELAYJOINS; break; case 'r': remove |= MODE_REGONLY; break; - case 'c': remove |= MODE_NOCOLORS; + case 'c': remove |= MODE_NOCOLORS; break; case 'C': remove |= MODE_NOCTCPS; break; + case 'z': remove |= MODE_REGISTERED; break; } } @@ -2344,9 +2762,10 @@ reg_privmsg_func(struct userNode *user, privmsg_func_t handler) { unsigned int numeric = user->num_local; if (numeric >= num_privmsg_funcs) { - int newnum = numeric + 8; + int newnum = numeric + 8, ii; privmsg_funcs = realloc(privmsg_funcs, newnum*sizeof(privmsg_func_t)); - memset(privmsg_funcs+num_privmsg_funcs, 0, (newnum-num_privmsg_funcs)*sizeof(privmsg_func_t)); + for (ii = num_privmsg_funcs; ii < newnum; ++ii) + privmsg_funcs[ii] = NULL; num_privmsg_funcs = newnum; } if (privmsg_funcs[numeric]) @@ -2355,20 +2774,12 @@ reg_privmsg_func(struct userNode *user, privmsg_func_t handler) } void -unreg_privmsg_func(struct userNode *user, privmsg_func_t handler) +unreg_privmsg_func(struct userNode *user) { - unsigned int x; - - if (!user || handler) - return; /* this really only works with users */ + if (!IsLocal(user) || user->num_local >= num_privmsg_funcs) + return; /* this really only works with users */ - memset(privmsg_funcs+user->num_local, 0, sizeof(privmsg_func_t)); - - for (x = user->num_local+1; x < num_privmsg_funcs; x++) - memmove(privmsg_funcs+x-1, privmsg_funcs+x, sizeof(privmsg_func_t)); - - privmsg_funcs = realloc(privmsg_funcs, num_privmsg_funcs*sizeof(privmsg_func_t)); - num_privmsg_funcs--; + privmsg_funcs[user->num_local] = NULL; } @@ -2377,9 +2788,10 @@ reg_notice_func(struct userNode *user, privmsg_func_t handler) { unsigned int numeric = user->num_local; if (numeric >= num_notice_funcs) { - int newnum = numeric + 8; + int newnum = numeric + 8, ii; notice_funcs = realloc(notice_funcs, newnum*sizeof(privmsg_func_t)); - memset(notice_funcs+num_notice_funcs, 0, (newnum-num_notice_funcs)*sizeof(privmsg_func_t)); + for (ii = num_privmsg_funcs; ii < newnum; ++ii) + privmsg_funcs[ii] = NULL; num_notice_funcs = newnum; } if (notice_funcs[numeric]) @@ -2388,34 +2800,25 @@ reg_notice_func(struct userNode *user, privmsg_func_t handler) } void -unreg_notice_func(struct userNode *user, privmsg_func_t handler) +unreg_notice_func(struct userNode *user) { - unsigned int x; - - if (!user || handler) - return; /* this really only works with users */ - - memset(notice_funcs+user->num_local, 0, sizeof(privmsg_func_t)); + if (!IsLocal(user) || user->num_local >= num_privmsg_funcs) + return; /* this really only works with users */ - for (x = user->num_local+1; x < num_notice_funcs; x++) - memmove(notice_funcs+x-1, notice_funcs+x, sizeof(privmsg_func_t)); - - memset(notice_funcs+user->num_local, 0, sizeof(privmsg_func_t)); - notice_funcs = realloc(notice_funcs, num_notice_funcs*sizeof(privmsg_func_t)); - num_notice_funcs--; + notice_funcs[user->num_local] = NULL; } 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)); - } + 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; } @@ -2427,7 +2830,7 @@ call_oper_funcs(struct userNode *user) if (IsLocal(user)) return; for (n=0; n