summaryrefslogtreecommitdiffstats
path: root/src/modules/dcc/requests.cpp
diff options
context:
space:
mode:
authortpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-02-24 02:13:59 +0000
committertpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-02-24 02:13:59 +0000
commita6d58bb6052ac8cb01805a48c4ad2f129126116f (patch)
treedd867a099fcbb263a8009a9fb22695b87855dad6 /src/modules/dcc/requests.cpp
downloadkvirc-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/requests.cpp')
-rw-r--r--src/modules/dcc/requests.cpp1154
1 files changed, 1154 insertions, 0 deletions
diff --git a/src/modules/dcc/requests.cpp b/src/modules/dcc/requests.cpp
new file mode 100644
index 00000000..e1e18143
--- /dev/null
+++ b/src/modules/dcc/requests.cpp
@@ -0,0 +1,1154 @@
+//=============================================================================
+//
+// File : requests.cpp
+// Creation date : Tue Jul 23 02:44:38 2002 GMT by Szymon Stefanek
+//
+// This file is part of the KVirc irc client distribution
+// Copyright (C) 2002 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.
+//
+//=============================================================================
+
+#define _KVI_DEBUG_CHECK_RANGE_
+#include "kvi_debug.h"
+#include "kvi_settings.h"
+#include "kvi_string.h"
+#include "kvi_module.h"
+#include "kvi_sparser.h"
+#include "kvi_locale.h"
+#include "kvi_out.h"
+#include "kvi_console.h"
+#include "kvi_netutils.h"
+#include "kvi_frame.h"
+#include "kvi_console.h"
+
+#include "kvi_error.h"
+#include "kvi_options.h"
+#include "kvi_defaults.h"
+#include "kvi_sharedfiles.h"
+#include "kvi_mirccntrl.h"
+#include "kvi_app.h"
+#include "kvi_ircconnection.h"
+#include "kvi_ircconnectionuserinfo.h"
+
+#include "gsmcodec.h"
+#include "broker.h"
+#include "voice.h"
+#include "utils.h"
+#include "send.h"
+
+#include <qfileinfo.h>
+
+#ifdef COMPILE_ON_WINDOWS
+ // Ugly Windoze compiler...
+ #include "dialogs.h"
+#endif
+
+//#warning "KviOption_boolIgnoreDccChat and other types too"
+
+extern KVIRC_API KviSharedFilesManager * g_pSharedFilesManager;
+extern KviDccBroker * g_pDccBroker;
+
+static void dcc_module_reply_errmsg(KviDccRequest * dcc,const QString& errText)
+{
+ dcc->ctcpMsg->msg->console()->connection()->sendFmtData(
+ "NOTICE %s :%cERRMSG %s%c",
+ dcc->ctcpMsg->msg->console()->connection()->encodeText(dcc->ctcpMsg->pSource->nick()).data(),0x01,
+ dcc->ctcpMsg->msg->console()->connection()->encodeText(errText).data()
+ ,0x01);
+}
+
+static void dcc_module_request_error(KviDccRequest * dcc,const QString& errText)
+{
+ dcc->ctcpMsg->msg->console()->output(KVI_OUT_DCCERROR,
+ __tr2qs_ctx("Unable to process the above request: %Q, %Q","dcc"),
+ &errText,
+ KVI_OPTION_BOOL(KviOption_boolNotifyFailedDccHandshakes) ? &(__tr2qs_ctx("Ignoring and notifying failure","dcc")) : &(__tr2qs_ctx("Ignoring","dcc")));
+
+ if(KVI_OPTION_BOOL(KviOption_boolNotifyFailedDccHandshakes))
+ {
+ QString szError = QString("Sorry, your DCC %1 request can't be satisfied: %2").arg(dcc->szType.ptr()).arg(errText);
+ dcc_module_reply_errmsg(dcc,szError);
+ }
+}
+
+static bool dcc_module_check_concurrent_transfers_limit(KviDccRequest * dcc)
+{
+ if(KVI_OPTION_UINT(KviOption_uintMaxDccSendTransfers) > 0)
+ {
+ unsigned int uTransfers = KviDccFileTransfer::runningTransfersCount();
+ if(uTransfers >= KVI_OPTION_UINT(KviOption_uintMaxDccSendTransfers))
+ {
+ KviStr szError(KviStr::Format,__tr2qs_ctx("Concurrent transfer limit reached (%u of %u transfers running)","dcc"),
+ uTransfers,KVI_OPTION_UINT(KviOption_uintMaxDccSendTransfers));
+ dcc_module_request_error(dcc,szError.ptr());
+ return false;
+ }
+ }
+ return true;
+}
+
+static bool dcc_module_check_limits(KviDccRequest * dcc)
+{
+ if(KVI_OPTION_UINT(KviOption_uintMaxDccSlots) > 0)
+ {
+ unsigned int uWindows = g_pDccBroker->dccWindowsCount();
+ if(uWindows >= KVI_OPTION_UINT(KviOption_uintMaxDccSlots))
+ {
+ KviStr szError(KviStr::Format,__tr2qs_ctx("Slot limit reached (%u slots of %u)","dcc"),
+ uWindows,KVI_OPTION_UINT(KviOption_uintMaxDccSlots));
+ dcc_module_request_error(dcc,szError.ptr());
+ return false;
+ }
+ }
+ if(g_pDccBroker->dccBoxCount() >= 32)
+ {
+ // there are too many pending dcc requests: the user isn't watching....
+ dcc_module_request_error(dcc,__tr2qs_ctx("Too many pending connections","dcc"));
+ return false;
+ }
+ return true;
+}
+
+static void dcc_fill_local_nick_user_host(KviDccDescriptor * d,KviDccRequest * dcc)
+{
+ if(dcc->pConsole->connection())
+ {
+ d->szLocalNick = dcc->pConsole->connection()->userInfo()->nickName();
+ d->szLocalUser = dcc->pConsole->connection()->userInfo()->userName();
+ d->szLocalHost = dcc->pConsole->connection()->userInfo()->hostName();
+ } else {
+ d->szLocalNick = __tr_ctx("unknown","dcc");
+ d->szLocalUser = __tr2qs_ctx("unknown","dcc");
+ d->szLocalHost = __tr2qs_ctx("unknown","dcc");
+ }
+}
+
+static void dcc_module_set_dcc_type(KviDccDescriptor * d,const char * szBaseType)
+{
+ d->szType = szBaseType;
+#ifdef COMPILE_SSL_SUPPORT
+ if(d->bIsSSL)d->szType.prepend('S');
+#endif
+ if(d->bIsTdcc)d->szType.prepend('T');
+}
+
+
+static bool dcc_module_normalize_target_data(KviDccRequest * dcc,KviStr &ipaddr,KviStr &port)
+{
+ if(!port.isUnsignedNum())
+ {
+ if(!dcc->ctcpMsg->msg->haltOutput())
+ {
+ KviStr szError(KviStr::Format,__tr2qs_ctx("Invalid port number %s","dcc"),port.ptr());
+ dcc_module_request_error(dcc,szError.ptr());
+ }
+ return false;
+ }
+
+ struct in_addr addr;
+
+ if(ipaddr.isUnsignedNum())
+ {
+ addr.s_addr = htonl((unsigned long)ipaddr.toULong());
+ QString tmp;
+ if(!kvi_binaryIpToStringIp(addr,tmp))
+ {
+ if(!dcc->ctcpMsg->msg->haltOutput())
+ {
+ KviStr szError(KviStr::Format,__tr2qs_ctx("Invalid IP address in old format %s","dcc"),ipaddr.ptr());
+ dcc_module_request_error(dcc,szError.ptr());
+ }
+ return false;
+ }
+ ipaddr = tmp;
+ } else {
+ if(!kvi_stringIpToBinaryIp(ipaddr,&addr))
+ {
+#ifdef COMPILE_IPV6_SUPPORT
+ struct in6_addr addr6;
+ if(kvi_stringIpToBinaryIp_V6(ipaddr,&addr6))
+ {
+ dcc->bIpV6 = true;
+ return true; // IPV6 address.
+ }
+#endif
+ if(!dcc->ctcpMsg->msg->haltOutput())
+ {
+ KviStr szError(KviStr::Format,__tr2qs_ctx("Invalid IP address %s","dcc"),ipaddr.ptr());
+ dcc_module_request_error(dcc,szError.ptr());
+ }
+ return false;
+ }
+ }
+ return true;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// CHAT
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+static void dccModuleParseDccChat(KviDccRequest *dcc)
+{
+ //
+ // We have received a DCC CHAT request in the following form:
+ //
+ // DCC CHAT chat <ipaddress> <port>
+ //
+ // This means that we're requested to setup an ACTIVE chat connection
+ // ... Easy task :)
+ //
+ // Anybody understands the meaning of the secondo "chat" in there ?
+ // It was meant to simplify the parsing ? :DDD
+ //
+ // There is a mIrc extension that allows <port> to be 0
+ // and adds a last parameter that seems to be a random number (thnx YaP :)
+ // that is used to keep track of the connection.
+ // This extension is used by firewalled machines to initiate a DCC CHAT:
+ // the receiving side should respond with a DCC CHAT offer
+ // with the same random number appended, and then should listen for a connection.
+ //
+ // when a zero port request is initiated by another party we get
+ //
+ // DCC CHAT chat <fakeipaddress> 0 <tag>
+ //
+ // and we reply with
+ //
+ // DCC CHAT chat <ourip> <ourport> <tag>
+ //
+ // when a zero port request is initiated by us we send out
+ //
+ // DCC CHAT chat <fakeipaddress> 0 <tag>
+ //
+ // and we get
+ //
+ // DCC CHAT chat <remoteip> <remoteport> <tag>
+ //
+ // Thus if there is a <tag> and the port is 0, then the remote party
+ // wanted to estabilish a dcc with us and wants us to listen, but if the port is nonzero then
+ // we have sent out a zero port request and the remote party acked it
+ // thus we have to connect instead!
+ //
+
+ // First of all we check the dcc slot limits
+ if(!dcc_module_check_limits(dcc))return;
+
+ // Then we check the target host data
+ if(!dcc_module_normalize_target_data(dcc,dcc->szParam2,dcc->szParam3))return;
+
+ if(!kvi_strEqualCI(dcc->szParam1.ptr(),"chat"))
+ {
+ if(!dcc->ctcpMsg->msg->haltOutput())
+ {
+ dcc->ctcpMsg->msg->console()->output(KVI_OUT_DCCMSG,
+ __tr2qs_ctx("The above request is broken: The second parameter is '%s' and should be 'chat', trying to continue","dcc"),dcc->szParam1.ptr());
+ }
+ }
+
+ KviStr szExtensions = dcc->szType;
+ szExtensions.cutRight(4); // cut off CHAT
+
+#ifdef COMPILE_SSL_SUPPORT
+ bool bSSLExtension = szExtensions.contains('S',false);
+#else //!COMPILE_SSL_SUPPORT
+ if(szExtensions.contains('S',false))
+ {
+ dcc_module_request_error(dcc,__tr2qs_ctx("This executable has been compiled without SSL support, the SSL extension to DCC CHAT is not available","dcc"));
+ return;
+ }
+#endif //!COMPILE_SSL_SUPPORT
+
+ KviDccDescriptor * d = new KviDccDescriptor(dcc->pConsole);
+
+ d->szNick = dcc->ctcpMsg->pSource->nick();
+ d->szUser = dcc->ctcpMsg->pSource->username();
+ d->szHost = dcc->ctcpMsg->pSource->host();
+
+ dcc_fill_local_nick_user_host(d,dcc);
+
+ d->szIp = dcc->szParam2.ptr();
+ d->szPort = dcc->szParam3.ptr();
+
+
+ if(dcc->szParam4.hasData())
+ {
+ // zero port tag ?
+ if(d->szPort == "0")
+ {
+ // zero port request
+ if(KVI_OPTION_BOOL(KviOption_boolDccSendFakeAddressByDefault))
+ {
+ d->szFakeIp = KVI_OPTION_STRING(KviOption_stringDefaultDccFakeAddress);
+ if(d->szFakeIp.isEmpty())KVI_OPTION_BOOL(KviOption_boolDccSendFakeAddressByDefault) = false;
+ }
+ d->setZeroPortRequestTag(dcc->szParam4.ptr());
+ QString tmp;
+ if(!dcc_kvs_get_listen_ip_address(0,d->console(),tmp))d->szListenIp = "0.0.0.0";
+ else d->szListenIp=tmp;
+ d->szListenPort = "0"; // any port is OK
+ d->bAutoAccept = KVI_OPTION_BOOL(KviOption_boolAutoAcceptDccChat);
+ d->bActive = false; // we must listen then...
+ } else {
+ // zero port acknowledge
+ // check if this is a tag that we have sent out
+ QString szTag = QString(dcc->szParam4.ptr());
+ KviDccZeroPortTag * t = g_pDccBroker->findZeroPortTag(szTag);
+ if(!t)
+ {
+ // hum.. not our tag
+
+ // FIXME: As segnaled by PRAEDO, ezbounce seems to send a fourth parameter in response to /quote ezb log
+ // Pragma: That's a bug in ezbounce, it sends the filesize of the log as a DCC CHAT parameter...
+ // The author probably copied and pasted the CTCP line from DCC SEND and forgot to remove the filesize.
+ // We *could* add an option to ignore the last parameter and treat it as a standard dcc chat
+ // request, but since we don't encourage bugs, we don't do it :D
+ // Mail me at pragma at kvirc dot net if you really think it's necessary.
+ dcc->ctcpMsg->msg->console()->output(KVI_OUT_DCCMSG,
+ __tr2qs_ctx("The above request is broken: it looks like a zero port tag acknowledge but I have either never seen this tag or it was sent more than 120 seconds ago","dcc"));
+ dcc_module_request_error(dcc,__tr2qs_ctx("It seems that I haven't requested this dcc chat","dcc"));
+ delete d;
+ return;
+ } else {
+ g_pDccBroker->removeZeroPortTag(szTag);
+ }
+
+ d->bAutoAccept = true; // auto-accept it (we have sent it out)
+ d->bActive = true;
+ }
+ } else {
+ d->bAutoAccept = KVI_OPTION_BOOL(KviOption_boolAutoAcceptDccChat);
+ d->bActive = true; // we have to connct (standard active chat)
+ }
+
+#ifdef COMPILE_SSL_SUPPORT
+ d->bIsSSL = bSSLExtension;
+#endif
+
+ dcc_module_set_dcc_type(d,"CHAT");
+ d->triggerCreationEvent();
+
+ g_pDccBroker->handleChatRequest(d);
+}
+
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// SEND
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+static void dccModuleParseDccRecv(KviDccRequest * dcc);
+
+static void dccModuleParseDccSend(KviDccRequest *dcc)
+{
+//#warning "Ignore files depending on file type ? (MediaType ?)"
+ //
+ // We have received a DCC SEND request in the following form
+ //
+ // DCC [ST]SEND <filename> <ipaddress> <port> <filesize>
+ //
+ // Now the things are a bit tricky... we eventually can
+ // reply with a DCC RESUME and receive a DCC ACCEPT then
+ // The format of these requests is:
+ //
+ // DCC RESUME <filename> <port> <resumepos>
+ // ACCEPT <filename> <port> <resumepos>
+ //
+ // There is a mIrc extension that allows <port> to be 0
+ // and adds a last parameter that seems to be a random number (thnx YaP :)
+ // that is used to keep track of the connection.
+ // This extension is used by firewalled machines to initiate a DCC SEND:
+ // the receiving side should respond with a DCC SEND offer
+ // with the same random number appended, listen for a connection, and receive the file
+ // instead of sending it.
+ //
+ // when a zero port request is initiated by another party we get
+ // DCC SEND <filename> <fakeipaddress> 0 <filesize> <tag>
+ // if (and only if) we want to resume we reply with
+ // DCC RESUME <filename> 0 <resumesize> <tag>
+ // in this case the remote part replies again with
+ // DCC ACCEPT <filename> 0 <resumesize> <tag>
+ // and we finally reply with
+ // DCC SEND <filename> <ourip> <ourport> <filesize> <tag>
+ //
+ // when a zero port request is initiated by us we send out
+ // DCC SEND <filename> <fakeipaddress> 0 <filesize> <tag>
+ // and if the remote party wants to resume then we get
+ // DCC RESUME <filename> 0 <resumesize> <tag>
+ // and we eventually reply with
+ // DCC ACCEPT <filename> 0 <resumesize> <tag>
+ // and we finally get
+ // DCC SEND <filename> <remoteip> <remoteport> <filesize> <tag>
+ //
+ // Thus if there is a <tag> and the port is 0, then the remote party
+ // is trying to send a file to us, but if the port is nonzero then
+ // we have sent out a zero port request and the remote party acked it
+ //
+
+ if((!kvi_strEqualCS(dcc->szParam3.ptr(),"0")) && dcc->szParam5.hasData())
+ {
+ // DCC SEND <filename> <remoteip> <remoteport> <filesize> <tag>
+ // zero port acknowledge: treat as a RECV that should look like
+ // DCC [TS]RECV <filename> <remoteip> <remoteport> <resume-filesize>
+ // but since we have stored the sharedfile with the name <tag>
+ // we do exchange the params :)
+
+ KviDccZeroPortTag * t = g_pDccBroker->findZeroPortTag(dcc->szParam5.ptr());
+ if(t)
+ {
+ dcc->szParam4.sprintf("%u",t->m_uResumePosition);
+ g_pDccBroker->removeZeroPortTag(dcc->szParam5.ptr());
+ } else {
+ // this should never happen since we always add
+ // a zero port tag for out outgoing requests
+ // but well... maybe the user did something behing our back...
+ dcc->szParam4 = "0"; // no resume possible in this case
+ }
+
+ // swap the tag and the filename (we have added a fileoffer with this tag)
+ dcc->szParam1 = dcc->szParam5;
+ dcc->szParam5 = "";
+
+ dccModuleParseDccRecv(dcc);
+ return;
+ }
+
+ // First of all we check the transfer limits
+ dcc->szParam1=dcc->pConsole->decodeText(dcc->szParam1);
+ if(!dcc_module_check_limits(dcc))return;
+ if(!dcc_module_check_concurrent_transfers_limit(dcc))return;
+
+ // Then we ensure that the data that the remote end has sent are valid
+ if(!dcc_module_normalize_target_data(dcc,dcc->szParam2,dcc->szParam3))return;
+
+ if(!(dcc->szParam4.isUnsignedNum()))
+ {
+ if(!dcc->ctcpMsg->msg->haltOutput())
+ {
+ dcc->ctcpMsg->msg->console()->output(KVI_OUT_DCCMSG,
+ __tr2qs_ctx("The above request is broken: The fourth parameter should be the file size but does not appear to be an unsigned number, trying to continue","dcc"),dcc->szParam4.ptr());
+ }
+ dcc->szParam4 = __tr2qs_ctx("<unknown size>","dcc");
+ }
+
+ if(dcc->szParam1.contains('/'))
+ {
+ if(!dcc->ctcpMsg->msg->haltOutput())
+ {
+ dcc->ctcpMsg->msg->console()->output(KVI_OUT_DCCMSG,
+ __tr2qs_ctx("The above request is broken: The filename contains path components, stripping the leading path and trying to continue","dcc"),dcc->szParam1.ptr());
+ }
+ dcc->szParam1.cutToLast('/');
+ }
+
+ KviStr szExtensions = dcc->szType;
+ szExtensions.cutRight(4); // cut off SEND
+
+ bool bTurboExtension = szExtensions.contains('T',false);
+#ifdef COMPILE_SSL_SUPPORT
+ bool bSSLExtension = szExtensions.contains('S',false);
+#else //!COMPILE_SSL_SUPPORT
+ if(szExtensions.contains('S',false))
+ {
+ dcc_module_request_error(dcc,__tr2qs_ctx("This executable has been compiled without SSL support, the SSL extension to DCC SEND is not available","dcc"));
+ return;
+ }
+#endif //!COMPILE_SSL_SUPPORT
+
+ KviDccDescriptor * d = new KviDccDescriptor(dcc->pConsole);
+ d->szNick = dcc->ctcpMsg->pSource->nick();
+ d->szUser = dcc->ctcpMsg->pSource->username();
+ d->szHost = dcc->ctcpMsg->pSource->host();
+ dcc_fill_local_nick_user_host(d,dcc);
+
+ d->szIp = dcc->szParam2.ptr();
+ d->szPort = dcc->szParam3.ptr();
+ d->szFileName = dcc->szParam1.ptr();
+ d->szFileSize = dcc->szParam4.ptr();
+
+ if(d->szPort=="0" && dcc->szParam5.hasData())
+ {
+ if(KVI_OPTION_BOOL(KviOption_boolDccSendFakeAddressByDefault))
+ {
+ d->szFakeIp = KVI_OPTION_STRING(KviOption_stringDefaultDccFakeAddress);
+ if(d->szFakeIp.isEmpty())KVI_OPTION_BOOL(KviOption_boolDccSendFakeAddressByDefault) = false;
+ }
+ d->setZeroPortRequestTag(dcc->szParam5.ptr());
+ QString tmp;
+ if(!dcc_kvs_get_listen_ip_address(0,d->console(),tmp))d->szListenIp = "0.0.0.0";
+ else d->szListenIp=QString(tmp);
+ d->szListenPort = "0"; // any port is OK
+ d->bSendRequest = true;
+ d->szLocalFileSize = d->szFileSize;
+ }
+
+ d->bActive = !d->isZeroPortRequest(); // we have to connect unless it is a zero port request
+
+ d->bResume = false;
+ d->bRecvFile = true;
+ d->bIsTdcc = bTurboExtension;
+ d->bNoAcks = d->bIsTdcc;
+#ifdef COMPILE_SSL_SUPPORT
+ d->bIsSSL = bSSLExtension;
+#endif
+ d->bOverrideMinimize = false;
+ d->bAutoAccept = KVI_OPTION_BOOL(KviOption_boolAutoAcceptDccSend);
+
+ d->bIsIncomingAvatar = g_pApp->findPendingAvatarChange(dcc->pConsole,d->szNick,d->szFileName);
+ dcc_module_set_dcc_type(d,"RECV");
+ if(KVI_OPTION_BOOL(KviOption_boolAutoAcceptIncomingAvatars))d->bAutoAccept = d->bAutoAccept || d->bIsIncomingAvatar;
+ d->triggerCreationEvent();
+
+ g_pDccBroker->recvFileManage(d);
+}
+
+static void dccModuleParseDccAccept(KviDccRequest *dcc)
+{
+ // this is usually DCC ACCEPT <filename> <port> <resumesize>
+ // but may be also
+ // DCC ACCEPT <filename> 0 <resumesize> <tag>
+ if(!g_pDccBroker->handleResumeAccepted(dcc->szParam1.ptr(),dcc->szParam2.ptr(),dcc->szParam4.ptr()))
+ {
+//#warning "IF KviOption_boolReplyCtcpErrmsgOnInvalidAccept..."
+ if(!dcc->ctcpMsg->msg->haltOutput())
+ {
+ KviStr szError(KviStr::Format,__tr2qs_ctx("Can't proceed with DCC RECV: Transfer not initiated for file %s on port %s","dcc"),dcc->szParam1.ptr(),dcc->szParam2.ptr());
+ dcc_module_request_error(dcc,szError.ptr());
+ }
+ }
+}
+
+static void dccModuleParseDccResume(KviDccRequest *dcc)
+{
+ // This is usually RESUME <filename> <port> <resumesize>
+
+ // when a zero port request is initiated by us we send out
+ // DCC SEND <filename> <fakeipaddress> 0 <filesize> <tag>
+ // and if the remote party wants to resume then we get
+ // DCC RESUME <filename> 0 <resumesize> <tag>
+ // and we eventually reply with
+ // DCC ACCEPT <filename> 0 <resumesize> <tag>
+ // and we finally get
+ // DCC SEND <filename> <remoteip> <remoteport> <filesize> <tag>
+
+ bool bOk;
+ unsigned int filePos = dcc->szParam3.toUInt(&bOk);
+ if(!bOk)
+ {
+ if(!dcc->ctcpMsg->msg->haltOutput())
+ {
+ KviStr szError(KviStr::Format,__tr2qs_ctx("Invalid resume position argument '%s'","dcc"),dcc->szParam3.ptr());
+ dcc_module_request_error(dcc,szError.ptr());
+ }
+ return;
+ }
+
+ if(!g_pDccBroker->handleResumeRequest(dcc,dcc->szParam1.ptr(),dcc->szParam2.ptr(),filePos,dcc->szParam4.ptr()))
+ {
+//#warning "IF KviOption_boolReplyCtcpErrmsgOnInvalidResume..."
+ if(!dcc->ctcpMsg->msg->haltOutput())
+ {
+ KviStr szError(KviStr::Format,
+ __tr2qs_ctx("Can't proceed with DCC SEND: Transfer not initiated for file %s on port %s, or invalid resume size","dcc"),
+ dcc->szParam1.ptr(),dcc->szParam2.ptr());
+ dcc_module_request_error(dcc,szError.ptr());
+ }
+ }
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// RECV
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+static void dccModuleParseDccRecv(KviDccRequest * dcc)
+{
+ // DCC [TS]RECV <filename> <ipaddr> <port> <resume-filesize>
+ if(!dcc_module_check_limits(dcc))return;
+ if(!dcc_module_check_concurrent_transfers_limit(dcc))return;
+
+ if(!dcc_module_normalize_target_data(dcc,dcc->szParam2,dcc->szParam3))return;
+
+ if(!(dcc->szParam4.isUnsignedNum()))
+ {
+ if(!dcc->ctcpMsg->msg->haltOutput())
+ {
+ dcc->ctcpMsg->msg->console()->outputNoFmt(KVI_OUT_DCCMSG,
+ __tr2qs_ctx("The above request has resume file size missing, assuming a resume file size of 0","dcc"));
+ }
+ dcc->szParam4 = "0";
+ }
+
+ if(dcc->szParam1.contains('/'))
+ {
+ if(!dcc->ctcpMsg->msg->haltOutput())
+ {
+ dcc->ctcpMsg->msg->console()->output(KVI_OUT_DCCMSG,
+ __tr2qs_ctx("The above request is broken: The filename contains path components, stripping the leading path and trying to continue","dcc"),dcc->szParam1.ptr());
+ }
+ dcc->szParam1.cutToLast('/');
+ }
+
+ KviStr szExtensions = dcc->szType;
+ szExtensions.cutRight(4); // cut off RECV
+
+ bool bTurboExtension = szExtensions.contains('T',false);
+#ifdef COMPILE_SSL_SUPPORT
+ bool bSSLExtension = szExtensions.contains('S',false);
+#else //!COMPILE_SSL_SUPPORT
+ if(szExtensions.contains('S',false))
+ {
+ dcc_module_request_error(dcc,__tr2qs_ctx("This executable has been compiled without SSL support, the SSL extension to DCC RECV is not available","dcc"));
+ return;
+ }
+#endif //!COMPILE_SSL_SUPPORT
+
+ // If we have a file offer for this...do it automatically
+ KviSharedFile * o = g_pSharedFilesManager->lookupSharedFile(dcc->szParam1.ptr(),dcc->ctcpMsg->pSource,0);
+ if(o)
+ {
+
+ unsigned int uResumeSize = dcc->szParam4.toUInt(); // this will NEVER fail
+ if(uResumeSize >= o->fileSize())
+ {
+ // senseless request
+ KviStr szError(KviStr::Format,
+ __tr2qs_ctx("Invalid RECV request: Position %u is is larger than file size","dcc"),uResumeSize);
+ dcc_module_request_error(dcc,szError.ptr());
+ return;
+ }
+
+ // ok...we have requested this send
+// #warning "Maybe remove this file offer now ?"
+ KviDccDescriptor * d = new KviDccDescriptor(dcc->pConsole);
+
+ d->szNick = dcc->ctcpMsg->pSource->nick();
+ d->szUser = dcc->ctcpMsg->pSource->user();
+ d->szHost = dcc->ctcpMsg->pSource->host();
+
+ d->szFileName = dcc->szParam1.ptr();
+ d->szFileSize = dcc->szParam4.ptr();
+
+ //d->bResume = false; // This is actually useless
+
+ d->szLocalFileName = o->absFilePath();
+ d->szLocalFileSize.setNum(o->fileSize()); // Should we look it up again ?
+
+
+ d->bRecvFile = false;
+ d->bNoAcks = bTurboExtension;
+
+ d->bAutoAccept = true;
+ d->bIsIncomingAvatar = false;
+
+ d->bIsTdcc = bTurboExtension;
+#ifdef COMPILE_SSL_SUPPORT
+ d->bIsSSL = bSSLExtension;
+#endif
+
+ d->bOverrideMinimize = false;
+
+ // We know everything
+ dcc_fill_local_nick_user_host(d,dcc);
+
+
+ d->bDoTimeout = true;
+
+ d->szIp = dcc->szParam2.ptr();
+ d->szPort = dcc->szParam3.ptr();
+
+ d->bActive = true;
+ dcc_module_set_dcc_type(d,"SEND");
+ d->triggerCreationEvent();
+ g_pDccBroker->sendFileExecute(0,d);
+
+ return;
+
+ } else {
+
+ dcc->ctcpMsg->msg->console()->output(KVI_OUT_DCCMSG,
+ __tr2qs_ctx("%Q [%Q@%Q] is ready to receive the file \"%s\"","dcc"),
+ &(dcc->ctcpMsg->pSource->nick()),
+ &(dcc->ctcpMsg->pSource->username()),
+ &(dcc->ctcpMsg->pSource->host()),
+ dcc->szParam1.ptr());
+ dcc->ctcpMsg->msg->console()->output(KVI_OUT_DCCMSG,
+ __tr2qs_ctx("The remote client is listening on interface %s and port %s","dcc"),dcc->szParam2.ptr(),dcc->szParam3.ptr());
+ KviStr szSwitches = "-c";
+ if(bTurboExtension)szSwitches.prepend("-t ");
+#ifdef COMPILE_SSL_SUPPORT
+ if(bSSLExtension)szSwitches.prepend("-s ");
+#endif
+ dcc->ctcpMsg->msg->console()->output(KVI_OUT_DCCMSG,
+ __tr2qs_ctx("Use %c\r![!dbl]dcc.send %s -i=%s -p=%s %Q\r/dcc.send %s -i=%s -p=%s %Q\r%c to send the file (or double-click on the socket)","dcc"),
+ KVI_TEXT_BOLD,
+ szSwitches.ptr(),
+ dcc->szParam2.ptr(),dcc->szParam3.ptr(),&(dcc->ctcpMsg->pSource->nick()),
+ szSwitches.ptr(),
+ dcc->szParam2.ptr(),dcc->szParam3.ptr(),&(dcc->ctcpMsg->pSource->nick()),
+ KVI_TEXT_BOLD);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// RSEND
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+static void dccModuleParseDccRSend(KviDccRequest *dcc)
+{
+ // DCC RSEND <filename> <filesize>
+//#warning "Ignore files depending on file type ? (MediaType ?)"
+ //
+ // We have received a DCC RSEND request in the following form
+ //
+ // DCC [ST]RSEND <filename> <filesize>
+ //
+ dcc->szParam1 = dcc->pConsole->decodeText(dcc->szParam1);
+ if(!dcc_module_check_limits(dcc))return;
+ if(!dcc_module_check_concurrent_transfers_limit(dcc))return;
+
+ if(!(dcc->szParam2.isUnsignedNum()))
+ {
+ if(!dcc->ctcpMsg->msg->haltOutput())
+ {
+ dcc->ctcpMsg->msg->console()->output(KVI_OUT_DCCMSG,
+ __tr2qs_ctx("The above request is broken: The fourth parameter should be the file size but does not appear to be an unsigned number; trying to continue","dcc"),dcc->szParam2.ptr());
+ }
+ dcc->szParam2 = __tr_ctx("<unknown size>","dcc");
+ }
+
+ if(dcc->szParam1.contains('/'))
+ {
+ if(!dcc->ctcpMsg->msg->haltOutput())
+ {
+ dcc->ctcpMsg->msg->console()->output(KVI_OUT_DCCMSG,
+ __tr2qs_ctx("The above request is broken: The filename contains path components, stripping the leading path and trying to continue","dcc"),dcc->szParam1.ptr());
+ }
+ dcc->szParam1.cutToLast('/');
+ }
+
+ KviStr szExtensions = dcc->szType;
+ szExtensions.cutRight(4); // cut off SEND
+
+ bool bTurboExtension = szExtensions.contains('T',false);
+#ifdef COMPILE_SSL_SUPPORT
+ bool bSSLExtension = szExtensions.contains('S',false);
+#else //!COMPILE_SSL_SUPPORT
+ if(szExtensions.contains('S',false))
+ {
+ dcc_module_request_error(dcc,__tr2qs_ctx("This executable has been compiled without SSL support, the SSL extension to DCC RSEND is not available","dcc"));
+ return;
+ }
+#endif //!COMPILE_SSL_SUPPORT
+
+//#warning "When behind a firewall, we should reply an error message and avoid setting up the listening connection"
+
+ KviDccDescriptor * d = new KviDccDescriptor(dcc->pConsole);
+ d->szNick = dcc->ctcpMsg->pSource->nick();
+ d->szUser = dcc->ctcpMsg->pSource->username();
+ d->szHost = dcc->ctcpMsg->pSource->host();
+ d->szIp = __tr2qs_ctx("(unknown)","dcc");
+ d->szPort = d->szIp;
+ QString tmp;
+ if(!dcc_kvs_get_listen_ip_address(0,d->console(),tmp))
+ {
+ d->console()->output(KVI_OUT_DCCMSG,
+ __tr2qs_ctx("No suitable interface to listen on, trying to continue anyway...","dcc"));
+ d->szListenIp = "0.0.0.0";
+ } else
+ d->szListenIp=QString(tmp);
+
+ d->szListenPort = "0";
+ dcc_fill_local_nick_user_host(d,dcc);
+
+
+ d->szFileName = dcc->szParam1.ptr();
+ d->szFileSize = dcc->szParam2.ptr();
+ d->bActive = false; // we have to listen!
+ d->bResume = false;
+ d->bRecvFile = true; // we have to receive the file!
+
+#ifdef COMPILE_SSL_SUPPORT
+ d->bIsSSL = bSSLExtension;
+#endif
+ d->bIsTdcc = bTurboExtension;
+ d->bSendRequest = true; // we have to send the [ST]RECV request back
+ d->bNoAcks = d->bIsTdcc;
+ d->bOverrideMinimize = false;
+ d->bAutoAccept = KVI_OPTION_BOOL(KviOption_boolAutoAcceptDccSend);
+ d->bIsIncomingAvatar = g_pApp->findPendingAvatarChange(dcc->pConsole,d->szNick.utf8().data(),d->szFileName.utf8().data());
+
+ if(KVI_OPTION_BOOL(KviOption_boolDccSendFakeAddressByDefault))
+ {
+ d->szFakeIp = KVI_OPTION_STRING(KviOption_stringDefaultDccFakeAddress);
+ if(d->szFakeIp.isEmpty())KVI_OPTION_BOOL(KviOption_boolDccSendFakeAddressByDefault) = false;
+ }
+
+ if(KVI_OPTION_BOOL(KviOption_boolAutoAcceptIncomingAvatars))d->bAutoAccept = d->bAutoAccept || d->bIsIncomingAvatar;
+
+ dcc_module_set_dcc_type(d,"RECV");
+ d->triggerCreationEvent();
+ g_pDccBroker->recvFileManage(d);
+}
+
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// GET
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+static void dccModuleParseDccGet(KviDccRequest *dcc)
+{
+ // DCC [TS]GET <filename> [filesize]
+ // -> DCC [TS]SEND <filename> <ipaddr> <port> <filesize>
+ // ...
+ dcc->szParam1=dcc->pConsole->decodeText(dcc->szParam1);
+ bool bOk;
+ unsigned int uSize = dcc->szParam2.toUInt(&bOk);
+ if(!bOk)uSize = 0;
+
+ if(!dcc_module_check_limits(dcc))return;
+ if(!dcc_module_check_concurrent_transfers_limit(dcc))return;
+
+ KviStr szExtensions = dcc->szType;
+ szExtensions.cutRight(3); // cut off GET
+
+ bool bTurboExtension = szExtensions.contains('T',false);
+#ifdef COMPILE_SSL_SUPPORT
+ bool bSSLExtension = szExtensions.contains('S',false);
+#else //!COMPILE_SSL_SUPPORT
+ if(szExtensions.contains('S',false))
+ {
+ dcc_module_request_error(dcc,__tr2qs_ctx("This executable has been compiled without SSL support, the SSL extension to DCC GET is not available","dcc"));
+ return;
+ }
+#endif //!COMPILE_SSL_SUPPORT
+
+ KviSharedFile * o = g_pSharedFilesManager->lookupSharedFile(dcc->szParam1.ptr(),dcc->ctcpMsg->pSource,uSize);
+ if(!o)
+ {
+ if(!dcc->ctcpMsg->msg->haltOutput())
+ {
+ KviStr szError(KviStr::Format,
+ __tr2qs_ctx("No file offer named '%s' (with size %s) available for %Q [%Q@%Q]","dcc"),
+ dcc->szParam1.ptr(),uSize > 0 ? dcc->szParam2.ptr() : __tr_ctx("\"any\"","dcc"),
+ &(dcc->ctcpMsg->pSource->nick()),
+ &(dcc->ctcpMsg->pSource->username()),
+ &(dcc->ctcpMsg->pSource->host()));
+ dcc_module_request_error(dcc,szError.ptr());
+ }
+ return;
+ }
+
+//#warning "IF NOT IGNORE DCC GET!"
+
+//#warning "CREATE IT MINIMIZED ETC..."
+//#warning "MAYBE USE A DIALOG TO ACCEPT THE REQUEST ?"
+//#warning "DO NOT ACCEPT /etc/* requests..."
+
+ if(KVI_OPTION_BOOL(KviOption_boolCantAcceptIncomingDccConnections))
+ {
+ // we have to use DCC RSEND , otherwise it will not work
+ KviStr szSubproto("RSEND");
+ szSubproto.prepend(szExtensions);
+
+
+ QString szFileName = QFileInfo(o->absFilePath()).fileName();
+ if(o->name() != szFileName)
+ {
+ // BUG
+ // If the file offer was added with a name that is senseless (like "mediaXYZ" for an *.mp3 file)
+ // then we would be going to RSEND that name here: the remote user woulnd't be
+ // able to recognize the file.
+ // Here we add another temporary offer with the right filename.
+
+ // now add a file offer , so he we will accept it automatically
+ // 120 secs is a reasonable timeout
+ QString szMask;
+ dcc->ctcpMsg->pSource->mask(szMask,KviIrcMask::NickUserHost);
+
+ KviSharedFile * pOld = o;
+ o = g_pSharedFilesManager->addSharedFile(szFileName,o->absFilePath(),szMask,120);
+ if(!o)o = pOld; // give up (FIXME: should we notify that ?)
+ }
+
+ if(!dcc->ctcpMsg->msg->haltOutput())
+ {
+ dcc->ctcpMsg->msg->console()->output(KVI_OUT_DCCMSG,
+ __tr2qs_ctx("Accepting file request from %Q [%Q@%Q] for '%s' (real file: %Q), offering DCC %s since we can't accept incoming connections (user option)","dcc"),
+ &(dcc->ctcpMsg->pSource->nick()),
+ &(dcc->ctcpMsg->pSource->username()),
+ &(dcc->ctcpMsg->pSource->host()),dcc->szParam1.ptr(),
+ &(o->absFilePath()),szSubproto.ptr());
+ }
+
+ dcc->pConsole->connection()->sendFmtData("PRIVMSG %s :%cDCC %s %s %u%c",
+ dcc->pConsole->connection()->encodeText(dcc->ctcpMsg->pSource->nick()).data(),
+ 0x01,szSubproto.ptr(),
+ dcc->pConsole->connection()->encodeText(dcc->szParam1.ptr()).data(),o->fileSize(),0x01);
+ return;
+ }
+
+
+ KviDccDescriptor * d = new KviDccDescriptor(dcc->pConsole);
+ d->szNick = dcc->ctcpMsg->pSource->nick();
+ d->szLocalFileName = o->absFilePath();
+ d->szUser = dcc->ctcpMsg->pSource->username();
+ d->szHost = dcc->ctcpMsg->pSource->host();
+ d->bRecvFile = false;
+ dcc_fill_local_nick_user_host(d,dcc);
+
+ QString tmp;
+ if(!dcc_kvs_get_listen_ip_address(0,d->console(),tmp))
+ {
+ d->console()->output(KVI_OUT_DCCMSG,
+ __tr2qs_ctx("No suitable interface to listen on, trying to continue anyway...","dcc"));
+ d->szListenIp = "0.0.0.0";
+ } else
+ d->szListenIp=QString(tmp);
+//#warning "DO STH WITH THIS PORT (HOW TO SPECIFY IT ?)"
+ d->szListenPort = "0"; // any port is ok
+
+ if(KVI_OPTION_BOOL(KviOption_boolDccSendFakeAddressByDefault))
+ {
+ d->szFakeIp = KVI_OPTION_STRING(KviOption_stringDefaultDccFakeAddress);
+ if(d->szFakeIp.isEmpty())KVI_OPTION_BOOL(KviOption_boolDccSendFakeAddressByDefault) = false;
+ }
+
+ d->bDoTimeout = true;
+ d->szIp = __tr2qs_ctx("(unknown)","dcc");
+ d->szPort = d->szIp;
+ d->bActive = false;
+ d->bSendRequest = true;
+ d->bIsTdcc = bTurboExtension;
+#ifdef COMPILE_SSL_SUPPORT
+ d->bIsSSL = bSSLExtension;
+#endif
+ d->bNoAcks = d->bIsTdcc;
+ d->bOverrideMinimize = false;
+
+ dcc_module_set_dcc_type(d,"SEND");
+
+ if(!dcc->ctcpMsg->msg->haltOutput())
+ {
+ dcc->ctcpMsg->msg->console()->output(KVI_OUT_DCCMSG,
+ __tr2qs_ctx("Accepting file request from %Q [%Q@%Q] for '%s' (real file: %Q), offering DCC %Q","dcc"),
+ &(dcc->ctcpMsg->pSource->nick()),
+ &(dcc->ctcpMsg->pSource->username()),
+ &(dcc->ctcpMsg->pSource->host()),
+ dcc->szParam1.ptr(),
+ &(o->absFilePath()),&(d->szType));
+ }
+ d->triggerCreationEvent();
+ g_pDccBroker->sendFileExecute(0,d);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// VOICE
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+static void dccModuleParseDccVoice(KviDccRequest *dcc)
+{
+ //
+ // We have received a DCC VOICE request in the following form:
+ //
+ // DCC VOICE codec <ipaddress> <port> <sample-rate>
+ //
+ // This means that we're requested to setup an ACTIVE voice connection
+ // ... Easy task :)
+ //
+
+ if(!dcc_module_check_limits(dcc))return;
+
+ if(!dcc_module_normalize_target_data(dcc,dcc->szParam2,dcc->szParam3))return;
+
+#ifdef COMPILE_DISABLE_DCC_VOICE
+ if(!dcc->ctcpMsg->msg->haltOutput())
+ {
+ dcc->ctcpMsg->msg->console()->output(KVI_OUT_DCCERROR,
+ __tr2qs_ctx("The above request cannot be accepted: DCC VOICE support not enabled at compilation time ","dcc"));
+ return;
+ }
+#endif
+ // Actually unused parameter
+ if(!kvi_dcc_voice_is_valid_codec(dcc->szParam1.ptr()))
+ {
+ if(!dcc->ctcpMsg->msg->haltOutput())
+ {
+ dcc->ctcpMsg->msg->console()->output(KVI_OUT_DCCERROR,
+ __tr2qs_ctx("The above request cannot be accepted: Unsupported codec '%s'","dcc"),dcc->szParam1.ptr());
+ return;
+ }
+ }
+
+ bool bOk;
+
+ int iSampleRate = dcc->szParam4.toInt(&bOk);
+ if(!bOk)
+ {
+ if(!dcc->ctcpMsg->msg->haltOutput())
+ {
+ dcc->ctcpMsg->msg->console()->output(KVI_OUT_DCCMSG,
+ __tr2qs_ctx("The above request appears to be broken: Invalid sample-rate '%s', defaulting to 8000","dcc"),dcc->szParam4.ptr());
+ }
+ iSampleRate = 8000;
+ }
+
+
+ KviDccDescriptor * d = new KviDccDescriptor(dcc->pConsole);
+ d->szNick = dcc->ctcpMsg->pSource->nick();
+ d->szUser = dcc->ctcpMsg->pSource->username();
+ d->szHost = dcc->ctcpMsg->pSource->host();
+ dcc_fill_local_nick_user_host(d,dcc);
+
+
+ d->szIp = dcc->szParam2.ptr();
+ d->szPort = dcc->szParam3.ptr();
+ d->bActive = true; // we have to connect
+ d->bIsTdcc = false;
+ d->bNoAcks = false; // this has no meaning in voice
+ d->szCodec = dcc->szParam1;
+ d->iSampleRate = iSampleRate;
+ d->bOverrideMinimize = false;
+ d->bAutoAccept = KVI_OPTION_BOOL(KviOption_boolAutoAcceptDccVoice);
+ dcc_module_set_dcc_type(d,"VOICE");
+ d->triggerCreationEvent();
+ g_pDccBroker->activeVoiceManage(d);
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// CANVAS
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+static void dccModuleParseDccCanvas(KviDccRequest *dcc)
+{
+ //
+ // We have received a DCC CANVAS request in the following form:
+ //
+ // DCC CANVAS unused <ipaddress> <port>
+ //
+ // This means that we're requested to setup an ACTIVE canvas connection
+ // ... Easy task :)
+ //
+ if(!dcc_module_check_limits(dcc))return;
+
+ if(!dcc_module_normalize_target_data(dcc,dcc->szParam2,dcc->szParam3))return;
+
+// Actually unused parameter
+// if(!(kvi_strEqualCI("canvas",dcc->szParam1.ptr())))
+// {
+// if(!dcc->ctcpMsg->msg->haltOutput())
+// {
+// dcc->ctcpMsg->msg->console()->output(KVI_OUT_DCCMSG,
+// __tr("The above request is broken: the second parameter is '%s' and shoud be 'chat'; trying to continue"),dcc->szParam1.ptr());
+// }
+// }
+#ifdef COMPILE_DCC_CANVAS
+ KviDccDescriptor * d = new KviDccDescriptor(dcc->pConsole);
+ d->szNick = dcc->ctcpMsg->pSource->nick();
+ d->szUser = dcc->ctcpMsg->pSource->username();
+ d->szHost = dcc->ctcpMsg->pSource->host();
+ dcc_fill_local_nick_user_host(d,dcc);
+
+
+ d->szIp = dcc->szParam2.ptr();
+ d->szPort = dcc->szParam3.ptr();
+ d->bActive = true; // we have to connect
+ d->bIsTdcc = false;
+ d->bNoAcks = false; // this has no meaning in canvas
+ d->bOverrideMinimize = false;
+ d->bAutoAccept = KVI_OPTION_BOOL(KviOption_boolAutoAcceptDccCanvas);
+ dcc_module_set_dcc_type(d,"CANVAS");
+ d->triggerCreationEvent();
+ g_pDccBroker->activeCanvasManage(d);
+#endif
+}
+
+
+static void dccModuleParseDccList(KviDccRequest *dcc)
+{
+ // DCC LIST <mask> <ipaddr> <port>
+ // FIXME!
+}
+
+
+
+typedef void (*dccParseProc)(KviDccRequest *);
+typedef struct _dccParseProcEntry
+{
+ const char * type;
+ dccParseProc proc;
+} dccParseProcEntry;
+
+#define KVI_NUM_KNOWN_DCC_TYPES 27
+
+static dccParseProcEntry dccParseProcTable[KVI_NUM_KNOWN_DCC_TYPES]=
+{
+ { "CHAT" , dccModuleParseDccChat },
+ { "SCHAT" , dccModuleParseDccChat },
+ { "SEND" , dccModuleParseDccSend },
+ { "TSEND" , dccModuleParseDccSend },
+ { "SSEND" , dccModuleParseDccSend },
+ { "TSSEND" , dccModuleParseDccSend },
+ { "STSEND" , dccModuleParseDccSend },
+ { "GET" , dccModuleParseDccGet },
+ { "SGET" , dccModuleParseDccGet },
+ { "TGET" , dccModuleParseDccGet },
+ { "STGET" , dccModuleParseDccGet },
+ { "TSGET" , dccModuleParseDccGet },
+ { "LIST" , dccModuleParseDccList },
+ { "ACCEPT" , dccModuleParseDccAccept },
+ { "RESUME" , dccModuleParseDccResume },
+ { "RECV" , dccModuleParseDccRecv },
+ { "SRECV" , dccModuleParseDccRecv },
+ { "TRECV" , dccModuleParseDccRecv },
+ { "TSRECV" , dccModuleParseDccRecv },
+ { "STRECV" , dccModuleParseDccRecv },
+ { "RSEND" , dccModuleParseDccRSend },
+ { "SRSEND" , dccModuleParseDccRSend },
+ { "TRSEND" , dccModuleParseDccRSend },
+ { "STRSEND", dccModuleParseDccRSend },
+ { "TSRSEND", dccModuleParseDccRSend },
+ { "CANVAS" , dccModuleParseDccCanvas },
+ { "VOICE" , dccModuleParseDccVoice }
+};
+
+
+
+// We want C linkage on this one: we want to be able to dlsym() it with a simple name
+// FIXME: Is this portable enough ? Or is better to have a table entry ?
+
+KVIMODULEEXPORTFUNC void dccModuleCtcpDccParseRoutine(KviDccRequest *dcc)
+{
+ dcc->szType.toUpper();
+
+ for(int i=0;i<KVI_NUM_KNOWN_DCC_TYPES;i++)
+ {
+ if(kvi_strEqualCS(dccParseProcTable[i].type,dcc->szType.ptr()))
+ {
+ (dccParseProcTable[i].proc)(dcc);
+ return;
+ }
+ }
+ // ops...we don't know this dcc type
+ if(!dcc->ctcpMsg->msg->haltOutput())
+ {
+ KviStr szError(KviStr::Format,
+ __tr2qs_ctx("Unknown DCC type '%s'","dcc"),dcc->szType.ptr());
+ dcc_module_request_error(dcc,szError.ptr());
+ }
+}