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