1 /***************************************************************************
2 * Copyright (C) 2005-10 by the Quassel Project *
3 * devel@quassel-irc.org *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) version 3. *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19 ***************************************************************************/
20 #include "ircserverhandler.h"
24 #include "coresession.h"
25 #include "coreirclisthelper.h"
26 #include "coreidentity.h"
27 #include "ctcphandler.h"
30 #include "coreircchannel.h"
39 IrcServerHandler::IrcServerHandler(CoreNetwork *parent)
40 : CoreBasicHandler(parent),
46 IrcServerHandler::~IrcServerHandler() {
50 /*! Handle a raw message string sent by the server. We try to find a suitable handler, otherwise we call a default handler. */
51 void IrcServerHandler::handleServerMsg(QByteArray msg) {
53 qWarning() << "Received empty string from server!";
57 // Now we split the raw message into its various parts...
62 // First, check for a trailing parameter introduced by " :", since this might screw up splitting the msg
63 // NOTE: This assumes that this is true in raw encoding, but well, hopefully there are no servers running in japanese on protocol level...
64 int idx = msg.indexOf(" :");
66 if(msg.length() > idx + 2)
67 trailing = msg.mid(idx + 2);
70 // OK, now it is safe to split...
71 QList<QByteArray> params = msg.split(' ');
73 // This could still contain empty elements due to (faulty?) ircds sending multiple spaces in a row
74 // Also, QByteArray is not nearly as convenient to work with as QString for such things :)
75 QList<QByteArray>::iterator iter = params.begin();
76 while(iter != params.end()) {
78 iter = params.erase(iter);
83 if(!trailing.isEmpty()) params << trailing;
84 if(params.count() < 1) {
85 qWarning() << "Received invalid string from server!";
89 QString foo = serverDecode(params.takeFirst());
91 // with SASL, the command is 'AUTHENTICATE +' and we should check for this here.
92 /* obsolete because of events
93 if(foo == QString("AUTHENTICATE +")) {
98 // a colon as the first chars indicates the existence of a prefix
102 if(params.count() < 1) {
103 qWarning() << "Received invalid string from server!";
106 foo = serverDecode(params.takeFirst());
109 // next string without a whitespace is the command
110 cmd = foo.trimmed().toUpper();
112 // numeric replies have the target as first param (RFC 2812 - 2.4). this is usually our own nick. Remove this!
113 uint num = cmd.toUInt();
115 if(params.count() == 0) {
116 qWarning() << "Message received from server violates RFC and is ignored!" << msg;
119 _target = serverDecode(params.takeFirst());
124 // note that the IRC server is still alive
125 network()->resetPingTimeout();
127 // Now we try to find a handler for this message. BTW, I do love the Trolltech guys ;-)
128 handle(cmd, Q_ARG(QString, prefix), Q_ARG(QList<QByteArray>, params));
132 void IrcServerHandler::defaultHandler(QString cmd, const QString &prefix, const QList<QByteArray> &rawparams) {
133 // many commands are handled by the event system now
139 //******************************/
140 // IRC SERVER HANDLER
141 //******************************/
143 void IrcServerHandler::handleNotice(const QString &prefix, const QList<QByteArray> ¶ms) {
144 if(!checkParamCount("IrcServerHandler::handleNotice()", params, 2))
148 QStringList targets = serverDecode(params[0]).split(',', QString::SkipEmptyParts);
149 QStringList::const_iterator targetIter;
150 for(targetIter = targets.constBegin(); targetIter != targets.constEnd(); targetIter++) {
151 QString target = *targetIter;
153 // special treatment for welcome messages like:
154 // :ChanServ!ChanServ@services. NOTICE egst :[#apache] Welcome, this is #apache. Please read the in-channel topic message. This channel is being logged by IRSeekBot. If you have any question please see http://blog.freenode.net/?p=68
155 if(!network()->isChannelName(target)) {
156 QString msg = serverDecode(params[1]);
157 QRegExp welcomeRegExp("^\\[([^\\]]+)\\] ");
158 if(welcomeRegExp.indexIn(msg) != -1) {
159 QString channelname = welcomeRegExp.cap(1);
160 msg = msg.mid(welcomeRegExp.matchedLength());
161 CoreIrcChannel *chan = static_cast<CoreIrcChannel *>(network()->ircChannel(channelname)); // we only have CoreIrcChannels in the core, so this cast is safe
162 if(chan && !chan->receivedWelcomeMsg()) {
163 chan->setReceivedWelcomeMsg();
164 emit displayMsg(Message::Notice, BufferInfo::ChannelBuffer, channelname, msg, prefix);
170 if(prefix.isEmpty() || target == "AUTH") {
173 if(!target.isEmpty() && network()->prefixes().contains(target[0]))
174 target = target.mid(1);
175 if(!network()->isChannelName(target))
176 target = nickFromMask(prefix);
179 network()->ctcpHandler()->parse(Message::Notice, prefix, target, params[1]);
184 void IrcServerHandler::handlePing(const QString &prefix, const QList<QByteArray> ¶ms) {
186 putCmd("PONG", params);
189 void IrcServerHandler::handlePrivmsg(const QString &prefix, const QList<QByteArray> ¶ms) {
190 if(!checkParamCount("IrcServerHandler::handlePrivmsg()", params, 1))
193 IrcUser *ircuser = network()->updateNickFromMask(prefix);
195 qWarning() << "IrcServerHandler::handlePrivmsg(): Unknown IrcUser!";
199 if(params.isEmpty()) {
200 qWarning() << "IrcServerHandler::handlePrivmsg(): received PRIVMSG without target or message from:" << prefix;
204 QString senderNick = nickFromMask(prefix);
206 QByteArray msg = params.count() < 2
210 QStringList targets = serverDecode(params[0]).split(',', QString::SkipEmptyParts);
211 QStringList::const_iterator targetIter;
212 for(targetIter = targets.constBegin(); targetIter != targets.constEnd(); targetIter++) {
213 const QString &target = network()->isChannelName(*targetIter)
218 msg = decrypt(target, msg);
220 // it's possible to pack multiple privmsgs into one param using ctcp
221 // - > we let the ctcpHandler do the work
222 network()->ctcpHandler()->parse(Message::Plain, prefix, target, msg);
226 // FIXME networkConnection()->setChannelKey("") for all ERR replies indicating that a JOIN went wrong
227 // mostly, these are codes in the 47x range
231 void IrcServerHandler::tryNextNick(const QString &errnick, bool erroneus) {
232 QStringList desiredNicks = coreSession()->identity(network()->identity())->nicks();
233 int nextNickIdx = desiredNicks.indexOf(errnick) + 1;
235 if(nextNickIdx > 0 && desiredNicks.size() > nextNickIdx) {
236 nextNick = desiredNicks[nextNickIdx];
239 emit displayMsg(Message::Error, BufferInfo::StatusBuffer, "", tr("No free and valid nicks in nicklist found. use: /nick <othernick> to continue"));
242 nextNick = errnick + "_";
245 putCmd("NICK", serverEncode(nextNick));
248 bool IrcServerHandler::checkParamCount(const QString &methodName, const QList<QByteArray> ¶ms, int minParams) {
249 if(params.count() < minParams) {
250 qWarning() << qPrintable(methodName) << "requires" << minParams << "parameters but received only" << params.count() << serverDecode(params);
258 QByteArray IrcServerHandler::decrypt(const QString &bufferName, const QByteArray &message_, bool isTopic) {
259 if(message_.isEmpty())
262 Cipher *cipher = network()->cipher(bufferName);
266 QByteArray message = message_;
267 message = isTopic? cipher->decryptTopic(message) : cipher->decrypt(message);