4355b30a6666130fe1d67c2050bab4280a365fbd
[quassel.git] / src / core / ircserverhandler.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-09 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 IrcServerHandler::IrcServerHandler(CoreNetwork *parent)
36   : BasicHandler(parent),
37     _whois(false)
38 {
39 }
40
41 IrcServerHandler::~IrcServerHandler() {
42 }
43
44 /*! Handle a raw message string sent by the server. We try to find a suitable handler, otherwise we call a default handler. */
45 void IrcServerHandler::handleServerMsg(QByteArray msg) {
46   if(msg.isEmpty()) {
47     qWarning() << "Received empty string from server!";
48     return;
49   }
50
51   // Now we split the raw message into its various parts...
52   QString prefix = "";
53   QByteArray trailing;
54   QString cmd;
55
56   // First, check for a trailing parameter introduced by " :", since this might screw up splitting the msg
57   // NOTE: This assumes that this is true in raw encoding, but well, hopefully there are no servers running in japanese on protocol level...
58   int idx = msg.indexOf(" :");
59   if(idx >= 0) {
60     if(msg.length() > idx + 2)
61       trailing = msg.mid(idx + 2);
62     msg = msg.left(idx);
63   }
64   // OK, now it is safe to split...
65   QList<QByteArray> params = msg.split(' ');
66
67   // This could still contain empty elements due to (faulty?) ircds sending multiple spaces in a row
68   // Also, QByteArray is not nearly as convenient to work with as QString for such things :)
69   QList<QByteArray>::iterator iter = params.begin();
70   while(iter != params.end()) {
71     if(iter->isEmpty())
72       iter = params.erase(iter);
73     else
74       ++iter;
75   }
76
77   if(!trailing.isEmpty()) params << trailing;
78   if(params.count() < 1) {
79     qWarning() << "Received invalid string from server!";
80     return;
81   }
82
83   QString foo = serverDecode(params.takeFirst());
84
85   // a colon as the first chars indicates the existence of a prefix
86   if(foo[0] == ':') {
87     foo.remove(0, 1);
88     prefix = foo;
89     if(params.count() < 1) {
90       qWarning() << "Received invalid string from server!";
91       return;
92     }
93     foo = serverDecode(params.takeFirst());
94   }
95
96   // next string without a whitespace is the command
97   cmd = foo.trimmed().toUpper();
98
99   // numeric replies have the target as first param (RFC 2812 - 2.4). this is usually our own nick. Remove this!
100   uint num = cmd.toUInt();
101   if(num > 0) {
102     if(params.count() == 0) {
103       qWarning() << "Message received from server violates RFC and is ignored!" << msg;
104       return;
105     }
106     _target = serverDecode(params.takeFirst());
107   } else {
108     _target = QString();
109   }
110
111   // note that the IRC server is still alive
112   network()->resetPingTimeout();
113
114   // Now we try to find a handler for this message. BTW, I do love the Trolltech guys ;-)
115   handle(cmd, Q_ARG(QString, prefix), Q_ARG(QList<QByteArray>, params));
116 }
117
118
119 void IrcServerHandler::defaultHandler(QString cmd, const QString &prefix, const QList<QByteArray> &rawparams) {
120   // we assume that all this happens in server encoding
121   QStringList params = serverDecode(rawparams);
122   uint num = cmd.toUInt();
123   if(num) {
124     // A lot of server messages don't really need their own handler because they don't do much.
125     // Catch and handle these here.
126     switch(num) {
127       // Welcome, status, info messages. Just display these.
128       case 2: case 3: case 4: case 5: case 251: case 252: case 253: case 254: case 255: case 372: case 375:
129         emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", params.join(" "), prefix);
130         break;
131       // Server error messages without param, just display them
132       case 409: case 411: case 412: case 422: case 424: case 445: case 446: case 451: case 462:
133       case 463: case 464: case 465: case 466: case 472: case 481: case 483: case 485: case 491: case 501: case 502:
134       case 431: // ERR_NONICKNAMEGIVEN
135         emit displayMsg(Message::Error, BufferInfo::StatusBuffer, "", params.join(" "), prefix);
136         break;
137       // Server error messages, display them in red. First param will be appended.
138       case 401: {
139         QString target = params.takeFirst();
140         emit displayMsg(Message::Error, target, params.join(" ") + " " + target, prefix, Message::Redirected);
141         break;
142       }
143       case 402: case 403: case 404: case 406: case 408: case 415: case 421: case 442: {
144         QString channelName = params.takeFirst();
145         emit displayMsg(Message::Error, BufferInfo::StatusBuffer, "", params.join(" ") + " " + channelName, prefix);
146         break;
147       }
148       // Server error messages which will be displayed with a colon between the first param and the rest
149       case 413: case 414: case 423: case 441: case 444: case 461:  // FIXME see below for the 47x codes
150       case 467: case 471: case 473: case 474: case 475: case 476: case 477: case 478: case 482:
151       case 436: // ERR_NICKCOLLISION
152       {
153         QString p = params.takeFirst();
154         emit displayMsg(Message::Error, BufferInfo::StatusBuffer, "", p + ": " + params.join(" "));
155         break;
156       }
157       // Ignore these commands.
158       case 321: case 366: case 376:
159         break;
160
161       // Everything else will be marked in red, so we can add them somewhere.
162       default:
163         if(_whois) {
164           // many nets define their own WHOIS fields. we fetch those not in need of special attention here:
165           emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", "[Whois] " + params.join(" "), prefix);
166         } else {
167           if(coreSession()->ircListHelper()->requestInProgress(network()->networkId()))
168             coreSession()->ircListHelper()->reportError(params.join(" "));
169           else
170             emit displayMsg(Message::Error, BufferInfo::StatusBuffer, "", cmd + " " + params.join(" "), prefix);
171         }
172     }
173     //qDebug() << prefix <<":"<<cmd<<params;
174   } else {
175     emit displayMsg(Message::Error, BufferInfo::StatusBuffer, "", QString("Unknown: ") + cmd + " " + params.join(" "), prefix);
176     //qDebug() << prefix <<":"<<cmd<<params;
177   }
178 }
179
180 //******************************/
181 // IRC SERVER HANDLER
182 //******************************/
183 void IrcServerHandler::handleJoin(const QString &prefix, const QList<QByteArray> &params) {
184   if(!checkParamCount("IrcServerHandler::handleJoin()", params, 1))
185     return;
186
187   QString channel = serverDecode(params[0]);
188   IrcUser *ircuser = network()->updateNickFromMask(prefix);
189   emit displayMsg(Message::Join, BufferInfo::ChannelBuffer, channel, channel, prefix);
190   //qDebug() << "IrcServerHandler::handleJoin()" << prefix << params;
191   ircuser->joinChannel(channel);
192   if(network()->isMe(ircuser)) {
193     network()->setChannelJoined(channel);
194     putCmd("MODE", params[0]); // we want to know the modes of the channel we just joined, so we ask politely
195   }
196 }
197
198 void IrcServerHandler::handleKick(const QString &prefix, const QList<QByteArray> &params) {
199   if(!checkParamCount("IrcServerHandler::handleKick()", params, 2))
200     return;
201
202   network()->updateNickFromMask(prefix);
203   IrcUser *victim = network()->ircUser(params[1]);
204   if(!victim)
205     return;
206
207   QString channel = serverDecode(params[0]);
208   victim->partChannel(channel);
209
210   QString msg;
211   if(params.count() > 2) // someone got a reason!
212     msg = QString("%1 %2").arg(victim->nick()).arg(channelDecode(channel, params[2]));
213   else
214     msg = victim->nick();
215
216   emit displayMsg(Message::Kick, BufferInfo::ChannelBuffer, channel, msg, prefix);
217   //if(network()->isMe(victim)) network()->setKickedFromChannel(channel);
218 }
219
220 void IrcServerHandler::handleMode(const QString &prefix, const QList<QByteArray> &params) {
221   if(!checkParamCount("IrcServerHandler::handleMode()", params, 2))
222     return;
223
224   if(network()->isChannelName(serverDecode(params[0]))) {
225     // Channel Modes
226     emit displayMsg(Message::Mode, BufferInfo::ChannelBuffer, serverDecode(params[0]), serverDecode(params).join(" "), prefix);
227
228     IrcChannel *channel = network()->ircChannel(params[0]);
229     if(!channel) {
230       // we received mode information for a channel we're not in. that means probably we've just been kicked out or something like that
231       // anyways: we don't have a place to store the data --> discard the info.
232       return;
233     }
234
235     QString modes = params[1];
236     bool add = true;
237     int paramOffset = 2;
238     for(int c = 0; c < modes.length(); c++) {
239       if(modes[c] == '+') {
240         add = true;
241         continue;
242       }
243       if(modes[c] == '-') {
244         add = false;
245         continue;
246       }
247
248       if(network()->prefixModes().contains(modes[c])) {
249         // user channel modes (op, voice, etc...)
250         if(paramOffset < params.count()) {
251           IrcUser *ircUser = network()->ircUser(params[paramOffset]);
252           if(add)
253             channel->addUserMode(ircUser, QString(modes[c]));
254           else
255             channel->removeUserMode(ircUser, QString(modes[c]));
256         } else {
257           qWarning() << "Received MODE with too few parameters:" << serverDecode(params);
258         }
259         paramOffset++;
260       } else {
261         // regular channel modes
262         QString value;
263         Network::ChannelModeType modeType = network()->channelModeType(modes[c]);
264         if(modeType == Network::A_CHANMODE || modeType == Network::B_CHANMODE || (modeType == Network::C_CHANMODE && add)) {
265           if(paramOffset < params.count()) {
266             value = params[paramOffset];
267           } else {
268             qWarning() << "Received MODE with too few parameters:" << serverDecode(params);
269           }
270           paramOffset++;
271         }
272
273         if(add)
274           channel->addChannelMode(modes[c], value);
275         else
276           channel->removeChannelMode(modes[c], value);
277       }
278     }
279
280   } else {
281     // pure User Modes
282     IrcUser *ircUser = network()->newIrcUser(params[0]);
283     QString modeString(serverDecode(params[1]));
284     QString addModes;
285     QString removeModes;
286     bool add = false;
287     for(int c = 0; c < modeString.count(); c++) {
288       if(modeString[c] == '+') {
289         add = true;
290         continue;
291       }
292       if(modeString[c] == '-') {
293         add = false;
294         continue;
295       }
296       if(add)
297         addModes += modeString[c];
298       else
299         removeModes += modeString[c];
300     }
301     if(!addModes.isEmpty())
302       ircUser->addUserModes(addModes);
303     if(!removeModes.isEmpty())
304       ircUser->removeUserModes(removeModes);
305
306     // FIXME: redirect
307     emit displayMsg(Message::Mode, BufferInfo::StatusBuffer, "", serverDecode(params).join(" "), prefix);
308   }
309 }
310
311 void IrcServerHandler::handleNick(const QString &prefix, const QList<QByteArray> &params) {
312   if(!checkParamCount("IrcServerHandler::handleNick()", params, 1))
313     return;
314
315   IrcUser *ircuser = network()->updateNickFromMask(prefix);
316   if(!ircuser) {
317     qWarning() << "IrcServerHandler::handleNick(): Unknown IrcUser!";
318     return;
319   }
320   QString newnick = serverDecode(params[0]);
321   QString oldnick = ircuser->nick();
322
323   QString sender = network()->isMyNick(oldnick)
324     ? newnick
325     : prefix;
326
327
328   // the order is cruicial
329   // otherwise the client would rename the buffer, see that the assigned ircuser doesn't match anymore
330   // and remove the ircuser from the querybuffer leading to a wrong on/offline state
331   ircuser->setNick(newnick);
332   coreSession()->renameBuffer(network()->networkId(), newnick, oldnick);
333
334   foreach(QString channel, ircuser->channels())
335     emit displayMsg(Message::Nick, BufferInfo::ChannelBuffer, channel, newnick, sender);
336 }
337
338 void IrcServerHandler::handleNotice(const QString &prefix, const QList<QByteArray> &params) {
339   if(!checkParamCount("IrcServerHandler::handleNotice()", params, 2))
340     return;
341
342   QString target = serverDecode(params[0]);
343
344   // special treatment for welcome messages like:
345   // :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
346   if(!network()->isChannelName(target)) {
347     QString msg = serverDecode(params[1]);
348     QRegExp welcomeRegExp("^\\[([^\\]]+)\\] ");
349     if(welcomeRegExp.indexIn(msg) != -1) {
350       QString channelname = welcomeRegExp.cap(1);
351       msg = msg.mid(welcomeRegExp.matchedLength());
352       CoreIrcChannel *chan = static_cast<CoreIrcChannel *>(network()->ircChannel(channelname)); // we only have CoreIrcChannels in the core, so this cast is safe
353       if(chan && !chan->receivedWelcomeMsg()) {
354         chan->setReceivedWelcomeMsg();
355         emit displayMsg(Message::Notice, BufferInfo::ChannelBuffer, channelname, msg, prefix);
356         return;
357       }
358     }
359   }
360
361   if(prefix.isEmpty() || target == "AUTH") {
362     target = "";
363   } else {
364     if(!target.isEmpty() && network()->prefixes().contains(target[0]))
365       target = target.mid(1);
366     if(!network()->isChannelName(target))
367       target = nickFromMask(prefix);
368   }
369
370   network()->ctcpHandler()->parse(Message::Notice, prefix, target, params[1]);
371 }
372
373 void IrcServerHandler::handlePart(const QString &prefix, const QList<QByteArray> &params) {
374   if(!checkParamCount("IrcServerHandler::handlePart()", params, 1))
375     return;
376
377   IrcUser *ircuser = network()->updateNickFromMask(prefix);
378   QString channel = serverDecode(params[0]);
379   if(!ircuser) {
380     qWarning() << "IrcServerHandler::handlePart(): Unknown IrcUser!";
381     return;
382   }
383
384   ircuser->partChannel(channel);
385
386   QString msg;
387   if(params.count() > 1)
388     msg = userDecode(ircuser->nick(), params[1]);
389
390   emit displayMsg(Message::Part, BufferInfo::ChannelBuffer, channel, msg, prefix);
391   if(network()->isMe(ircuser)) network()->setChannelParted(channel);
392 }
393
394 void IrcServerHandler::handlePing(const QString &prefix, const QList<QByteArray> &params) {
395   Q_UNUSED(prefix);
396   putCmd("PONG", params);
397 }
398
399 void IrcServerHandler::handlePong(const QString &prefix, const QList<QByteArray> &params) {
400   Q_UNUSED(prefix);
401   // the server is supposed to send back what we passed as param. and we send a timestamp
402   // but using quote and whatnought one can send arbitrary pings, so we have to do some sanity checks
403   if(params.count() < 2)
404     return;
405
406   QString timestamp = serverDecode(params[1]);
407   QTime sendTime = QTime::fromString(timestamp, "hh:mm:ss.zzz");
408   if(!sendTime.isValid()) {
409     emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", "PONG " + serverDecode(params).join(" "), prefix);
410     return;
411   }
412
413   network()->setLatency(sendTime.msecsTo(QTime::currentTime()) / 2);
414 }
415
416 void IrcServerHandler::handlePrivmsg(const QString &prefix, const QList<QByteArray> &params) {
417   if(!checkParamCount("IrcServerHandler::handlePrivmsg()", params, 1))
418     return;
419
420   IrcUser *ircuser = network()->updateNickFromMask(prefix);
421   if(!ircuser) {
422     qWarning() << "IrcServerHandler::handlePrivmsg(): Unknown IrcUser!";
423     return;
424   }
425
426   if(params.isEmpty()) {
427     qWarning() << "IrcServerHandler::handlePrivmsg(): received PRIVMSG without target or message from:" << prefix;
428     return;
429   }
430
431   QString target = serverDecode(params[0]);
432
433   QByteArray msg = params.count() < 2
434     ? QByteArray("")
435     : params[1];
436
437   if(!network()->isChannelName(target))
438     target = nickFromMask(prefix);
439
440   // it's possible to pack multiple privmsgs into one param using ctcp
441   // - > we let the ctcpHandler do the work
442   network()->ctcpHandler()->parse(Message::Plain, prefix, target, msg);
443 }
444
445 void IrcServerHandler::handleQuit(const QString &prefix, const QList<QByteArray> &params) {
446   IrcUser *ircuser = network()->updateNickFromMask(prefix);
447   if(!ircuser) return;
448
449   QString msg;
450   if(params.count() > 0)
451     msg = userDecode(ircuser->nick(), params[0]);
452
453   foreach(QString channel, ircuser->channels())
454     emit displayMsg(Message::Quit, BufferInfo::ChannelBuffer, channel, msg, prefix);
455
456   ircuser->quit();
457 }
458
459 void IrcServerHandler::handleTopic(const QString &prefix, const QList<QByteArray> &params) {
460   if(!checkParamCount("IrcServerHandler::handleTopic()", params, 1))
461     return;
462
463   IrcUser *ircuser = network()->updateNickFromMask(prefix);
464   if(!ircuser)
465     return;
466
467   IrcChannel *channel = network()->ircChannel(serverDecode(params[0]));
468   if(!channel)
469     return;
470
471   QString topic;
472   if(params.count() > 1)
473     topic = channelDecode(channel->name(), params[1]);
474
475   channel->setTopic(topic);
476
477   emit displayMsg(Message::Server, BufferInfo::ChannelBuffer, channel->name(), tr("%1 has changed topic for %2 to: \"%3\"").arg(ircuser->nick()).arg(channel->name()).arg(topic));
478 }
479
480 /* RPL_WELCOME */
481 void IrcServerHandler::handle001(const QString &prefix, const QList<QByteArray> &params) {
482   network()->setCurrentServer(prefix);
483
484   if(params.isEmpty()) {
485     emit displayMsg(Message::Error, BufferInfo::StatusBuffer, "", QString("%1 didn't supply a valid welcome message... expect some serious issues..."));
486   }
487   // there should be only one param: "Welcome to the Internet Relay Network <nick>!<user>@<host>"
488   QString param = serverDecode(params[0]);
489   QString myhostmask = param.section(' ', -1, -1);
490
491   network()->setMyNick(nickFromMask(myhostmask));
492
493   emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", param, prefix);
494 }
495
496 /* RPL_ISUPPORT */
497 // TODO Complete 005 handling, also use sensible defaults for non-sent stuff
498 void IrcServerHandler::handle005(const QString &prefix, const QList<QByteArray> &params) {
499   Q_UNUSED(prefix);
500   const int numParams = params.size();
501   if(numParams == 0) {
502     emit displayMsg(Message::Error, BufferInfo::StatusBuffer, "", tr("Received RPL_ISUPPORT (005) without parameters!"), prefix);
503     return;
504   }
505
506   emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", serverDecode(params).join(" "), prefix);
507
508   QString rpl_isupport_suffix = serverDecode(params.last());
509   if(!rpl_isupport_suffix.toLower().contains("are supported by this server")) {
510     emit displayMsg(Message::Error, BufferInfo::StatusBuffer, "", tr("Received non RFC compliant RPL_ISUPPORT: this can lead to unexpected behavior!"), prefix);
511   }
512
513   QString rawSupport;
514   QString key, value;
515   for(int i = 0; i < numParams - 1; i++) {
516     QString rawSupport = serverDecode(params[i]);
517     QString key = rawSupport.section("=", 0, 0);
518     QString value = rawSupport.section("=", 1);
519     network()->addSupport(key, value);
520   }
521 }
522
523 /* RPL_UMODEIS - "<user_modes> [<user_mode_params>]" */
524 void IrcServerHandler::handle221(const QString &prefix, const QList<QByteArray> &params) {
525   Q_UNUSED(prefix)
526   //TODO: save information in network object
527   emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("%1").arg(serverDecode(params).join(" ")));
528 }
529
530 /* RPL_STATSCONN - "Highest connection cout: 8000 (7999 clients)" */
531 void IrcServerHandler::handle250(const QString &prefix, const QList<QByteArray> &params) {
532   Q_UNUSED(prefix)
533   //TODO: save information in network object
534   emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("%1").arg(serverDecode(params).join(" ")));
535 }
536
537 /* RPL_LOCALUSERS - "Current local user: 5024  Max: 7999 */
538 void IrcServerHandler::handle265(const QString &prefix, const QList<QByteArray> &params) {
539   Q_UNUSED(prefix)
540   //TODO: save information in network object
541   emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("%1").arg(serverDecode(params).join(" ")));
542 }
543
544 /* RPL_GLOBALUSERS - "Current global users: 46093  Max: 47650" */
545 void IrcServerHandler::handle266(const QString &prefix, const QList<QByteArray> &params) {
546   Q_UNUSED(prefix)
547   //TODO: save information in network object
548   emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("%1").arg(serverDecode(params).join(" ")));
549 }
550
551 /*
552 WHOIS-Message:
553    Replies 311 - 313, 317 - 319 are all replies generated in response to a WHOIS message.
554   and 301 (RPL_AWAY)
555               "<nick> :<away message>"
556 WHO-Message:
557    Replies 352 and 315 paired are used to answer a WHO message.
558
559 WHOWAS-Message:
560    Replies 314 and 369 are responses to a WHOWAS message.
561
562 */
563
564
565 /*   RPL_AWAY - "<nick> :<away message>" */
566 void IrcServerHandler::handle301(const QString &prefix, const QList<QByteArray> &params) {
567   Q_UNUSED(prefix);
568   if(!checkParamCount("IrcServerHandler::handle301()", params, 2))
569     return;
570
571
572   QString nickName = serverDecode(params[0]);
573   QString awayMessage = userDecode(nickName, params[1]);
574
575   IrcUser *ircuser = network()->ircUser(nickName);
576   if(ircuser) {
577     ircuser->setAwayMessage(awayMessage);
578     ircuser->setAway(true);
579   }
580
581   // FIXME: proper redirection needed
582   if(_whois) {
583     emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("[Whois] %1 is away: \"%2\"").arg(nickName).arg(awayMessage));
584   } else {
585     if(ircuser) {
586       int now = QDateTime::currentDateTime().toTime_t();
587       int silenceTime = 60;
588       if(ircuser->lastAwayMessage() + silenceTime < now) {
589         emit displayMsg(Message::Server, BufferInfo::QueryBuffer, params[0], tr("%1 is away: \"%2\"").arg(nickName).arg(awayMessage));
590       }
591       ircuser->setLastAwayMessage(now);
592     } else {
593       // probably should not happen
594       emit displayMsg(Message::Server, BufferInfo::QueryBuffer, params[0], tr("%1 is away: \"%2\"").arg(nickName).arg(awayMessage));
595     }
596   }
597 }
598
599 // 305  RPL_UNAWAY
600 //      ":You are no longer marked as being away"
601 void IrcServerHandler::handle305(const QString &prefix, const QList<QByteArray> &params) {
602   Q_UNUSED(prefix);
603   IrcUser *me = network()->me();
604   if(me)
605     me->setAway(false);
606
607   if(!network()->autoAwayActive()) {
608     if(!params.isEmpty())
609       emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", serverDecode(params[0]));
610   } else {
611     network()->setAutoAwayActive(false);
612   }
613 }
614
615 // 306  RPL_NOWAWAY
616 //      ":You have been marked as being away"
617 void IrcServerHandler::handle306(const QString &prefix, const QList<QByteArray> &params) {
618   Q_UNUSED(prefix);
619   IrcUser *me = network()->me();
620   if(me)
621     me->setAway(true);
622
623   if(!params.isEmpty() && !network()->autoAwayActive())
624     emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", serverDecode(params[0]));
625 }
626
627 /* RPL_WHOISSERVICE - "<user> is registered nick" */
628 void IrcServerHandler::handle307(const QString &prefix, const QList<QByteArray> &params) {
629   Q_UNUSED(prefix)
630   if(!checkParamCount("IrcServerHandler::handle307()", params, 1))
631     return;
632
633   QString whoisServiceReply = serverDecode(params).join(" ");
634   IrcUser *ircuser = network()->ircUser(serverDecode(params[0]));
635   if(ircuser) {
636     ircuser->setWhoisServiceReply(whoisServiceReply);
637   }
638   emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("[Whois] %1").arg(whoisServiceReply));
639 }
640
641 /* RPL_SUSERHOST - "<user> is available for help." */
642 void IrcServerHandler::handle310(const QString &prefix, const QList<QByteArray> &params) {
643   Q_UNUSED(prefix)
644   if(!checkParamCount("IrcServerHandler::handle310()", params, 1))
645     return;
646
647   QString suserHost = serverDecode(params).join(" ");
648   IrcUser *ircuser = network()->ircUser(serverDecode(params[0]));
649   if(ircuser) {
650     ircuser->setSuserHost(suserHost);
651   }
652   emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("[Whois] %1").arg(suserHost));
653 }
654
655 /*  RPL_WHOISUSER - "<nick> <user> <host> * :<real name>" */
656 void IrcServerHandler::handle311(const QString &prefix, const QList<QByteArray> &params) {
657   Q_UNUSED(prefix)
658   if(!checkParamCount("IrcServerHandler::handle311()", params, 3))
659     return;
660
661   _whois = true;
662   IrcUser *ircuser = network()->ircUser(serverDecode(params[0]));
663   if(ircuser) {
664     ircuser->setUser(serverDecode(params[1]));
665     ircuser->setHost(serverDecode(params[2]));
666     ircuser->setRealName(serverDecode(params.last()));
667     emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("[Whois] %1 is %2 (%3)") .arg(ircuser->nick()).arg(ircuser->hostmask()).arg(ircuser->realName()));
668   } else {
669     emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("[Whois] %1 is %2 (%3)") .arg(serverDecode(params[1])).arg(serverDecode(params[2])).arg(serverDecode(params.last())));
670   }
671 }
672
673 /*  RPL_WHOISSERVER -  "<nick> <server> :<server info>" */
674 void IrcServerHandler::handle312(const QString &prefix, const QList<QByteArray> &params) {
675   Q_UNUSED(prefix)
676   if(!checkParamCount("IrcServerHandler::handle312()", params, 2))
677     return;
678
679   IrcUser *ircuser = network()->ircUser(serverDecode(params[0]));
680   if(ircuser) {
681     ircuser->setServer(serverDecode(params[1]));
682   }
683
684   QString returnString = tr("%1 is online via %2 (%3)").arg(serverDecode(params[0])).arg(serverDecode(params[1])).arg(serverDecode(params.last()));
685   if(_whois) {
686     emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("[Whois] %1").arg(returnString));
687   } else {
688     emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("[Whowas] %1").arg(returnString));
689   }
690 }
691
692 /*  RPL_WHOISOPERATOR - "<nick> :is an IRC operator" */
693 void IrcServerHandler::handle313(const QString &prefix, const QList<QByteArray> &params) {
694   Q_UNUSED(prefix)
695   if(!checkParamCount("IrcServerHandler::handle313()", params, 1))
696     return;
697
698   IrcUser *ircuser = network()->ircUser(serverDecode(params[0]));
699   if(ircuser) {
700     ircuser->setIrcOperator(params.last());
701   }
702   emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("[Whois] %1").arg(serverDecode(params).join(" ")));
703 }
704
705 /*  RPL_WHOWASUSER - "<nick> <user> <host> * :<real name>" */
706 void IrcServerHandler::handle314(const QString &prefix, const QList<QByteArray> &params) {
707   Q_UNUSED(prefix)
708   if(!checkParamCount("IrcServerHandler::handle314()", params, 3))
709     return;
710
711   QString nick = serverDecode(params[0]);
712   QString hostmask = QString("%1@%2").arg(serverDecode(params[1])).arg(serverDecode(params[2]));
713   QString realName = serverDecode(params.last());
714   emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("[Whowas] %1 was %2 (%3)").arg(nick).arg(hostmask).arg(realName));
715 }
716
717 /*  RPL_ENDOFWHO: "<name> :End of WHO list" */
718 void IrcServerHandler::handle315(const QString &prefix, const QList<QByteArray> &params) {
719   Q_UNUSED(prefix);
720   if(!checkParamCount("IrcServerHandler::handle315()", params, 1))
721     return;
722
723   QStringList p = serverDecode(params);
724   if(network()->setAutoWhoDone(p[0])) {
725     return; // stay silent
726   }
727   p.takeLast(); // should be "End of WHO list"
728   emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("[Who] End of /WHO list for %1").arg(p.join(" ")));
729 }
730
731 /*  RPL_WHOISIDLE - "<nick> <integer> :seconds idle"
732    (real life: "<nick> <integer> <integer> :seconds idle, signon time) */
733 void IrcServerHandler::handle317(const QString &prefix, const QList<QByteArray> &params) {
734   Q_UNUSED(prefix);
735   if(!checkParamCount("IrcServerHandler::handle317()", params, 2))
736     return;
737
738   QString nick = serverDecode(params[0]);
739   IrcUser *ircuser = network()->ircUser(nick);
740   if(ircuser) {
741     QDateTime now = QDateTime::currentDateTime();
742     int idleSecs = serverDecode(params[1]).toInt();
743     idleSecs *= -1;
744     ircuser->setIdleTime(now.addSecs(idleSecs));
745     if(params.size() > 3) { // if we have more then 3 params we have the above mentioned "real life" situation
746       int loginTime = serverDecode(params[2]).toInt();
747       ircuser->setLoginTime(QDateTime::fromTime_t(loginTime));
748       emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("[Whois] %1 is logged in since %2").arg(ircuser->nick()).arg(ircuser->loginTime().toString()));
749     }
750     emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("[Whois] %1 is idling for %2 (%3)").arg(ircuser->nick()).arg(secondsToString(ircuser->idleTime().secsTo(now))).arg(ircuser->idleTime().toString()));
751
752   } else {
753     emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("[Whois] idle message: %1").arg(userDecode(nick, params).join(" ")));
754   }
755 }
756
757 /*  RPL_ENDOFWHOIS - "<nick> :End of WHOIS list" */
758 void IrcServerHandler::handle318(const QString &prefix, const QList<QByteArray> &params) {
759   Q_UNUSED(prefix)
760   _whois = false;
761   QStringList parameter = serverDecode(params);
762   parameter.removeFirst();
763   emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("[Whois] %1").arg(parameter.join(" ")));
764 }
765
766 /*  RPL_WHOISCHANNELS - "<nick> :*( ( "@" / "+" ) <channel> " " )" */
767 void IrcServerHandler::handle319(const QString &prefix, const QList<QByteArray> &params) {
768   Q_UNUSED(prefix)
769   if(!checkParamCount("IrcServerHandler::handle319()", params, 2))
770     return;
771
772   QString nick = serverDecode(params.first());
773   QStringList op;
774   QStringList voice;
775   QStringList user;
776   foreach (QString channel, serverDecode(params.last()).split(" ")) {
777     if(channel.startsWith("@"))
778        op.append(channel.remove(0,1));
779     else if(channel.startsWith("+"))
780       voice.append(channel.remove(0,1));
781     else
782       user.append(channel);
783   }
784   if(!user.isEmpty())
785     emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("[Whois] %1 is a user on channels: %2").arg(nick).arg(user.join(" ")));
786   if(!voice.isEmpty())
787     emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("[Whois] %1 has voice on channels: %2").arg(nick).arg(voice.join(" ")));
788   if(!op.isEmpty())
789     emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("[Whois] %1 is an operator on channels: %2").arg(nick).arg(op.join(" ")));
790 }
791
792 /*  RPL_WHOISVIRT - "<nick> is identified to services" */
793 void IrcServerHandler::handle320(const QString &prefix, const QList<QByteArray> &params) {
794   Q_UNUSED(prefix);
795   emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("[Whois] %1").arg(serverDecode(params).join(" ")));
796 }
797
798 /* RPL_LIST -  "<channel> <# visible> :<topic>" */
799 void IrcServerHandler::handle322(const QString &prefix, const QList<QByteArray> &params) {
800   Q_UNUSED(prefix)
801   QString channelName;
802   quint32 userCount = 0;
803   QString topic;
804
805   int paramCount = params.count();
806   switch(paramCount) {
807   case 3:
808     topic = serverDecode(params[2]);
809   case 2:
810     userCount = serverDecode(params[1]).toUInt();
811   case 1:
812     channelName = serverDecode(params[0]);
813   default:
814     break;
815   }
816   if(!coreSession()->ircListHelper()->addChannel(network()->networkId(), channelName, userCount, topic))
817     emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("Channel %1 has %2 users. Topic is: %3").arg(channelName).arg(userCount).arg(topic));
818 }
819
820 /* RPL_LISTEND ":End of LIST" */
821 void IrcServerHandler::handle323(const QString &prefix, const QList<QByteArray> &params) {
822   Q_UNUSED(prefix)
823   Q_UNUSED(params)
824
825   if(!coreSession()->ircListHelper()->endOfChannelList(network()->networkId()))
826     emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("End of channel list"));
827 }
828
829 /* RPL_CHANNELMODEIS - "<channel> <mode> <mode params>" */
830 void IrcServerHandler::handle324(const QString &prefix, const QList<QByteArray> &params) {
831   Q_UNUSED(prefix);
832   handleMode(prefix, params);
833 }
834
835 /* RPL_??? - "<channel> <homepage> */
836 void IrcServerHandler::handle328(const QString &prefix, const QList<QByteArray> &params) {
837   Q_UNUSED(prefix);
838   if(!checkParamCount("IrcServerHandler::handle328()", params, 2))
839     return;
840
841   QString channel = serverDecode(params[0]);
842   QString homepage = serverDecode(params[1]);
843
844   emit displayMsg(Message::Server, BufferInfo::ChannelBuffer, channel, tr("Homepage for %1 is %2").arg(channel, homepage));
845 }
846
847
848 /* RPL_??? - "<channel> <creation time (unix)>" */
849 void IrcServerHandler::handle329(const QString &prefix, const QList<QByteArray> &params) {
850   Q_UNUSED(prefix);
851   if(!checkParamCount("IrcServerHandler::handle329()", params, 2))
852     return;
853
854   QString channel = serverDecode(params[0]);
855   uint unixtime = params[1].toUInt();
856   if(!unixtime) {
857     qWarning() << Q_FUNC_INFO << "received invalid timestamp:" << params[1];
858     return;
859   }
860   QDateTime time = QDateTime::fromTime_t(unixtime);
861
862   emit displayMsg(Message::Server, BufferInfo::ChannelBuffer, channel, tr("Channel %1 created on %2").arg(channel, time.toString()));
863 }
864
865 /* RPL_NOTOPIC */
866 void IrcServerHandler::handle331(const QString &prefix, const QList<QByteArray> &params) {
867   Q_UNUSED(prefix);
868   if(!checkParamCount("IrcServerHandler::handle331()", params, 1))
869     return;
870
871   QString channel = serverDecode(params[0]);
872   IrcChannel *chan = network()->ircChannel(channel);
873   if(chan)
874     chan->setTopic(QString());
875
876   emit displayMsg(Message::Server, BufferInfo::ChannelBuffer, channel, tr("No topic is set for %1.").arg(channel));
877 }
878
879 /* RPL_TOPIC */
880 void IrcServerHandler::handle332(const QString &prefix, const QList<QByteArray> &params) {
881   Q_UNUSED(prefix);
882   if(!checkParamCount("IrcServerHandler::handle332()", params, 2))
883     return;
884
885   QString channel = serverDecode(params[0]);
886   QString topic = channelDecode(channel, params[1]);
887   IrcChannel *chan = network()->ircChannel(channel);
888   if(chan)
889     chan->setTopic(topic);
890
891   emit displayMsg(Message::Server, BufferInfo::ChannelBuffer, channel, tr("Topic for %1 is \"%2\"").arg(channel, topic));
892 }
893
894 /* Topic set by... */
895 void IrcServerHandler::handle333(const QString &prefix, const QList<QByteArray> &params) {
896   Q_UNUSED(prefix);
897   if(!checkParamCount("IrcServerHandler::handle333()", params, 3))
898     return;
899
900   QString channel = serverDecode(params[0]);
901   emit displayMsg(Message::Server, BufferInfo::ChannelBuffer, channel,
902                   tr("Topic set by %1 on %2") .arg(serverDecode(params[1]), QDateTime::fromTime_t(channelDecode(channel, params[2]).toUInt()).toString()));
903 }
904
905 /*  RPL_WHOREPLY: "<channel> <user> <host> <server> <nick>
906               ( "H" / "G" > ["*"] [ ( "@" / "+" ) ] :<hopcount> <real name>" */
907 void IrcServerHandler::handle352(const QString &prefix, const QList<QByteArray> &params) {
908   Q_UNUSED(prefix)
909   if(!checkParamCount("IrcServerHandler::handle352()", params, 6))
910     return;
911
912   QString channel = serverDecode(params[0]);
913   IrcUser *ircuser = network()->ircUser(serverDecode(params[4]));
914   if(ircuser) {
915     ircuser->setUser(serverDecode(params[1]));
916     ircuser->setHost(serverDecode(params[2]));
917
918     bool away = serverDecode(params[5]).startsWith("G") ? true : false;
919     ircuser->setAway(away);
920     ircuser->setServer(serverDecode(params[3]));
921     ircuser->setRealName(serverDecode(params.last()).section(" ", 1));
922   }
923
924   if(!network()->isAutoWhoInProgress(channel)) {
925     emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("[Who] %1").arg(serverDecode(params).join(" ")));
926   }
927 }
928
929 /* RPL_NAMREPLY */
930 void IrcServerHandler::handle353(const QString &prefix, const QList<QByteArray> &params) {
931   Q_UNUSED(prefix);
932   if(!checkParamCount("IrcServerHandler::handle353()", params, 3))
933     return;
934
935   // param[0] is either "=", "*" or "@" indicating a public, private or secret channel
936   // we don't use this information at the time beeing
937   QString channelname = serverDecode(params[1]);
938
939   IrcChannel *channel = network()->ircChannel(channelname);
940   if(!channel) {
941     qWarning() << "IrcServerHandler::handle353(): received unknown target channel:" << channelname;
942     return;
943   }
944
945   QStringList nicks;
946   QStringList modes;
947
948   foreach(QString nick, serverDecode(params[2]).split(' ')) {
949     QString mode = QString();
950
951     if(network()->prefixes().contains(nick[0])) {
952       mode = network()->prefixToMode(nick[0]);
953       nick = nick.mid(1);
954     }
955
956     nicks << nick;
957     modes << mode;
958   }
959
960   channel->joinIrcUsers(nicks, modes);
961 }
962
963 /*  RPL_ENDOFWHOWAS - "<nick> :End of WHOWAS" */
964 void IrcServerHandler::handle369(const QString &prefix, const QList<QByteArray> &params) {
965   Q_UNUSED(prefix)
966   emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("[Whowas] %1").arg(serverDecode(params).join(" ")));
967 }
968
969 /* ERR_ERRONEUSNICKNAME */
970 void IrcServerHandler::handle432(const QString &prefix, const QList<QByteArray> &params) {
971   Q_UNUSED(prefix);
972
973   QString errnick;
974   if(params.size() < 2) {
975     // handle unreal-ircd bug, where unreal ircd doesnt supply a TARGET in ERR_ERRONEUSNICKNAME during registration phase:
976     // nick @@@
977     // :irc.scortum.moep.net 432  @@@ :Erroneous Nickname: Illegal characters
978     // correct server reply:
979     // :irc.scortum.moep.net 432 * @@@ :Erroneous Nickname: Illegal characters
980     errnick = target();
981   } else {
982     errnick = params[0];
983   }
984   emit displayMsg(Message::Error, BufferInfo::StatusBuffer, "", tr("Nick %1 contains illegal characters").arg(errnick));
985   tryNextNick(errnick, true /* erroneus */);
986 }
987
988 /* ERR_NICKNAMEINUSE */
989 void IrcServerHandler::handle433(const QString &prefix, const QList<QByteArray> &params) {
990   Q_UNUSED(prefix);
991   if(!checkParamCount("IrcServerHandler::handle433()", params, 1))
992     return;
993
994   QString errnick = serverDecode(params[0]);
995   emit displayMsg(Message::Error, BufferInfo::StatusBuffer, "", tr("Nick already in use: %1").arg(errnick));
996
997   // if there is a problem while connecting to the server -> we handle it
998   // but only if our connection has not been finished yet...
999   if(!network()->currentServer().isEmpty())
1000     return;
1001
1002   tryNextNick(errnick);
1003 }
1004
1005 /* */
1006
1007 // FIXME networkConnection()->setChannelKey("") for all ERR replies indicating that a JOIN went wrong
1008 //       mostly, these are codes in the 47x range
1009
1010 /* */
1011
1012 void IrcServerHandler::tryNextNick(const QString &errnick, bool erroneus) {
1013   QStringList desiredNicks = coreSession()->identity(network()->identity())->nicks();
1014   int nextNickIdx = desiredNicks.indexOf(errnick) + 1;
1015   QString nextNick;
1016   if(nextNickIdx > 0 && desiredNicks.size() > nextNickIdx) {
1017     nextNick = desiredNicks[nextNickIdx];
1018   } else {
1019     if(erroneus) {
1020       emit displayMsg(Message::Error, BufferInfo::StatusBuffer, "", tr("No free and valid nicks in nicklist found. use: /nick <othernick> to continue"));
1021       return;
1022     } else {
1023       nextNick = errnick + "_";
1024     }
1025   }
1026   putCmd("NICK", serverEncode(nextNick));
1027 }
1028
1029 bool IrcServerHandler::checkParamCount(const QString &methodName, const QList<QByteArray> &params, int minParams) {
1030   if(params.count() < minParams) {
1031     qWarning() << qPrintable(methodName) << "requires" << minParams << "parameters but received only" << params.count() << serverDecode(params);
1032     return false;
1033   } else {
1034     return true;
1035   }
1036 }
1037
1038
1039 /***********************************************************************************/
1040
1041