ircnetworkadapter.cpp
1 //------------------------------------------------------------------------------
2 // ircnetworkadapter.cpp
3 //------------------------------------------------------------------------------
4 //
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
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 Free Software
17 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18 // 02110-1301, USA.
19 //
20 //------------------------------------------------------------------------------
21 // Copyright (C) 2010 "Zalewa" <zalewapl@gmail.com>
22 //------------------------------------------------------------------------------
23 #include "ircnetworkadapter.h"
24 #include "irc/configuration/chatnetworkscfg.h"
25 #include "irc/configuration/ircconfig.h"
26 #include "irc/entities/ircuserprefix.h"
27 #include "irc/ops/ircdelayedoperationban.h"
28 #include "irc/ircchanneladapter.h"
29 #include "irc/ircglobal.h"
30 #include "irc/ircisupportparser.h"
31 #include "irc/ircmessageclass.h"
32 #include "irc/ircprivadapter.h"
33 #include "irc/ircrequestparser.h"
34 #include "irc/ircresponseparser.h"
35 #include "irc/ircuserinfo.h"
36 #include "irc/ircuserlist.h"
37 #include "log.h"
38 #include <QDateTime>
39 
40 IRCNetworkAdapter::IRCNetworkAdapter(const IRCNetworkConnectionInfo &connectionInfo)
41 {
42  this->connectionInfo = connectionInfo;
43  this->bIsJoining = false;
44  this->bEmitAllIRCMessages = false;
45 
46  pIrcSocketSignalsAdapter = new IRCSocketSignalsAdapter(this);
47  ircISupportParser = new IRCISupportParser();
48  ircResponseParser = new IRCResponseParser(this);
49  ircClient.connectSocketSignals(pIrcSocketSignalsAdapter);
50 
51  QObject::connect(&ircClient, SIGNAL( ircServerResponse(const QString&) ),
52  this, SLOT( ircServerResponse(const QString&) ) );
53 
54  // Request parser
55  QObject::connect(&ircRequestParser, SIGNAL( echoPrivmsg(const QString&, const QString&) ),
56  this, SLOT( echoPrivmsg(const QString&, const QString&) ) );
57 
58  QObject::connect(&ircRequestParser, SIGNAL( query(const QString&) ),
59  this, SLOT( openNewAdapter(const QString&) ) );
60 
61  // Response parser begins here.
62  QObject::connect(ircResponseParser, SIGNAL( helloClient(const QString&) ),
63  this, SLOT( helloClient(const QString&) ) );
64 
65  QObject::connect(ircResponseParser, SIGNAL(iSupportReceived(QString)),
66  SLOT(appendISupportLine(QString)));
67 
68  QObject::connect(ircResponseParser, SIGNAL( kick(const QString&, const QString&, const QString&, const QString&) ),
69  this, SLOT( kick(const QString&, const QString&, const QString&, const QString&) ) );
70 
71  QObject::connect(ircResponseParser, SIGNAL( kill(const QString&, const QString&) ),
72  this, SLOT( kill(const QString&, const QString&) ) );
73 
74  QObject::connect(ircResponseParser, SIGNAL( modeInfo(const QString&, const QString&, const QString&) ),
75  this, SLOT( modeInfo(const QString&, const QString&, const QString&) ) );
76 
77  QObject::connect(ircResponseParser, SIGNAL( namesListReceived(const QString&, const QStringList&) ),
78  this, SLOT( namesListReceived(const QString&, const QStringList&) ) );
79 
80  QObject::connect(ircResponseParser, SIGNAL( namesListEndReceived(const QString&) ),
81  this, SLOT( namesListEndReceived(const QString&) ) );
82 
83  QObject::connect(ircResponseParser, SIGNAL( nicknameInUse(const QString&) ),
84  this, SLOT( nicknameInUse(const QString&) ) );
85 
86  QObject::connect(ircResponseParser, SIGNAL( noSuchNickname(const QString&) ),
87  this, SLOT( noSuchNickname(const QString&) ) );
88 
89  QObject::connect(ircResponseParser, SIGNAL ( parseError(const QString&) ),
90  this, SLOT( parseError(const QString&) ) );
91 
92  // This connect must be direct as it might interfere with other operations
93  // of printing done in the window.
94  QObject::connect(ircResponseParser, SIGNAL( print(const QString&, const QString&)),
95  this, SLOT(print(const QString&, const QString&) ), Qt::DirectConnection);
96  QObject::connect(ircResponseParser,
97  SIGNAL(printWithClass(const QString&, const QString&, const IRCMessageClass&)),
98  this, SLOT(printWithClass(const QString&, const QString&, const IRCMessageClass&)),
99  Qt::DirectConnection);
100  QObject::connect(ircResponseParser, SIGNAL(printToNetworksCurrentChatBox(QString, IRCMessageClass)),
101  SIGNAL(messageToNetworksCurrentChatBox(QString, IRCMessageClass)));
102 
103  QObject::connect(ircResponseParser, SIGNAL ( privMsgReceived(const QString&, const QString&, const QString&) ),
104  this, SLOT( privMsgReceived(const QString&, const QString&, const QString&) ) );
105  QObject::connect(ircResponseParser, SIGNAL ( privMsgLiteralReceived(QString, QString, IRCMessageClass) ),
106  this, SLOT( printMsgLiteral(QString, QString, IRCMessageClass) ) );
107 
108  QObject::connect(ircResponseParser, SIGNAL ( sendPongMessage(const QString&) ),
109  this, SLOT( sendPong(const QString&) ) );
110 
111  QObject::connect(ircResponseParser, SIGNAL ( userChangesNickname(const QString&, const QString&) ),
112  this, SLOT( userChangesNickname(const QString&, const QString&) ) );
113 
114  QObject::connect(ircResponseParser, SIGNAL(userIdleTime(QString, int)),
115  this, SLOT(userIdleTime(QString, int)));
116 
117  QObject::connect(ircResponseParser, SIGNAL ( userJoinsChannel(const QString&, const QString&, const QString&) ),
118  this, SLOT( userJoinsChannel(const QString&, const QString&, const QString&) ) );
119 
120  QObject::connect(ircResponseParser,
121  SIGNAL(userModeChanged(const QString&, const QString&, const QList<char>&, const QList<char>&)),
122  this,
123  SLOT(userModeChanged(const QString&, const QString&, const QList<char>&, const QList<char>&)));
124 
125  QObject::connect(ircResponseParser, SIGNAL(userNetworkJoinDateTime(QString, QDateTime)),
126  this, SLOT(userNetworkJoinDateTime(QString, QDateTime)));
127 
128  QObject::connect(ircResponseParser, SIGNAL ( userPartsChannel(const QString&, const QString&, const QString&) ),
129  this, SLOT( userPartsChannel(const QString&, const QString&, const QString&) ) );
130 
131  QObject::connect(ircResponseParser, SIGNAL ( userQuitsNetwork(const QString&, const QString&) ),
132  this, SLOT( userQuitsNetwork(const QString&, const QString&) ) );
133 
134  QObject::connect(ircResponseParser, SIGNAL ( whoIsUser(const QString&, const QString&, const QString&, const QString&) ),
135  this, SLOT( whoIsUser(const QString&, const QString&, const QString&, const QString&) ) );
136 }
137 
138 IRCNetworkAdapter::~IRCNetworkAdapter()
139 {
140  disconnect(gIRCConfig.personal.quitMessage);
141 
142  killAllChatWindows();
143  delete this->ircResponseParser;
144  delete this->ircISupportParser;
145  delete this->pIrcSocketSignalsAdapter;
146 }
147 
148 void IRCNetworkAdapter::appendISupportLine(const QString &line)
149 {
150  print(line, QString());
151  ircISupportParser->appendLine(line);
152  ircISupportParser->parse();
153 }
154 
155 void IRCNetworkAdapter::banUser(const QString& nickname, const QString& reason, const QString& channel)
156 {
157  IRCDelayedOperationBan *op = new IRCDelayedOperationBan(this, channel, nickname, this);
158  op->setReason(reason);
159  op->start();
160 }
161 
162 QList<IRCAdapterBase*> IRCNetworkAdapter::childrenAdapters()
163 {
164  QList<IRCAdapterBase*> result;
165  foreach (IRCChatAdapter *adapter, chatWindows.values())
166  {
167  result << adapter;
168  }
169  return result;
170 }
171 
172 void IRCNetworkAdapter::connect()
173 {
174  emit titleChange();
175  ircClient.connect(connectionInfo.networkEntity.address(), connectionInfo.networkEntity.port());
176 }
177 
179 {
180  chatWindows.remove(pAdapter->recipient().toLower());
181 }
182 
183 void IRCNetworkAdapter::disconnect(const QString& farewellMessage)
184 {
185  sendMessage("/quit " + farewellMessage);
186  ircClient.disconnect();
187 }
188 
189 void IRCNetworkAdapter::doSendMessage(const QString& message, IRCAdapterBase* pOrigin)
190 {
191  if (pOrigin == NULL)
192  {
193  pOrigin = this;
194  }
195 
196  if (!ircClient.isConnected())
197  {
198  pOrigin->emitError(tr("You are not connected to the network."));
199  return;
200  }
201 
202  IRCRequestParser::IRCRequestParseResult result = ircRequestParser.parse(pOrigin, message);
203  QString parsedMessage = ircRequestParser.output();
204 
205  switch (result)
206  {
207  case IRCRequestParser::ErrorInputInsufficientParameters:
208  pOrigin->emitError(tr("Insufficient parameters."));
209  break;
210 
211  case IRCRequestParser::ErrorInputNotPrependedWithSlash:
212  emit error(tr("This is a server window. All commands must be prepended with a '/' character."));
213  break;
214 
215  case IRCRequestParser::ErrorMessageEmpty:
216  pOrigin->emitError(tr("Attempted to send empty message."));
217  break;
218 
219  case IRCRequestParser::ErrorMessageTooLong:
220  pOrigin->emitError(tr("Command is too long."));
221  break;
222 
223  case IRCRequestParser::ErrorChatWindowOnly:
224  pOrigin->emitError(tr("Not a chat window."));
225  break;
226 
227  case IRCRequestParser::Ok:
228  ircClient.sendMessage(parsedMessage);
229  break;
230 
231  case IRCRequestParser::QuitCommand:
232  ircClient.sendMessage(parsedMessage);
233  emit messageWithClass(tr("Quit"), IRCMessageClass::NetworkAction);
234  break;
235  }
236 }
237 
238 void IRCNetworkAdapter::echoPrivmsg(const QString& recipient, const QString& content)
239 {
240  // We will echo only chat messages for recipients for whom
241  // we have windows open.
242  if (hasRecipient(recipient))
243  {
244  const QString& sender = this->myNickname();
245  privMsgReceived(recipient, sender, content);
246  }
247 }
248 
249 IRCChatAdapter* IRCNetworkAdapter::getChatAdapter(const QString& recipient)
250 {
251  if (recipient.isEmpty())
252  {
253  emit error("Doomseeker error: getChatAdapter() received empty recipient.");
254  return NULL;
255  }
256 
257  QString recipientLowercase = recipient.toLower();
258  if (hasRecipient(recipientLowercase))
259  {
260  return chatWindows[recipientLowercase];
261  }
262 
263  return NULL;
264 }
265 
266 const IRCChatAdapter* IRCNetworkAdapter::getChatAdapter(const QString& recipient) const
267 {
268  if (recipient.isEmpty())
269  {
270  return NULL;
271  }
272 
273  QString recipientLowercase = recipient.toLower();
274  if (hasRecipient(recipientLowercase))
275  {
276  return chatWindows[recipientLowercase];
277  }
278 
279  return NULL;
280 }
281 
282 IRCChatAdapter* IRCNetworkAdapter::getOrCreateNewChatAdapter(const QString& recipient)
283 {
284  IRCChatAdapter* pAdapter = NULL;
285 
286  if (recipient.isEmpty())
287  {
288  emit error("Doomseeker error: getOrCreateNewChatAdapter() received empty recipient.");
289  return NULL;
290  }
291 
292  QString recipientLowercase = recipient.toLower();
293 
294  if (hasRecipient(recipientLowercase))
295  {
296  return chatWindows[recipientLowercase];
297  }
298 
299 #ifndef NDEBUG
300  Log::instance << QString("IRCNetworkAdapter::getOrCreateNewChatAdapter() Creating new adapter for recipient: %1").arg(recipientLowercase);
301 #endif
302 
303  if (IRCGlobal::isChannelName(recipient))
304  {
305  pAdapter = new IRCChannelAdapter(this, recipient);
306  }
307  else
308  {
309  pAdapter = new IRCPrivAdapter(this, recipient);
310  }
311 
312  chatWindows.insert(recipientLowercase, pAdapter);
313  emit newChatWindowIsOpened(pAdapter);
314 
315  return pAdapter;
316 }
317 
318 bool IRCNetworkAdapter::hasRecipient(const QString& recipient) const
319 {
320  QString recipientLowercase = recipient.toLower();
321  return (chatWindows.find(recipientLowercase) != chatWindows.end());
322 }
323 
324 void IRCNetworkAdapter::helloClient(const QString& nickname)
325 {
326  // This method can only be called
327  // when network is in joining state.
328 
329  connectionInfo.nick = nickname;
330  IRCNetworkEntity& network = connectionInfo.networkEntity;
331 
332  gLog << tr("IRC: Successfuly registered on network %1 [%2:%3]").arg(network.description(), network.address()).arg(network.port());
333 
334  this->bIsJoining = false;
335 
336  if (!network.nickservPassword().isEmpty())
337  {
338  QString messageNickserv = network.nickservCommand();
339  messageNickserv = messageNickserv.arg(network.nickservPassword());
340 
341  this->sendMessage(messageNickserv);
342  }
343  foreach (const QString& command, network.autojoinCommands())
344  {
345  if (!command.trimmed().isEmpty())
346  {
347  this->sendMessage(command);
348  }
349  }
350 
351  foreach (const QString& channel, network.autojoinChannels())
352  {
353  if (IRCGlobal::isChannelName(channel))
354  {
355  this->openNewAdapter(channel);
356  }
357  }
358 
359  // Emit this just to be safe.
360  emit titleChange();
361 }
362 
363 const PatternList &IRCNetworkAdapter::ignoredUsersPatterns() const
364 {
365  return connection().networkEntity.ignoredUsers();
366 }
367 
368 void IRCNetworkAdapter::ircServerResponse(const QString& message)
369 {
370  IRCResponseParseResult result = ircResponseParser->parse(message);
371 
372  if (this->bEmitAllIRCMessages || !result.wasParsed())
373  {
374  emit this->message(message.trimmed().replace("\n", "\\n"));
375  }
376 
377  if (!result.isValid())
378  {
379  emit this->error(tr("Invalid parse result for mesage: %1").arg(message));
380  }
381 }
382 
384 {
385  if (this == pAdapter)
386  {
387  return true;
388  }
389 
390  QList<IRCChatAdapter*> adaptersList = chatWindows.values();
391  foreach (IRCChatAdapter* pChatWindow, adaptersList)
392  {
393  if (pChatWindow == pAdapter)
394  {
395  return true;
396  }
397  }
398 
399  return false;
400 }
401 
402 bool IRCNetworkAdapter::isMyNickname(const QString& nickname) const
403 {
404  IRCUserInfo myUserInfo(this->connectionInfo.nick, this);
405  return (myUserInfo.isSameNickname(nickname));
406 }
407 
408 bool IRCNetworkAdapter::isOperator(const QString& nickname, const QString& channel) const
409 {
410  if (IRCGlobal::isChannelName(channel))
411  {
412  const IRCChannelAdapter* pChannelAdapter = (const IRCChannelAdapter*) this->getChatAdapter(channel);
413  if (pChannelAdapter != NULL)
414  {
415  return pChannelAdapter->isOperator(nickname);
416  }
417  }
418 
419  return false;
420 }
421 
422 void IRCNetworkAdapter::kick(const QString& channel, const QString& byWhom, const QString& whoIsKicked, const QString& reason)
423 {
424  if (hasRecipient(channel))
425  {
426  IRCChannelAdapter* pAdapter = (IRCChannelAdapter*) this->getOrCreateNewChatAdapter(channel);
427 
428  if (isMyNickname(whoIsKicked))
429  {
430  this->emitMessageWithClass(tr("You have been kicked from channel %1 by %2 (Reason: %3)").arg(channel, byWhom, reason), IRCMessageClass::ChannelAction);
431  killChatWindow(channel);
432  }
433  else
434  {
435  pAdapter->emitMessageWithClass(tr("%1 was kicked by %2 (%3)").arg(whoIsKicked, byWhom, reason), IRCMessageClass::ChannelAction);
436  pAdapter->removeNameFromCachedList(whoIsKicked);
437  }
438  }
439 }
440 
441 void IRCNetworkAdapter::kill(const QString& victim, const QString& comment)
442 {
443  emit message(QString("Connection for user %1 was killed. (%2)").arg(victim, comment));
444 
445  // We need to iterate through EVERY adapter and notify them
446  // about this quit.
447  // Implementation of each adapter should recognize if this quit actually
448  // has anything to do with that adapter.
449  QList<IRCChatAdapter*> adaptersList = chatWindows.values();
450  foreach (IRCChatAdapter* pAdapter, adaptersList)
451  {
452  pAdapter->userLeaves(victim, comment, IRCChatAdapter::NetworkKill);
453  }
454 }
455 
456 void IRCNetworkAdapter::killAllChatWindows()
457 {
458  QList<IRCChatAdapter*> pWindows = chatWindows.values();
459  foreach (IRCChatAdapter* pAdapter, pWindows)
460  {
461  // Make sure that the adapter destructor won't call the
462  // detachChatWindow() method or the program will be shot to oblivion.
463  pAdapter->setNetwork(NULL);
464  delete pAdapter;
465  }
466 
467  chatWindows.clear();
468 }
469 
470 void IRCNetworkAdapter::killChatWindow(const QString& recipient)
471 {
472  if (hasRecipient(recipient))
473  {
474  IRCChatAdapter* pAdapter = getChatAdapter(recipient);
475  chatWindows.remove(recipient.toLower());
476 
477  // Make sure that the adapter destructor won't call the
478  // detachChatWindow() method or the program will be shot to oblivion.
479  pAdapter->setNetwork(NULL);
480  delete pAdapter;
481  }
482 }
483 
484 void IRCNetworkAdapter::modeInfo(const QString& channel, const QString& whoSetThis, const QString& modeParams)
485 {
486  IRCChannelAdapter* pAdapter = (IRCChannelAdapter*) this->getOrCreateNewChatAdapter(channel);
487  pAdapter->emitMessageWithClass(tr("%1 sets mode: %2").arg(whoSetThis, modeParams), IRCMessageClass::ChannelAction);
488 }
489 
490 void IRCNetworkAdapter::namesListReceived(const QString& channel, const QStringList& names)
491 {
492  if (this->hasRecipient(channel))
493  {
494  IRCChannelAdapter* pAdapter = (IRCChannelAdapter*) this->getOrCreateNewChatAdapter(channel);
495  pAdapter->appendNamesToCachedList(names);
496  }
497 }
498 
499 void IRCNetworkAdapter::namesListEndReceived(const QString& channel)
500 {
501  if (this->hasRecipient(channel))
502  {
503  IRCChannelAdapter* pAdapter = (IRCChannelAdapter*) this->getOrCreateNewChatAdapter(channel);
504  pAdapter->emitCachedNameListUpdated();
505  }
506 }
507 
508 void IRCNetworkAdapter::nicknameInUse(const QString& nickname)
509 {
510  emit messageToNetworksCurrentChatBox(tr("Nickname %1 is already taken.").arg(nickname), IRCMessageClass::Error);
511  if (this->bIsJoining)
512  {
513  const QString& altNick = this->connectionInfo.alternateNick;
514 
515  if (this->connectionInfo.nick.compare(altNick, Qt::CaseInsensitive) == 0)
516  {
517  emit messageWithClass(tr("Both nickname and alternate nickname are taken on this network."), IRCMessageClass::Error);
518  }
519  else if (altNick.isEmpty())
520  {
521  emit messageWithClass(tr("No alternate nickname specified."), IRCMessageClass::Error);
522  }
523  else
524  {
525  this->connectionInfo.nick = altNick;
526 
527  emit messageWithClass(tr("Using alternate nickname %1 to join.").arg(altNick), IRCMessageClass::NetworkAction);
528  QString message = QString("/nick %1").arg(altNick);
529  sendMessage(message);
530  }
531  }
532 }
533 
534 void IRCNetworkAdapter::noSuchNickname(const QString& nickname)
535 {
536  emit messageToNetworksCurrentChatBox(tr("User %1 is not logged in.").arg(nickname), IRCMessageClass::Error);
537 }
538 
539 void IRCNetworkAdapter::openNewAdapter(const QString& recipientName)
540 {
541  if (!isConnected() || recipientName.isEmpty())
542  {
543  return;
544  }
545 
546  bool bStandardRoutine = !IRCGlobal::isChannelName(recipientName)
547  || hasRecipient(recipientName);
548 
549  if (bStandardRoutine)
550  {
551  IRCChatAdapter* pAdapter = this->getOrCreateNewChatAdapter(recipientName);
552  pAdapter->emitFocusRequest();
553  }
554  else if (IRCGlobal::isChannelName(recipientName))
555  {
556  this->sendMessage("/join " + recipientName);
557  }
558 }
559 
560 void IRCNetworkAdapter::parseError(const QString& error)
561 {
562  emit this->error(tr("IRC parse error: %1").arg(error));
563 }
564 
565 void IRCNetworkAdapter::print(const QString& printWhat, const QString& printWhere)
566 {
567  printWithClass(printWhat, printWhere, IRCMessageClass(IRCMessageClass::Normal));
568 }
569 
570 void IRCNetworkAdapter::printWithClass(const QString& printWhat,
571  const QString& printWhere, const IRCMessageClass& msgClass)
572 {
573  IRCAdapterBase* pAdapter = this;
574 
575  if (!printWhere.isEmpty())
576  {
577  IRCAdapterBase* pAdapterCandidate = getChatAdapter(printWhere);
578  if (pAdapterCandidate != NULL)
579  {
580  pAdapter = pAdapterCandidate;
581  }
582  }
583 
584  // In case if the target adapter is unknown, the message will still get
585  // printed to this adapter.
586  if (pAdapter == NULL)
587  {
588  this->emitMessageWithClass(tr("FROM %1: %2").arg(printWhere, printWhat), msgClass);
589  }
590  else
591  {
592  // If bEmitAllIRCMessages is set to true, the message will be already
593  // printed for this adapter in ircServerResponse().
594  // There is no need to print it again.
595  if ( pAdapter == this && this->bEmitAllIRCMessages )
596  {
597  return;
598  }
599 
600  pAdapter->emitMessageWithClass(printWhat, msgClass);
601  }
602 }
603 
604 void IRCNetworkAdapter::printToCurrentChatBox(const QString& printWhat, const IRCMessageClass& msgClass)
605 {
606  emit messageToNetworksCurrentChatBox(printWhat, msgClass);
607 }
608 
609 void IRCNetworkAdapter::privMsgReceived(const QString& recipient, const QString& sender, const QString& content)
610 {
611  IRCChatAdapter* pAdapter = this->getOrCreateNewChatAdapter(recipient);
612  pAdapter->emitChatMessage(sender, content);
613 }
614 
615 void IRCNetworkAdapter::printMsgLiteral(const QString& recipient, const QString& content,
616  const IRCMessageClass& msgClass)
617 {
618  this->getOrCreateNewChatAdapter(recipient);
619  printWithClass(content, recipient, msgClass);
620 }
621 
622 void IRCNetworkAdapter::reloadNetworkEntityFromConfig()
623 {
624  ChatNetworksCfg cfg;
625  IRCNetworkEntity entity = cfg.network(connectionInfo.networkEntity.description());
626  if (entity.isValid())
627  {
628  setNetworkEntity(entity);
629  }
630 }
631 
632 void IRCNetworkAdapter::setNetworkEntity(const IRCNetworkEntity &entity)
633 {
634  IRCNetworkEntity oldEntity = connectionInfo.networkEntity;
635  connectionInfo.networkEntity = entity;
636  if (oldEntity.description() != entity.description())
637  {
638  emit titleChange();
639  }
640 }
641 
642 void IRCNetworkAdapter::sendCtcp(const QString &nickname, const QString &command)
643 {
644  QString msg = QString("/PRIVMSG %1 %2%3%2").arg(nickname).arg(QChar(0x1)).arg(command);
645  sendMessage(msg);
646 }
647 
648 void IRCNetworkAdapter::sendPong(const QString& toWhom)
649 {
650  QString message = QString("/PONG %1").arg(toWhom);
651  sendMessage(message);
652 }
653 
654 void IRCNetworkAdapter::setChannelMode(const QString& channel, const QString& nickname, const QString& flag, bool bSet)
655 {
656  QString cleanNickname = userPrefixes().cleanNickname(nickname);
657 
658  QString flagPrefixed;
659  if (bSet)
660  {
661  flagPrefixed = "+" + flag.trimmed();
662  }
663  else
664  {
665  flagPrefixed = "-" + flag.trimmed();
666  }
667 
668  QString message = QString("/mode %1 %2 %3").arg(channel, flagPrefixed, cleanNickname);
669  this->sendMessage(message);
670 }
671 
673 {
674  return connectionInfo.networkEntity.description() + " ( " + myNickname() + " )";
675 }
676 
677 void IRCNetworkAdapter::userChangesNickname(const QString& oldNickname, const QString& newNickname)
678 {
679  if (isMyNickname(oldNickname))
680  {
681  emit messageToNetworksCurrentChatBox(tr("Updated own nickname to %1.").arg(newNickname),
682  IRCMessageClass::NetworkAction);
683  connectionInfo.nick = newNickname;
684 
685  emit titleChange();
686  }
687 
688  QList<IRCChatAdapter*> adaptersList = chatWindows.values();
689  foreach (IRCChatAdapter* pAdapter, adaptersList)
690  {
691  pAdapter->userChangesNickname(oldNickname, newNickname);
692  }
693 
694  // MAKE SURE TO SEE IF WE HAVE A CHAT WINDOW OPEN WITH THIS
695  // USER AND FIX THE KEY IN THE CHAT WINDOW MAP FOR THIS NETWORK!!!
696  if (hasRecipient(oldNickname))
697  {
698  QString oldNicknameLowercase = oldNickname.toLower();
699  QString newNicknameLowercase = newNickname.toLower();
700 
701  IRCChatAdapter* pAdapter = getChatAdapter(oldNickname);
702  chatWindows.remove(oldNicknameLowercase);
703  chatWindows.insert(newNicknameLowercase, pAdapter);
704  }
705 }
706 
707 void IRCNetworkAdapter::userJoinsChannel(const QString& channel, const QString& nickname, const QString& fullSignature)
708 {
709  if (!isMyNickname(nickname))
710  {
711  if (hasRecipient(channel))
712  {
713  IRCChannelAdapter* pChannel = (IRCChannelAdapter*)this->getOrCreateNewChatAdapter(channel);
714  pChannel->userJoins(nickname, fullSignature);
715  }
716  }
717  else
718  {
719  IRCChannelAdapter* pChannel = (IRCChannelAdapter*)this->getOrCreateNewChatAdapter(channel);
720  pChannel->emitFocusRequest();
721  }
722 }
723 
724 void IRCNetworkAdapter::userIdleTime(const QString &nick, int secondsIdle)
725 {
726  QString msg = tr("Last activity of user %1 was %2 ago.").arg(
727  nick, Strings::formatTime(secondsIdle));
728  emit messageToNetworksCurrentChatBox(msg, IRCMessageClass::NetworkAction);
729 }
730 
731 void IRCNetworkAdapter::userModeChanged(const QString& channel, const QString& nickname,
732  const QList<char> &addedFlags, const QList<char> &removedFlags)
733 {
734  if (hasRecipient(channel))
735  {
736  IRCChatAdapter* pAdapter = this->getOrCreateNewChatAdapter(channel);
737  pAdapter->userModeChanges(nickname, addedFlags, removedFlags);
738  }
739 }
740 
741 void IRCNetworkAdapter::userNetworkJoinDateTime(const QString &nick, const QDateTime &timeOfJoin)
742 {
743  QString msg = tr("%1 joined the network on %2").arg(nick,
744  timeOfJoin.toString("yyyy-MM-dd HH:mm:ss"));
745  emit messageToNetworksCurrentChatBox(msg, IRCMessageClass::NetworkAction);
746 }
747 
748 void IRCNetworkAdapter::userPartsChannel(const QString& channel, const QString& nickname, const QString& farewellMessage)
749 {
750  if (hasRecipient(channel))
751  {
752  IRCChannelAdapter* pChannel = (IRCChannelAdapter*)getChatAdapter(channel);
753 
754  if (isMyNickname(nickname))
755  {
756  emit messageWithClass(tr("You left channel %1.").arg(channel), IRCMessageClass::ChannelAction);
757  killChatWindow(channel);
758  }
759  else
760  {
761  pChannel->userLeaves(nickname, farewellMessage, IRCChatAdapter::ChannelPart);
762  }
763  }
764 }
765 
767 {
768  return ircISupportParser->userPrefixes();
769 }
770 
771 void IRCNetworkAdapter::userQuitsNetwork(const QString& nickname, const QString& farewellMessage)
772 {
773  // We need to iterate through EVERY adapter and notify them
774  // about this quit.
775  // Implementation of each adapter should recognize if this quit actually
776  // has anything to do with that adapter.
777  QList<IRCChatAdapter*> adaptersList = chatWindows.values();
778  foreach (IRCChatAdapter* pAdapter, adaptersList)
779  {
780  pAdapter->userLeaves(nickname, farewellMessage, IRCChatAdapter::NetworkQuit);
781  }
782 }
783 
784 void IRCNetworkAdapter::userPing(const QString &nickname, qint64 ping)
785 {
786  qint64 delta = QDateTime::currentMSecsSinceEpoch() - ping;
787  emit messageToNetworksCurrentChatBox(
788  tr("Ping to user %1: %2ms").arg(nickname).arg(delta),
789  IRCMessageClass::Ctcp);
790 }
791 
792 void IRCNetworkAdapter::whoIsUser(const QString& nickname, const QString& user, const QString& hostName, const QString& realName)
793 {
794  emit messageToNetworksCurrentChatBox(
795  QString("%1 %2 %3 %4").arg(nickname, user, hostName, realName),
796  IRCMessageClass::NetworkAction);
797 }
798 
800 
801 void IRCSocketSignalsAdapter::connected()
802 {
803  pParent->bIsJoining = true;
804  pParent->emitMessageWithClass(tr("Connected. Sending registration messages."), IRCMessageClass::NetworkAction);
805 
806  IRCNetworkConnectionInfo& connectionInfo = pParent->connectionInfo;
807 
808  QString messagePass = "/PASS %1";
809  QString messageNick = "/NICK %1";
810  QString messageUser = "/USER %1 %2 %3 :%4";
811 
812  IRCNetworkEntity& network = connectionInfo.networkEntity;
813 
814  if (!network.password().isEmpty())
815  {
816  pParent->sendMessage(messagePass.arg(network.password()));
817  }
818 
819  pParent->sendMessage(messageNick.arg(connectionInfo.nick));
820  pParent->sendMessage(messageUser.arg(connectionInfo.nick).arg(connectionInfo.nick).arg(connectionInfo.nick).arg(connectionInfo.realName));
821 }
822 
823 void IRCSocketSignalsAdapter::disconnected()
824 {
825  pParent->killAllChatWindows();
826  gLog << tr("IRC: Disconnected from network %1").arg(pParent->connectionInfo.networkEntity.description());
827  emit pParent->message("Disconnected");
828 }
829 
830 void IRCSocketSignalsAdapter::errorReceived(QAbstractSocket::SocketError error)
831 {
832  emit pParent->error(pSocket->errorString());
833 }
834 
835 void IRCSocketSignalsAdapter::infoMessage(const QString& message)
836 {
837  gLog << message;
838  emit pParent->message(message);
839 }
840 
841 void IRCSocketSignalsAdapter::hostLookupError(QHostInfo::HostInfoError errorValue)
842 {
843  switch (errorValue)
844  {
845  case QHostInfo::HostNotFound:
846  emit pParent->error("Host lookup error: host not found.");
847  break;
848 
849  default:
850  emit pParent->error("Unknown host lookup error.");
851  break;
852  }
853 }
void setNetwork(IRCNetworkAdapter *pNetwork)
Sets IRCNetworkAdapter for this chat window. This adapter is not detached from the old network...
void emitCachedNameListUpdated()
Emits cached list of names. This should be called when end of names list message is received for this...
Class type that is used for conversations within a channel.
void userJoins(const QString &nickname, const QString &fullSignature)
Use this to register the fact that user has joined the chat.
unsigned short port() const
Port of the server or network to connect to.
void openNewAdapter(const QString &recipientName)
Opens a new chat adapter for specified recipient.
void print(const QString &printWhat, const QString &printWhere)
static Log instance
Global instance of the logger.
Definition: log.h:39
void removeNameFromCachedList(const QString &name)
Removes a name from the sortecd cachedNames list.
static QString formatTime(float seconds)
Formats a numerical time value into a string.
Definition: strings.cpp:286
void appendNamesToCachedList(const QStringList &names)
Appends a list of names to cachedNames list. This ensures that no duplicate names are found on the li...
QString title() const
Gets title for this adapter.
void banUser(const QString &nickname, const QString &reason, const QString &channel)
Bans specified user from a channel.
Provides an unified communication interface between a client and IRC network entities.
IRCResponseParseResult parse(const QString &message)
Parses the message received from the network.
void doSendMessage(const QString &message, IRCAdapterBase *pOrigin)
Implemented to support direct communication between client and server.
const QStringList & autojoinChannels() const
List of channels to which a /join command will be issued automatically when a connection with this ne...
Struct containing information about client&#39;s connection to the IRC server.
virtual void emitChatMessage(const QString &sender, const QString &content)
Emits message() signal formatting it to present sender&#39;s message.
const QString & description() const
A short, human-readable description for the network. (Preferably a single word).
IRCNetworkEntity networkEntity
Information about the network to which we will connect.
Handles chatting through IRC.
IRCNetworkAdapter * network()
The idea of the adapter system is that each adapter is either a network or is a child of a network...
virtual void userModeChanges(const QString &nickname, const QList< char > &addedFlags, const QList< char > &removedFlags)=0
Use this to register the fact that user MODE flags have changed.
IRCRequestParseResult parse(IRCAdapterBase *pAdapter, QString input)
Parses input string and returns it through output string. Additional information is passed through re...
void titleChange()
Can be called when the variable returned by title() might have changed and the application should be ...
void userLeaves(const QString &nickname, const QString &farewellMessage, IRCQuitType quitType)
Use this to register the fact that user has left the chat.
bool isSameNickname(const IRCUserInfo &otherUser) const
Check if this user and user specified as parameter are the same user.
Definition: ircuserinfo.cpp:79
bool isOperator(const QString &nickname, const QString &channel) const
Checks if user is an operator on a given channel.
bool wasParsed() const
true if response message was parsed, false if IRCResponseParser ignored the response.
virtual void userChangesNickname(const QString &oldNickname, const QString &newNickname)=0
Use this to register the fact that user has changed his/hers nickname.
Normal has no representation in string, ie. it represents a global style for the widget.
Holds information flags about given nickname.
Definition: ircuserinfo.h:35
void detachChatWindow(const IRCChatAdapter *pAdapter)
Detaches the specified IRCChatAdapter instance from this network without deleting it...
const IRCUserPrefix & userPrefixes() const
All allowed modes with their nickname prefixes for this network.
Result info generated by the IRCResponseParser.
Class type that is used for private conversations with other users directly.
void setChannelMode(const QString &channel, const QString &nickname, const QString &flag, bool bSet)
Sets channel flags.
One-to-one association of visible prefixes to user mode.
Definition: ircuserprefix.h:37
Data structure that describes and defines a connection to an IRC network or server.
const QStringList & autojoinCommands() const
List of commands executed on network join.
QString realName
User&#39;s real name. Optional.
bool isOperator(const QString &nickname) const
Checks if user is an operator on this channel.
void newChatWindowIsOpened(IRCChatAdapter *pWindow)
Signal emitted when a new chat (priv or channel) is opened from this network.
const QString & password() const
Password for the server or network. Ignored if empty.
virtual void userLeaves(const QString &nickname, const QString &farewellMessage, IRCQuitType quitType)=0
Use this to register the fact that user has left the chat.
bool isAdapterRelated(const IRCAdapterBase *pAdapter) const
Checks if pAdapter equals this or is one of chat windows of this network.
QString alternateNick
Alternate nickname in case if &#39; nick &#39; is taken when connecting.
const QString & address() const
Address of the server or network to connect to.
QString nick
Original nickname. This variable will always store the current nickname of the client.