//=============================================================================
//
// 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 <tqfileinfo.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 TQString & 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 TQString & 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 ) )
{
TQString szError = TQString ( " Sorry, your DCC %1 request can't be satisfied: %2 " ) . tqarg ( dcc - > szType . ptr ( ) ) . tqarg ( 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 ( ) ) ;
TQString 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 ( ) ) ;
TQString 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
TQString szTag = TQString ( 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 ( ) ) ;
TQString tmp ;
if ( ! dcc_kvs_get_listen_ip_address ( 0 , d - > console ( ) , tmp ) ) d - > szListenIp = " 0.0.0.0 " ;
else d - > szListenIp = TQString ( 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 ;
TQString 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 = TQString ( 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 ) ;
TQString szFileName = TQFileInfo ( 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
TQString 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 ) ;
TQString 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 = TQString ( 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 ( ) ) ;
}
}