X-Git-Url: http://git.pk910.de/?a=blobdiff_plain;f=src%2Fproto-p10.c;h=f544f9f019a2ddc3799bb19de4e0dc013e0a982a;hb=0a7f0a288c131806db7b8538554b918a99c1a21d;hp=4d64e830b9a4a60062a93826c18fc9aaf817b504;hpb=de32b9d4f1ce5452935401196a60799afc3882f2;p=srvx.git diff --git a/src/proto-p10.c b/src/proto-p10.c index 4d64e83..f544f9f 100644 --- a/src/proto-p10.c +++ b/src/proto-p10.c @@ -1,5 +1,5 @@ /* proto-p10.c - IRC protocol output - * Copyright 2000-2006 srvx Development Team + * Copyright 2000-2008 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" @@ -40,7 +40,8 @@ #define CMD_EOB_ACK "EOB_ACK" #define CMD_ERROR "ERROR" #define CMD_FAKEHOST "FAKE" -#define CMD_GET "GET" +#define CMD_FAKEHOST2 "FAKE2" +#define CMD_GET "GET" #define CMD_GLINE "GLINE" #define CMD_HASH "HASH" #define CMD_HELP "HELP" @@ -68,24 +69,27 @@ #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_RELAY "RELAY" +#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" #define CMD_SQUIT "SQUIT" #define CMD_STATS "STATS" #define CMD_SVSNICK "SVSNICK" +#define CMD_SVSMODE "SVSMODE" +#define CMD_SVSJOIN "SVSJOIN" #define CMD_TIME "TIME" #define CMD_TOPIC "TOPIC" #define CMD_TRACE "TRACE" @@ -101,11 +105,13 @@ #define CMD_WHO "WHO" #define CMD_WHOIS "WHOIS" #define CMD_WHOWAS "WHOWAS" +#define CMD_XQUERY "XQUERY" +#define CMD_XRESPONSE "XRESPONSE" /* 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" @@ -122,7 +128,8 @@ #define TOK_EOB_ACK "EA" #define TOK_ERROR "Y" #define TOK_FAKEHOST "FA" -#define TOK_GET "GET" +#define TOK_FAKEHOST2 "NFH" +#define TOK_GET "GET" #define TOK_GLINE "GL" #define TOK_HASH "HASH" #define TOK_HELP "HELP" @@ -150,24 +157,27 @@ #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_RELAY "RL" +#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" #define TOK_SQUIT "SQ" #define TOK_STATS "R" #define TOK_SVSNICK "SN" +#define TOK_SVSMODE "SM" +#define TOK_SVSJOIN "SJ" #define TOK_TIME "TI" #define TOK_TOPIC "T" #define TOK_TRACE "TR" @@ -183,6 +193,8 @@ #define TOK_WHO "H" #define TOK_WHOIS "W" #define TOK_WHOWAS "X" +#define TOK_XQUERY "XQ" +#define TOK_XRESPONSE "XR" /* Protocol messages; aliased to full commands or tokens depending on compile-time configuration. ircu prefers tokens WITH THE @@ -196,9 +208,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) @@ -215,7 +227,8 @@ #define P10_EOB_ACK TYPE(EOB_ACK) #define P10_ERROR TYPE(ERROR) #define P10_FAKEHOST TYPE(FAKEHOST) -#define P10_GET TYPE(GET) +#define P10_FAKEHOST2 TYPE(FAKEHOST2) +#define P10_GET TYPE(GET) #define P10_GLINE TYPE(GLINE) #define P10_HASH TYPE(HASH) #define P10_HELP TYPE(HELP) @@ -243,24 +256,27 @@ #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_RELAY TYPE(RELAY) +#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) #define P10_SQUIT TYPE(SQUIT) #define P10_STATS TYPE(STATS) #define P10_SVSNICK TYPE(SVSNICK) +#define P10_SVSMODE TYPE(SVSMODE) +#define P10_SVSJOIN TYPE(SVSJOIN) #define P10_TIME TYPE(TIME) #define P10_TOPIC TYPE(TOPIC) #define P10_TRACE TYPE(TRACE) @@ -276,6 +292,8 @@ #define P10_WHO TYPE(WHO) #define P10_WHOIS TYPE(WHOIS) #define P10_WHOWAS TYPE(WHOWAS) +#define P10_XQUERY TYPE(XQUERY) +#define P10_XRESPONSE TYPE(XRESPONSE) /* Servers claiming to have a boot or link time before PREHISTORY * trigger errors to the log. We hope no server has been running @@ -289,14 +307,28 @@ 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; +static const char *his_servername; +static const char *his_servercomment; +static struct channelList dead_channels; -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); +/* These correspond to 1 << X: 012345678901234567890123 */ +const char irc_user_mode_chars[] = "o iw dkgn x ISDXHst"; + +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); extern int off_channel; -static int parse_oplevel(char *str); +/* + * Oplevel parsing + */ +static int +parse_oplevel(char *str) +{ + int oplevel = 0; + while (isdigit(*str)) + oplevel = oplevel * 10 + *str++ - '0'; + return oplevel; +} /* Numerics can be XYY, XYYY, or XXYYY; with X's identifying the * server and Y's indentifying the client on that server. */ @@ -368,11 +400,11 @@ 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 +s6 :%s", - srv->name, srv->hops+1, srv->boot, srv->link, srv->numeric, extranum, srv->description); + putsock(P10_SERVER " %s %d %lu %lu J10 %s%s +s6 :%s", + srv->name, srv->hops+1, srv->boot, srv->link_time, srv->numeric, extranum, srv->description); } else { - putsock("%s " P10_SERVER " %s %d %li %li %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); + putsock("%s " P10_SERVER " %s %d %lu %lu %c10 %s%s +s6 :%s", + self->numeric, srv->name, srv->hops+1, srv->boot, srv->link_time, (srv->self_burst ? 'J' : 'P'), srv->numeric, extranum, srv->description); } } @@ -392,7 +424,7 @@ irc_p10_pton(irc_in_addr_t *ip, const char *input) do { if (*input == '_') { unsigned int left; - for (left = (25 - strlen(input)) / 3; left; left--) + for (left = (25 - strlen(input)) / 3 - pos; left; left--) ip->in6[pos++] = 0; input++; } else { @@ -443,6 +475,7 @@ irc_p10_ntop(char *output, const irc_in_addr_t *ip) } else { inttobase64(output, ntohs(ip->in6[ii]), 3); output += 3; + ii += 1; } } *output = '\0'; @@ -459,45 +492,50 @@ irc_user(struct userNode *user) return; irc_p10_ntop(b64ip, &user->ip); if (user->modes) { - int modelen; char modes[32]; - - 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 (IsDeaf(user)) - modes[modelen++] = 'd'; - if (IsGlobal(user)) - modes[modelen++] = 'g'; - if (IsHiddenHost(user)) - modes[modelen++] = 'x'; - 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); + irc_user_modes(user, modes, sizeof(modes)); + 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); } } void -irc_account(struct userNode *user, const char *stamp) +irc_account(struct userNode *user, const char *stamp, unsigned long timestamp, unsigned long serial) { - putsock("%s " P10_ACCOUNT " %s %s", self->numeric, user->numeric, stamp); + putsock("%s " P10_ACCOUNT " %s %s %lu %lu", self->numeric, user->numeric, stamp, timestamp, serial); } void -irc_fakehost(struct userNode *user, const char *host) +irc_fakehost(struct userNode *user, const char *host, const char *ident, int force) { - putsock("%s " P10_FAKEHOST " %s %s", self->numeric, user->numeric, host); + /* SRVX added the possibility for FAKE IDENTS + * but this is currently *NOT* supported by our IRCu + * + * edit 24.11.11: + * NFH (P10_FAKEHOST2) is now supported by our IRCu (git-65-21592a4) + */ + putsock("%s " P10_FAKEHOST2 " %s %s %s%s", self->numeric, user->numeric, ident, host, force ? " FORCE" : ""); +} + +void +irc_relay(char *message) +{ + putsock("%s " P10_RELAY " %s", self->numeric, message); +} + +void +irc_simul(struct userNode *target, char *command) +{ + putsock("%s " P10_RELAY " %s SI %s :%s", self->numeric, target->numeric, target->numeric, command); +} + +void +irc_keepconn(struct userNode *target, unsigned int timeout) +{ + putsock("%s " P10_RELAY " %s KC %s %u", self->numeric, target->numeric, target->numeric, timeout); } void @@ -509,7 +547,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 @@ -598,6 +636,24 @@ irc_privmsg(struct userNode *from, const char *to, const char *message) putsock("%s " P10_PRIVMSG " %s :%s", from->numeric, to, message); } +void +irc_svsmode(struct userNode *from, struct userNode *user, const char *modes) +{ +putsock("%s " P10_SVSMODE " %s %s", from->numeric, user->numeric, modes); +} + +void +irc_svsjoin(struct userNode *from, struct userNode *user, struct chanNode *chan) +{ +putsock("%s " P10_SVSJOIN " %s %s", from->numeric, user->numeric, chan->name); +} + +void +irc_svsjoinchan(struct userNode *from, struct userNode *user, const char *chan) +{ +putsock("%s " P10_SVSJOIN " %s %s", from->numeric, user->numeric, chan); +} + void irc_eob(void) { @@ -627,14 +683,14 @@ irc_pong_asll(const char *who, const char *orig_ts) { char *delim; struct timeval orig; - struct timeval now; + struct timeval sys_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 " FMT_TIME_T ".%06u", self->numeric, who, orig_ts, diff, now.tv_sec, (unsigned)now.tv_usec); + gettimeofday(&sys_now, NULL); + diff = (sys_now.tv_sec - orig.tv_sec) * 1000 + (sys_now.tv_usec - orig.tv_usec) / 1000; + putsock("%s " P10_PONG " %s %s %d %lu.%06lu", self->numeric, who, orig_ts, diff, (unsigned long)sys_now.tv_sec, (unsigned long)sys_now.tv_usec); } void @@ -658,27 +714,58 @@ irc_introduce(const char *passwd) void irc_gline(struct server *srv, struct gline *gline) { + // GL [!][+|-|>|<] [] [] [] [:] + //expiration = relative time (seconds) + //lastmod = timestamp + //livetime = timestamp if (gline->lastmod) - putsock("%s " P10_GLINE " %s +%s %ld %ld :%s", - self->numeric, (srv ? srv->numeric : "*"), gline->target, gline->expires-now, gline->lastmod, gline->reason); + putsock("%s " P10_GLINE " %s +%s %lu %lu %lu :%s", self->numeric, (srv ? srv->numeric : "*"), + gline->target, gline->expires-now, gline->lastmod, gline->lifetime, gline->reason); else - putsock("%s " P10_GLINE " %s +%s %ld :%s", - self->numeric, (srv ? srv->numeric : "*"), gline->target, gline->expires-now, gline->reason); + putsock("%s " P10_GLINE " %s +%s %lu :%s", self->numeric, (srv ? srv->numeric : "*"), + gline->target, 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 irc_ungline(const char *mask) { - putsock("%s " P10_GLINE " * -%s", self->numeric, mask); + putsock("%s " P10_GLINE " * -%s %lu", self->numeric, mask, now); + //putsock("%s " P10_GLINE " * %s * %lu", self->numeric, mask, now); +} + +/* Return negative if *(struct modeNode**)pa is "less than" pb, + * positive if pa is "larger than" pb. Comparison is based on sorting + * so that non-voiced/non-opped users are first, voiced-only users are + * next, and the "strongest" oplevels are before "weaker" oplevels. + * Within those sets, ordering is arbitrary. + */ +static int +modeNode_sort_p10(const void *pa, const void *pb) +{ + struct modeNode *a = *(struct modeNode**)pa; + struct modeNode *b = *(struct modeNode**)pb; + + if (a->modes & MODE_CHANOP) { + if (!(b->modes & MODE_CHANOP)) + return 1; + else if ((a->modes & MODE_VOICE) != (b->modes & MODE_VOICE)) + return (a->modes & MODE_VOICE) - (b->modes & MODE_VOICE); + else if (a->oplevel != b->oplevel) + return a->oplevel - b->oplevel; + } else if (b->modes & MODE_CHANOP) + return -1; + else if ((a->modes & MODE_VOICE) != (b->modes & MODE_VOICE)) + return (a->modes & MODE_VOICE) - (b->modes & MODE_VOICE); + return (a < b) ? -1 : 1; } static void @@ -688,17 +775,23 @@ irc_burst(struct chanNode *chan) int pos, base_len, len; struct modeNode *mn; struct banNode *bn; - long last_mode=-1; + int last_oplevel = 0; + int last_mode = 0; + int new_modes; 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 > 0 && chan->members.used > 0) burst_line[pos++] = ' '; + /* sort the users for oplevel-sending purposes */ + qsort(chan->members.list, chan->members.used, sizeof(chan->members.list[0]), modeNode_sort_p10); + /* dump the users */ for (n=0; nmembers.used; n++) { mn = chan->members.list[n]; @@ -706,17 +799,32 @@ irc_burst(struct chanNode *chan) burst_line[pos-1] = 0; /* -1 to back up over the space or comma */ putsock("%s", burst_line); pos = base_len; - last_mode = -1; + last_mode = 0; + last_oplevel = 0; } memcpy(burst_line+pos, mn->user->numeric, strlen(mn->user->numeric)); pos += strlen(mn->user->numeric); - if (mn->modes && (mn->modes != last_mode)) { - last_mode = mn->modes; + new_modes = mn->modes & (MODE_CHANOP | MODE_VOICE); + if (new_modes != last_mode) { + last_mode = new_modes; burst_line[pos++] = ':'; - if (last_mode & MODE_CHANOP) - burst_line[pos++] = 'o'; - if (last_mode & MODE_VOICE) + if (new_modes & MODE_VOICE) burst_line[pos++] = 'v'; + /* Note: :vNNN (oplevel NNN with voice) resets the + * implicit oplevel back to zero, so we always use the raw + * oplevel value here. Read ircu's m_burst.c for more + * examples. + */ + if (new_modes & MODE_CHANOP) { + last_oplevel = mn->oplevel; + if (mn->oplevel < MAXOPLEVEL) + pos += sprintf(burst_line + pos, "%u", mn->oplevel); + else + burst_line[pos++] = 'o'; + } + } else if ((last_mode & MODE_CHANOP) && (mn->oplevel != last_oplevel)) { + pos += sprintf(burst_line + pos, ":%u", mn->oplevel - last_oplevel); + last_oplevel = mn->oplevel; } if ((n+1)members.used) burst_line[pos++] = ','; @@ -776,8 +884,8 @@ void irc_kill(struct userNode *from, struct userNode *target, const char *message) { if (from) { - putsock("%s " P10_KILL " %s :%s!%s (%s)", - from->numeric, target->numeric, self->name, from->nick, message); + putsock("%s " P10_KILL " %s :%s (%s)", + from->numeric, target->numeric, from->nick, message); } else { putsock("%s " P10_KILL " %s :%s (%s)", self->numeric, target->numeric, self->name, message); @@ -787,9 +895,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 @@ -803,9 +911,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); } } @@ -828,7 +937,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 @@ -864,6 +973,12 @@ irc_numeric(struct userNode *user, unsigned int num, const char *format, ...) putsock(":%s %03d %s %s", self->name, num, user->nick, buffer); } +void +irc_xresponse(struct server *target, const char *routing, const char *response) +{ + putsock("%s " P10_XRESPONSE " %s %s :%s", self->numeric, target->numeric, routing, response); +} + static void send_burst(void); static void @@ -888,6 +1003,8 @@ static CMD_FUNC(cmd_whois) { struct userNode *from; struct userNode *who; + char buf[MAXLEN]; + unsigned int i, mlen, len; if (argc < 3) return 0; @@ -895,21 +1012,77 @@ static CMD_FUNC(cmd_whois) log_module(MAIN_LOG, LOG_ERROR, "Could not find WHOIS origin user %s", origin); return 0; } - if(!(who = GetUserH(argv[2]))) { + 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; + + if (IsFakeHost(who) && IsFakeIdent(who) && IsHiddenHost(who)) + irc_numeric(from, RPL_WHOISUSER, "%s %s %s * :%s", who->nick, who->fakeident, who->fakehost, who->info); + else if (IsFakeIdent(who) && IsHiddenHost(who)) + irc_numeric(from, RPL_WHOISUSER, "%s %s %s * :%s", who->nick, who->fakeident, who->hostname, who->info); + else 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); } - irc_numeric(from, RPL_WHOISUSER, "%s %s %s * :%s", who->nick, who->ident, who->hostname, who->info); - if (his_servername && his_servercomment) + + 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 :is a megalomaniacal power hungry tyrant", who->nick); + 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; } @@ -945,7 +1118,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)) { @@ -956,11 +1129,11 @@ static CMD_FUNC(cmd_server) * Alternately, we are same age, but we accept their time * since we are linking to them. */ self->boot = srv->boot; - ioset_set_time(srv->link); + ioset_set_time(srv->link_time); } } if (srv == self->uplink) { - extern time_t burst_begin; + extern unsigned long burst_begin; burst_begin = now; } return 1; @@ -992,7 +1165,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; @@ -1033,7 +1206,7 @@ static CMD_FUNC(cmd_error_nick) struct create_desc { struct userNode *user; - time_t when; + unsigned long when; }; static void @@ -1054,6 +1227,8 @@ create_helper(char *name, void *data) return; } + handle_new_channel_created(name, cd->user); + AddChannelUser(cd->user, AddChannel(name, cd->when, NULL, NULL)); } @@ -1126,25 +1301,41 @@ static CMD_FUNC(cmd_nick) static CMD_FUNC(cmd_account) { struct userNode *user; + unsigned long timestamp = 0; + unsigned long serial = 0; if ((argc < 3) || !origin || !GetServerH(origin)) return 0; /* Origin must be server. */ user = GetUserN(argv[1]); if (!user) return 1; /* A QUIT probably passed the ACCOUNT. */ - call_account_func(user, argv[2]); + if (argc > 3) + timestamp = strtoul(argv[3], NULL, 10); + if (argc > 4) + serial = strtoul(argv[4], NULL, 10); + call_account_func(user, argv[2], timestamp, serial); return 1; } static CMD_FUNC(cmd_fakehost) { struct userNode *user; + const char *host, *ident; if ((argc < 3) || !origin || !GetServerH(origin)) return 0; if (!(user = GetUserN(argv[1]))) return 1; - assign_fakehost(user, argv[2], 0); + + if (argc > 3) { + ident = argv[2]; + host = argv[3]; + } else { + ident = NULL; + host = argv[2]; + } + + assign_fakehost(user, host, ident, 0, 0); return 1; } @@ -1157,9 +1348,9 @@ static CMD_FUNC(cmd_burst) struct userNode *un; struct modeNode *mNode; long mode; - int oplevel = -1; + int oplevel = 0; char *user, *end, sep; - time_t in_timestamp; + unsigned long in_timestamp; if (argc < 3) return 0; @@ -1171,8 +1362,10 @@ static CMD_FUNC(cmd_burst) int n_modes; for (pos=argv[next], n_modes = 1; *pos; pos++) if ((*pos == 'k') || (*pos == 'l') || (*pos == 'A') - || (*pos == 'U')) + || (*pos == 'U') || (*pos == 'a') || (*pos == 'F')) n_modes++; + if (next + n_modes > argc) + n_modes = argc - next; unsplit_string(argv+next, n_modes, modes); next += n_modes; break; @@ -1199,16 +1392,13 @@ static CMD_FUNC(cmd_burst) while ((sep = *end++)) { if (sep == 'o') { mode |= MODE_CHANOP; - oplevel = -1; + oplevel = MAXOPLEVEL; } else if (sep == 'v') { mode |= MODE_VOICE; - oplevel = -1; + oplevel = 0; } else if (isdigit(sep)) { mode |= MODE_CHANOP; - if (oplevel >= 0) - oplevel += parse_oplevel(end); - else - oplevel = parse_oplevel(end); + oplevel += parse_oplevel(end - 1); while (isdigit(*end)) end++; } else break; @@ -1242,6 +1432,7 @@ static CMD_FUNC(cmd_mode) log_module(MAIN_LOG, LOG_ERROR, "Unable to find user %s whose mode is changing.", argv[1]); return 0; } + argv[2] = unsplit_string(argv + 2, argc - 2, NULL); mod_usermode(un, argv[2]); return 1; } @@ -1316,7 +1507,7 @@ static CMD_FUNC(cmd_clearmode) static CMD_FUNC(cmd_topic) { struct chanNode *cn; - time_t chan_ts, topic_ts; + unsigned long topic_ts; if (argc < 3) return 0; @@ -1326,10 +1517,8 @@ static CMD_FUNC(cmd_topic) } if (argc >= 5) { /* Looks like an Asuka style topic burst. */ - chan_ts = atoi(argv[2]); topic_ts = atoi(argv[3]); } else { - chan_ts = cn->timestamp; topic_ts = now; } SetChannelTopic(cn, GetUserH(origin), argv[argc-1], 0); @@ -1375,11 +1564,14 @@ static CMD_FUNC(cmd_num_topic) static CMD_FUNC(cmd_num_gline) { - time_t lastmod; + unsigned long lastmod; + unsigned long lifetime; + if (argc < 6) return 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); + lifetime = (argc > 6) ? strtoul(argv[6], NULL, 0) : 0; + gline_add(origin, argv[3], atoi(argv[4])-now, argv[argc - 1], now, lastmod, lifetime, 0); return 1; } @@ -1425,6 +1617,8 @@ static CMD_FUNC(cmd_kick) { if (argc < 3) return 0; + if (GetUserN(argv[2]) && IsOper(GetUserN(argv[2]))) + operpart(GetChannel(argv[1]), GetUserN(argv[2])); ChannelUserKicked(GetUserH(origin), GetUserN(argv[2]), GetChannel(argv[1])); return 1; } @@ -1453,13 +1647,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; } @@ -1467,13 +1661,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; } @@ -1494,15 +1688,22 @@ static CMD_FUNC(cmd_away) static CMD_FUNC(cmd_gline) { - time_t lastmod; +#define PASTWATCH (5*365*24*3600) + unsigned long lastmod; + unsigned long lifetime; + unsigned long expiration; if (argc < 3) return 0; if (argv[2][0] == '+') { if (argc < 5) return 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); + expiration = strtoul(argv[3], NULL, 10); + if (expiration < now - PASTWATCH) + expiration += now; + lastmod = (argc > 5) ? strtoul(argv[4], NULL, 10) : 0; + lifetime = (argc > 6) ? strtoul(argv[5], NULL, 10) : 0; + gline_add(origin, argv[2]+1, expiration - now, argv[argc-1], now, lastmod, lifetime, 0); return 1; } else if (argv[2][0] == '-') { gline_remove(argv[2]+1, 0); @@ -1523,8 +1724,131 @@ static CMD_FUNC(cmd_svsnick) return 1; } -static oper_func_t *of_list; -static unsigned int of_size = 0, of_used = 0; +static CMD_FUNC(cmd_time) +{ + extern int clock_skew; + char buf[MAXLEN]; + struct userNode *who; + time_t when; + + who = GetUserH(origin); + if (!who) + return 0; + + when = time(NULL); + strftime(buf, sizeof(buf), "%a %b %d %Y -- %H:%M %z", localtime(&when)); + irc_numeric(who, 391, "%s %lu %d :%s", self->name, now, clock_skew, buf); + return 1; +} + +static CMD_FUNC(cmd_relay) +{ + struct server *sNode; + unsigned int len; + char buf[3]; + // RELAY + len = strlen(argv[1]); + buf[2] = 0; + switch(len) { + case 2: + sNode = GetServerN(argv[1]); + break; + case 5: + buf[0] = argv[1][0]; + buf[1] = argv[1][1]; + sNode = GetServerN(buf); + break; + case 6: + buf[0] = argv[1][1]; + buf[1] = argv[1][2]; + sNode = GetServerN(buf); + break; + default: + /* Invalid destination. Ignore. */ + return 0; + } + if(sNode->numeric == self->numeric) { + //ok someone relayed something to us! + if(strcmp("LQ", argv[2]) == 0) { + //oooh thats exciting - we've got a LOC Query! :D + //LQ !ABADE pk910 80.153.5.212 server.zoelle1.de ~watchcat :test + //ok let's check the login datas + struct handle_info *hi; + char tmp[MAXLEN], tmp2[MAXLEN]; + sprintf(tmp, "%s@%s",argv[7],argv[6]); + sprintf(tmp2, "%s@%s",argv[7],argv[5]); + if((hi = checklogin(argv[4],argv[argc-1],&argv[3][1],tmp,tmp2))) { + //login ok + struct devnull_class *th; + char devnull[512]; + if(hi->devnull && (th = devnull_get(hi->devnull))) { + const char *devnull_modes = DEVNULL_MODES; + int ii, flen; + char flags[50]; + for (ii=flen=0; devnull_modes[ii]; ++ii) + if (th->modes & (1 << ii)) + flags[flen++] = devnull_modes[ii]; + flags[flen] = 0; + sprintf(devnull, "+%s %s %lu %lu",flags,th->name,th->maxchan,th->maxsendq); + } else { + devnull[0] = 0; + } + if(!HANDLE_FLAGGED(hi, AUTOHIDE)) { + sprintf(tmp,"%s LA %s 0 %s\n",argv[3],hi->handle,devnull); + } else if(getfakehost(argv[4])) { + sprintf(tmp,"%s LA %s %s %s\n",argv[3],hi->handle,getfakehost(argv[4]),devnull); + } else { + extern const char *hidden_host_suffix; + sprintf(tmp,"%s LA %s %s.%s %s\n",argv[3],hi->handle,hi->handle,hidden_host_suffix,devnull); + } + irc_relay(tmp); + } else { + //login rejected + sprintf(tmp,"%s LR\n",argv[3]); + irc_relay(tmp); + } + } else if(strcmp("UC", argv[2]) == 0) { + char tmp[MAXLEN]; + sprintf(tmp,"%s UC %s %s",argv[3],argv[3],argv[4]); + irc_relay(tmp); + } else if(strcmp("JA", argv[2]) == 0) { + struct userData *uData; + struct chanNode *cn; + struct userNode *user; + char tmp[MAXLEN]; + cn = GetChannel(argv[4]); + if (!cn) return 0; + if (!(user = GetUserN(argv[3]))) return 0; + if(!cn->channel_info) { + //channel not registered + sprintf(tmp,"%s JAA %s %s\n",argv[3],cn->name,argv[6]); + } else if((uData = GetChannelUser(cn->channel_info, user->handle_info))) { + if(uData->access >= atoi(argv[5])) { + //we can join + sprintf(tmp,"%s JAA %s %s\n",argv[3],cn->name,argv[6]); + } else { + //access too low + sprintf(tmp,"%s JAR %s %i %i\n",argv[3],cn->name,uData->access,uData->access); + } + } else { + //0 access + sprintf(tmp,"%s JAR %s %s %s\n",argv[3],cn->name,"0","0"); + } + irc_relay(tmp); + } + } + return 1; +} + +static CMD_FUNC(cmd_xquery) +{ + struct server *source; + if ((argc < 4) + || !(source = GetServerH(origin))) + return 0; + call_xquery_funcs(source, argv[2], argv[3]); + return 1; +} void free_user(struct userNode *user) @@ -1552,6 +1876,8 @@ parse_cleanup(void) static void p10_conf_reload(void) { hidden_host_suffix = conf_get_data("server/hidden_host", RECDB_QSTRING); + his_servername = conf_get_data("server/his_servername", RECDB_QSTRING); + his_servercomment = conf_get_data("server/his_servercomment", RECDB_QSTRING); } static void @@ -1589,11 +1915,6 @@ init_parse(void) else inttobase64(numer, (numnick << 18) + (usermask & 0x3ffff), 5); - str = conf_get_data("server/his_servername", RECDB_QSTRING); - his_servername = str ? strdup(str) : NULL; - str = conf_get_data("server/his_servercomment", RECDB_QSTRING); - his_servercomment = str ? strdup(str) : NULL; - str = conf_get_data("server/hostname", RECDB_QSTRING); desc = conf_get_data("server/description", RECDB_QSTRING); if (!str || !desc) { @@ -1656,6 +1977,8 @@ init_parse(void) dict_insert(irc_func_dict, TOK_STATS, cmd_stats); dict_insert(irc_func_dict, CMD_SVSNICK, cmd_svsnick); dict_insert(irc_func_dict, TOK_SVSNICK, cmd_svsnick); + dict_insert(irc_func_dict, CMD_RELAY, cmd_relay); + dict_insert(irc_func_dict, TOK_RELAY, cmd_relay); dict_insert(irc_func_dict, CMD_WHOIS, cmd_whois); dict_insert(irc_func_dict, TOK_WHOIS, cmd_whois); dict_insert(irc_func_dict, CMD_GLINE, cmd_gline); @@ -1668,6 +1991,10 @@ init_parse(void) dict_insert(irc_func_dict, TOK_VERSION, cmd_version); dict_insert(irc_func_dict, CMD_ADMIN, cmd_admin); dict_insert(irc_func_dict, TOK_ADMIN, cmd_admin); + dict_insert(irc_func_dict, CMD_TIME, cmd_time); + dict_insert(irc_func_dict, TOK_TIME, cmd_time); + /* We don't handle XR or the (not really defined) XQUERY. */ + dict_insert(irc_func_dict, TOK_XQUERY, cmd_xquery); /* In P10, DESTRUCT doesn't do anything except be broadcast to servers. * Apparently to obliterate channels from any servers that think they @@ -1722,6 +2049,7 @@ init_parse(void) memset(notice_funcs, 0, sizeof(privmsg_func_t)*num_notice_funcs); userList_init(&dead_users); + channelList_init(&dead_channels); reg_del_channel_func(remove_unbursted_channel); reg_exit_func(parse_cleanup); } @@ -1758,6 +2086,9 @@ parse_line(char *line, int recursive) for (i=0; inum_mask = base64toint(numeric+slen, mlen); sNode->hops = hops; sNode->boot = boot; - sNode->link = link; + sNode->link_time = link_time; strncpy(sNode->numeric, numeric, slen); safestrncpy(sNode->description, description, sizeof(sNode->description)); sNode->users = calloc(sNode->num_mask+1, sizeof(*sNode->users)); @@ -1937,7 +2268,6 @@ AddLocalUser(const char *nick, const char *ident, const char *hostname, const ch { char numeric[COMBO_NUMERIC_LEN+1]; int local_num = get_local_numeric(); - time_t timestamp = now; struct userNode *old_user = GetUserH(nick); if (!modes) @@ -1945,7 +2275,6 @@ AddLocalUser(const char *nick, const char *ident, const char *hostname, const ch if (old_user) { if (IsLocal(old_user)) return old_user; - timestamp = old_user->timestamp - 1; } if (local_num == -1) { log_module(MAIN_LOG, LOG_ERROR, "Unable to allocate numnick for service %s", nick); @@ -1962,7 +2291,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) { @@ -1994,23 +2323,23 @@ 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, 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; } @@ -2018,19 +2347,25 @@ AddUser(struct server* uplink, const char *nick, const char *ident, const char * if (dummy) { ++modes; } else if (!is_valid_nick(nick)) { - log_module(MAIN_LOG, LOG_WARNING, "AddUser(%p, %s, ...): invalid nickname detected.", uplink, 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))) { + /* 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 */ + ignore_user = 1; + if (!uplink->burst) + irc_user(oldUser); + } else if (oldUser->timestamp > timestamp) { + /* "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 @@ -2049,6 +2384,7 @@ AddUser(struct server* uplink, const char *nick, const char *ident, const char * safestrncpy(uNode->numeric, numeric, sizeof(uNode->numeric)); 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) { @@ -2069,9 +2405,8 @@ AddUser(struct server* uplink, const char *nick, const char *ident, const char * } if (IsLocal(uNode)) irc_user(uNode); - for (n=0; ndead; n++) + nuf_list[n](uNode); return uNode; } @@ -2144,61 +2479,115 @@ void mod_usermode(struct userNode *user, const char *mode_change) { 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 '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) { + operdel(user); + userList_remove(&curr_opers, user); + } else if (!userList_contains(&curr_opers, user)) { + operadd(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 'd': do_user_mode(FLAGS_DEAF); break; - case 'k': do_user_mode(FLAGS_SERVICE); break; - case 'g': do_user_mode(FLAGS_GLOBAL); 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 'S': do_user_mode(FLAGS_NETSERV); break; + case 'D': do_user_mode(FLAGS_SECURITYSERV); break; + case 'X': do_user_mode(FLAGS_XTRAOP); break; + case 's': do_user_mode(FLAGS_SERVERNOTICE); break; + case 'H': do_user_mode(FLAGS_HIDDENOPER); break; + case 't': do_user_mode(FLAGS_SEENOIDLE); break; case 'x': do_user_mode(FLAGS_HIDDEN_HOST); break; case 'r': if (*word) { char tag[MAXLEN]; + char *sep; unsigned int ii; - for (ii=0; (*word != ' ') && (*word != '\0'); ) + unsigned long ts = 0; + unsigned long id = 0; + + for (ii=0; (*word != ' ') && (*word != '\0') && (*word != ':'); ) tag[ii++] = *word++; - tag[ii] = 0; + if (*word == ':') { + ts = strtoul(word + 1, &sep, 10); + if (*sep == ':') { + id = strtoul(sep + 1, &sep, 10); + } else if (*sep != ' ' && *sep != '\0') { + ts = 0; + } + word = sep; + } + tag[ii] = '\0'; while (*word == ' ') word++; - call_account_func(user, tag); + call_account_func(user, tag, ts, id); } break; - case 'f': + case 'h': if (*word) { - char host[MAXLEN]; + char mask[MAXLEN]; + char *host, *ident; unsigned int ii; + for (ii=0; (*word != ' ') && (*word != '\0'); ) - host[ii++] = *word++; - host[ii] = 0; + mask[ii++] = *word++; + mask[ii] = 0; while (*word == ' ') word++; - assign_fakehost(user, host, 0); + + if ((host = strrchr(mask, '@'))) { + ident = mask; + *host++ = '\0'; + } else { + ident = NULL; + host = mask; + } + user->modes |= FLAGS_HIDDEN_HOST; + assign_fakehost(user, host, ident, 0, 0); } break; - } + } #undef do_user_mode } } +static int +keyncpy(char output[], char input[], size_t output_size) +{ + size_t ii; + + if (input[0] == ':') + { + output[0] = '\0'; + return 1; + } + + for (ii = 0; (ii + 1 < output_size) && (input[ii] != '\0'); ++ii) + { + output[ii] = input[ii]; + } + + output[ii] = '\0'; + return 0; +} + struct mod_chanmode * -mod_chanmode_parse(struct chanNode *channel, char **modes, unsigned int argc, unsigned int flags, short base_oplevel) +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; @@ -2222,6 +2611,10 @@ mod_chanmode_parse(struct chanNode *channel, char **modes, unsigned int argc, un case 'C': do_chan_mode(MODE_NOCTCPS); break; case 'D': do_chan_mode(MODE_DELAYJOINS); break; case 'c': do_chan_mode(MODE_NOCOLORS); break; + case 'M': do_chan_mode(MODE_NOAMSGS); break; + case 'N': do_chan_mode(MODE_NONOTICES); break; + case 'u': do_chan_mode(MODE_AUDITORIUM); break; + case 'S': do_chan_mode(MODE_SSLCHAN); break; case 'i': do_chan_mode(MODE_INVITEONLY); break; case 'm': do_chan_mode(MODE_MODERATED); break; case 'n': do_chan_mode(MODE_NOPRIVMSGS); break; @@ -2229,14 +2622,16 @@ 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; + case 'z': + if (!(flags & MCP_REGISTERED) && (!(flags & MCP_IGN_REGISTERED) || add)) { + do_chan_mode(MODE_REGISTERED); + } else if (flags & MCP_IGN_REGISTERED) { + /* ignore the modechange but continue parsing */ + } else { + mod_chanmode_free(change); + return NULL; + } + break; #undef do_chan_mode case 'l': if (add) { @@ -2249,12 +2644,23 @@ mod_chanmode_parse(struct chanNode *channel, char **modes, unsigned int argc, un change->modes_clear |= MODE_LIMIT; } break; - case 'k': + case 'a': if (add) { if (in_arg >= argc) goto error; + change->modes_set |= MODE_ACCESS; + change->new_access = atoi(modes[in_arg++]); + } else { + change->modes_set &= ~MODE_ACCESS; + change->modes_clear |= MODE_ACCESS; + } + break; + case 'k': + if (add) { + if ((in_arg >= argc) + || keyncpy(change->new_key, modes[in_arg++], sizeof(change->new_key))) + goto error; change->modes_set |= MODE_KEY; - safestrncpy(change->new_key, modes[in_arg++], sizeof(change->new_key)); } else { change->modes_clear |= MODE_KEY; if (!(flags & MCP_KEY_FREE)) { @@ -2264,13 +2670,79 @@ mod_chanmode_parse(struct chanNode *channel, char **modes, unsigned int argc, un } } break; + case 'f': + if (add) { + if (in_arg >= argc) + goto error; + char *mode = modes[in_arg++]; + if(mode[0] == '!' && !(flags & MCP_OPERMODE)) //noflood flag also for overriders + goto error;//only allow opers + else if(mode[0] == '!') + mode++; + + if(mode[0] == '+' || mode[0] == '@') { + mode++; + } + char *p; + int count = 0, time = 0; + for(p = mode; p[0]; p++) { + if(p[0] == ':') { + char tmpchar = p[0]; + p[0] = '\0'; + count = strtoul(mode,0,10); + p[0] = tmpchar; + p++; + time = strtoul(p,0,10); + break; + } + } + if(count <= 0 || time <= 0 || count > 100 || time > 600) + goto error; + change->modes_set |= MODE_NOFLOOD; + safestrncpy(change->new_noflood, modes[in_arg - 1], sizeof(change->new_noflood)); + } else { + change->modes_clear |= MODE_NOFLOOD; + } + break; + case 'F': + if (add) { + if (in_arg >= argc) + goto error; + char *altchan = modes[in_arg++]; + struct chanNode *target; + if(!IsChannelName(altchan) || !(target = GetChannel(altchan))) + goto error; + if(!(flags & MCP_OPERMODE)) { + //check if the user has the permissions to use this channel as target + struct modeNode *mn; + struct userData *uData; + struct chanData *cData; + if(user && (mn = GetUserMode(target, user)) && (mn->modes & MODE_CHANOP)) { + //allow - user is opped on target channel + } else if(user && user->handle_info && + (uData = GetChannelUser(channel->channel_info, user->handle_info)) && + (cData = uData->channel) && + uData->access >= cData->lvlOpts[lvlGiveOps] + ) { + //allow - user has access to get op on the channel + } else + goto error; + } + change->modes_set |= MODE_ALTCHAN; + safestrncpy(change->new_altchan, altchan, sizeof(change->new_altchan)); + } else { + change->modes_clear |= MODE_ALTCHAN; + } + break; case 'U': + if (flags & MCP_NO_APASS) + goto error; if (add) { - if (in_arg >= argc) - goto error; - change->modes_set |= MODE_UPASS; - safestrncpy(change->new_upass, modes[in_arg++], sizeof(change->new_upass)); + if ((in_arg >= argc) + || keyncpy(change->new_upass, modes[in_arg++], sizeof(change->new_upass))) + goto error; + change->modes_set |= MODE_UPASS; } else { change->modes_clear |= MODE_UPASS; if (!(flags & MCP_UPASS_FREE)) { @@ -2281,11 +2753,13 @@ mod_chanmode_parse(struct chanNode *channel, char **modes, unsigned int argc, un } break; case 'A': + if (flags & MCP_NO_APASS) + goto error; if (add) { - if (in_arg >= argc) + if ((in_arg >= argc) + || keyncpy(change->new_apass, modes[in_arg++], sizeof(change->new_apass))) 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)) { @@ -2325,7 +2799,7 @@ mod_chanmode_parse(struct chanNode *channel, char **modes, unsigned int argc, un else if (channel->modes & MODE_UPASS) oplevel = base_oplevel + 1; else - oplevel = -1; + oplevel = MAXOPLEVEL; /* Check that oplevel is within bounds. */ if (oplevel > MAXOPLEVEL) @@ -2346,8 +2820,9 @@ mod_chanmode_parse(struct chanNode *channel, char **modes, unsigned int argc, un continue; if ((change->args[ch_arg].u.member = GetUserMode(channel, victim))) { - /* Apply the oplevel change */ - change->args[ch_arg].u.member->oplevel = oplevel; + /* Apply the oplevel change if the user is being (de-)opped */ + if (modes[0][ii] == 'o') + change->args[ch_arg].u.member->oplevel = oplevel; ch_arg++; } break; @@ -2366,6 +2841,11 @@ mod_chanmode_parse(struct chanNode *channel, char **modes, unsigned int argc, un change->modes_set &= ~(MODE_SECRET); change->modes_clear |= MODE_SECRET; } + if (change->modes_clear & MODE_REGISTERED) { + /* Horribly cheat by using the lock/unlock semantics. */ + LockChannel(channel); + channelList_append(&dead_channels, channel); + } return change; error: mod_chanmode_free(change); @@ -2434,17 +2914,24 @@ mod_chanmode_announce(struct userNode *who, struct chanNode *channel, struct mod DO_MODE_CHAR(INVITEONLY, 'i'); DO_MODE_CHAR(NOPRIVMSGS, 'n'); DO_MODE_CHAR(LIMIT, 'l'); + DO_MODE_CHAR(ACCESS, 'a'); + DO_MODE_CHAR(ALTCHAN, 'F'); + DO_MODE_CHAR(NOFLOOD, 'f'); DO_MODE_CHAR(DELAYJOINS, 'D'); DO_MODE_CHAR(REGONLY, 'r'); DO_MODE_CHAR(NOCOLORS, 'c'); DO_MODE_CHAR(NOCTCPS, 'C'); + DO_MODE_CHAR(NONOTICES, 'N'); + DO_MODE_CHAR(NOAMSGS, 'M'); + DO_MODE_CHAR(AUDITORIUM, 'u'); + DO_MODE_CHAR(SSLCHAN, 'S'); 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) + if (change->modes_clear & channel->modes & MODE_APASS) mod_chanmode_append(&chbuf, 'A', channel->apass); } for (arg = 0; arg < change->argc; ++arg) { @@ -2481,6 +2968,10 @@ 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(NONOTICES, 'N'); + DO_MODE_CHAR(NOAMSGS, 'M'); + DO_MODE_CHAR(AUDITORIUM, 'u'); + DO_MODE_CHAR(SSLCHAN, 'S'); DO_MODE_CHAR(REGISTERED, 'z'); #undef DO_MODE_CHAR if(change->modes_set & MODE_KEY) @@ -2493,6 +2984,14 @@ mod_chanmode_announce(struct userNode *who, struct chanNode *channel, struct mod sprintf(int_buff, "%d", change->new_limit); mod_chanmode_append(&chbuf, 'l', int_buff); } + if(change->modes_set & MODE_ACCESS) { + sprintf(int_buff, "%d", change->new_access); + mod_chanmode_append(&chbuf, 'a', int_buff); + } + if (change->modes_set & MODE_ALTCHAN) + mod_chanmode_append(&chbuf, 'F', change->new_altchan); + if (change->modes_set & MODE_NOFLOOD) + mod_chanmode_append(&chbuf, 'f', change->new_noflood); } for (arg = 0; arg < change->argc; ++arg) { if (change->args[arg].mode & MODE_REMOVE) @@ -2525,6 +3024,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++] = '-'; @@ -2536,6 +3038,9 @@ mod_chanmode_format(struct mod_chanmode *change, char *outbuff) DO_MODE_CHAR(INVITEONLY, 'i'); DO_MODE_CHAR(NOPRIVMSGS, 'n'); DO_MODE_CHAR(LIMIT, 'l'); + DO_MODE_CHAR(ACCESS, 'a'); + DO_MODE_CHAR(ALTCHAN, 'F'); + DO_MODE_CHAR(NOFLOOD, 'f'); DO_MODE_CHAR(KEY, 'k'); DO_MODE_CHAR(UPASS, 'U'); DO_MODE_CHAR(APASS, 'A'); @@ -2543,6 +3048,10 @@ 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(NONOTICES, 'N'); + DO_MODE_CHAR(NOAMSGS, 'M'); + DO_MODE_CHAR(AUDITORIUM, 'u'); + DO_MODE_CHAR(SSLCHAN, 'S'); DO_MODE_CHAR(REGISTERED, 'z'); #undef DO_MODE_CHAR } @@ -2559,113 +3068,89 @@ 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(NONOTICES, 'N'); + DO_MODE_CHAR(NOAMSGS, 'M'); + DO_MODE_CHAR(AUDITORIUM, 'u'); + DO_MODE_CHAR(SSLCHAN, 'S'); DO_MODE_CHAR(REGISTERED, 'z'); + 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); + DO_MODE_CHAR(ACCESS, 'a'), args_used += sprintf(args + args_used, " %d", change->new_access); + DO_MODE_CHAR(ALTCHAN, 'F'), args_used += sprintf(args + args_used, " %s", change->new_altchan); + DO_MODE_CHAR(NOFLOOD, 'f'), args_used += sprintf(args + args_used, " %s", change->new_noflood); + DO_MODE_CHAR(UPASS, 'U'), args_used += sprintf(args + args_used, " %s", change->new_upass); + DO_MODE_CHAR(APASS, 'A'), args_used += sprintf(args + args_used, " %s", change->new_apass); #undef DO_MODE_CHAR - 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; - 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; } static int clear_chanmode(struct chanNode *channel, const char *modes) { - unsigned int remove; + unsigned int cleared; - for (remove = 0; *modes; modes++) { + for (cleared = 0; *modes; modes++) { switch (*modes) { - case 'o': remove |= MODE_CHANOP; break; - case 'v': remove |= MODE_VOICE; break; - case 'p': remove |= MODE_PRIVATE; break; - case 's': remove |= MODE_SECRET; break; - case 'm': remove |= MODE_MODERATED; break; - case 't': remove |= MODE_TOPICLIMIT; break; - case 'i': remove |= MODE_INVITEONLY; break; - case 'n': remove |= MODE_NOPRIVMSGS; break; + case 'o': cleared |= MODE_CHANOP; break; + case 'v': cleared |= MODE_VOICE; break; + case 'p': cleared |= MODE_PRIVATE; break; + case 's': cleared |= MODE_SECRET; break; + case 'm': cleared |= MODE_MODERATED; break; + case 't': cleared |= MODE_TOPICLIMIT; break; + case 'i': cleared |= MODE_INVITEONLY; break; + case 'n': cleared |= MODE_NOPRIVMSGS; break; + case 'F': + cleared |= MODE_ALTCHAN; + channel->altchan[0] = '\0'; + break; + case 'f': + cleared |= MODE_NOFLOOD; + channel->noflood[0] = '\0'; + break; case 'k': - remove |= MODE_KEY; + cleared |= MODE_KEY; channel->key[0] = '\0'; break; case 'A': - remove |= MODE_APASS; + cleared |= MODE_APASS; channel->apass[0] = '\0'; break; case 'U': - remove |= MODE_UPASS; + cleared |= MODE_UPASS; channel->upass[0] = '\0'; break; case 'l': - remove |= MODE_LIMIT; + cleared |= MODE_LIMIT; channel->limit = 0; break; - case 'b': remove |= MODE_BAN; break; - case 'D': remove |= MODE_DELAYJOINS; break; - case 'r': remove |= MODE_REGONLY; break; - case 'c': remove |= MODE_NOCOLORS; break; - case 'C': remove |= MODE_NOCTCPS; break; - case 'z': remove |= MODE_REGISTERED; break; + case 'a': + cleared |= MODE_ACCESS; + channel->access = 0; + break; + case 'b': cleared |= MODE_BAN; break; + case 'D': cleared |= MODE_DELAYJOINS; break; + case 'r': cleared |= MODE_REGONLY; break; + case 'c': cleared |= MODE_NOCOLORS; break; + case 'C': cleared |= MODE_NOCTCPS; break; + case 'M': cleared |= MODE_NOAMSGS; break; + case 'u': cleared |= MODE_AUDITORIUM; break; + case 'S': cleared |= MODE_SSLCHAN; break; + case 'N': cleared |= MODE_NONOTICES; break; + case 'z': cleared |= MODE_REGISTERED; break; } } - if (!remove) + if (!cleared) return 1; /* Remove simple modes. */ - channel->modes &= ~remove; + channel->modes &= ~cleared; /* If removing bans, kill 'em all. */ - if ((remove & MODE_BAN) && channel->banlist.used) { + if ((cleared & MODE_BAN) && channel->banlist.used) { unsigned int i; for (i=0; ibanlist.used; i++) free(channel->banlist.list[i]); @@ -2673,8 +3158,8 @@ clear_chanmode(struct chanNode *channel, const char *modes) } /* Remove member modes. */ - if ((remove & (MODE_CHANOP | MODE_VOICE)) && channel->members.used) { - int mask = ~(remove & (MODE_CHANOP | MODE_VOICE)); + if ((cleared & (MODE_CHANOP | MODE_VOICE)) && channel->members.used) { + int mask = ~(cleared & (MODE_CHANOP | MODE_VOICE)); unsigned int i; for (i = 0; i < channel->members.used; i++) @@ -2689,9 +3174,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]) @@ -2700,20 +3186,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; } @@ -2722,9 +3200,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_notice_funcs; ii < newnum; ++ii) + notice_funcs[ii] = NULL; num_notice_funcs = newnum; } if (notice_funcs[numeric]) @@ -2733,46 +3212,12 @@ 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)); - - for (x = user->num_local+1; x < num_notice_funcs; x++) - memmove(notice_funcs+x-1, notice_funcs+x, sizeof(privmsg_func_t)); + if (!IsLocal(user) || user->num_local >= num_privmsg_funcs) + return; /* this really only works with users */ - 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--; -} - -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; nnum_local] = NULL; } static void @@ -2804,15 +3249,3 @@ send_burst(void) for (it = dict_first(channels); it; it = iter_next(it)) dict_insert(unbursted_channels, iter_key(it), iter_data(it)); } - -/* - * Oplevel parsing - */ -static int -parse_oplevel(char *str) -{ - int oplevel = 0; - while (isdigit(*str)) - oplevel = oplevel * 10 + *str++ - '0'; - return oplevel; -}