diff options
author | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-02-24 02:13:59 +0000 |
---|---|---|
committer | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-02-24 02:13:59 +0000 |
commit | a6d58bb6052ac8cb01805a48c4ad2f129126116f (patch) | |
tree | dd867a099fcbb263a8009a9fb22695b87855dad6 /src/modules/dcc/chat.cpp | |
download | kvirc-a6d58bb6052ac8cb01805a48c4ad2f129126116f.tar.gz kvirc-a6d58bb6052ac8cb01805a48c4ad2f129126116f.zip |
Added KDE3 version of kvirc
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/kvirc@1095341 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'src/modules/dcc/chat.cpp')
-rw-r--r-- | src/modules/dcc/chat.cpp | 842 |
1 files changed, 842 insertions, 0 deletions
diff --git a/src/modules/dcc/chat.cpp b/src/modules/dcc/chat.cpp new file mode 100644 index 00000000..715d17b9 --- /dev/null +++ b/src/modules/dcc/chat.cpp @@ -0,0 +1,842 @@ +//======================================================================================= +// +// File : chat.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) 1999-2000 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 "chat.h" +#include "marshal.h" +#include "broker.h" + +#ifdef COMPILE_ON_WINDOWS + // Ugly Windoze compiler... + #include "dialogs.h" +#endif + +#define _KVI_DEBUG_CHECK_RANGE_ +#include "kvi_debug.h" +#include "kvi_options.h" +#include "kvi_input.h" +#include "kvi_ircview.h" +#include "kvi_iconmanager.h" +#include "kvi_locale.h" +#include "kvi_error.h" +#include "kvi_out.h" +#include "kvi_netutils.h" +#include "kvi_console.h" +#include "kvi_frame.h" +#include "kvi_malloc.h" +#include "kvi_memmove.h" +#include "kvi_thread.h" +#include "kvi_ircsocket.h" +#include "kvi_settings.h" +#include "kvi_themedlabel.h" +#include "kvi_socket.h" +#include "kvi_app.h" +#include "kvi_parameterlist.h" +#include "kvi_ircconnection.h" +#include "kvi_ircconnectionuserinfo.h" +#include "kvi_kvs_eventtriggers.h" +#include "kvi_qcstring.h" + +#ifdef COMPILE_CRYPT_SUPPORT + #include "kvi_crypt.h" + #include "kvi_cryptcontroller.h" + #include "kvi_mirccntrl.h" +#endif + +#include <qsplitter.h> +#include <qevent.h> +#include "kvi_tal_vbox.h" + + +#ifdef COMPILE_SSL_SUPPORT + #include "kvi_sslmaster.h" +#endif + + + +extern KviDccBroker * g_pDccBroker; + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +////// +////// WINDOW +////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +KviDccChat::KviDccChat(KviFrame *pFrm,KviDccDescriptor * dcc,const char * name) +: KviDccWindow(KVI_WINDOW_TYPE_DCCCHAT,pFrm,name,dcc) +{ + m_pTopSplitter = new QSplitter(Qt::Horizontal,this,"top_splitter"); + KviThemedLabel * dummy; + dummy = new KviThemedLabel(m_pTopSplitter,"dummy_label"); + KviTalVBox * box = new KviTalVBox(m_pTopSplitter); + +#ifdef COMPILE_CRYPT_SUPPORT + createCryptControllerButton(box); +#endif + + m_pSplitter = new QSplitter(Qt::Horizontal,this,"splitter"); + 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 KviDccMarshal(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(); +} + +KviDccChat::~KviDccChat() +{ + g_pDccBroker->unregisterDccWindow(this); + if(m_pSlaveThread) + { + m_pSlaveThread->terminate(); + delete m_pSlaveThread; + m_pSlaveThread = 0; + } + KviThreadManager::killPendingEvents(this); +} + +void KviDccChat::textViewRightClicked() +{ + KVS_TRIGGER_EVENT_1(KviEvent_OnDCCChatPopupRequest,this,m_pDescriptor->idString()); +} + +void KviDccChat::triggerCreationEvents() +{ + KVS_TRIGGER_EVENT_1(KviEvent_OnDCCChatWindowCreated,this,m_pDescriptor->idString()); +} + +void KviDccChat::triggerDestructionEvents() +{ + KVS_TRIGGER_EVENT_1(KviEvent_OnDCCChatWindowClosing,this,m_pDescriptor->idString()); +} + +void KviDccChat::startConnection() +{ + if(!(m_pDescriptor->bActive)) + { + // PASSIVE CONNECTION + output(KVI_OUT_DCCMSG,__tr2qs_ctx("Attempting a passive DCC %s connection","dcc"),m_pDescriptor->szType.utf8().data()); +#ifdef COMPILE_SSL_SUPPORT + int ret = m_pMarshal->dccListen(m_pDescriptor->szListenIp,m_pDescriptor->szListenPort,m_pDescriptor->bDoTimeout,m_pDescriptor->bIsSSL); +#else + int ret = m_pMarshal->dccListen(m_pDescriptor->szListenIp,m_pDescriptor->szListenPort,m_pDescriptor->bDoTimeout); +#endif + if(ret != KviError_success)handleMarshalError(ret); + + } else { + // ACTIVE CONNECTION + output(KVI_OUT_DCCMSG,__tr2qs_ctx("Attempting an active DCC %s connection","dcc"),m_pDescriptor->szType.utf8().data()); +#ifdef COMPILE_SSL_SUPPORT + int ret = m_pMarshal->dccConnect(m_pDescriptor->szIp.utf8().data(),m_pDescriptor->szPort.utf8().data(),m_pDescriptor->bDoTimeout,m_pDescriptor->bIsSSL); +#else + int ret = m_pMarshal->dccConnect(m_pDescriptor->szIp.utf8().data(),m_pDescriptor->szPort.utf8().data(),m_pDescriptor->bDoTimeout); +#endif + if(ret != KviError_success)handleMarshalError(ret); + } +} + +void KviDccChat::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) + { + + KviStr 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()) + { + KviStr tmp = m_pDescriptor->console()->connection() ? m_pDescriptor->console()->connection()->userInfo()->hostIp().utf8().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(kvi_stringIpToBinaryIp(ip.ptr(),&a))ip.setNum(htonl(a.s_addr)); + + QString szReq = QString("PRIVMSG %1 :%2DCC %3 chat %4 %5").arg(m_pDescriptor->szNick).arg((char)0x01).arg(m_pDescriptor->szType).arg(ip.ptr()).arg(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 KviDccChat::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 KviDccChat::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 & KviDccChat::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 KviDccChat::fillCaptionBuffers() +{ + QString tmp = QString("DCC %1 %2@%3:%4").arg( +#ifdef COMPILE_SSL_SUPPORT + m_pDescriptor->bIsSSL ? "SChat" : "Chat").arg( +#else + "Chat").arg( +#endif + m_pDescriptor->szNick).arg(m_pDescriptor->szIp).arg(m_pDescriptor->szPort); + + m_szPlainTextCaption = tmp; + + m_szHtmlActiveCaption.sprintf("<nobr><font color=\"%s\"><b>%s</b></font></nobr>", + KVI_OPTION_COLOR(KviOption_colorCaptionTextActive).name().ascii(),tmp.utf8().data()); + m_szHtmlInactiveCaption.sprintf("<nobr><font color=\"%s\"><b>%s</b></font></nobr>", + KVI_OPTION_COLOR(KviOption_colorCaptionTextInactive).name().ascii(),tmp.utf8().data()); +} + +QPixmap * KviDccChat::myIconPtr() +{ + return g_pIconManager->getSmallIcon(KVI_SMALLICON_DCCMSG); +} + + +void KviDccChat::getBaseLogFileName(KviStr &buffer) +{ + buffer.sprintf("%s_%s_%s",m_pDescriptor->szNick.utf8().data(),m_pDescriptor->szIp.utf8().data(),m_pDescriptor->szPort.utf8().data()); +} + +void KviDccChat::ownMessage(const QString &text) +{ + if(!m_pSlaveThread) + { + output(KVI_OUT_SYSTEMWARNING,__tr2qs_ctx("Cannot send data: No active connection","dcc")); + return; + } + + KviQCString szData = encodeText(text); + const char * d = szData.data(); + if(!d)return; + +#ifdef COMPILE_CRYPT_SUPPORT + if(cryptSessionInfo()) + { + if(cryptSessionInfo()->bDoEncrypt) + { + if(*d != KVI_TEXT_CRYPTESCAPE) + { + KviStr encrypted; + cryptSessionInfo()->pEngine->setMaxEncryptLen(-1); + switch(cryptSessionInfo()->pEngine->encrypt(d,encrypted)) + { + case KviCryptEngine::Encrypted: + { + KviStr buf(KviStr::Format,"%s\r\n",encrypted.ptr()); + m_pSlaveThread->sendRawData(buf.ptr(),buf.len()); + m_pFrm->firstConsole()->outputPrivmsg(this,KVI_OUT_OWNPRIVMSGCRYPTED, + m_pDescriptor->szLocalNick.utf8().data(),m_pDescriptor->szLocalUser.utf8().data(), + m_pDescriptor->szLocalHost.utf8().data(),text,KviConsole::NoNotifications); + } + break; + case KviCryptEngine::Encoded: + { + KviStr buf(KviStr::Format,"%s\r\n",encrypted.ptr()); + m_pSlaveThread->sendRawData(buf.ptr(),buf.len()); + QString encr = decodeText(encrypted.ptr()); + m_pFrm->firstConsole()->outputPrivmsg(this,KVI_OUT_OWNPRIVMSG, + m_pDescriptor->szLocalNick.utf8().data(),m_pDescriptor->szLocalUser.utf8().data(), + m_pDescriptor->szLocalHost.utf8().data(),encr,KviConsole::NoNotifications); + } + break; + default: // also case KviCryptEngine::EncryptError + { + QString szErr = cryptSessionInfo()->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 + KviStr buf(KviStr::Format,"%s\r\n",d); + QString tmp = text.right(text.length() - 1); + m_pSlaveThread->sendRawData(buf.ptr(),buf.len()); + m_pFrm->firstConsole()->outputPrivmsg(this,KVI_OUT_OWNPRIVMSG, + m_pDescriptor->szLocalNick.utf8().data(),m_pDescriptor->szLocalUser.utf8().data(), + m_pDescriptor->szLocalHost.utf8().data(),tmp,KviConsole::NoNotifications); + return; + } + } + } +#endif + KviStr buf(KviStr::Format,"%s\r\n",d); + m_pSlaveThread->sendRawData(buf.ptr(),buf.len()); + m_pFrm->firstConsole()->outputPrivmsg(this,KVI_OUT_OWNPRIVMSG, + m_pDescriptor->szLocalNick.utf8().data(),m_pDescriptor->szLocalUser.utf8().data(), + m_pDescriptor->szLocalHost.utf8().data(),text,KviConsole::NoNotifications); +} + +const QString & KviDccChat::localNick() +{ + // FIXME: This is just a complete HACK + m_szLocalNick = m_pDescriptor->szLocalNick; + return m_szLocalNick; +} + +void KviDccChat::ownAction(const QString &text) +{ + if(m_pSlaveThread) + { + KviQCString szData = encodeText(text); + const char * d = szData.data(); + if(!d)return; + KviStr buf(KviStr::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),&text); + } else { + output(KVI_OUT_SYSTEMWARNING,__tr2qs_ctx("Cannot send data: No active connection","dcc")); + } +} + +bool KviDccChat::event(QEvent *e) +{ + if(e->type() == KVI_THREAD_EVENT) + { + switch(((KviThreadEvent *)e)->id()) + { + case KVI_DCC_THREAD_EVENT_ERROR: + { + int * err = ((KviThreadDataEvent<int> *)e)->getData(); + QString szErr = KviError::getDescription(*err); + 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 err; + return true; + } + break; + case KVI_DCC_THREAD_EVENT_DATA: + { + KviStr * encoded = ((KviThreadDataEvent<KviStr> *)e)->getData(); + KviStr d=KviStr(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()); + } else { + +#ifdef COMPILE_CRYPT_SUPPORT + if(KviCryptSessionInfo * cinf = cryptSessionInfo()) + { + if(cinf->bDoDecrypt) + { + KviStr decryptedStuff; + switch(cinf->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.utf8().data(),m_pDescriptor->szUser.utf8().data(), + m_pDescriptor->szHost.utf8().data(),decryptedStuff.ptr()); + } + delete encoded; + return true; + break; + + default: // also case KviCryptEngine::DecryptError + { + QString szErr = cinf->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.utf8().data(),m_pDescriptor->szUser.utf8().data(), + m_pDescriptor->szHost.utf8().data(),d.ptr()); +#ifdef COMPILE_CRYPT_SUPPORT + } +#endif + } + delete encoded; + return true; + } + break; + } + } + return KviWindow::event(e); +} + +void KviDccChat::resizeEvent(QResizeEvent *e) +{ + int hght = m_pInput->heightHint(); + int hght2 = m_pTopSplitter->sizeHint().height(); + m_pTopSplitter->setGeometry(0,0,width(),hght2); + m_pSplitter->setGeometry(0,hght2,width(),height() - (hght + hght2)); + m_pInput->setGeometry(0,height() - hght,width(),hght); +} + +QSize KviDccChat::sizeHint() const +{ + QSize ret(m_pIrcView->sizeHint().width(), + m_pIrcView->sizeHint().height() + m_pInput->heightHint()); + return ret; +} + +void KviDccChat::handleMarshalError(int err) +{ + QString szErr = KviError::getDescription(err); + 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 KviDccChat::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 KviDccChatThread(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())); + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +////// +////// THREAD +////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + +KviDccChatThread::KviDccChatThread(KviWindow *wnd,kvi_socket_t fd) +: KviDccThread(wnd,fd) +{ + m_pOutBuffers = new KviPointerList<KviDataBuffer>; + m_pOutBuffers->setAutoDelete(true); +} + + +KviDccChatThread::~KviDccChatThread() +{ + if(m_pOutBuffers)delete m_pOutBuffers; +} + + +void KviDccChatThread::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 *) kvi_realloc(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 *)kvi_realloc(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 *)kvi_realloc(data.buffer,data.iLen * sizeof(char)); + } else { + kvi_free(data.buffer); + data.buffer = 0; + } + + if(!handleInvalidSocketRead(readLen)) + { + if(data.iLen)handleIncomingData(&data,true); // critical + __range_invalid(data.iLen); + break; // error + } + } + } + msleep(100); + } + } + +out_of_the_loop: + + + if(data.iLen)kvi_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 KviDccChatThread::handleIncomingData(KviDccThreadIncomingData * data,bool bCritical) +{ + __range_valid(data->iLen); + __range_valid(data->buffer); + char * aux = data->buffer; + char * end = data->buffer + data->iLen; + while(aux != end) + { + if((*aux == '\n') || (*aux == '\0')) + { + KviThreadDataEvent<KviStr> * e = new KviThreadDataEvent<KviStr>(KVI_DCC_THREAD_EVENT_DATA); + // The left part is len chars long + int len = aux - data->buffer; +// debug("LEN = %d, iLen = %d",len,data->iLen); +//#warning "DO IT BETTER (the \r cutting)" + KviStr * s = new KviStr(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); +// debug("iLen now = %d",data->iLen); + __range_valid(data->iLen >= 0); + if(data->iLen > 0) + { + // memmove the remaining part to the beginning + // aux points after \n or \0 + kvi_memmove(data->buffer,aux,data->iLen); + data->buffer = (char *)kvi_realloc(data->buffer,data->iLen); + end = data->buffer + data->iLen; + aux = data->buffer; + } else { + // no more data in the buffer + __range_valid(data->iLen == 0); + kvi_free(data->buffer); + data->buffer = end = aux = 0; + } + postEvent(parent(),e); + } else aux++; +// debug("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<KviStr> * e = new KviThreadDataEvent<KviStr>(KVI_DCC_THREAD_EVENT_DATA); + KviStr * s = new KviStr(data->buffer,data->iLen); + if(s->lastCharIs('\r'))s->cutRight(1); + e->setData(s); + data->iLen = 0; + kvi_free(data->buffer); + data->buffer = 0; + postEvent(parent(),e); + } + } + return true; +} + +void KviDccChatThread::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 KviDccChatThread::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(); +#ifdef COMPILE_ON_WINDOWS + 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; +} + +#include "m_chat.moc" |