/* * Copyright (C) 2002-2004 Morten Brix Pedersen * * 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 * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #include #include #include #include #include #include "Parser.h" #include "Utils.h" #include "Channel.h" #include "FrontEnd.h" #include "LostIRCApp.h" #include "ServerConnection.h" #include "DCC.h" using std::vector; using Glib::ustring; /* different functions used in for_each */ namespace algo { struct removeUser : public std::unary_function { removeUser(const Glib::ustring& n) : nick(n) { } void operator() (ChannelBase* x) { Channel *c = dynamic_cast(x); if (c) c->removeUser(nick); } Glib::ustring nick; }; struct renameUser : public std::unary_function { renameUser(const Glib::ustring& f, const Glib::ustring& t) : from(f), to(t) { } void operator() (ChannelBase* x) { x->renameUser(from, to); } Glib::ustring from; Glib::ustring to; }; } Parser::Parser(ServerConnection *conn) : _conn(conn) { } void Parser::parseLine(ustring& data) { #ifdef DEBUG App->log << "<< " << data << std::endl; #endif if (App->options.strip_colors) data = stripColors(data, App->options.strip_boldandunderline); if (data[0] == ':') { /* * Message in the form: * message = [ ":" prefix SPACE ] command [ params ] crlf * prefix = servername / ( nickname [ [ "!" user ] "@" host ] ) * command = 1*letter / 3digit * params = *14( SPACE middle ) [ SPACE ":" trailing ] * =/ 14( SPACE middle ) [ SPACE [ ":" ] trailing ] */ // Find prefix ustring::size_type pos1 = data.find(" ", 1); ustring from = data.substr(1, pos1 - 1); // Find command ustring::size_type pos2 = data.find(" ", pos1 + 1); ustring command = data.substr(pos1 + 1, (pos2 - 1) - pos1); // Check whether there is any params ustring::size_type pos3 = data.find(" :", pos2); ustring param; if (pos3 != pos2) { // We have params param = data.substr(pos2 + 1, (pos3 - 1) - pos2); } // Get rest (whats after the ':', if there were any ':') ustring rest; if (pos3 != ustring::npos) { rest = data.substr(pos3 + 2); } #ifdef DEBUG App->log << "\t[from '" << from << "']"; App->log << " [command '" << command << "']"; App->log << " [param '" << param << "']"; App->log << " [rest '" << rest << "']" << std::endl; #endif // Redirect to the right parsing function... int n = Util::convert(command); if (n) numeric(n, from, param, rest); else if (command == "PRIVMSG") Privmsg(from, param, rest); else if (command == "JOIN") Join(from, param, rest); else if (command == "PART") Part(from, param, rest); else if (command == "QUIT") Quit(from, rest); else if (command == "PONG") // we received our lag check.. _conn->Session.sentLagCheck = false; else if (command == "MODE") Mode(from, param, rest); else if (command == "TOPIC") Topic(from, param, rest); else if (command == "NOTICE") Notice(from, param, rest); else if (command == "KICK") Kick(from, param, rest); else if (command == "NICK") Nick(from, rest); else if (command == "WALLOPS") Wallops(from, rest); else if (command == "INVITE") Invite(from, rest); else if (command == "KILL") Kill(from, rest); else FE::emit(FE::get(UNKNOWN) << data, FE::CURRENT, _conn); } else { // Parse ustring in form, eg. 'PING :23523525' ustring::size_type pos1 = data.find_first_of(" ", 0); ustring command = data.substr(0, pos1); // Check whether there are any params ustring::size_type pos2 = data.find_first_of(":", pos1 + 1); ustring param; if ((pos2 - 1) != pos1) { // We have params param = data.substr(pos1 + 1, (pos2 - 2) - pos1); } // Get rest (whats after the ':') ustring rest = data.substr(pos2 + 1); #ifdef DEBUG App->log << "\t[command '" << command << "']"; App->log << " [param '" << param << "']"; App->log << " [rest '" << rest << "']" << std::endl; #endif // Redirect to the right parsing function... if (command == "PING") Ping(rest); else if (command == "NOTICE") Notice(param + " :" + rest); else if (command == "ERRORMSG") FE::emit(FE::get(ERRORMSG) << param + " " + rest, FE::CURRENT, _conn); else FE::emit(FE::get(SERVERMSG1) << data, FE::CURRENT, _conn); } } inline void Parser::Ping(const ustring& rest) { _conn->sendPong(rest); } void Parser::Privmsg(const ustring& from, const ustring& param, const ustring& rest) { if (rest[0] == '\001') { // We got CTCP Ctcp(from, param, rest); } else { // Normal privmsg ChannelBase *chan; if (param == _conn->Session.nick) { // The message was intended for *us*, so its a query. chan = _conn->findQuery(findNick(from)); if (!chan) { chan = new Query(findNick(from)); _conn->Session.channels.push_back(chan); } } else { chan = _conn->findChannel(param); // Even though this should never happen, it happens with some // bouncers. if (!chan) return; } if (shouldHighlight(rest)) { FE::emit(FE::get(PRIVMSG_HIGHLIGHT) << findNick(from) << rest, *chan, _conn); App->fe->highlight(*chan, _conn); } else { FE::emit(FE::get(PRIVMSG) << findNick(from) << rest, *chan, _conn); } } } void Parser::Ctcp(const ustring& from, const ustring& param, const ustring& rest) { ustring::size_type pos = rest.find_first_of(" \001", 1); ustring command = rest.substr(1, pos - 1); if (command == "VERSION") { _conn->sendVersion(findNick(from)); } else if (command == "ACTION") { ustring rest_ = rest.substr(pos + 1, (rest.length() - pos) - 2); ChannelBase *chan; if (param == _conn->Session.nick) { // The message was intended for *us*, so its a query. chan = _conn->findQuery(findNick(from)); if (!chan) { chan = new Query(findNick(from)); _conn->Session.channels.push_back(chan); } } else { chan = _conn->findChannel(param); // Even though this should never happen, it happens with some // bouncers. if (!chan) return; } if (shouldHighlight(rest)) { FE::emit(FE::get(ACTION_HIGHLIGHT) << findNick(from) << rest_, *chan, _conn); App->fe->highlight(*chan, _conn); } else { FE::emit(FE::get(ACTION) << findNick(from) << rest_, *chan, _conn); } return; } else if (command == "PING") { // Reply to the client with the same timestamp they sent us. ustring rest_ = rest.substr(pos + 1, (rest.length() - pos) - 2); _conn->sendCtcpNotice(findNick(from), "PING " + rest_); } else if (command == "DCC") { ustring dcc_type = getWord(rest, 2); ustring dcc_address = getWord(rest, 4); ustring dcc_port = getWord(rest, 5); if (dcc_type == "SEND") { ustring dcc_size = getWord(rest, 6); ustring dcc_filename = getWord(rest, 3); std::istringstream ss(dcc_address); unsigned long address; ss >> address; std::istringstream ss2(dcc_port); unsigned short port; ss2 >> port; std::istringstream ss3(dcc_size); unsigned long size; ss3 >> size; int n = App->getDcc().addDccSendIn(dcc_filename, findNick(from), address, port, size); if (n) FE::emit(FE::get(DCC_RECEIVE) << findNick(from) << dcc_filename << n, FE::CURRENT); } else if (dcc_type == "CHAT") { FE::emit(FE::get(SERVERMSG1) << findNick(from) << rest, FE::CURRENT); std::istringstream ss(dcc_address); unsigned long address; ss >> address; std::istringstream ss2(dcc_port); unsigned short port; ss2 >> port; //DCC d(findNick(from), address, port); } return; } if (param == _conn->Session.nick) FE::emit(FE::get(CTCP) << command << findNick(from), FE::CURRENT, _conn); else FE::emit(FE::get(CTCP_MULTI) << command << findNick(from) << param, FE::CURRENT, _conn); } void Parser::Notice(const ustring& from, const ustring& to, const ustring& rest) { if (rest[0] == '\001') { // CTCP notice std::string tmp = rest; std::string::iterator i = remove(tmp.begin(), tmp.end(), '\001'); ustring output(tmp.begin(), i); FE::emit(FE::get(CTCP_REPLY) << getWord(output, 1) << findNick(from) << skipFirstWord(output), FE::CURRENT, _conn); } else { // Normal notice if (to == _conn->Session.nick) { FE::emit(FE::get(NOTICEPRIV) << findNick(from) << rest, FE::CURRENT, _conn); } else { Channel *c = _conn->findChannel(to); if (c) FE::emit(FE::get(NOTICEPUBL) << findNick(from) << to << rest, *c, _conn); else FE::emit(FE::get(NOTICEPUBL) << findNick(from) << to << rest, FE::CURRENT, _conn); } } } void Parser::Notice(const ustring& msg) { ustring::size_type pos = msg.find_first_of(" "); ustring from = msg.substr(0, pos); ustring rest = msg.substr(pos + 1); FE::emit(FE::get(NOTICEPRIV) << from << rest, FE::CURRENT, _conn); } void Parser::Kick(const ustring& from, const ustring& param, const ustring& msg) { ustring chan, nick; std::istringstream ss(param); ss >> chan; ss >> nick; Channel *c = _conn->findChannel(chan); assert(c); c->removeUser(findNick(nick)); FE::emit(FE::get(KICKED) << nick << chan << findNick(from) << msg, *c, _conn); App->fe->kick(findNick(from), *c, nick, msg, _conn); if (nick == _conn->Session.nick) // We got kicked _conn->removeChannel(chan); } void Parser::Join(const ustring& nick, const ustring& param, const ustring& rest) { ustring chan = param; // Some clients/servers/bouncers might accidently send the channel name // in the 'rest' string, a bug there, but we would like to avoid a // segfault here. I noticed the same hack in the xchat source. if (chan.empty() && !rest.empty()) chan = getWord(rest, 1); Channel *c; if (findNick(nick) == _conn->Session.nick) { c = new Channel(chan); _conn->Session.channels.push_back(c); } else { c = _conn->findChannel(chan); assert(c); c->addUser(findNick(nick)); } App->fe->join(findNick(nick), *c, _conn); FE::emit(FE::get(JOIN) << findNick(nick) << chan << findHost(nick), *c, _conn); } void Parser::Part(const ustring& nick, const ustring& param, ustring& rest) { ustring chan = param; // Some clients/servers/bouncers might accidently send the channel name // in the 'rest' string, a bug there, but we would like to avoid a // segfault here. I noticed the same hack in the xchat sources. if (chan.empty() && !rest.empty()) { chan = getWord(rest, 1); rest.erase(); } Channel *c = _conn->findChannel(chan); if (c) { c->removeUser(findNick(nick)); if (rest.empty()) FE::emit(FE::get(PART2) << findNick(nick) << chan << findHost(nick) << rest, *c, _conn); else FE::emit(FE::get(PART) << findNick(nick) << chan << findHost(nick) << rest, *c, _conn); App->fe->part(findNick(nick), *c, _conn); if (findNick(nick) == _conn->Session.nick) _conn->removeChannel(chan); } } void Parser::Quit(const ustring& nick, const ustring& msg) { vector chans = _conn->findUser(findNick(nick)); for_each(chans.begin(), chans.end(), algo::removeUser(nick)); App->fe->quit(findNick(nick), chans, _conn); if (msg.empty()) FE::emit(FE::get(QUIT2) << findNick(nick), chans, _conn); else FE::emit(FE::get(QUIT) << findNick(nick) << msg, chans, _conn); } void Parser::Nick(const ustring& from, const ustring& to) { // When we receive an error that "nick change was too fast", 'to' will // be empty. just return if it is. if (to.empty()) return; // Check whethers it's us who has changed nick if (findNick(from) == _conn->Session.nick) { _conn->Session.nick = to; } vector chans = _conn->findUser(findNick(from)); App->fe->nick(findNick(from), to, chans, _conn); for_each(chans.begin(), chans.end(), algo::renameUser(findNick(from), to)); FE::emit(FE::get(NICK) << findNick(from) << to, chans, _conn); } void Parser::Invite(const ustring& from, const ustring& rest) { FE::emit(FE::get(INVITED) << findNick(from) << rest, FE::CURRENT, _conn); } void Parser::Kill(const ustring& from, const ustring& rest) { FE::emit(FE::get(KILLED) << findNick(from) << rest, FE::CURRENT, _conn); } void Parser::Mode(const ustring& from, const ustring& param, const ustring& rest) { if (rest.empty()) { // We encountered a channel mode message CMode(from, param); } else { // User mode message // We got line in the form: 'user +x' FE::emit(FE::get(MODE) << findNick(from) << param << rest, FE::CURRENT, _conn); } } void Parser::CMode(const ustring& from, const ustring& param) { // Parse line in the form: '#chan +ovo nick nick2 nick3' ustring::size_type pos1 = param.find_first_of(" "); ustring::size_type pos2 = param.find_first_of(" ", pos1 + 1); // No real modes? No reason to continue. if (pos1 == ustring::npos) return; ustring channel = param.substr(0, pos1); ustring modes = param.substr(pos1 + 1, (pos2 - pos1) - 1); ustring args = param.substr(pos2 + 1); Channel *chan = _conn->findChannel(channel); // Channel not found? Not sane to continue. // This happened on a proxy/bouncer where no channel was mentioned in // MODE, just this: // :nick!ident@host.com MODE nick +iw // Note the lack of ':' before +iw. if (!chan) return; // Get arguments vector arguments; Util::tokenizeWords(args, arguments); std::vector modesvec; bool sign = false; // Used to track whether we get a + or a - vector::const_iterator arg_i = arguments.begin(); for (ustring::iterator i = modes.begin(); i != modes.end(); ++i) { switch (*i) { case '+': sign = true; break; case '-': sign = false; break; case 'a': { Event e; IRC::UserMode mode = IRC::ADMIN; sign ? (e = ADMINED) : (e = DEADMINED); ustring nick = *arg_i++; User *user = chan->getUser(nick); sign ? (user->setMode(mode)) : (user->removeMode(mode)); modesvec.push_back(*user); FE::emit(FE::get(e) << findNick(from) << nick, *chan, _conn); } break; case 'o': { Event e; IRC::UserMode mode = IRC::OP; sign ? (e = OPPED) : (e = DEOPPED); ustring nick = *arg_i++; User *user = chan->getUser(nick); sign ? (user->setMode(mode)) : (user->removeMode(mode)); modesvec.push_back(*user); FE::emit(FE::get(e) << findNick(from) << nick, *chan, _conn); } break; case 'v': { Event e; IRC::UserMode mode = IRC::VOICE; sign ? (e = VOICED) : (e = DEVOICED); ustring nick = *arg_i++; User *user = chan->getUser(nick); sign ? (user->setMode(mode)) : (user->removeMode(mode)); modesvec.push_back(*user); FE::emit(FE::get(e) << findNick(from) << nick, *chan, _conn); } break; case 'h': { Event e; IRC::UserMode mode = IRC::HALFOP; sign ? (e = HALFOPPED) : (e = HALFDEOPPED); ustring nick = *arg_i++; User *user = chan->getUser(nick); sign ? (user->setMode(mode)) : (user->removeMode(mode)); modesvec.push_back(*user); FE::emit(FE::get(e) << findNick(from) << nick, *chan, _conn); } break; case 'b': { Event e; sign ? (e = BANNED) : (e = UNBANNED); ustring nick = *arg_i++; FE::emit(FE::get(e) << findNick(from) << nick, *chan, _conn); break; } default: { // none of the above modes, just display a default // "foo set mode +X user" message. or alternatively // MODE #chan +l 500 char modebuf[3]; if (sign) modebuf[0] = '+'; else modebuf[0] = '-'; modebuf[1] = (*i); modebuf[2] = '\0'; if (isChannelMode(*i)) { FE::emit(FE::get(CMODE) << findNick(from) << modebuf << channel, *chan, _conn); } else { ustring nick; if (arg_i == arguments.end()) nick = *--arg_i; else nick = *arg_i++; FE::emit(FE::get(MODE) << findNick(from) << modebuf << nick, *chan, _conn); } } } } // Channel user mode App->fe->CUMode(findNick(from), *chan, modesvec, _conn); } void Parser::Topic(const ustring& from, const ustring& chan, const ustring& rest) { Channel *c = _conn->findChannel(chan); if (c) FE::emit(FE::get(TOPICCHANGE) << findNick(from) << rest, *c, _conn); else FE::emit(FE::get(TOPICCHANGE) << findNick(from) << rest, FE::CURRENT, _conn); } void Parser::Topic(const ustring& param, const ustring& rest) { ustring::size_type pos1 = param.find_first_of("#&+!"); ustring::size_type pos2 = param.find_first_of(" ", pos1); ustring chan = param.substr(pos1, pos2 - pos1); Channel *c = _conn->findChannel(chan); if (c) FE::emit(FE::get(TOPICIS) << chan << rest, *c, _conn); else FE::emit(FE::get(TOPICIS) << chan << rest, FE::CURRENT, _conn); } void Parser::TopicTime(const ustring& param) { // Find channel ustring::size_type pos1 = param.find_first_of("#&+!"); ustring::size_type pos2 = param.find_first_of(" ", pos1); ustring chan = param.substr(pos1, pos2 - pos1); // Find user who set the topic ustring::size_type pos3 = param.find_first_of(" ", pos2 + 1); ustring nick = param.substr(pos2 + 1, (pos3 - pos2) - 1); ustring time = param.substr(pos3 + 1); long date = std::atol(time.c_str()); time = std::ctime(reinterpret_cast(&date)); Channel *c = _conn->findChannel(chan); if (c) FE::emit(FE::get(TOPICTIME) << nick << time.substr(0, time.size() - 1), *c, _conn); else FE::emit(FE::get(TOPICTIME) << nick << time.substr(0, time.size() - 1), FE::CURRENT, _conn); } void Parser::Away(const ustring& param, const ustring& rest) { ustring param1, param2; std::istringstream ss(param); ss >> param1; ss >> param2; FE::emit(FE::get(AWAY) << param2 << rest, FE::CURRENT, _conn); } void Parser::Wallops(const ustring& from, const ustring& rest) { FE::emit(FE::get(WALLOPS) << findNick(from) << rest, FE::CURRENT, _conn); } void Parser::Banlist(const ustring& param) { ustring dummy, chan, banmask, owner, time; std::istringstream ss(param); ss >> dummy; ss >> chan; ss >> banmask; ss >> owner; ss >> time; long date = std::atol(time.c_str()); time = std::ctime((const time_t *)&date); Channel *c = _conn->findChannel(chan); assert(c); FE::emit(FE::get(BANLIST) << banmask << owner, *c, _conn); } // Function to parse the 005 numeric string, also known as RPL_ISUPPORT // Right now only a small subset of the parameters are being used, namely: // // CHANTYPES, PREFIX, CHANMODES, NETWORK // // The specification can be found (at the time of writing) at: // http://www.irc.org/tech_docs/005.html // http://www.irc.org/tech_docs/draft-brocklesby-irc-isupport-03.txt void Parser::parseIRCSupports(const ustring& supports) { ustring::size_type pos; pos = supports.find("CHANTYPES="); if (pos != ustring::npos) { pos += 10; ustring::size_type endpos = supports.find_first_of(" ", pos); _conn->supports.chantypes = supports.substr(pos, endpos - pos); } pos = supports.find("PREFIX="); if (pos != ustring::npos) { pos += 7; ustring::size_type endpos = supports.find_first_of(" ", pos); _conn->supports.prefix = supports.substr(pos, endpos - pos); } pos = supports.find("CHANMODES="); if (pos != ustring::npos) { pos += 10; ustring::size_type endpos = supports.find_first_of(" ", pos); _conn->supports.chanmodes = supports.substr(pos, endpos - pos); } pos = supports.find("NETWORK="); if (pos != ustring::npos) { pos += 8; ustring::size_type endpos = supports.find_first_of(" ", pos); _conn->supports.network = supports.substr(pos, endpos - pos); } } void Parser::numeric(int n, const ustring& from, const ustring& param, const ustring& rest) { switch(n) { case 5: // RPL_ISUPPORT parseIRCSupports(skipFirstWord(param)); case 4: // RPL_MYINFO case 252: // RPL_LUSEROP case 253: // RPL_LUSERUNKNOWN case 254: // RPL_LUSERCHANNELS FE::emit(FE::get(SERVERMSG1) << skipFirstWord(param) << rest, FE::CURRENT, _conn); break; case 1: // RPL_WELCOME _conn->Session.servername = from; _conn->Session.hasRegistered = true; _conn->Session.nick = param; _conn->addConnectionTimerCheck(); App->fe->connected(_conn); case 2: // RPL_YOURHOST case 3: // RPL_CREATED case 251: // RPL_LUSERCLIENT case 255: // RPL_LUSERME FE::emit(FE::get(SERVERMSG1) << rest, FE::CURRENT, _conn); break; case 301: // RPL_AWAY Away(param, rest); break; case 305: // RPL_UNAWAY _conn->Session.isAway = false; App->fe->away(false, _conn); FE::emit(FE::get(SERVERMSG1) << rest, FE::CURRENT, _conn); break; case 306: // RPL_NOWAWAY _conn->Session.isAway = true; App->fe->away(true, _conn); FE::emit(FE::get(SERVERMSG1) << rest, FE::CURRENT, _conn); break; case 332: // RPL_TOPIC Topic(param, rest); break; case 333: TopicTime(param); break; case 367: // RPL_BANLIST Banlist(param); break; case 368: // RPL_END_OF_BANLIST case 372: // RPL_MOTD case 375: // RPL_MOTDSTART FE::emit(FE::get(SERVERMSG1) << rest, FE::CURRENT, _conn); break; case 376: // RPL_ENDOFMOTD FE::emit(FE::get(SERVERMSG1) << rest, FE::CURRENT, _conn); _conn->Session.endOfMotd = true; _conn->sendCmds(); break; case 401: // ERR_NOSUCNICK case 403: // ERR_NOSUCHCHANNEL case 404: // ERR_CANNOTSENDTOCHAN case 405: // ERR_TOOMANYCHANNELS FE::emit(FE::get(ERRORMSG) << skipFirstWord(param) + ": " + rest, FE::CURRENT, _conn); break; case 412: // ERR_NOTEXTTOSEND FE::emit(FE::get(SERVERMSG1) << rest, FE::CURRENT, _conn); break; case 422: // ERR_NOMOTD _conn->sendCmds(); break; case 433: // ERR_NICKNAMEINUSE // Apply a _ to the nickname if we havn't received MOTD // XXX: also send msg to frontend? if (!_conn->Session.endOfMotd) _conn->sendNick(_conn->Session.nick += "_"); else FE::emit(FE::get(ERRORMSG) << rest, FE::CURRENT, _conn); break; case 438: // Nick change to fast case 442: // ERR_NOTONCHANNEL case 443: // ERR_USERONCHANNEL case 451: // ERR_NOTREGISTERED case 461: // ERR_NEEDMOREPARAMS case 462: // ERR_ALLREADYREGISTERED case 464: // ERR_PASSWDMISMATCH case 465: // ERR_YOUREBANNEDCREEP case 467: // ERR_KEYSET case 471: // ERR_CHANNELISFULL case 472: // ERR_UNKNOWMODE case 473: // ERR_INVITEONLYCHAN case 474: // ERR_BANNEDFROMCHAN case 475: // ERR_BADCHANNELKEY case 481: // ERR_NOPRIVILEGES case 482: // ERR_CHANOPRIVSNEEDED case 491: // ERR_NOOPERHOST case 501: // ERR_UMODEUNKNOWNFLAG case 502: // ERR_USERSDONTMATCH FE::emit(FE::get(ERRORMSG) << skipFirstWord(param) + " " + rest, FE::CURRENT, _conn); break; case 353: // RPL_NAMREPLY Names(param, rest); break; case 366: // RPL_ENDOFNAMES { Channel *c = _conn->findChannel(getWord(param, 2)); if (c && !c->endOfNames) { c->endOfNames = true; App->fe->names(*c, _conn); } else { FE::emit(FE::get(SERVERMSG2) << getWord(param, 2) << rest, FE::CURRENT, _conn); } } break; case 317: // RPL_WHOISIDLE { long idle = Util::convert(getWord(param, 3)); std::ostringstream ss; ss << _("idle: "); ss << idle / 3600 << ":" << (idle / 60) % 60 << ":" << idle % 60; long date = std::atol(getWord(param, 4).c_str()); ustring time = std::ctime(reinterpret_cast(&date)); ss << _(", signon time: ") << time.substr(0, time.size() - 1); FE::emit(FE::get(WHOIS_GENERIC) << getWord(param, 2) << ss.str(), FE::CURRENT, _conn); } break; case 314: // RPL_WHOWASUSER case 311: // RPL_WHOISUSER FE::emit(FE::get(WHOIS_USER) << getWord(param, 2) << getWord(param, 3) << getWord(param, 4) << rest, FE::CURRENT, _conn); break; case 312: // RPL_WHOISSERVER FE::emit(FE::get(WHOIS_SERVER) << getWord(param, 2) << getWord(param, 3) << rest, FE::CURRENT, _conn); break; case 313: // RPL_WHOISOPERATOR // We need this find_first_of to omit the first word FE::emit(FE::get(WHOIS_GENERIC) << getWord(param, 2) << rest, FE::CURRENT, _conn); break; case 318: // RPL_ENDOFWHOIS break; case 319: // RPL_WHOISCHANNELS FE::emit(FE::get(WHOIS_CHANNELS) << getWord(param, 2) << rest, FE::CURRENT, _conn); break; case 320: // NickServ (freenode.net only?) "foo is an identified user" reply. FE::emit(FE::get(WHOIS_GENERIC) << getWord(param, 2) << rest, FE::CURRENT, _conn); break; case 330: // QuakeNet "foo is authed as" reply. FE::emit(FE::get(WHOIS_GENERIC) << getWord(param, 2) << rest + " " + getWord(param, 3), FE::CURRENT, _conn); break; case 421: // ERR_UNKNOWNCOMMAND FE::emit(FE::get(SERVERMSG2) << getWord(param, 2) << rest, FE::CURRENT, _conn); break; default: FE::emit(FE::get(SERVERMSG2) << skipFirstWord(param) << rest, FE::CURRENT, _conn); } } void Parser::Names(const ustring& chan, const ustring& names) { // Find channel from a ustring like 'nick = #chan' ustring::size_type pos = chan.find_first_of("#&+!"); ustring channel = chan.substr(pos); Channel *c = _conn->findChannel(channel); if (c && !c->endOfNames) { vector nicks; Util::tokenizeWords(names, nicks); vector::const_iterator i; for (i = nicks.begin(); i != nicks.end(); ++i) { if ((*i)[0] == '*') c->addUser(i->substr(1), IRC::OWNER); else if ((*i)[0] == '!') c->addUser(i->substr(1), IRC::ADMIN); else if ((*i)[0] == '@') c->addUser(i->substr(1), IRC::OP); else if ((*i)[0] == '+') c->addUser(i->substr(1), IRC::VOICE); else if ((*i)[0] == '%') c->addUser(i->substr(1), IRC::HALFOP); else c->addUser(*i, IRC::NONE); } } else { FE::emit(FE::get(NAMES) << channel << names, FE::CURRENT, _conn); } } bool Parser::shouldHighlight(const ustring& str) { if (str.find(_conn->Session.nick) != ustring::npos) return true; std::istringstream ss(App->options.highlight_words->raw()); ustring tmp; while (ss >> tmp) if (str.find(tmp) != ustring::npos) return true; return false; } bool Parser::isChannelMode(char m) { if (_conn->supports.chanmodes.find_first_of(m) != ustring::npos) return true; return false; } ustring getWord(const ustring& str, int n) { int count = 0; ustring::size_type lastPos = str.find_first_not_of(" ", 0); ustring::size_type pos = str.find_first_of(" ", lastPos); while (pos != ustring::npos || lastPos != ustring::npos) { if ((count + 1) == n) return str.substr(lastPos, pos - lastPos); lastPos = str.find_first_not_of(" ", pos); pos = str.find_first_of(" ", lastPos); count++; } return ""; } // Strip mIRC colors. Spec at: http://www.mirc.co.uk/help/color.txt // Only strips color-strings. Not bold and underline. ustring stripColors(const ustring& str, const bool stripBoldAndUnderline) { ustring newstr; bool color = false; int numbercount = 0; for (ustring::size_type i = 0; i < str.length(); ++i) { if (str[i] == '\017') { // RESET color = false; } else if (stripBoldAndUnderline && str[i] == '\002') { // BOLD // No-op. } else if (stripBoldAndUnderline && str[i] == '\037') { // UNDERLINE // No-op. } else if (str[i] == '\003') { // COLOR color = true; } else if (color && isdigit(str[i]) && numbercount < 2) { numbercount++; } else if (color && str[i] == ',' && numbercount < 3) { numbercount = 0; } else { numbercount = 0; color = false; newstr += str[i]; } } return newstr; } Glib::ustring findNick(const Glib::ustring& str) { return str.substr(0, str.find_first_of("!")); } Glib::ustring findHost(const Glib::ustring& str) { return str.substr(str.find_first_of("!") + 1); } Glib::ustring skipFirstWord(const Glib::ustring& str) { return str.substr(str.find_first_of(" ") + 1); }