/* proto-p10.c - IRC protocol output
* Copyright 2000-2004 srvx Development Team
*
- * This file is part of srvx.
+ * This file is part of x3.
*
- * srvx is free software; you can redistribute it and/or modify
+ * x3 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.
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
+#include "nickserv.h"
+#include "chanserv.h"
+#include "hosthiding.h"
#include "proto-common.c"
/* Full commands. */
#define CMD_LIST "LIST"
#define CMD_LUSERS "LUSERS"
#define CMD_MAP "MAP"
+#define CMD_MARK "MARK"
#define CMD_MODE "MODE"
#define CMD_MOTD "MOTD"
#define CMD_NAMES "NAMES"
#define CMD_SERVSET "SERVSET"
#define CMD_SET "SET"
#define CMD_SETTIME "SETTIME"
+#define CMD_SHUN "SHUN"
#define CMD_SILENCE "SILENCE"
#define CMD_SQUERY "SQUERY"
#define CMD_SQUIT "SQUIT"
#define CMD_STATS "STATS"
#define CMD_SVSNICK "SVSNICK"
+#define CMD_SWHOIS "SWHOIS"
#define CMD_TIME "TIME"
#define CMD_TOPIC "TOPIC"
#define CMD_TRACE "TRACE"
#define TOK_EOB "EB"
#define TOK_EOB_ACK "EA"
#define TOK_ERROR "Y"
+#define TOK_EXEMPT "EX"
#define TOK_FAKEHOST "FA"
#define TOK_GET "GET"
#define TOK_GLINE "GL"
#define TOK_LIST "LIST"
#define TOK_LUSERS "LU"
#define TOK_MAP "MAP"
+#define TOK_MARK "MK"
#define TOK_MODE "M"
#define TOK_MOTD "MO"
#define TOK_NAMES "E"
#define TOK_SERVSET "SERVSET"
#define TOK_SET "SET"
#define TOK_SETTIME "SE"
+#define TOK_SHUN "SU"
#define TOK_SILENCE "U"
#define TOK_SQUERY "SQUERY"
#define TOK_SQUIT "SQ"
#define TOK_STATS "R"
#define TOK_SVSNICK "SN"
+#define TOK_SWHOIS "SW"
#define TOK_TIME "TI"
#define TOK_TOPIC "T"
#define TOK_TRACE "TR"
#define P10_LIST TYPE(LIST)
#define P10_LUSERS TYPE(LUSERS)
#define P10_MAP TYPE(MAP)
+#define P10_MARK TYPE(MARK)
#define P10_MODE TYPE(MODE)
#define P10_MOTD TYPE(MOTD)
#define P10_NAMES TYPE(NAMES)
#define P10_SERVSET TYPE(SERVSET)
#define P10_SET TYPE(SET)
#define P10_SETTIME TYPE(SETTIME)
+#define P10_SHUN TYPE(SHUN)
#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_SWHOIS TYPE(SWHOIS)
#define P10_TIME TYPE(TIME)
#define P10_TOPIC TYPE(TOPIC)
#define P10_TRACE TYPE(TRACE)
#define P10_WHO TYPE(WHO)
#define P10_WHOIS TYPE(WHOIS)
#define P10_WHOWAS TYPE(WHOWAS)
+#define P10_EXEMPT TYPE(EXEMPT)
/* 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 char *his_servername;
static char *his_servercomment;
+static int extended_accounts;
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];
+ char b64ip[25];
if (!user)
return;
- inttobase64(b64ip, ntohl(user->ip.s_addr), 6);
+ irc_p10_ntop(b64ip, &user->ip);
if (user->modes) {
int modelen;
char modes[32];
}
void
-irc_account(struct userNode *user, const char *stamp)
+irc_rename(struct userNode *user, const char *new_handle)
+{
+ if(extended_accounts)
+ putsock("%s " P10_ACCOUNT " %s M %s", self->numeric, user->numeric, new_handle);
+}
+
+void
+irc_delete(struct userNode *user)
+{
+ if(extended_accounts)
+ putsock("%s " P10_ACCOUNT " %s U", self->numeric, user->numeric);
+}
+
+void
+irc_account(struct userNode *user, const char *stamp, time_t timestamp)
{
- putsock("%s " P10_ACCOUNT " %s %s", self->numeric, user->numeric, stamp);
+ if(extended_accounts)
+ putsock("%s " P10_ACCOUNT " %s R %s %lu", self->numeric, user->numeric, stamp, timestamp);
+ else
+ putsock("%s " P10_ACCOUNT " %s %s %lu", self->numeric, user->numeric, stamp, timestamp);
}
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)
+irc_gline(struct server *srv, struct gline *gline, int silent)
+{
+ putsock("%s " P10_GLINE " %s +%s %ld :%s<%s> %s",
+ self->numeric, (srv ? srv->numeric : "*"), gline->target, gline->expires-now, silent ? "AUTO " : "", gline->issuer, gline->reason);
+}
+
+void
+irc_shun(struct server *srv, struct shun *shun)
{
- putsock("%s " P10_GLINE " %s +%s %ld :<%s> %s",
- self->numeric, (srv ? srv->numeric : "*"), gline->target, gline->expires-now, gline->issuer, gline->reason);
+ putsock("%s " P10_SHUN " %s +%s %ld :<%s> %s",
+ self->numeric, (srv ? srv->numeric : "*"), shun->target, shun->expires-now, shun->issuer, shun->reason);
}
void
putsock("%s " P10_GLINE " * -%s", self->numeric, mask);
}
+void
+irc_unshun(const char *mask)
+{
+ putsock("%s " P10_SHUN " * -%s", self->numeric, mask);
+}
+
static void
irc_burst(struct chanNode *chan)
{
void
irc_mode(struct userNode *from, struct chanNode *target, const char *modes)
{
+ call_channel_mode_funcs(from, target, (char **)&modes, 0);
putsock("%s " P10_MODE " %s %s "FMT_TIME_T,
(from ? from->numeric : self->numeric),
target->name, modes, target->timestamp);
}
+/* Added to allow services to mode users
+ 2005 - 8 - 10 by Life4Christ
+*/
+void
+irc_umode(struct userNode *target, const char *modes)
+{
+ putsock("%s " P10_MODE " %s %s ",self->numeric,target->nick, modes);
+}
+
+
void
irc_invite(struct userNode *from, struct userNode *who, struct chanNode *to)
{
putsock("%s " P10_INVITE " %s %s", from->numeric, who->nick, to->name);
}
+void
+irc_silence(struct userNode *who, const char *mask, int add)
+{
+ putsock("%s " P10_SILENCE " %s %s%s", self->numeric, who->numeric, add ? "+" : "-", mask);
+}
+
void
irc_join(struct userNode *who, struct chanNode *what)
{
putsock("%s " P10_SVSNICK " %s %s "FMT_TIME_T, from->uplink->numeric, target->numeric, newnick, now);
}
+void
+irc_swhois(struct userNode *from, struct userNode *target, const char *message)
+{
+ putsock("%s " P10_SWHOIS " %s %s%s", from->uplink->numeric, target->numeric, message ? ":" : "",
+ message ? message : "");
+
+}
+
void
irc_part(struct userNode *who, struct chanNode *what, const char *reason)
{
}
void
-irc_topic(struct userNode *who, struct chanNode *what, const char *topic)
+irc_topic(struct userNode *service, struct userNode *who, struct chanNode *what, const char *topic)
{
- putsock("%s " P10_TOPIC " %s :%s", who->numeric, what->name, topic);
+
+ int type = 4;
+ const char *str;
+ str = conf_get_data("server/type", RECDB_QSTRING);
+ if(str)
+ type = atoi(str);
+ else
+ type = 4;/* default to 040 style topics */
+
+ if (type == 5) {
+ putsock("%s " P10_TOPIC " %s %s " FMT_TIME_T " " FMT_TIME_T " :%s", service->numeric, what->name, who->nick, what->timestamp, now, topic);
+ } else {
+ who = service;
+ putsock("%s " P10_TOPIC " %s :%s", who->numeric, what->name, topic);
+ }
}
void
}
irc_numeric(from, RPL_WHOISUSER, "%s %s %s * :%s", who->nick, who->ident, who->hostname, who->info);
if (his_servername && his_servercomment)
- irc_numeric(from, RPL_WHOISSERVER, "%s %s :%s", who->nick, his_servername, his_servercomment);
+ 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 (IsOper(who)) {
+ irc_numeric(from, RPL_WHOISSERVER, "%s %s :%s", who->nick, who->uplink->name, who->uplink->description);
+ if (IsOper(who))
irc_numeric(from, RPL_WHOISOPERATOR, "%s :is a megalomaniacal power hungry tyrant", who->nick);
- }
irc_numeric(from, RPL_ENDOFWHOIS, "%s :End of /WHOIS list", who->nick);
return 1;
}
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)))
static CMD_FUNC(cmd_account)
{
struct userNode *user;
+ struct server *server;
+ struct handle_info *hi;
- if ((argc < 3) || !origin || !GetServerH(origin))
+ if ((argc < 3) || !origin || !(server = GetServerH(origin)))
return 0; /* Origin must be server. */
+
+ /* This next line appears to tremple origin.. why? */
user = GetUserN(argv[1]);
if (!user)
return 1; /* A QUIT probably passed the ACCOUNT. */
- call_account_func(user, argv[2]);
+
+ if(!extended_accounts) /* any need for this function without? */
+ return 1;
+
+ if(!strcmp(argv[2],"C"))
+ {
+ if((hi = loc_auth(argv[4], argv[5])))
+ {
+ /* Return a AC A */
+ putsock("%s " P10_ACCOUNT " %s A %s %lu", self->numeric, server->numeric , argv[3], hi->registered);
+
+ }
+ else
+ {
+ /* Return a AC D */
+ putsock("%s " P10_ACCOUNT " %s D %s", self->numeric, server->numeric , argv[3]);
+ }
+ return 1;
+ }
+ else if(!strcmp(argv[2],"R"))
+ call_account_func(user, argv[3]);
+ else
+ call_account_func(user, argv[2]); /* For backward compatability */
return 1;
}
extern int rel_age;
char modes[MAXLEN], *members = "";
static char exemptlist[MAXLEN], banlist[MAXLEN];
- unsigned int next = 3;
- unsigned int res = 1;
+ unsigned int next = 3, res = 1;
int ctype = 0, echeck = 0, bcheck = 0;
struct chanNode *cNode;
struct userNode *un;
struct modeNode *mNode;
long mode;
+ int oplevel = -1;
char *user, *end, sep;
time_t in_timestamp;
char* parm = NULL;
case '+': {
const char *pos;
int n_modes;
- for (pos=argv[next], n_modes = 1; *pos; pos++) {
- if ((*pos == 'k') || (*pos == 'l'))
+ for (pos=argv[next], n_modes = 1; *pos; pos++)
+ if ((*pos == 'k') || (*pos == 'l') || (*pos == 'A')
+ || (*pos == 'U'))
n_modes++;
- }
unsplit_string(argv+next, n_modes, modes);
next += n_modes;
break;
if (ctype == 1) {
if (bcheck == 0) {
/* strip % char off start of very first ban */
- strncat(banlist, strtok(parm, "%"), sizeof(banlist) - 1 - strlen(banlist));
- strncat(banlist, " ", sizeof(banlist) - 1 - strlen(banlist));
+ if (strlen(parm) > 1) {
+ strncat(banlist, strtok(parm, "%"), sizeof(banlist) - 1 - strlen(banlist));
+ strncat(banlist, " ", sizeof(banlist) - 1 - strlen(banlist));
+ }
bcheck = 1;
} else {
strncat(banlist, parm, sizeof(banlist) - 1 - strlen(banlist));
next++;
break;
}
- default: {
- members = argv[next++];
- break;
- }
+ default: members = argv[next++]; break;
}
}
-
in_timestamp = atoi(argv[2]);
if ((cNode = dict_find(unbursted_channels, argv[1], NULL))) {
cNode->timestamp = in_timestamp;
dict_remove(unbursted_channels, cNode->name);
irc_burst(cNode);
}
-
- /* Burst channel members in now. */
cNode = AddChannel(argv[1], in_timestamp, modes, banlist, exemptlist);
+ /* Burst channel members in now. */
for (user = members, sep = *members, mode = 0; sep; user = end) {
for (end = user + 3; isalnum(*end) || *end == '[' || *end == ']'; end++) ;
sep = *end++; end[-1] = 0;
if (sep == ':') {
mode = 0;
while ((sep = *end++)) {
- if (sep == 'o')
+ if (sep == 'o') {
mode |= MODE_CHANOP;
- else if (sep == 'h')
+ oplevel = -1;
+ } else if (sep == 'h') {
mode |= MODE_HALFOP;
- 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;
{
struct chanNode *cn;
time_t chan_ts, topic_ts;
+ struct userNode *user;
if (argc < 3)
return 0;
log_module(MAIN_LOG, LOG_ERROR, "Unable to find channel %s whose topic is being set", argv[1]);
return 0;
}
- if (argc >= 5) {
- /* Looks like an Asuka style topic burst. */
+
+
+ if (argc == 5) { /* Asuka / Topic Bursting IRCu's */
+ user = GetUserH(origin);
chan_ts = atoi(argv[2]);
topic_ts = atoi(argv[3]);
- } else {
+ } else if (argc >= 6) { /* Nefarious 0.5.0 */
+ user = GetUserH(strtok(argv[2], "!"));
+ chan_ts = atoi(argv[3]);
+ topic_ts = atoi(argv[4]);
+ } else { /* Regular IRCu (No Topic Bursting)*/
+ user = GetUserH(origin);
chan_ts = cn->timestamp;
topic_ts = now;
}
- SetChannelTopic(cn, GetUserH(origin), argv[argc-1], 0);
+
+ SetChannelTopic(cn, user, user, argv[argc-1], 0);
cn->topic_time = topic_ts;
return 1;
}
{
if (argc < 6)
return 0;
- gline_add(origin, argv[3], atoi(argv[4])-now, argv[5], now, 0);
+ gline_add(origin, argv[3], atoi(argv[4])-now, argv[5], now, 0, 0);
+ return 1;
+}
+
+static CMD_FUNC(cmd_num_shun)
+{
+ if (argc < 6)
+ return 0;
+ shun_add(origin, argv[3], atoi(argv[4])-now, argv[5], now, 0);
return 1;
}
return 1;
}
+static CMD_FUNC(cmd_silence)
+{
+ struct userNode *user;
+ char *mask;
+ unsigned int i;
+
+ if (argc < 2)
+ return 0;
+
+ user = GetUserN(argv[1]);
+
+ /* Sanity, go nuts if this happens */
+ if (!user)
+ return 0;
+
+ /* We can safely ignore this if a user adding a silence is not
+ * ignored. However this brings up a TODO. If a user logs in and
+ * they have silences on the IRCd then we need to set them here
+ * somehow
+ */
+ if (!user->handle_info)
+ return 1;
+
+ mask = strdup(argv[2]);
+
+ if (*mask == '-') {
+ for (i=0; i<user->handle_info->ignores->used; i++) {
+ if (!irccasecmp(mask+1, user->handle_info->ignores->list[i]))
+ user->handle_info->ignores->list[i] = user->handle_info->ignores->list[--user->handle_info->ignores->used];
+ }
+ } else {
+ for (i=0; i<user->handle_info->ignores->used; i++) {
+ if (!strcmp(mask+1, user->handle_info->ignores->list[i]))
+ return 1; /* Already on the users NickServ ignore list, safely ignore */
+ }
+
+ string_list_append(user->handle_info->ignores, mask+1);
+ }
+
+ return 1;
+}
+
static CMD_FUNC(cmd_kick)
{
if (argc < 3)
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);
+ gline_add(origin, argv[2]+1, strtoul(argv[3], NULL, 0), argv[argc-1], now, 0, 0);
return 1;
} else if (argv[2][0] == '-') {
gline_remove(argv[2]+1, 0);
return 0;
}
+static CMD_FUNC(cmd_shun)
+{
+ if (argc < 3)
+ return 0;
+ if (argv[2][0] == '+') {
+ if (argc < 5)
+ return 0;
+ shun_add(origin, argv[2]+1, strtoul(argv[3], NULL, 0), argv[argc-1], now, 0);
+ return 1;
+ } else if (argv[2][0] == '-') {
+ shun_remove(argv[2]+1, 0);
+ return 1;
+ } else
+ return 0;
+}
+
static CMD_FUNC(cmd_svsnick)
{
struct userNode *target, *dest;
char numer[COMBO_NUMERIC_LEN+1];
/* read config items */
+ str = conf_get_data("server/extended_accounts", RECDB_QSTRING);
+ extended_accounts = str ? enabled_string(str) : 1;
str = conf_get_data("server/ping_freq", RECDB_QSTRING);
ping_freq = str ? ParseInterval(str) : 120;
str = conf_get_data("server/ping_timeout", RECDB_QSTRING);
dict_insert(irc_func_dict, TOK_TOPIC, cmd_topic);
dict_insert(irc_func_dict, CMD_AWAY, cmd_away);
dict_insert(irc_func_dict, TOK_AWAY, cmd_away);
- dict_insert(irc_func_dict, CMD_SILENCE, cmd_dummy);
- dict_insert(irc_func_dict, TOK_SILENCE, cmd_dummy);
+ dict_insert(irc_func_dict, CMD_SILENCE, cmd_silence);
+ dict_insert(irc_func_dict, TOK_SILENCE, cmd_silence);
dict_insert(irc_func_dict, CMD_KICK, cmd_kick);
dict_insert(irc_func_dict, TOK_KICK, cmd_kick);
dict_insert(irc_func_dict, CMD_SQUIT, cmd_squit);
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_SWHOIS, cmd_dummy);
+ dict_insert(irc_func_dict, TOK_SWHOIS, cmd_dummy);
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);
dict_insert(irc_func_dict, TOK_GLINE, cmd_gline);
+ dict_insert(irc_func_dict, CMD_SHUN, cmd_shun);
+ dict_insert(irc_func_dict, TOK_SHUN, cmd_shun);
dict_insert(irc_func_dict, CMD_OPMODE, cmd_opmode);
dict_insert(irc_func_dict, TOK_OPMODE, cmd_opmode);
dict_insert(irc_func_dict, CMD_CLEARMODE, cmd_clearmode);
dict_insert(irc_func_dict, TOK_WALLOPS, cmd_dummy);
dict_insert(irc_func_dict, CMD_WALLHOPS, cmd_dummy);
dict_insert(irc_func_dict, TOK_WALLHOPS, cmd_dummy);
+ dict_insert(irc_func_dict, TOK_WALLUSERS, cmd_dummy);
+ /* Ignore dnsbl exemptions */
+ dict_insert(irc_func_dict, TOK_EXEMPT, cmd_dummy);
+ dict_insert(irc_func_dict, TOK_MARK, cmd_dummy);
+ /* Ignore privs */
+ dict_insert(irc_func_dict, TOK_PRIVS, cmd_dummy);
+ /* Ignore remote luser */
+ dict_insert(irc_func_dict, TOK_LUSERS, cmd_dummy);
/* We have reliable clock! Always! Wraaa! */
dict_insert(irc_func_dict, CMD_SETTIME, cmd_dummy);
dict_insert(irc_func_dict, TOK_SETTIME, cmd_dummy);
+
+ /* ignore /trace and /motd commands targetted at us */
+ dict_insert(irc_func_dict, TOK_TRACE, cmd_dummy);
+ dict_insert(irc_func_dict, TOK_MOTD, cmd_dummy);
+
/* handle topics */
dict_insert(irc_func_dict, "331", cmd_num_topic);
dict_insert(irc_func_dict, "332", cmd_num_topic);
/* ban list resetting */
/* "stats g" responses */
dict_insert(irc_func_dict, "247", cmd_num_gline);
+ dict_insert(irc_func_dict, "542", cmd_num_shun);
dict_insert(irc_func_dict, "219", cmd_dummy); /* "End of /STATS report" */
/* other numeric responses we might get */
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?) */
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, const char *hostname)
+AddService(const char *nick, const char *modes, const char *desc, const char *hostname)
{
char numeric[COMBO_NUMERIC_LEN+1];
int local_num = get_local_numeric();
if (!hostname)
hostname = self->name;
make_numeric(self, local_num, numeric);
- return AddUser(self, nick, nick, hostname, "+oik", numeric, desc, now, "AAAAAA");
+ /* TODO: Make these modes part of the conf file */
+ return AddUser(self, nick, nick, hostname, modes ? modes : "+oik", 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;
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);
+
+ if (irc_in_addr_is_ipv4(uNode->ip)) {
+ make_virtip((char*)irc_ntoa(&uNode->ip), (char*)irc_ntoa(&uNode->ip), uNode->cryptip);
+ make_virthost((char*)irc_ntoa(&uNode->ip), uNode->hostname, uNode->crypthost);
+ } else if (irc_in_addr_is_ipv6(uNode->ip)) {
+ make_ipv6virthost((char*)irc_ntoa(&uNode->ip), uNode->hostname, uNode->crypthost);
+ }
+
uNode->timestamp = timestamp;
modeList_init(&uNode->channels);
uNode->uplink = uplink;
for (n=0; n<nuf_used; n++)
if (nuf_list[n](uNode))
break;
+
+ if ((uNode->loc == 1) && (uNode->handle_info))
+ send_func_list(uNode);
+
return uNode;
}
{
unsigned int n;
+ verify(user);
+
/* mark them as dead, in case anybody cares */
user->dead = 1;
+ /* remove pending adduser commands */
+ wipe_adduser_pending(NULL, user);
+
/* remove user from all channels */
while (user->channels.used > 0)
DelChannelUser(user, user->channels.list[user->channels.used-1]->channel, false, 0);
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++; \
+
+ call_user_mode_funcs(user, mode_change);
+
+ while (*word != ' ' && *word) word++;
+ while (*word == ' ') word++;
while (1) {
#define do_user_mode(FLAG) do { if (add) user->modes |= FLAG; else user->modes &= ~FLAG; } while (0)
switch (*mode_change++) {
case '+': 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);
+ if(!IsOper(user)) { /* Dont re-oper an oper */
+ userList_append(&curr_opers, user);
+ call_oper_funcs(user);
+ }
} else {
userList_remove(&curr_opers, user);
}
+ do_user_mode(FLAGS_OPER);
break;
case 'O': do_user_mode(FLAGS_LOCOP); break;
case 'i': do_user_mode(FLAGS_INVISIBLE);
}
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 'Q': do_chan_mode(MODE_NOQUITMSGS); break;
case 'T': do_chan_mode(MODE_NOAMSG); break;
case 'O': do_chan_mode(MODE_OPERSONLY); break;
-// uncomment this when ssl is enabled on the network.
-// case 'z': do_chan_mode(MODE_SSLONLY); break;
+ case 'Z': do_chan_mode(MODE_SSLONLY); break;
+ case 'L': do_chan_mode(MODE_HIDEMODE); 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 'e':
if (!(flags & MCP_ALLOW_OVB))
change->args[ch_arg].mode = MODE_EXEMPT;
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 'h': 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 = GetUserH(modes[in_arg++]);
if (!victim)
continue;
- if ((change->args[ch_arg].member = GetUserMode(channel, victim)))
+ if ((change->args[ch_arg].u.member = GetUserMode(channel, victim)))
+ {
+ /* Apply the oplevel change */
+ change->args[ch_arg].u.member->oplevel = oplevel;
ch_arg++;
+ }
break;
}
default:
DO_MODE_CHAR(NOQUITMSGS, 'Q');
DO_MODE_CHAR(NOAMSG, 'T');
DO_MODE_CHAR(OPERSONLY, 'O');
- // uncomment this for ssl support
- //DO_MODE_CHAR(SSLONLY, 'z');
+ DO_MODE_CHAR(REGISTERED, 'z');
+ DO_MODE_CHAR(SSLONLY, 'Z');
+ DO_MODE_CHAR(HIDEMODE, 'L');
#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;
case MODE_EXEMPT:
- mod_chanmode_append(&chbuf, 'e', change->args[arg].hostmask);
+ mod_chanmode_append(&chbuf, 'e', 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_HALFOP)
- mod_chanmode_append(&chbuf, 'h', change->args[arg].member->user->numeric);
+ mod_chanmode_append(&chbuf, 'h', 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(NOQUITMSGS, 'Q');
DO_MODE_CHAR(NOAMSG, 'T');
DO_MODE_CHAR(OPERSONLY, 'O');
- // uncomment this for ssl support
- //DO_MODE_CHAR(SSLONLY, 'z');
+ DO_MODE_CHAR(REGISTERED, 'z');
+ DO_MODE_CHAR(SSLONLY, 'Z');
+ DO_MODE_CHAR(HIDEMODE, 'L');
#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;
case MODE_EXEMPT:
- mod_chanmode_append(&chbuf, 'e', change->args[arg].hostmask);
+ mod_chanmode_append(&chbuf, 'e', 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_HALFOP)
- mod_chanmode_append(&chbuf, 'h', change->args[arg].member->user->numeric);
+ mod_chanmode_append(&chbuf, 'h', 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(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(NOQUITMSGS, 'Q');
DO_MODE_CHAR(NOAMSG, 'T');
DO_MODE_CHAR(OPERSONLY, 'O');
- // uncomment this for ssl support
- //DO_MODE_CHAR(SSLONLY, 'z');
+ DO_MODE_CHAR(REGISTERED, 'z');
+ DO_MODE_CHAR(SSLONLY, 'Z');
+ DO_MODE_CHAR(HIDEMODE, 'L');
#undef DO_MODE_CHAR
}
if (change->modes_set) {
DO_MODE_CHAR(NOQUITMSGS, 'Q');
DO_MODE_CHAR(NOAMSG, 'T');
DO_MODE_CHAR(OPERSONLY, 'O');
- // uncomment this for ssl support
- //DO_MODE_CHAR(SSLONLY, 'z');
+ DO_MODE_CHAR(REGISTERED, 'z');
+ DO_MODE_CHAR(SSLONLY, 'Z');
+ DO_MODE_CHAR(HIDEMODE, 'L');
#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);
+
+ 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 'e': remove |= MODE_EXEMPT; 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 'S': remove |= MODE_STRIPCOLOR; break;
case 'M': remove |= MODE_MODUNREG; break;
case 'Q': remove |= MODE_NOQUITMSGS; break;
case 'T': remove |= MODE_NOAMSG; break;
case 'O': remove |= MODE_OPERSONLY; break;
+ case 'z': remove |= MODE_REGISTERED; break;
+ case 'Z': remove |= MODE_SSLONLY; break;
+ case 'L': remove |= MODE_HIDEMODE; break;
}
}
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;
+}
+