1 //=============================================================================
2 //
3 //   File : DccChatWindow.cpp
4 //   Creation date : Tue Sep 20 09 2000 15:13:13 by Szymon Stefanek
5 //
6 //   This file is part of the KVIrc IRC client distribution
7 //   Copyright (C) 2000-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 "DccChatWindow.h"
26 #include "DccMarshal.h"
27 #include "DccBroker.h"
28 
29 #ifdef COMPILE_ON_WINDOWS
30 // Ugly Windoze compiler...
31 #include "DccDialog.h"
32 #endif
33 
34 #include "kvi_debug.h"
35 #include "KviOptions.h"
36 #include "KviInput.h"
37 #include "KviIrcView.h"
38 #include "KviIconManager.h"
39 #include "KviLocale.h"
40 #include "kvi_out.h"
41 #include "KviNetUtils.h"
42 #include "KviConsoleWindow.h"
43 #include "KviMainWindow.h"
44 #include "KviMemory.h"
45 #include "KviThread.h"
46 #include "KviIrcSocket.h"
47 #include "kvi_settings.h"
48 #include "kvi_socket.h"
49 #include "KviApplication.h"
50 #include "KviIrcConnection.h"
51 #include "KviIrcConnectionUserInfo.h"
52 #include "KviKvsEventTriggers.h"
53 #include "KviControlCodes.h"
54 #include "KviTalVBox.h"
55 
56 #include <QEvent>
57 #include <QResizeEvent>
58 #include <QByteArray>
59 
60 #ifdef COMPILE_CRYPT_SUPPORT
61 #include "KviCryptEngine.h"
62 #include "KviCryptController.h"
63 #endif
64 
65 #ifdef COMPILE_SSL_SUPPORT
66 #include "KviSSLMaster.h"
67 #endif
68 
69 extern DccBroker * g_pDccBroker;
70 
71 //
72 // WINDOW
73 //
74 
DccChatWindow(DccDescriptor * dcc,const char * name)75 DccChatWindow::DccChatWindow(DccDescriptor * dcc, const char * name)
76     : DccWindow(KviWindow::DccChat, name, dcc)
77 {
78 	m_pButtonBox = new KviTalHBox(this);
79 
80 	m_pLabel = new KviThemedLabel(m_pButtonBox, this, "dcc_chat_label");
81 	m_pLabel->setText(name);
82 	m_pButtonBox->setStretchFactor(m_pLabel, 1);
83 
84 	m_pButtonContainer = new KviTalHBox(m_pButtonBox);
85 	createTextEncodingButton(m_pButtonContainer);
86 
87 #ifdef COMPILE_CRYPT_SUPPORT
88 	createCryptControllerButton(m_pButtonContainer);
89 #endif
90 
91 	m_pSplitter = new KviTalSplitter(Qt::Horizontal, this);
92 	m_pSplitter->setObjectName("dcc_chat_splitter");
93 	m_pSplitter->setChildrenCollapsible(false);
94 
95 	m_pIrcView = new KviIrcView(m_pSplitter, this);
96 	connect(m_pIrcView, SIGNAL(rightClicked()), this, SLOT(textViewRightClicked()));
97 	m_pInput = new KviInput(this);
98 
99 	//setFocusHandler(m_pInput,this);
100 
101 	m_pSlaveThread = nullptr;
102 
103 	if(KVI_OPTION_BOOL(KviOption_boolAutoLogDccChat))
104 		m_pIrcView->startLogging();
105 
106 	m_pMarshal = new DccMarshal(this);
107 	connect(m_pMarshal, SIGNAL(error(KviError::Code)), this, SLOT(handleMarshalError(KviError::Code)));
108 	connect(m_pMarshal, SIGNAL(connected()), this, SLOT(connected()));
109 	connect(m_pMarshal, SIGNAL(inProgress()), this, SLOT(connectionInProgress()));
110 #ifdef COMPILE_SSL_SUPPORT
111 	connect(m_pMarshal, SIGNAL(startingSSLHandshake()), this, SLOT(startingSSLHandshake()));
112 	connect(m_pMarshal, SIGNAL(sslError(const char *)), this, SLOT(sslError(const char *)));
113 #endif
114 
115 	m_pSlaveThread = nullptr;
116 
117 	startConnection();
118 }
119 
~DccChatWindow()120 DccChatWindow::~DccChatWindow()
121 {
122 	g_pDccBroker->unregisterDccWindow(this);
123 	if(m_pSlaveThread)
124 	{
125 		m_pSlaveThread->terminate();
126 		delete m_pSlaveThread;
127 		m_pSlaveThread = nullptr;
128 	}
129 	KviThreadManager::killPendingEvents(this);
130 }
131 
textViewRightClicked()132 void DccChatWindow::textViewRightClicked()
133 {
134 	KVS_TRIGGER_EVENT_1(KviEvent_OnDCCChatPopupRequest, this, m_pDescriptor->idString());
135 }
136 
triggerCreationEvents()137 void DccChatWindow::triggerCreationEvents()
138 {
139 	KVS_TRIGGER_EVENT_1(KviEvent_OnDCCChatWindowCreated, this, m_pDescriptor->idString());
140 }
141 
triggerDestructionEvents()142 void DccChatWindow::triggerDestructionEvents()
143 {
144 	KVS_TRIGGER_EVENT_1(KviEvent_OnDCCChatWindowClosing, this, m_pDescriptor->idString());
145 }
146 
startConnection()147 void DccChatWindow::startConnection()
148 {
149 	if(!(m_pDescriptor->bActive))
150 	{
151 		// PASSIVE CONNECTION
152 		output(KVI_OUT_DCCMSG, __tr2qs_ctx("Attempting a passive DCC %s connection", "dcc"), m_pDescriptor->szType.toUtf8().data());
153 #ifdef COMPILE_SSL_SUPPORT
154 		KviError::Code eError = m_pMarshal->dccListen(m_pDescriptor->szListenIp, m_pDescriptor->szListenPort, m_pDescriptor->bDoTimeout, m_pDescriptor->bIsSSL);
155 #else
156 		KviError::Code eError = m_pMarshal->dccListen(m_pDescriptor->szListenIp, m_pDescriptor->szListenPort, m_pDescriptor->bDoTimeout);
157 #endif
158 		if(eError != KviError::Success)
159 			handleMarshalError(eError);
160 	}
161 	else
162 	{
163 		// ACTIVE CONNECTION
164 		output(KVI_OUT_DCCMSG, __tr2qs_ctx("Attempting an active DCC %s connection", "dcc"), m_pDescriptor->szType.toUtf8().data());
165 #ifdef COMPILE_SSL_SUPPORT
166 		KviError::Code eError = m_pMarshal->dccConnect(m_pDescriptor->szIp.toUtf8().data(), m_pDescriptor->szPort.toUtf8().data(), m_pDescriptor->bDoTimeout, m_pDescriptor->bIsSSL);
167 #else
168 		KviError::Code eError = m_pMarshal->dccConnect(m_pDescriptor->szIp.toUtf8().data(), m_pDescriptor->szPort.toUtf8().data(), m_pDescriptor->bDoTimeout);
169 #endif
170 		if(eError != KviError::Success)
171 			handleMarshalError(eError);
172 	}
173 }
174 
connectionInProgress()175 void DccChatWindow::connectionInProgress()
176 {
177 	if(m_pDescriptor->bActive)
178 	{
179 		output(KVI_OUT_DCCMSG, __tr2qs_ctx("Contacting host %Q on port %Q", "dcc"), &(m_pDescriptor->szIp), &(m_pDescriptor->szPort));
180 	}
181 	else
182 	{
183 		output(KVI_OUT_DCCMSG, __tr2qs_ctx("Listening on interface %Q port %Q", "dcc"), &(m_pMarshal->localIp()), &(m_pMarshal->localPort()));
184 
185 		if(m_pDescriptor->bSendRequest)
186 		{
187 			KviCString ip;
188 			if(!m_pDescriptor->szFakeIp.isEmpty())
189 			{
190 				ip = m_pDescriptor->szFakeIp;
191 			}
192 			else
193 			{
194 				ip = m_pDescriptor->szListenIp;
195 
196 				if(KVI_OPTION_BOOL(KviOption_boolDccGuessIpFromServerWhenLocalIsUnroutable))
197 				{
198 					if(!kvi_isRoutableIpString(ip.ptr()))
199 					{
200 						// try to get the IP that the IRC server can see
201 						if(m_pDescriptor->console())
202 						{
203 							KviCString tmp = m_pDescriptor->console()->connection() ? m_pDescriptor->console()->connection()->userInfo()->hostIp().toUtf8().data() : "";
204 							if(tmp.hasData())
205 							{
206 								ip = tmp;
207 								output(KVI_OUT_DCCMSG, __tr2qs_ctx("The local IP address is private, determining from IRC server: %s", "dcc"), ip.ptr());
208 							}
209 							else
210 							{
211 								output(KVI_OUT_DCCMSG, __tr2qs_ctx("The local IP address is private, but unable to determine it from the IRC server", "dcc"));
212 							}
213 						}
214 						else
215 						{
216 							output(KVI_OUT_DCCMSG, __tr2qs_ctx("The local IP address is private, but have no IRC server to determine it from", "dcc"));
217 						}
218 					}
219 				}
220 			}
221 
222 			QString port = !m_pDescriptor->szFakePort.isEmpty() ? m_pDescriptor->szFakePort : QString(m_pMarshal->localPort());
223 
224 			//FIXME: #warning "OPTION FOR SENDING 127.0.0.1 and so on (not an unsigned number)"
225 			struct in_addr a;
226 			if(KviNetUtils::stringIpToBinaryIp(ip.ptr(), &a))
227 				ip.setNum(htonl(a.s_addr));
228 
229 			QString szReq = QString("PRIVMSG %1 :%2DCC %3 chat %4 %5").arg(m_pDescriptor->szNick, QChar(0x01), m_pDescriptor->szType, ip.ptr(), port);
230 
231 			if(m_pDescriptor->isZeroPortRequest())
232 			{
233 				szReq.append(" ");
234 				szReq += m_pDescriptor->zeroPortRequestTag();
235 			}
236 			szReq.append((char)(0x01));
237 
238 			m_pDescriptor->console()->connection()->sendData(m_pDescriptor->console()->connection()->encodeText(szReq).data());
239 			output(KVI_OUT_DCCMSG, __tr2qs_ctx("Sent DCC %Q request to %Q, waiting for the remote client to connect...", "dcc"),
240 			    &(m_pDescriptor->szType), &(m_pDescriptor->szNick));
241 			//qDebug(m_pDescriptor->szNick);
242 		}
243 		else
244 			output(KVI_OUT_DCCMSG, __tr2qs_ctx("DCC %Q request not sent, awaiting manual connection", "dcc"), &(m_pDescriptor->szType));
245 	}
246 	KVS_TRIGGER_EVENT_1(KviEvent_OnDCCChatConnectionInProgress, this, m_pDescriptor->idString());
247 }
248 
startingSSLHandshake()249 void DccChatWindow::startingSSLHandshake()
250 {
251 #ifdef COMPILE_SSL_SUPPORT
252 	outputNoFmt(KVI_OUT_SSL, __tr2qs_ctx("Low-level transport connection established", "dcc"));
253 	outputNoFmt(KVI_OUT_SSL, __tr2qs_ctx("Starting Secure Socket Layer handshake", "dcc"));
254 #endif
255 }
256 
sslError(const char * msg)257 void DccChatWindow::sslError(const char * msg)
258 {
259 #ifdef COMPILE_SSL_SUPPORT
260 	if(!KVS_TRIGGER_EVENT_2_HALTED(KviEvent_OnDCCChatError, this, QString(msg), m_pDescriptor->idString()))
261 		output(KVI_OUT_DCCERROR, __tr2qs_ctx("[SSL ERROR]: %s", "dcc"), msg);
262 #endif
263 }
264 
target()265 const QString & DccChatWindow::target()
266 {
267 	// This may change on the fly...
268 	m_szTarget = m_pDescriptor->szNick;
269 	m_szTarget += "@";
270 	m_szTarget += m_pDescriptor->szIp;
271 	m_szTarget += ":";
272 	m_szTarget += m_pDescriptor->szPort;
273 	return m_szTarget;
274 }
275 
fillCaptionBuffers()276 void DccChatWindow::fillCaptionBuffers()
277 {
278 	QString tmp = QString("DCC %1 %2@%3:%4").arg(
279 #ifdef COMPILE_SSL_SUPPORT
280 	    m_pDescriptor->bIsSSL ? "SChat" : "Chat",
281 #else
282 	    "Chat",
283 #endif
284 	    m_pDescriptor->szNick, m_pDescriptor->szIp, m_pDescriptor->szPort);
285 
286 	m_szPlainTextCaption = tmp;
287 }
288 
myIconPtr()289 QPixmap * DccChatWindow::myIconPtr()
290 {
291 	return g_pIconManager->getSmallIcon(KviIconManager::DccMsg);
292 }
293 
getBaseLogFileName(QString & buffer)294 void DccChatWindow::getBaseLogFileName(QString & buffer)
295 {
296 	buffer.sprintf("%s_%s_%s", m_pDescriptor->szNick.toUtf8().data(), m_pDescriptor->szIp.toUtf8().data(), m_pDescriptor->szPort.toUtf8().data());
297 }
298 
ownMessage(const QString & text,bool bUserFeedback)299 void DccChatWindow::ownMessage(const QString & text, bool bUserFeedback)
300 {
301 	if(!m_pSlaveThread)
302 	{
303 		output(KVI_OUT_SYSTEMWARNING, __tr2qs_ctx("Can't send data: no active connection", "dcc"));
304 		return;
305 	}
306 
307 	QByteArray szData = encodeText(text);
308 	const char * d = szData.data();
309 	if(!d)
310 		return;
311 
312 #ifdef COMPILE_CRYPT_SUPPORT
313 	if(cryptSessionInfo())
314 	{
315 		if(cryptSessionInfo()->m_bDoEncrypt)
316 		{
317 			if(*d != KviControlCodes::CryptEscape)
318 			{
319 				KviCString encrypted;
320 				cryptSessionInfo()->m_pEngine->setMaxEncryptLen(-1);
321 				switch(cryptSessionInfo()->m_pEngine->encrypt(d, encrypted))
322 				{
323 					case KviCryptEngine::Encrypted:
324 					{
325 						KviCString buf(KviCString::Format, "%s\r\n", encrypted.ptr());
326 						m_pSlaveThread->sendRawData(buf.ptr(), buf.len());
327 						if(bUserFeedback)
328 							g_pMainWindow->firstConsole()->outputPrivmsg(this, KVI_OUT_OWNPRIVMSGCRYPTED,
329 							    m_pDescriptor->szLocalNick.toUtf8().data(), m_pDescriptor->szLocalUser.toUtf8().data(),
330 							    m_pDescriptor->szLocalHost.toUtf8().data(), text, KviConsoleWindow::NoNotifications);
331 					}
332 					break;
333 					case KviCryptEngine::Encoded:
334 					{
335 						KviCString buf(KviCString::Format, "%s\r\n", encrypted.ptr());
336 						m_pSlaveThread->sendRawData(buf.ptr(), buf.len());
337 						if(bUserFeedback)
338 						{
339 							QString encr = decodeText(encrypted.ptr());
340 							g_pMainWindow->firstConsole()->outputPrivmsg(this, KVI_OUT_OWNPRIVMSG,
341 							    m_pDescriptor->szLocalNick.toUtf8().data(), m_pDescriptor->szLocalUser.toUtf8().data(),
342 							    m_pDescriptor->szLocalHost.toUtf8().data(), encr, KviConsoleWindow::NoNotifications);
343 						}
344 					}
345 					break;
346 					default: // also case KviCryptEngine::EncryptError
347 					{
348 						QString szErr = cryptSessionInfo()->m_pEngine->lastError();
349 						output(KVI_OUT_SYSTEMERROR,
350 						    __tr2qs_ctx("The encryption engine was not able to encrypt the current message (%Q): %Q, no data was sent to the remote end", "dcc"),
351 						    &text, &szErr);
352 					}
353 					break;
354 				}
355 				return;
356 			}
357 			else
358 			{
359 				d++; //eat the escape code
360 				KviCString buf(KviCString::Format, "%s\r\n", d);
361 				QString tmp = text.right(text.length() - 1);
362 				m_pSlaveThread->sendRawData(buf.ptr(), buf.len());
363 
364 				if(bUserFeedback)
365 					g_pMainWindow->firstConsole()->outputPrivmsg(this, KVI_OUT_OWNPRIVMSG,
366 					    m_pDescriptor->szLocalNick.toUtf8().data(), m_pDescriptor->szLocalUser.toUtf8().data(),
367 					    m_pDescriptor->szLocalHost.toUtf8().data(), tmp, KviConsoleWindow::NoNotifications);
368 				return;
369 			}
370 		}
371 	}
372 #endif
373 	KviCString buf(KviCString::Format, "%s\r\n", d);
374 	m_pSlaveThread->sendRawData(buf.ptr(), buf.len());
375 
376 	if(bUserFeedback)
377 		g_pMainWindow->firstConsole()->outputPrivmsg(this, KVI_OUT_OWNPRIVMSG,
378 		    m_pDescriptor->szLocalNick.toUtf8().data(), m_pDescriptor->szLocalUser.toUtf8().data(),
379 		    m_pDescriptor->szLocalHost.toUtf8().data(), text, KviConsoleWindow::NoNotifications);
380 }
381 
localNick()382 const QString & DccChatWindow::localNick()
383 {
384 	// FIXME: This is just a complete HACK
385 	m_szLocalNick = m_pDescriptor->szLocalNick;
386 	return m_szLocalNick;
387 }
388 
ownAction(const QString & text)389 void DccChatWindow::ownAction(const QString & text)
390 {
391 	if(m_pSlaveThread)
392 	{
393 		QString szTmpBuffer;
394 		//see bug ticket #220
395 		if(KVI_OPTION_BOOL(KviOption_boolStripMircColorsInUserMessages))
396 		{
397 			szTmpBuffer = KviControlCodes::stripControlBytes(text);
398 		}
399 		else
400 		{
401 			szTmpBuffer = text;
402 		}
403 
404 		QByteArray szData = encodeText(szTmpBuffer);
405 		const char * d = szData.data();
406 		if(!d)
407 			return;
408 		KviCString buf(KviCString::Format, "%cACTION %s%c\r\n", 0x01, d, 0x01);
409 		m_pSlaveThread->sendRawData(buf.ptr(), buf.len());
410 		output(KVI_OUT_OWNACTION, "%Q %Q", &(m_pDescriptor->szLocalNick), &szTmpBuffer);
411 	}
412 	else
413 	{
414 		output(KVI_OUT_SYSTEMWARNING, __tr2qs_ctx("Can't send data: no active connection", "dcc"));
415 	}
416 }
417 
event(QEvent * e)418 bool DccChatWindow::event(QEvent * e)
419 {
420 	if(e->type() == KVI_THREAD_EVENT)
421 	{
422 		switch(((KviThreadEvent *)e)->id())
423 		{
424 			case KVI_DCC_THREAD_EVENT_ERROR:
425 			{
426 				KviError::Code * pError = ((KviThreadDataEvent<KviError::Code> *)e)->getData();
427 				QString szErr = KviError::getDescription(*pError);
428 				if(!KVS_TRIGGER_EVENT_2_HALTED(KviEvent_OnDCCChatError, this, szErr, m_pDescriptor->idString()))
429 					output(KVI_OUT_DCCERROR, __tr2qs_ctx("ERROR: %Q", "dcc"), &szErr);
430 				KVS_TRIGGER_EVENT_1(KviEvent_OnDCCChatDisconnected, this, m_pDescriptor->idString());
431 				delete pError;
432 				return true;
433 			}
434 			break;
435 			case KVI_DCC_THREAD_EVENT_DATA:
436 			{
437 				KviCString * encoded = ((KviThreadDataEvent<KviCString> *)e)->getData();
438 				KviCString d = KviCString(decodeText(encoded->ptr()));
439 				if(d.firstCharIs(0x01))
440 				{
441 					d.cutLeft(1);
442 					if(d.lastCharIs(0x01))
443 						d.cutRight(1);
444 					if(kvi_strEqualCIN("ACTION", d.ptr(), 6))
445 						d.cutLeft(6);
446 					d.stripLeftWhiteSpace();
447 					output(KVI_OUT_ACTION, "%Q %s", &(m_pDescriptor->szNick), d.ptr());
448 					if(!hasAttention(KviWindow::MainWindowIsVisible))
449 					{
450 						if(KVI_OPTION_BOOL(KviOption_boolFlashDccChatWindowOnNewMessages))
451 						{
452 							demandAttention();
453 						}
454 						if(KVI_OPTION_BOOL(KviOption_boolPopupNotifierOnNewDccChatMessages))
455 						{
456 							QString szMsg = "<b>";
457 							szMsg += m_pDescriptor->szNick;
458 							szMsg += "</b> ";
459 							szMsg += KviQString::toHtmlEscaped(QString(d.ptr()));
460 							//qDebug("KviIrcServerParser_ctcp.cpp:975 debug: %s",szMsg.data());
461 							g_pApp->notifierMessage(this, KVI_OPTION_MSGTYPE(KVI_OUT_ACTION).pixId(), szMsg, KVI_OPTION_UINT(KviOption_uintNotifierAutoHideTime));
462 						}
463 					}
464 				}
465 				else
466 				{
467 
468 #ifdef COMPILE_CRYPT_SUPPORT
469 					if(KviCryptSessionInfo * cinf = cryptSessionInfo())
470 					{
471 						if(cinf->m_bDoDecrypt)
472 						{
473 							KviCString decryptedStuff;
474 							switch(cinf->m_pEngine->decrypt(d.ptr(), decryptedStuff))
475 							{
476 								case KviCryptEngine::DecryptOkWasEncrypted:
477 								case KviCryptEngine::DecryptOkWasEncoded:
478 								case KviCryptEngine::DecryptOkWasPlainText:
479 									if(!KVS_TRIGGER_EVENT_2_HALTED(KviEvent_OnDCCChatMessage, this, QString(decryptedStuff.ptr()), m_pDescriptor->idString()))
480 									{
481 										g_pMainWindow->firstConsole()->outputPrivmsg(this, KVI_OUT_DCCCHATMSG,
482 										    m_pDescriptor->szNick.toUtf8().data(), m_pDescriptor->szUser.toUtf8().data(),
483 										    m_pDescriptor->szHost.toUtf8().data(), decryptedStuff.ptr());
484 									}
485 									delete encoded;
486 									return true;
487 									break;
488 
489 								default: // also case KviCryptEngine::DecryptError
490 								{
491 									QString szErr = cinf->m_pEngine->lastError();
492 									output(KVI_OUT_SYSTEMERROR,
493 									    __tr2qs_ctx("The following message appears to be encrypted, but the encryption engine failed to decode it: %Q", "dcc"),
494 									    &szErr);
495 								}
496 								break;
497 							}
498 						}
499 					}
500 					else
501 					{
502 #endif
503 						// FIXME!
504 						if(!KVS_TRIGGER_EVENT_2_HALTED(KviEvent_OnDCCChatMessage, this, QString(d.ptr()), m_pDescriptor->idString()))
505 						{
506 							g_pMainWindow->firstConsole()->outputPrivmsg(this, KVI_OUT_DCCCHATMSG,
507 							    m_pDescriptor->szNick.toUtf8().data(), m_pDescriptor->szUser.toUtf8().data(),
508 							    m_pDescriptor->szHost.toUtf8().data(), d.ptr());
509 							if(!hasAttention(KviWindow::MainWindowIsVisible))
510 							{
511 								if(KVI_OPTION_BOOL(KviOption_boolFlashDccChatWindowOnNewMessages))
512 								{
513 									demandAttention();
514 								}
515 								if(KVI_OPTION_BOOL(KviOption_boolPopupNotifierOnNewDccChatMessages))
516 								{
517 									QString szMsg = KviQString::toHtmlEscaped(QString(d.ptr()));
518 									g_pApp->notifierMessage(this, KviIconManager::DccChatMsg, szMsg, KVI_OPTION_UINT(KviOption_uintNotifierAutoHideTime));
519 								}
520 							}
521 						}
522 #ifdef COMPILE_CRYPT_SUPPORT
523 					}
524 #endif
525 				}
526 				delete encoded;
527 				return true;
528 			}
529 			break;
530 		}
531 	}
532 	return KviWindow::event(e);
533 }
534 
resizeEvent(QResizeEvent *)535 void DccChatWindow::resizeEvent(QResizeEvent *)
536 {
537 	int hght = m_pInput->heightHint();
538 	int hght2 = m_pButtonBox->sizeHint().height();
539 	m_pButtonBox->setGeometry(0, 0, width(), hght2);
540 	m_pSplitter->setGeometry(0, hght2, width(), height() - (hght + hght2));
541 	m_pInput->setGeometry(0, height() - hght, width(), hght);
542 }
543 
sizeHint() const544 QSize DccChatWindow::sizeHint() const
545 {
546 	QSize ret(m_pIrcView->sizeHint().width(),
547 	    m_pIrcView->sizeHint().height() + m_pInput->heightHint());
548 	return ret;
549 }
550 
handleMarshalError(KviError::Code eError)551 void DccChatWindow::handleMarshalError(KviError::Code eError)
552 {
553 	QString szErr = KviError::getDescription(eError);
554 	if(!KVS_TRIGGER_EVENT_2_HALTED(KviEvent_OnDCCChatError, this, szErr, m_pDescriptor->idString()))
555 		output(KVI_OUT_DCCERROR, __tr2qs_ctx("DCC %Q failed: %Q", "dcc"), &(m_pDescriptor->szType), &szErr);
556 }
557 
connected()558 void DccChatWindow::connected()
559 {
560 	if(!(m_pDescriptor->bActive))
561 	{
562 		// PASSIVE CONNECTION...Find out the remote end
563 		m_pDescriptor->szIp = m_pMarshal->remoteIp();
564 		m_pDescriptor->szPort = m_pMarshal->remotePort();
565 		m_pDescriptor->szHost = m_pMarshal->remoteIp();
566 	}
567 
568 	updateCaption();
569 
570 	m_pSlaveThread = new DccChatThread(this, m_pMarshal->releaseSocket());
571 
572 #ifdef COMPILE_SSL_SUPPORT
573 	KviSSL * s = m_pMarshal->releaseSSL();
574 	if(s)
575 	{
576 		KviSSLMaster::printSSLConnectionInfo(this, s);
577 		m_pSlaveThread->setSSL(s);
578 	}
579 #endif
580 	m_pSlaveThread->start();
581 
582 	if(!KVS_TRIGGER_EVENT_1_HALTED(KviEvent_OnDCCChatConnected, this, m_pDescriptor->idString()))
583 	{
584 		output(KVI_OUT_DCCMSG, __tr2qs_ctx("Connected to %Q:%Q", "dcc"),
585 		    &(m_pMarshal->remoteIp()), &(m_pMarshal->remotePort()));
586 		output(KVI_OUT_DCCMSG, __tr2qs_ctx("Local end is %Q:%Q", "dcc"),
587 		    &(m_pMarshal->localIp()), &(m_pMarshal->localPort()));
588 #ifdef COMPILE_SSL_SUPPORT
589 		QString tmp = QString("DCC: %1 %2@%3:%4").arg(m_pDescriptor->bIsSSL ? "SChat" : "Chat", m_pDescriptor->szNick, m_pDescriptor->szIp, m_pDescriptor->szPort);
590 #else
591 		QString tmp = QString("DCC: %1 %2@%3:%4").arg("Chat", m_pDescriptor->szNick, m_pDescriptor->szIp, m_pDescriptor->szPort);
592 #endif
593 		m_pLabel->setText(tmp);
594 	}
595 }
596 
597 //
598 // THREAD
599 //
600 
DccChatThread(KviWindow * wnd,kvi_socket_t fd)601 DccChatThread::DccChatThread(KviWindow * wnd, kvi_socket_t fd)
602     : DccThread(wnd, fd)
603 {
604 }
605 
run()606 void DccChatThread::run()
607 {
608 	KviDccThreadIncomingData data;
609 	data.iLen = 0;
610 	data.buffer = nullptr;
611 
612 	for(;;)
613 	{
614 		// Dequeue events
615 		while(KviThreadEvent * e = dequeueEvent())
616 		{
617 			if(e->id() == KVI_THREAD_EVENT_TERMINATE)
618 			{
619 				delete e;
620 				goto out_of_the_loop;
621 			}
622 			else
623 			{
624 				// Other events are senseless to us
625 				delete e;
626 			}
627 		}
628 
629 		bool bCanRead;
630 		bool bCanWrite;
631 		if(kvi_select(m_fd, &bCanRead, &bCanWrite))
632 		{
633 			if(bCanWrite)
634 			{
635 				if(!tryFlushOutBuffers())
636 					goto out_of_the_loop;
637 			}
638 			if(bCanRead)
639 			{
640 				data.buffer = (char *)KviMemory::reallocate(data.buffer, (data.iLen + 512) * sizeof(char));
641 				int readLen;
642 #ifdef COMPILE_SSL_SUPPORT
643 				if(m_pSSL)
644 				{
645 					readLen = m_pSSL->read(data.buffer + data.iLen, 512);
646 				}
647 				else
648 				{
649 #endif
650 					readLen = kvi_socket_recv(m_fd, data.buffer + data.iLen, 512);
651 #ifdef COMPILE_SSL_SUPPORT
652 				}
653 #endif
654 				if(readLen > 0)
655 				{
656 					data.iLen += readLen;
657 					data.buffer = (char *)KviMemory::reallocate(data.buffer, data.iLen * sizeof(char));
658 					if(!handleIncomingData(&data, false))
659 						break; // non critical...
660 				}
661 				else
662 				{
663 
664 #ifdef COMPILE_SSL_SUPPORT
665 					if(m_pSSL)
666 					{
667 						// ssl error....?
668 						switch(m_pSSL->getProtocolError(readLen))
669 						{
670 							case KviSSL::ZeroReturn:
671 								readLen = 0;
672 								break;
673 							case KviSSL::WantRead:
674 							case KviSSL::WantWrite:
675 								// hmmm...
676 								break;
677 							case KviSSL::SyscallError:
678 							{
679 								int iE = m_pSSL->getLastError(true);
680 								if(iE != 0)
681 								{
682 									raiseSSLError();
683 									postErrorEvent(KviError::SSLError);
684 									goto out_of_the_loop;
685 								}
686 							}
687 							break;
688 							case KviSSL::SSLError:
689 							{
690 								raiseSSLError();
691 								postErrorEvent(KviError::SSLError);
692 								goto out_of_the_loop;
693 							}
694 							break;
695 							default:
696 								// Raise unknown SSL ERROR
697 								postErrorEvent(KviError::SSLError);
698 								goto out_of_the_loop;
699 								break;
700 						}
701 					}
702 #endif
703 					if(data.iLen > 0)
704 					{
705 						data.buffer = (char *)KviMemory::reallocate(data.buffer, data.iLen * sizeof(char));
706 					}
707 					else
708 					{
709 						KviMemory::free(data.buffer);
710 						data.buffer = nullptr;
711 					}
712 
713 					if(!handleInvalidSocketRead(readLen))
714 					{
715 						if(data.iLen)
716 							handleIncomingData(&data, true); // critical
717 						KVI_ASSERT(!data.iLen);
718 						break; // error
719 					}
720 				}
721 			}
722 			msleep(100);
723 		}
724 	}
725 
726 out_of_the_loop:
727 
728 	if(data.iLen)
729 		KviMemory::free(data.buffer);
730 
731 #ifdef COMPILE_SSL_SUPPORT
732 	if(m_pSSL)
733 	{
734 		KviSSLMaster::freeSSL(m_pSSL);
735 		m_pSSL = nullptr;
736 	}
737 #endif
738 
739 	if(m_fd != KVI_INVALID_SOCKET)
740 		::kvi_socket_close(m_fd);
741 	m_fd = KVI_INVALID_SOCKET;
742 }
743 
handleIncomingData(KviDccThreadIncomingData * data,bool bCritical)744 bool DccChatThread::handleIncomingData(KviDccThreadIncomingData * data, bool bCritical)
745 {
746 	KVI_ASSERT(data->iLen);
747 	KVI_ASSERT(data->buffer);
748 	char * aux = data->buffer;
749 	char * end = data->buffer + data->iLen;
750 	while(aux != end)
751 	{
752 		if((*aux == '\n') || (*aux == '\0'))
753 		{
754 			KviThreadDataEvent<KviCString> * e = new KviThreadDataEvent<KviCString>(KVI_DCC_THREAD_EVENT_DATA);
755 			// The left part is len chars long
756 			int len = aux - data->buffer;
757 			//			qDebug("LEN = %d, iLen = %d",len,data->iLen);
758 			//#warning "DO IT BETTER (the \r cutting)"
759 			KviCString * s = new KviCString(data->buffer, len);
760 			if(s->lastCharIs('\r'))
761 				s->cutRight(1);
762 			e->setData(s);
763 			// but we cut also \n (or \0)
764 			++aux;
765 			// so len += 1; --> new data->iLen -= len;
766 			data->iLen -= (len + 1);
767 			//			qDebug("iLen now = %d",data->iLen);
768 			KVI_ASSERT(data->iLen >= 0);
769 			if(data->iLen > 0)
770 			{
771 				// memmove the remaining part to the beginning
772 				// aux points after \n or \0
773 				KviMemory::move(data->buffer, aux, data->iLen);
774 				data->buffer = (char *)KviMemory::reallocate(data->buffer, data->iLen);
775 				end = data->buffer + data->iLen;
776 				aux = data->buffer;
777 			}
778 			else
779 			{
780 				// no more data in the buffer
781 				KVI_ASSERT(data->iLen == 0);
782 				KviMemory::free(data->buffer);
783 				data->buffer = end = aux = nullptr;
784 			}
785 			postEvent(parent(), e);
786 		}
787 		else
788 			aux++;
789 		//		qDebug("PASSING CHAR %c",*aux);
790 	}
791 	// now aux == end
792 	if(bCritical)
793 	{
794 		// need to flush everything...
795 		if(data->iLen > 0)
796 		{
797 			// in the last part there are no NULL and \n chars
798 			KviThreadDataEvent<KviCString> * e = new KviThreadDataEvent<KviCString>(KVI_DCC_THREAD_EVENT_DATA);
799 			KviCString * s = new KviCString(data->buffer, data->iLen);
800 			if(s->lastCharIs('\r'))
801 				s->cutRight(1);
802 			e->setData(s);
803 			data->iLen = 0;
804 			KviMemory::free(data->buffer);
805 			data->buffer = nullptr;
806 			postEvent(parent(), e);
807 		}
808 	}
809 	return true;
810 }
811 
sendRawData(const void * buffer,int len)812 void DccChatThread::sendRawData(const void * buffer, int len)
813 {
814 	m_pMutex->lock();
815 	m_pOutBuffers.emplace_back(new KviDataBuffer((unsigned int)len, (const unsigned char *)buffer));
816 	m_pMutex->unlock();
817 }
818 
tryFlushOutBuffers()819 bool DccChatThread::tryFlushOutBuffers()
820 {
821 	bool bRet = true;
822 	m_pMutex->lock();
823 	while(!m_pOutBuffers.empty())
824 	{
825 		auto & b = m_pOutBuffers.front();
826 		int sentLen;
827 
828 #ifdef COMPILE_SSL_SUPPORT
829 		if(m_pSSL)
830 		{
831 			sentLen = m_pSSL->write((const char *)b->data(), b->size());
832 		}
833 		else
834 		{
835 #endif
836 			sentLen = kvi_socket_send(m_fd, b->data(), b->size());
837 #ifdef COMPILE_SSL_SUPPORT
838 		}
839 #endif
840 		if(sentLen > 0)
841 		{
842 			if(sentLen == b->size())
843 				m_pOutBuffers.pop_front();
844 			else
845 			{
846 				// just a part
847 				b->remove((unsigned int)sentLen);
848 				break;
849 			}
850 		}
851 		else
852 		{
853 #ifdef COMPILE_SSL_SUPPORT
854 			if(m_pSSL)
855 			{
856 				// ops...might be an SSL error
857 				switch(m_pSSL->getProtocolError(sentLen))
858 				{
859 					case KviSSL::WantWrite:
860 					case KviSSL::WantRead:
861 						// Async continue...
862 						goto handle_system_error;
863 						break;
864 					case KviSSL::SyscallError:
865 						if(sentLen == 0)
866 						{
867 							raiseSSLError();
868 							postErrorEvent(KviError::RemoteEndClosedConnection);
869 							bRet = false;
870 							goto out_of_the_loop;
871 						}
872 						else
873 						{
874 							int iSSLErr = m_pSSL->getLastError(true);
875 							if(iSSLErr != 0)
876 							{
877 								raiseSSLError();
878 								postErrorEvent(KviError::SSLError);
879 								bRet = false;
880 								goto out_of_the_loop;
881 							}
882 							else
883 							{
884 								goto handle_system_error;
885 							}
886 						}
887 						break;
888 					case KviSSL::SSLError:
889 						raiseSSLError();
890 						postErrorEvent(KviError::SSLError);
891 						bRet = false;
892 						goto out_of_the_loop;
893 						break;
894 					default:
895 						postErrorEvent(KviError::SSLError);
896 						bRet = false;
897 						goto out_of_the_loop;
898 						break;
899 				}
900 			}
901 #endif
902 		handle_system_error:
903 			if(sentLen < 0)
904 			{
905 				int err = kvi_socket_error();
906 #if defined(COMPILE_ON_WINDOWS) || defined(COMPILE_ON_MINGW)
907 				if((err != EAGAIN) && (err != EINTR) && (err != WSAEWOULDBLOCK))
908 #else
909 				if((err != EAGAIN) && (err != EINTR))
910 #endif
911 				{
912 					postErrorEvent(KviError::translateSystemError(err));
913 					bRet = false;
914 					goto out_of_the_loop;
915 				}
916 			}
917 			break; // send error
918 		}
919 	}
920 out_of_the_loop:
921 	m_pMutex->unlock();
922 	return bRet;
923 }
924