-/* IRCParser.c - NeonServ v5.2
- * Copyright (C) 2011 Philipp Kreil (pk910)
+/* IRCParser.c - NeonServ v5.4
+ * 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"
struct irc_cmd *irc_commands = NULL;
static struct UserNode *registering_users = NULL;
static 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
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]);
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]);
- if(!chan && !(user->flags & USERFLAG_ISBOT)) return 0;
- if(chan && ((!isBot(user) && chan->chanbot != client->user) || (isBot(user) && client->user != user))) return 1; //we ignore it - but it's not a parse error
+ if(!chan && (!user || !(user->flags & USERFLAG_ISBOT))) {
+ DESYNCHRONIZE(cache_sync);
+ return 0;
+ }
+ if(chan && (((!user || !isBot(user)) && chan->chanbot != client->user) || ((user && isBot(user)) && client->user != user))) {
+ DESYNCHRONIZE(cache_sync);
+ return 1; //we ignore it - but it's not a parse error
+ }
//let Bots always add themselves! (maybe they join invisible)
if(user == NULL) {
user = addUserMask(from);
//request member list
chan->chanbot = user;
struct ChanUser *chanuser = addChanUser(chan, user); //it must be a bot
- get_userlist(chan, got_channel_userlist, chanuser);
+ 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(!isUserOnChan(user, chan) && ((chan->flags & CHANFLAG_RECEIVED_USERLIST) || isBot(user))) {
struct ChanUser *chanuser = addChanUser(chan, user);
- if(isModeSet(chan->modes, 'D'))
+ if(isBot(user) && isModeSet(chan->modes, 'D')) //if the bot joins a channel it could also be invisible
chanuser->flags |= CHANUSERFLAG_INVISIBLE;
if(!noEvent) {
if(wasRegistering)
chanuser->flags &= ~CHANUSERFLAG_VOICED;
chanuser->flags |= CHANUSERFLAG_OPPED;
}
+ 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;
+ if(user == NULL) {
+ DESYNCHRONIZE(cache_sync);
+ return 0;
+ }
struct ChanNode *chan = getChanByName(argv[0]);
- if(chan == NULL) return 0;
- if((!isBot(user) && chan->chanbot != client->user) || (isBot(user) && client->user != user)) return 1; //we ignore it - but it's not a parse error
+ if(chan == NULL) {
+ DESYNCHRONIZE(cache_sync);
+ return 0;
+ }
+ if((!isBot(user) && chan->chanbot != client->user) || (isBot(user) && client->user != user)) {
+ DESYNCHRONIZE(cache_sync);
+ 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;
}
free(chan->rejoin_array);
chan->flags &= ~CHANFLAG_REJOINING;
+ DESYNCHRONIZE(cache_sync);
return 0;
} else if(isUserOnChan(user, chan) && (chan->flags & CHANFLAG_RECEIVED_USERLIST)) {
struct ChanUser *chanuser = getChanUser(user, chan);
putsock(client, "PART %s :magic hop", chan->name);
putsock(client, "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
+ if(user == NULL) {
+ DESYNCHRONIZE(cache_sync);
+ return 0;
+ }
+ if(!is_firstBotSeeUser(client, user)) {
+ DESYNCHRONIZE(cache_sync);
+ 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
+ if((registering && (user->flags & USERFLAG_ISBOT))) {
+ DESYNCHRONIZE(cache_sync);
+ 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) {
struct ClientSocket *bot;
for(bot = getBots(0, NULL); bot; bot = getBots(0, bot)) {
if(bot->user == user) {
+ bot->flags &= ~SOCKET_FLAG_READY;
bot->user = NULL;
break;
}
registering_users = user;
} else
delUser(user, 1); //now we fully free the user
+ DESYNCHRONIZE(cache_sync);
return 1;
}
}
}
client->user = NULL;
+ client->flags &= ~SOCKET_FLAG_READY;
}
}
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]);
- if(chan == NULL || target == NULL) return 0;
- if(((!isBot(user) && chan->chanbot != client->user) || (isBot(user) && client->user != user))) return 1; //we ignore it - but it's not a parse error
+ if(chan == NULL || target == NULL) {
+ DESYNCHRONIZE(cache_sync);
+ return 0;
+ }
+ if(((!isBot(target) && chan->chanbot != client->user) || (isBot(target) && client->user != target))) {
+ DESYNCHRONIZE(cache_sync);
+ 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 = createTempUserMask(from);
+ if(!user) {
+ DESYNCHRONIZE(cache_sync);
+ return 0;
+ }
user->flags |= USERFLAG_ISTMPUSER;
}
struct ChanUser *chanuser = getChanUser(target, chan);
if(keep_channel && (chan->flags & CHANFLAG_RECEIVED_USERLIST) && !(chan->flags & CHANFLAG_REJOINING)) {
check_full_rejoin(chan);
}
+ DESYNCHRONIZE(cache_sync);
return 1;
}
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(chan->chanbot != client->user) {
+ 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;
}
static IRC_CMD(raw_privmsg) {
if(from == NULL || argc < 2) return 0;
+ if(!stricmplen(from, "*status", 7) || !stricmplen(from, "-sBNC", 5)) {
+ #ifdef HAVE_THREADS
+ unsigned int tid = (unsigned int) pthread_self_tid();
+ while(!clientsocket_parseorder_top(tid)) {
+ usleep(1000); //1ms
+ }
+ #endif
+ if(!match("Disconnected from IRC.*", argv[1])) {
+ //ZNC DISCONNECT
+ bot_disconnect(client);
+ return 1;
+ }
+ if(!match("* disconnected from the server.", argv[1])) {
+ //sBNC DISCONNECT
+ bot_disconnect(client);
+ return 1;
+ }
+ }
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
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
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 0;
+ }
+ if(isBot(user)) {
+ if(client->user != user) {
+ DESYNCHRONIZE(cache_sync);
+ return 1;
+ }
+ client_renamed(client);
+ }
+ else if(!is_firstBotSeeUser(client, user)) {
+ DESYNCHRONIZE(cache_sync);
+ return 1; //we ignore it - but it's not a parse error
+ }
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(chan->chanbot != client->user) {
+ 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
}
+ 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;
+}
+
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);
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];