1 //=============================================================================
2 //
3 // File : KviIrcConnection.cpp
4 // Creation date : Mon 03 May 2004 01:45:42 by Szymon Stefanek
5 //
6 // This file is part of the KVIrc IRC client distribution
7 // Copyright (C) 2004-2010 Szymon Stefanek <pragma at kvirc dot net>
8 //
9 // This program is FREE software. You can redistribute it and/or
10 // modify it under the terms of the GNU General Public License
11 // as published by the Free Software Foundation; either version 2
12 // of the License, or (at your option) any later version.
13 //
14 // This program is distributed in the HOPE that it will be USEFUL,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17 // See the GNU General Public License for more details.
18 //
19 // You should have received a copy of the GNU General Public License
20 // along with this program. If not, write to the Free Software Foundation,
21 // Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 //
23 //=============================================================================
24
25 #include "KviIrcConnection.h"
26 #include "KviIrcConnectionTarget.h"
27 #include "KviIrcConnectionUserInfo.h"
28 #include "KviIrcConnectionServerInfo.h"
29 #include "KviIrcConnectionStateData.h"
30 #include "KviIrcConnectionAntiCtcpFloodData.h"
31 #include "KviIrcConnectionNetsplitDetectorData.h"
32 #include "KviIrcConnectionAsyncWhoisData.h"
33 #include "KviIrcConnectionRequestQueue.h"
34 #include "KviIrcConnectionStatistics.h"
35 #include "KviIrcLink.h"
36 #include "KviIrcSocket.h"
37 #include "KviLocale.h"
38 #include "KviIrcServerDataBase.h"
39 #include "KviIrcServerReconnectInfo.h"
40 #include "KviProxyDataBase.h"
41 #include "KviError.h"
42 #include "kvi_out.h"
43 #include "KviOptions.h"
44 #include "KviConsoleWindow.h"
45 #include "KviNetUtils.h"
46 #include "KviInternalCommand.h"
47 #include "KviMainWindow.h"
48 #include "KviMexLinkFilter.h"
49 #include "KviMemory.h"
50 #include "kvi_debug.h"
51 #include "KviChannelWindow.h"
52 #include "KviQueryWindow.h"
53 #include "KviApplication.h"
54 #include "KviDataBuffer.h"
55 #include "KviNotifyList.h"
56 #include "KviDnsResolver.h"
57 #include "kvi_defaults.h"
58 #include "KviIrcServerParser.h"
59 #include "KviIrcDataStreamMonitor.h"
60 #include "KviLagMeter.h"
61 #include "KviKvsEventTriggers.h"
62 #include "KviKvsScript.h"
63 #include "KviControlCodes.h"
64 #include "KviUserIdentity.h"
65 #include "KviIdentityProfileSet.h"
66 #include "KviSASL.h"
67 #include "KviNickColors.h"
68 #include "KviIrcNetwork.h"
69
70 #include <QTimer>
71 #include <QTextCodec>
72 #include <QtGlobal>
73
74 #include <algorithm>
75 #include <memory>
76
77 extern KVIRC_API KviIrcServerDataBase * g_pServerDataBase;
78 extern KVIRC_API KviProxyDataBase * g_pProxyDataBase;
79
KviIrcConnection(KviIrcContext * pContext,KviIrcConnectionTarget * pTarget,KviUserIdentity * pIdentity)80 KviIrcConnection::KviIrcConnection(KviIrcContext * pContext, KviIrcConnectionTarget * pTarget, KviUserIdentity * pIdentity)
81 : QObject(), m_pContext(pContext), m_pTarget(pTarget), m_pUserIdentity(pIdentity)
82 {
83 m_pConsole = pContext->console();
84 m_pLink = new KviIrcLink(this);
85 m_pUserDataBase = new KviIrcUserDataBase();
86 m_pUserInfo = new KviIrcConnectionUserInfo();
87 m_pServerInfo = new KviIrcConnectionServerInfo();
88 m_pStateData = new KviIrcConnectionStateData();
89 m_pAntiCtcpFloodData = new KviIrcConnectionAntiCtcpFloodData();
90 m_pNetsplitDetectorData = new KviIrcConnectionNetsplitDetectorData();
91 m_pAsyncWhoisData = new KviIrcConnectionAsyncWhoisData();
92 m_pStatistics = std::make_unique<KviIrcConnectionStatistics>();
93 m_pRequestQueue = new KviIrcConnectionRequestQueue();
94 setupSrvCodec();
95 setupTextCodec();
96 }
97
~KviIrcConnection()98 KviIrcConnection::~KviIrcConnection()
99 {
100 if(m_bIdentdAttached)
101 g_pMainWindow->executeInternalCommand(KVI_INTERNALCOMMAND_IDENT_STOP);
102
103 m_bIdentdAttached = false;
104 if(m_pLocalhostDns)
105 {
106 QObject::disconnect(m_pLocalhostDns, SIGNAL(lookupDone(KviDnsResolver *)), nullptr, nullptr);
107 if(m_pLocalhostDns->isRunning())
108 m_pLocalhostDns->deleteLater();
109 else
110 delete m_pLocalhostDns;
111 }
112
113 delete m_pNotifyListTimer;
114 m_pNotifyListTimer = nullptr;
115
116 delete m_pNotifyListManager; // destroy this before the userDb
117 m_pNotifyListManager = nullptr;
118
119 delete m_pLagMeter;
120 m_pLagMeter = nullptr;
121
122 delete m_pLink; // <-- this MAY trigger a linkTerminated() or something like this!
123 delete m_pTarget;
124 delete m_pUserDataBase;
125 delete m_pUserInfo;
126 delete m_pServerInfo;
127 delete m_pStateData;
128 delete m_pAntiCtcpFloodData;
129 delete m_pNetsplitDetectorData;
130 delete m_pAsyncWhoisData;
131 delete m_pUserIdentity;
132 m_pRequestQueue->deleteLater();
133 }
134
outputQueueSize()135 unsigned int KviIrcConnection::outputQueueSize()
136 {
137 return m_pLink->outputQueueSize();
138 }
139
clearOutputQueue(bool bPrivateMessagesOnly)140 void KviIrcConnection::clearOutputQueue(bool bPrivateMessagesOnly)
141 {
142 m_pLink->clearOutputQueue(bPrivateMessagesOnly);
143 }
144
setEncoding(const QString & szEncoding)145 void KviIrcConnection::setEncoding(const QString & szEncoding)
146 {
147 QTextCodec * c = KviLocale::instance()->codecForName(szEncoding.toLatin1());
148 if(c == m_pTextCodec)
149 return;
150 if(!c)
151 {
152 m_pConsole->output(KVI_OUT_SYSTEMERROR, __tr2qs("Failed to set the encoding to %Q: mapping not available."), &szEncoding);
153 return;
154 }
155
156 QString szTmp = c->name();
157 for(auto & ch : m_pChannelList)
158 {
159 if((ch->textCodec() != c) && (ch->textCodec() != ch->defaultTextCodec())) // actually not using the default!
160 {
161 ch->forceTextCodec(c);
162 if(_OUTPUT_VERBOSE)
163 ch->output(KVI_OUT_VERBOSE, __tr2qs("Changed text encoding to %Q"), &szTmp);
164 }
165 }
166
167 for(auto & q : m_pQueryList)
168 {
169 if((q->textCodec() != c) && (q->textCodec() != q->defaultTextCodec())) // actually not using the default!
170 {
171 q->forceTextCodec(c);
172 if(_OUTPUT_VERBOSE)
173 q->output(KVI_OUT_VERBOSE, __tr2qs("Changed text encoding to %Q"), &szTmp);
174 }
175 }
176
177 m_pSrvCodec = c;
178 m_pTextCodec = c;
179 m_pConsole->setTextEncoding(szEncoding);
180 }
181
setupSrvCodec()182 void KviIrcConnection::setupSrvCodec()
183 {
184 // grab the codec: first look it up in the server data
185 m_pSrvCodec = nullptr;
186 if(!m_pTarget->server()->encoding().isEmpty())
187 {
188 m_pSrvCodec = KviLocale::instance()->codecForName(m_pTarget->server()->encoding().toLatin1());
189 if(!m_pSrvCodec)
190 qDebug("KviIrcConnection: can't find QTextCodec for encoding %s", m_pTarget->server()->encoding().toUtf8().data());
191 }
192
193 if(!m_pSrvCodec)
194 {
195 // try the network
196 if(!m_pTarget->network()->encoding().isEmpty())
197 {
198 m_pSrvCodec = KviLocale::instance()->codecForName(m_pTarget->network()->encoding().toLatin1());
199 if(!m_pSrvCodec)
200 qDebug("KviIrcConnection: can't find QTextCodec for encoding %s", m_pTarget->network()->encoding().toUtf8().data());
201 }
202 }
203
204 if(!m_pSrvCodec)
205 m_pSrvCodec = KviApplication::defaultSrvCodec();
206
207 m_pConsole->setTextEncoding(QString(m_pSrvCodec->name()));
208 }
209
setupTextCodec()210 void KviIrcConnection::setupTextCodec()
211 {
212 // grab the codec: first look it up in the server data
213 m_pTextCodec = nullptr;
214 if(!m_pTarget->server()->textEncoding().isEmpty())
215 {
216 m_pTextCodec = KviLocale::instance()->codecForName(m_pTarget->server()->textEncoding().toLatin1());
217 if(!m_pTextCodec)
218 qDebug("KviIrcConnection: can't find QTextCodec for encoding %s", m_pTarget->server()->textEncoding().toUtf8().data());
219 }
220
221 if(!m_pTextCodec)
222 {
223 // try the network
224 if(!m_pTarget->network()->textEncoding().isEmpty())
225 {
226 m_pTextCodec = KviLocale::instance()->codecForName(m_pTarget->network()->textEncoding().toLatin1());
227 if(!m_pTextCodec)
228 qDebug("KviIrcConnection: can't find QTextCodec for encoding %s", m_pTarget->network()->textEncoding().toUtf8().data());
229 }
230 }
231
232 if(!m_pTextCodec)
233 m_pTextCodec = KviApplication::defaultTextCodec();
234 }
235
236 /*
237 * We're asking the connection itself to encode/decode text: use server specific encoding,
238 * instead of the "user text" encoding used in channels
239 */
encodeText(const QString & szText)240 QByteArray KviIrcConnection::encodeText(const QString & szText)
241 {
242 if(!m_pSrvCodec)
243 return szText.toUtf8();
244 return m_pSrvCodec->fromUnicode(szText);
245 }
246
decodeText(const char * pcText)247 QString KviIrcConnection::decodeText(const char * pcText)
248 {
249 if(!m_pSrvCodec)
250 return QString(pcText);
251 return m_pSrvCodec->toUnicode(pcText);
252 }
253
serverInfoReceived(const QString & szServerName,const QString & szUserModes,const QString & szChanModes)254 void KviIrcConnection::serverInfoReceived(const QString & szServerName, const QString & szUserModes, const QString & szChanModes)
255 {
256 serverInfo()->setName(szServerName);
257 serverInfo()->setSupportedUserModes(szUserModes);
258 serverInfo()->setSupportedChannelModes(szChanModes);
259 m_pConsole->updateCaption(); // for server name
260 g_pMainWindow->childConnectionServerInfoChange(this);
261 }
262
currentNetworkName() const263 const QString & KviIrcConnection::currentNetworkName() const
264 {
265 return m_pServerInfo->networkName();
266 }
267
abort()268 void KviIrcConnection::abort()
269 {
270 // this WILL trigger linkAttemptFailed() or linkTerminated()
271 m_pLink->abort();
272 }
273
start()274 void KviIrcConnection::start()
275 {
276 m_eState = Connecting;
277 if(KVI_OPTION_BOOL(KviOption_boolUseIdentService) && KVI_OPTION_BOOL(KviOption_boolUseIdentServiceOnlyOnConnect))
278 {
279 g_pMainWindow->executeInternalCommand(KVI_INTERNALCOMMAND_IDENT_START);
280 m_bIdentdAttached = true;
281 }
282 m_pLink->start();
283 }
284
linkEstablished()285 void KviIrcConnection::linkEstablished()
286 {
287 m_eState = Connected;
288
289 // setup reasonable defaults before notifying anyone
290 m_pStatistics->setConnectionStartTime(kvi_unixTime());
291 m_pStatistics->setLastMessageTime(kvi_unixTime());
292 m_pServerInfo->setName(target()->server()->hostName());
293 m_pServerInfo->setNetworkName(target()->network()->name());
294
295 // FIXME: With STARTTLS this is called TWICE!
296 for(auto & m : context()->monitorList())
297 m->connectionInitiated();
298
299 // FIXME: With STARTTLS this is called TWICE!
300 context()->connectionEstablished();
301
302 // Ok...we're logging in now
303 resolveLocalHost();
304
305 if(target()->server()->enabledCAP())
306 {
307 /*
308 * HACK: this is needed to avoid timeouts connecting to server that does not
309 * support the CAP extension (yet). Adding a somewhat broken message to a
310 * CAP LS request will force the server to output an error message.
311 *
312 * I'm currently aware of 2 methods:
313 * 1) using a broken nick (NICK -) to force a 437: ERR_UNAVAILRESOURCE
314 * 2) ping the server before registration to get a // 451: ERR_NOTREGISTERED
315 *
316 * Both method works, but the first method could compromise STARTTLS on some
317 * server that won't allow you to use tsl if the registration has already started
318 *
319 * Please note that IRC bouncers are currently broken for this setup: eg. psyBNC
320 * will let the user login with a "-" (single dash) nickname or executing any
321 * invalid command without throwing any error at all.
322 *
323 * This MUST go as one network packet to avoid possible handshake problems
324 * on slow connections.
325 */
326
327 m_pStateData->setInsideInitialCapLs(true);
328 m_pStateData->setIgnoreOneYouHaveNotRegisteredError(true);
329
330 // FIXME: The PING method does NOT work with bouncers. We need a timeout here.
331
332 if(sendFmtData("CAP LS\r\nPING :%Q", &(target()->server()->hostName())))
333 return;
334
335 //m_pConsole->output(KVI_OUT_SYSTEMMESSAGE,__tr2qs("Failed to send the CAP LS request. Server capabilities will not be detected."));
336 }
337
338 if(!link() || !link()->socket())
339 return;
340
341 if((!link()->socket()->usingSSL()) && target()->server()->enabledSTARTTLS())
342 {
343 #ifdef COMPILE_SSL_SUPPORT
344 // STARTTLS without CAP (forced request)
345
346 m_pStateData->setInsideInitialStartTls(true);
347 m_pStateData->setIgnoreOneYouHaveNotRegisteredError(true);
348
349 // STARTTLS requested but CAP not requested.
350 if(trySTARTTLS(true))
351 return; // STARTTLS negotiation in progress
352
353 m_pStateData->setInsideInitialStartTls(false);
354 m_pStateData->setIgnoreOneYouHaveNotRegisteredError(false);
355 #endif
356 }
357
358 loginToIrcServer();
359 }
360
361 #ifdef COMPILE_SSL_SUPPORT
trySTARTTLS(bool bAppendPing)362 bool KviIrcConnection::trySTARTTLS(bool bAppendPing)
363 {
364 // Check if the server supports STARTTLS protocol and we want to
365 // connect through it
366 bool bRet = bAppendPing ? sendFmtData("STARTTLS\r\nPING :%Q", &(target()->server()->hostName())) : sendFmtData("STARTTLS");
367
368 if(!bRet)
369 {
370 // Cannot send command
371 m_pConsole->output(KVI_OUT_SOCKETERROR, __tr2qs("Impossible to send STARTTLS command to the IRC server. Your connection will NOT be encrypted"));
372 return false;
373 }
374
375 m_pStateData->setSentStartTls();
376 return true;
377 }
378
handleFailedInitialStartTls()379 void KviIrcConnection::handleFailedInitialStartTls()
380 {
381 m_pStateData->setInsideInitialStartTls(false);
382 loginToIrcServer();
383 }
384
enableStartTlsSupport(bool bEnable)385 void KviIrcConnection::enableStartTlsSupport(bool bEnable)
386 {
387 m_pStateData->setInsideInitialStartTls(false);
388
389 if(bEnable)
390 {
391 // Ok, the server supports STARTTLS protocol
392 // ssl handshake e switch del socket
393 //qDebug("Starting SSL handshake...");
394 link()->socket()->enterSSLMode(); // FIXME: this should be forwarded through KviIrcLink, probably
395 }
396 else
397 {
398 // The server does not support STARTTLS
399 m_pConsole->output(KVI_OUT_SOCKETERROR, __tr2qs("The server does not support STARTTLS command. Your connection will NOT be encrypted"));
400 }
401 }
402 #endif // COMPILE_SSL_SUPPORT
403
handleInitialCapLs()404 void KviIrcConnection::handleInitialCapLs()
405 {
406 if(!m_pStateData->isInsideInitialCapLs())
407 return; // We shouldn't be here...
408
409 m_pStateData->setInsideInitialCapLs(false);
410
411 // STARTTLS support: this has to be checked first because it could imply
412 // a full cap renegotiation
413 #ifdef COMPILE_SSL_SUPPORT
414 if((!link()->socket()->usingSSL()) && target()->server()->enabledSTARTTLS() && serverInfo()->supportedCaps().contains("tls", Qt::CaseInsensitive))
415 {
416 if(trySTARTTLS(false))
417 return; // STARTTLS negotiation in progress
418 }
419 #endif
420
421 QString szRequests;
422
423 auto cap_add = [&](const char * c) {
424 if(serverInfo()->supportedCaps().contains(c, Qt::CaseInsensitive))
425 {
426 szRequests.append(c);
427 szRequests.append(" ");
428 }
429 };
430
431 if(target()->server()->enabledSASL())
432 cap_add("sasl");
433
434 cap_add("znc.in/server-time-iso");
435 cap_add("server-time");
436 cap_add("multi-prefix");
437 cap_add("away-notify");
438 cap_add("account-notify");
439 cap_add("extended-join");
440 cap_add("userhost-in-names");
441 cap_add("chghost");
442 cap_add("znc.in/self-message");
443
444 if(szRequests.isEmpty())
445 {
446 endInitialCapNegotiation();
447 }
448 else
449 {
450 sendFmtData("CAP REQ :%s", szRequests.trimmed().toUtf8().data());
451 m_pStateData->setInsideInitialCapReq(true);
452 }
453 }
454
handleInitialCapAck()455 void KviIrcConnection::handleInitialCapAck()
456 {
457 if(!m_pStateData->isInsideInitialCapReq())
458 return; // We shouldn't be here
459
460 m_pStateData->setInsideInitialCapReq(false);
461
462 bool bUsed = false;
463
464 //SASL
465 if(target()->server()->enabledSASL() && m_pStateData->enabledCaps().contains("sasl", Qt::CaseInsensitive))
466 {
467 if(target()->server()->saslMethod() == QStringLiteral("EXTERNAL"))
468 {
469 if(KVI_OPTION_BOOL(KviOption_boolUseSSLCertificate) && link()->socket()->usingSSL())
470 {
471 bUsed = true;
472 sendFmtData("AUTHENTICATE EXTERNAL");
473 m_pStateData->setSentSaslMethod(QStringLiteral("EXTERNAL"));
474 }
475 }
476
477 // Assume PLAIN if all other SASL methods are not chosen or we're attempting a fallback
478 if(!bUsed && !target()->server()->saslNick().isEmpty() && !target()->server()->saslPass().isEmpty())
479 {
480 bUsed = true;
481 sendFmtData("AUTHENTICATE PLAIN");
482 m_pStateData->setSentSaslMethod(QStringLiteral("PLAIN"));
483 }
484 }
485
486 if(bUsed)
487 m_pStateData->setInsideAuthenticate(true);
488 else
489 endInitialCapNegotiation();
490 }
491
handleAuthenticate(KviCString & szAuth)492 void KviIrcConnection::handleAuthenticate(KviCString & szAuth)
493 {
494 //SASL
495 if(!m_pStateData->isInsideAuthenticate())
496 return;
497
498 QByteArray szNick = encodeText(target()->server()->saslNick());
499 QByteArray szPass = encodeText(target()->server()->saslPass());
500
501 KviCString szOut;
502 bool bSendString = false;
503 if(m_pStateData->sentSaslMethod() == QStringLiteral("EXTERNAL"))
504 bSendString = KviSASL::externalMethod(szAuth, szOut);
505 else // Assume PLAIN
506 bSendString = KviSASL::plainMethod(szAuth, szOut, szNick, szPass);
507
508 if(bSendString)
509 sendFmtData("AUTHENTICATE %s", szOut.ptr());
510 else
511 sendFmtData("AUTHENTICATE *");
512 }
513
handleInitialCapNak()514 void KviIrcConnection::handleInitialCapNak()
515 {
516 endInitialCapNegotiation();
517 }
518
endInitialCapNegotiation()519 void KviIrcConnection::endInitialCapNegotiation()
520 {
521 m_pStateData->setInsideAuthenticate(false);
522 sendFmtData("CAP END");
523 loginToIrcServer();
524 }
525
handleFailedInitialCapLs()526 void KviIrcConnection::handleFailedInitialCapLs()
527 {
528 m_pConsole->output(KVI_OUT_SYSTEMMESSAGE, __tr2qs("Extended Capabilities don't seem to be supported by the server"));
529
530 m_pStateData->setInsideInitialCapLs(false);
531 loginToIrcServer();
532 }
533
linkTerminated()534 void KviIrcConnection::linkTerminated()
535 {
536 if(m_bIdentdAttached)
537 {
538 g_pMainWindow->executeInternalCommand(KVI_INTERNALCOMMAND_IDENT_STOP);
539 m_bIdentdAttached = false;
540 }
541 m_eState = Idle;
542
543 delete m_pNotifyListManager;
544 m_pNotifyListManager = nullptr;
545
546 delete m_pLagMeter;
547 m_pLagMeter = nullptr;
548
549 for(auto & m : context()->monitorList())
550 m->connectionTerminated();
551
552 // Prepare data for an eventual reconnect
553 context()->connectionTerminated();
554 }
555
linkAttemptFailed(int iError)556 void KviIrcConnection::linkAttemptFailed(int iError)
557 {
558 if(m_bIdentdAttached)
559 {
560 g_pMainWindow->executeInternalCommand(KVI_INTERNALCOMMAND_IDENT_STOP);
561 m_bIdentdAttached = false;
562 }
563 m_eState = Idle;
564 context()->connectionFailed(iError);
565 }
566
findChannel(const QString & szName)567 KviChannelWindow * KviIrcConnection::findChannel(const QString & szName)
568 {
569 for(auto & c : m_pChannelList)
570 {
571 if(KviQString::equalCI(szName, c->windowName()))
572 return c;
573 }
574 return nullptr;
575 }
576
getCommonChannels(const QString & szNick,QString & szChansBuffer,bool bAddEscapeSequences)577 int KviIrcConnection::getCommonChannels(const QString & szNick, QString & szChansBuffer, bool bAddEscapeSequences)
578 {
579 int iCount = 0;
580 for(auto & c : m_pChannelList)
581 {
582 if(c->isOn(szNick))
583 {
584 if(!szChansBuffer.isEmpty())
585 szChansBuffer.append(", ");
586
587 char uFlag = c->getUserFlag(szNick);
588 if(uFlag)
589 {
590 KviQString::appendFormatted(szChansBuffer, bAddEscapeSequences ? "%c\r!c\r%Q\r" : "%c%Q", uFlag, &(c->windowName()));
591 }
592 else
593 {
594 if(bAddEscapeSequences)
595 KviQString::appendFormatted(szChansBuffer, "\r!c\r%Q\r", &(c->windowName()));
596 else
597 szChansBuffer.append(c->windowName());
598 }
599 iCount++;
600 }
601 }
602 return iCount;
603 }
604
unhighlightAllChannels()605 void KviIrcConnection::unhighlightAllChannels()
606 {
607 for(auto & c : m_pChannelList)
608 c->unhighlight();
609 }
610
unhighlightAllQueries()611 void KviIrcConnection::unhighlightAllQueries()
612 {
613 for(auto & c : m_pQueryList)
614 c->unhighlight();
615 }
616
closeAllChannels()617 void KviIrcConnection::closeAllChannels()
618 {
619 while(!m_pChannelList.empty())
620 {
621 auto & c = m_pChannelList.front();
622 c->close();
623 QApplication::processEvents(QEventLoop::ExcludeSocketNotifiers | QEventLoop::ExcludeUserInputEvents);
624 }
625 }
626
closeAllQueries()627 void KviIrcConnection::closeAllQueries()
628 {
629 while(!m_pQueryList.empty())
630 {
631 auto & q = m_pChannelList.front();
632 q->close();
633 QApplication::processEvents(QEventLoop::ExcludeSocketNotifiers | QEventLoop::ExcludeUserInputEvents);
634 }
635 }
636
createChannel(const QString & szName)637 KviChannelWindow * KviIrcConnection::createChannel(const QString & szName)
638 {
639 KviChannelWindow * c = m_pContext->findDeadChannel(szName);
640 if(c)
641 {
642 c->setAliveChan();
643 if(!KVI_OPTION_BOOL(KviOption_boolCreateMinimizedChannels))
644 {
645 g_pMainWindow->setActiveWindow(c);
646 }
647 }
648 else
649 {
650 c = new KviChannelWindow(m_pConsole, szName);
651 g_pMainWindow->addWindow(c, !KVI_OPTION_BOOL(KviOption_boolCreateMinimizedChannels));
652 }
653 return c;
654 }
655
createQuery(const QString & szNick,CreateQueryVisibilityMode eVisibilityMode)656 KviQueryWindow * KviIrcConnection::createQuery(const QString & szNick, CreateQueryVisibilityMode eVisibilityMode)
657 {
658 KviQueryWindow * q = m_pContext->findDeadQuery(szNick);
659 if(!q)
660 {
661 q = findQuery(szNick);
662 if(q)
663 return q; // hm ?
664 }
665
666 bool bShowIt;
667
668 // adjust visibility mode
669 switch(eVisibilityMode)
670 {
671 //case CreateQueryFollowGlobalVisibilitySetting:
672 case CreateQueryVisibilityMinimized:
673 bShowIt = false;
674 break;
675 case CreateQueryVisibilityVisible:
676 bShowIt = true;
677 break;
678 default:
679 bShowIt = !KVI_OPTION_BOOL(KviOption_boolCreateIncomingQueriesAsMinimized);
680 break;
681 }
682
683 if(q)
684 {
685 q->setAliveQuery();
686 if(bShowIt)
687 {
688 g_pMainWindow->setActiveWindow(q);
689 }
690 }
691 else
692 {
693 q = new KviQueryWindow(m_pConsole, szNick);
694 g_pMainWindow->addWindow(q, bShowIt);
695 }
696 return q;
697 }
698
findQuery(const QString & szName)699 KviQueryWindow * KviIrcConnection::findQuery(const QString & szName)
700 {
701 for(auto & q : m_pQueryList)
702 {
703 if(KviQString::equalCI(szName, q->windowName()))
704 return q;
705 }
706 return nullptr;
707 }
708
registerChannel(KviChannelWindow * c)709 void KviIrcConnection::registerChannel(KviChannelWindow * c)
710 {
711 m_pChannelList.push_back(c);
712 if(KVI_OPTION_BOOL(KviOption_boolLogChannelHistory))
713 g_pApp->addRecentChannel(c->windowName(), m_pServerInfo->networkName());
714 emit(channelRegistered(c));
715 emit(chanListChanged());
716 }
717
unregisterChannel(KviChannelWindow * c)718 void KviIrcConnection::unregisterChannel(KviChannelWindow * c)
719 {
720 m_pChannelList.erase(std::remove(m_pChannelList.begin(), m_pChannelList.end(), c), m_pChannelList.end());
721 requestQueue()->dequeueChannel(c);
722 emit(channelUnregistered(c));
723 emit(chanListChanged());
724 }
725
registerQuery(KviQueryWindow * q)726 void KviIrcConnection::registerQuery(KviQueryWindow * q)
727 {
728 m_pQueryList.push_back(q);
729 }
730
unregisterQuery(KviQueryWindow * q)731 void KviIrcConnection::unregisterQuery(KviQueryWindow * q)
732 {
733 m_pQueryList.erase(std::remove(m_pQueryList.begin(), m_pQueryList.end(), q), m_pQueryList.end());
734 }
735
keepChannelsOpenAfterDisconnect()736 void KviIrcConnection::keepChannelsOpenAfterDisconnect()
737 {
738 while(!m_pChannelList.empty())
739 {
740 KviChannelWindow * c = m_pChannelList.front();
741 c->outputNoFmt(KVI_OUT_SOCKETERROR, __tr2qs("Connection to server lost"));
742 c->setDeadChan();
743 }
744 }
745
keepQueriesOpenAfterDisconnect()746 void KviIrcConnection::keepQueriesOpenAfterDisconnect()
747 {
748 while(!m_pQueryList.empty())
749 {
750 KviQueryWindow * q = m_pQueryList.front();
751 q->outputNoFmt(KVI_OUT_SOCKETERROR, __tr2qs("Connection to server lost"));
752 q->setDeadQuery();
753 }
754 }
755
resurrectDeadQueries()756 void KviIrcConnection::resurrectDeadQueries()
757 {
758 while(KviQueryWindow * q = m_pContext->firstDeadQuery())
759 {
760 q->outputNoFmt(KVI_OUT_SOCKETMESSAGE, __tr2qs("Connection to server established"));
761 q->setAliveQuery();
762 }
763 }
764
765 //=== Message send stuff ====================================================//
766 // Max buffer that can be sent to an IRC server is 512 bytes
767 // including CRLF. (ircd simply 'cuts' messages to 512 bytes
768 // and discards the remainig part)
769 // Note that 510 bytes of data is a reasonably long message :)
770 //
771 // 01234567890123456789012345678901234567890123456789
772 // 01234567890123456789012345678901234567890123456789
773 // 01234567890123456789012345678901234567890123456789
774 // 01234567890123456789012345678901234567890123456789
775 // 01234567890123456789012345678901234567890123456789
776 // 01234567890123456789012345678901234567890123456789
777 // 01234567890123456789012345678901234567890123456789
778 // 01234567890123456789012345678901234567890123456789
779 // 01234567890123456789012345678901234567890123456789
780 // 01234567890123456789012345678901234567890123456789
781 // 0123456789\r\n
782 //
783 // We keep a list of data to send, and flush it as soon as we can.
784 //
785
sendFmtData(const char * pcFmt,...)786 bool KviIrcConnection::sendFmtData(const char * pcFmt, ...)
787 {
788 KviDataBuffer * pData = new KviDataBuffer(512);
789 kvi_va_list(list);
790 kvi_va_start(list, pcFmt);
791 bool bTruncated;
792 //sprintf the buffer up to 512 chars (adds a CRLF too)
793 int iLen = kvi_irc_vsnprintf((char *)(pData->data()), pcFmt, list, &bTruncated);
794 kvi_va_end(list);
795
796 //adjust the buffer size
797 if(iLen < 512)
798 pData->resize(iLen);
799
800 if(bTruncated)
801 {
802 if(!_OUTPUT_MUTE)
803 m_pConsole->outputNoFmt(KVI_OUT_SOCKETWARNING, __tr2qs("[LINK WARNING]: Socket message truncated to 512 bytes."));
804 }
805
806 QString szMsg = QString::fromLatin1((const char *)(pData->data()), iLen - 2);
807
808 // notify the monitors
809 for(auto & m : context()->monitorList())
810 {
811 if(m->outgoingMessage(szMsg.toLatin1().data()))
812 {
813 delete pData;
814 return true;
815 }
816 }
817
818 // Trigger OnOutboundTraffic event
819 KVS_TRIGGER_EVENT_1(KviEvent_OnOutboundTraffic, m_pConsole->activeWindow(), szMsg);
820
821 return m_pLink->sendPacket(pData);
822 }
823
sendData(const char * pcBuffer,int iBuflen)824 bool KviIrcConnection::sendData(const char * pcBuffer, int iBuflen)
825 {
826 if(iBuflen < 0)
827 iBuflen = (int)strlen(pcBuffer);
828 if(iBuflen > 510)
829 {
830 iBuflen = 510;
831 if(!_OUTPUT_MUTE)
832 m_pConsole->outputNoFmt(KVI_OUT_SOCKETWARNING, __tr2qs("[LINK WARNING]: Socket message truncated to 512 bytes."));
833 }
834
835 KviDataBuffer * pData = new KviDataBuffer(iBuflen + 2);
836 KviMemory::move(pData->data(), pcBuffer, iBuflen);
837 *(pData->data() + iBuflen) = '\r';
838 *(pData->data() + iBuflen + 1) = '\n';
839
840 QString szMsg = (const char *)(pData->data());
841 szMsg.truncate(iBuflen);
842
843 // notify the monitors
844 for(auto & m : context()->monitorList())
845 {
846 if(m->outgoingMessage(szMsg.toUtf8().data()))
847 {
848 delete pData;
849 return true;
850 }
851 }
852
853 // Trigger OnOutboundTraffic event
854 KVS_TRIGGER_EVENT_1(KviEvent_OnOutboundTraffic, m_pConsole->activeWindow(), szMsg);
855
856 return m_pLink->sendPacket(pData);
857 }
858
859 //
860 // notify list management
861 //
862
delayedStartNotifyList()863 void KviIrcConnection::delayedStartNotifyList()
864 {
865 KVI_ASSERT(!m_pNotifyListTimer);
866
867 delete m_pNotifyListTimer;
868
869 m_pNotifyListTimer = new QTimer();
870 m_pNotifyListTimer->setInterval(15000);
871 m_pNotifyListTimer->setSingleShot(true);
872 m_pNotifyListTimer->start();
873 connect(m_pNotifyListTimer, SIGNAL(timeout()), this, SLOT(restartNotifyList()));
874
875 // This delay is large enough to fire after the MOTD has been sent,
876 // even on the weirdest network.
877 // If there is no MOTD, this timer will fire after 15 secs,
878 // If there is a MOTD, restartNotifyList() will be triggered by RPL_ENDOFMOTD and
879 // will kill the timer before it has fired.
880 }
881
endOfMotdReceived()882 void KviIrcConnection::endOfMotdReceived()
883 {
884 // if the timer is still there running then just
885 if(m_pNotifyListTimer)
886 restartNotifyList();
887 }
888
restartNotifyList()889 void KviIrcConnection::restartNotifyList()
890 {
891 delete m_pNotifyListTimer;
892 m_pNotifyListTimer = nullptr;
893
894 // clear it
895 if(m_pNotifyListManager)
896 {
897 m_pNotifyListManager->stop(); // may need to remove watch entries
898 delete m_pNotifyListManager;
899 m_pNotifyListManager = nullptr;
900 }
901
902 if(!KVI_OPTION_BOOL(KviOption_boolUseNotifyList))
903 return;
904
905 if(serverInfo()->supportsWatchList() && KVI_OPTION_BOOL(KviOption_boolUseWatchListIfAvailable))
906 {
907 if(_OUTPUT_VERBOSE)
908 m_pConsole->output(KVI_OUT_VERBOSE, __tr2qs("The server seems to support the WATCH notify list method, will try to use it"));
909 m_pNotifyListManager = new KviWatchNotifyListManager(this);
910 }
911 else
912 {
913 if(KVI_OPTION_BOOL(KviOption_boolUseIntelligentNotifyListManager))
914 m_pNotifyListManager = new KviIsOnNotifyListManager(this);
915 else
916 m_pNotifyListManager = new KviStupidNotifyListManager(this);
917 }
918 m_pNotifyListManager->start();
919 }
920
restartLagMeter()921 void KviIrcConnection::restartLagMeter()
922 {
923 delete m_pLagMeter;
924 m_pLagMeter = nullptr;
925
926 if(!KVI_OPTION_BOOL(KviOption_boolUseLagMeterEngine))
927 return;
928 m_pLagMeter = new KviLagMeter(this);
929 }
930
resolveLocalHost()931 void KviIrcConnection::resolveLocalHost()
932 {
933 QString szIp;
934
935 if(!link()->socket()->getLocalHostIp(szIp, target()->server()->isIPv6()))
936 {
937 bool bGotIp = false;
938 if(!KVI_OPTION_STRING(KviOption_stringLocalHostIp).isEmpty())
939 {
940 #ifdef COMPILE_IPV6_SUPPORT
941 if(target()->server()->isIPv6())
942 {
943 if(KviNetUtils::isValidStringIPv6(KVI_OPTION_STRING(KviOption_stringLocalHostIp)))
944 bGotIp = true;
945 }
946 else
947 {
948 #endif
949 if(KviNetUtils::isValidStringIp(KVI_OPTION_STRING(KviOption_stringLocalHostIp)))
950 bGotIp = true;
951 #ifdef COMPILE_IPV6_SUPPORT
952 }
953 #endif
954 }
955
956 if(bGotIp)
957 {
958 m_pUserInfo->setLocalHostIp(KVI_OPTION_STRING(KviOption_stringLocalHostIp));
959 if(!_OUTPUT_MUTE)
960 m_pConsole->output(KVI_OUT_SYSTEMWARNING, __tr2qs("Can't resolve local host address, using user supplied one (%Q)"),
961 &(m_pUserInfo->localHostIp()));
962 }
963 else
964 {
965 // FIXME : Maybe check for IPv6 here too ?
966 m_pUserInfo->setLocalHostIp("127.0.0.1");
967 if(!_OUTPUT_MUTE)
968 m_pConsole->output(KVI_OUT_SYSTEMWARNING, __tr2qs("Can't resolve local host address, using default 127.0.0.1"),
969 &(m_pUserInfo->localHostIp()));
970 }
971 }
972 else
973 {
974 m_pUserInfo->setLocalHostIp(szIp);
975 if(!_OUTPUT_QUIET)
976 m_pConsole->output(KVI_OUT_SYSTEMMESSAGE, __tr2qs("Local host address is %Q"),
977 &(m_pUserInfo->localHostIp()));
978 }
979
980 // For now this is the only we know
981 m_pUserInfo->setHostName(m_pUserInfo->localHostIp());
982 m_pUserInfo->setHostIp(m_pUserInfo->localHostIp());
983 }
984
changeAwayState(bool bAway)985 void KviIrcConnection::changeAwayState(bool bAway)
986 {
987 if(bAway)
988 m_pUserInfo->setAway();
989 else
990 m_pUserInfo->setBack();
991
992 // Update the user entry
993 KviIrcUserEntry * e = userDataBase()->find(userInfo()->nickName());
994 if(e)
995 {
996 e->setAway(bAway);
997 // immediately update the userlist of the current channel
998 if(g_pActiveWindow->isChannel())
999 ((KviChannelWindow *)g_pActiveWindow)->userListView()->updateArea();
1000 }
1001
1002 m_pConsole->updateCaption();
1003 g_pMainWindow->childConnectionAwayStateChange(this);
1004
1005 emit awayStateChanged();
1006 }
1007
userInfoReceived(const QString & szUserName,const QString & szHostName)1008 void KviIrcConnection::userInfoReceived(const QString & szUserName, const QString & szHostName)
1009 {
1010 userInfo()->setUserName(szUserName);
1011 QString szUnmaskedHost = m_pUserInfo->unmaskedHostName();
1012 // Update the user entry
1013 KviIrcUserEntry * e = userDataBase()->find(userInfo()->nickName());
1014 if(e) // should be there! (we have the permanent entry in the notify list view)
1015 {
1016 e->setUser(szUserName);
1017 if(!szHostName.isEmpty())
1018 e->setHost(szHostName);
1019 } // else buuug
1020
1021 if(szHostName.isEmpty())
1022 return; // nothing to do anyway
1023
1024 if(KviQString::equalCS(m_pUserInfo->hostName(), szHostName))
1025 return; // again nothing to do
1026
1027 static bool warned_once = false;
1028
1029 if(!warned_once)
1030 {
1031 if(!(m_pUserInfo->hostName().isEmpty() || KviQString::equalCS(m_pUserInfo->hostName(), m_pUserInfo->localHostIp())))
1032 {
1033 // ok, something weird is probably going on
1034 // is is non-empty and it is NOT the IP address we have set
1035 // at connection startup...
1036 // ...the server (or more likely the bouncer) must have changed his mind...
1037 if(!_OUTPUT_MUTE)
1038 {
1039 m_pConsole->output(KVI_OUT_SYSTEMWARNING, __tr2qs("The server reports a hostname differing from what KVIrc set for the local hostname."));
1040 m_pConsole->output(KVI_OUT_SYSTEMWARNING, __tr2qs("This can happen if you use a broken bouncer, the IRC server masks your IP/Hostname or something weird is happening on the IRC server."));
1041 }
1042 warned_once = true;
1043 }
1044 }
1045
1046 // set it
1047 m_pUserInfo->setHostName(szHostName);
1048
1049 bool bChangeIp = true;
1050
1051 // if we don't have any routable IP yet, then it is worth to lookup the new hostname
1052
1053 #ifdef COMPILE_IPV6_SUPPORT
1054 if((KviNetUtils::isValidStringIp(m_pUserInfo->hostIp()) && KviNetUtils::isRoutableIpString(m_pUserInfo->hostIp())) || KviNetUtils::isValidStringIPv6(m_pUserInfo->hostIp()))
1055 #else
1056 if((KviNetUtils::isValidStringIp(m_pUserInfo->hostIp()) && KviNetUtils::isRoutableIpString(m_pUserInfo->hostIp())))
1057 #endif
1058 {
1059 if(KVI_OPTION_BOOL(KviOption_boolDccGuessIpFromServerWhenLocalIsUnroutable) && KVI_OPTION_BOOL(KviOption_boolDccBrokenBouncerHack))
1060 {
1061 if(!_OUTPUT_MUTE)
1062 m_pConsole->outputNoFmt(KVI_OUT_SYSTEMMESSAGE, __tr2qs("Here goes your \"broken bouncer hack\": The server has changed the hostname but I'll ignore the IP address change"));
1063 bChangeIp = false;
1064 }
1065 }
1066
1067 if(bChangeIp)
1068 {
1069 // lookup the new hostname then...
1070 #ifdef COMPILE_IPV6_SUPPORT
1071 if(KviNetUtils::isValidStringIp(szHostName) || KviNetUtils::isValidStringIPv6(szHostName))
1072 #else
1073 if(KviNetUtils::isValidStringIp(szHostName))
1074 #endif
1075 {
1076 if(!_OUTPUT_MUTE)
1077 m_pConsole->output(KVI_OUT_SYSTEMMESSAGE, __tr2qs("The local IP address as seen by the IRC server is %Q"), &szHostName);
1078 m_pUserInfo->setHostIp(szHostName);
1079 }
1080 else
1081 #ifdef COMPILE_IPV6_SUPPORT
1082 if(KviNetUtils::isValidStringIp(szUnmaskedHost) || KviNetUtils::isValidStringIPv6(szUnmaskedHost))
1083 #else
1084 if(KviNetUtils::isValidStringIp(szUnmaskedHost))
1085 #endif
1086 {
1087 if(!_OUTPUT_MUTE)
1088 m_pConsole->output(KVI_OUT_SYSTEMMESSAGE, __tr2qs("The local IP address as seen by the IRC server is %Q"), &szUnmaskedHost);
1089 m_pUserInfo->setHostIp(szUnmaskedHost);
1090 }
1091 else
1092 {
1093 // look it up too
1094 delete m_pLocalhostDns; // it could be only another local host lookup
1095 m_pLocalhostDns = new KviDnsResolver();
1096 connect(m_pLocalhostDns, SIGNAL(lookupDone(KviDnsResolver *)), this, SLOT(hostNameLookupTerminated(KviDnsResolver *)));
1097
1098 if(!m_pLocalhostDns->lookup(szHostName, KviDnsResolver::Any))
1099 {
1100 if(!_OUTPUT_MUTE)
1101 {
1102 // don't change the string to aid the translators
1103 QString szTmp = __tr2qs("Can't start the DNS slave thread");
1104 m_pConsole->output(KVI_OUT_SYSTEMMESSAGE, __tr2qs("Unable to resolve the local hostname as seen by the IRC server: %Q"), &szTmp);
1105 }
1106 delete m_pLocalhostDns;
1107 m_pLocalhostDns = nullptr;
1108 }
1109 else
1110 {
1111 if(!_OUTPUT_MUTE)
1112 m_pConsole->output(KVI_OUT_SYSTEMMESSAGE, __tr2qs("Looking up the local hostname as seen by the IRC server (%Q)"), &szHostName);
1113 }
1114 }
1115 }
1116 }
1117
hostNameLookupTerminated(KviDnsResolver *)1118 void KviIrcConnection::hostNameLookupTerminated(KviDnsResolver *)
1119 {
1120 if(!m_pLocalhostDns)
1121 {
1122 qDebug("Something weird is happening: pDns != 0 but m_pLocalhostDns == 0 :/");
1123 return;
1124 }
1125
1126 if(m_pLocalhostDns->state() != KviDnsResolver::Success)
1127 {
1128 QString szErr = KviError::getDescription(m_pLocalhostDns->error());
1129 if(!m_pUserInfo->hostIp().isEmpty())
1130 m_pConsole->output(KVI_OUT_SYSTEMMESSAGE, __tr2qs("Unable to resolve the local hostname as seen by the IRC server: %Q, using previously resolved %Q"),
1131 &szErr, &(m_pUserInfo->hostIp()));
1132 else
1133 m_pConsole->output(KVI_OUT_SYSTEMMESSAGE, __tr2qs("Unable to resolve the local hostname as seen by the IRC server: %Q"),
1134 &szErr);
1135 }
1136 else
1137 {
1138 QString szIpAddr = m_pLocalhostDns->firstIpAddress();
1139 m_pConsole->output(KVI_OUT_SYSTEMMESSAGE, __tr2qs("Local hostname as seen by the IRC server resolved to %Q"), &szIpAddr);
1140 m_pUserInfo->setHostIp(m_pLocalhostDns->firstIpAddress());
1141 }
1142
1143 delete m_pLocalhostDns;
1144 m_pLocalhostDns = nullptr;
1145 }
1146
useRealName(const QString & szRealName)1147 void KviIrcConnection::useRealName(const QString & szRealName)
1148 {
1149 // Evaluate functions in the real name (so we can have $version() inside)
1150
1151 QString szRealNameBuffer = szRealName;
1152
1153 if(!szRealNameBuffer.isEmpty())
1154 {
1155 KviQString::escapeKvs(&szRealNameBuffer, KviQString::PermitVariables | KviQString::PermitFunctions);
1156
1157 KviKvsVariant vRet;
1158 if(KviKvsScript::evaluate(szRealNameBuffer, console(), nullptr, &vRet))
1159 vRet.asString(szRealNameBuffer);
1160 }
1161
1162 m_pUserInfo->setRealName(szRealNameBuffer);
1163 }
1164
useProfileData(KviIdentityProfileSet * pSet,const QString & szNetwork)1165 void KviIrcConnection::useProfileData(KviIdentityProfileSet * pSet, const QString & szNetwork)
1166 {
1167 KviIdentityProfile * pProfile = pSet->findNetwork(szNetwork);
1168 if(!pProfile)
1169 return;
1170
1171 // Update connection data
1172 //m_pUserInfo->setNickName(pProfile->nick());
1173 m_pUserInfo->setUserName(pProfile->userName());
1174 useRealName(pProfile->realName());
1175 }
1176
pickNextLoginNickName(bool bForceDefaultIfPrimaryNicknamesEmpty,const QString & szBaseNickForRandomChoices,QString & szChoiceDescriptionBuffer)1177 QString KviIrcConnection::pickNextLoginNickName(bool bForceDefaultIfPrimaryNicknamesEmpty, const QString & szBaseNickForRandomChoices, QString & szChoiceDescriptionBuffer)
1178 {
1179 QString szNick;
1180
1181 KVI_ASSERT(target());
1182
1183 // try profiles first
1184
1185 if((int)(m_pStateData->loginNickNameState()) < (int)(KviIrcConnectionStateData::UsedProfileSpecificNickName))
1186 {
1187 KviIrcNetwork * pNetwork = target()->network();
1188 KVI_ASSERT(pNetwork);
1189
1190 KviIdentityProfileSet * pSet = KviIdentityProfileSet::instance();
1191 bool bProfilesEnabled = pSet ? (pSet->isEnabled() && !pSet->isEmpty()) : false;
1192 if(bProfilesEnabled)
1193 {
1194 KviIdentityProfile * pProfile = pSet->findNetwork(pNetwork->name());
1195 if(pProfile)
1196 {
1197 szNick = pProfile->nick().trimmed();
1198 if(!szNick.isEmpty())
1199 {
1200 szChoiceDescriptionBuffer = __tr2qs("profile specific option");
1201 m_pStateData->setLoginNickNameState(KviIrcConnectionStateData::UsedProfileSpecificNickName);
1202 return szNick;
1203 }
1204 }
1205 }
1206 }
1207
1208 if((int)(m_pStateData->loginNickNameState()) < (int)(KviIrcConnectionStateData::UsedAlternativeProfileSpecificNickName))
1209 {
1210 KviIrcNetwork * pNetwork = target()->network();
1211 KVI_ASSERT(pNetwork);
1212
1213 KviIdentityProfileSet * pSet = KviIdentityProfileSet::instance();
1214 bool bProfilesEnabled = pSet ? (pSet->isEnabled() && !pSet->isEmpty()) : false;
1215 if(bProfilesEnabled)
1216 {
1217 KviIdentityProfile * pProfile = pSet->findNetwork(pNetwork->name());
1218 if(pProfile)
1219 {
1220 szNick = pProfile->nick().trimmed();
1221 if(!szNick.isEmpty())
1222 {
1223 szChoiceDescriptionBuffer = __tr2qs("profile specific alternative option");
1224 m_pStateData->setLoginNickNameState(KviIrcConnectionStateData::UsedAlternativeProfileSpecificNickName);
1225 return szNick;
1226 }
1227 }
1228 }
1229 }
1230
1231 // try server specific choices
1232
1233 if((int)(m_pStateData->loginNickNameState()) < (int)(KviIrcConnectionStateData::UsedServerSpecificNickName))
1234 {
1235 KviIrcServer * pServer = target()->server();
1236 KVI_ASSERT(pServer);
1237
1238 szNick = pServer->nickName().trimmed();
1239 if(!szNick.isEmpty())
1240 {
1241 szChoiceDescriptionBuffer = __tr2qs("server specific");
1242 m_pStateData->setLoginNickNameState(KviIrcConnectionStateData::UsedServerSpecificNickName);
1243 return szNick;
1244 }
1245 }
1246
1247 if((int)(m_pStateData->loginNickNameState()) < (int)(KviIrcConnectionStateData::UsedAlternativeServerSpecificNickName))
1248 {
1249 KviIrcServer * pServer = target()->server();
1250 KVI_ASSERT(pServer);
1251
1252 szNick = pServer->alternativeNickName().trimmed();
1253 if(!szNick.isEmpty())
1254 {
1255 szChoiceDescriptionBuffer = __tr2qs("server specific alternative option");
1256 m_pStateData->setLoginNickNameState(KviIrcConnectionStateData::UsedAlternativeServerSpecificNickName);
1257 return szNick;
1258 }
1259 }
1260
1261 // then try network specific ones
1262
1263 if((int)(m_pStateData->loginNickNameState()) < (int)(KviIrcConnectionStateData::UsedNetworkSpecificNickName))
1264 {
1265 KviIrcNetwork * pNetwork = target()->network();
1266 KVI_ASSERT(pNetwork);
1267
1268 szNick = pNetwork->nickName().trimmed();
1269 if(!szNick.isEmpty())
1270 {
1271 szChoiceDescriptionBuffer = __tr2qs("network specific option");
1272 m_pStateData->setLoginNickNameState(KviIrcConnectionStateData::UsedNetworkSpecificNickName);
1273 return szNick;
1274 }
1275 }
1276
1277 if((int)(m_pStateData->loginNickNameState()) < (int)(KviIrcConnectionStateData::UsedAlternativeNetworkSpecificNickName))
1278 {
1279 KviIrcNetwork * pNetwork = target()->network();
1280 KVI_ASSERT(pNetwork);
1281
1282 szNick = pNetwork->alternativeNickName().trimmed();
1283 if(!szNick.isEmpty())
1284 {
1285 szChoiceDescriptionBuffer = __tr2qs("network specific alternative option");
1286 m_pStateData->setLoginNickNameState(KviIrcConnectionStateData::UsedAlternativeNetworkSpecificNickName);
1287 return szNick;
1288 }
1289 }
1290
1291 // look in global options
1292
1293 if((int)(m_pStateData->loginNickNameState()) < (int)(KviIrcConnectionStateData::UsedGlobalNickName1))
1294 {
1295 szNick = KVI_OPTION_STRING(KviOption_stringNickname1).trimmed();
1296 if(bForceDefaultIfPrimaryNicknamesEmpty && szNick.isEmpty())
1297 {
1298 KVI_OPTION_STRING(KviOption_stringNickname1) = QString::fromUtf8(KVI_DEFAULT_NICKNAME1);
1299 szNick = KVI_OPTION_STRING(KviOption_stringNickname1).trimmed();
1300 }
1301 if(!szNick.isEmpty())
1302 {
1303 szChoiceDescriptionBuffer = __tr2qs("primary nickname specified in options");
1304 m_pStateData->setLoginNickNameState(KviIrcConnectionStateData::UsedGlobalNickName1);
1305 return szNick;
1306 }
1307 }
1308
1309 if((int)(m_pStateData->loginNickNameState()) < (int)(KviIrcConnectionStateData::UsedGlobalNickName2))
1310 {
1311 szNick = KVI_OPTION_STRING(KviOption_stringNickname2).trimmed();
1312 if(!szNick.isEmpty())
1313 {
1314 szChoiceDescriptionBuffer = __tr2qs("alternative nickname specified in options");
1315 m_pStateData->setLoginNickNameState(KviIrcConnectionStateData::UsedGlobalNickName2);
1316 return szNick;
1317 }
1318 }
1319
1320 if((int)(m_pStateData->loginNickNameState()) < (int)(KviIrcConnectionStateData::UsedGlobalNickName3))
1321 {
1322 szNick = KVI_OPTION_STRING(KviOption_stringNickname3).trimmed();
1323 if(!szNick.isEmpty())
1324 {
1325 szChoiceDescriptionBuffer = __tr2qs("second alternative nickname specified in options");
1326 m_pStateData->setLoginNickNameState(KviIrcConnectionStateData::UsedGlobalNickName3);
1327 return szNick;
1328 }
1329 }
1330
1331 if((int)(m_pStateData->loginNickNameState()) < (int)(KviIrcConnectionStateData::UsedGlobalNickName4))
1332 {
1333 szNick = KVI_OPTION_STRING(KviOption_stringNickname4).trimmed();
1334 if(!szNick.isEmpty())
1335 {
1336 szChoiceDescriptionBuffer = __tr2qs("third alternative nickname specified in options");
1337 m_pStateData->setLoginNickNameState(KviIrcConnectionStateData::UsedGlobalNickName4);
1338 return szNick;
1339 }
1340 }
1341
1342 // fallback to 4 random alternatives
1343 if((int)(m_pStateData->loginNickNameState()) < (int)(KviIrcConnectionStateData::UsedRandomNickName4))
1344 {
1345 const QString nickList[] = {
1346 szBaseNickForRandomChoices,
1347 KVI_OPTION_STRING(KviOption_stringNickname1),
1348 KVI_OPTION_STRING(KviOption_stringNickname2),
1349 KVI_OPTION_STRING(KviOption_stringNickname3),
1350 KVI_OPTION_STRING(KviOption_stringNickname4),
1351 QString::fromUtf8(KVI_DEFAULT_NICKNAME1)
1352 };
1353
1354 for(const auto & ii : nickList)
1355 {
1356 szNick = ii.trimmed();
1357
1358 if(!szNick.isEmpty())
1359 break;
1360 }
1361
1362 szNick = szNick.left(7);
1363 while(szNick.length() < 8)
1364 {
1365 QString num;
1366 num.setNum(rand() % 10);
1367 szNick.append(num);
1368 }
1369
1370 szChoiceDescriptionBuffer = __tr2qs("random nickname");
1371
1372 if((int)(m_pStateData->loginNickNameState()) < (int)(KviIrcConnectionStateData::UsedRandomNickName1))
1373 m_pStateData->setLoginNickNameState(KviIrcConnectionStateData::UsedRandomNickName1);
1374 else if((int)(m_pStateData->loginNickNameState()) < (int)(KviIrcConnectionStateData::UsedRandomNickName2))
1375 m_pStateData->setLoginNickNameState(KviIrcConnectionStateData::UsedRandomNickName2);
1376 else if((int)(m_pStateData->loginNickNameState()) < (int)(KviIrcConnectionStateData::UsedRandomNickName3))
1377 m_pStateData->setLoginNickNameState(KviIrcConnectionStateData::UsedRandomNickName3);
1378 else
1379 m_pStateData->setLoginNickNameState(KviIrcConnectionStateData::UsedRandomNickName4);
1380 return szNick;
1381 }
1382
1383 // give up
1384
1385 m_pStateData->setLoginNickNameState(KviIrcConnectionStateData::UsedManualNickname);
1386 return szNick; // empty
1387 }
1388
loginToIrcServer()1389 void KviIrcConnection::loginToIrcServer()
1390 {
1391 KviIrcServer * pServer = target()->server();
1392 KviIrcNetwork * pNet = target()->network();
1393
1394 // For now this is the only we know
1395 m_pServerInfo->setName(pServer->hostName());
1396
1397 QString szTmpNick, szTmpUser, szTmpPass, szTmpName;
1398 // Username
1399 szTmpUser = pServer->userName().trimmed();
1400 if(!szTmpUser.isEmpty())
1401 {
1402 if(!_OUTPUT_MUTE)
1403 m_pConsole->output(KVI_OUT_VERBOSE, __tr2qs("Using server specific username (%Q)"), &(szTmpUser));
1404 }
1405 else
1406 {
1407 szTmpUser = pNet->userName().trimmed();
1408 if(!szTmpUser.isEmpty())
1409 {
1410 if(!_OUTPUT_MUTE)
1411 m_pConsole->output(KVI_OUT_VERBOSE, __tr2qs("Using network specific username (%Q)"), &(szTmpUser));
1412 }
1413 else
1414 {
1415 szTmpUser = KVI_OPTION_STRING(KviOption_stringUsername).trimmed();
1416 if(szTmpUser.isEmpty())
1417 szTmpUser = KVI_DEFAULT_USERNAME;
1418 }
1419 }
1420
1421 m_pUserInfo->setUserName(szTmpUser);
1422
1423 // Nick
1424 if(pServer->reconnectInfo())
1425 szTmpNick = pServer->reconnectInfo()->m_szNick;
1426
1427 // set this one as default (also for pickNextLoginNickName() below)
1428 m_pStateData->setLoginNickNameState(KviIrcConnectionStateData::UsedConnectionSpecificNickName);
1429
1430 QString szChoiceDescription;
1431
1432 if(!szTmpNick.isEmpty())
1433 {
1434 szChoiceDescription = __tr2qs("connection specific");
1435 }
1436 else
1437 {
1438 szTmpNick = pickNextLoginNickName(true, QString(), szChoiceDescription);
1439 KVI_ASSERT(!szTmpNick.isEmpty());
1440 }
1441
1442 if(!_OUTPUT_MUTE)
1443 {
1444 QString szOut = __tr2qs("Using %1 as nickname").arg(szTmpNick);
1445 if(_OUTPUT_VERBOSE)
1446 szOut += QString::fromLatin1(" (%1)").arg(szChoiceDescription);
1447
1448 m_pConsole->outputNoFmt(KVI_OUT_VERBOSE, szOut);
1449 }
1450
1451 m_pUserInfo->setNickName(szTmpNick);
1452
1453 // Real name
1454 szTmpName = pServer->realName().trimmed();
1455 if(!szTmpName.isEmpty())
1456 {
1457 if(!_OUTPUT_MUTE)
1458 m_pConsole->output(KVI_OUT_VERBOSE, __tr2qs("Using server specific real name (%Q)"), &(szTmpName));
1459 }
1460 else
1461 {
1462 szTmpName = pNet->realName().trimmed();
1463 if(!szTmpName.isEmpty())
1464 {
1465 if(!_OUTPUT_MUTE)
1466 m_pConsole->output(KVI_OUT_VERBOSE, __tr2qs("Using network specific real name (%Q)"), &(szTmpName));
1467 }
1468 else
1469 {
1470 szTmpName = KVI_OPTION_STRING(KviOption_stringRealname);
1471 }
1472 }
1473
1474 useRealName(szTmpName);
1475
1476 // Pass
1477 if(pServer->reconnectInfo())
1478 szTmpPass = pServer->reconnectInfo()->m_szPass;
1479
1480 if(!szTmpPass.isEmpty())
1481 {
1482 if(!_OUTPUT_MUTE)
1483 {
1484 QString szHidden = QString(szTmpPass.length(), QChar('*'));
1485 m_pConsole->output(KVI_OUT_VERBOSE, __tr2qs("Using connection specific password (%Q)"), &(szHidden));
1486 }
1487 }
1488 else
1489 {
1490 szTmpPass = pServer->password().trimmed();
1491 if(!szTmpPass.isEmpty())
1492 {
1493 if(!_OUTPUT_MUTE)
1494 {
1495 QString szHidden = QString(szTmpPass.length(), QChar('*'));
1496 m_pConsole->output(KVI_OUT_VERBOSE, __tr2qs("Using server specific password (%Q)"), &(szHidden));
1497 }
1498 }
1499 else
1500 {
1501 szTmpPass = pNet->password().trimmed();
1502 if(!szTmpPass.isEmpty())
1503 {
1504 if(!_OUTPUT_MUTE)
1505 {
1506 QString szHidden = QString(szTmpPass.length(), QChar('*'));
1507 m_pConsole->output(KVI_OUT_VERBOSE, __tr2qs("Using network specific password (%Q)"), &(szHidden));
1508 }
1509 }
1510 }
1511 }
1512
1513 m_pUserInfo->setPassword(szTmpPass);
1514
1515 // Check for identity profiles
1516
1517 // FIXME: Shouldn't this be inside the code above ?
1518 KviIdentityProfileSet * pSet = KviIdentityProfileSet::instance();
1519 bool bProfilesEnabled = pSet ? (pSet->isEnabled() && !pSet->isEmpty()) : false;
1520 if(bProfilesEnabled)
1521 useProfileData(pSet, pNet->name());
1522
1523 // FIXME: The server's encoding!
1524 setupTextCodec();
1525 QByteArray szNick = encodeText(m_pUserInfo->nickName()); // never empty
1526 QByteArray szUser = encodeText(m_pUserInfo->userName()); // never empty
1527 QByteArray szReal = encodeText(m_pUserInfo->realName()); // may be empty
1528 QByteArray szPass = encodeText(m_pUserInfo->password()); // may be empty
1529
1530 if(!_OUTPUT_MUTE)
1531 m_pConsole->output(KVI_OUT_SYSTEMMESSAGE, __tr2qs("Logging in as %Q!%Q :%Q"),
1532 &(m_pUserInfo->nickName()), &(m_pUserInfo->userName()), &(m_pUserInfo->realName()));
1533
1534 // spity, 27.03.2005: follow the RFC2812 suggested order for connection registration
1535 // first the PASS, then NICK and then USER
1536
1537 // The pass ?
1538 if(!m_pUserInfo->password().isEmpty())
1539 {
1540 if(!sendFmtData("PASS :%s", szPass.data()))
1541 {
1542 // disconnected in the meantime
1543 return;
1544 }
1545 }
1546
1547 // disconnected :(
1548 if(!sendFmtData("NICK %s", szNick.data()))
1549 return;
1550
1551 unsigned int iGenderAvatarTag = 0;
1552
1553 if(KVI_OPTION_BOOL(KviOption_boolPrependGenderInfoToRealname) && !KVI_OPTION_STRING(KviOption_stringCtcpUserInfoGender).isEmpty())
1554 {
1555 if(KVI_OPTION_STRING(KviOption_stringCtcpUserInfoGender).startsWith("m", Qt::CaseInsensitive))
1556 iGenderAvatarTag |= 1;
1557 else if(KVI_OPTION_STRING(KviOption_stringCtcpUserInfoGender).startsWith("f", Qt::CaseInsensitive))
1558 iGenderAvatarTag |= 2;
1559 }
1560
1561 if(KVI_OPTION_BOOL(KviOption_boolPrependAvatarInfoToRealname) && !KVI_OPTION_STRING(KviOption_stringMyAvatar).isEmpty())
1562 {
1563 iGenderAvatarTag |= 4;
1564 }
1565
1566 if(KVI_OPTION_BOOL(KviOption_boolPrependNickColorInfoToRealname) && KVI_OPTION_BOOL(KviOption_boolUseSpecifiedSmartColorForOwnNick))
1567 {
1568 QString szTags;
1569 int iBack = KVI_OPTION_UINT(KviOption_uintUserIrcViewOwnBackground);
1570 if(iBack != KviControlCodes::Transparent)
1571 {
1572 szTags.sprintf("%c%d,%d%c",
1573 KviControlCodes::Color,
1574 KVI_OPTION_UINT(KviOption_uintUserIrcViewOwnForeground),
1575 iBack,
1576 KviControlCodes::Reset);
1577 }
1578 szReal.prepend(szTags.toUtf8());
1579 }
1580
1581 if(iGenderAvatarTag != 0)
1582 {
1583 QString szTags;
1584 szTags.sprintf("%c%d%c",
1585 KviControlCodes::Color,
1586 iGenderAvatarTag,
1587 KviControlCodes::Reset);
1588 szReal.prepend(szTags.toUtf8());
1589 }
1590
1591 // disconnected in the meantime!
1592 if(!sendFmtData("USER %s 0 %s :%s", szUser.data(), pServer->hostName().toUtf8().data(), szReal.data()))
1593 return;
1594
1595 // permanent info in the user database
1596 m_pConsole->notifyListView()->join(m_pUserInfo->nickName(), "*", "*");
1597
1598 // set own avatar if we have it
1599 KviIrcUserEntry * e = userDataBase()->find(userInfo()->nickName());
1600
1601 // our nick should be there!
1602 if(e && !e->avatar())
1603 {
1604 KviAvatar * av = m_pConsole->defaultAvatarFromOptions();
1605 if(av)
1606 {
1607 e->setAvatar(av);
1608 m_pConsole->notifyListView()->avatarChanged(userInfo()->nickName());
1609 }
1610 } // else buuug, couldn't find our nick
1611
1612 if(KVI_OPTION_STRING(KviOption_stringCtcpUserInfoGender).startsWith("m", Qt::CaseInsensitive))
1613 e->setGender(KviIrcUserEntry::Male);
1614 else if(KVI_OPTION_STRING(KviOption_stringCtcpUserInfoGender).startsWith("f", Qt::CaseInsensitive))
1615 e->setGender(KviIrcUserEntry::Female);
1616
1617 // on connect stuff ?
1618
1619 QString szTmp = pNet->onConnectCommand().trimmed();
1620 if(!szTmp.isEmpty())
1621 {
1622 if(_OUTPUT_VERBOSE)
1623 m_pConsole->output(KVI_OUT_VERBOSE, __tr2qs("Executing scheduled network specific \"on connect\" commands"));
1624 KviKvsScript::run(szTmp, m_pConsole);
1625 }
1626
1627 szTmp = pServer->onConnectCommand().trimmed();
1628 if(!szTmp.isEmpty())
1629 {
1630 if(_OUTPUT_VERBOSE)
1631 m_pConsole->output(KVI_OUT_VERBOSE, __tr2qs("Executing scheduled server specific \"on connect\" commands"));
1632 KviKvsScript::run(szTmp, m_pConsole);
1633 }
1634
1635 szTmp = m_pUserIdentity->onConnectCommand().trimmed();
1636 if(!szTmp.isEmpty())
1637 {
1638 if(_OUTPUT_VERBOSE)
1639 m_pConsole->output(KVI_OUT_VERBOSE, __tr2qs("Executing scheduled identity specific \"on connect\" commands"));
1640 KviKvsScript::run(szTmp, m_pConsole);
1641 }
1642
1643 // and wait for the server to agree...
1644 }
1645
nickChange(const QString & szNewNick)1646 void KviIrcConnection::nickChange(const QString & szNewNick)
1647 {
1648 // FIXME: should the new nickname be decoded in some way ?
1649 m_pConsole->notifyListView()->nickChange(m_pUserInfo->nickName(), szNewNick);
1650 m_pUserInfo->setNickName(szNewNick);
1651 m_pConsole->updateCaption();
1652 g_pMainWindow->childConnectionNickNameChange(this);
1653 emit nickNameChanged();
1654 g_pApp->addRecentNickname(szNewNick);
1655 }
1656
changeUserMode(char cMode,bool bSet)1657 bool KviIrcConnection::changeUserMode(char cMode, bool bSet)
1658 {
1659 if(bSet)
1660 {
1661 if(m_pUserInfo->hasUserMode(cMode))
1662 return false;
1663 m_pUserInfo->addUserMode(cMode);
1664 }
1665 else
1666 {
1667 if(!m_pUserInfo->hasUserMode(cMode))
1668 return false;
1669 m_pUserInfo->removeUserMode(cMode);
1670 }
1671 m_pConsole->updateCaption();
1672 g_pMainWindow->childConnectionUserModeChange(this);
1673 emit userModeChanged();
1674 return true;
1675 }
1676
gatherChannelAndPasswordPairs(std::vector<std::pair<QString,QString>> & lChannelsAndPasses)1677 void KviIrcConnection::gatherChannelAndPasswordPairs(std::vector<std::pair<QString, QString>> & lChannelsAndPasses)
1678 {
1679 for(const auto & c : m_pChannelList)
1680 lChannelsAndPasses.emplace_back(
1681 c->windowName(),
1682 c->hasChannelMode('k') ? c->channelModeParam('k') : QString());
1683 }
1684
gatherQueryNames(QStringList & lQueryNames)1685 void KviIrcConnection::gatherQueryNames(QStringList & lQueryNames)
1686 {
1687 for(const auto & q : m_pQueryList)
1688 lQueryNames.append(q->target());
1689 }
1690
joinChannels(const std::vector<std::pair<QString,QString>> & lChannelsAndPasses)1691 void KviIrcConnection::joinChannels(const std::vector<std::pair<QString, QString>> & lChannelsAndPasses)
1692 {
1693 if(lChannelsAndPasses.empty())
1694 return;
1695
1696 // Sort the list so the channels with passwords come first
1697 std::vector<std::pair<QString, QString>> lSorted = lChannelsAndPasses;
1698
1699 std::sort(lSorted.begin(), lSorted.end(),
1700 [](const std::pair<QString, QString> & left,
1701 const std::pair<QString, QString> & right)
1702 {
1703 return left.second.count() > right.second.count();
1704 });
1705
1706 // We send the channel list in chunks to avoid overflowing the 510 character limit on the message.
1707 QString szChans, szPasses;
1708
1709 for(auto & oChanAndPass : lSorted)
1710 {
1711
1712 if(!szChans.isEmpty())
1713 szChans.append(',');
1714 szChans.append(oChanAndPass.first);
1715
1716 if(!oChanAndPass.second.isEmpty())
1717 {
1718 if(!szPasses.isEmpty())
1719 szPasses.append(',');
1720 szPasses.append(oChanAndPass.second);
1721 }
1722
1723 // empirical limit
1724 if((szChans.length() + szPasses.length()) > 450)
1725 {
1726 QString szCommand = szChans;
1727 if(!szPasses.isEmpty())
1728 {
1729 szCommand.append(' ');
1730 szCommand.append(szPasses);
1731 }
1732 sendFmtData("JOIN %s", encodeText(szCommand).data());
1733 szChans.clear();
1734 szPasses.clear();
1735 }
1736 }
1737
1738 QString szCommand = szChans;
1739 if(!szPasses.isEmpty())
1740 {
1741 szCommand.append(' ');
1742 szCommand.append(szPasses);
1743 }
1744 sendFmtData("JOIN %s", encodeText(szCommand).data());
1745 }
1746
loginComplete(const QString & szNickName)1747 void KviIrcConnection::loginComplete(const QString & szNickName)
1748 {
1749 if(context()->state() == KviIrcContext::Connected)
1750 return;
1751
1752 // Stop ignoring ERR_NOTREGISTERED errors. This is related to the initial CAP LS message.
1753 // Well.. we should already have stopped ignoring the errors as we should have received
1754 // the one we expected (because of the PING after CAP LS). Moreover the server shouldn't
1755 // be sending these messages after the login has been completed.
1756 // ...but to be on the safe side we just disable the special handling here.
1757 m_pStateData->setIgnoreOneYouHaveNotRegisteredError(false);
1758
1759 context()->loginComplete();
1760
1761 if(m_bIdentdAttached)
1762 {
1763 g_pMainWindow->executeInternalCommand(KVI_INTERNALCOMMAND_IDENT_STOP);
1764 m_bIdentdAttached = false;
1765 }
1766
1767 if(szNickName != m_pUserInfo->nickName())
1768 {
1769 m_pConsole->output(KVI_OUT_SYSTEMMESSAGE, __tr2qs("The server refused the suggested nickname (%s) and named you %s instead"), m_pUserInfo->nickName().toUtf8().data(), szNickName.toUtf8().data());
1770 m_pConsole->notifyListView()->nickChange(m_pUserInfo->nickName(), szNickName);
1771 m_pUserInfo->setNickName(szNickName);
1772 }
1773
1774 g_pApp->addRecentNickname(szNickName);
1775
1776 if(!KVS_TRIGGER_EVENT_0_HALTED(KviEvent_OnIRC, m_pConsole))
1777 m_pConsole->outputNoFmt(KVI_OUT_IRC, __tr2qs("Login operations complete, happy ircing!"));
1778
1779 resurrectDeadQueries();
1780
1781 // on connect stuff ?
1782 QString szTmp = target()->network()->onLoginCommand().trimmed();
1783 if(!szTmp.isEmpty())
1784 {
1785 if(_OUTPUT_VERBOSE)
1786 m_pConsole->output(KVI_OUT_VERBOSE, __tr2qs("Executing scheduled network specific \"on login\" commands"));
1787 KviKvsScript::run(szTmp, m_pConsole);
1788 }
1789
1790 szTmp = target()->server()->onLoginCommand().trimmed();
1791 if(!szTmp.isEmpty())
1792 {
1793 if(_OUTPUT_VERBOSE)
1794 m_pConsole->output(KVI_OUT_VERBOSE, __tr2qs("Executing scheduled server specific \"on login\" commands"));
1795 KviKvsScript::run(szTmp, m_pConsole);
1796 }
1797
1798 szTmp = m_pUserIdentity->onLoginCommand().trimmed();
1799 if(!szTmp.isEmpty())
1800 {
1801 if(_OUTPUT_VERBOSE)
1802 m_pConsole->output(KVI_OUT_VERBOSE, __tr2qs("Executing scheduled identity specific \"on login\" commands"));
1803 KviKvsScript::run(szTmp, m_pConsole);
1804 }
1805
1806 // Set the configured umode
1807 QString szModeStr = target()->server()->initUMode();
1808
1809 if(szModeStr.isEmpty())
1810 szModeStr = KVI_OPTION_STRING(KviOption_stringDefaultUserMode);
1811
1812 if(!szModeStr.isEmpty()) // may be still empty if there are no default modes, if so, don't send the MODE command at all
1813 {
1814 if(_OUTPUT_VERBOSE)
1815 m_pConsole->output(KVI_OUT_VERBOSE, __tr2qs("Setting configured user mode"));
1816 sendFmtData("MODE %s +%s", encodeText(m_pUserInfo->nickName()).data(), encodeText(szModeStr).data());
1817 }
1818
1819 delayedStartNotifyList();
1820 restartLagMeter();
1821
1822 if(KVI_OPTION_BOOL(KviOption_boolShowChannelsJoinOnIrc))
1823 g_pMainWindow->executeInternalCommand(KVI_INTERNALCOMMAND_CHANNELSJOIN_OPEN);
1824
1825 // join saved channels
1826
1827 // FIXME: It should be possible to delay the channel join process after identification
1828 // (or maybe just delay by a fixed amount ?)
1829 // (or maybe retry joining after a while ?)
1830 //
1831 // It's quite hard to figure out when the identification has taken place.
1832 // There is no standard for this purpose. We might look at a special NOTICE by NickServ
1833 // but the format is not well defined and we would need to make it user configurable.
1834 // Not very reliable, actually :/
1835
1836 QString szChannels, szProtectedChannels, szPasswords, szCurPass, szCurChan;
1837
1838 if(!(m_pStateData->commandToExecAfterConnect().isEmpty()))
1839 {
1840 KviCString tmp = m_pStateData->commandToExecAfterConnect();
1841 KviKvsScript::run(tmp.ptr(), m_pConsole);
1842 }
1843
1844 bool bJoinStdChannels = true;
1845
1846 if(target()->server()->reconnectInfo())
1847 {
1848 if(!target()->server()->reconnectInfo()->m_lJoinChannels.empty())
1849 {
1850 bJoinStdChannels = false;
1851 joinChannels(target()->server()->reconnectInfo()->m_lJoinChannels);
1852 }
1853
1854 KviQueryWindow * pQuery;
1855
1856 for(const auto & it : target()->server()->reconnectInfo()->m_lOpenQueries)
1857 {
1858 QString szNick = it;
1859 pQuery = findQuery(szNick);
1860 if(!pQuery)
1861 {
1862 pQuery = createQuery(szNick);
1863 QString szUser;
1864 QString szHost;
1865
1866 KviIrcUserDataBase * db = userDataBase();
1867 if(db)
1868 {
1869 KviIrcUserEntry * e = db->find(szNick);
1870 if(e)
1871 {
1872 szUser = e->user();
1873 szHost = e->host();
1874 }
1875 }
1876 pQuery->setTarget(szNick, szUser, szHost);
1877 }
1878 }
1879 target()->server()->clearReconnectInfo();
1880 }
1881
1882 if(bJoinStdChannels)
1883 {
1884 std::vector<std::pair<QString, QString>> lChansAndPass;
1885
1886 if(target()->network()->autoJoinChannelList())
1887 {
1888 if(_OUTPUT_VERBOSE)
1889 m_pConsole->output(KVI_OUT_VERBOSE, __tr2qs("Auto-joining network specific channels"));
1890
1891 QStringList * l = target()->network()->autoJoinChannelList();
1892 for(const auto & it : *l)
1893 {
1894 szCurPass = it.section(':', 1);
1895 szCurChan = it.section(':', 0, 0);
1896 if(szCurChan.isEmpty())
1897 continue;
1898 if(!m_pServerInfo->isSupportedChannelType(szCurChan[0]))
1899 szCurChan.prepend('#');
1900 lChansAndPass.emplace_back(szCurChan, szCurPass);
1901 }
1902 }
1903
1904 if(target()->server()->autoJoinChannelList())
1905 {
1906 if(_OUTPUT_VERBOSE)
1907 m_pConsole->output(KVI_OUT_VERBOSE, __tr2qs("Auto-joining server specific channels"));
1908
1909 QStringList * l = target()->server()->autoJoinChannelList();
1910 for(const auto & it : *l)
1911 {
1912 szCurPass = it.section(':', 1);
1913 szCurChan = it.section(':', 0, 0);
1914 if(szCurChan.isEmpty())
1915 continue;
1916 if(!m_pServerInfo->isSupportedChannelType(szCurChan[0]))
1917 szCurChan.prepend('#');
1918 lChansAndPass.emplace_back(szCurChan, szCurPass);
1919 }
1920 }
1921
1922 joinChannels(lChansAndPass);
1923 }
1924 }
1925
incomingMessage(const char * pcMessage)1926 void KviIrcConnection::incomingMessage(const char * pcMessage)
1927 {
1928 // A message has arrived from the current server
1929 // First of all, notify the monitors
1930 for(auto & m : context()->monitorList())
1931 {
1932 if(m->incomingMessage(pcMessage))
1933 return;
1934 }
1935 // set the last message time
1936 m_pStatistics->setLastMessageTime(kvi_unixTime());
1937 // and pass it to the server parser for processing
1938 g_pServerParser->parseMessage(pcMessage, this);
1939 }
1940
incomingMessageNoFilter(const char * pcMessage)1941 void KviIrcConnection::incomingMessageNoFilter(const char * pcMessage)
1942 {
1943 // set the last message time
1944 m_pStatistics->setLastMessageTime(kvi_unixTime());
1945 // and pass it to the server parser for processing
1946 g_pServerParser->parseMessage(pcMessage, this);
1947 }
1948
heartbeat(kvi_time_t tNow)1949 void KviIrcConnection::heartbeat(kvi_time_t tNow)
1950 {
1951 if(m_eState == Connected)
1952 {
1953 if(KVI_OPTION_BOOL(KviOption_boolEnableAwayListUpdates))
1954 {
1955 if(m_pStateData->enabledCaps().contains("away-notify"))
1956 {
1957 // no need to send WHO requests if the away-notify extension is enabled
1958 return;
1959 }
1960 // update the channel WHO lists (fixes users away state)
1961 // first of all, we send our request not more often than every 50 secs
1962 if((tNow - stateData()->lastSentChannelWhoRequest()) > 50)
1963 {
1964 // we also make sure that the last sent request is older than
1965 // the last received reply
1966 if(stateData()->lastSentChannelWhoRequest() <= stateData()->lastReceivedChannelWhoReply())
1967 {
1968 // find the channel that has the older list now
1969 kvi_time_t tOldest = tNow;
1970 KviChannelWindow * pOldest = nullptr;
1971 for(auto & pChan : m_pChannelList)
1972 {
1973 if(pChan->lastReceivedWhoReply() < tOldest)
1974 {
1975 pOldest = pChan;
1976 tOldest = pChan->lastReceivedWhoReply();
1977 }
1978 }
1979 // if the oldest chan who list is older than 150 secs, update it
1980 if((tNow - tOldest) > 150)
1981 {
1982 // ok, sent the request for this channel
1983 stateData()->setLastSentChannelWhoRequest(tNow);
1984 QString szChanName = encodeText(pOldest->windowName()).data();
1985 if(_OUTPUT_PARANOIC)
1986 console()->output(KVI_OUT_VERBOSE, __tr2qs("Updating away state for channel %Q"), &szChanName);
1987 if(lagMeter())
1988 {
1989 KviCString tmp;
1990 if(serverInfo()->supportsWhox())
1991 tmp.append(KviCString::Format, "WHO %s %acdfhlnrsu", encodeText(pOldest->windowName()).data());
1992 else
1993 tmp.append(KviCString::Format, "WHO %s", encodeText(pOldest->windowName()).data());
1994 lagMeter()->lagCheckRegister(tmp.ptr(), 70);
1995 }
1996 pOldest->setSentSyncWhoRequest();
1997 if(serverInfo()->supportsWhox())
1998 {
1999 if(!sendFmtData("WHO %s %acdfhlnrsu", encodeText(pOldest->windowName()).data()))
2000 return;
2001 }
2002 else
2003 {
2004 if(!sendFmtData("WHO %s", encodeText(pOldest->windowName()).data()))
2005 return;
2006 }
2007 }
2008 }
2009 }
2010 }
2011 }
2012 }
2013
currentServerName() const2014 const QString & KviIrcConnection::currentServerName() const
2015 {
2016 return serverInfo()->name();
2017 }
2018
currentNickName() const2019 const QString & KviIrcConnection::currentNickName() const
2020 {
2021 return userInfo()->nickName();
2022 }
2023
currentUserName() const2024 const QString & KviIrcConnection::currentUserName() const
2025 {
2026 return userInfo()->userName();
2027 }
2028