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 }
void setNetwork(IRCNetworkAdapter *pNetwork)
Sets IRCNetworkAdapter for this chat window. This adapter is not detached from the old network...
Class type that is used for conversations within a channel.
QString userName
User name sent in /user command.
QString alternateNick
Alternate nickname in case if &#39; nick &#39; is taken when connecting.
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:45
static QString formatTime(float seconds)
Formats a numerical time value into a string.
Definition: strings.cpp:286
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.
const QStringList & autojoinChannels() const
List of channels to which a /join command will be issued automatically when a connection with this ne...
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).
QString nick
Original nickname. This variable will always store the current nickname of the client.
Struct containing information about client&#39;s connection to the IRC server.
Handles chatting through IRC.
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.
IRCNetworkAdapter * network() override
The idea of the adapter system is that each adapter is either a network or is a child of a network...
void doSendMessage(const QString &message, IRCAdapterBase *pOrigin) override
Implemented to support direct communication between client and server.
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 ...
QString title() const override
Gets title for this adapter.
bool isSameNickname(const IRCUserInfo &otherUser) const
Check if this user and user specified as parameter are the same user.
Definition: ircuserinfo.cpp:77
bool isOperator(const QString &nickname, const QString &channel) const
Checks if user is an operator on a given channel.
QString realName
User&#39;s real name. Optional.
bool wasParsed() const
true if response message was parsed, false if IRCResponseParser ignored the response.
IRCNetworkEntity networkEntity
Information about the network to which we will connect.
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.
virtual void userJoins(const QString &nickname, const QString &fullSignature)=0
Use this to register the fact that user has joined the chat.
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.
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.
bool isAdapterRelated(const IRCAdapterBase *pAdapter) const
Checks if pAdapter equals this or is one of chat windows of this network.
const QString & address() const
Address of the server or network to connect to.