X-Git-Url: https://git.quassel-irc.org/?p=quassel.git;a=blobdiff_plain;f=src%2Fcore%2Fctcpparser.cpp;h=7517db6f4729756b13e7ac48639099b52103c044;hp=63c3dde92150e15bf9da31ac17ee48b1cc4f62e4;hb=fd92bfb1f093dcc6295ca9de2c34780d288b0fe2;hpb=fdfd62334f728bd05470c5191194d55027fec86e diff --git a/src/core/ctcpparser.cpp b/src/core/ctcpparser.cpp index 63c3dde9..7517db6f 100644 --- a/src/core/ctcpparser.cpp +++ b/src/core/ctcpparser.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2005-2012 by the Quassel Project * + * Copyright (C) 2005-2013 by the Quassel Project * * devel@quassel-irc.org * * * * This program is free software; you can redistribute it and/or modify * @@ -15,11 +15,12 @@ * 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. * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #include "ctcpparser.h" +#include "corenetworkconfig.h" #include "coresession.h" #include "ctcpevent.h" #include "messageevent.h" @@ -27,236 +28,319 @@ const QByteArray XDELIM = "\001"; CtcpParser::CtcpParser(CoreSession *coreSession, QObject *parent) - : QObject(parent), + : QObject(parent), _coreSession(coreSession) { - QByteArray MQUOTE = QByteArray("\020"); - _ctcpMDequoteHash[MQUOTE + '0'] = QByteArray(1, '\000'); - _ctcpMDequoteHash[MQUOTE + 'n'] = QByteArray(1, '\n'); - _ctcpMDequoteHash[MQUOTE + 'r'] = QByteArray(1, '\r'); - _ctcpMDequoteHash[MQUOTE + MQUOTE] = MQUOTE; + QByteArray MQUOTE = QByteArray("\020"); + _ctcpMDequoteHash[MQUOTE + '0'] = QByteArray(1, '\000'); + _ctcpMDequoteHash[MQUOTE + 'n'] = QByteArray(1, '\n'); + _ctcpMDequoteHash[MQUOTE + 'r'] = QByteArray(1, '\r'); + _ctcpMDequoteHash[MQUOTE + MQUOTE] = MQUOTE; - QByteArray XQUOTE = QByteArray("\134"); - _ctcpXDelimDequoteHash[XQUOTE + XQUOTE] = XQUOTE; - _ctcpXDelimDequoteHash[XQUOTE + QByteArray("a")] = XDELIM; + setStandardCtcp(_coreSession->networkConfig()->standardCtcp()); - connect(this, SIGNAL(newEvent(Event *)), _coreSession->eventManager(), SLOT(postEvent(Event *))); + connect(_coreSession->networkConfig(), SIGNAL(standardCtcpSet(bool)), this, SLOT(setStandardCtcp(bool))); + connect(this, SIGNAL(newEvent(Event *)), _coreSession->eventManager(), SLOT(postEvent(Event *))); } + +void CtcpParser::setStandardCtcp(bool enabled) +{ + QByteArray XQUOTE = QByteArray("\134"); + if (enabled) + _ctcpXDelimDequoteHash[XQUOTE + XQUOTE] = XQUOTE; + else + _ctcpXDelimDequoteHash.remove(XQUOTE + XQUOTE); + _ctcpXDelimDequoteHash[XQUOTE + QByteArray("a")] = XDELIM; +} + + void CtcpParser::displayMsg(NetworkEvent *event, Message::Type msgType, const QString &msg, const QString &sender, - const QString &target, Message::Flags msgFlags) { - if(event->testFlag(EventManager::Silent)) - return; + const QString &target, Message::Flags msgFlags) +{ + if (event->testFlag(EventManager::Silent)) + return; - MessageEvent *msgEvent = new MessageEvent(msgType, event->network(), msg, sender, target, msgFlags); - msgEvent->setTimestamp(event->timestamp()); + MessageEvent *msgEvent = new MessageEvent(msgType, event->network(), msg, sender, target, msgFlags); + msgEvent->setTimestamp(event->timestamp()); - emit newEvent(msgEvent); + emit newEvent(msgEvent); } -QByteArray CtcpParser::lowLevelQuote(const QByteArray &message) { - QByteArray quotedMessage = message; - QHash quoteHash = _ctcpMDequoteHash; - QByteArray MQUOTE = QByteArray("\020"); - quoteHash.remove(MQUOTE + MQUOTE); - quotedMessage.replace(MQUOTE, MQUOTE + MQUOTE); +QByteArray CtcpParser::lowLevelQuote(const QByteArray &message) +{ + QByteArray quotedMessage = message; + + QHash quoteHash = _ctcpMDequoteHash; + QByteArray MQUOTE = QByteArray("\020"); + quoteHash.remove(MQUOTE + MQUOTE); + quotedMessage.replace(MQUOTE, MQUOTE + MQUOTE); - QHash::const_iterator quoteIter = quoteHash.constBegin(); - while(quoteIter != quoteHash.constEnd()) { - quotedMessage.replace(quoteIter.value(), quoteIter.key()); - quoteIter++; - } - return quotedMessage; + QHash::const_iterator quoteIter = quoteHash.constBegin(); + while (quoteIter != quoteHash.constEnd()) { + quotedMessage.replace(quoteIter.value(), quoteIter.key()); + quoteIter++; + } + return quotedMessage; } -QByteArray CtcpParser::lowLevelDequote(const QByteArray &message) { - QByteArray dequotedMessage; - QByteArray messagepart; - QHash::iterator ctcpquote; - - // copy dequote Message - for(int i = 0; i < message.size(); i++) { - messagepart = message.mid(i,1); - if(i+1 < message.size()) { - for(ctcpquote = _ctcpMDequoteHash.begin(); ctcpquote != _ctcpMDequoteHash.end(); ++ctcpquote) { - if(message.mid(i,2) == ctcpquote.key()) { - messagepart = ctcpquote.value(); - ++i; - break; + +QByteArray CtcpParser::lowLevelDequote(const QByteArray &message) +{ + QByteArray dequotedMessage; + QByteArray messagepart; + QHash::iterator ctcpquote; + + // copy dequote Message + for (int i = 0; i < message.size(); i++) { + messagepart = message.mid(i, 1); + if (i+1 < message.size()) { + for (ctcpquote = _ctcpMDequoteHash.begin(); ctcpquote != _ctcpMDequoteHash.end(); ++ctcpquote) { + if (message.mid(i, 2) == ctcpquote.key()) { + messagepart = ctcpquote.value(); + ++i; + break; + } + } } - } + dequotedMessage += messagepart; } - dequotedMessage += messagepart; - } - return dequotedMessage; + return dequotedMessage; } -QByteArray CtcpParser::xdelimQuote(const QByteArray &message) { - QByteArray quotedMessage = message; - QHash::const_iterator quoteIter = _ctcpXDelimDequoteHash.constBegin(); - while(quoteIter != _ctcpXDelimDequoteHash.constEnd()) { - quotedMessage.replace(quoteIter.value(), quoteIter.key()); - quoteIter++; - } - return quotedMessage; + +QByteArray CtcpParser::xdelimQuote(const QByteArray &message) +{ + QByteArray quotedMessage = message; + QHash::const_iterator quoteIter = _ctcpXDelimDequoteHash.constBegin(); + while (quoteIter != _ctcpXDelimDequoteHash.constEnd()) { + quotedMessage.replace(quoteIter.value(), quoteIter.key()); + quoteIter++; + } + return quotedMessage; } -QByteArray CtcpParser::xdelimDequote(const QByteArray &message) { - QByteArray dequotedMessage; - QByteArray messagepart; - QHash::iterator xdelimquote; - - for(int i = 0; i < message.size(); i++) { - messagepart = message.mid(i,1); - if(i+1 < message.size()) { - for(xdelimquote = _ctcpXDelimDequoteHash.begin(); xdelimquote != _ctcpXDelimDequoteHash.end(); ++xdelimquote) { - if(message.mid(i,2) == xdelimquote.key()) { - messagepart = xdelimquote.value(); - i++; - break; + +QByteArray CtcpParser::xdelimDequote(const QByteArray &message) +{ + QByteArray dequotedMessage; + QByteArray messagepart; + QHash::iterator xdelimquote; + + for (int i = 0; i < message.size(); i++) { + messagepart = message.mid(i, 1); + if (i+1 < message.size()) { + for (xdelimquote = _ctcpXDelimDequoteHash.begin(); xdelimquote != _ctcpXDelimDequoteHash.end(); ++xdelimquote) { + if (message.mid(i, 2) == xdelimquote.key()) { + messagepart = xdelimquote.value(); + i++; + break; + } + } } - } + dequotedMessage += messagepart; } - dequotedMessage += messagepart; - } - return dequotedMessage; + return dequotedMessage; } -void CtcpParser::processIrcEventRawNotice(IrcEventRawMessage *event) { - parse(event, Message::Notice); -} -void CtcpParser::processIrcEventRawPrivmsg(IrcEventRawMessage *event) { - parse(event, Message::Plain); +void CtcpParser::processIrcEventRawNotice(IrcEventRawMessage *event) +{ + parse(event, Message::Notice); } -void CtcpParser::parse(IrcEventRawMessage *e, Message::Type messagetype) { - QByteArray ctcp; - //lowlevel message dequote - QByteArray dequotedMessage = lowLevelDequote(e->rawMessage()); +void CtcpParser::processIrcEventRawPrivmsg(IrcEventRawMessage *event) +{ + parse(event, Message::Plain); +} - CtcpEvent::CtcpType ctcptype = e->type() == EventManager::IrcEventRawNotice - ? CtcpEvent::Reply - : CtcpEvent::Query; - Message::Flags flags = (ctcptype == CtcpEvent::Reply && !e->network()->isChannelName(e->target())) - ? Message::Redirected - : Message::None; +void CtcpParser::parse(IrcEventRawMessage *e, Message::Type messagetype) +{ + //lowlevel message dequote + QByteArray dequotedMessage = lowLevelDequote(e->rawMessage()); - QList ctcpEvents; - QUuid uuid; // needed to group all replies together + CtcpEvent::CtcpType ctcptype = e->type() == EventManager::IrcEventRawNotice + ? CtcpEvent::Reply + : CtcpEvent::Query; - // extract tagged / extended data - int xdelimPos = -1; - int xdelimEndPos = -1; - int spacePos = -1; - while((xdelimPos = dequotedMessage.indexOf(XDELIM)) != -1) { - if(xdelimPos > 0) - displayMsg(e, messagetype, targetDecode(e, dequotedMessage.left(xdelimPos)), e->prefix(), e->target(), flags); + Message::Flags flags = (ctcptype == CtcpEvent::Reply && !e->network()->isChannelName(e->target())) + ? Message::Redirected + : Message::None; - xdelimEndPos = dequotedMessage.indexOf(XDELIM, xdelimPos + 1); - if(xdelimEndPos == -1) { - // no matching end delimiter found... treat rest of the message as ctcp - xdelimEndPos = dequotedMessage.count(); - } - ctcp = xdelimDequote(dequotedMessage.mid(xdelimPos + 1, xdelimEndPos - xdelimPos - 1)); - dequotedMessage = dequotedMessage.mid(xdelimEndPos + 1); + if (coreSession()->networkConfig()->standardCtcp()) + parseStandard(e, messagetype, dequotedMessage, ctcptype, flags); + else + parseSimple(e, messagetype, dequotedMessage, ctcptype, flags); +} - //dispatch the ctcp command - QString ctcpcmd = targetDecode(e, ctcp.left(spacePos)); - QString ctcpparam = targetDecode(e, ctcp.mid(spacePos + 1)); - spacePos = ctcp.indexOf(' '); - if(spacePos != -1) { - ctcpcmd = targetDecode(e, ctcp.left(spacePos)); - ctcpparam = targetDecode(e, ctcp.mid(spacePos + 1)); +// only accept CTCPs in their simplest form, i.e. one ctcp, from start to +// end, no text around it; not as per the 'specs', but makes people happier +void CtcpParser::parseSimple(IrcEventRawMessage *e, Message::Type messagetype, QByteArray dequotedMessage, CtcpEvent::CtcpType ctcptype, Message::Flags flags) +{ + if (dequotedMessage.count(XDELIM) != 2 || dequotedMessage[0] != '\001' || dequotedMessage[dequotedMessage.count() -1] != '\001') { + displayMsg(e, messagetype, targetDecode(e, dequotedMessage), e->prefix(), e->target(), flags); } else { - ctcpcmd = targetDecode(e, ctcp); - ctcpparam = QString(); + int spacePos = -1; + QString ctcpcmd, ctcpparam; + + QByteArray ctcp = xdelimDequote(dequotedMessage.mid(1, dequotedMessage.count() - 2)); + spacePos = ctcp.indexOf(' '); + if (spacePos != -1) { + ctcpcmd = targetDecode(e, ctcp.left(spacePos)); + ctcpparam = targetDecode(e, ctcp.mid(spacePos + 1)); + } else { + ctcpcmd = targetDecode(e, ctcp); + ctcpparam = QString(); + } + ctcpcmd = ctcpcmd.toUpper(); + + // we don't want to block /me messages by the CTCP ignore list + if (ctcpcmd == QLatin1String("ACTION") || !coreSession()->ignoreListManager()->ctcpMatch(e->prefix(), e->network()->networkName(), ctcpcmd)) { + QUuid uuid = QUuid::createUuid(); + _replies.insert(uuid, CtcpReply(coreNetwork(e), nickFromMask(e->prefix()))); + CtcpEvent *event = new CtcpEvent(EventManager::CtcpEvent, e->network(), e->prefix(), e->target(), + ctcptype, ctcpcmd, ctcpparam, e->timestamp(), uuid); + emit newEvent(event); + CtcpEvent *flushEvent = new CtcpEvent(EventManager::CtcpEventFlush, e->network(), e->prefix(), e->target(), + ctcptype, "INVALID", QString(), e->timestamp(), uuid); + emit newEvent(flushEvent); + } } +} - ctcpcmd = ctcpcmd.toUpper(); - // we don't want to block /me messages by the CTCP ignore list - if(ctcpcmd == QLatin1String("ACTION") || !coreSession()->ignoreListManager()->ctcpMatch(e->prefix(), e->network()->networkName(), ctcpcmd)) { - if(uuid.isNull()) - uuid = QUuid::createUuid(); +void CtcpParser::parseStandard(IrcEventRawMessage *e, Message::Type messagetype, QByteArray dequotedMessage, CtcpEvent::CtcpType ctcptype, Message::Flags flags) +{ + QByteArray ctcp; + + QList ctcpEvents; + QUuid uuid; // needed to group all replies together + + // extract tagged / extended data + int xdelimPos = -1; + int xdelimEndPos = -1; + int spacePos = -1; + while ((xdelimPos = dequotedMessage.indexOf(XDELIM)) != -1) { + if (xdelimPos > 0) + displayMsg(e, messagetype, targetDecode(e, dequotedMessage.left(xdelimPos)), e->prefix(), e->target(), flags); + + xdelimEndPos = dequotedMessage.indexOf(XDELIM, xdelimPos + 1); + if (xdelimEndPos == -1) { + // no matching end delimiter found... treat rest of the message as ctcp + xdelimEndPos = dequotedMessage.count(); + } + ctcp = xdelimDequote(dequotedMessage.mid(xdelimPos + 1, xdelimEndPos - xdelimPos - 1)); + dequotedMessage = dequotedMessage.mid(xdelimEndPos + 1); + + //dispatch the ctcp command + QString ctcpcmd = targetDecode(e, ctcp.left(spacePos)); + QString ctcpparam = targetDecode(e, ctcp.mid(spacePos + 1)); + + spacePos = ctcp.indexOf(' '); + if (spacePos != -1) { + ctcpcmd = targetDecode(e, ctcp.left(spacePos)); + ctcpparam = targetDecode(e, ctcp.mid(spacePos + 1)); + } + else { + ctcpcmd = targetDecode(e, ctcp); + ctcpparam = QString(); + } + + ctcpcmd = ctcpcmd.toUpper(); - CtcpEvent *event = new CtcpEvent(EventManager::CtcpEvent, e->network(), e->prefix(), e->target(), - ctcptype, ctcpcmd, ctcpparam, e->timestamp(), uuid); - ctcpEvents << event; + // we don't want to block /me messages by the CTCP ignore list + if (ctcpcmd == QLatin1String("ACTION") || !coreSession()->ignoreListManager()->ctcpMatch(e->prefix(), e->network()->networkName(), ctcpcmd)) { + if (uuid.isNull()) + uuid = QUuid::createUuid(); + + CtcpEvent *event = new CtcpEvent(EventManager::CtcpEvent, e->network(), e->prefix(), e->target(), + ctcptype, ctcpcmd, ctcpparam, e->timestamp(), uuid); + ctcpEvents << event; + } } - } - if(!ctcpEvents.isEmpty()) { - _replies.insert(uuid, CtcpReply(coreNetwork(e), nickFromMask(e->prefix()))); - CtcpEvent *flushEvent = new CtcpEvent(EventManager::CtcpEventFlush, e->network(), e->prefix(), e->target(), - ctcptype, "INVALID", QString(), e->timestamp(), uuid); - ctcpEvents << flushEvent; - foreach(CtcpEvent *event, ctcpEvents) { - emit newEvent(event); + if (!ctcpEvents.isEmpty()) { + _replies.insert(uuid, CtcpReply(coreNetwork(e), nickFromMask(e->prefix()))); + CtcpEvent *flushEvent = new CtcpEvent(EventManager::CtcpEventFlush, e->network(), e->prefix(), e->target(), + ctcptype, "INVALID", QString(), e->timestamp(), uuid); + ctcpEvents << flushEvent; + foreach(CtcpEvent *event, ctcpEvents) { + emit newEvent(event); + } } - } - if(!dequotedMessage.isEmpty()) - displayMsg(e, messagetype, targetDecode(e, dequotedMessage), e->prefix(), e->target(), flags); + if (!dequotedMessage.isEmpty()) + displayMsg(e, messagetype, targetDecode(e, dequotedMessage), e->prefix(), e->target(), flags); } -void CtcpParser::sendCtcpEvent(CtcpEvent *e) { - CoreNetwork *net = coreNetwork(e); - if(e->type() == EventManager::CtcpEvent) { - QByteArray quotedReply; - QString bufname = nickFromMask(e->prefix()); - if(e->ctcpType() == CtcpEvent::Query && !e->reply().isNull()) { - if(_replies.contains(e->uuid())) - _replies[e->uuid()].replies << lowLevelQuote(pack(net->serverEncode(e->ctcpCmd()), - net->userEncode(bufname, e->reply()))); - else - // reply not caused by a request processed in here, so send it off immediately - reply(net, bufname, e->ctcpCmd(), e->reply()); + +void CtcpParser::sendCtcpEvent(CtcpEvent *e) +{ + CoreNetwork *net = coreNetwork(e); + if (e->type() == EventManager::CtcpEvent) { + QByteArray quotedReply; + QString bufname = nickFromMask(e->prefix()); + if (e->ctcpType() == CtcpEvent::Query && !e->reply().isNull()) { + if (_replies.contains(e->uuid())) + _replies[e->uuid()].replies << lowLevelQuote(pack(net->serverEncode(e->ctcpCmd()), + net->userEncode(bufname, e->reply()))); + else + // reply not caused by a request processed in here, so send it off immediately + reply(net, bufname, e->ctcpCmd(), e->reply()); + } + } + else if (e->type() == EventManager::CtcpEventFlush && _replies.contains(e->uuid())) { + CtcpReply reply = _replies.take(e->uuid()); + if (reply.replies.count()) + packedReply(net, reply.bufferName, reply.replies); } - } else if(e->type() == EventManager::CtcpEventFlush && _replies.contains(e->uuid())) { - CtcpReply reply = _replies.take(e->uuid()); - if(reply.replies.count()) - packedReply(net, reply.bufferName, reply.replies); - } } -QByteArray CtcpParser::pack(const QByteArray &ctcpTag, const QByteArray &message) { - if(message.isEmpty()) - return XDELIM + ctcpTag + XDELIM; - return XDELIM + ctcpTag + ' ' + xdelimQuote(message) + XDELIM; +QByteArray CtcpParser::pack(const QByteArray &ctcpTag, const QByteArray &message) +{ + if (message.isEmpty()) + return XDELIM + ctcpTag + XDELIM; + + return XDELIM + ctcpTag + ' ' + xdelimQuote(message) + XDELIM; } -void CtcpParser::query(CoreNetwork *net, const QString &bufname, const QString &ctcpTag, const QString &message) { - QList params; - params << net->serverEncode(bufname) << lowLevelQuote(pack(net->serverEncode(ctcpTag), net->userEncode(bufname, message))); - net->putCmd("PRIVMSG", params); + +void CtcpParser::query(CoreNetwork *net, const QString &bufname, const QString &ctcpTag, const QString &message) +{ + QList params; + params << net->serverEncode(bufname) << lowLevelQuote(pack(net->serverEncode(ctcpTag), net->userEncode(bufname, message))); + net->putCmd("PRIVMSG", params); } -void CtcpParser::reply(CoreNetwork *net, const QString &bufname, const QString &ctcpTag, const QString &message) { - QList params; - params << net->serverEncode(bufname) << lowLevelQuote(pack(net->serverEncode(ctcpTag), net->userEncode(bufname, message))); - net->putCmd("NOTICE", params); + +void CtcpParser::reply(CoreNetwork *net, const QString &bufname, const QString &ctcpTag, const QString &message) +{ + QList params; + params << net->serverEncode(bufname) << lowLevelQuote(pack(net->serverEncode(ctcpTag), net->userEncode(bufname, message))); + net->putCmd("NOTICE", params); } -void CtcpParser::packedReply(CoreNetwork *net, const QString &bufname, const QList &replies) { - QList params; - int answerSize = 0; - for(int i = 0; i < replies.count(); i++) { - answerSize += replies.at(i).size(); - } +void CtcpParser::packedReply(CoreNetwork *net, const QString &bufname, const QList &replies) +{ + QList params; + + int answerSize = 0; + for (int i = 0; i < replies.count(); i++) { + answerSize += replies.at(i).size(); + } - QByteArray quotedReply; - quotedReply.reserve(answerSize); - for(int i = 0; i < replies.count(); i++) { - quotedReply.append(replies.at(i)); - } + QByteArray quotedReply; + quotedReply.reserve(answerSize); + for (int i = 0; i < replies.count(); i++) { + quotedReply.append(replies.at(i)); + } - params << net->serverEncode(bufname) << quotedReply; - // FIXME user proper event - net->putCmd("NOTICE", params); + params << net->serverEncode(bufname) << quotedReply; + // FIXME user proper event + net->putCmd("NOTICE", params); }