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  IRCChatAdapter* pAdapter = NULL;
252 
253  if (recipient.isEmpty())
254  {
255  emit error("Doomseeker error: getChatAdapter() received empty recipient.");
256  return NULL;
257  }
258 
259  QString recipientLowercase = recipient.toLower();
260  if (hasRecipient(recipientLowercase))
261  {
262  return chatWindows[recipientLowercase];
263  }
264 
265  return NULL;
266 }
267 
268 const IRCChatAdapter* IRCNetworkAdapter::getChatAdapter(const QString& recipient) const
269 {
270  const IRCChatAdapter* pAdapter = NULL;
271 
272  if (recipient.isEmpty())
273  {
274  return NULL;
275  }
276 
277  QString recipientLowercase = recipient.toLower();
278  if (hasRecipient(recipientLowercase))
279  {
280  return chatWindows[recipientLowercase];
281  }
282 
283  return NULL;
284 }
285 
286 IRCChatAdapter* IRCNetworkAdapter::getOrCreateNewChatAdapter(const QString& recipient)
287 {
288  IRCChatAdapter* pAdapter = NULL;
289 
290  if (recipient.isEmpty())
291  {
292  emit error("Doomseeker error: getOrCreateNewChatAdapter() received empty recipient.");
293  return NULL;
294  }
295 
296  QString recipientLowercase = recipient.toLower();
297 
298  if (hasRecipient(recipientLowercase))
299  {
300  return chatWindows[recipientLowercase];
301  }
302 
303 #ifndef NDEBUG
304  Log::instance << QString("IRCNetworkAdapter::getOrCreateNewChatAdapter() Creating new adapter for recipient: %1").arg(recipientLowercase);
305 #endif
306 
307  if (IRCGlobal::isChannelName(recipient))
308  {
309  pAdapter = new IRCChannelAdapter(this, recipient);
310  }
311  else
312  {
313  pAdapter = new IRCPrivAdapter(this, recipient);
314  }
315 
316  chatWindows.insert(recipientLowercase, pAdapter);
317  emit newChatWindowIsOpened(pAdapter);
318 
319  return pAdapter;
320 }
321 
322 bool IRCNetworkAdapter::hasRecipient(const QString& recipient) const
323 {
324  QString recipientLowercase = recipient.toLower();
325  return (chatWindows.find(recipientLowercase) != chatWindows.end());
326 }
327 
328 void IRCNetworkAdapter::helloClient(const QString& nickname)
329 {
330  // This method can only be called
331  // when network is in joining state.
332 
333  connectionInfo.nick = nickname;
334  IRCNetworkEntity& network = connectionInfo.networkEntity;
335 
336  gLog << tr("IRC: Successfuly registered on network %1 [%2:%3]").arg(network.description(), network.address()).arg(network.port());
337 
338  this->bIsJoining = false;
339 
340  if (!network.nickservPassword().isEmpty())
341  {
342  QString messageNickserv = network.nickservCommand();
343  messageNickserv = messageNickserv.arg(network.nickservPassword());
344 
345  this->sendMessage(messageNickserv);
346  }
347  foreach (const QString& command, network.autojoinCommands())
348  {
349  if (!command.trimmed().isEmpty())
350  {
351  this->sendMessage(command);
352  }
353  }
354 
355  foreach (const QString& channel, network.autojoinChannels())
356  {
357  if (IRCGlobal::isChannelName(channel))
358  {
359  this->openNewAdapter(channel);
360  }
361  }
362 
363  // Emit this just to be safe.
364  emit titleChange();
365 }
366 
367 const PatternList &IRCNetworkAdapter::ignoredUsersPatterns() const
368 {
369  return connection().networkEntity.ignoredUsers();
370 }
371 
372 void IRCNetworkAdapter::ircServerResponse(const QString& message)
373 {
374  IRCResponseParseResult result = ircResponseParser->parse(message);
375 
376  if (this->bEmitAllIRCMessages || !result.wasParsed())
377  {
378  emit this->message(message.trimmed().replace("\n", "\\n"));
379  }
380 
381  if (!result.isValid())
382  {
383  emit this->error(tr("Invalid parse result for mesage: %1").arg(message));
384  }
385 }
386 
388 {
389  if (this == pAdapter)
390  {
391  return true;
392  }
393 
394  QList<IRCChatAdapter*> adaptersList = chatWindows.values();
395  foreach (IRCChatAdapter* pChatWindow, adaptersList)
396  {
397  if (pChatWindow == pAdapter)
398  {
399  return true;
400  }
401  }
402 
403  return false;
404 }
405 
406 bool IRCNetworkAdapter::isMyNickname(const QString& nickname) const
407 {
408  IRCUserInfo myUserInfo(this->connectionInfo.nick, this);
409  return (myUserInfo.isSameNickname(nickname));
410 }
411 
412 bool IRCNetworkAdapter::isOperator(const QString& nickname, const QString& channel) const
413 {
414  if (IRCGlobal::isChannelName(channel))
415  {
416  const IRCChannelAdapter* pChannelAdapter = (const IRCChannelAdapter*) this->getChatAdapter(channel);
417  if (pChannelAdapter != NULL)
418  {
419  return pChannelAdapter->isOperator(nickname);
420  }
421  }
422 
423  return false;
424 }
425 
426 void IRCNetworkAdapter::kick(const QString& channel, const QString& byWhom, const QString& whoIsKicked, const QString& reason)
427 {
428  if (hasRecipient(channel))
429  {
430  IRCChannelAdapter* pAdapter = (IRCChannelAdapter*) this->getOrCreateNewChatAdapter(channel);
431 
432  if (isMyNickname(whoIsKicked))
433  {
434  this->emitMessageWithClass(tr("You have been kicked from channel %1 by %2 (Reason: %3)").arg(channel, byWhom, reason), IRCMessageClass::ChannelAction);
435  killChatWindow(channel);
436  }
437  else
438  {
439  pAdapter->emitMessageWithClass(tr("%1 was kicked by %2 (%3)").arg(whoIsKicked, byWhom, reason), IRCMessageClass::ChannelAction);
440  pAdapter->removeNameFromCachedList(whoIsKicked);
441  }
442  }
443 }
444 
445 void IRCNetworkAdapter::kill(const QString& victim, const QString& comment)
446 {
447  emit message(QString("Connection for user %1 was killed. (%2)").arg(victim, comment));
448 
449  // We need to iterate through EVERY adapter and notify them
450  // about this quit.
451  // Implementation of each adapter should recognize if this quit actually
452  // has anything to do with that adapter.
453  QList<IRCChatAdapter*> adaptersList = chatWindows.values();
454  foreach (IRCChatAdapter* pAdapter, adaptersList)
455  {
456  pAdapter->userLeaves(victim, comment, IRCChatAdapter::NetworkKill);
457  }
458 }
459 
460 void IRCNetworkAdapter::killAllChatWindows()
461 {
462  QList<IRCChatAdapter*> pWindows = chatWindows.values();
463  foreach (IRCChatAdapter* pAdapter, pWindows)
464  {
465  // Make sure that the adapter destructor won't call the
466  // detachChatWindow() method or the program will be shot to oblivion.
467  pAdapter->setNetwork(NULL);
468  delete pAdapter;
469  }
470 
471  chatWindows.clear();
472 }
473 
474 void IRCNetworkAdapter::killChatWindow(const QString& recipient)
475 {
476  if (hasRecipient(recipient))
477  {
478  IRCChatAdapter* pAdapter = getChatAdapter(recipient);
479  chatWindows.remove(recipient.toLower());
480 
481  // Make sure that the adapter destructor won't call the
482  // detachChatWindow() method or the program will be shot to oblivion.
483  pAdapter->setNetwork(NULL);
484  delete pAdapter;
485  }
486 }
487 
488 void IRCNetworkAdapter::modeInfo(const QString& channel, const QString& whoSetThis, const QString& modeParams)
489 {
490  IRCChannelAdapter* pAdapter = (IRCChannelAdapter*) this->getOrCreateNewChatAdapter(channel);
491  pAdapter->emitMessageWithClass(tr("%1 sets mode: %2").arg(whoSetThis, modeParams), IRCMessageClass::ChannelAction);
492 }
493 
494 void IRCNetworkAdapter::namesListReceived(const QString& channel, const QStringList& names)
495 {
496  if (this->hasRecipient(channel))
497  {
498  IRCChannelAdapter* pAdapter = (IRCChannelAdapter*) this->getOrCreateNewChatAdapter(channel);
499  pAdapter->appendNamesToCachedList(names);
500  }
501 }
502 
503 void IRCNetworkAdapter::namesListEndReceived(const QString& channel)
504 {
505  if (this->hasRecipient(channel))
506  {
507  IRCChannelAdapter* pAdapter = (IRCChannelAdapter*) this->getOrCreateNewChatAdapter(channel);
508  pAdapter->emitCachedNameListUpdated();
509  }
510 }
511 
512 void IRCNetworkAdapter::nicknameInUse(const QString& nickname)
513 {
514  emit messageToNetworksCurrentChatBox(tr("Nickname %1 is already taken.").arg(nickname), IRCMessageClass::Error);
515  if (this->bIsJoining)
516  {
517  const QString& altNick = this->connectionInfo.alternateNick;
518 
519  if (this->connectionInfo.nick.compare(altNick, Qt::CaseInsensitive) == 0)
520  {
521  emit messageWithClass(tr("Both nickname and alternate nickname are taken on this network."), IRCMessageClass::Error);
522  }
523  else if (altNick.isEmpty())
524  {
525  emit messageWithClass(tr("No alternate nickname specified."), IRCMessageClass::Error);
526  }
527  else
528  {
529  this->connectionInfo.nick = altNick;
530 
531  emit messageWithClass(tr("Using alternate nickname %1 to join.").arg(altNick), IRCMessageClass::NetworkAction);
532  QString message = QString("/nick %1").arg(altNick);
533  sendMessage(message);
534  }
535  }
536 }
537 
538 void IRCNetworkAdapter::noSuchNickname(const QString& nickname)
539 {
540  emit messageToNetworksCurrentChatBox(tr("User %1 is not logged in.").arg(nickname), IRCMessageClass::Error);
541 }
542 
543 void IRCNetworkAdapter::openNewAdapter(const QString& recipientName)
544 {
545  if (!isConnected() || recipientName.isEmpty())
546  {
547  return;
548  }
549 
550  bool bStandardRoutine = !IRCGlobal::isChannelName(recipientName)
551  || hasRecipient(recipientName);
552 
553  if (bStandardRoutine)
554  {
555  IRCChatAdapter* pAdapter = this->getOrCreateNewChatAdapter(recipientName);
556  pAdapter->emitFocusRequest();
557  }
558  else if (IRCGlobal::isChannelName(recipientName))
559  {
560  this->sendMessage("/join " + recipientName);
561  }
562 }
563 
564 void IRCNetworkAdapter::parseError(const QString& error)
565 {
566  emit this->error(tr("IRC parse error: %1").arg(error));
567 }
568 
569 void IRCNetworkAdapter::print(const QString& printWhat, const QString& printWhere)
570 {
571  printWithClass(printWhat, printWhere, IRCMessageClass(IRCMessageClass::Normal));
572 }
573 
574 void IRCNetworkAdapter::printWithClass(const QString& printWhat,
575  const QString& printWhere, const IRCMessageClass& msgClass)
576 {
577  IRCAdapterBase* pAdapter = this;
578 
579  if (!printWhere.isEmpty())
580  {
581  IRCAdapterBase* pAdapterCandidate = getChatAdapter(printWhere);
582  if (pAdapterCandidate != NULL)
583  {
584  pAdapter = pAdapterCandidate;
585  }
586  }
587 
588  // In case if the target adapter is unknown, the message will still get
589  // printed to this adapter.
590  if (pAdapter == NULL)
591  {
592  this->emitMessageWithClass(tr("FROM %1: %2").arg(printWhere, printWhat), msgClass);
593  }
594  else
595  {
596  // If bEmitAllIRCMessages is set to true, the message will be already
597  // printed for this adapter in ircServerResponse().
598  // There is no need to print it again.
599  if ( pAdapter == this && this->bEmitAllIRCMessages )
600  {
601  return;
602  }
603 
604  pAdapter->emitMessageWithClass(printWhat, msgClass);
605  }
606 }
607 
608 void IRCNetworkAdapter::printToCurrentChatBox(const QString& printWhat, const IRCMessageClass& msgClass)
609 {
610  emit messageToNetworksCurrentChatBox(printWhat, msgClass);
611 }
612 
613 void IRCNetworkAdapter::privMsgReceived(const QString& recipient, const QString& sender, const QString& content)
614 {
615  IRCChatAdapter* pAdapter = this->getOrCreateNewChatAdapter(recipient);
616  pAdapter->emitChatMessage(sender, content);
617 }
618 
619 void IRCNetworkAdapter::printMsgLiteral(const QString& recipient, const QString& content,
620  const IRCMessageClass& msgClass)
621 {
622  this->getOrCreateNewChatAdapter(recipient);
623  printWithClass(content, recipient, msgClass);
624 }
625 
626 void IRCNetworkAdapter::reloadNetworkEntityFromConfig()
627 {
628  ChatNetworksCfg cfg;
629  IRCNetworkEntity entity = cfg.network(connectionInfo.networkEntity.description());
630  if (entity.isValid())
631  {
632  setNetworkEntity(entity);
633  }
634 }
635 
636 void IRCNetworkAdapter::setNetworkEntity(const IRCNetworkEntity &entity)
637 {
638  IRCNetworkEntity oldEntity = connectionInfo.networkEntity;
639  connectionInfo.networkEntity = entity;
640  if (oldEntity.description() != entity.description())
641  {
642  emit titleChange();
643  }
644 }
645 
646 void IRCNetworkAdapter::sendCtcp(const QString &nickname, const QString &command)
647 {
648  QString msg = QString("/PRIVMSG %1 %2%3%2").arg(nickname).arg(QChar(0x1)).arg(command);
649  sendMessage(msg);
650 }
651 
652 void IRCNetworkAdapter::sendPong(const QString& toWhom)
653 {
654  QString message = QString("/PONG %1").arg(toWhom);
655  sendMessage(message);
656 }
657 
658 void IRCNetworkAdapter::setChannelMode(const QString& channel, const QString& nickname, const QString& flag, bool bSet)
659 {
660  QString cleanNickname = userPrefixes().cleanNickname(nickname);
661 
662  QString flagPrefixed;
663  if (bSet)
664  {
665  flagPrefixed = "+" + flag.trimmed();
666  }
667  else
668  {
669  flagPrefixed = "-" + flag.trimmed();
670  }
671 
672  QString message = QString("/mode %1 %2 %3").arg(channel, flagPrefixed, cleanNickname);
673  this->sendMessage(message);
674 }
675 
677 {
678  return connectionInfo.networkEntity.description() + " ( " + myNickname() + " )";
679 }
680 
681 void IRCNetworkAdapter::userChangesNickname(const QString& oldNickname, const QString& newNickname)
682 {
683  if (isMyNickname(oldNickname))
684  {
685  emit messageToNetworksCurrentChatBox(tr("Updated own nickname to %1.").arg(newNickname),
686  IRCMessageClass::NetworkAction);
687  connectionInfo.nick = newNickname;
688 
689  emit titleChange();
690  }
691 
692  QList<IRCChatAdapter*> adaptersList = chatWindows.values();
693  foreach (IRCChatAdapter* pAdapter, adaptersList)
694  {
695  pAdapter->userChangesNickname(oldNickname, newNickname);
696  }
697 
698  // MAKE SURE TO SEE IF WE HAVE A CHAT WINDOW OPEN WITH THIS
699  // USER AND FIX THE KEY IN THE CHAT WINDOW MAP FOR THIS NETWORK!!!
700  if (hasRecipient(oldNickname))
701  {
702  QString oldNicknameLowercase = oldNickname.toLower();
703  QString newNicknameLowercase = newNickname.toLower();
704 
705  IRCChatAdapter* pAdapter = getChatAdapter(oldNickname);
706  chatWindows.remove(oldNicknameLowercase);
707  chatWindows.insert(newNicknameLowercase, pAdapter);
708  }
709 }
710 
711 void IRCNetworkAdapter::userJoinsChannel(const QString& channel, const QString& nickname, const QString& fullSignature)
712 {
713  if (!isMyNickname(nickname))
714  {
715  if (hasRecipient(channel))
716  {
717  IRCChannelAdapter* pChannel = (IRCChannelAdapter*)this->getOrCreateNewChatAdapter(channel);
718  pChannel->userJoins(nickname, fullSignature);
719  }
720  }
721  else
722  {
723  IRCChannelAdapter* pChannel = (IRCChannelAdapter*)this->getOrCreateNewChatAdapter(channel);
724  pChannel->emitFocusRequest();
725  }
726 }
727 
728 void IRCNetworkAdapter::userIdleTime(const QString &nick, int secondsIdle)
729 {
730  QString msg = tr("Last activity of user %1 was %2 ago.").arg(
731  nick, Strings::formatTime(secondsIdle));
732  emit messageToNetworksCurrentChatBox(msg, IRCMessageClass::NetworkAction);
733 }
734 
735 void IRCNetworkAdapter::userModeChanged(const QString& channel, const QString& nickname,
736  const QList<char> &addedFlags, const QList<char> &removedFlags)
737 {
738  if (hasRecipient(channel))
739  {
740  IRCChatAdapter* pAdapter = this->getOrCreateNewChatAdapter(channel);
741  pAdapter->userModeChanges(nickname, addedFlags, removedFlags);
742  }
743 }
744 
745 void IRCNetworkAdapter::userNetworkJoinDateTime(const QString &nick, const QDateTime &timeOfJoin)
746 {
747  QString msg = tr("%1 joined the network on %2").arg(nick,
748  timeOfJoin.toString("yyyy-MM-dd HH:mm:ss"));
749  emit messageToNetworksCurrentChatBox(msg, IRCMessageClass::NetworkAction);
750 }
751 
752 void IRCNetworkAdapter::userPartsChannel(const QString& channel, const QString& nickname, const QString& farewellMessage)
753 {
754  if (hasRecipient(channel))
755  {
756  IRCChannelAdapter* pChannel = (IRCChannelAdapter*)getChatAdapter(channel);
757 
758  if (isMyNickname(nickname))
759  {
760  emit messageWithClass(tr("You left channel %1.").arg(channel), IRCMessageClass::ChannelAction);
761  killChatWindow(channel);
762  }
763  else
764  {
765  pChannel->userLeaves(nickname, farewellMessage, IRCChatAdapter::ChannelPart);
766  }
767  }
768 }
769 
771 {
772  return ircISupportParser->userPrefixes();
773 }
774 
775 void IRCNetworkAdapter::userQuitsNetwork(const QString& nickname, const QString& farewellMessage)
776 {
777  // We need to iterate through EVERY adapter and notify them
778  // about this quit.
779  // Implementation of each adapter should recognize if this quit actually
780  // has anything to do with that adapter.
781  QList<IRCChatAdapter*> adaptersList = chatWindows.values();
782  foreach (IRCChatAdapter* pAdapter, adaptersList)
783  {
784  pAdapter->userLeaves(nickname, farewellMessage, IRCChatAdapter::NetworkQuit);
785  }
786 }
787 
788 void IRCNetworkAdapter::userPing(const QString &nickname, qint64 ping)
789 {
790  qint64 delta = QDateTime::currentMSecsSinceEpoch() - ping;
791  emit messageToNetworksCurrentChatBox(
792  tr("Ping to user %1: %2ms").arg(nickname).arg(delta),
793  IRCMessageClass::Ctcp);
794 }
795 
796 void IRCNetworkAdapter::whoIsUser(const QString& nickname, const QString& user, const QString& hostName, const QString& realName)
797 {
798  emit messageToNetworksCurrentChatBox(
799  QString("%1 %2 %3 %4").arg(nickname, user, hostName, realName),
800  IRCMessageClass::NetworkAction);
801 }
802 
804 
805 void IRCSocketSignalsAdapter::connected()
806 {
807  pParent->bIsJoining = true;
808  pParent->emitMessageWithClass(tr("Connected. Sending registration messages."), IRCMessageClass::NetworkAction);
809 
810  IRCNetworkConnectionInfo& connectionInfo = pParent->connectionInfo;
811 
812  QString messagePass = "/PASS %1";
813  QString messageNick = "/NICK %1";
814  QString messageUser = "/USER %1 %2 %3 :%4";
815 
816  IRCNetworkEntity& network = connectionInfo.networkEntity;
817 
818  if (!network.password().isEmpty())
819  {
820  pParent->sendMessage(messagePass.arg(network.password()));
821  }
822 
823  pParent->sendMessage(messageNick.arg(connectionInfo.nick));
824  pParent->sendMessage(messageUser.arg(connectionInfo.nick).arg(connectionInfo.nick).arg(connectionInfo.nick).arg(connectionInfo.realName));
825 }
826 
827 void IRCSocketSignalsAdapter::disconnected()
828 {
829  pParent->killAllChatWindows();
830  gLog << tr("IRC: Disconnected from network %1").arg(pParent->connectionInfo.networkEntity.description());
831  emit pParent->message("Disconnected");
832 }
833 
834 void IRCSocketSignalsAdapter::errorReceived(QAbstractSocket::SocketError error)
835 {
836  emit pParent->error(pSocket->errorString());
837 }
838 
839 void IRCSocketSignalsAdapter::infoMessage(const QString& message)
840 {
841  gLog << message;
842  emit pParent->message(message);
843 }
844 
845 void IRCSocketSignalsAdapter::hostLookupError(QHostInfo::HostInfoError errorValue)
846 {
847  switch (errorValue)
848  {
849  case QHostInfo::HostNotFound:
850  emit pParent->error("Host lookup error: host not found.");
851  break;
852 
853  default:
854  emit pParent->error("Unknown host lookup error.");
855  break;
856  }
857 }
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:284
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's connection to the IRC server.
virtual void emitChatMessage(const QString &sender, const QString &content)
Emits message() signal formatting it to present sender'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 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'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 ' nick ' 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.