Quassel IRC  Pre-Release
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros
eventstringifier.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  * Copyright (C) 2005-2015 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  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
19  ***************************************************************************/
20 
21 #include "eventstringifier.h"
22 
23 #include "coresession.h"
24 #include "ctcpevent.h"
25 #include "messageevent.h"
26 
28  _coreSession(parent),
29  _whois(false)
30 {
31  connect(this, SIGNAL(newMessageEvent(Event *)), coreSession()->eventManager(), SLOT(postEvent(Event *)));
32 }
33 
34 
35 void EventStringifier::displayMsg(NetworkEvent *event, Message::Type msgType, const QString &msg, const QString &sender,
36  const QString &target, Message::Flags msgFlags)
37 {
38  if (event->flags().testFlag(EventManager::Silent))
39  return;
40 
41  MessageEvent *msgEvent = createMessageEvent(event, msgType, msg, sender, target, msgFlags);
42  //sendMessageEvent(msgEvent);
43  emit newMessageEvent(msgEvent);
44 }
45 
46 
47 MessageEvent *EventStringifier::createMessageEvent(NetworkEvent *event, Message::Type msgType, const QString &msg, const QString &sender,
48  const QString &target, Message::Flags msgFlags)
49 {
50  MessageEvent *msgEvent = new MessageEvent(msgType, event->network(), msg, sender, target, msgFlags);
51  msgEvent->setTimestamp(event->timestamp());
52  return msgEvent;
53 }
54 
55 
57 {
58  if (e->params().count() < minParams) {
59  if (e->type() == EventManager::IrcEventNumeric) {
60  qWarning() << "Command " << static_cast<IrcEventNumeric *>(e)->number() << " requires " << minParams << "params, got: " << e->params();
61  }
62  else {
63  QString name = coreSession()->eventManager()->enumName(e->type());
64  qWarning() << qPrintable(name) << "requires" << minParams << "params, got:" << e->params();
65  }
66  e->stop();
67  return false;
68  }
69  return true;
70 }
71 
72 
73 /* These are only for legacy reasons; remove as soon as we handle NetworkSplitEvents properly */
75 {
76  QString msg = e->users().join("#:#") + "#:#" + e->quitMessage();
77  displayMsg(e, Message::NetsplitJoin, msg, QString(), e->channel());
78 }
79 
80 
82 {
83  QString msg = e->users().join("#:#") + "#:#" + e->quitMessage();
84  displayMsg(e, Message::NetsplitQuit, msg, QString(), e->channel());
85 }
86 
87 
88 /* End legacy */
89 
91 {
92  //qDebug() << e->number();
93  switch (e->number()) {
94  // Welcome, status, info messages. Just display these.
95  case 1:
96  case 2:
97  case 3:
98  case 4:
99  case 5:
100  case 221:
101  case 250:
102  case 251:
103  case 252:
104  case 253:
105  case 254:
106  case 255:
107  case 256:
108  case 257:
109  case 258:
110  case 259:
111  case 265:
112  case 266:
113  case 372:
114  case 375:
115  displayMsg(e, Message::Server, e->params().join(" "), e->prefix());
116  break;
117 
118  // Server error messages without param, just display them
119  case 263:
120  case 409:
121  case 411:
122  case 412:
123  case 422:
124  case 424:
125  case 445:
126  case 446:
127  case 451:
128  case 462:
129  case 463:
130  case 464:
131  case 465:
132  case 466:
133  case 472:
134  case 481:
135  case 483:
136  case 485:
137  case 491:
138  case 501:
139  case 502:
140  case 431: // ERR_NONICKNAMEGIVEN
141  displayMsg(e, Message::Error, e->params().join(" "), e->prefix());
142  break;
143 
144  // Server error messages, display them in red. Colon between first param and rest.
145  case 401:
146  {
147  if (!checkParamCount(e, 1))
148  return;
149 
150  QStringList params = e->params();
151  QString target = params.takeFirst();
152  displayMsg(e, Message::Error, target + ": " + params.join(" "), e->prefix(), target, Message::Redirected);
153  break;
154  }
155 
156  case 402:
157  case 403:
158  case 404:
159  case 406:
160  case 408:
161  case 415:
162  case 421:
163  case 442:
164  {
165  if (!checkParamCount(e, 1))
166  return;
167 
168  QStringList params = e->params();
169  QString channelName = params.takeFirst();
170  displayMsg(e, Message::Error, channelName + ": " + params.join(" "), e->prefix());
171  break;
172  }
173 
174  // Server error messages which will be displayed with a colon between the first param and the rest
175  case 413:
176  case 414:
177  case 423:
178  case 441:
179  case 444:
180  case 461: // FIXME see below for the 47x codes
181  case 467:
182  case 471:
183  case 473:
184  case 474:
185  case 475:
186  case 476:
187  case 477:
188  case 478:
189  case 482:
190  case 436: // ERR_NICKCOLLISION
191  {
192  if (!checkParamCount(e, 1))
193  return;
194 
195  QStringList params = e->params();
196  QString p = params.takeFirst();
197  displayMsg(e, Message::Error, p + ": " + params.join(" "));
198  break;
199  }
200 
201  // Ignore these commands.
202  case 321:
203  case 353:
204  case 366:
205  case 376:
206  break;
207 
208  // CAP stuff
209  case 900:
210  case 903:
211  case 904:
212  case 905:
213  case 906:
214  case 907:
215  {
216  displayMsg(e, Message::Info, "CAP: " + e->params().join(""));
217  break;
218  }
219 
220  // Everything else will be marked in red, so we can add them somewhere.
221  default:
222  if (_whois) {
223  // many nets define their own WHOIS fields. we fetch those not in need of special attention here:
224  displayMsg(e, Message::Server, tr("[Whois] ") + e->params().join(" "), e->prefix());
225  }
226  else {
227  // FIXME figure out how/where to do this in the future
228  //if(coreSession()->ircListHelper()->requestInProgress(network()->networkId()))
229  // coreSession()->ircListHelper()->reportError(params.join(" "));
230  //else
231  displayMsg(e, Message::Error, QString("%1 %2").arg(e->number(), 3, 10, QLatin1Char('0')).arg(e->params().join(" ")), e->prefix());
232  }
233  }
234 }
235 
236 
238 {
239  displayMsg(e, Message::Invite, tr("%1 invited you to channel %2").arg(e->nick(), e->params().at(1)));
240 }
241 
242 
244 {
246  return;
247 
248  displayMsg(e, Message::Join, e->params()[0], e->prefix(), e->params()[0]);
249 }
250 
251 
253 {
254  if (!checkParamCount(e, 2))
255  return;
256 
257  IrcUser *victim = e->network()->ircUser(e->params().at(1));
258  if (victim) {
259  QString channel = e->params().at(0);
260  QString msg = victim->nick();
261  if (e->params().count() > 2)
262  msg += " " + e->params().at(2);
263 
264  displayMsg(e, Message::Kick, msg, e->prefix(), channel);
265  }
266 }
267 
268 
270 {
271  if (e->network()->isChannelName(e->params().first())) {
272  // Channel Modes
273  displayMsg(e, Message::Mode, e->params().join(" "), e->prefix(), e->params().first());
274  }
275  else {
276  // User Modes
277  // FIXME: redirect
278  displayMsg(e, Message::Mode, e->params().join(" "), e->prefix());
279  }
280 }
281 
282 
283 // this needs to be called before the ircuser is renamed!
285 {
286  if (!checkParamCount(e, 1))
287  return;
288 
289  IrcUser *ircuser = e->network()->updateNickFromMask(e->prefix());
290  if (!ircuser) {
291  qWarning() << Q_FUNC_INFO << "Unknown IrcUser!";
292  return;
293  }
294 
295  QString newnick = e->params().at(0);
296  QString oldnick = ircuser->nick();
297 
298  QString sender = e->network()->isMyNick(oldnick) ? newnick : e->prefix();
299  foreach(const QString &channel, ircuser->channels())
300  displayMsg(e, Message::Nick, newnick, sender, channel);
301 }
302 
303 
305 {
306  if (!checkParamCount(e, 1))
307  return;
308 
309  QString channel = e->params().at(0);
310  QString msg = e->params().count() > 1 ? e->params().at(1) : QString();
311 
312  displayMsg(e, Message::Part, msg, e->prefix(), channel);
313 }
314 
315 
317 {
318  QString timestamp = e->params().at(1);
319  QTime sendTime = QTime::fromString(timestamp, "hh:mm:ss.zzz");
320  if (!sendTime.isValid())
321  displayMsg(e, Message::Server, "PONG " + e->params().join(" "), e->prefix());
322 }
323 
324 
326 {
328  return;
329 
330  IrcUser *ircuser = e->network()->updateNickFromMask(e->prefix());
331  if (!ircuser)
332  return;
333 
334  foreach(const QString &channel, ircuser->channels())
335  displayMsg(e, Message::Quit, e->params().count() ? e->params().first() : QString(), e->prefix(), channel);
336 }
337 
338 
340 {
341  displayMsg(e, Message::Topic, tr("%1 has changed topic for %2 to: \"%3\"")
342  .arg(e->nick(), e->params().at(0), e->params().at(1)), QString(), e->params().at(0));
343 }
344 
346 {
347  displayMsg(e, Message::Server, tr("[Operwall] %1: %2").arg(e->nick(), e->params().join(" ")));
348 }
349 
350 
351 /* RPL_ISUPPORT */
353 {
354  if (!e->params().last().contains(QRegExp("are supported (by|on) this server")))
355  displayMsg(e, Message::Error, tr("Received non-RFC-compliant RPL_ISUPPORT: this can lead to unexpected behavior!"), e->prefix());
356  displayMsg(e, Message::Server, e->params().join(" "), e->prefix());
357 }
358 
359 
360 /* RPL_AWAY - "<nick> :<away message>" */
362 {
363  QString nick = e->params().at(0);
364  QString awayMsg = e->params().at(1);
365  QString msg, target;
366  bool send = true;
367 
368  // FIXME: proper redirection needed
369  if (_whois) {
370  msg = tr("[Whois] ");
371  }
372  else {
373  target = nick;
374  IrcUser *ircuser = e->network()->ircUser(nick);
375  if (ircuser) {
376  int now = QDateTime::currentDateTime().toTime_t();
377  const int silenceTime = 60;
378  if (ircuser->lastAwayMessage() + silenceTime >= now)
379  send = false;
380  ircuser->setLastAwayMessage(now);
381  }
382  }
383  if (send)
384  displayMsg(e, Message::Server, msg + tr("%1 is away: \"%2\"").arg(nick, awayMsg), QString(), target);
385 }
386 
387 
388 /* RPL_UNAWAY - ":You are no longer marked as being away" */
390 {
391  displayMsg(e, Message::Server, tr("You are no longer marked as being away"));
392 }
393 
394 
395 /* RPL_NOWAWAY - ":You have been marked as being away" */
397 {
398  if (!e->network()->autoAwayActive())
399  displayMsg(e, Message::Server, tr("You have been marked as being away"));
400 }
401 
402 
403 /*
404 WHOIS-Message:
405  Replies 311 - 313, 317 - 319 are all replies generated in response to a WHOIS message.
406  and 301 (RPL_AWAY)
407  "<nick> :<away message>"
408 WHO-Message:
409  Replies 352 and 315 paired are used to answer a WHO message.
410 
411 WHOWAS-Message:
412  Replies 314 and 369 are responses to a WHOWAS message.
413 
414 */
415 
416 /* RPL_WHOISUSER - "<nick> <user> <host> * :<real name>" */
418 {
419  _whois = true;
420 
421  const QString whoisUserString = tr("[Whois] %1 is %2 (%3)");
422 
423  IrcUser *ircuser = e->network()->ircUser(e->params().at(0));
424  if (ircuser)
425  displayMsg(e, Message::Server, whoisUserString.arg(ircuser->nick(), ircuser->hostmask(), ircuser->realName()));
426  else {
427  QString host = QString("%1!%2@%3").arg(e->params().at(0), e->params().at(1), e->params().at(2));
428  displayMsg(e, Message::Server, whoisUserString.arg(e->params().at(0), host, e->params().last()));
429  }
430 }
431 
432 
433 /* RPL_WHOISSERVER - "<nick> <server> :<server info>" */
435 {
436  if (_whois)
437  displayMsg(e, Message::Server, tr("[Whois] %1 is online via %2 (%3)").arg(e->params().at(0), e->params().at(1), e->params().last()));
438  else
439  displayMsg(e, Message::Server, tr("[Whowas] %1 was online via %2 (%3)").arg(e->params().at(0), e->params().at(1), e->params().last()));
440 }
441 
442 
443 /* RPL_WHOWASUSER - "<nick> <user> <host> * :<real name>" */
445 {
446  if (!checkParamCount(e, 3))
447  return;
448 
449  displayMsg(e, Message::Server, tr("[Whowas] %1 was %2@%3 (%4)").arg(e->params()[0], e->params()[1], e->params()[2], e->params().last()));
450 }
451 
452 
453 /* RPL_ENDOFWHO: "<name> :End of WHO list" */
455 {
456  QStringList p = e->params();
457  p.takeLast(); // should be "End of WHO list"
458  displayMsg(e, Message::Server, tr("[Who] End of /WHO list for %1").arg(p.join(" ")));
459 }
460 
461 
462 /* RPL_WHOISIDLE - "<nick> <integer> :seconds idle"
463  (real life: "<nick> <integer> <integer> :seconds idle, signon time) */
465 {
466  int idleSecs = e->params()[1].toInt();
467 
468  if (e->params().count() > 3) { // if we have more then 3 params we have the above mentioned "real life" situation
469  QDateTime loginTime = QDateTime::fromTime_t(e->params()[2].toInt()).toUTC();
470  displayMsg(e, Message::Server, tr("[Whois] %1 is logged in since %2")
471  .arg(e->params()[0], loginTime.toString("yyyy-MM-dd hh:mm:ss UTC")));
472  }
473  QDateTime idlingSince = e->timestamp().toLocalTime().addSecs(-idleSecs).toUTC();
474  displayMsg(e, Message::Server, tr("[Whois] %1 is idling for %2 (since %3)")
475  .arg(e->params()[0], secondsToString(idleSecs),
476  idlingSince.toString("yyyy-MM-dd hh:mm:ss UTC")));
477 }
478 
479 
480 /* RPL_ENDOFWHOIS - "<nick> :End of WHOIS list" */
482 {
483  _whois = false;
484  displayMsg(e, Message::Server, tr("[Whois] End of /WHOIS list"));
485 }
486 
487 
488 /* RPL_WHOISCHANNELS - "<nick> :*( ( "@" / "+" ) <channel> " " )" */
490 {
491  if (!checkParamCount(e, 2))
492  return;
493 
494  QString nick = e->params().first();
495  QStringList op;
496  QStringList voice;
497  QStringList user;
498  foreach(QString channel, e->params().last().split(" ")) {
499  if (channel.startsWith("@"))
500  op.append(channel.remove(0, 1));
501  else if (channel.startsWith("+"))
502  voice.append(channel.remove(0, 1));
503  else
504  user.append(channel);
505  }
506  if (!user.isEmpty())
507  displayMsg(e, Message::Server, tr("[Whois] %1 is a user on channels: %2").arg(nick, user.join(" ")));
508  if (!voice.isEmpty())
509  displayMsg(e, Message::Server, tr("[Whois] %1 has voice on channels: %2").arg(nick, voice.join(" ")));
510  if (!op.isEmpty())
511  displayMsg(e, Message::Server, tr("[Whois] %1 is an operator on channels: %2").arg(nick, op.join(" ")));
512 }
513 
514 
515 /* RPL_LIST - "<channel> <# visible> :<topic>" */
517 {
518  QString channelName;
519  quint32 userCount = 0;
520  QString topic;
521 
522  switch (e->params().count()) {
523  case 3:
524  topic = e->params()[2];
525  case 2:
526  userCount = e->params()[1].toUInt();
527  case 1:
528  channelName = e->params()[0];
529  default:
530  break;
531  }
532  displayMsg(e, Message::Server, tr("Channel %1 has %2 users. Topic is: \"%3\"")
533  .arg(channelName).arg(userCount).arg(topic));
534 }
535 
536 
537 /* RPL_LISTEND ":End of LIST" */
539 {
540  displayMsg(e, Message::Server, tr("End of channel list"));
541 }
542 
543 
544 /* RPL_CHANNELMODEIS - "<channel> <mode> <mode params>" */
546 {
548 }
549 
550 
551 /* RPL_??? - "<channel> <homepage> */
553 {
554  if (!checkParamCount(e, 2))
555  return;
556 
557  QString channel = e->params()[0];
558  displayMsg(e, Message::Topic, tr("Homepage for %1 is %2").arg(channel, e->params()[1]), QString(), channel);
559 }
560 
561 
562 /* RPL_??? - "<channel> <creation time (unix)>" */
564 {
565  if (!checkParamCount(e, 2))
566  return;
567 
568  QString channel = e->params()[0];
569  uint unixtime = e->params()[1].toUInt();
570  if (!unixtime) {
571  qWarning() << Q_FUNC_INFO << "received invalid timestamp:" << e->params()[1];
572  return;
573  }
574  QDateTime time = QDateTime::fromTime_t(unixtime).toUTC();
575  displayMsg(e, Message::Topic, tr("Channel %1 created on %2")
576  .arg(channel, time.toString("yyyy-MM-dd hh:mm:ss UTC")),
577  QString(), channel);
578 }
579 
580 
581 /* RPL_WHOISACCOUNT: "<nick> <account> :is authed as */
583 {
584  if (e->params().count() < 3)
585  return;
586 
587  // check for whois or whowas
588  if (_whois) {
589  displayMsg(e, Message::Server, tr("[Whois] %1 is authed as %2").arg(e->params()[0], e->params()[1]));
590  }
591  else {
592  displayMsg(e, Message::Server, tr("[Whowas] %1 was authed as %2").arg(e->params()[0], e->params()[1]));
593  }
594 }
595 
596 
597 /* RPL_NOTOPIC */
599 {
600  QString channel = e->params().first();
601  displayMsg(e, Message::Topic, tr("No topic is set for %1.").arg(channel), QString(), channel);
602 }
603 
604 
605 /* RPL_TOPIC */
607 {
608  QString channel = e->params().first();
609  displayMsg(e, Message::Topic, tr("Topic for %1 is \"%2\"").arg(channel, e->params()[1]), QString(), channel);
610 }
611 
612 
613 /* Topic set by... */
615 {
616  if (!checkParamCount(e, 3))
617  return;
618 
619  QString channel = e->params().first();
620  QDateTime topicSetTime = QDateTime::fromTime_t(e->params()[2].toInt()).toUTC();
621  displayMsg(e, Message::Topic, tr("Topic set by %1 on %2")
622  .arg(e->params()[1],
623  topicSetTime.toString("yyyy-MM-dd hh:mm:ss UTC")), QString(), channel);
624 }
625 
626 
627 /* RPL_INVITING - "<nick> <channel>*/
629 {
630  if (!checkParamCount(e, 2))
631  return;
632 
633  QString channel = e->params()[1];
634  displayMsg(e, Message::Server, tr("%1 has been invited to %2").arg(e->params().first(), channel), QString(), channel);
635 }
636 
637 
638 /* RPL_WHOREPLY: "<channel> <user> <host> <server> <nick>
639  ( "H" / "G" > ["*"] [ ( "@" / "+" ) ] :<hopcount> <real name>" */
641 {
642  displayMsg(e, Message::Server, tr("[Who] %1").arg(e->params().join(" ")));
643 }
644 
645 
646 /* RPL_ENDOFWHOWAS - "<nick> :End of WHOWAS" */
648 {
649  displayMsg(e, Message::Server, tr("End of /WHOWAS"));
650 }
651 
652 
653 /* ERR_ERRONEUSNICKNAME */
655 {
656  if (!checkParamCount(e, 1))
657  return;
658 
659  displayMsg(e, Message::Error, tr("Nick %1 contains illegal characters").arg(e->params()[0]));
660 }
661 
662 
663 /* ERR_NICKNAMEINUSE */
665 {
666  if (!checkParamCount(e, 1))
667  return;
668 
669  displayMsg(e, Message::Error, tr("Nick already in use: %1").arg(e->params()[0]));
670 }
671 
672 
673 /* ERR_UNAVAILRESOURCE */
675 {
676  if (!checkParamCount(e, 1))
677  return;
678 
679  displayMsg(e, Message::Error, tr("Nick/channel is temporarily unavailable: %1").arg(e->params()[0]));
680 }
681 
682 
683 // template
684 /*
685 
686 void EventStringifier::processIrcEvent(IrcEvent *e) {
687 
688 }
689 
690 */
691 
692 /*******************************/
693 /******** CTCP HANDLING ********/
694 /*******************************/
695 
697 {
698  if (e->type() != EventManager::CtcpEvent)
699  return;
700 
701  if (e->testFlag(EventManager::Self)) {
702  displayMsg(e, Message::Action, tr("sending CTCP-%1 request to %2").arg(e->ctcpCmd(), e->target()), e->network()->myNick());
703  return;
704  }
705 
706  handle(e->ctcpCmd(), Q_ARG(CtcpEvent *, e));
707 }
708 
709 
710 void EventStringifier::defaultHandler(const QString &ctcpCmd, CtcpEvent *e)
711 {
712  Q_UNUSED(ctcpCmd);
713  if (e->ctcpType() == CtcpEvent::Query) {
714  QString unknown;
715  if (e->reply().isNull()) // all known core-side handlers (except for ACTION) set a reply!
716  //: Optional "unknown" in "Received unknown CTCP-FOO request by bar"
717  unknown = tr("unknown") + ' ';
718  displayMsg(e, Message::Server, tr("Received %1CTCP-%2 request by %3").arg(unknown, e->ctcpCmd(), e->prefix()));
719  return;
720  }
721  displayMsg(e, Message::Server, tr("Received CTCP-%1 answer from %2: %3").arg(e->ctcpCmd(), nickFromMask(e->prefix()), e->param()));
722 }
723 
724 
726 {
727  displayMsg(e, Message::Action, e->param(), e->prefix(), e->target());
728 }
729 
730 
732 {
733  if (e->ctcpType() == CtcpEvent::Query)
734  defaultHandler(e->ctcpCmd(), e);
735  else {
736  displayMsg(e, Message::Server, tr("Received CTCP-PING answer from %1 with %2 milliseconds round trip time")
737  .arg(nickFromMask(e->prefix())).arg(QDateTime::fromMSecsSinceEpoch(e->param().toULongLong()).msecsTo(e->timestamp())));
738  }
739 }