You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
kvirc/src/modules/dcc/chat.cpp

843 lines
25 KiB

//=======================================================================================
//
// 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 <tqsplitter.h>
#include <tqevent.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 TQSplitter(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 TQSplitter(Qt::Horizontal,this,"splitter");
m_pIrcView = new KviIrcView(m_pSplitter,pFrm,this);
connect(m_pIrcView,TQT_SIGNAL(rightClicked()),this,TQT_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,TQT_SIGNAL(error(int)),this,TQT_SLOT(handleMarshalError(int)));
connect(m_pMarshal,TQT_SIGNAL(connected()),this,TQT_SLOT(connected()));
connect(m_pMarshal,TQT_SIGNAL(inProgress()),this,TQT_SLOT(connectionInProgress()));
#ifdef COMPILE_SSL_SUPPORT
connect(m_pMarshal,TQT_SIGNAL(startingSSLHandshake()),this,TQT_SLOT(startingSSLHandshake()));
connect(m_pMarshal,TQT_SIGNAL(sslError(const char *)),this,TQT_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(TQT_TQOBJECT(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"));
}
}
}
}
TQString port = !m_pDescriptor->szFakePort.isEmpty() ? m_pDescriptor->szFakePort : TQString(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));
TQString szReq = TQString("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,TQString(msg),m_pDescriptor->idString()))
output(KVI_OUT_DCCERROR,__tr2qs_ctx("[SSL ERROR]: %s","dcc"),msg);
#endif
}
const TQString & 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()
{
TQString tmp = TQString("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>",
TQString(KVI_OPTION_COLOR(KviOption_colorCaptionTextActive).name()).ascii(),tmp.utf8().data());
m_szHtmlInactiveCaption.sprintf("<nobr><font color=\"%s\"><b>%s</b></font></nobr>",
TQString(KVI_OPTION_COLOR(KviOption_colorCaptionTextInactive).name()).ascii(),tmp.utf8().data());
}
TQPixmap * 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 TQString &text)
{
if(!m_pSlaveThread)
{
output(KVI_OUT_SYSTEMWARNING,__tr2qs_ctx("Cannot send data: No active connection","dcc"));
return;
}
KviTQCString 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());
TQString 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
{
TQString 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);
TQString 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 TQString & KviDccChat::localNick()
{
// FIXME: This is just a complete HACK
m_szLocalNick = m_pDescriptor->szLocalNick;
return m_szLocalNick;
}
void KviDccChat::ownAction(const TQString &text)
{
if(m_pSlaveThread)
{
KviTQCString 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(TQEvent *e)
{
if(e->type() == KVI_THREAD_EVENT)
{
switch(((KviThreadEvent *)e)->id())
{
case KVI_DCC_THREAD_EVENT_ERROR:
{
int * err = ((KviThreadDataEvent<int> *)e)->getData();
TQString 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,TQString(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
{
TQString 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,TQString(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(TQResizeEvent *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);
}
TQSize KviDccChat::sizeHint() const
{
TQSize ret(m_pIrcView->sizeHint().width(),
m_pIrcView->sizeHint().height() + m_pInput->heightHint());
return ret;
}
void KviDccChat::handleMarshalError(int err)
{
TQString 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(TQT_TQOBJECT(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"