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