ircnetworkadapter.cpp
1 //------------------------------------------------------------------------------
2 // ircnetworkadapter.cpp
3 //------------------------------------------------------------------------------
4 //
5 // This library is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU Lesser General Public
7 // License as published by the Free Software Foundation; either
8 // version 2.1 of the License, or (at your option) any later version.
9 //
10 // This library 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 GNU
13 // Lesser General Public License for more details.
14 //
15 // You should have received a copy of the GNU Lesser General Public
16 // License along with this library; 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 "irc/configuration/chatnetworkscfg.h"
24 #include "irc/configuration/ircconfig.h"
25 #include "irc/entities/ircuserprefix.h"
26 #include "irc/ircchanneladapter.h"
27 #include "irc/ircglobal.h"
28 #include "irc/ircisupportparser.h"
29 #include "irc/ircmessageclass.h"
30 #include "irc/ircprivadapter.h"
31 #include "irc/ircrequestparser.h"
32 #include "irc/ircresponseparser.h"
33 #include "irc/ircuserinfo.h"
34 #include "irc/ircuserlist.h"
35 #include "irc/ops/ircdelayedoperationban.h"
36 #include "ircnetworkadapter.h"
37 #include "log.h"
38 #include <QDateTime>
39 
40 IRCNetworkAdapter::IRCNetworkAdapter(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  auto 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  for (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 == nullptr)
192  pOrigin = this;
193 
194  if (!ircClient.isConnected())
195  {
196  pOrigin->emitError(tr("You are not connected to the network."));
197  return;
198  }
199 
200  IRCRequestParser::IRCRequestParseResult result = ircRequestParser.parse(pOrigin, message);
201  QString parsedMessage = ircRequestParser.output();
202 
203  switch (result)
204  {
205  case IRCRequestParser::ErrorInputInsufficientParameters:
206  pOrigin->emitError(tr("Insufficient parameters."));
207  break;
208 
209  case IRCRequestParser::ErrorInputNotPrependedWithSlash:
210  emit error(tr("This is a server window. All commands must be prepended with a '/' character."));
211  break;
212 
213  case IRCRequestParser::ErrorMessageEmpty:
214  pOrigin->emitError(tr("Attempted to send empty message."));
215  break;
216 
217  case IRCRequestParser::ErrorMessageTooLong:
218  pOrigin->emitError(tr("Command is too long."));
219  break;
220 
221  case IRCRequestParser::ErrorChatWindowOnly:
222  pOrigin->emitError(tr("Not a chat window."));
223  break;
224 
225  case IRCRequestParser::Ok:
226  ircClient.sendMessage(parsedMessage);
227  break;
228 
229  case IRCRequestParser::QuitCommand:
230  ircClient.sendMessage(parsedMessage);
231  emit messageWithClass(tr("Quit"), IRCMessageClass::NetworkAction);
232  break;
233  }
234 }
235 
236 void IRCNetworkAdapter::echoPrivmsg(const QString &recipient, const QString &content)
237 {
238  // We will echo only chat messages for recipients for whom
239  // we have windows open.
240  if (hasRecipient(recipient))
241  {
242  const QString &sender = this->myNickname();
243  privMsgReceived(recipient, sender, content);
244  }
245 }
246 
247 IRCChatAdapter *IRCNetworkAdapter::getChatAdapter(const QString &recipient)
248 {
249  if (recipient.isEmpty())
250  {
251  emit error("Doomseeker error: getChatAdapter() received empty recipient.");
252  return nullptr;
253  }
254 
255  QString recipientLowercase = recipient.toLower();
256  if (hasRecipient(recipientLowercase))
257  return chatWindows[recipientLowercase];
258 
259  return nullptr;
260 }
261 
262 const IRCChatAdapter *IRCNetworkAdapter::getChatAdapter(const QString &recipient) const
263 {
264  if (recipient.isEmpty())
265  return nullptr;
266 
267  QString recipientLowercase = recipient.toLower();
268  if (hasRecipient(recipientLowercase))
269  return chatWindows[recipientLowercase];
270 
271  return nullptr;
272 }
273 
274 IRCChatAdapter *IRCNetworkAdapter::getOrCreateNewChatAdapter(const QString &recipient)
275 {
276  IRCChatAdapter *pAdapter = nullptr;
277 
278  if (recipient.isEmpty())
279  {
280  emit error("Doomseeker error: getOrCreateNewChatAdapter() received empty recipient.");
281  return nullptr;
282  }
283 
284  QString recipientLowercase = recipient.toLower();
285 
286  if (hasRecipient(recipientLowercase))
287  return chatWindows[recipientLowercase];
288 
289  #ifndef NDEBUG
290  Log::instance << QString("IRCNetworkAdapter::getOrCreateNewChatAdapter() Creating new adapter for recipient: %1").arg(recipientLowercase);
291  #endif
292 
293  if (IRCGlobal::isChannelName(recipient))
294  pAdapter = new IRCChannelAdapter(this, recipient);
295  else
296  pAdapter = new IRCPrivAdapter(this, recipient);
297 
298  chatWindows.insert(recipientLowercase, pAdapter);
299  emit newChatWindowIsOpened(pAdapter);
300 
301  return pAdapter;
302 }
303 
304 bool IRCNetworkAdapter::hasRecipient(const QString &recipient) const
305 {
306  QString recipientLowercase = recipient.toLower();
307  return chatWindows.find(recipientLowercase) != chatWindows.end();
308 }
309 
310 void IRCNetworkAdapter::helloClient(const QString &nickname)
311 {
312  // This method can only be called
313  // when network is in joining state.
314 
315  connectionInfo.nick = nickname;
316  IRCNetworkEntity &network = connectionInfo.networkEntity;
317 
318  gLog << tr("IRC: Successfully registered on network %1 [%2:%3]").arg(network.description(), network.address()).arg(network.port());
319 
320  this->bIsJoining = false;
321 
322  if (!network.nickservPassword().isEmpty())
323  {
324  QString messageNickserv = network.nickservCommand();
325  messageNickserv = messageNickserv.arg(network.nickservPassword());
326 
327  this->sendMessage(messageNickserv);
328  }
329  for (const QString &command : network.autojoinCommands())
330  {
331  if (!command.trimmed().isEmpty())
332  this->sendMessage(command);
333  }
334 
335  for (const QString &channel : network.autojoinChannels())
336  {
337  if (IRCGlobal::isChannelName(channel))
338  this->openNewAdapter(channel);
339  }
340 
341  // Emit this just to be safe.
342  emit titleChange();
343 }
344 
345 const PatternList &IRCNetworkAdapter::ignoredUsersPatterns() const
346 {
347  return connection().networkEntity.ignoredUsers();
348 }
349 
350 void IRCNetworkAdapter::ircServerResponse(const QString &message)
351 {
352  IRCResponseParseResult result = ircResponseParser->parse(message);
353 
354  if (this->bEmitAllIRCMessages || !result.wasParsed())
355  emit this->message(message.trimmed().replace("\n", "\\n"));
356 
357  if (!result.isValid())
358  emit this->error(tr("Invalid parse result for message: %1").arg(message));
359 }
360 
362 {
363  if (this == pAdapter)
364  return true;
365 
366  QList<IRCChatAdapter *> adaptersList = chatWindows.values();
367  for (IRCChatAdapter *pChatWindow : adaptersList)
368  {
369  if (pChatWindow == pAdapter)
370  return true;
371  }
372 
373  return false;
374 }
375 
376 bool IRCNetworkAdapter::isMyNickname(const QString &nickname) const
377 {
378  IRCUserInfo myUserInfo(this->connectionInfo.nick, this);
379  return myUserInfo.isSameNickname(nickname);
380 }
381 
382 bool IRCNetworkAdapter::isOperator(const QString &nickname, const QString &channel) const
383 {
384  if (IRCGlobal::isChannelName(channel))
385  {
386  const auto pChannelAdapter = (const IRCChannelAdapter *) this->getChatAdapter(channel);
387  if (pChannelAdapter != nullptr)
388  return pChannelAdapter->isOperator(nickname);
389  }
390 
391  return false;
392 }
393 
394 void IRCNetworkAdapter::kick(const QString &channel, const QString &byWhom, const QString &whoIsKicked, const QString &reason)
395 {
396  if (hasRecipient(channel))
397  {
398  auto pAdapter = (IRCChannelAdapter *) this->getOrCreateNewChatAdapter(channel);
399 
400  if (isMyNickname(whoIsKicked))
401  {
402  this->emitMessageWithClass(tr("You have been kicked from channel %1 by %2 (Reason: %3)").arg(channel, byWhom, reason), IRCMessageClass::ChannelAction);
403  killChatWindow(channel);
404  }
405  else
406  {
407  pAdapter->emitMessageWithClass(tr("%1 was kicked by %2 (%3)").arg(whoIsKicked, byWhom, reason), IRCMessageClass::ChannelAction);
408  pAdapter->removeNameFromCachedList(whoIsKicked);
409  }
410  }
411 }
412 
413 void IRCNetworkAdapter::kill(const QString &victim, const QString &comment)
414 {
415  emit message(QString("Connection for user %1 was killed. (%2)").arg(victim, comment));
416 
417  // We need to iterate through EVERY adapter and notify them
418  // about this quit.
419  // Implementation of each adapter should recognize if this quit actually
420  // has anything to do with that adapter.
421  QList<IRCChatAdapter *> adaptersList = chatWindows.values();
422  for (IRCChatAdapter *pAdapter : adaptersList)
423  {
424  pAdapter->userLeaves(victim, comment, IRCChatAdapter::NetworkKill);
425  }
426 }
427 
428 void IRCNetworkAdapter::killAllChatWindows()
429 {
430  QList<IRCChatAdapter *> pWindows = chatWindows.values();
431  for (IRCChatAdapter *pAdapter : pWindows)
432  {
433  // Make sure that the adapter destructor won't call the
434  // detachChatWindow() method or the program will be shot to oblivion.
435  pAdapter->setNetwork(nullptr);
436  delete pAdapter;
437  }
438 
439  chatWindows.clear();
440 }
441 
442 void IRCNetworkAdapter::killChatWindow(const QString &recipient)
443 {
444  if (hasRecipient(recipient))
445  {
446  IRCChatAdapter *pAdapter = getChatAdapter(recipient);
447  chatWindows.remove(recipient.toLower());
448 
449  // Make sure that the adapter destructor won't call the
450  // detachChatWindow() method or the program will be shot to oblivion.
451  pAdapter->setNetwork(nullptr);
452  delete pAdapter;
453  }
454 }
455 
456 void IRCNetworkAdapter::modeInfo(const QString &channel, const QString &whoSetThis, const QString &modeParams)
457 {
458  auto pAdapter = (IRCChannelAdapter *) this->getOrCreateNewChatAdapter(channel);
459  pAdapter->emitMessageWithClass(tr("%1 sets mode: %2").arg(whoSetThis, modeParams), IRCMessageClass::ChannelAction);
460 }
461 
462 void IRCNetworkAdapter::namesListReceived(const QString &channel, const QStringList &names)
463 {
464  if (this->hasRecipient(channel))
465  {
466  auto pAdapter = (IRCChannelAdapter *) this->getOrCreateNewChatAdapter(channel);
467  pAdapter->appendNamesToCachedList(names);
468  }
469 }
470 
471 void IRCNetworkAdapter::namesListEndReceived(const QString &channel)
472 {
473  if (this->hasRecipient(channel))
474  {
475  auto pAdapter = (IRCChannelAdapter *) this->getOrCreateNewChatAdapter(channel);
476  pAdapter->emitCachedNameListUpdated();
477  }
478 }
479 
480 void IRCNetworkAdapter::nicknameInUse(const QString &nickname)
481 {
482  emit messageToNetworksCurrentChatBox(tr("Nickname %1 is already taken.").arg(nickname), IRCMessageClass::Error);
483  if (this->bIsJoining)
484  {
485  const QString &altNick = this->connectionInfo.alternateNick;
486 
487  if (this->connectionInfo.nick.compare(altNick, Qt::CaseInsensitive) == 0)
488  emit messageWithClass(tr("Both nickname and alternate nickname are taken on this network."), IRCMessageClass::Error);
489  else if (altNick.isEmpty())
490  emit messageWithClass(tr("No alternate nickname specified."), IRCMessageClass::Error);
491  else
492  {
493  this->connectionInfo.nick = altNick;
494 
495  emit messageWithClass(tr("Using alternate nickname %1 to join.").arg(altNick), IRCMessageClass::NetworkAction);
496  QString message = QString("/nick %1").arg(altNick);
497  sendMessage(message);
498  }
499  }
500 }
501 
502 void IRCNetworkAdapter::noSuchNickname(const QString &nickname)
503 {
504  emit messageToNetworksCurrentChatBox(tr("User %1 is not logged in.").arg(nickname), IRCMessageClass::Error);
505 }
506 
507 void IRCNetworkAdapter::openNewAdapter(const QString &recipientName)
508 {
509  if (!isConnected() || recipientName.isEmpty())
510  return;
511 
512  bool bStandardRoutine = !IRCGlobal::isChannelName(recipientName)
513  || hasRecipient(recipientName);
514 
515  if (bStandardRoutine)
516  {
517  IRCChatAdapter *pAdapter = this->getOrCreateNewChatAdapter(recipientName);
518  pAdapter->emitFocusRequest();
519  }
520  else if (IRCGlobal::isChannelName(recipientName))
521  this->sendMessage("/join " + recipientName);
522 }
523 
524 void IRCNetworkAdapter::parseError(const QString &error)
525 {
526  emit this->error(tr("IRC parse error: %1").arg(error));
527 }
528 
529 void IRCNetworkAdapter::print(const QString &printWhat, const QString &printWhere)
530 {
531  printWithClass(printWhat, printWhere, IRCMessageClass(IRCMessageClass::Normal));
532 }
533 
534 void IRCNetworkAdapter::printWithClass(const QString &printWhat,
535  const QString &printWhere, const IRCMessageClass &msgClass)
536 {
537  IRCAdapterBase *pAdapter = this;
538 
539  if (!printWhere.isEmpty())
540  {
541  IRCAdapterBase *pAdapterCandidate = getChatAdapter(printWhere);
542  if (pAdapterCandidate != nullptr)
543  pAdapter = pAdapterCandidate;
544  }
545 
546  // In case if the target adapter is unknown, the message will still get
547  // printed to this adapter.
548  if (pAdapter == nullptr)
549  this->emitMessageWithClass(tr("FROM %1: %2").arg(printWhere, printWhat), msgClass);
550  else
551  {
552  // If bEmitAllIRCMessages is set to true, the message will be already
553  // printed for this adapter in ircServerResponse().
554  // There is no need to print it again.
555  if ( pAdapter == this && this->bEmitAllIRCMessages )
556  return;
557 
558  pAdapter->emitMessageWithClass(printWhat, msgClass);
559  }
560 }
561 
562 void IRCNetworkAdapter::printToCurrentChatBox(const QString &printWhat, const IRCMessageClass &msgClass)
563 {
564  emit messageToNetworksCurrentChatBox(printWhat, msgClass);
565 }
566 
567 void IRCNetworkAdapter::privMsgReceived(const QString &recipient, const QString &sender, const QString &content)
568 {
569  IRCChatAdapter *pAdapter = this->getOrCreateNewChatAdapter(recipient);
570  pAdapter->emitChatMessage(sender, content);
571 }
572 
573 void IRCNetworkAdapter::printMsgLiteral(const QString &recipient, const QString &content,
574  const IRCMessageClass &msgClass)
575 {
576  this->getOrCreateNewChatAdapter(recipient);
577  printWithClass(content, recipient, msgClass);
578 }
579 
580 void IRCNetworkAdapter::reloadNetworkEntityFromConfig()
581 {
582  ChatNetworksCfg cfg;
583  IRCNetworkEntity entity = cfg.network(connectionInfo.networkEntity.description());
584  if (entity.isValid())
585  setNetworkEntity(entity);
586 }
587 
588 void IRCNetworkAdapter::setNetworkEntity(const IRCNetworkEntity &entity)
589 {
590  IRCNetworkEntity oldEntity = connectionInfo.networkEntity;
591  connectionInfo.networkEntity = entity;
592  if (oldEntity.description() != entity.description())
593  emit titleChange();
594 }
595 
596 void IRCNetworkAdapter::sendCtcp(const QString &nickname, const QString &command)
597 {
598  QString msg = QString("/PRIVMSG %1 %2%3%2").arg(nickname).arg(QChar(0x1)).arg(command);
599  sendMessage(msg);
600 }
601 
602 void IRCNetworkAdapter::sendPong(const QString &toWhom)
603 {
604  QString message = QString("/PONG %1").arg(toWhom);
605  sendMessage(message);
606 }
607 
608 void IRCNetworkAdapter::setChannelMode(const QString &channel, const QString &nickname, const QString &flag, bool bSet)
609 {
610  QString cleanNickname = userPrefixes().cleanNickname(nickname);
611 
612  QString flagPrefixed;
613  if (bSet)
614  flagPrefixed = "+" + flag.trimmed();
615  else
616  flagPrefixed = "-" + flag.trimmed();
617 
618  QString message = QString("/mode %1 %2 %3").arg(channel, flagPrefixed, cleanNickname);
619  this->sendMessage(message);
620 }
621 
623 {
624  return connectionInfo.networkEntity.description() + " ( " + myNickname() + " )";
625 }
626 
627 void IRCNetworkAdapter::userChangesNickname(const QString &oldNickname, const QString &newNickname)
628 {
629  if (isMyNickname(oldNickname))
630  {
631  emit messageToNetworksCurrentChatBox(tr("Updated own nickname to %1.").arg(newNickname),
632  IRCMessageClass::NetworkAction);
633  connectionInfo.nick = newNickname;
634 
635  emit titleChange();
636  }
637 
638  QList<IRCChatAdapter *> adaptersList = chatWindows.values();
639  for (IRCChatAdapter *pAdapter : adaptersList)
640  {
641  pAdapter->userChangesNickname(oldNickname, newNickname);
642  }
643 
644  // MAKE SURE TO SEE IF WE HAVE A CHAT WINDOW OPEN WITH THIS
645  // USER AND FIX THE KEY IN THE CHAT WINDOW MAP FOR THIS NETWORK!!!
646  if (hasRecipient(oldNickname))
647  {
648  QString oldNicknameLowercase = oldNickname.toLower();
649  QString newNicknameLowercase = newNickname.toLower();
650 
651  IRCChatAdapter *pAdapter = getChatAdapter(oldNickname);
652  chatWindows.remove(oldNicknameLowercase);
653  chatWindows.insert(newNicknameLowercase, pAdapter);
654  }
655 }
656 
657 void IRCNetworkAdapter::userJoinsChannel(const QString &channel, const QString &nickname, const QString &fullSignature)
658 {
659  if (!isMyNickname(nickname))
660  {
661  if (hasRecipient(channel))
662  {
663  auto pChannel = (IRCChannelAdapter *)this->getOrCreateNewChatAdapter(channel);
664  pChannel->userJoins(nickname, fullSignature);
665  }
666  }
667  else
668  {
669  auto pChannel = (IRCChannelAdapter *)this->getOrCreateNewChatAdapter(channel);
670  pChannel->emitFocusRequest();
671  }
672 }
673 
674 void IRCNetworkAdapter::userIdleTime(const QString &nick, int secondsIdle)
675 {
676  QString msg = tr("Last activity of user %1 was %2 ago.").arg(
677  nick, Strings::formatTime(secondsIdle));
678  emit messageToNetworksCurrentChatBox(msg, IRCMessageClass::NetworkAction);
679 }
680 
681 void IRCNetworkAdapter::userModeChanged(const QString &channel, const QString &nickname,
682  const QList<char> &addedFlags, const QList<char> &removedFlags)
683 {
684  if (hasRecipient(channel))
685  {
686  IRCChatAdapter *pAdapter = this->getOrCreateNewChatAdapter(channel);
687  pAdapter->userModeChanges(nickname, addedFlags, removedFlags);
688  }
689 }
690 
691 void IRCNetworkAdapter::userNetworkJoinDateTime(const QString &nick, const QDateTime &timeOfJoin)
692 {
693  QString msg = tr("%1 joined the network on %2").arg(nick,
694  timeOfJoin.toString("yyyy-MM-dd HH:mm:ss"));
695  emit messageToNetworksCurrentChatBox(msg, IRCMessageClass::NetworkAction);
696 }
697 
698 void IRCNetworkAdapter::userPartsChannel(const QString &channel, const QString &nickname, const QString &farewellMessage)
699 {
700  if (hasRecipient(channel))
701  {
702  auto pChannel = (IRCChannelAdapter *)getChatAdapter(channel);
703 
704  if (isMyNickname(nickname))
705  {
706  emit messageWithClass(tr("You left channel %1.").arg(channel), IRCMessageClass::ChannelAction);
707  killChatWindow(channel);
708  }
709  else
710  pChannel->userLeaves(nickname, farewellMessage, IRCChatAdapter::ChannelPart);
711  }
712 }
713 
715 {
716  return ircISupportParser->userPrefixes();
717 }
718 
719 void IRCNetworkAdapter::userQuitsNetwork(const QString &nickname, const QString &farewellMessage)
720 {
721  // We need to iterate through EVERY adapter and notify them
722  // about this quit.
723  // Implementation of each adapter should recognize if this quit actually
724  // has anything to do with that adapter.
725  QList<IRCChatAdapter *> adaptersList = chatWindows.values();
726  for (IRCChatAdapter *pAdapter : adaptersList)
727  {
728  pAdapter->userLeaves(nickname, farewellMessage, IRCChatAdapter::NetworkQuit);
729  }
730 }
731 
732 void IRCNetworkAdapter::userPing(const QString &nickname, qint64 ping)
733 {
734  qint64 delta = QDateTime::currentMSecsSinceEpoch() - ping;
735  emit messageToNetworksCurrentChatBox(
736  tr("Ping to user %1: %2ms").arg(nickname).arg(delta),
737  IRCMessageClass::Ctcp);
738 }
739 
740 void IRCNetworkAdapter::whoIsUser(const QString &nickname, const QString &user, const QString &hostName, const QString &realName)
741 {
742  emit messageToNetworksCurrentChatBox(
743  QString("%1 %2 %3 %4").arg(nickname, user, hostName, realName),
744  IRCMessageClass::NetworkAction);
745 }
746 
748 
749 void IRCSocketSignalsAdapter::connected()
750 {
751  pParent->bIsJoining = true;
752  pParent->emitMessageWithClass(tr("Connected. Sending registration messages."), IRCMessageClass::NetworkAction);
753 
754  IRCNetworkConnectionInfo &connectionInfo = pParent->connectionInfo;
755 
756  QString messagePass = "/PASS %1";
757  QString messageNick = "/NICK %1";
758  QString messageUser = "/USER %1 0 * :%2"; // RFC 2812
759 
760  IRCNetworkEntity &network = connectionInfo.networkEntity;
761 
762  if (!network.password().isEmpty())
763  pParent->sendMessage(messagePass.arg(network.password()));
764 
765  pParent->sendMessage(messageNick.arg(connectionInfo.nick));
766  pParent->sendMessage(messageUser.arg(connectionInfo.userName).arg(connectionInfo.realName));
767 }
768 
769 void IRCSocketSignalsAdapter::disconnected()
770 {
771  pParent->killAllChatWindows();
772  gLog << tr("IRC: Disconnected from network %1").arg(pParent->connectionInfo.networkEntity.description());
773  emit pParent->message("Disconnected");
774 }
775 
776 void IRCSocketSignalsAdapter::errorReceived(QAbstractSocket::SocketError error)
777 {
778  Q_UNUSED(error);
779  emit pParent->error(pSocket->errorString());
780 }
781 
782 void IRCSocketSignalsAdapter::infoMessage(const QString &message)
783 {
784  gLog << message;
785  emit pParent->message(message);
786 }