/* proto-p10.c - IRC protocol output
- * Copyright 2000-2004 srvx Development Team
+ * Copyright 2000-2006 srvx Development Team
*
- * This program is free software; you can redistribute it and/or modify
+ * This file is part of srvx.
+ *
+ * srvx is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version. Important limitations are
- * listed in the COPYING file that accompanies this software.
+ * (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, email srvx-maintainers@srvx.net.
+ * along with srvx; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
#include "proto-common.c"
#define CMD_EOB "END_OF_BURST"
#define CMD_EOB_ACK "EOB_ACK"
#define CMD_ERROR "ERROR"
+#define CMD_FAKEHOST "FAKE"
#define CMD_GET "GET"
#define CMD_GLINE "GLINE"
#define CMD_HASH "HASH"
#define TOK_EOB "EB"
#define TOK_EOB_ACK "EA"
#define TOK_ERROR "Y"
+#define TOK_FAKEHOST "FA"
#define TOK_GET "GET"
#define TOK_GLINE "GL"
#define TOK_HASH "HASH"
#define P10_EOB TYPE(EOB)
#define P10_EOB_ACK TYPE(EOB_ACK)
#define P10_ERROR TYPE(ERROR)
+#define P10_FAKEHOST TYPE(FAKEHOST)
#define P10_GET TYPE(GET)
#define P10_GLINE TYPE(GLINE)
#define P10_HASH TYPE(HASH)
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 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. */
struct server*
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 %li %li 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 %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);
}
}
+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];
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_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))
{
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)
+{
+ 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
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 " FMT_TIME_T ".%06u", self->numeric, who, orig_ts, diff, now.tv_sec, (unsigned)now.tv_usec);
+}
+
void
irc_pass(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 %ld %ld :%s",
+ self->numeric, (srv ? srv->numeric : "*"), gline->target, gline->expires-now, gline->lastmod, gline->reason);
+ else
+ putsock("%s " P10_GLINE " %s +%s %ld :%s",
+ self->numeric, (srv ? srv->numeric : "*"), gline->target, gline->expires-now, gline->reason);
}
void
struct modeNode *mn;
struct banNode *bn;
long last_mode=-1;
+ unsigned int first_ban;
unsigned int n;
- if (!chan->members.used)
- return;
base_len = sprintf(burst_line, "%s " P10_BURST " %s " FMT_TIME_T " ",
self->numeric, chan->name, 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 */
if ((n+1)<chan->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; n<chan->banlist.used; n++) {
+ for (n=0; n<chan->banlist.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);
{
const char *numeric;
struct modeNode *mn = GetUserMode(channel, who);
- numeric = (mn && (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);
}
}
}
+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 = 0; i < who->channels.used; i++)
+ {
+ 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));
+
+ irc_numeric(from, RPL_ENDOFWHOIS, "%s :End of /WHOIS list", who->nick);
+ return 1;
+}
+
static CMD_FUNC(cmd_server)
{
struct server *srv;
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)
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)))
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;
struct userNode *un;
struct modeNode *mNode;
long mode;
+ int oplevel = -1;
char *user, *end, sep;
time_t in_timestamp;
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++;
unsplit_string(argv+next, n_modes, modes);
next += n_modes;
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)
res = 0;
continue;
}
- if ((mNode = AddChannelUser(un, cNode)))
+ if ((mNode = AddChannelUser(un, cNode))) {
mNode->modes = mode;
+ mNode->oplevel = oplevel;
+ }
}
return res;
static CMD_FUNC(cmd_num_topic)
{
- static struct chanNode *cn;
+ struct chanNode *cn;
if (!argv[0])
return 0; /* huh? */
static CMD_FUNC(cmd_num_gline)
{
+ time_t 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;
}
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)
static CMD_FUNC(cmd_gline)
{
+ time_t 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);
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; nn<dead_users.used; nn++)
inttobase64(numer, (numnick << 12) + (usermask & 0x00fff), 3);
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) {
dict_insert(irc_func_dict, TOK_NICK, cmd_nick);
dict_insert(irc_func_dict, CMD_ACCOUNT, cmd_account);
dict_insert(irc_func_dict, TOK_ACCOUNT, cmd_account);
+ dict_insert(irc_func_dict, CMD_FAKEHOST, cmd_fakehost);
+ dict_insert(irc_func_dict, TOK_FAKEHOST, cmd_fakehost);
dict_insert(irc_func_dict, CMD_PASS, cmd_pass);
dict_insert(irc_func_dict, TOK_PASS, cmd_pass);
dict_insert(irc_func_dict, CMD_PING, cmd_ping);
dict_insert(irc_func_dict, "331", cmd_num_topic);
dict_insert(irc_func_dict, "332", cmd_num_topic);
dict_insert(irc_func_dict, "333", cmd_num_topic);
+ dict_insert(irc_func_dict, "345", cmd_dummy); /* blah has been invited to blah */
dict_insert(irc_func_dict, "432", cmd_error_nick); /* Erroneus [sic] nickname */
/* ban list resetting */
/* "stats g" responses */
dict_insert(irc_func_dict, "401", cmd_dummy); /* target left network */
dict_insert(irc_func_dict, "403", cmd_dummy); /* no such channel */
dict_insert(irc_func_dict, "404", cmd_dummy); /* cannot send to channel */
+ dict_insert(irc_func_dict, "439", cmd_dummy); /* target change too fast */
dict_insert(irc_func_dict, "441", cmd_dummy); /* target isn't on that channel */
dict_insert(irc_func_dict, "442", cmd_dummy); /* you aren't on that channel */
dict_insert(irc_func_dict, "443", cmd_dummy); /* is already on channel (after invite?) */
dict_insert(irc_func_dict, "461", cmd_dummy); /* Not enough parameters (after TOPIC w/ 0 args) */
+ dict_insert(irc_func_dict, "467", cmd_dummy); /* Channel key already set */
num_privmsg_funcs = 16;
privmsg_funcs = malloc(sizeof(privmsg_func_t)*num_privmsg_funcs);
parse_foreach(char *target_list, foreach_chanfunc cf, foreach_nonchan nc, foreach_userfunc uf, foreach_nonuser nu, void *data)
{
char *j, old;
+
do {
j = target_list;
while (*j != 0 && *j != ',')
j++;
old = *j;
*j = 0;
+
if (IsChannelName(target_list)
|| (target_list[0] == '0' && target_list[1] == '\0')) {
struct chanNode *chan = GetChannel(target_list);
+
if (chan) {
if (cf)
cf(chan, data);
}
struct userNode *
-AddService(const char *nick, const char *desc)
+AddLocalUser(const char *nick, const char *ident, const char *hostname, const char *desc, const char *modes)
{
char numeric[COMBO_NUMERIC_LEN+1];
int local_num = get_local_numeric();
time_t timestamp = now;
struct userNode *old_user = GetUserH(nick);
+ if (!modes)
+ modes = "+oik";
if (old_user) {
if (IsLocal(old_user))
return old_user;
log_module(MAIN_LOG, LOG_ERROR, "Unable to allocate numnick for service %s", nick);
return 0;
}
+ if (!hostname)
+ hostname = self->name;
make_numeric(self, local_num, numeric);
- return AddUser(self, nick, nick, self->name, "+oik", numeric, desc, now, "AAAAAA");
+ return AddUser(self, nick, ident, hostname, modes, numeric, desc, now, "AAAAAA");
}
struct userNode *
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;
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)
{
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);
return NULL;
}
- if (!is_valid_nick(nick)) {
+ dummy = modes && modes[0] == '*';
+ if (dummy) {
+ ++modes;
+ } else if (!is_valid_nick(nick)) {
log_module(MAIN_LOG, LOG_WARNING, "AddUser(%p, %s, ...): invalid nickname detected.", uplink, nick);
return NULL;
}
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;
modeList_init(&uNode->channels);
uNode->uplink = uplink;
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;
{
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. */
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
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++) {
userList_remove(&curr_opers, user);
}
break;
- case 'O': do_user_mode(FLAGS_LOCOP); break;
case 'i': do_user_mode(FLAGS_INVISIBLE);
if (add)
invis_clients++;
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;
+ 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) {
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;
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) {
}
}
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;
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> */
+ *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)
victim = GetUserN(modes[in_arg++]);
else
victim = GetUserH(modes[in_arg++]);
- if ((change->args[ch_arg].member = GetUserMode(channel, victim)))
+ if (!victim)
+ continue;
+ 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:
+ if (!(flags & MCP_FROM_SERVER))
+ goto error;
+ break;
}
}
change->argc = ch_arg; /* in case any turned out to be ignored */
struct modeNode *mn;
char int_buff[32], mode = '\0';
+ assert(change->argc <= change->alloc_argc);
memset(&chbuf, 0, sizeof(chbuf));
chbuf.channel = channel;
chbuf.actor = who;
chbuf.chname_len = strlen(channel->name);
- if ((mn = GetUserMode(channel, who)) && (mn->modes & MODE_CHANOP))
+
+ mn = GetUserMode(channel, who);
+ if ((mn && (mn->modes & MODE_CHANOP)) || off_channel)
chbuf.is_chanop = 1;
/* First remove modes */
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))
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;
}
}
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);
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;
}
}
mod_chanmode_format(struct mod_chanmode *change, char *outbuff)
{
unsigned int used = 0;
+ assert(change->argc <= change->alloc_argc);
if (change->modes_clear) {
outbuff[used++] = '-';
#define DO_MODE_CHAR(BIT, CHAR) if (change->modes_clear & MODE_##BIT) outbuff[used++] = CHAR
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) {
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;
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;
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;
}
}
privmsg_funcs[numeric] = handler;
}
+void
+unreg_privmsg_func(struct userNode *user, privmsg_func_t handler)
+{
+ unsigned int x;
+
+ if (!user || handler)
+ 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--;
+}
+
+
void
reg_notice_func(struct userNode *user, privmsg_func_t handler)
{
notice_funcs[numeric] = handler;
}
+void
+unreg_notice_func(struct userNode *user, privmsg_func_t handler)
+{
+ 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));
+
+ 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)
{
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;
+}