diff options
Diffstat (limited to 'src/modules/dcc/DccChatWindow.cpp')
| -rw-r--r-- | src/modules/dcc/DccChatWindow.cpp | 893 |
1 files changed, 893 insertions, 0 deletions
diff --git a/src/modules/dcc/DccChatWindow.cpp b/src/modules/dcc/DccChatWindow.cpp new file mode 100644 index 000000000..bf5a935c9 --- /dev/null +++ b/src/modules/dcc/DccChatWindow.cpp @@ -0,0 +1,893 @@ +//============================================================================= +// +// 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 opinion) 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 "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 "KviMircCntrl.h" +#include "KviTalVBox.h" + +#include <QEvent> +#include <QResizeEvent> +#include <QByteArray> +#include <QTextDocument> //for Qt::escape + +#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(KviMainWindow *pFrm,DccDescriptor * dcc,const char * name) +: DccWindow(KVI_WINDOW_TYPE_DCCCHAT,pFrm,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,pFrm,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(int)),this,SLOT(handleMarshalError(int))); + 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(KVI_SMALLICON_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("Cannot 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 != KviMircCntrl::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) + m_pFrm->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()); + m_pFrm->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 crypto 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) + m_pFrm->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) + m_pFrm->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 = KviMircCntrl::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("Cannot 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<KviError::Code> *)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<KviCString> *)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()) + { + if(KVI_OPTION_BOOL(KviOption_boolFlashDccChatWindowOnNewMessages)) + { + demandAttention(); + } + if(KVI_OPTION_BOOL(KviOption_boolPopupNotifierOnNewDccChatMessages)) + { + QString szMsg = "<b>"; + szMsg += m_pDescriptor->szNick; + szMsg += "</b> "; + szMsg += Qt::escape(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())) + { + m_pFrm->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 crypto 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())) + { + m_pFrm->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()) + { + if(KVI_OPTION_BOOL(KviOption_boolFlashDccChatWindowOnNewMessages)) + { + demandAttention(); + } + if(KVI_OPTION_BOOL(KviOption_boolPopupNotifierOnNewDccChatMessages)) + { + QString szMsg = Qt::escape(QString(d.ptr())); + g_pApp->notifierMessage(this,KVI_SMALLICON_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<KviDataBuffer>; + 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<KviCString> * e = new KviThreadDataEvent<KviCString>(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<KviCString> * e = new KviThreadDataEvent<KviCString>(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; +} + +#ifndef COMPILE_USE_STANDALONE_MOC_SOURCES +#include "m_DccChatWindow.moc" +#endif //!COMPILE_USE_STANDALONE_MOC_SOURCES |
