-/* IRCParser.c - NeonServ v5.5
+/* IRCParser.c - NeonServ v5.6
* Copyright (C) 2011-2012 Philipp Kreil (pk910)
*
* This program is free software: you can redistribute it and/or modify
#include "ModeNode.h"
#include "tools.h"
#include "bots.h"
+#include "timeq.h"
+#include "ConfigParser.h"
+#include "statistics.h"
+#include "log.h"
struct irc_cmd *irc_commands = NULL;
//static struct UserNode *registering_users = NULL;
int statistics_network_users = 0;
int statistics_network_channels = 0;
-static void parse_line(struct ClientSocket *client, char *line);
static void register_irc_function(char *command, irc_cmd_t *func);
static void parse_raw(struct ClientSocket *client, char *from, char *cmd, char **argv, int argc);
-int parse_lines(struct ClientSocket *client, char *lines, int len) {
- int i, used = 0;
- char *line = lines;
- for(i = 0; i < len; i++) {
- if(lines[i] == '\r') //just zero it out :D
- lines[i] = 0;
- if(lines[i] == '\n') {
- lines[i] = 0;
- parse_line(client, line);
- line = lines+(i+1);
- used = i+1;
- }
- }
- return used;
-}
+struct OplessRejoinUserbot {
+ char *nick;
+ char *auth;
+};
-static void parse_line(struct ClientSocket *client, char *line) {
+struct OplessRejoinBot {
+ unsigned char is_client;
+ union {
+ struct ClientSocket *client;
+ struct OplessRejoinUserbot *userbot;
+ } bot;
+ struct OplessRejoinBot *next;
+};
+
+void parse_line(struct ClientSocket *client, char *line) {
int argc = 0;
char *argv[MAXNUMPARAMS];
#ifdef HAVE_THREADS
- putlog(LOGLEVEL_RAW, "[%d recv %lu] %s\n", getCurrentThreadID(), (unsigned long) strlen(line), line);
+ printf_log("main", LOG_IRCRAW, "[%d recv %lu] %s\n", getCurrentThreadID(), (unsigned long) strlen(line), line);
#else
- putlog(LOGLEVEL_RAW, "[recv %lu] %s\n", (unsigned long) strlen(line), line);
+ printf_log("main", LOG_IRCRAW, "[recv %lu] %s\n", (unsigned long) strlen(line), line);
#endif
if(line[0] == ':')
line++;
struct irc_cmd *irc_cmd = malloc(sizeof(*irc_cmd));
if (!irc_cmd)
{
- perror("malloc() failed");
+ printf_log("main", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__);
return;
}
irc_cmd->cmd = command;
if(!irc_cmd) {
event_raw(client, from, cmd, argv, argc);
} else if(!ret) {
- fprintf(stderr,"PARSE ERROR: %s\n", cmd);
+ printf_log("main", LOG_WARNING | LOG_IRCRAW, "PARSE ERROR: %s %s %s\n", (from ? from : "*"), cmd, merge_argv(argv, 0, argc));
}
}
return 1;
}
+static int check_userbot_rejoin(struct UserNode *user) {
+ if(!(user->flags & USERFLAG_ISAUTHED)) return 0;
+ char tmp[MAXLEN];
+ sprintf(tmp, "General.UserBots.%s.enabled", user->auth);
+ if(!get_int_field(tmp))
+ return 0;
+ sprintf(tmp, "General.UserBots.%s.nicks", user->auth);
+ char *nicks = get_string_field(tmp);
+ if(nicks) {
+ char *cnick = nicks;
+ int matching_nick = 0;
+ do {
+ nicks = strchr(cnick, ',');
+ if(nicks)
+ *nicks = '\0';
+ if(!match(cnick, user->nick))
+ matching_nick = 1;
+ if(nicks) {
+ *nicks = ',';
+ nicks++;
+ }
+ } while((cnick = nicks) && !matching_nick);
+ if(!matching_nick)
+ return 0;
+ }
+ sprintf(tmp, "General.UserBots.%s.opless_part", user->auth);
+ if(!get_string_field(tmp))
+ return 0;
+ return 1;
+}
+
+static void free_rejoin_clients(struct ChanNode *chan, int rejoin) {
+ struct OplessRejoinBot *rejoin_bot, *next_rejoin_bot;
+ char tmp[MAXLEN];
+ int sourceid;
+ struct ClientSocket *bot;
+ for(rejoin_bot = chan->rejoin_bots; rejoin_bot; rejoin_bot = next_rejoin_bot) {
+ next_rejoin_bot = rejoin_bot->next;
+ if(rejoin) {
+ if(rejoin_bot->is_client) {
+ putsock(rejoin_bot->bot.client, "JOIN %s", chan->name);
+ } else {
+ sprintf(tmp, "General.UserBots.%s.sourcebot", rejoin_bot->bot.userbot->auth);
+ if(get_string_field(tmp)) {
+ sourceid = resolve_botalias(get_string_field(tmp));
+ if(sourceid == -1)
+ sourceid = 0;
+ } else
+ sourceid = 0;
+ bot = getChannelBot(NULL, sourceid);
+ if(!bot)
+ bot = getChannelBot(NULL, 0);
+ sprintf(tmp, "General.UserBots.%s.opless_join", rejoin_bot->bot.userbot->auth);
+ if(get_string_field(tmp))
+ putsock(bot, get_string_field(tmp), rejoin_bot->bot.userbot->nick, chan->name);
+ }
+ }
+ if(!rejoin_bot->is_client) {
+ free(rejoin_bot->bot.userbot->nick);
+ free(rejoin_bot->bot.userbot->auth);
+ free(rejoin_bot->bot.userbot);
+ }
+ free(rejoin_bot);
+ }
+ if(chan->rejoin_timeout)
+ timeq_del(chan->rejoin_timeout);
+}
+
+static TIMEQ_CALLBACK(full_rejoin_timeout) {
+ struct ChanNode *chan = data;
+ chan->rejoin_timeout = NULL;
+ free_rejoin_clients(chan, 1);
+ chan->flags &= ~CHANFLAG_REJOINING;
+}
+
+static void check_full_rejoin(struct ChanNode *chan) {
+ struct ChanUser *chanuser;
+ char do_rejoin = 1;
+ int botcount = 0;
+ int userbots = 0;
+ for(chanuser = getChannelUsers(chan, NULL); chanuser; chanuser = getChannelUsers(chan, chanuser)) {
+ if((chanuser->flags & CHANUSERFLAG_OPPED) || !(isBot(chanuser->user) || check_userbot_rejoin(chanuser->user))) {
+ do_rejoin = 0;
+ break;
+ }
+ botcount++;
+ if(!isBot(chanuser->user))
+ userbots++;
+ }
+ if(do_rejoin) {
+ struct OplessRejoinBot *rejoin_bot;
+ struct ClientSocket *bot, *chanbot = NULL;
+ chan->rejoin_bots = NULL;
+ for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) {
+ if(!isUserOnChan(bot->user, chan))
+ continue;
+ if(!chanbot && ((bot->flags & SOCKET_FLAG_PREFERRED) || !getBots(SOCKET_FLAG_READY, bot)))
+ chanbot = bot;
+ else {
+ rejoin_bot = malloc(sizeof(*rejoin_bot));
+ rejoin_bot->is_client = 1;
+ rejoin_bot->bot.client = bot;
+ rejoin_bot->next = chan->rejoin_bots;
+ chan->rejoin_bots = rejoin_bot;
+ putsock(bot, "PART %s :rejoining", chan->name);
+ }
+ }
+ if(userbots) {
+ char tmp[MAXLEN];
+ int sourceid;
+ for(chanuser = getChannelUsers(chan, NULL); chanuser; chanuser = getChannelUsers(chan, chanuser)) {
+ if(check_userbot_rejoin(chanuser->user)) {
+ rejoin_bot = malloc(sizeof(*rejoin_bot));
+ rejoin_bot->is_client = 0;
+ rejoin_bot->bot.userbot = malloc(sizeof(struct OplessRejoinUserbot));
+ rejoin_bot->bot.userbot->nick = strdup(chanuser->user->nick);
+ rejoin_bot->bot.userbot->auth = strdup(chanuser->user->auth);
+ rejoin_bot->next = chan->rejoin_bots;
+ chan->rejoin_bots = rejoin_bot;
+ sprintf(tmp, "General.UserBots.%s.sourcebot", chanuser->user->auth);
+ if(get_string_field(tmp)) {
+ sourceid = resolve_botalias(get_string_field(tmp));
+ if(sourceid == -1)
+ sourceid = 0;
+ } else
+ sourceid = 0;
+ bot = getChannelBot(NULL, sourceid);
+ if(!bot)
+ bot = getChannelBot(NULL, 0);
+ sprintf(tmp, "General.UserBots.%s.opless_part", chanuser->user->auth);
+ putsock(bot, get_string_field(tmp), chanuser->user->nick, chan->name);
+ }
+ }
+ }
+
+ if(botcount == 1) {
+ //we're alone
+ free(chan->rejoin_bots);
+ putsock(chanbot, "PART %s :magic hop", chan->name);
+ putsock(chanbot, "JOIN %s", chan->name);
+ } else {
+ chan->flags |= CHANFLAG_REJOINING;
+ chan->rejoin_timeout = timeq_add(10, 0, full_rejoin_timeout, chan);
+ }
+ }
+}
+
static IRC_CMD(raw_join) {
if(from == NULL || argc < 1) return 0;
SYNCHRONIZE(cache_sync);
chan = addChannel(argv[0]);
chanuser = addChanUser(chan, user);
chanuser->visCount = 1;
+ chan->botcount = 1;
get_userlist_with_invisible(chan, 0, got_channel_userlist, chanuser);
putsock(client, "MODE %s", chan->name);
putsock(client, "MODE %s +b", chan->name);
event_registered(user, from);
user->flags &= ~USERFLAG_WAS_REGISTERING;
+ if(user->last_who > REWHO_TIMEOUT)
+ user->last_who -= REWHO_TIMEOUT;
+
+ event_join(chanuser);
} else if(!(chan->flags & CHANFLAG_RECEIVED_USERLIST)) {
if(client->user != user) { //bots are allowed to add themselves
DESYNCHRONIZE(cache_sync);
return 1; //ignore join
}
- chanuser = addChanUser(chan, user);
- chanuser->visCount = 1;
+ if(!(chanuser = getChanUser(user, chan))) {
+ chanuser = addChanUser(chan, user);
+ }
+ chanuser->visCount++;
+ chan->botcount++;
if(isModeSet(chan->modes, 'D')) //if the bot joins a channel it could also be invisible
chanuser->flags |= CHANUSERFLAG_INVISIBLE;
//join user to an existing channel
chanuser = addChanUser(chan, user);
chanuser->visCount = 1;
- if(isBot(user)) {
+ if(isBot(user) && client->user == user) {
if(isModeSet(chan->modes, 'D')) //if the bot joins a channel it could also be invisible
chanuser->flags |= CHANUSERFLAG_INVISIBLE;
increase_viscount_butone(chan, chanuser);
+ chan->botcount++;
}
event_join(chanuser);
if(!(user->flags & USERFLAG_ISBOT) && (chan->flags & CHANFLAG_REJOINING)) {
//ABORT AUTOMATIC REJOIN (security break)
- struct ClientSocket **clients = chan->rejoin_array;
- while(*clients) {
- putsock(*clients, "JOIN %s", chan->name);
- clients++;
- }
- free(chan->rejoin_array);
+ free_rejoin_clients(chan, 1);
chan->flags &= ~CHANFLAG_REJOINING;
}
} else {
chanuser = getChanUser(user, chan);
chanuser->visCount++;
- if(isBot(user) && !(chanuser->flags & CHANUSERFLAG_INVISIBLE))
+ if(isBot(user) && client->user == user) {
increase_viscount_butone(chan, chanuser);
+ chan->botcount++;
+ }
+
+ if(chanuser->visCount > chan->botcount) {
+ printf_log("main", LOG_WARNING, "visCount (%d) bigger than botcount (%d) on channel %s (user %s).", chanuser->visCount, chan->botcount, chan->name, user->nick);
+ chanuser->visCount = chan->botcount;
+ }
//if multiple bots see the user, it can't be invisible
chanuser->flags &= ~CHANUSERFLAG_INVISIBLE;
return 1;
}
-static void check_full_rejoin(struct ChanNode *chan) {
- struct ChanUser *chanuser;
- char do_rejoin = 1;
- int botcount = 0;
- for(chanuser = getChannelUsers(chan, NULL); chanuser; chanuser = getChannelUsers(chan, chanuser)) {
- if((chanuser->flags & CHANUSERFLAG_OPPED) || !(chanuser->user->flags & USERFLAG_ISBOT)) {
- do_rejoin = 0;
- break;
- }
- if((chanuser->user->flags & USERFLAG_ISBOT))
- botcount++;
- }
- if(do_rejoin) {
- struct ClientSocket **clients = calloc(botcount, sizeof(*clients));
- struct ClientSocket *bot, *chanbot = NULL;
- int i = 0;
- for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) {
- if(!isUserOnChan(bot->user, chan))
- continue;
- if(!chanbot && ((bot->flags & SOCKET_FLAG_PREFERRED) || !getBots(SOCKET_FLAG_READY, bot)))
- chanbot = bot;
- else {
- clients[i++] = bot;
- putsock(bot, "PART %s :rejoining", chan->name);
- }
- }
- if(botcount == 1) {
- //we're alone
- free(clients);
- putsock(chanbot, "PART %s :magic hop", chan->name);
- putsock(chanbot, "JOIN %s", chan->name);
- } else {
- chan->flags |= CHANFLAG_REJOINING;
- chan->rejoin_array = clients;
- }
- }
-}
-
static IRC_CMD(raw_part) {
if(from == NULL || argc < 1) return 0;
SYNCHRONIZE(cache_sync);
DESYNCHRONIZE(cache_sync);
return 0;
}
- if(isBot(user) && user == client->user)
+ if(isBot(user) && user == client->user) {
decrease_viscount_butone(chan, chanuser);
+ chan->botcount--;
+ }
+ if(chanuser->flags & CHANUSERFLAG_PARTING)
+ chanuser->old_visCount--;
chanuser->visCount--;
if(chanuser->visCount == 0) {
delChanUser(chanuser, 0); //not free, yet!
event_part(chanuser, 0, (argc > 1 ? argv[1] : NULL));
freeChanUser(chanuser);
+ } else if(!(chanuser->flags & CHANUSERFLAG_PARTING)) {
+ chanuser->flags |= CHANUSERFLAG_PARTING;
+ chanuser->old_visCount = chanuser->visCount;
+ } else if(chanuser->old_visCount == 0) {
+ int visCount = chanuser->visCount;
+ delChanUser(chanuser, 0); //not free, yet!
+ event_part(chanuser, 0, (argc > 1 ? argv[1] : NULL));
+ freeChanUser(chanuser);
+ chanuser = addChanUser(chan, user);
+ chanuser->visCount = visCount;
+ event_join(chanuser);
}
//check if channel is still present
int keep_channel = 1;
if(chan->usercount == 0) {
if((chan->flags & CHANFLAG_REJOINING)) {
- struct ClientSocket **clients = chan->rejoin_array;
- while(*clients) {
- putsock(*clients, "JOIN %s", chan->name);
- clients++;
- }
- free(chan->rejoin_array);
+ free_rejoin_clients(chan, 1);
chan->flags &= ~CHANFLAG_REJOINING;
}
delChannel(chan, 1);
DESYNCHRONIZE(cache_sync);
return 0;
}
- if(isBot(target) && target == client->user)
+ if(isBot(target) && target == client->user) {
decrease_viscount_butone(chan, chanuser);
+ chan->botcount--;
+ }
chanuser->visCount--;
if(chanuser->visCount == 0) {
delChanUser(chanuser, 0); //not free, yet!
- event_kick(user, chanuser, argv[2]);
+ if(user)
+ event_kick(user, chanuser, argv[2]);
freeChanUser(chanuser);
}
int keep_channel = 1;
if(chan->usercount == 0) {
if((chan->flags & CHANFLAG_REJOINING)) {
- struct ClientSocket **clients = chan->rejoin_array;
- while(*clients) {
- putsock(*clients, "JOIN %s", chan->name);
- clients++;
- }
- free(chan->rejoin_array);
+ free_rejoin_clients(chan, 1);
chan->flags &= ~CHANFLAG_REJOINING;
}
delChannel(chan, 1);
for(chanuser = getUserChannels(user, NULL); chanuser; chanuser = next_chanuser) {
next_chanuser = getUserChannels(user, chanuser);
decrease_viscount_butone(chanuser->chan, chanuser);
+ chanuser->chan->botcount--;
chanuser->visCount--;
if(chanuser->visCount <= 0) {
delChanUser(chanuser, 0); //not free, yet!
if(from == NULL || argc < 2) return 0;
struct UserNode *user = getUserByMask(from);
if(user == NULL) {
+ if(stricmplen(from, "***!", 4) == 0) return 1; /* ZNC Playback */
user = createTempUserMask(from);
if(!user) return 0;
user->flags |= USERFLAG_ISTMPUSER;
if(argv[0][0] == '#') { //Channel message
struct ChanNode *chan = getChanByName(argv[0]);
if(chan && client == get_first_prefered_bot_in_channel(chan)) {
- if(statistics_enabled)
- statistics_privmsg++;
+ statistics_privmsg++;
if(argv[1][0] == '\001') {
char *cmd = &argv[1][1];
char *text = strstr(cmd, " ");
}
static IRC_CMD(raw_notice) {
+ if(from == NULL && argc && !stricmp(argv[0], "AUTH")) return 1; //NOTICE AUTH is NOT a parse error ;)
if(from == NULL || argc < 2) return 0;
struct UserNode *user = getUserByMask(from);
if(user == NULL) {
if(bot == client) continue;
if(!bot->network_name) continue;
if(stricmp(bot->network_name, value)) {
- putlog(LOGLEVEL_ERROR, "WARNING: Network name '%s' (%s) differs from '%s' (%s)! Connecting to multiple IRC-Networks with one instance is NOT supported!\n", client->network_name, client->nick, bot->network_name, bot->nick);
+ printf_log("main", LOG_ERROR, "WARNING: Network name '%s' (%s) differs from '%s' (%s)! Connecting to multiple IRC-Networks with one instance is NOT supported!\n", client->network_name, client->nick, bot->network_name, bot->nick);
break;
}
}