Use late CoreSessionEventProcessor handler rather than early EventStringifier ones
[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 #include "messageevent.h"
29 #include "netsplit.h"
30
31 CoreSessionEventProcessor::CoreSessionEventProcessor(CoreSession *session)
32   : QObject(session),
33   _coreSession(session)
34 {
35   connect(coreSession(), SIGNAL(networkDisconnected(NetworkId)), this, SLOT(destroyNetsplits(NetworkId)));
36 }
37
38 bool CoreSessionEventProcessor::checkParamCount(IrcEvent *e, int minParams) {
39   if(e->params().count() < minParams) {
40     if(e->type() == EventManager::IrcEventNumeric) {
41       qWarning() << "Command " << static_cast<IrcEventNumeric *>(e)->number() << " requires " << minParams << "params, got: " << e->params();
42     } else {
43       QString name = coreSession()->eventManager()->enumName(e->type());
44       qWarning() << qPrintable(name) << "requires" << minParams << "params, got:" << e->params();
45     }
46     e->stop();
47     return false;
48   }
49   return true;
50 }
51
52 void CoreSessionEventProcessor::tryNextNick(NetworkEvent *e, const QString &errnick, bool erroneus) {
53   QStringList desiredNicks = coreSession()->identity(e->network()->identity())->nicks();
54   int nextNickIdx = desiredNicks.indexOf(errnick) + 1;
55   QString nextNick;
56   if(nextNickIdx > 0 && desiredNicks.size() > nextNickIdx) {
57     nextNick = desiredNicks[nextNickIdx];
58   } else {
59     if(erroneus) {
60       // FIXME Make this an ErrorEvent or something like that, so it's translated in the client
61       MessageEvent *msgEvent = new MessageEvent(Message::Error, e->network(),
62                                                 tr("No free and valid nicks in nicklist found. use: /nick <othernick> to continue"),
63                                                 QString(), QString(), Message::None, e->timestamp());
64       coreSession()->eventManager()->sendEvent(msgEvent);
65       return;
66     } else {
67       nextNick = errnick + "_";
68     }
69   }
70   // FIXME Use a proper output event for this
71   coreNetwork(e)->putRawLine("NICK " + coreNetwork(e)->encodeServerString(nextNick));
72 }
73
74 void CoreSessionEventProcessor::processIrcEventNumeric(IrcEventNumeric *e) {
75   switch(e->number()) {
76
77   // CAP stuff
78   case 903: case 904: case 905: case 906: case 907:
79     qobject_cast<CoreNetwork *>(e->network())->putRawLine("CAP END");
80     break;
81
82   default:
83     break;
84   }
85 }
86
87 void CoreSessionEventProcessor::processIrcEventAuthenticate(IrcEvent *e) {
88   if(!checkParamCount(e, 1))
89     return;
90
91   if(e->params().at(0) != "+") {
92     qWarning() << "Invalid AUTHENTICATE" << e;
93     return;
94   }
95
96   CoreNetwork *net = coreNetwork(e);
97
98   QString construct = net->saslAccount();
99   construct.append(QChar(QChar::Null));
100   construct.append(net->saslAccount());
101   construct.append(QChar(QChar::Null));
102   construct.append(net->saslPassword());
103   QByteArray saslData = QByteArray(construct.toAscii().toBase64());
104   saslData.prepend("AUTHENTICATE ");
105   net->putRawLine(saslData);
106 }
107
108 void CoreSessionEventProcessor::processIrcEventCap(IrcEvent *e) {
109   // for SASL, there will only be a single param of 'sasl', however you can check here for
110   // additional CAP messages (ls, multi-prefix, et cetera).
111
112   if(e->params().count() == 3) {
113     if(e->params().at(2) == "sasl") {
114       // FIXME use event
115       coreNetwork(e)->putRawLine(coreNetwork(e)->serverEncode("AUTHENTICATE PLAIN")); // Only working with PLAIN atm, blowfish later
116     }
117   }
118 }
119
120 void CoreSessionEventProcessor::processIrcEventInvite(IrcEvent *e) {
121   if(checkParamCount(e, 2)) {
122     e->network()->updateNickFromMask(e->prefix());
123   }
124 }
125
126 void CoreSessionEventProcessor::processIrcEventJoin(IrcEvent *e) {
127   if(e->testFlag(EventManager::Fake)) // generated by handleEarlyNetsplitJoin
128     return;
129
130   if(!checkParamCount(e, 1))
131     return;
132
133   CoreNetwork *net = coreNetwork(e);
134   QString channel = e->params()[0];
135   IrcUser *ircuser = net->updateNickFromMask(e->prefix());
136
137   bool handledByNetsplit = false;
138   foreach(Netsplit* n, _netsplits.value(e->network())) {
139     handledByNetsplit = n->userJoined(e->prefix(), channel);
140     if(handledByNetsplit)
141       break;
142   }
143
144   if(!handledByNetsplit)
145     ircuser->joinChannel(channel);
146   else
147     e->setFlag(EventManager::Netsplit);
148
149   if(net->isMe(ircuser)) {
150     net->setChannelJoined(channel);
151      // FIXME use event
152     net->putRawLine(net->serverEncode("MODE " + channel)); // we want to know the modes of the channel we just joined, so we ask politely
153   }
154 }
155
156 void CoreSessionEventProcessor::lateProcessIrcEventKick(IrcEvent *e) {
157   if(checkParamCount(e, 2)) {
158     e->network()->updateNickFromMask(e->prefix());
159     IrcUser *victim = e->network()->ircUser(e->params().at(1));
160     if(victim) {
161       victim->partChannel(e->params().at(0));
162       //if(e->network()->isMe(victim)) e->network()->setKickedFromChannel(channel);
163     }
164   }
165 }
166
167 void CoreSessionEventProcessor::processIrcEventMode(IrcEvent *e) {
168   if(!checkParamCount(e, 2))
169     return;
170
171   if(e->network()->isChannelName(e->params().first())) {
172     // Channel Modes
173
174     IrcChannel *channel = e->network()->ircChannel(e->params()[0]);
175     if(!channel) {
176       // we received mode information for a channel we're not in. that means probably we've just been kicked out or something like that
177       // anyways: we don't have a place to store the data --> discard the info.
178       return;
179     }
180
181     QString modes = e->params()[1];
182     bool add = true;
183     int paramOffset = 2;
184     for(int c = 0; c < modes.length(); c++) {
185       if(modes[c] == '+') {
186         add = true;
187         continue;
188       }
189       if(modes[c] == '-') {
190         add = false;
191         continue;
192       }
193
194       if(e->network()->prefixModes().contains(modes[c])) {
195         // user channel modes (op, voice, etc...)
196         if(paramOffset < e->params().count()) {
197           IrcUser *ircUser = e->network()->ircUser(e->params()[paramOffset]);
198           if(!ircUser) {
199             qWarning() << Q_FUNC_INFO << "Unknown IrcUser:" << e->params()[paramOffset];
200           } else {
201             if(add) {
202               bool handledByNetsplit = false;
203               QHash<QString, Netsplit *> splits = _netsplits.value(e->network());
204               foreach(Netsplit* n, _netsplits.value(e->network())) {
205                 handledByNetsplit = n->userAlreadyJoined(ircUser->hostmask(), channel->name());
206                 if(handledByNetsplit) {
207                   n->addMode(ircUser->hostmask(), channel->name(), QString(modes[c]));
208                   break;
209                 }
210               }
211               if(!handledByNetsplit)
212                 channel->addUserMode(ircUser, QString(modes[c]));
213             }
214             else
215               channel->removeUserMode(ircUser, QString(modes[c]));
216           }
217         } else {
218           qWarning() << "Received MODE with too few parameters:" << e->params();
219         }
220         ++paramOffset;
221       } else {
222         // regular channel modes
223         QString value;
224         Network::ChannelModeType modeType = e->network()->channelModeType(modes[c]);
225         if(modeType == Network::A_CHANMODE || modeType == Network::B_CHANMODE || (modeType == Network::C_CHANMODE && add)) {
226           if(paramOffset < e->params().count()) {
227             value = e->params()[paramOffset];
228           } else {
229             qWarning() << "Received MODE with too few parameters:" << e->params();
230           }
231           ++paramOffset;
232         }
233
234         if(add)
235           channel->addChannelMode(modes[c], value);
236         else
237           channel->removeChannelMode(modes[c], value);
238       }
239     }
240
241   } else {
242     // pure User Modes
243     IrcUser *ircUser = e->network()->newIrcUser(e->params().first());
244     QString modeString(e->params()[1]);
245     QString addModes;
246     QString removeModes;
247     bool add = false;
248     for(int c = 0; c < modeString.count(); c++) {
249       if(modeString[c] == '+') {
250         add = true;
251         continue;
252       }
253       if(modeString[c] == '-') {
254         add = false;
255         continue;
256       }
257       if(add)
258         addModes += modeString[c];
259       else
260         removeModes += modeString[c];
261     }
262     if(!addModes.isEmpty())
263       ircUser->addUserModes(addModes);
264     if(!removeModes.isEmpty())
265       ircUser->removeUserModes(removeModes);
266
267     if(e->network()->isMe(ircUser)) {
268       coreNetwork(e)->updatePersistentModes(addModes, removeModes);
269     }
270   }
271 }
272
273 void CoreSessionEventProcessor::lateProcessIrcEventNick(IrcEvent *e) {
274   if(checkParamCount(e, 1)) {
275     IrcUser *ircuser = e->network()->updateNickFromMask(e->prefix());
276     if(!ircuser) {
277       qWarning() << Q_FUNC_INFO << "Unknown IrcUser!";
278       return;
279     }
280     QString newnick = e->params().at(0);
281     QString oldnick = ircuser->nick();
282
283     // the order is cruicial
284     // otherwise the client would rename the buffer, see that the assigned ircuser doesn't match anymore
285     // and remove the ircuser from the querybuffer leading to a wrong on/offline state
286     ircuser->setNick(newnick);
287     coreSession()->renameBuffer(e->networkId(), newnick, oldnick);
288   }
289 }
290
291 void CoreSessionEventProcessor::lateProcessIrcEventPart(IrcEvent *e) {
292   if(checkParamCount(e, 1)) {
293     IrcUser *ircuser = e->network()->updateNickFromMask(e->prefix());
294     if(!ircuser) {
295       qWarning() << Q_FUNC_INFO<< "Unknown IrcUser!";
296       return;
297     }
298     QString channel = e->params().at(0);
299     ircuser->partChannel(channel);
300     if(e->network()->isMe(ircuser))
301       qobject_cast<CoreNetwork *>(e->network())->setChannelParted(channel);
302   }
303 }
304
305 void CoreSessionEventProcessor::processIrcEventPing(IrcEvent *e) {
306   QString param = e->params().count()? e->params().first() : QString();
307   // FIXME use events
308   coreNetwork(e)->putRawLine("PONG " + coreNetwork(e)->serverEncode(param));
309 }
310
311 void CoreSessionEventProcessor::processIrcEventPong(IrcEvent *e) {
312   // the server is supposed to send back what we passed as param. and we send a timestamp
313   // but using quote and whatnought one can send arbitrary pings, so we have to do some sanity checks
314   if(checkParamCount(e, 2)) {
315     QString timestamp = e->params().at(1);
316     QTime sendTime = QTime::fromString(timestamp, "hh:mm:ss.zzz");
317     if(sendTime.isValid())
318       e->network()->setLatency(sendTime.msecsTo(QTime::currentTime()) / 2);
319   }
320 }
321
322 void CoreSessionEventProcessor::processIrcEventQuit(IrcEvent *e) {
323   IrcUser *ircuser = e->network()->updateNickFromMask(e->prefix());
324   if(!ircuser)
325     return;
326
327   QString msg;
328   if(e->params().count() > 0)
329     msg = e->params()[0];
330
331   // check if netsplit
332   if(Netsplit::isNetsplit(msg)) {
333     Netsplit *n;
334     if(!_netsplits[e->network()].contains(msg)) {
335       n = new Netsplit(e->network(), this);
336       connect(n, SIGNAL(finished()), this, SLOT(handleNetsplitFinished()));
337       connect(n, SIGNAL(netsplitJoin(Network*,QString,QStringList,QStringList,QString)),
338               this, SLOT(handleNetsplitJoin(Network*,QString,QStringList,QStringList,QString)));
339       connect(n, SIGNAL(netsplitQuit(Network*,QString,QStringList,QString)),
340               this, SLOT(handleNetsplitQuit(Network*,QString,QStringList,QString)));
341       connect(n, SIGNAL(earlyJoin(Network*,QString,QStringList,QStringList)),
342               this, SLOT(handleEarlyNetsplitJoin(Network*,QString,QStringList,QStringList)));
343       _netsplits[e->network()].insert(msg, n);
344     }
345     else {
346       n = _netsplits[e->network()][msg];
347     }
348     // add this user to the netsplit
349     n->userQuit(e->prefix(), ircuser->channels(), msg);
350     e->setFlag(EventManager::Netsplit);
351   }
352   // normal quit is handled in lateProcessIrcEventQuit()
353 }
354
355 void CoreSessionEventProcessor::lateProcessIrcEventQuit(IrcEvent *e) {
356   if(e->testFlag(EventManager::Netsplit))
357     return;
358
359   IrcUser *ircuser = e->network()->updateNickFromMask(e->prefix());
360   if(!ircuser)
361     return;
362
363   ircuser->quit();
364 }
365
366 void CoreSessionEventProcessor::processIrcEventTopic(IrcEvent *e) {
367   if(checkParamCount(e, 2)) {
368     e->network()->updateNickFromMask(e->prefix());
369     IrcChannel *channel = e->network()->ircChannel(e->params().at(0));
370     if(channel)
371       channel->setTopic(e->params().at(1));
372   }
373 }
374
375 /* RPL_WELCOME */
376 void CoreSessionEventProcessor::processIrcEvent001(IrcEvent *e) {
377   if(!checkParamCount(e, 1))
378     return;
379
380   QString myhostmask = e->params().at(0).section(' ', -1, -1);
381   e->network()->setCurrentServer(e->prefix());
382   e->network()->setMyNick(nickFromMask(myhostmask));
383 }
384
385 /* RPL_ISUPPORT */
386 // TODO Complete 005 handling, also use sensible defaults for non-sent stuff
387 void CoreSessionEventProcessor::processIrcEvent005(IrcEvent *e) {
388   if(!checkParamCount(e, 1))
389     return;
390
391   QString key, value;
392   for(int i = 0; i < e->params().count() - 1; i++) {
393     QString key = e->params()[i].section("=", 0, 0);
394     QString value = e->params()[i].section("=", 1);
395     e->network()->addSupport(key, value);
396   }
397
398   /* determine our prefixes here to get an accurate result */
399   e->network()->determinePrefixes();
400 }
401
402 /* RPL_UMODEIS - "<user_modes> [<user_mode_params>]" */
403 void CoreSessionEventProcessor::processIrcEvent221(IrcEvent *) {
404   // TODO: save information in network object
405 }
406
407 /* RPL_STATSCONN - "Highest connection cout: 8000 (7999 clients)" */
408 void CoreSessionEventProcessor::processIrcEvent250(IrcEvent *) {
409   // TODO: save information in network object
410 }
411
412 /* RPL_LOCALUSERS - "Current local user: 5024  Max: 7999 */
413 void CoreSessionEventProcessor::processIrcEvent265(IrcEvent *) {
414   // TODO: save information in network object
415 }
416
417 /* RPL_GLOBALUSERS - "Current global users: 46093  Max: 47650" */
418 void CoreSessionEventProcessor::processIrcEvent266(IrcEvent *) {
419   // TODO: save information in network object
420 }
421
422 /*
423 WHOIS-Message:
424    Replies 311 - 313, 317 - 319 are all replies generated in response to a WHOIS message.
425   and 301 (RPL_AWAY)
426               "<nick> :<away message>"
427 WHO-Message:
428    Replies 352 and 315 paired are used to answer a WHO message.
429
430 WHOWAS-Message:
431    Replies 314 and 369 are responses to a WHOWAS message.
432
433 */
434
435 /* RPL_AWAY - "<nick> :<away message>" */
436 void CoreSessionEventProcessor::processIrcEvent301(IrcEvent *e) {
437   if(!checkParamCount(e, 2))
438     return;
439
440   IrcUser *ircuser = e->network()->ircUser(e->params().at(0));
441   if(ircuser) {
442     ircuser->setAway(true);
443     ircuser->setAwayMessage(e->params().at(1));
444     //ircuser->setLastAwayMessage(now);
445   }
446 }
447
448 /* RPL_UNAWAY - ":You are no longer marked as being away" */
449 void CoreSessionEventProcessor::processIrcEvent305(IrcEvent *e) {
450   IrcUser *me = e->network()->me();
451   if(me)
452     me->setAway(false);
453
454   if(e->network()->autoAwayActive()) {
455     e->network()->setAutoAwayActive(false);
456     e->setFlag(EventManager::Silent);
457   }
458 }
459
460 /* RPL_NOWAWAY - ":You have been marked as being away" */
461 void CoreSessionEventProcessor::processIrcEvent306(IrcEvent *e) {
462   IrcUser *me = e->network()->me();
463   if(me)
464     me->setAway(true);
465 }
466
467 /* RPL_WHOISSERVICE - "<user> is registered nick" */
468 void CoreSessionEventProcessor::processIrcEvent307(IrcEvent *e) {
469   if(!checkParamCount(e, 1))
470     return;
471
472   IrcUser *ircuser = e->network()->ircUser(e->params().at(0));
473   if(ircuser)
474     ircuser->setWhoisServiceReply(e->params().join(" "));
475 }
476
477 /* RPL_SUSERHOST - "<user> is available for help." */
478 void CoreSessionEventProcessor::processIrcEvent310(IrcEvent *e) {
479   if(!checkParamCount(e, 1))
480     return;
481
482   IrcUser *ircuser = e->network()->ircUser(e->params().at(0));
483   if(ircuser)
484     ircuser->setSuserHost(e->params().join(" "));
485 }
486
487 /*  RPL_WHOISUSER - "<nick> <user> <host> * :<real name>" */
488 void CoreSessionEventProcessor::processIrcEvent311(IrcEvent *e) {
489   if(!checkParamCount(e, 3))
490     return;
491
492   IrcUser *ircuser = e->network()->ircUser(e->params().at(0));
493   if(ircuser) {
494     ircuser->setUser(e->params().at(1));
495     ircuser->setHost(e->params().at(2));
496     ircuser->setRealName(e->params().last());
497   }
498 }
499
500 /*  RPL_WHOISSERVER -  "<nick> <server> :<server info>" */
501 void CoreSessionEventProcessor::processIrcEvent312(IrcEvent *e) {
502   if(!checkParamCount(e, 2))
503     return;
504
505   IrcUser *ircuser = e->network()->ircUser(e->params().at(0));
506   if(ircuser)
507     ircuser->setServer(e->params().at(1));
508 }
509
510 /*  RPL_WHOISOPERATOR - "<nick> :is an IRC operator" */
511 void CoreSessionEventProcessor::processIrcEvent313(IrcEvent *e) {
512   if(!checkParamCount(e, 1))
513     return;
514
515   IrcUser *ircuser = e->network()->ircUser(e->params().at(0));
516   if(ircuser)
517     ircuser->setIrcOperator(e->params().last());
518 }
519
520 /*  RPL_ENDOFWHO: "<name> :End of WHO list" */
521 void CoreSessionEventProcessor::processIrcEvent315(IrcEvent *e) {
522   if(!checkParamCount(e, 1))
523     return;
524
525   if(coreNetwork(e)->setAutoWhoDone(e->params()[0]))
526     e->setFlag(EventManager::Silent);
527 }
528
529 /*  RPL_WHOISIDLE - "<nick> <integer> :seconds idle"
530    (real life: "<nick> <integer> <integer> :seconds idle, signon time) */
531 void CoreSessionEventProcessor::processIrcEvent317(IrcEvent *e) {
532   if(!checkParamCount(e, 2))
533     return;
534
535   QDateTime loginTime;
536
537   int idleSecs = e->params()[1].toInt();
538   if(e->params().count() > 3) { // if we have more then 3 params we have the above mentioned "real life" situation
539     int logintime = e->params()[2].toInt();
540     loginTime = QDateTime::fromTime_t(logintime);
541   }
542
543   IrcUser *ircuser = e->network()->ircUser(e->params()[0]);
544   if(ircuser) {
545     ircuser->setIdleTime(e->timestamp().addSecs(-idleSecs));
546     if(loginTime.isValid())
547       ircuser->setLoginTime(loginTime);
548   }
549 }
550
551 /* RPL_LIST -  "<channel> <# visible> :<topic>" */
552 void CoreSessionEventProcessor::processIrcEvent322(IrcEvent *e) {
553   if(!checkParamCount(e, 1))
554     return;
555
556   QString channelName;
557   quint32 userCount = 0;
558   QString topic;
559
560   switch(e->params().count()) {
561   case 3:
562     topic = e->params()[2];
563   case 2:
564     userCount = e->params()[1].toUInt();
565   case 1:
566     channelName = e->params()[0];
567   default:
568     break;
569   }
570   if(coreSession()->ircListHelper()->addChannel(e->networkId(), channelName, userCount, topic))
571     e->stop(); // consumed by IrcListHelper, so don't further process/show this event
572 }
573
574 /* RPL_LISTEND ":End of LIST" */
575 void CoreSessionEventProcessor::processIrcEvent323(IrcEvent *e) {
576   if(!checkParamCount(e, 1))
577     return;
578
579   if(coreSession()->ircListHelper()->endOfChannelList(e->networkId()))
580     e->stop(); // consumed by IrcListHelper, so don't further process/show this event
581 }
582
583 /* RPL_CHANNELMODEIS - "<channel> <mode> <mode params>" */
584 void CoreSessionEventProcessor::processIrcEvent324(IrcEvent *e) {
585   processIrcEventMode(e);
586 }
587
588 /* RPL_NOTOPIC */
589 void CoreSessionEventProcessor::processIrcEvent331(IrcEvent *e) {
590   if(!checkParamCount(e, 1))
591     return;
592
593   IrcChannel *chan = e->network()->ircChannel(e->params()[0]);
594   if(chan)
595     chan->setTopic(QString());
596 }
597
598 /* RPL_TOPIC */
599 void CoreSessionEventProcessor::processIrcEvent332(IrcEvent *e) {
600   if(!checkParamCount(e, 2))
601     return;
602
603   IrcChannel *chan = e->network()->ircChannel(e->params()[0]);
604   if(chan)
605     chan->setTopic(e->params()[1]);
606 }
607
608 /*  RPL_WHOREPLY: "<channel> <user> <host> <server> <nick>
609               ( "H" / "G" > ["*"] [ ( "@" / "+" ) ] :<hopcount> <real name>" */
610 void CoreSessionEventProcessor::processIrcEvent352(IrcEvent *e) {
611   if(!checkParamCount(e, 6))
612     return;
613
614   QString channel = e->params()[0];
615   IrcUser *ircuser = e->network()->ircUser(e->params()[4]);
616   if(ircuser) {
617     ircuser->setUser(e->params()[1]);
618     ircuser->setHost(e->params()[2]);
619
620     bool away = e->params()[5].startsWith("G");
621     ircuser->setAway(away);
622     ircuser->setServer(e->params()[3]);
623     ircuser->setRealName(e->params().last().section(" ", 1));
624   }
625
626   if(coreNetwork(e)->isAutoWhoInProgress(channel))
627     e->setFlag(EventManager::Silent);
628 }
629
630 /* RPL_NAMREPLY */
631 void CoreSessionEventProcessor::processIrcEvent353(IrcEvent *e) {
632   if(!checkParamCount(e, 3))
633     return;
634
635   // param[0] is either "=", "*" or "@" indicating a public, private or secret channel
636   // we don't use this information at the time beeing
637   QString channelname = e->params()[1];
638
639   IrcChannel *channel = e->network()->ircChannel(channelname);
640   if(!channel) {
641     qWarning() << Q_FUNC_INFO << "Received unknown target channel:" << channelname;
642     return;
643   }
644
645   QStringList nicks;
646   QStringList modes;
647
648   foreach(QString nick, e->params()[2].split(' ')) {
649     QString mode;
650
651     if(e->network()->prefixes().contains(nick[0])) {
652       mode = e->network()->prefixToMode(nick[0]);
653       nick = nick.mid(1);
654     }
655
656     nicks << nick;
657     modes << mode;
658   }
659
660   channel->joinIrcUsers(nicks, modes);
661 }
662
663 /* ERR_ERRONEUSNICKNAME */
664 void CoreSessionEventProcessor::processIrcEvent432(IrcEventNumeric *e) {
665   QString errnick;
666   if(e->params().count() < 2) {
667     // handle unreal-ircd bug, where unreal ircd doesnt supply a TARGET in ERR_ERRONEUSNICKNAME during registration phase:
668     // nick @@@
669     // :irc.scortum.moep.net 432  @@@ :Erroneous Nickname: Illegal characters
670     // correct server reply:
671     // :irc.scortum.moep.net 432 * @@@ :Erroneous Nickname: Illegal characters
672     e->params().prepend(e->target());
673     e->setTarget("*");
674   }
675   errnick = e->params()[0];
676
677   tryNextNick(e, errnick, true /* erroneus */);
678 }
679
680 /* ERR_NICKNAMEINUSE */
681 void CoreSessionEventProcessor::processIrcEvent433(IrcEventNumeric *e) {
682   if(!checkParamCount(e, 1))
683     return;
684
685   QString errnick = e->params().first();
686
687   // if there is a problem while connecting to the server -> we handle it
688   // but only if our connection has not been finished yet...
689   if(!e->network()->currentServer().isEmpty())
690     return;
691
692   tryNextNick(e, errnick);
693 }
694
695 /* ERR_UNAVAILRESOURCE */
696 void CoreSessionEventProcessor::processIrcEvent437(IrcEventNumeric *e) {
697   if(!checkParamCount(e, 1))
698     return;
699
700   QString errnick = e->params().first();
701
702   // if there is a problem while connecting to the server -> we handle it
703   // but only if our connection has not been finished yet...
704   if(!e->network()->currentServer().isEmpty())
705     return;
706
707   if(!e->network()->isChannelName(errnick))
708     tryNextNick(e, errnick);
709 }
710
711 /* template
712 void CoreSessionEventProcessor::processIrcEvent(IrcEvent *e) {
713   if(!checkParamCount(e, 1))
714     return;
715
716 }
717 */
718
719 /* Handle signals from Netsplit objects  */
720
721 void CoreSessionEventProcessor::handleNetsplitJoin(Network *net,
722                                                    const QString &channel,
723                                                    const QStringList &users,
724                                                    const QStringList &modes,
725                                                    const QString& quitMessage)
726 {
727   IrcChannel *ircChannel = net->ircChannel(channel);
728   if(!ircChannel) {
729     return;
730   }
731   QList<IrcUser *> ircUsers;
732   QStringList newModes = modes;
733   QStringList newUsers = users;
734
735   foreach(const QString &user, users) {
736     IrcUser *iu = net->ircUser(nickFromMask(user));
737     if(iu)
738       ircUsers.append(iu);
739     else { // the user already quit
740       int idx = users.indexOf(user);
741       newUsers.removeAt(idx);
742       newModes.removeAt(idx);
743     }
744   }
745
746   ircChannel->joinIrcUsers(ircUsers, newModes);
747   NetworkSplitEvent *event = new NetworkSplitEvent(EventManager::NetworkSplitJoin, net, channel, newUsers, quitMessage);
748   coreSession()->eventManager()->sendEvent(event);
749 }
750
751 void CoreSessionEventProcessor::handleNetsplitQuit(Network *net, const QString &channel, const QStringList &users, const QString& quitMessage) {
752   NetworkSplitEvent *event = new NetworkSplitEvent(EventManager::NetworkSplitQuit, net, channel, users, quitMessage);
753   coreSession()->eventManager()->sendEvent(event);
754   foreach(QString user, users) {
755     IrcUser *iu = net->ircUser(nickFromMask(user));
756     if(iu)
757       iu->quit();
758   }
759 }
760
761 void CoreSessionEventProcessor::handleEarlyNetsplitJoin(Network *net, const QString &channel, const QStringList &users, const QStringList &modes) {
762   IrcChannel *ircChannel = net->ircChannel(channel);
763   if(!ircChannel) {
764     qDebug() << "handleEarlyNetsplitJoin(): channel " << channel << " invalid";
765     return;
766   }
767   QList<NetworkEvent *> events;
768   QList<IrcUser *> ircUsers;
769   QStringList newModes = modes;
770
771   foreach(QString user, users) {
772     IrcUser *iu = net->updateNickFromMask(user);
773     if(iu) {
774       ircUsers.append(iu);
775       // fake event for scripts that consume join events
776       events << new IrcEvent(EventManager::IrcEventJoin, net, iu->hostmask(), QStringList() << channel);
777     }
778     else {
779       newModes.removeAt(users.indexOf(user));
780     }
781   }
782   ircChannel->joinIrcUsers(ircUsers, newModes);
783   foreach(NetworkEvent *event, events) {
784     event->setFlag(EventManager::Fake); // ignore this in here!
785     coreSession()->eventManager()->sendEvent(event);
786   }
787 }
788
789 void CoreSessionEventProcessor::handleNetsplitFinished() {
790   Netsplit* n = qobject_cast<Netsplit*>(sender());
791   Q_ASSERT(n);
792   QHash<QString, Netsplit *> splithash  = _netsplits.take(n->network());
793   splithash.remove(splithash.key(n));
794   if(splithash.count())
795     _netsplits[n->network()] = splithash;
796   n->deleteLater();
797 }
798
799 void CoreSessionEventProcessor::destroyNetsplits(NetworkId netId) {
800   Network *net = coreSession()->network(netId);
801   if(!net)
802     return;
803
804   QHash<QString, Netsplit *> splits = _netsplits.take(net);
805   qDeleteAll(splits);
806 }