-/* IRCParser.c - NeonServ v5.2
- * Copyright (C) 2011 Philipp Kreil (pk910)
+/* IRCParser.c - NeonServ v5.6
+ * Copyright (C) 2011-2012 Philipp Kreil (pk910)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
#include "DBHelper.h"
#include "BanNode.h"
#include "ModeNode.h"
+#include "tools.h"
+#include "bots.h"
+#include "timeq.h"
+#include "ConfigParser.h"
+#include "statistics.h"
struct irc_cmd *irc_commands = NULL;
-static struct UserNode *registering_users = NULL;
+//static struct UserNode *registering_users = NULL;
int statistics_privmsg = 0;
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;
-}
-
-static void parse_line(struct ClientSocket *client, char *line) {
+struct OplessRejoinUserbot {
+ char *nick;
+ char *auth;
+};
+
+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];
- printf("[recv %lu] %s\n", (unsigned long) strlen(line), line);
+ #ifdef HAVE_THREADS
+ putlog(LOGLEVEL_RAW, "[%d recv %lu] %s\n", getCurrentThreadID(), (unsigned long) strlen(line), line);
+ #else
+ putlog(LOGLEVEL_RAW, "[recv %lu] %s\n", (unsigned long) strlen(line), line);
+ #endif
if(line[0] == ':')
line++;
else
}
}
+static void increase_viscount_butone(struct ChanNode *chan, struct ChanUser *ignore) {
+ struct ChanUser *chanuser;
+
+ for(chanuser = getChannelUsers(chan, NULL); chanuser; chanuser = getChannelUsers(chan, chanuser)) {
+ if(chanuser == ignore)
+ continue;
+ chanuser->visCount++;
+ }
+}
+
+static void decrease_viscount_butone(struct ChanNode *chan, struct ChanUser *ignore) {
+ struct ChanUser *chanuser, *next_chanuser;
+
+ for(chanuser = getChannelUsers(chan, NULL); chanuser; chanuser = next_chanuser) {
+ next_chanuser = getChannelUsers(chan, chanuser);
+ if(chanuser == ignore)
+ continue;
+ chanuser->visCount--;
+ if(chanuser->visCount <= 0)
+ delChanUser(chanuser, 1);
+ }
+}
+
static USERLIST_CALLBACK(got_channel_userlist) {
struct ChanUser *chanuser = data;
+
+ increase_viscount_butone(chanuser->chan, chanuser);
+
event_join(chanuser);
}
-static IRC_CMD(raw_001) {
+static IRC_CMD(raw_002) { //fixed: ZNC fakes a 001 raw even if we're not connected!
struct UserNode *user = getUserByNick(argv[0]);
if(!user)
user = addUser(argv[0]);
return 1;
}
-static int is_firstBotSeeUser(struct ClientSocket *client, struct UserNode *user) {
- struct ClientSocket *bot, *pref_bot = NULL, *unpref_bot = NULL;
- struct ChanUser *chanuser;
- int found;
- for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) {
- found = 0;
- for(chanuser = getUserChannels(bot->user, NULL); chanuser; chanuser = getUserChannels(bot->user, chanuser)) {
- if(isUserOnChan(user, chanuser->chan)) {
- found = 1;
- break;
+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++;
}
- }
- if(!found) continue;
- if(bot->flags & SOCKET_FLAG_PREFERRED) {
- pref_bot = bot;
- break;
- } else
- unpref_bot = bot;
+ } while((cnick = nicks) && !matching_nick);
+ if(!matching_nick)
+ return 0;
}
- bot = (pref_bot ? pref_bot : unpref_bot);
- if(client == bot)
- return 1;
- else
+ sprintf(tmp, "General.UserBots.%s.opless_part", user->auth);
+ if(!get_string_field(tmp))
return 0;
+ return 1;
}
-static int is_onlyBotSeeUser(struct ClientSocket *client, struct UserNode *user) {
+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;
- struct ChanUser *chanuser;
- for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) {
- if(bot == client) continue;
- for(chanuser = getUserChannels(bot->user, NULL); chanuser; chanuser = getUserChannels(bot->user, chanuser)) {
- if(isUserOnChan(user, chanuser->chan)) {
- return 0;
+ 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);
}
- return 1;
+ if(chan->rejoin_timeout)
+ timeq_del(chan->rejoin_timeout);
}
-static IRC_CMD(raw_join) {
- if(from == NULL || argc < 1) return 0;
- struct UserNode *user = getUserByMask(from);
- struct ChanNode *chan = getChanByName(argv[0]);
- if(!chan && !(user->flags & USERFLAG_ISBOT)) return 0;
- if(chan && chan->chanbot != client->user) return 1; //we ignore it - but it's not a parse error
- if(user == NULL) {
- user = addUserMask(from);
- }
- struct UserNode *registering, *last_registering = NULL, *next_registering;
- int noEvent = 0, wasRegistering = 0;
- for(registering = registering_users; registering; registering = next_registering) {
- next_registering = registering->next;
- if(!strcmp(registering->nick, user->nick)) {
- noEvent = event_registered(registering, user);
- wasRegistering = 1;
- if(last_registering)
- last_registering->next = registering->next;
- else
- registering_users = registering->next;
- delUser(registering, 1);
- } else if(time(0) - registering->created > 2) {
- if(last_registering)
- last_registering->next = registering->next;
- else
- registering_users = registering->next;
- delUser(registering, 1);
- } else
- last_registering = registering;
- }
- if(chan == NULL) {
- chan = addChannel(argv[0]);
- //request member list
- chan->chanbot = user;
- struct ChanUser *chanuser = addChanUser(chan, user); //it must be a bot
- get_userlist(chan, got_channel_userlist, chanuser);
- putsock(client, "MODE %s", chan->name);
- putsock(client, "MODE %s +b", chan->name);
- } else if(!isUserOnChan(user, chan) && (chan->flags & CHANFLAG_RECEIVED_USERLIST)) {
- struct ChanUser *chanuser = addChanUser(chan, user);
- if(!noEvent) {
- if(wasRegistering)
- user->flags |= USERFLAG_WAS_REGISTRING;
- event_join(chanuser);
- if(wasRegistering)
- user->flags &= ~USERFLAG_WAS_REGISTRING;
- }
- if(!(user->flags & USERFLAG_ISBOT) && (chan->flags & CHANFLAG_REJOINING)) {
- //ABORT REJOIN (security break)
- struct ClientSocket **clients = chan->rejoin_array;
- while(*clients) {
- putsock(*clients, "JOIN %s", chan->name);
- clients++;
- }
- free(chan->rejoin_array);
- chan->flags &= ~CHANFLAG_REJOINING;
- }
- } else if(chan->usercount == 1 && isUserOnChan(user, chan)) {
- //first bot rejoined
- struct ChanUser *chanuser = getChanUser(user, chan);
- chanuser->flags &= ~CHANUSERFLAG_VOICED;
- chanuser->flags |= CHANUSERFLAG_OPPED;
- }
- return 1;
+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) || !(chanuser->user->flags & USERFLAG_ISBOT)) {
+ if((chanuser->flags & CHANUSERFLAG_OPPED) || !(isBot(chanuser->user) || check_userbot_rejoin(chanuser->user))) {
do_rejoin = 0;
break;
}
- if((chanuser->user->flags & USERFLAG_ISBOT))
- botcount++;
+ botcount++;
+ if(!isBot(chanuser->user))
+ userbots++;
}
if(do_rejoin) {
- struct ClientSocket **clients = calloc(botcount, sizeof(*clients));
- struct ClientSocket *bot, *chanbot;
- int i = 0;
+ 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(bot->user != chan->chanbot && isUserOnChan(bot->user, chan)) {
- clients[i++] = bot;
- putsock(bot, "PART %s :rejoining", chan->name);
- } else if(bot->user == chan->chanbot)
+ 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);
+ }
}
- chan->flags |= CHANFLAG_REJOINING;
- chan->rejoin_array = clients;
+ 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);
+ struct UserNode *user = getUserByMask(from);
+ struct ChanNode *chan = getChanByName(argv[0]);
+ struct ChanUser *chanuser;
+ if(!chan && (!user || !(user->flags & USERFLAG_ISBOT))) {
+ //parse error if the channel is not known, yet and it's not a bot joining
+ DESYNCHRONIZE(cache_sync);
+ return 0;
+ }
+ if(user == NULL)
+ user = addUserMask(from);
+ if(chan == NULL) {
+ //new channel
+ 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);
+ } else if((user->flags & USERFLAG_WAS_REGISTERING)) {
+ //user rejoined after registering (should still be present in the channel)
+ if(!(chanuser = getChanUser(user, chan)))
+ chanuser = addChanUser(chan, user);
+ chanuser->visCount++;
+
+ 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
+ }
+
+ 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;
+
+ get_userlist_with_invisible(chan, 0, got_channel_userlist, chanuser);
+ } else if(!isUserOnChan(user, chan)) {
+ //join user to an existing channel
+ chanuser = addChanUser(chan, user);
+ chanuser->visCount = 1;
+ 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)
+ free_rejoin_clients(chan, 1);
+ chan->flags &= ~CHANFLAG_REJOINING;
+ }
+ } else {
+ //user is already in the channel
+ chanuser = getChanUser(user, chan);
+ chanuser->visCount++;
+
+ if(isBot(user) && client->user == user) {
+ increase_viscount_butone(chan, chanuser);
+ chan->botcount++;
}
+
+ if(chanuser->visCount > chan->botcount) {
+ chanuser->visCount = chan->botcount;
+ //TODO: Trigger WARNING
+ }
+
+ //if multiple bots see the user, it can't be invisible
+ chanuser->flags &= ~CHANUSERFLAG_INVISIBLE;
}
+ DESYNCHRONIZE(cache_sync);
+ return 1;
}
static IRC_CMD(raw_part) {
if(from == NULL || argc < 1) return 0;
+ SYNCHRONIZE(cache_sync);
struct UserNode *user = getUserByMask(from);
- if(user == NULL) return 0;
struct ChanNode *chan = getChanByName(argv[0]);
- if(chan == NULL) return 0;
- if(chan->chanbot != client->user) return 1; //we ignore it - but it's not a parse error
- int keep_channel = 1;
- if(chan->chanbot == user && (chan->flags & CHANFLAG_REJOINING)) {
- struct ClientSocket **clients = chan->rejoin_array;
- while(*clients) {
- putsock(*clients, "JOIN %s", chan->name);
- clients++;
- }
- free(chan->rejoin_array);
- chan->flags &= ~CHANFLAG_REJOINING;
+ struct ChanUser *chanuser;
+ if(user == NULL || chan == NULL || !(chanuser = getChanUser(user, chan))) {
+ DESYNCHRONIZE(cache_sync);
return 0;
- } else if(isUserOnChan(user, chan) && (chan->flags & CHANFLAG_RECEIVED_USERLIST)) {
- struct ChanUser *chanuser = getChanUser(user, chan);
- delChanUser(chanuser, 0); //we need to free the chanuser manually!
- event_part(chanuser, (argc > 1 ? argv[1] : NULL));
+ }
+ 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);
- if(chan->chanbot == user) {
- //check if theres another bot in the channel - otherwise free it
- keep_channel = checkChannelVisibility(chan);
- }
+ } 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);
}
- if(user->channel == NULL && !(user->flags & USERFLAG_ISBOT)) {
- //remove the user
+
+ //check if channel is still present
+ int keep_channel = 1;
+ if(chan->usercount == 0) {
+ if((chan->flags & CHANFLAG_REJOINING)) {
+ free_rejoin_clients(chan, 1);
+ chan->flags &= ~CHANFLAG_REJOINING;
+ }
+ delChannel(chan, 1);
+ keep_channel = 0;
+ } else if(isBot(user)) //bot parted - check if theres another bot in the channel
+ keep_channel = checkChannelVisibility(chan);
+
+ // free user if he/she is in no other channel
+ if(user->channel == NULL && !(user->flags & USERFLAG_ISBOT))
delUser(user, 1);
- }
- if(keep_channel && (chan->flags & CHANFLAG_RECEIVED_USERLIST) && !(chan->flags & CHANFLAG_REJOINING)) {
+
+ if(keep_channel && (chan->flags & CHANFLAG_RECEIVED_USERLIST) && !(chan->flags & CHANFLAG_REJOINING))
check_full_rejoin(chan);
+ else if(keep_channel && (chan->flags & CHANFLAG_REJOINING) && chan->usercount == 1) {
+ //bot is alone... rejoin!
+ struct ClientSocket *bot;
+ //find the last bot in the channel
+ for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) {
+ if(isUserOnChan(bot->user, chan)) {
+ putsock(bot, "PART %s :magic hop", chan->name);
+ putsock(bot, "JOIN %s", chan->name);
+ }
+ }
+ }
+ DESYNCHRONIZE(cache_sync);
+ return 1;
+}
+
+static IRC_CMD(raw_kick) {
+ if(from == NULL || argc < 3) return 0;
+ SYNCHRONIZE(cache_sync);
+ struct UserNode *user = getUserByMask(from);
+ struct UserNode *target = getUserByNick(argv[1]);
+ struct ChanNode *chan = getChanByName(argv[0]);
+ struct ChanUser *chanuser;
+ if(chan == NULL || target == NULL || !(chanuser = getChanUser(target, chan))) {
+ DESYNCHRONIZE(cache_sync);
+ return 0;
}
+ 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]);
+ freeChanUser(chanuser);
+ }
+
+ //check if channel is still present
+ int keep_channel = 1;
+ if(chan->usercount == 0) {
+ if((chan->flags & CHANFLAG_REJOINING)) {
+ free_rejoin_clients(chan, 1);
+ chan->flags &= ~CHANFLAG_REJOINING;
+ }
+ delChannel(chan, 1);
+ keep_channel = 0;
+ } else if(isBot(target)) //bot parted - check if theres another bot in the channel
+ keep_channel = checkChannelVisibility(chan);
+
+ // free user if he/she is in no other channel
+ if(target->channel == NULL && !(target->flags & USERFLAG_ISBOT))
+ delUser(target, 1);
+
+ if(keep_channel && (chan->flags & CHANFLAG_RECEIVED_USERLIST) && !(chan->flags & CHANFLAG_REJOINING))
+ check_full_rejoin(chan);
else if(keep_channel && (chan->flags & CHANFLAG_REJOINING) && chan->usercount == 1) {
//bot is alone... rejoin!
- putsock(client, "PART %s :magic hop", chan->name);
- putsock(client, "JOIN %s", chan->name);
+ struct ClientSocket *bot;
+ //find the last bot in the channel
+ for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) {
+ if(isUserOnChan(bot->user, chan)) {
+ putsock(bot, "PART %s :magic hop", chan->name);
+ putsock(bot, "JOIN %s", chan->name);
+ }
+ }
}
+ DESYNCHRONIZE(cache_sync);
return 1;
}
static IRC_CMD(raw_quit) {
if(from == NULL || argc < 1) return 0;
+ SYNCHRONIZE(cache_sync);
struct UserNode *user = getUserByMask(from);
- if(user == NULL) return 0;
- if(!is_firstBotSeeUser(client, user)) return 1; //we ignore it - but it's not a parse error
- int registering = !stricmp(argv[0], "Registered");
- if((registering && (user->flags & USERFLAG_ISBOT))) return 1; //bot is registering - just ignore it
- delUser(user, 0); //a little bit crazy, but we want to delete the user on the channel's userlists - but not the users channel list
- event_quit(user, argv[0]);
- if(user->flags & USERFLAG_ISBOT) {
- //check if there are other bots in the users channel - otherwise free them
- struct ChanUser *chanuser, *next;
- for(chanuser = getUserChannels(user, NULL); chanuser; chanuser = next) {
- next = getUserChannels(user, chanuser);
- if(chanuser->chan->chanbot == user)
- checkChannelVisibility(chanuser->chan);
- }
- //search the user representing the bot in the world of IRC
- struct ClientSocket *bot;
- for(bot = getBots(0, NULL); bot; bot = getBots(0, bot)) {
- if(bot->user == user) {
- bot->user = NULL;
- break;
+ if(user == NULL) {
+ DESYNCHRONIZE(cache_sync);
+ return 0;
+ }
+
+ if(client->user == user) {
+ DESYNCHRONIZE(cache_sync);
+ return 0;
+ }
+
+ //check if user is just registering
+ if(!stricmp(argv[0], "Registered"))
+ user->flags |= USERFLAG_WAS_REGISTERING;
+
+ //decrease visCount counter
+ struct ChanUser *chanuser, *next_chanuser;
+ for(chanuser = getUserChannels(user, NULL); chanuser; chanuser = next_chanuser) {
+ next_chanuser = getUserChannels(user, chanuser);
+ //decrease visCount counter only if client is in the channel
+ if(isUserOnChan(client->user, chanuser->chan)) {
+ chanuser->visCount--;
+ if(chanuser->visCount <= 0 && !(user->flags & USERFLAG_WAS_REGISTERING)) {
+ delChanUser(chanuser, 0); //not free, yet!
+ event_part(chanuser, 1, argv[0]);
+ if((chanuser->chan->flags & CHANFLAG_RECEIVED_USERLIST) && !(chanuser->chan->flags & CHANFLAG_REJOINING))
+ check_full_rejoin(chanuser->chan);
+ freeChanUser(chanuser);
}
}
- } else if(!registering) {
- struct ChanUser *chanuser;
- struct ChanNode *chan;
- for(chanuser = user->channel; chanuser; chanuser = chanuser->next_chan) {
- chan = chanuser->chan;
- if((chan->flags & CHANFLAG_RECEIVED_USERLIST) && !(chan->flags & CHANFLAG_REJOINING))
- check_full_rejoin(chan);
- }
- }
- if(registering && !(user->flags & USERFLAG_ISBOT)) {
- user->next = registering_users;
- user->created = time(0);
- registering_users = user;
- } else
- delUser(user, 1); //now we fully free the user
+ }
+
+ if(user->channel == NULL) {
+ if(isBot(user)) {
+ //ASSERT
+ return 0;
+ }
+ if((user->flags & USERFLAG_WAS_REGISTERING)) {
+ //TODO: set a timeout or sth like that?
+ } else
+ delUser(user, 1);
+ }
+
+ DESYNCHRONIZE(cache_sync);
return 1;
}
void bot_disconnect(struct ClientSocket *client) {
struct UserNode *user = client->user;
- struct ChanUser *chanuser, *next;
if(user) {
- if(is_onlyBotSeeUser(client, user)) {
- //ok the bot-user is not seen by any other bots so we can simply free it.
- delUser(user, 0);
- event_quit(user, "disconnected");
- for(chanuser = getUserChannels(user, NULL); chanuser; chanuser = next) {
- next = getUserChannels(user, chanuser);
- if(chanuser->chan->chanbot == user)
+ //just remove the bot mark from the user and handle a normal quit :)
+ user->flags &= ~USERFLAG_ISBOT;
+ client->user = NULL;
+ client->flags &= ~SOCKET_FLAG_READY;
+
+ //decrease visCount counter
+ struct ChanUser *chanuser, *next_chanuser;
+ 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!
+ event_part(chanuser, 1, "QUIT");
+ if(chanuser->chan->flags & CHANFLAG_RECEIVED_USERLIST)
checkChannelVisibility(chanuser->chan);
freeChanUser(chanuser);
}
- user->channel = NULL;
- delUser(user, 1); //now we fully free the user
- } else {
- //we need to transform the bot-user back to a normal user (BNC FIX)
- user->flags &= ~USERFLAG_ISBOT;
- for(chanuser = getUserChannels(user, NULL); chanuser; chanuser = next) {
- next = getUserChannels(user, chanuser);
- if(chanuser->chan->chanbot == user)
- checkChannelVisibility(chanuser->chan);
- }
}
- client->user = NULL;
+
+ if(user->channel == NULL)
+ delUser(user, 1);
}
}
-static IRC_CMD(raw_kick) {
- if(from == NULL || argc < 3) return 0;
- struct UserNode *user = getUserByMask(from);
- struct UserNode *target = getUserByNick(argv[1]);
- struct ChanNode *chan = getChanByName(argv[0]);
- if(chan == NULL || target == NULL) return 0;
- if(chan->chanbot != client->user) return 1; //we ignore it - but it's not a parse error
- int keep_channel = 1;
- if(isUserOnChan(target, chan) && (chan->flags & CHANFLAG_RECEIVED_USERLIST)) {
- if(user == NULL) {
- user = createTempUser(from);
- user->flags |= USERFLAG_ISTMPUSER;
- }
- struct ChanUser *chanuser = getChanUser(target, chan);
- delChanUser(chanuser, 0); //we need to free the chanuser manually!
- event_kick(user, chanuser, argv[1]);
- if(chanuser->chan->chanbot == user) {
- //check if theres another bot in the channel - otherwise free it
- keep_channel = checkChannelVisibility(chan);
- }
- freeChanUser(chanuser);
- }
- if(target->channel == NULL && !(target->flags & USERFLAG_ISBOT)) {
- //remove the user
- delUser(target, 1);
- }
- if(keep_channel && (chan->flags & CHANFLAG_RECEIVED_USERLIST) && !(chan->flags & CHANFLAG_REJOINING)) {
- check_full_rejoin(chan);
+static struct ClientSocket *get_first_prefered_bot_in_channel(struct ChanNode *chan) {
+ struct ClientSocket *bot, *chanbot = NULL;
+ for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) {
+ if(!isUserOnChan(bot->user, chan))
+ continue;
+ if(bot->flags & SOCKET_FLAG_PREFERRED)
+ return bot;
+ chanbot = bot;
}
- return 1;
+ return chanbot;
}
static IRC_CMD(raw_topic) {
if(from == NULL || argc < 2) return 0;
+ SYNCHRONIZE(cache_sync);
struct UserNode *user = getUserByMask(from);
struct ChanNode *chan = getChanByName(argv[0]);
- if(chan == NULL) return 0;
- if(chan->chanbot != client->user) return 1; //just ignore it to prevent event duplicates
+ if(chan == NULL) {
+ DESYNCHRONIZE(cache_sync);
+ return 0;
+ }
+ if(client != get_first_prefered_bot_in_channel(chan)) {
+ DESYNCHRONIZE(cache_sync);
+ return 1; //just ignore it to prevent event duplicates
+ }
if(user == NULL) {
- user = createTempUser(from);
+ user = createTempUserMask(from);
+ if(!user) {
+ DESYNCHRONIZE(cache_sync);
+ return 0;
+ }
user->flags |= USERFLAG_ISTMPUSER;
}
event_topic(user, chan, argv[1]);
strcpy(chan->topic, argv[1]);
+ DESYNCHRONIZE(cache_sync);
return 1;
}
if(from == NULL || argc < 2) return 0;
struct UserNode *user = getUserByMask(from);
if(user == NULL) {
- user = createTempUser(from);
+ 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 && chan->chanbot == client->user) {
- if(statistics_enabled)
- statistics_privmsg++;
+ if(chan && client == get_first_prefered_bot_in_channel(chan)) {
+ statistics_privmsg++;
if(argv[1][0] == '\001') {
char *cmd = &argv[1][1];
char *text = strstr(cmd, " ");
if(from == NULL || argc < 2) return 0;
struct UserNode *user = getUserByMask(from);
if(user == NULL) {
- user = createTempUser(from);
+ user = createTempUserMask(from);
+ if(!user) return 0;
user->flags |= USERFLAG_ISTMPUSER;
}
if(argv[0][0] == '#') { //Channel notice
struct ChanNode *chan = getChanByName(argv[0]);
- if(chan && chan->chanbot == client->user)
+ if(chan && client == get_first_prefered_bot_in_channel(chan))
event_channotice(user, chan, argv[1]);
} else {
struct UserNode *target = getUserByNick(argv[0]);
return 1;
}
+static void client_renamed(struct ClientSocket *client);
+
static IRC_CMD(raw_nick) {
if(from == NULL || argc == 0) return 0;
+ SYNCHRONIZE(cache_sync);
struct UserNode *user = getUserByMask(from);
- if(user == NULL) return 0;
- if(!is_firstBotSeeUser(client, user)) return 1; //we ignore it - but it's not a parse error
+ if(user == NULL) {
+ DESYNCHRONIZE(cache_sync);
+ return 1; //maybe already renamed - no parse error
+ }
+ if(isBot(user)) {
+ if(client->user != user) {
+ DESYNCHRONIZE(cache_sync);
+ return 1;
+ }
+ client_renamed(client);
+ }
+ else if(!strcmp(user->nick, argv[0])) {
+ DESYNCHRONIZE(cache_sync);
+ return 1; //user has already this nick (case sensitive)
+ }
event_nick(user, argv[0]);
renameUser(user, argv[0]);
+ DESYNCHRONIZE(cache_sync);
return 1;
}
if(from == NULL || argc < 2) return 0;
struct UserNode *user = getUserByMask(from);
if(user == NULL) {
- user = createTempUser(from);
+ user = createTempUserMask(from);
+ if(!user) return 0;
user->flags |= USERFLAG_ISTMPUSER;
}
event_invite(client, user, argv[1]);
static IRC_CMD(raw_mode) {
if(from == NULL || argc < 2) return 0;
+ SYNCHRONIZE(cache_sync);
struct UserNode *user = getUserByMask(from);
if(user == NULL) {
- user = createTempUser(from);
+ user = createTempUserMask(from);
+ if(!user) {
+ DESYNCHRONIZE(cache_sync);
+ return 0;
+ }
user->flags |= USERFLAG_ISTMPUSER;
}
if(argv[0][0] == '#') {
//ChannelMode
struct ChanNode *chan = getChanByName(argv[0]);
- if(!chan) return 0;
- if(chan->chanbot != client->user) return 1;
- event_mode(user, chan, argv[1], argv+2, argc-2);
+ if(!chan) {
+ DESYNCHRONIZE(cache_sync);
+ return 0;
+ }
+ if(client != get_first_prefered_bot_in_channel(chan)) {
+ DESYNCHRONIZE(cache_sync);
+ return 1;
+ }
parseModes(chan->modes, argv[1], argv+2, argc-2);
+ event_mode(user, chan, argv[1], argv+2, argc-2);
} else {
//UserMode
+ if(stricmp(client->user->nick, argv[0])) {
+ DESYNCHRONIZE(cache_sync);
+ return 0;
+ }
+ parseUserModes(client->user, argv[1]);
}
+ DESYNCHRONIZE(cache_sync);
return 1;
}
if(argc < 2) return 0;
char *total_user_str = argv[1];
char total_visible[20], total_invisible[20];
- int i, total_visible_pos = 0, total_invisible_pos = 0;
+ int i = 0, total_visible_pos = 0, total_invisible_pos = 0;
while(*total_user_str) {
if(*total_user_str == ' ') {
i++;
return 1;
}
+struct ClientRenamePartedChannel {
+ char channel[CHANNELLEN+1];
+ struct ClientRenamePartedChannel *next;
+};
+
+static IRC_CMD(raw_437) { //can NOT change nick
+ struct ClientRenamePartedChannel *partedchan = malloc(sizeof(*partedchan));
+ strcpy(partedchan->channel, argv[1]);
+ if((client->flags & SOCKET_FLAG_CHANGENICK))
+ partedchan->next = client->changenick_channels;
+ else
+ partedchan->next = NULL;
+ client->changenick_channels = partedchan;
+ client->flags |= SOCKET_FLAG_CHANGENICK;
+ putsock(client, "PART %s", argv[1]);
+ putsock(client, "NICK %s", client->nick);
+ return 1;
+}
+
+static void client_renamed(struct ClientSocket *client) {
+ if((client->flags & SOCKET_FLAG_CHANGENICK)) {
+ struct ClientRenamePartedChannel *partedchan, *nextchan;
+ for(partedchan = client->changenick_channels; partedchan; partedchan = nextchan) {
+ nextchan = partedchan->next;
+ putsock(client, "JOIN %s", partedchan->channel);
+ free(partedchan);
+ }
+ client->flags &= ~SOCKET_FLAG_CHANGENICK;
+ }
+}
+
+static void raw_005_network(struct ClientSocket *client, char *value) {
+ if(!value) return;
+ //check all other networknames
+ //if they are NOT simular to value throw a warning
+ SYNCHRONIZE(cache_sync); //all bots connect to the same time so there is a higher chance that this code is running on multiple threads at the same time
+ if(client->network_name)
+ free(client->network_name);
+ client->network_name = strdup(value);
+ struct ClientSocket *bot;
+ for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) {
+ 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);
+ break;
+ }
+ }
+ DESYNCHRONIZE(cache_sync);
+}
+
+static IRC_CMD(raw_005) {
+ char *ptr1 = merge_argv(argv, 1, argc);
+ char *ptr2, *name, *value;
+ do {
+ ptr2 = strchr(ptr1, ' ');
+ if(ptr2)
+ *ptr2 = '\0';
+ name = ptr1;
+ if((value = strchr(ptr1, '='))) {
+ *value = '\0';
+ value++;
+ }
+ if(!stricmp(name, "NETWORK")) raw_005_network(client, value);
+ if(ptr2)
+ ptr1 = ptr2 + 1;
+ } while(ptr2);
+ return 1;
+}
+
+static IRC_CMD(raw_nojoin) {
+ if(from == NULL || argc < 3) return 0;
+ struct ChanNode *chan = getChanByName(argv[1]);
+ if(chan == NULL) return 0;
+ if(client->flags & SOCKET_FLAG_REQUEST_INVITE)
+ requestInvite(client->user, chan);
+ return 1;
+}
+
void init_parser() {
//all the raws we receive...
- register_irc_function("001", raw_001);
+ register_irc_function("437", raw_437);
+ register_irc_function("002", raw_002);
+ register_irc_function("005", raw_005);
register_irc_function("251", raw_251);
register_irc_function("254", raw_254);
register_irc_function("324", raw_324);
register_irc_function("332", raw_332);
register_irc_function("367", raw_367);
+ register_irc_function("471", raw_nojoin);
+ register_irc_function("473", raw_nojoin);
+ register_irc_function("474", raw_nojoin);
+ register_irc_function("475", raw_nojoin);
register_irc_function("INVITE", raw_invite);
register_irc_function("NOTICE", raw_notice);
register_irc_function("TOPIC", raw_topic);
for(i = start; i < end; i++) {
p = argv[i];
while(*p) p++;
- *p = seperator;
+ if(i < end-1) {
+ while(p != argv[i+1]) {
+ *p++ = seperator;
+ }
+ } else
+ *p = seperator;
}
if(p) *p = '\0';
return argv[start];