1 /***************************************************************************
2 * Copyright (C) 2005-10 by the Quassel Project *
3 * devel@quassel-irc.org *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) version 3. *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19 ***************************************************************************/
20 #include "ircserverhandler.h"
24 #include "coresession.h"
25 #include "coreirclisthelper.h"
26 #include "coreidentity.h"
27 #include "ctcphandler.h"
30 #include "coreircchannel.h"
39 IrcServerHandler::IrcServerHandler(CoreNetwork *parent)
40 : CoreBasicHandler(parent),
43 connect(parent, SIGNAL(disconnected(NetworkId)), this, SLOT(destroyNetsplits()));
46 IrcServerHandler::~IrcServerHandler() {
50 /*! Handle a raw message string sent by the server. We try to find a suitable handler, otherwise we call a default handler. */
51 void IrcServerHandler::handleServerMsg(QByteArray msg) {
53 qWarning() << "Received empty string from server!";
57 // Now we split the raw message into its various parts...
62 // First, check for a trailing parameter introduced by " :", since this might screw up splitting the msg
63 // NOTE: This assumes that this is true in raw encoding, but well, hopefully there are no servers running in japanese on protocol level...
64 int idx = msg.indexOf(" :");
66 if(msg.length() > idx + 2)
67 trailing = msg.mid(idx + 2);
70 // OK, now it is safe to split...
71 QList<QByteArray> params = msg.split(' ');
73 // This could still contain empty elements due to (faulty?) ircds sending multiple spaces in a row
74 // Also, QByteArray is not nearly as convenient to work with as QString for such things :)
75 QList<QByteArray>::iterator iter = params.begin();
76 while(iter != params.end()) {
78 iter = params.erase(iter);
83 if(!trailing.isEmpty()) params << trailing;
84 if(params.count() < 1) {
85 qWarning() << "Received invalid string from server!";
89 QString foo = serverDecode(params.takeFirst());
91 // with SASL, the command is 'AUTHENTICATE +' and we should check for this here.
92 /* obsolete because of events
93 if(foo == QString("AUTHENTICATE +")) {
98 // a colon as the first chars indicates the existence of a prefix
102 if(params.count() < 1) {
103 qWarning() << "Received invalid string from server!";
106 foo = serverDecode(params.takeFirst());
109 // next string without a whitespace is the command
110 cmd = foo.trimmed().toUpper();
112 // numeric replies have the target as first param (RFC 2812 - 2.4). this is usually our own nick. Remove this!
113 uint num = cmd.toUInt();
115 if(params.count() == 0) {
116 qWarning() << "Message received from server violates RFC and is ignored!" << msg;
119 _target = serverDecode(params.takeFirst());
124 // note that the IRC server is still alive
125 network()->resetPingTimeout();
127 // Now we try to find a handler for this message. BTW, I do love the Trolltech guys ;-)
128 handle(cmd, Q_ARG(QString, prefix), Q_ARG(QList<QByteArray>, params));
132 void IrcServerHandler::defaultHandler(QString cmd, const QString &prefix, const QList<QByteArray> &rawparams) {
133 // many commands are handled by the event system now
139 //******************************/
140 // IRC SERVER HANDLER
141 //******************************/
143 void IrcServerHandler::handleJoin(const QString &prefix, const QList<QByteArray> ¶ms) {
144 if(!checkParamCount("IrcServerHandler::handleJoin()", params, 1))
147 QString channel = serverDecode(params[0]);
148 IrcUser *ircuser = network()->updateNickFromMask(prefix);
150 bool handledByNetsplit = false;
151 if(!_netsplits.empty()) {
152 foreach(Netsplit* n, _netsplits) {
153 handledByNetsplit = n->userJoined(prefix, channel);
154 if(handledByNetsplit)
160 if(!handledByNetsplit) {
161 emit displayMsg(Message::Join, BufferInfo::ChannelBuffer, channel, channel, prefix);
162 ircuser->joinChannel(channel);
164 //qDebug() << "IrcServerHandler::handleJoin()" << prefix << params;
166 if(network()->isMe(ircuser)) {
167 network()->setChannelJoined(channel);
168 putCmd("MODE", params[0]); // we want to know the modes of the channel we just joined, so we ask politely
172 void IrcServerHandler::handleMode(const QString &prefix, const QList<QByteArray> ¶ms) {
173 if(!checkParamCount("IrcServerHandler::handleMode()", params, 2))
176 if(network()->isChannelName(serverDecode(params[0]))) {
178 emit displayMsg(Message::Mode, BufferInfo::ChannelBuffer, serverDecode(params[0]), serverDecode(params).join(" "), prefix);
180 IrcChannel *channel = network()->ircChannel(params[0]);
182 // we received mode information for a channel we're not in. that means probably we've just been kicked out or something like that
183 // anyways: we don't have a place to store the data --> discard the info.
187 QString modes = params[1];
190 for(int c = 0; c < modes.length(); c++) {
191 if(modes[c] == '+') {
195 if(modes[c] == '-') {
200 if(network()->prefixModes().contains(modes[c])) {
201 // user channel modes (op, voice, etc...)
202 if(paramOffset < params.count()) {
203 IrcUser *ircUser = network()->ircUser(params[paramOffset]);
205 qWarning() << Q_FUNC_INFO << "Unknown IrcUser:" << params[paramOffset];
208 bool handledByNetsplit = false;
209 if(!_netsplits.empty()) {
210 foreach(Netsplit* n, _netsplits) {
211 handledByNetsplit = n->userAlreadyJoined(ircUser->hostmask(), channel->name());
212 if(handledByNetsplit) {
213 n->addMode(ircUser->hostmask(), channel->name(), QString(modes[c]));
218 if(!handledByNetsplit)
219 channel->addUserMode(ircUser, QString(modes[c]));
222 channel->removeUserMode(ircUser, QString(modes[c]));
225 qWarning() << "Received MODE with too few parameters:" << serverDecode(params);
229 // regular channel modes
231 Network::ChannelModeType modeType = network()->channelModeType(modes[c]);
232 if(modeType == Network::A_CHANMODE || modeType == Network::B_CHANMODE || (modeType == Network::C_CHANMODE && add)) {
233 if(paramOffset < params.count()) {
234 value = params[paramOffset];
236 qWarning() << "Received MODE with too few parameters:" << serverDecode(params);
242 channel->addChannelMode(modes[c], value);
244 channel->removeChannelMode(modes[c], value);
250 IrcUser *ircUser = network()->newIrcUser(params[0]);
251 QString modeString(serverDecode(params[1]));
255 for(int c = 0; c < modeString.count(); c++) {
256 if(modeString[c] == '+') {
260 if(modeString[c] == '-') {
265 addModes += modeString[c];
267 removeModes += modeString[c];
269 if(!addModes.isEmpty())
270 ircUser->addUserModes(addModes);
271 if(!removeModes.isEmpty())
272 ircUser->removeUserModes(removeModes);
274 if(network()->isMe(ircUser)) {
275 network()->updatePersistentModes(addModes, removeModes);
279 emit displayMsg(Message::Mode, BufferInfo::StatusBuffer, "", serverDecode(params).join(" "), prefix);
283 void IrcServerHandler::handleNotice(const QString &prefix, const QList<QByteArray> ¶ms) {
284 if(!checkParamCount("IrcServerHandler::handleNotice()", params, 2))
288 QStringList targets = serverDecode(params[0]).split(',', QString::SkipEmptyParts);
289 QStringList::const_iterator targetIter;
290 for(targetIter = targets.constBegin(); targetIter != targets.constEnd(); targetIter++) {
291 QString target = *targetIter;
293 // special treatment for welcome messages like:
294 // :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
295 if(!network()->isChannelName(target)) {
296 QString msg = serverDecode(params[1]);
297 QRegExp welcomeRegExp("^\\[([^\\]]+)\\] ");
298 if(welcomeRegExp.indexIn(msg) != -1) {
299 QString channelname = welcomeRegExp.cap(1);
300 msg = msg.mid(welcomeRegExp.matchedLength());
301 CoreIrcChannel *chan = static_cast<CoreIrcChannel *>(network()->ircChannel(channelname)); // we only have CoreIrcChannels in the core, so this cast is safe
302 if(chan && !chan->receivedWelcomeMsg()) {
303 chan->setReceivedWelcomeMsg();
304 emit displayMsg(Message::Notice, BufferInfo::ChannelBuffer, channelname, msg, prefix);
310 if(prefix.isEmpty() || target == "AUTH") {
313 if(!target.isEmpty() && network()->prefixes().contains(target[0]))
314 target = target.mid(1);
315 if(!network()->isChannelName(target))
316 target = nickFromMask(prefix);
319 network()->ctcpHandler()->parse(Message::Notice, prefix, target, params[1]);
324 void IrcServerHandler::handlePing(const QString &prefix, const QList<QByteArray> ¶ms) {
326 putCmd("PONG", params);
329 void IrcServerHandler::handlePrivmsg(const QString &prefix, const QList<QByteArray> ¶ms) {
330 if(!checkParamCount("IrcServerHandler::handlePrivmsg()", params, 1))
333 IrcUser *ircuser = network()->updateNickFromMask(prefix);
335 qWarning() << "IrcServerHandler::handlePrivmsg(): Unknown IrcUser!";
339 if(params.isEmpty()) {
340 qWarning() << "IrcServerHandler::handlePrivmsg(): received PRIVMSG without target or message from:" << prefix;
344 QString senderNick = nickFromMask(prefix);
346 QByteArray msg = params.count() < 2
350 QStringList targets = serverDecode(params[0]).split(',', QString::SkipEmptyParts);
351 QStringList::const_iterator targetIter;
352 for(targetIter = targets.constBegin(); targetIter != targets.constEnd(); targetIter++) {
353 const QString &target = network()->isChannelName(*targetIter)
358 msg = decrypt(target, msg);
360 // it's possible to pack multiple privmsgs into one param using ctcp
361 // - > we let the ctcpHandler do the work
362 network()->ctcpHandler()->parse(Message::Plain, prefix, target, msg);
366 void IrcServerHandler::handleQuit(const QString &prefix, const QList<QByteArray> ¶ms) {
367 IrcUser *ircuser = network()->updateNickFromMask(prefix);
371 if(params.count() > 0)
372 msg = userDecode(ircuser->nick(), params[0]);
375 if(Netsplit::isNetsplit(msg)) {
377 if(!_netsplits.contains(msg)) {
379 connect(n, SIGNAL(finished()), this, SLOT(handleNetsplitFinished()));
380 connect(n, SIGNAL(netsplitJoin(const QString&, const QStringList&, const QStringList&, const QString&)),
381 this, SLOT(handleNetsplitJoin(const QString&, const QStringList&, const QStringList&, const QString&)));
382 connect(n, SIGNAL(netsplitQuit(const QString&, const QStringList&, const QString&)),
383 this, SLOT(handleNetsplitQuit(const QString&, const QStringList&, const QString&)));
384 connect(n, SIGNAL(earlyJoin(const QString&, const QStringList&, const QStringList&)),
385 this, SLOT(handleEarlyNetsplitJoin(const QString&, const QStringList&, const QStringList&)));
386 _netsplits.insert(msg, n);
391 // add this user to the netsplit
392 n->userQuit(prefix, ircuser->channels(),msg);
396 foreach(QString channel, ircuser->channels())
397 emit displayMsg(Message::Quit, BufferInfo::ChannelBuffer, channel, msg, prefix);
403 // TODO Complete 005 handling, also use sensible defaults for non-sent stuff
404 void IrcServerHandler::handle005(const QString &prefix, const QList<QByteArray> ¶ms) {
406 const int numParams = params.size();
408 emit displayMsg(Message::Error, BufferInfo::StatusBuffer, "", tr("Received RPL_ISUPPORT (005) without parameters!"), prefix);
412 emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", serverDecode(params).join(" "), prefix);
414 QString rpl_isupport_suffix = serverDecode(params.last());
415 if(!rpl_isupport_suffix.toLower().contains("are supported by this server")) {
416 emit displayMsg(Message::Error, BufferInfo::StatusBuffer, "", tr("Received non RFC compliant RPL_ISUPPORT: this can lead to unexpected behavior!"), prefix);
421 for(int i = 0; i < numParams - 1; i++) {
422 QString rawSupport = serverDecode(params[i]);
423 QString key = rawSupport.section("=", 0, 0);
424 QString value = rawSupport.section("=", 1);
425 network()->addSupport(key, value);
428 /* determine our prefixes here to get an accurate result */
429 network()->determinePrefixes();
432 /* RPL_UMODEIS - "<user_modes> [<user_mode_params>]" */
433 void IrcServerHandler::handle221(const QString &prefix, const QList<QByteArray> ¶ms) {
435 //TODO: save information in network object
436 emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("%1").arg(serverDecode(params).join(" ")));
439 /* RPL_STATSCONN - "Highest connection cout: 8000 (7999 clients)" */
440 void IrcServerHandler::handle250(const QString &prefix, const QList<QByteArray> ¶ms) {
442 //TODO: save information in network object
443 emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("%1").arg(serverDecode(params).join(" ")));
446 /* RPL_LOCALUSERS - "Current local user: 5024 Max: 7999 */
447 void IrcServerHandler::handle265(const QString &prefix, const QList<QByteArray> ¶ms) {
449 //TODO: save information in network object
450 emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("%1").arg(serverDecode(params).join(" ")));
453 /* RPL_GLOBALUSERS - "Current global users: 46093 Max: 47650" */
454 void IrcServerHandler::handle266(const QString &prefix, const QList<QByteArray> ¶ms) {
456 //TODO: save information in network object
457 emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("%1").arg(serverDecode(params).join(" ")));
462 Replies 311 - 313, 317 - 319 are all replies generated in response to a WHOIS message.
464 "<nick> :<away message>"
466 Replies 352 and 315 paired are used to answer a WHO message.
469 Replies 314 and 369 are responses to a WHOWAS message.
474 /* RPL_AWAY - "<nick> :<away message>" */
475 void IrcServerHandler::handle301(const QString &prefix, const QList<QByteArray> ¶ms) {
477 if(!checkParamCount("IrcServerHandler::handle301()", params, 2))
481 QString nickName = serverDecode(params[0]);
482 QString awayMessage = userDecode(nickName, params[1]);
484 IrcUser *ircuser = network()->ircUser(nickName);
486 ircuser->setAwayMessage(awayMessage);
487 ircuser->setAway(true);
490 // FIXME: proper redirection needed
492 emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("[Whois] %1 is away: \"%2\"").arg(nickName).arg(awayMessage));
495 int now = QDateTime::currentDateTime().toTime_t();
496 int silenceTime = 60;
497 if(ircuser->lastAwayMessage() + silenceTime < now) {
498 emit displayMsg(Message::Server, BufferInfo::QueryBuffer, params[0], tr("%1 is away: \"%2\"").arg(nickName).arg(awayMessage));
500 ircuser->setLastAwayMessage(now);
502 // probably should not happen
503 emit displayMsg(Message::Server, BufferInfo::QueryBuffer, params[0], tr("%1 is away: \"%2\"").arg(nickName).arg(awayMessage));
509 // ":You are no longer marked as being away"
510 void IrcServerHandler::handle305(const QString &prefix, const QList<QByteArray> ¶ms) {
512 IrcUser *me = network()->me();
516 if(!network()->autoAwayActive()) {
517 if(!params.isEmpty())
518 emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", serverDecode(params[0]));
520 network()->setAutoAwayActive(false);
525 // ":You have been marked as being away"
526 void IrcServerHandler::handle306(const QString &prefix, const QList<QByteArray> ¶ms) {
528 IrcUser *me = network()->me();
532 if(!params.isEmpty() && !network()->autoAwayActive())
533 emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", serverDecode(params[0]));
536 /* RPL_WHOISSERVICE - "<user> is registered nick" */
537 void IrcServerHandler::handle307(const QString &prefix, const QList<QByteArray> ¶ms) {
539 if(!checkParamCount("IrcServerHandler::handle307()", params, 1))
542 QString whoisServiceReply = serverDecode(params).join(" ");
543 IrcUser *ircuser = network()->ircUser(serverDecode(params[0]));
545 ircuser->setWhoisServiceReply(whoisServiceReply);
547 emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("[Whois] %1").arg(whoisServiceReply));
550 /* RPL_SUSERHOST - "<user> is available for help." */
551 void IrcServerHandler::handle310(const QString &prefix, const QList<QByteArray> ¶ms) {
553 if(!checkParamCount("IrcServerHandler::handle310()", params, 1))
556 QString suserHost = serverDecode(params).join(" ");
557 IrcUser *ircuser = network()->ircUser(serverDecode(params[0]));
559 ircuser->setSuserHost(suserHost);
561 emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("[Whois] %1").arg(suserHost));
564 /* RPL_WHOISUSER - "<nick> <user> <host> * :<real name>" */
565 void IrcServerHandler::handle311(const QString &prefix, const QList<QByteArray> ¶ms) {
567 if(!checkParamCount("IrcServerHandler::handle311()", params, 3))
571 IrcUser *ircuser = network()->ircUser(serverDecode(params[0]));
573 ircuser->setUser(serverDecode(params[1]));
574 ircuser->setHost(serverDecode(params[2]));
575 ircuser->setRealName(serverDecode(params.last()));
576 emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("[Whois] %1 is %2 (%3)") .arg(ircuser->nick()).arg(ircuser->hostmask()).arg(ircuser->realName()));
578 QString host = QString("%1!%2@%3").arg(serverDecode(params[0])).arg(serverDecode(params[1])).arg(serverDecode(params[2]));
579 emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("[Whois] %1 is %2 (%3)") .arg(serverDecode(params[0])).arg(host).arg(serverDecode(params.last())));
583 /* RPL_WHOISSERVER - "<nick> <server> :<server info>" */
584 void IrcServerHandler::handle312(const QString &prefix, const QList<QByteArray> ¶ms) {
586 if(!checkParamCount("IrcServerHandler::handle312()", params, 2))
589 IrcUser *ircuser = network()->ircUser(serverDecode(params[0]));
591 ircuser->setServer(serverDecode(params[1]));
594 QString returnString = tr("%1 is online via %2 (%3)").arg(serverDecode(params[0])).arg(serverDecode(params[1])).arg(serverDecode(params.last()));
596 emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("[Whois] %1").arg(returnString));
598 emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("[Whowas] %1").arg(returnString));
602 /* RPL_WHOISOPERATOR - "<nick> :is an IRC operator" */
603 void IrcServerHandler::handle313(const QString &prefix, const QList<QByteArray> ¶ms) {
605 if(!checkParamCount("IrcServerHandler::handle313()", params, 1))
608 IrcUser *ircuser = network()->ircUser(serverDecode(params[0]));
610 ircuser->setIrcOperator(params.last());
612 emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("[Whois] %1").arg(serverDecode(params).join(" ")));
615 /* RPL_WHOWASUSER - "<nick> <user> <host> * :<real name>" */
616 void IrcServerHandler::handle314(const QString &prefix, const QList<QByteArray> ¶ms) {
618 if(!checkParamCount("IrcServerHandler::handle314()", params, 3))
621 QString nick = serverDecode(params[0]);
622 QString hostmask = QString("%1@%2").arg(serverDecode(params[1])).arg(serverDecode(params[2]));
623 QString realName = serverDecode(params.last());
624 emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("[Whowas] %1 was %2 (%3)").arg(nick).arg(hostmask).arg(realName));
627 /* RPL_ENDOFWHO: "<name> :End of WHO list" */
628 void IrcServerHandler::handle315(const QString &prefix, const QList<QByteArray> ¶ms) {
630 if(!checkParamCount("IrcServerHandler::handle315()", params, 1))
633 QStringList p = serverDecode(params);
634 if(network()->setAutoWhoDone(p[0])) {
635 return; // stay silent
637 p.takeLast(); // should be "End of WHO list"
638 emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("[Who] End of /WHO list for %1").arg(p.join(" ")));
641 /* RPL_WHOISIDLE - "<nick> <integer> :seconds idle"
642 (real life: "<nick> <integer> <integer> :seconds idle, signon time) */
643 void IrcServerHandler::handle317(const QString &prefix, const QList<QByteArray> ¶ms) {
645 if(!checkParamCount("IrcServerHandler::handle317()", params, 2))
648 QString nick = serverDecode(params[0]);
649 IrcUser *ircuser = network()->ircUser(nick);
651 QDateTime now = QDateTime::currentDateTime();
652 int idleSecs = serverDecode(params[1]).toInt();
656 ircuser->setIdleTime(now.addSecs(idleSecs));
657 if(params.size() > 3) { // if we have more then 3 params we have the above mentioned "real life" situation
658 int loginTime = serverDecode(params[2]).toInt();
659 ircuser->setLoginTime(QDateTime::fromTime_t(loginTime));
660 emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("[Whois] %1 is logged in since %2").arg(ircuser->nick()).arg(ircuser->loginTime().toString()));
662 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()));
664 QDateTime idleSince = now.addSecs(idleSecs);
665 if (params.size() > 3) { // we have a signon time
666 int loginTime = serverDecode(params[2]).toInt();
667 QDateTime datetime = QDateTime::fromTime_t(loginTime);
668 emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("[Whois] %1 is logged in since %2").arg(nick).arg(datetime.toString()));
670 emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("[Whois] %1 is idling for %2 (%3)").arg(nick).arg(secondsToString(idleSince.secsTo(now))).arg(idleSince.toString()));
674 /* RPL_ENDOFWHOIS - "<nick> :End of WHOIS list" */
675 void IrcServerHandler::handle318(const QString &prefix, const QList<QByteArray> ¶ms) {
678 QStringList parameter = serverDecode(params);
679 parameter.removeFirst();
680 emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("[Whois] %1").arg(parameter.join(" ")));
683 /* RPL_WHOISCHANNELS - "<nick> :*( ( "@" / "+" ) <channel> " " )" */
684 void IrcServerHandler::handle319(const QString &prefix, const QList<QByteArray> ¶ms) {
686 if(!checkParamCount("IrcServerHandler::handle319()", params, 2))
689 QString nick = serverDecode(params.first());
693 foreach (QString channel, serverDecode(params.last()).split(" ")) {
694 if(channel.startsWith("@"))
695 op.append(channel.remove(0,1));
696 else if(channel.startsWith("+"))
697 voice.append(channel.remove(0,1));
699 user.append(channel);
702 emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("[Whois] %1 is a user on channels: %2").arg(nick).arg(user.join(" ")));
704 emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("[Whois] %1 has voice on channels: %2").arg(nick).arg(voice.join(" ")));
706 emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("[Whois] %1 is an operator on channels: %2").arg(nick).arg(op.join(" ")));
709 /* RPL_WHOISVIRT - "<nick> is identified to services" */
710 void IrcServerHandler::handle320(const QString &prefix, const QList<QByteArray> ¶ms) {
712 emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("[Whois] %1").arg(serverDecode(params).join(" ")));
715 /* RPL_LIST - "<channel> <# visible> :<topic>" */
716 void IrcServerHandler::handle322(const QString &prefix, const QList<QByteArray> ¶ms) {
719 quint32 userCount = 0;
722 int paramCount = params.count();
725 topic = serverDecode(params[2]);
727 userCount = serverDecode(params[1]).toUInt();
729 channelName = serverDecode(params[0]);
733 if(!coreSession()->ircListHelper()->addChannel(network()->networkId(), channelName, userCount, topic))
734 emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("Channel %1 has %2 users. Topic is: %3").arg(channelName).arg(userCount).arg(topic));
737 /* RPL_LISTEND ":End of LIST" */
738 void IrcServerHandler::handle323(const QString &prefix, const QList<QByteArray> ¶ms) {
742 if(!coreSession()->ircListHelper()->endOfChannelList(network()->networkId()))
743 emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("End of channel list"));
746 /* RPL_CHANNELMODEIS - "<channel> <mode> <mode params>" */
747 void IrcServerHandler::handle324(const QString &prefix, const QList<QByteArray> ¶ms) {
749 handleMode(prefix, params);
752 /* RPL_??? - "<channel> <homepage> */
753 void IrcServerHandler::handle328(const QString &prefix, const QList<QByteArray> ¶ms) {
755 if(!checkParamCount("IrcServerHandler::handle328()", params, 2))
758 QString channel = serverDecode(params[0]);
759 QString homepage = serverDecode(params[1]);
761 emit displayMsg(Message::Server, BufferInfo::ChannelBuffer, channel, tr("Homepage for %1 is %2").arg(channel, homepage));
765 /* RPL_??? - "<channel> <creation time (unix)>" */
766 void IrcServerHandler::handle329(const QString &prefix, const QList<QByteArray> ¶ms) {
768 if(!checkParamCount("IrcServerHandler::handle329()", params, 2))
771 QString channel = serverDecode(params[0]);
772 uint unixtime = params[1].toUInt();
774 qWarning() << Q_FUNC_INFO << "received invalid timestamp:" << params[1];
777 QDateTime time = QDateTime::fromTime_t(unixtime);
779 emit displayMsg(Message::Server, BufferInfo::ChannelBuffer, channel, tr("Channel %1 created on %2").arg(channel, time.toString()));
782 /* RPL_WHOISACCOUNT: "<nick> <account> :is authed as */
783 void IrcServerHandler::handle330(const QString &prefix, const QList<QByteArray> ¶ms) {
785 if(!checkParamCount("IrcServerHandler::handle330()", params, 3))
788 QString nick = serverDecode(params[0]);
789 QString account = serverDecode(params[1]);
791 emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("[Whois] %1 is authed as %2").arg(nick).arg(account));
795 void IrcServerHandler::handle331(const QString &prefix, const QList<QByteArray> ¶ms) {
797 if(!checkParamCount("IrcServerHandler::handle331()", params, 1))
800 QString channel = serverDecode(params[0]);
801 IrcChannel *chan = network()->ircChannel(channel);
803 chan->setTopic(QString());
805 emit displayMsg(Message::Topic, BufferInfo::ChannelBuffer, channel, tr("No topic is set for %1.").arg(channel));
809 void IrcServerHandler::handle332(const QString &prefix, const QList<QByteArray> ¶ms) {
811 if(!checkParamCount("IrcServerHandler::handle332()", params, 2))
814 QString channel = serverDecode(params[0]);
815 QByteArray rawTopic = params[1];
817 rawTopic = decrypt(channel, rawTopic, true);
819 QString topic = channelDecode(channel, rawTopic);
821 IrcChannel *chan = network()->ircChannel(channel);
823 chan->setTopic(topic);
825 emit displayMsg(Message::Topic, BufferInfo::ChannelBuffer, channel, tr("Topic for %1 is \"%2\"").arg(channel, topic));
828 /* Topic set by... */
829 void IrcServerHandler::handle333(const QString &prefix, const QList<QByteArray> ¶ms) {
831 if(!checkParamCount("IrcServerHandler::handle333()", params, 3))
834 QString channel = serverDecode(params[0]);
835 emit displayMsg(Message::Topic, BufferInfo::ChannelBuffer, channel,
836 tr("Topic set by %1 on %2") .arg(serverDecode(params[1]), QDateTime::fromTime_t(channelDecode(channel, params[2]).toUInt()).toString()));
839 /* RPL_INVITING - "<nick> <channel>*/
840 void IrcServerHandler::handle341(const QString &prefix, const QList<QByteArray> ¶ms) {
842 if(!checkParamCount("IrcServerHandler::handle341()", params, 2))
845 QString nick = serverDecode(params[0]);
847 IrcChannel *channel = network()->ircChannel(serverDecode(params[1]));
849 qWarning() << "IrcServerHandler::handle341(): unknown channel:" << params[1];
853 emit displayMsg(Message::Server, BufferInfo::ChannelBuffer, channel->name(), tr("%1 has been invited to %2").arg(nick).arg(channel->name()));
856 /* RPL_WHOREPLY: "<channel> <user> <host> <server> <nick>
857 ( "H" / "G" > ["*"] [ ( "@" / "+" ) ] :<hopcount> <real name>" */
858 void IrcServerHandler::handle352(const QString &prefix, const QList<QByteArray> ¶ms) {
860 if(!checkParamCount("IrcServerHandler::handle352()", params, 6))
863 QString channel = serverDecode(params[0]);
864 IrcUser *ircuser = network()->ircUser(serverDecode(params[4]));
866 ircuser->setUser(serverDecode(params[1]));
867 ircuser->setHost(serverDecode(params[2]));
869 bool away = serverDecode(params[5]).startsWith("G") ? true : false;
870 ircuser->setAway(away);
871 ircuser->setServer(serverDecode(params[3]));
872 ircuser->setRealName(serverDecode(params.last()).section(" ", 1));
875 if(!network()->isAutoWhoInProgress(channel)) {
876 emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("[Who] %1").arg(serverDecode(params).join(" ")));
881 void IrcServerHandler::handle353(const QString &prefix, const QList<QByteArray> ¶ms) {
883 if(!checkParamCount("IrcServerHandler::handle353()", params, 3))
886 // param[0] is either "=", "*" or "@" indicating a public, private or secret channel
887 // we don't use this information at the time beeing
888 QString channelname = serverDecode(params[1]);
890 IrcChannel *channel = network()->ircChannel(channelname);
892 qWarning() << "IrcServerHandler::handle353(): received unknown target channel:" << channelname;
899 foreach(QString nick, serverDecode(params[2]).split(' ')) {
900 QString mode = QString();
902 if(network()->prefixes().contains(nick[0])) {
903 mode = network()->prefixToMode(nick[0]);
911 channel->joinIrcUsers(nicks, modes);
914 /* RPL_ENDOFWHOWAS - "<nick> :End of WHOWAS" */
915 void IrcServerHandler::handle369(const QString &prefix, const QList<QByteArray> ¶ms) {
917 emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("[Whowas] %1").arg(serverDecode(params).join(" ")));
920 /* ERR_ERRONEUSNICKNAME */
921 void IrcServerHandler::handle432(const QString &prefix, const QList<QByteArray> ¶ms) {
925 if(params.size() < 2) {
926 // handle unreal-ircd bug, where unreal ircd doesnt supply a TARGET in ERR_ERRONEUSNICKNAME during registration phase:
928 // :irc.scortum.moep.net 432 @@@ :Erroneous Nickname: Illegal characters
929 // correct server reply:
930 // :irc.scortum.moep.net 432 * @@@ :Erroneous Nickname: Illegal characters
935 emit displayMsg(Message::Error, BufferInfo::StatusBuffer, "", tr("Nick %1 contains illegal characters").arg(errnick));
936 tryNextNick(errnick, true /* erroneus */);
939 /* ERR_NICKNAMEINUSE */
940 void IrcServerHandler::handle433(const QString &prefix, const QList<QByteArray> ¶ms) {
942 if(!checkParamCount("IrcServerHandler::handle433()", params, 1))
945 QString errnick = serverDecode(params[0]);
946 emit displayMsg(Message::Error, BufferInfo::StatusBuffer, "", tr("Nick already in use: %1").arg(errnick));
948 // if there is a problem while connecting to the server -> we handle it
949 // but only if our connection has not been finished yet...
950 if(!network()->currentServer().isEmpty())
953 tryNextNick(errnick);
956 /* ERR_UNAVAILRESOURCE */
957 void IrcServerHandler::handle437(const QString &prefix, const QList<QByteArray> ¶ms) {
959 if(!checkParamCount("IrcServerHandler::handle437()", params, 1))
962 QString errnick = serverDecode(params[0]);
963 emit displayMsg(Message::Error, BufferInfo::StatusBuffer, "", tr("Nick/channel is temporarily unavailable: %1").arg(errnick));
965 // if there is a problem while connecting to the server -> we handle it
966 // but only if our connection has not been finished yet...
967 if(!network()->currentServer().isEmpty())
970 if(!network()->isChannelName(errnick))
971 tryNextNick(errnick);
974 /* Handle signals from Netsplit objects */
976 void IrcServerHandler::handleNetsplitJoin(const QString &channel, const QStringList &users, const QStringList &modes, const QString& quitMessage)
978 IrcChannel *ircChannel = network()->ircChannel(channel);
982 QList<IrcUser *> ircUsers;
983 QStringList newModes = modes;
984 QStringList newUsers = users;
986 foreach(QString user, users) {
987 IrcUser *iu = network()->ircUser(nickFromMask(user));
990 else { // the user already quit
991 int idx = users.indexOf(user);
992 newUsers.removeAt(idx);
993 newModes.removeAt(idx);
997 QString msg = newUsers.join("#:#").append("#:#").append(quitMessage);
998 emit displayMsg(Message::NetsplitJoin, BufferInfo::ChannelBuffer, channel, msg);
999 ircChannel->joinIrcUsers(ircUsers, newModes);
1002 void IrcServerHandler::handleNetsplitQuit(const QString &channel, const QStringList &users, const QString& quitMessage)
1004 QString msg = users.join("#:#").append("#:#").append(quitMessage);
1005 emit displayMsg(Message::NetsplitQuit, BufferInfo::ChannelBuffer, channel, msg);
1006 foreach(QString user, users) {
1007 IrcUser *iu = network()->ircUser(nickFromMask(user));
1013 void IrcServerHandler::handleEarlyNetsplitJoin(const QString &channel, const QStringList &users, const QStringList &modes) {
1014 IrcChannel *ircChannel = network()->ircChannel(channel);
1016 qDebug() << "handleEarlyNetsplitJoin(): channel " << channel << " invalid";
1019 QList<IrcUser *> ircUsers;
1020 QStringList newModes = modes;
1022 foreach(QString user, users) {
1023 IrcUser *iu = network()->updateNickFromMask(user);
1025 ircUsers.append(iu);
1026 emit displayMsg(Message::Join, BufferInfo::ChannelBuffer, channel, channel, user);
1029 newModes.removeAt(users.indexOf(user));
1032 ircChannel->joinIrcUsers(ircUsers, newModes);
1034 void IrcServerHandler::handleNetsplitFinished()
1036 Netsplit* n = qobject_cast<Netsplit*>(sender());
1037 _netsplits.remove(_netsplits.key(n));
1043 // FIXME networkConnection()->setChannelKey("") for all ERR replies indicating that a JOIN went wrong
1044 // mostly, these are codes in the 47x range
1048 void IrcServerHandler::tryNextNick(const QString &errnick, bool erroneus) {
1049 QStringList desiredNicks = coreSession()->identity(network()->identity())->nicks();
1050 int nextNickIdx = desiredNicks.indexOf(errnick) + 1;
1052 if(nextNickIdx > 0 && desiredNicks.size() > nextNickIdx) {
1053 nextNick = desiredNicks[nextNickIdx];
1056 emit displayMsg(Message::Error, BufferInfo::StatusBuffer, "", tr("No free and valid nicks in nicklist found. use: /nick <othernick> to continue"));
1059 nextNick = errnick + "_";
1062 putCmd("NICK", serverEncode(nextNick));
1065 bool IrcServerHandler::checkParamCount(const QString &methodName, const QList<QByteArray> ¶ms, int minParams) {
1066 if(params.count() < minParams) {
1067 qWarning() << qPrintable(methodName) << "requires" << minParams << "parameters but received only" << params.count() << serverDecode(params);
1074 void IrcServerHandler::destroyNetsplits() {
1075 qDeleteAll(_netsplits);
1080 QByteArray IrcServerHandler::decrypt(const QString &bufferName, const QByteArray &message_, bool isTopic) {
1081 if(message_.isEmpty())
1084 Cipher *cipher = network()->cipher(bufferName);
1088 QByteArray message = message_;
1089 message = isTopic? cipher->decryptTopic(message) : cipher->decrypt(message);