//============================================================================= // // File : DccChatWindow.cpp // Creation date : Tue Sep 20 09 2000 15:13:13 by Szymon Stefanek // // This file is part of the KVIrc IRC client distribution // Copyright (C) 2000-2010 Szymon Stefanek (pragma at kvirc dot net) // // This program is FREE software. You can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the HOPE that it will be USEFUL, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. // See the GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, write to the Free Software Foundation, // Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //============================================================================= #include "DccChatWindow.h" #include "DccMarshal.h" #include "DccBroker.h" #ifdef COMPILE_ON_WINDOWS // Ugly Windoze compiler... #include "DccDialog.h" #endif #include "kvi_debug.h" #include "KviOptions.h" #include "KviInput.h" #include "KviIrcView.h" #include "KviIconManager.h" #include "KviLocale.h" #include "kvi_out.h" #include "KviNetUtils.h" #include "KviConsoleWindow.h" #include "KviMainWindow.h" #include "KviMemory.h" #include "KviThread.h" #include "KviIrcSocket.h" #include "kvi_settings.h" #include "kvi_socket.h" #include "KviApplication.h" #include "KviParameterList.h" #include "KviIrcConnection.h" #include "KviIrcConnectionUserInfo.h" #include "KviKvsEventTriggers.h" #include "KviControlCodes.h" #include "KviTalVBox.h" #include #include #include #ifdef COMPILE_CRYPT_SUPPORT #include "KviCryptEngine.h" #include "KviCryptController.h" #endif #ifdef COMPILE_SSL_SUPPORT #include "KviSSLMaster.h" #endif extern DccBroker * g_pDccBroker; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////// ////// WINDOW ////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// DccChatWindow::DccChatWindow(DccDescriptor * dcc, const char * name) : DccWindow(KviWindow::DccChat, name, dcc) { m_pButtonBox = new KviTalHBox(this); m_pLabel = new KviThemedLabel(m_pButtonBox, this, "dcc_chat_label"); m_pLabel->setText(name); m_pButtonBox->setStretchFactor(m_pLabel, 1); m_pButtonContainer = new KviTalHBox(m_pButtonBox); createTextEncodingButton(m_pButtonContainer); #ifdef COMPILE_CRYPT_SUPPORT createCryptControllerButton(m_pButtonContainer); #endif m_pSplitter = new KviTalSplitter(Qt::Horizontal, this); m_pSplitter->setObjectName("dcc_chat_splitter"); m_pSplitter->setChildrenCollapsible(false); m_pIrcView = new KviIrcView(m_pSplitter, this); connect(m_pIrcView, SIGNAL(rightClicked()), this, SLOT(textViewRightClicked())); m_pInput = new KviInput(this); //setFocusHandler(m_pInput,this); m_pSlaveThread = 0; if(KVI_OPTION_BOOL(KviOption_boolAutoLogDccChat)) m_pIrcView->startLogging(); m_pMarshal = new DccMarshal(this); connect(m_pMarshal, SIGNAL(error(KviError::Code)), this, SLOT(handleMarshalError(KviError::Code))); connect(m_pMarshal, SIGNAL(connected()), this, SLOT(connected())); connect(m_pMarshal, SIGNAL(inProgress()), this, SLOT(connectionInProgress())); #ifdef COMPILE_SSL_SUPPORT connect(m_pMarshal, SIGNAL(startingSSLHandshake()), this, SLOT(startingSSLHandshake())); connect(m_pMarshal, SIGNAL(sslError(const char *)), this, SLOT(sslError(const char *))); #endif m_pSlaveThread = 0; startConnection(); } DccChatWindow::~DccChatWindow() { g_pDccBroker->unregisterDccWindow(this); if(m_pSlaveThread) { m_pSlaveThread->terminate(); delete m_pSlaveThread; m_pSlaveThread = 0; } KviThreadManager::killPendingEvents(this); } void DccChatWindow::textViewRightClicked() { KVS_TRIGGER_EVENT_1(KviEvent_OnDCCChatPopupRequest, this, m_pDescriptor->idString()); } void DccChatWindow::triggerCreationEvents() { KVS_TRIGGER_EVENT_1(KviEvent_OnDCCChatWindowCreated, this, m_pDescriptor->idString()); } void DccChatWindow::triggerDestructionEvents() { KVS_TRIGGER_EVENT_1(KviEvent_OnDCCChatWindowClosing, this, m_pDescriptor->idString()); } void DccChatWindow::startConnection() { if(!(m_pDescriptor->bActive)) { // PASSIVE CONNECTION output(KVI_OUT_DCCMSG, __tr2qs_ctx("Attempting a passive DCC %s connection", "dcc"), m_pDescriptor->szType.toUtf8().data()); #ifdef COMPILE_SSL_SUPPORT KviError::Code eError = m_pMarshal->dccListen(m_pDescriptor->szListenIp, m_pDescriptor->szListenPort, m_pDescriptor->bDoTimeout, m_pDescriptor->bIsSSL); #else KviError::Code eError = m_pMarshal->dccListen(m_pDescriptor->szListenIp, m_pDescriptor->szListenPort, m_pDescriptor->bDoTimeout); #endif if(eError != KviError::Success) handleMarshalError(eError); } else { // ACTIVE CONNECTION output(KVI_OUT_DCCMSG, __tr2qs_ctx("Attempting an active DCC %s connection", "dcc"), m_pDescriptor->szType.toUtf8().data()); #ifdef COMPILE_SSL_SUPPORT KviError::Code eError = m_pMarshal->dccConnect(m_pDescriptor->szIp.toUtf8().data(), m_pDescriptor->szPort.toUtf8().data(), m_pDescriptor->bDoTimeout, m_pDescriptor->bIsSSL); #else KviError::Code eError = m_pMarshal->dccConnect(m_pDescriptor->szIp.toUtf8().data(), m_pDescriptor->szPort.toUtf8().data(), m_pDescriptor->bDoTimeout); #endif if(eError != KviError::Success) handleMarshalError(eError); } } void DccChatWindow::connectionInProgress() { if(m_pDescriptor->bActive) { output(KVI_OUT_DCCMSG, __tr2qs_ctx("Contacting host %Q on port %Q", "dcc"), &(m_pDescriptor->szIp), &(m_pDescriptor->szPort)); } else { output(KVI_OUT_DCCMSG, __tr2qs_ctx("Listening on interface %Q port %Q", "dcc"), &(m_pMarshal->localIp()), &(m_pMarshal->localPort())); if(m_pDescriptor->bSendRequest) { KviCString ip; if(!m_pDescriptor->szFakeIp.isEmpty()) { ip = m_pDescriptor->szFakeIp; } else { ip = m_pDescriptor->szListenIp; if(KVI_OPTION_BOOL(KviOption_boolDccGuessIpFromServerWhenLocalIsUnroutable)) { if(!kvi_isRoutableIpString(ip.ptr())) { // try to get the IP that the IRC server can see if(m_pDescriptor->console()) { KviCString tmp = m_pDescriptor->console()->connection() ? m_pDescriptor->console()->connection()->userInfo()->hostIp().toUtf8().data() : ""; if(tmp.hasData()) { ip = tmp; output(KVI_OUT_DCCMSG, __tr2qs_ctx("The local IP address is private, determining from IRC server: %s", "dcc"), ip.ptr()); } else { output(KVI_OUT_DCCMSG, __tr2qs_ctx("The local IP address is private, but unable to determine it from the IRC server", "dcc")); } } else { output(KVI_OUT_DCCMSG, __tr2qs_ctx("The local IP address is private, but have no IRC server to determine it from", "dcc")); } } } } QString port = !m_pDescriptor->szFakePort.isEmpty() ? m_pDescriptor->szFakePort : QString(m_pMarshal->localPort()); //FIXME: #warning "OPTION FOR SENDING 127.0.0.1 and so on (not an unsigned number)" struct in_addr a; if(KviNetUtils::stringIpToBinaryIp(ip.ptr(), &a)) ip.setNum(htonl(a.s_addr)); QString szReq = QString("PRIVMSG %1 :%2DCC %3 chat %4 %5").arg(m_pDescriptor->szNick, QChar(0x01), m_pDescriptor->szType, ip.ptr(), port); if(m_pDescriptor->isZeroPortRequest()) { szReq.append(" "); szReq += m_pDescriptor->zeroPortRequestTag(); } szReq.append((char)(0x01)); m_pDescriptor->console()->connection()->sendData(m_pDescriptor->console()->connection()->encodeText(szReq).data()); output(KVI_OUT_DCCMSG, __tr2qs_ctx("Sent DCC %Q request to %Q, waiting for the remote client to connect...", "dcc"), &(m_pDescriptor->szType), &(m_pDescriptor->szNick)); //qDebug(m_pDescriptor->szNick); } else output(KVI_OUT_DCCMSG, __tr2qs_ctx("DCC %Q request not sent, awaiting manual connection", "dcc"), &(m_pDescriptor->szType)); } KVS_TRIGGER_EVENT_1(KviEvent_OnDCCChatConnectionInProgress, this, m_pDescriptor->idString()); } void DccChatWindow::startingSSLHandshake() { #ifdef COMPILE_SSL_SUPPORT outputNoFmt(KVI_OUT_SSL, __tr2qs_ctx("Low-level transport connection established", "dcc")); outputNoFmt(KVI_OUT_SSL, __tr2qs_ctx("Starting Secure Socket Layer handshake", "dcc")); #endif } void DccChatWindow::sslError(const char * msg) { #ifdef COMPILE_SSL_SUPPORT if(!KVS_TRIGGER_EVENT_2_HALTED(KviEvent_OnDCCChatError, this, QString(msg), m_pDescriptor->idString())) output(KVI_OUT_DCCERROR, __tr2qs_ctx("[SSL ERROR]: %s", "dcc"), msg); #endif } const QString & DccChatWindow::target() { // This may change on the fly... m_szTarget = m_pDescriptor->szNick; m_szTarget += "@"; m_szTarget += m_pDescriptor->szIp; m_szTarget += ":"; m_szTarget += m_pDescriptor->szPort; return m_szTarget; } void DccChatWindow::fillCaptionBuffers() { QString tmp = QString("DCC %1 %2@%3:%4").arg( #ifdef COMPILE_SSL_SUPPORT m_pDescriptor->bIsSSL ? "SChat" : "Chat", #else "Chat", #endif m_pDescriptor->szNick, m_pDescriptor->szIp, m_pDescriptor->szPort); m_szPlainTextCaption = tmp; } QPixmap * DccChatWindow::myIconPtr() { return g_pIconManager->getSmallIcon(KviIconManager::DccMsg); } void DccChatWindow::getBaseLogFileName(QString & buffer) { buffer.sprintf("%s_%s_%s", m_pDescriptor->szNick.toUtf8().data(), m_pDescriptor->szIp.toUtf8().data(), m_pDescriptor->szPort.toUtf8().data()); } void DccChatWindow::ownMessage(const QString & text, bool bUserFeedback) { if(!m_pSlaveThread) { output(KVI_OUT_SYSTEMWARNING, __tr2qs_ctx("Can't send data: no active connection", "dcc")); return; } QByteArray szData = encodeText(text); const char * d = szData.data(); if(!d) return; #ifdef COMPILE_CRYPT_SUPPORT if(cryptSessionInfo()) { if(cryptSessionInfo()->m_bDoEncrypt) { if(*d != KviControlCodes::CryptEscape) { KviCString encrypted; cryptSessionInfo()->m_pEngine->setMaxEncryptLen(-1); switch(cryptSessionInfo()->m_pEngine->encrypt(d, encrypted)) { case KviCryptEngine::Encrypted: { KviCString buf(KviCString::Format, "%s\r\n", encrypted.ptr()); m_pSlaveThread->sendRawData(buf.ptr(), buf.len()); if(bUserFeedback) g_pMainWindow->firstConsole()->outputPrivmsg(this, KVI_OUT_OWNPRIVMSGCRYPTED, m_pDescriptor->szLocalNick.toUtf8().data(), m_pDescriptor->szLocalUser.toUtf8().data(), m_pDescriptor->szLocalHost.toUtf8().data(), text, KviConsoleWindow::NoNotifications); } break; case KviCryptEngine::Encoded: { KviCString buf(KviCString::Format, "%s\r\n", encrypted.ptr()); m_pSlaveThread->sendRawData(buf.ptr(), buf.len()); if(bUserFeedback) { QString encr = decodeText(encrypted.ptr()); g_pMainWindow->firstConsole()->outputPrivmsg(this, KVI_OUT_OWNPRIVMSG, m_pDescriptor->szLocalNick.toUtf8().data(), m_pDescriptor->szLocalUser.toUtf8().data(), m_pDescriptor->szLocalHost.toUtf8().data(), encr, KviConsoleWindow::NoNotifications); } } break; default: // also case KviCryptEngine::EncryptError { QString szErr = cryptSessionInfo()->m_pEngine->lastError(); output(KVI_OUT_SYSTEMERROR, __tr2qs_ctx("The encryption engine was not able to encrypt the current message (%Q): %Q, no data was sent to the remote end", "dcc"), &text, &szErr); } break; } return; } else { d++; //eat the escape code KviCString buf(KviCString::Format, "%s\r\n", d); QString tmp = text.right(text.length() - 1); m_pSlaveThread->sendRawData(buf.ptr(), buf.len()); if(bUserFeedback) g_pMainWindow->firstConsole()->outputPrivmsg(this, KVI_OUT_OWNPRIVMSG, m_pDescriptor->szLocalNick.toUtf8().data(), m_pDescriptor->szLocalUser.toUtf8().data(), m_pDescriptor->szLocalHost.toUtf8().data(), tmp, KviConsoleWindow::NoNotifications); return; } } } #endif KviCString buf(KviCString::Format, "%s\r\n", d); m_pSlaveThread->sendRawData(buf.ptr(), buf.len()); if(bUserFeedback) g_pMainWindow->firstConsole()->outputPrivmsg(this, KVI_OUT_OWNPRIVMSG, m_pDescriptor->szLocalNick.toUtf8().data(), m_pDescriptor->szLocalUser.toUtf8().data(), m_pDescriptor->szLocalHost.toUtf8().data(), text, KviConsoleWindow::NoNotifications); } const QString & DccChatWindow::localNick() { // FIXME: This is just a complete HACK m_szLocalNick = m_pDescriptor->szLocalNick; return m_szLocalNick; } void DccChatWindow::ownAction(const QString & text) { if(m_pSlaveThread) { QString szTmpBuffer; //see bug ticket #220 if(KVI_OPTION_BOOL(KviOption_boolStripMircColorsInUserMessages)) { szTmpBuffer = KviControlCodes::stripControlBytes(text); } else { szTmpBuffer = text; } QByteArray szData = encodeText(szTmpBuffer); const char * d = szData.data(); if(!d) return; KviCString buf(KviCString::Format, "%cACTION %s%c\r\n", 0x01, d, 0x01); m_pSlaveThread->sendRawData(buf.ptr(), buf.len()); output(KVI_OUT_ACTION, "%Q %Q", &(m_pDescriptor->szLocalNick), &szTmpBuffer); } else { output(KVI_OUT_SYSTEMWARNING, __tr2qs_ctx("Can't send data: no active connection", "dcc")); } } bool DccChatWindow::event(QEvent * e) { if(e->type() == KVI_THREAD_EVENT) { switch(((KviThreadEvent *)e)->id()) { case KVI_DCC_THREAD_EVENT_ERROR: { KviError::Code * pError = ((KviThreadDataEvent *)e)->getData(); QString szErr = KviError::getDescription(*pError); if(!KVS_TRIGGER_EVENT_2_HALTED(KviEvent_OnDCCChatError, this, szErr, m_pDescriptor->idString())) output(KVI_OUT_DCCERROR, __tr2qs_ctx("ERROR: %Q", "dcc"), &szErr); KVS_TRIGGER_EVENT_1(KviEvent_OnDCCChatDisconnected, this, m_pDescriptor->idString()); delete pError; return true; } break; case KVI_DCC_THREAD_EVENT_DATA: { KviCString * encoded = ((KviThreadDataEvent *)e)->getData(); KviCString d = KviCString(decodeText(encoded->ptr())); if(d.firstCharIs(0x01)) { d.cutLeft(1); if(d.lastCharIs(0x01)) d.cutRight(1); if(kvi_strEqualCIN("ACTION", d.ptr(), 6)) d.cutLeft(6); d.stripLeftWhiteSpace(); output(KVI_OUT_ACTION, "%Q %s", &(m_pDescriptor->szNick), d.ptr()); if(!hasAttention(KviWindow::MainWindowIsVisible)) { if(KVI_OPTION_BOOL(KviOption_boolFlashDccChatWindowOnNewMessages)) { demandAttention(); } if(KVI_OPTION_BOOL(KviOption_boolPopupNotifierOnNewDccChatMessages)) { QString szMsg = ""; szMsg += m_pDescriptor->szNick; szMsg += " "; szMsg += KviQString::toHtmlEscaped(QString(d.ptr())); //qDebug("KviIrcServerParser_ctcp.cpp:975 debug: %s",szMsg.data()); g_pApp->notifierMessage(this, KVI_OPTION_MSGTYPE(KVI_OUT_ACTION).pixId(), szMsg, KVI_OPTION_UINT(KviOption_uintNotifierAutoHideTime)); } } } else { #ifdef COMPILE_CRYPT_SUPPORT if(KviCryptSessionInfo * cinf = cryptSessionInfo()) { if(cinf->m_bDoDecrypt) { KviCString decryptedStuff; switch(cinf->m_pEngine->decrypt(d.ptr(), decryptedStuff)) { case KviCryptEngine::DecryptOkWasEncrypted: case KviCryptEngine::DecryptOkWasEncoded: case KviCryptEngine::DecryptOkWasPlainText: if(!KVS_TRIGGER_EVENT_2_HALTED(KviEvent_OnDCCChatMessage, this, QString(decryptedStuff.ptr()), m_pDescriptor->idString())) { g_pMainWindow->firstConsole()->outputPrivmsg(this, KVI_OUT_DCCCHATMSG, m_pDescriptor->szNick.toUtf8().data(), m_pDescriptor->szUser.toUtf8().data(), m_pDescriptor->szHost.toUtf8().data(), decryptedStuff.ptr()); } delete encoded; return true; break; default: // also case KviCryptEngine::DecryptError { QString szErr = cinf->m_pEngine->lastError(); output(KVI_OUT_SYSTEMERROR, __tr2qs_ctx("The following message appears to be encrypted, but the encryption engine failed to decode it: %Q", "dcc"), &szErr); } break; } } } else { #endif // FIXME! if(!KVS_TRIGGER_EVENT_2_HALTED(KviEvent_OnDCCChatMessage, this, QString(d.ptr()), m_pDescriptor->idString())) { g_pMainWindow->firstConsole()->outputPrivmsg(this, KVI_OUT_DCCCHATMSG, m_pDescriptor->szNick.toUtf8().data(), m_pDescriptor->szUser.toUtf8().data(), m_pDescriptor->szHost.toUtf8().data(), d.ptr()); if(!hasAttention(KviWindow::MainWindowIsVisible)) { if(KVI_OPTION_BOOL(KviOption_boolFlashDccChatWindowOnNewMessages)) { demandAttention(); } if(KVI_OPTION_BOOL(KviOption_boolPopupNotifierOnNewDccChatMessages)) { QString szMsg = KviQString::toHtmlEscaped(QString(d.ptr())); g_pApp->notifierMessage(this, KviIconManager::DccChatMsg, szMsg, KVI_OPTION_UINT(KviOption_uintNotifierAutoHideTime)); } } } #ifdef COMPILE_CRYPT_SUPPORT } #endif } delete encoded; return true; } break; } } return KviWindow::event(e); } void DccChatWindow::resizeEvent(QResizeEvent *) { int hght = m_pInput->heightHint(); int hght2 = m_pButtonBox->sizeHint().height(); m_pButtonBox->setGeometry(0, 0, width(), hght2); m_pSplitter->setGeometry(0, hght2, width(), height() - (hght + hght2)); m_pInput->setGeometry(0, height() - hght, width(), hght); } QSize DccChatWindow::sizeHint() const { QSize ret(m_pIrcView->sizeHint().width(), m_pIrcView->sizeHint().height() + m_pInput->heightHint()); return ret; } void DccChatWindow::handleMarshalError(KviError::Code eError) { QString szErr = KviError::getDescription(eError); if(!KVS_TRIGGER_EVENT_2_HALTED(KviEvent_OnDCCChatError, this, szErr, m_pDescriptor->idString())) output(KVI_OUT_DCCERROR, __tr2qs_ctx("DCC %Q failed: %Q", "dcc"), &(m_pDescriptor->szType), &szErr); } void DccChatWindow::connected() { if(!(m_pDescriptor->bActive)) { // PASSIVE CONNECTION...Find out the remote end m_pDescriptor->szIp = m_pMarshal->remoteIp(); m_pDescriptor->szPort = m_pMarshal->remotePort(); m_pDescriptor->szHost = m_pMarshal->remoteIp(); } updateCaption(); m_pSlaveThread = new DccChatThread(this, m_pMarshal->releaseSocket()); #ifdef COMPILE_SSL_SUPPORT KviSSL * s = m_pMarshal->releaseSSL(); if(s) { KviSSLMaster::printSSLConnectionInfo(this, s); m_pSlaveThread->setSSL(s); } #endif m_pSlaveThread->start(); if(!KVS_TRIGGER_EVENT_1_HALTED(KviEvent_OnDCCChatConnected, this, m_pDescriptor->idString())) { output(KVI_OUT_DCCMSG, __tr2qs_ctx("Connected to %Q:%Q", "dcc"), &(m_pMarshal->remoteIp()), &(m_pMarshal->remotePort())); output(KVI_OUT_DCCMSG, __tr2qs_ctx("Local end is %Q:%Q", "dcc"), &(m_pMarshal->localIp()), &(m_pMarshal->localPort())); #ifdef COMPILE_SSL_SUPPORT QString tmp = QString("dcc: %1 %2@%3:%4").arg(m_pDescriptor->bIsSSL ? "SChat" : "Chat", m_pDescriptor->szNick, m_pDescriptor->szIp, m_pDescriptor->szPort); #else QString tmp = QString("dcc: %1 %2@%3:%4").arg("Chat", m_pDescriptor->szNick, m_pDescriptor->szIp, m_pDescriptor->szPort); #endif m_pLabel->setText(tmp); } } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////// ////// THREAD ////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// DccChatThread::DccChatThread(KviWindow * wnd, kvi_socket_t fd) : DccThread(wnd, fd) { m_pOutBuffers = new KviPointerList; m_pOutBuffers->setAutoDelete(true); } DccChatThread::~DccChatThread() { if(m_pOutBuffers) delete m_pOutBuffers; } void DccChatThread::run() { KviDccThreadIncomingData data; data.iLen = 0; data.buffer = 0; for(;;) { // Dequeue events while(KviThreadEvent * e = dequeueEvent()) { if(e->id() == KVI_THREAD_EVENT_TERMINATE) { delete e; goto out_of_the_loop; } else { // Other events are senseless to us delete e; } } bool bCanRead; bool bCanWrite; if(kvi_select(m_fd, &bCanRead, &bCanWrite)) { if(bCanWrite) { if(!tryFlushOutBuffers()) goto out_of_the_loop; } if(bCanRead) { data.buffer = (char *)KviMemory::reallocate(data.buffer, (data.iLen + 512) * sizeof(char)); int readLen; #ifdef COMPILE_SSL_SUPPORT if(m_pSSL) { readLen = m_pSSL->read(data.buffer + data.iLen, 512); } else { #endif readLen = kvi_socket_recv(m_fd, data.buffer + data.iLen, 512); #ifdef COMPILE_SSL_SUPPORT } #endif if(readLen > 0) { data.iLen += readLen; data.buffer = (char *)KviMemory::reallocate(data.buffer, data.iLen * sizeof(char)); if(!handleIncomingData(&data, false)) break; // non critical... } else { #ifdef COMPILE_SSL_SUPPORT if(m_pSSL) { // ssl error....? switch(m_pSSL->getProtocolError(readLen)) { case KviSSL::ZeroReturn: readLen = 0; break; case KviSSL::WantRead: case KviSSL::WantWrite: // hmmm... break; case KviSSL::SyscallError: { int iE = m_pSSL->getLastError(true); if(iE != 0) { raiseSSLError(); postErrorEvent(KviError::SSLError); goto out_of_the_loop; } } break; case KviSSL::SSLError: { raiseSSLError(); postErrorEvent(KviError::SSLError); goto out_of_the_loop; } break; default: // Raise unknown SSL ERROR postErrorEvent(KviError::SSLError); goto out_of_the_loop; break; } } #endif if(data.iLen > 0) { data.buffer = (char *)KviMemory::reallocate(data.buffer, data.iLen * sizeof(char)); } else { KviMemory::free(data.buffer); data.buffer = 0; } if(!handleInvalidSocketRead(readLen)) { if(data.iLen) handleIncomingData(&data, true); // critical KVI_ASSERT(!data.iLen); break; // error } } } msleep(100); } } out_of_the_loop: if(data.iLen) KviMemory::free(data.buffer); #ifdef COMPILE_SSL_SUPPORT if(m_pSSL) { KviSSLMaster::freeSSL(m_pSSL); m_pSSL = 0; } #endif if(m_fd != KVI_INVALID_SOCKET) ::kvi_socket_close(m_fd); m_fd = KVI_INVALID_SOCKET; } bool DccChatThread::handleIncomingData(KviDccThreadIncomingData * data, bool bCritical) { KVI_ASSERT(data->iLen); KVI_ASSERT(data->buffer); char * aux = data->buffer; char * end = data->buffer + data->iLen; while(aux != end) { if((*aux == '\n') || (*aux == '\0')) { KviThreadDataEvent * e = new KviThreadDataEvent(KVI_DCC_THREAD_EVENT_DATA); // The left part is len chars long int len = aux - data->buffer; // qDebug("LEN = %d, iLen = %d",len,data->iLen); //#warning "DO IT BETTER (the \r cutting)" KviCString * s = new KviCString(data->buffer, len); if(s->lastCharIs('\r')) s->cutRight(1); e->setData(s); // but we cut also \n (or \0) ++aux; // so len += 1; --> new data->iLen -= len; data->iLen -= (len + 1); // qDebug("iLen now = %d",data->iLen); KVI_ASSERT(data->iLen >= 0); if(data->iLen > 0) { // memmove the remaining part to the beginning // aux points after \n or \0 KviMemory::move(data->buffer, aux, data->iLen); data->buffer = (char *)KviMemory::reallocate(data->buffer, data->iLen); end = data->buffer + data->iLen; aux = data->buffer; } else { // no more data in the buffer KVI_ASSERT(data->iLen == 0); KviMemory::free(data->buffer); data->buffer = end = aux = 0; } postEvent(parent(), e); } else aux++; // qDebug("PASSING CHAR %c",*aux); } // now aux == end if(bCritical) { // need to flush everything... if(data->iLen > 0) { // in the last part there are no NULL and \n chars KviThreadDataEvent * e = new KviThreadDataEvent(KVI_DCC_THREAD_EVENT_DATA); KviCString * s = new KviCString(data->buffer, data->iLen); if(s->lastCharIs('\r')) s->cutRight(1); e->setData(s); data->iLen = 0; KviMemory::free(data->buffer); data->buffer = 0; postEvent(parent(), e); } } return true; } void DccChatThread::sendRawData(const void * buffer, int len) { m_pMutex->lock(); m_pOutBuffers->append(new KviDataBuffer((unsigned int)len, (const unsigned char *)buffer)); m_pMutex->unlock(); } bool DccChatThread::tryFlushOutBuffers() { bool bRet = true; m_pMutex->lock(); while(KviDataBuffer * b = m_pOutBuffers->first()) { int sentLen; #ifdef COMPILE_SSL_SUPPORT if(m_pSSL) { sentLen = m_pSSL->write((const char *)b->data(), b->size()); } else { #endif sentLen = kvi_socket_send(m_fd, b->data(), b->size()); #ifdef COMPILE_SSL_SUPPORT } #endif if(sentLen > 0) { if(sentLen == b->size()) m_pOutBuffers->removeFirst(); else { // just a part b->remove((unsigned int)sentLen); break; } } else { #ifdef COMPILE_SSL_SUPPORT if(m_pSSL) { // ops...might be an SSL error switch(m_pSSL->getProtocolError(sentLen)) { case KviSSL::WantWrite: case KviSSL::WantRead: // Async continue... goto handle_system_error; break; case KviSSL::SyscallError: if(sentLen == 0) { raiseSSLError(); postErrorEvent(KviError::RemoteEndClosedConnection); bRet = false; goto out_of_the_loop; } else { int iSSLErr = m_pSSL->getLastError(true); if(iSSLErr != 0) { raiseSSLError(); postErrorEvent(KviError::SSLError); bRet = false; goto out_of_the_loop; } else { goto handle_system_error; } } break; case KviSSL::SSLError: raiseSSLError(); postErrorEvent(KviError::SSLError); bRet = false; goto out_of_the_loop; break; default: postErrorEvent(KviError::SSLError); bRet = false; goto out_of_the_loop; break; } } #endif handle_system_error: if(sentLen < 0) { int err = kvi_socket_error(); #if defined(COMPILE_ON_WINDOWS) || defined(COMPILE_ON_MINGW) if((err != EAGAIN) || (err != EINTR) || (err != WSAEWOULDBLOCK)) #else if((err != EAGAIN) || (err != EINTR)) #endif { postErrorEvent(KviError::translateSystemError(err)); bRet = false; goto out_of_the_loop; } } break; // send error } } out_of_the_loop: m_pMutex->unlock(); return bRet; }