Event backend porting
[quassel.git] / src / core / coresessioneventprocessor.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-2010 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
21 #include "coresessioneventprocessor.h"
22
23 #include "coreirclisthelper.h"
24 #include "corenetwork.h"
25 #include "coresession.h"
26 #include "ircevent.h"
27 #include "ircuser.h"
28
29 CoreSessionEventProcessor::CoreSessionEventProcessor(CoreSession *session)
30   : QObject(session),
31   _coreSession(session)
32 {
33
34 }
35
36 bool CoreSessionEventProcessor::checkParamCount(IrcEvent *e, int minParams) {
37   if(e->params().count() < minParams) {
38     if(e->type() == EventManager::IrcEventNumeric) {
39       qWarning() << "Command " << static_cast<IrcEventNumeric *>(e)->number() << " requires " << minParams << "params, got: " << e->params();
40     } else {
41       QString name = coreSession()->eventManager()->enumName(e->type());
42       qWarning() << qPrintable(name) << "requires" << minParams << "params, got:" << e->params();
43     }
44     e->stop();
45     return false;
46   }
47   return true;
48 }
49
50 void CoreSessionEventProcessor::processIrcEventNumeric(IrcEventNumeric *e) {
51   switch(e->number()) {
52
53   // CAP stuff
54   case 903: case 904: case 905: case 906: case 907:
55     qobject_cast<CoreNetwork *>(e->network())->putRawLine("CAP END");
56     break;
57
58   default:
59     break;
60   }
61 }
62
63 void CoreSessionEventProcessor::processIrcEventAuthenticate(IrcEvent *e) {
64   if(!checkParamCount(e, 1))
65     return;
66
67   if(e->params().at(0) != "+") {
68     qWarning() << "Invalid AUTHENTICATE" << e;
69     return;
70   }
71
72   CoreNetwork *net = coreNetwork(e);
73
74   QString construct = net->saslAccount();
75   construct.append(QChar(QChar::Null));
76   construct.append(net->saslAccount());
77   construct.append(QChar(QChar::Null));
78   construct.append(net->saslPassword());
79   QByteArray saslData = QByteArray(construct.toAscii().toBase64());
80   saslData.prepend("AUTHENTICATE ");
81   net->putRawLine(saslData);
82 }
83
84 void CoreSessionEventProcessor::processIrcEventCap(IrcEvent *e) {
85   // for SASL, there will only be a single param of 'sasl', however you can check here for
86   // additional CAP messages (ls, multi-prefix, et cetera).
87
88   if(e->params().count() == 3) {
89     if(e->params().at(2) == "sasl") {
90       // FIXME use event
91       coreNetwork(e)->putRawLine(coreNetwork(e)->serverEncode("AUTHENTICATE PLAIN")); // Only working with PLAIN atm, blowfish later
92     }
93   }
94 }
95
96 void CoreSessionEventProcessor::processIrcEventInvite(IrcEvent *e) {
97   if(checkParamCount(e, 2)) {
98     e->network()->updateNickFromMask(e->prefix());
99   }
100 }
101
102 void CoreSessionEventProcessor::processIrcEventKick(IrcEvent *e) {
103   if(checkParamCount(e, 2)) {
104     e->network()->updateNickFromMask(e->prefix());
105     IrcUser *victim = e->network()->ircUser(e->params().at(1));
106     if(victim) {
107       victim->partChannel(e->params().at(0));
108       //if(e->network()->isMe(victim)) e->network()->setKickedFromChannel(channel);
109     }
110   }
111 }
112
113 void CoreSessionEventProcessor::processIrcEventNick(IrcEvent *e) {
114   if(checkParamCount(e, 1)) {
115     IrcUser *ircuser = e->network()->updateNickFromMask(e->prefix());
116     if(!ircuser) {
117       qWarning() << Q_FUNC_INFO << "Unknown IrcUser!";
118       return;
119     }
120     QString newnick = e->params().at(0);
121     QString oldnick = ircuser->nick();
122
123     // the order is cruicial
124     // otherwise the client would rename the buffer, see that the assigned ircuser doesn't match anymore
125     // and remove the ircuser from the querybuffer leading to a wrong on/offline state
126     ircuser->setNick(newnick);
127     coreSession()->renameBuffer(e->networkId(), newnick, oldnick);
128   }
129 }
130
131 void CoreSessionEventProcessor::processIrcEventPart(IrcEvent *e) {
132   if(checkParamCount(e, 1)) {
133     IrcUser *ircuser = e->network()->updateNickFromMask(e->prefix());
134     if(!ircuser) {
135       qWarning() << Q_FUNC_INFO<< "Unknown IrcUser!";
136       return;
137     }
138     QString channel = e->params().at(0);
139     ircuser->partChannel(channel);
140     if(e->network()->isMe(ircuser))
141       qobject_cast<CoreNetwork *>(e->network())->setChannelParted(channel);
142   }
143 }
144
145 void CoreSessionEventProcessor::processIrcEventPong(IrcEvent *e) {
146   // the server is supposed to send back what we passed as param. and we send a timestamp
147   // but using quote and whatnought one can send arbitrary pings, so we have to do some sanity checks
148   if(checkParamCount(e, 2)) {
149     QString timestamp = e->params().at(1);
150     QTime sendTime = QTime::fromString(timestamp, "hh:mm:ss.zzz");
151     if(sendTime.isValid())
152       e->network()->setLatency(sendTime.msecsTo(QTime::currentTime()) / 2);
153   }
154 }
155
156 void CoreSessionEventProcessor::processIrcEventTopic(IrcEvent *e) {
157   if(checkParamCount(e, 2)) {
158     e->network()->updateNickFromMask(e->prefix());
159     IrcChannel *channel = e->network()->ircChannel(e->params().at(0));
160     if(channel)
161       channel->setTopic(e->params().at(1));
162   }
163 }
164
165 /* RPL_WELCOME */
166 void CoreSessionEventProcessor::processIrcEvent001(IrcEvent *e) {
167   if(!checkParamCount(e, 1))
168     return;
169
170   QString myhostmask = e->params().at(0).section(' ', -1, -1);
171   e->network()->setCurrentServer(e->prefix());
172   e->network()->setMyNick(nickFromMask(myhostmask));
173 }
174
175 /* RPL_ISUPPORT */
176 // TODO Complete 005 handling, also use sensible defaults for non-sent stuff
177 void CoreSessionEventProcessor::processIrcEvent005(IrcEvent *e) {
178   if(!checkParamCount(e, 1))
179     return;
180
181   QString key, value;
182   for(int i = 0; i < e->params().count() - 1; i++) {
183     QString key = e->params()[i].section("=", 0, 0);
184     QString value = e->params()[i].section("=", 1);
185     e->network()->addSupport(key, value);
186   }
187
188   /* determine our prefixes here to get an accurate result */
189   e->network()->determinePrefixes();
190 }
191
192 /* RPL_UMODEIS - "<user_modes> [<user_mode_params>]" */
193 void CoreSessionEventProcessor::processIrcEvent221(IrcEvent *) {
194   // TODO: save information in network object
195 }
196
197 /* RPL_STATSCONN - "Highest connection cout: 8000 (7999 clients)" */
198 void CoreSessionEventProcessor::processIrcEvent250(IrcEvent *) {
199   // TODO: save information in network object
200 }
201
202 /* RPL_LOCALUSERS - "Current local user: 5024  Max: 7999 */
203 void CoreSessionEventProcessor::processIrcEvent265(IrcEvent *) {
204   // TODO: save information in network object
205 }
206
207 /* RPL_GLOBALUSERS - "Current global users: 46093  Max: 47650" */
208 void CoreSessionEventProcessor::processIrcEvent266(IrcEvent *) {
209   // TODO: save information in network object
210 }
211
212 /*
213 WHOIS-Message:
214    Replies 311 - 313, 317 - 319 are all replies generated in response to a WHOIS message.
215   and 301 (RPL_AWAY)
216               "<nick> :<away message>"
217 WHO-Message:
218    Replies 352 and 315 paired are used to answer a WHO message.
219
220 WHOWAS-Message:
221    Replies 314 and 369 are responses to a WHOWAS message.
222
223 */
224
225 /* RPL_AWAY - "<nick> :<away message>" */
226 void CoreSessionEventProcessor::processIrcEvent301(IrcEvent *e) {
227   if(!checkParamCount(e, 2))
228     return;
229
230   IrcUser *ircuser = e->network()->ircUser(e->params().at(0));
231   if(ircuser) {
232     ircuser->setAway(true);
233     ircuser->setAwayMessage(e->params().at(1));
234     //ircuser->setLastAwayMessage(now);
235   }
236 }
237
238 /* RPL_UNAWAY - ":You are no longer marked as being away" */
239 void CoreSessionEventProcessor::processIrcEvent305(IrcEvent *e) {
240   IrcUser *me = e->network()->me();
241   if(me)
242     me->setAway(false);
243
244   if(e->network()->autoAwayActive()) {
245     e->network()->setAutoAwayActive(false);
246     e->setFlag(EventManager::Silent);
247   }
248 }
249
250 /* RPL_NOWAWAY - ":You have been marked as being away" */
251 void CoreSessionEventProcessor::processIrcEvent306(IrcEvent *e) {
252   IrcUser *me = e->network()->me();
253   if(me)
254     me->setAway(true);
255 }
256
257 /* RPL_WHOISSERVICE - "<user> is registered nick" */
258 void CoreSessionEventProcessor::processIrcEvent307(IrcEvent *e) {
259   if(!checkParamCount(e, 1))
260     return;
261
262   IrcUser *ircuser = e->network()->ircUser(e->params().at(0));
263   if(ircuser)
264     ircuser->setWhoisServiceReply(e->params().join(" "));
265 }
266
267 /* RPL_SUSERHOST - "<user> is available for help." */
268 void CoreSessionEventProcessor::processIrcEvent310(IrcEvent *e) {
269   if(!checkParamCount(e, 1))
270     return;
271
272   IrcUser *ircuser = e->network()->ircUser(e->params().at(0));
273   if(ircuser)
274     ircuser->setSuserHost(e->params().join(" "));
275 }
276
277 /*  RPL_WHOISUSER - "<nick> <user> <host> * :<real name>" */
278 void CoreSessionEventProcessor::processIrcEvent311(IrcEvent *e) {
279   if(!checkParamCount(e, 3))
280     return;
281
282   IrcUser *ircuser = e->network()->ircUser(e->params().at(0));
283   if(ircuser) {
284     ircuser->setUser(e->params().at(1));
285     ircuser->setHost(e->params().at(2));
286     ircuser->setRealName(e->params().last());
287   }
288 }
289
290 /*  RPL_WHOISSERVER -  "<nick> <server> :<server info>" */
291 void CoreSessionEventProcessor::processIrcEvent312(IrcEvent *e) {
292   if(!checkParamCount(e, 2))
293     return;
294
295   IrcUser *ircuser = e->network()->ircUser(e->params().at(0));
296   if(ircuser)
297     ircuser->setServer(e->params().at(1));
298 }
299
300 /*  RPL_WHOISOPERATOR - "<nick> :is an IRC operator" */
301 void CoreSessionEventProcessor::processIrcEvent313(IrcEvent *e) {
302   if(!checkParamCount(e, 1))
303     return;
304
305   IrcUser *ircuser = e->network()->ircUser(e->params().at(0));
306   if(ircuser)
307     ircuser->setIrcOperator(e->params().last());
308 }
309
310 /*  RPL_ENDOFWHO: "<name> :End of WHO list" */
311 void CoreSessionEventProcessor::processIrcEvent315(IrcEvent *e) {
312   if(!checkParamCount(e, 1))
313     return;
314
315   if(coreNetwork(e)->setAutoWhoDone(e->params()[0]))
316     e->setFlag(EventManager::Silent);
317 }
318
319 /*  RPL_WHOISIDLE - "<nick> <integer> :seconds idle"
320    (real life: "<nick> <integer> <integer> :seconds idle, signon time) */
321 void CoreSessionEventProcessor::processIrcEvent317(IrcEvent *e) {
322   if(!checkParamCount(e, 2))
323     return;
324
325   QDateTime loginTime;
326
327   int idleSecs = e->params()[1].toInt();
328   if(e->params().count() > 3) { // if we have more then 3 params we have the above mentioned "real life" situation
329     int logintime = e->params()[2].toInt();
330     loginTime = QDateTime::fromTime_t(logintime);
331   }
332
333   IrcUser *ircuser = e->network()->ircUser(e->params()[0]);
334   if(ircuser) {
335     ircuser->setIdleTime(e->timestamp().addSecs(-idleSecs));
336     if(loginTime.isValid())
337       ircuser->setLoginTime(loginTime);
338   }
339 }
340
341 /* RPL_LIST -  "<channel> <# visible> :<topic>" */
342 void CoreSessionEventProcessor::processIrcEvent322(IrcEvent *e) {
343   if(!checkParamCount(e, 1))
344     return;
345
346   QString channelName;
347   quint32 userCount = 0;
348   QString topic;
349
350   switch(e->params().count()) {
351   case 3:
352     topic = e->params()[2];
353   case 2:
354     userCount = e->params()[1].toUInt();
355   case 1:
356     channelName = e->params()[0];
357   default:
358     break;
359   }
360   if(coreSession()->ircListHelper()->addChannel(e->networkId(), channelName, userCount, topic))
361     e->stop(); // consumed by IrcListHelper, so don't further process/show this event
362 }
363
364 /* RPL_LISTEND ":End of LIST" */
365 void CoreSessionEventProcessor::processIrcEvent323(IrcEvent *e) {
366   if(!checkParamCount(e, 1))
367     return;
368
369   if(coreSession()->ircListHelper()->endOfChannelList(e->networkId()))
370     e->stop(); // consumed by IrcListHelper, so don't further process/show this event
371 }
372
373 /* RPL_NOTOPIC */
374 void CoreSessionEventProcessor::processIrcEvent331(IrcEvent *e) {
375   if(!checkParamCount(e, 1))
376     return;
377
378   IrcChannel *chan = e->network()->ircChannel(e->params()[0]);
379   if(chan)
380     chan->setTopic(QString());
381 }
382
383 /* RPL_TOPIC */
384 void CoreSessionEventProcessor::processIrcEvent332(IrcEvent *e) {
385   if(!checkParamCount(e, 2))
386     return;
387
388   IrcChannel *chan = e->network()->ircChannel(e->params()[0]);
389   if(chan)
390     chan->setTopic(e->params()[1]);
391 }
392
393 /*  RPL_WHOREPLY: "<channel> <user> <host> <server> <nick>
394               ( "H" / "G" > ["*"] [ ( "@" / "+" ) ] :<hopcount> <real name>" */
395 void CoreSessionEventProcessor::processIrcEvent352(IrcEvent *e) {
396   if(!checkParamCount(e, 6))
397     return;
398
399   QString channel = e->params()[0];
400   IrcUser *ircuser = e->network()->ircUser(e->params()[4]);
401   if(ircuser) {
402     ircuser->setUser(e->params()[1]);
403     ircuser->setHost(e->params()[2]);
404
405     bool away = e->params()[5].startsWith("G");
406     ircuser->setAway(away);
407     ircuser->setServer(e->params()[3]);
408     ircuser->setRealName(e->params().last().section(" ", 1));
409   }
410
411   if(coreNetwork(e)->isAutoWhoInProgress(channel))
412     e->setFlag(EventManager::Silent);
413 }
414
415 /* RPL_NAMREPLY */
416 void CoreSessionEventProcessor::processIrcEvent353(IrcEvent *e) {
417   if(!checkParamCount(e, 3))
418     return;
419
420   // param[0] is either "=", "*" or "@" indicating a public, private or secret channel
421   // we don't use this information at the time beeing
422   QString channelname = e->params()[1];
423
424   IrcChannel *channel = e->network()->ircChannel(channelname);
425   if(!channel) {
426     qWarning() << Q_FUNC_INFO << "Received unknown target channel:" << channelname;
427     return;
428   }
429
430   QStringList nicks;
431   QStringList modes;
432
433   foreach(QString nick, e->params()[2].split(' ')) {
434     QString mode;
435
436     if(e->network()->prefixes().contains(nick[0])) {
437       mode = e->network()->prefixToMode(nick[0]);
438       nick = nick.mid(1);
439     }
440
441     nicks << nick;
442     modes << mode;
443   }
444
445   channel->joinIrcUsers(nicks, modes);
446 }
447
448 /* template
449 void CoreSessionEventProcessor::processIrcEvent(IrcEvent *e) {
450   if(!checkParamCount(e, 1))
451     return;
452
453 }
454 */