#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_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
#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
static struct dict *unbursted_channels;
static const char *his_servername;
static const char *his_servercomment;
+static struct channelList dead_channels;
/* These correspond to 1 << X: 012345678901234567 */
-const char irc_user_mode_chars[] = "o iw dkgn I";
+const char irc_user_mode_chars[] = "o iw dkgn x I";
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);
}
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);
+ putsock("%s " P10_FAKEHOST " %s %s %s%s", self->numeric, user->numeric, ident, host, force ? " FORCE" : "");
}
void
irc_gline(struct server *srv, struct gline *gline)
{
if (gline->lastmod)
- putsock("%s " P10_GLINE " %s +%s %lu %lu :%s",
- self->numeric, (srv ? srv->numeric : "*"), gline->target, (unsigned long)(gline->expires-now), (unsigned long)gline->lastmod, gline->reason);
+ putsock("%s " P10_GLINE " %s +%s %lu %lu %lu :%s",
+ self->numeric, (srv ? srv->numeric : "*"), gline->target, gline->expires, gline->lastmod, gline->lifetime, gline->reason);
else
putsock("%s " P10_GLINE " %s +%s %lu :%s",
- self->numeric, (srv ? srv->numeric : "*"), gline->target, (unsigned long)(gline->expires-now), gline->reason);
+ self->numeric, (srv ? srv->numeric : "*"), gline->target, gline->expires, gline->reason);
}
void
void
irc_ungline(const char *mask)
{
- putsock("%s " P10_GLINE " * -%s", self->numeric, mask);
+ putsock("%s " P10_GLINE " * -%s %lu", self->numeric, mask, now);
}
/* Return negative if *(struct modeNode**)pa is "less than" pb,
* Within those sets, ordering is arbitrary.
*/
static int
-modeNode_sort(const void *pa, const void *pb)
+modeNode_sort_p10(const void *pa, const void *pb)
{
struct modeNode *a = *(struct modeNode**)pa;
struct modeNode *b = *(struct modeNode**)pb;
burst_line[pos++] = ' ';
/* sort the users for oplevel-sending purposes */
- qsort(chan->members.list, chan->members.used, sizeof(chan->members.list[0]), modeNode_sort);
+ qsort(chan->members.list, chan->members.used, sizeof(chan->members.list[0]), modeNode_sort_p10);
/* dump the users */
for (n=0; n<chan->members.used; n++) {
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);
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
return 1;
}
- if (IsFakeHost(who) && IsHiddenHost(who))
+ 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);
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;
}
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;
}
static CMD_FUNC(cmd_topic)
{
struct chanNode *cn;
- unsigned long chan_ts, topic_ts;
+ unsigned long topic_ts;
if (argc < 3)
return 0;
}
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);
static CMD_FUNC(cmd_num_gline)
{
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;
}
static CMD_FUNC(cmd_gline)
{
+#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);
return 1;
}
+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_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)
{
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
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);
}
for (i=0; i<dead_users.used; i++)
free_user(dead_users.list[i]);
dead_users.used = 0;
+ for (i=0; i<dead_channels.used; i++)
+ UnlockChannel(dead_channels.list[i]);
+ dead_channels.used = 0;
}
return res;
}
{
char numeric[COMBO_NUMERIC_LEN+1];
int local_num = get_local_numeric();
- unsigned long timestamp = now;
struct userNode *old_user = GetUserH(nick);
if (!modes)
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);
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;
}
case 's': do_chan_mode(MODE_SECRET); break;
case 't': do_chan_mode(MODE_TOPICLIMIT); break;
case 'z':
- if (!(flags & MCP_REGISTERED)) {
+ if (!(flags & MCP_REGISTERED))
do_chan_mode(MODE_REGISTERED);
- } else {
- mod_chanmode_free(change);
- return NULL;
- }
break;
#undef do_chan_mode
case 'l':
}
break;
case 'U':
+ if (flags & MCP_NO_APASS)
+ goto error;
if (add)
{
if ((in_arg >= argc)
}
break;
case 'A':
+ if (flags & MCP_NO_APASS)
+ goto error;
if (add) {
if ((in_arg >= argc)
|| keyncpy(change->new_apass, modes[in_arg++], sizeof(change->new_apass)))
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);
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++] = '-';
DO_MODE_CHAR(NOCOLORS, 'c');
DO_MODE_CHAR(NOCTCPS, 'C');
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(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;
}
if (numeric >= num_notice_funcs) {
int newnum = numeric + 8, ii;
notice_funcs = realloc(notice_funcs, newnum*sizeof(privmsg_func_t));
- for (ii = num_privmsg_funcs; ii < newnum; ++ii)
- privmsg_funcs[ii] = NULL;
+ for (ii = num_notice_funcs; ii < newnum; ++ii)
+ notice_funcs[ii] = NULL;
num_notice_funcs = newnum;
}
if (notice_funcs[numeric])