235f7d5e34643397ed35e9a4c13f28f291f6b2ec
[quassel.git] / src / core / ircserverhandler.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-10 by the Quassel Project                          *
3  *   devel@quassel-irc.org                                                 *
4  *                                                                         *
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.                                           *
9  *                                                                         *
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.                          *
14  *                                                                         *
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"
21
22 #include "util.h"
23
24 #include "coresession.h"
25 #include "coreirclisthelper.h"
26 #include "coreidentity.h"
27 #include "ctcphandler.h"
28
29 #include "ircuser.h"
30 #include "coreircchannel.h"
31 #include "logger.h"
32
33 #include <QDebug>
34
35 #ifdef HAVE_QCA2
36 #  include "cipher.h"
37 #endif
38
39 IrcServerHandler::IrcServerHandler(CoreNetwork *parent)
40   : CoreBasicHandler(parent),
41     _whois(false)
42 {
43
44 }
45
46 IrcServerHandler::~IrcServerHandler() {
47
48 }
49
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) {
52   if(msg.isEmpty()) {
53     qWarning() << "Received empty string from server!";
54     return;
55   }
56
57   // Now we split the raw message into its various parts...
58   QString prefix = "";
59   QByteArray trailing;
60   QString cmd;
61
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(" :");
65   if(idx >= 0) {
66     if(msg.length() > idx + 2)
67       trailing = msg.mid(idx + 2);
68     msg = msg.left(idx);
69   }
70   // OK, now it is safe to split...
71   QList<QByteArray> params = msg.split(' ');
72
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()) {
77     if(iter->isEmpty())
78       iter = params.erase(iter);
79     else
80       ++iter;
81   }
82
83   if(!trailing.isEmpty()) params << trailing;
84   if(params.count() < 1) {
85     qWarning() << "Received invalid string from server!";
86     return;
87   }
88
89   QString foo = serverDecode(params.takeFirst());
90
91   // with SASL, the command is 'AUTHENTICATE +' and we should check for this here.
92   /* obsolete because of events
93   if(foo == QString("AUTHENTICATE +")) {
94     handleAuthenticate();
95     return;
96   }
97   */
98   // a colon as the first chars indicates the existence of a prefix
99   if(foo[0] == ':') {
100     foo.remove(0, 1);
101     prefix = foo;
102     if(params.count() < 1) {
103       qWarning() << "Received invalid string from server!";
104       return;
105     }
106     foo = serverDecode(params.takeFirst());
107   }
108
109   // next string without a whitespace is the command
110   cmd = foo.trimmed().toUpper();
111
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();
114   if(num > 0) {
115     if(params.count() == 0) {
116       qWarning() << "Message received from server violates RFC and is ignored!" << msg;
117       return;
118     }
119     _target = serverDecode(params.takeFirst());
120   } else {
121     _target = QString();
122   }
123
124   // note that the IRC server is still alive
125   network()->resetPingTimeout();
126
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));
129 }
130
131
132 void IrcServerHandler::defaultHandler(QString cmd, const QString &prefix, const QList<QByteArray> &rawparams) {
133   // many commands are handled by the event system now
134   Q_UNUSED(cmd)
135   Q_UNUSED(prefix)
136   Q_UNUSED(rawparams)
137 }
138
139 //******************************/
140 // IRC SERVER HANDLER
141 //******************************/
142
143 void IrcServerHandler::handleNotice(const QString &prefix, const QList<QByteArray> &params) {
144   if(!checkParamCount("IrcServerHandler::handleNotice()", params, 2))
145     return;
146
147
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;
152
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);
165           continue;
166         }
167       }
168     }
169
170     if(prefix.isEmpty() || target == "AUTH") {
171       target = "";
172     } else {
173       if(!target.isEmpty() && network()->prefixes().contains(target[0]))
174         target = target.mid(1);
175       if(!network()->isChannelName(target))
176         target = nickFromMask(prefix);
177     }
178
179     network()->ctcpHandler()->parse(Message::Notice, prefix, target, params[1]);
180   }
181
182 }
183
184 void IrcServerHandler::handlePrivmsg(const QString &prefix, const QList<QByteArray> &params) {
185   if(!checkParamCount("IrcServerHandler::handlePrivmsg()", params, 1))
186     return;
187
188   IrcUser *ircuser = network()->updateNickFromMask(prefix);
189   if(!ircuser) {
190     qWarning() << "IrcServerHandler::handlePrivmsg(): Unknown IrcUser!";
191     return;
192   }
193
194   if(params.isEmpty()) {
195     qWarning() << "IrcServerHandler::handlePrivmsg(): received PRIVMSG without target or message from:" << prefix;
196     return;
197   }
198
199   QString senderNick = nickFromMask(prefix);
200
201   QByteArray msg = params.count() < 2
202     ? QByteArray("")
203     : params[1];
204
205   QStringList targets = serverDecode(params[0]).split(',', QString::SkipEmptyParts);
206   QStringList::const_iterator targetIter;
207   for(targetIter = targets.constBegin(); targetIter != targets.constEnd(); targetIter++) {
208     const QString &target = network()->isChannelName(*targetIter)
209       ? *targetIter
210       : senderNick;
211
212 #ifdef HAVE_QCA2
213     msg = decrypt(target, msg);
214 #endif
215     // it's possible to pack multiple privmsgs into one param using ctcp
216     // - > we let the ctcpHandler do the work
217     network()->ctcpHandler()->parse(Message::Plain, prefix, target, msg);
218   }
219 }
220
221 // FIXME networkConnection()->setChannelKey("") for all ERR replies indicating that a JOIN went wrong
222 //       mostly, these are codes in the 47x range
223
224 /* */
225
226 void IrcServerHandler::tryNextNick(const QString &errnick, bool erroneus) {
227   QStringList desiredNicks = coreSession()->identity(network()->identity())->nicks();
228   int nextNickIdx = desiredNicks.indexOf(errnick) + 1;
229   QString nextNick;
230   if(nextNickIdx > 0 && desiredNicks.size() > nextNickIdx) {
231     nextNick = desiredNicks[nextNickIdx];
232   } else {
233     if(erroneus) {
234       emit displayMsg(Message::Error, BufferInfo::StatusBuffer, "", tr("No free and valid nicks in nicklist found. use: /nick <othernick> to continue"));
235       return;
236     } else {
237       nextNick = errnick + "_";
238     }
239   }
240   putCmd("NICK", serverEncode(nextNick));
241 }
242
243 bool IrcServerHandler::checkParamCount(const QString &methodName, const QList<QByteArray> &params, int minParams) {
244   if(params.count() < minParams) {
245     qWarning() << qPrintable(methodName) << "requires" << minParams << "parameters but received only" << params.count() << serverDecode(params);
246     return false;
247   } else {
248     return true;
249   }
250 }
251
252 #ifdef HAVE_QCA2
253 QByteArray IrcServerHandler::decrypt(const QString &bufferName, const QByteArray &message_, bool isTopic) {
254   if(message_.isEmpty())
255     return message_;
256
257   Cipher *cipher = network()->cipher(bufferName);
258   if(!cipher)
259     return message_;
260
261   QByteArray message = message_;
262   message = isTopic? cipher->decryptTopic(message) : cipher->decrypt(message);
263   return message;
264 }
265 #endif