diff options
| author | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-02-24 02:13:59 +0000 | 
|---|---|---|
| committer | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-02-24 02:13:59 +0000 | 
| commit | a6d58bb6052ac8cb01805a48c4ad2f129126116f (patch) | |
| tree | dd867a099fcbb263a8009a9fb22695b87855dad6 /src/modules/dcc/libkvidcc.cpp | |
| download | kvirc-a6d58bb6052ac8cb01805a48c4ad2f129126116f.tar.gz kvirc-a6d58bb6052ac8cb01805a48c4ad2f129126116f.zip | |
Added KDE3 version of kvirc
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/kvirc@1095341 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'src/modules/dcc/libkvidcc.cpp')
| -rw-r--r-- | src/modules/dcc/libkvidcc.cpp | 2766 | 
1 files changed, 2766 insertions, 0 deletions
| diff --git a/src/modules/dcc/libkvidcc.cpp b/src/modules/dcc/libkvidcc.cpp new file mode 100644 index 0000000..717f568 --- /dev/null +++ b/src/modules/dcc/libkvidcc.cpp @@ -0,0 +1,2766 @@ +//============================================================================= +// +//   File : libkviobjects.cpp +//   Creation date : Wed Sep 09 2000 20:59:01 by Szymon Stefanek +// +//   This file is part of the KVirc irc client distribution +//   Copyright (C) 2000-2005 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 "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_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 "window.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; + +KviDccBroker * g_pDccBroker = 0; + + +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_kvs_parse_default_parameters(KviDccDescriptor * d,KviKvsModuleCommandCall *c) +{ +	d->bIsTdcc = c->switches()->find('t',"tdcc"); + +	KviKvsVariant * pSw = c->switches()->find('m',"minimize"); + +	if(pSw != 0) +		d->bOverrideMinimize = pSw->asBoolean(); +	else +		d->bOverrideMinimize = false; + +	if(!d->console()) +	{ +		// We don't need a console with -c and -n , otherwise we need it +		if(!(c->switches()->find('c',"connect") || c->switches()->find('n',"no-ctcp"))) +		{ +			delete d; +			c->error(__tr2qs_ctx("This window has no associated IRC context (an IRC context is required unless -c or -n are passed)","dcc")); +			return false; +		} else d->setConsole(c->window()->frame()->firstConsole()); +	} + +	__range_valid(d->console()); + +	if(!d->console()->isConnected()) +	{ +		// We don't need a connection with -c and -n , otherwise we need it +		if(!(c->switches()->find('c',"connect") || c->switches()->find('n',"no-ctcp"))) +		{ +			delete d; +			c->error(__tr2qs_ctx("You're not connected to a server (an active connection is required unless -c or -n are passed)","dcc")); +			return false; +		} else { +			// -c or -n , grab a local nick from somewhere +			d->szLocalNick  = KVI_OPTION_STRING(KviOption_stringNickname1); +			d->szLocalNick.stripWhiteSpace(); +			if(d->szLocalNick.isEmpty())d->szLocalNick = KVI_DEFAULT_NICKNAME1; +			d->szLocalUser  = __tr2qs_ctx("unknown","dcc"); // we can live without it +			d->szLocalHost  = d->szLocalUser;  // we can live without it +		} +	} else { +		// We know everything +		d->szLocalNick  = d->console()->connection()->userInfo()->nickName(); +		d->szLocalUser  = d->console()->connection()->userInfo()->userName(); +		d->szLocalHost  = d->console()->connection()->userInfo()->hostName(); +	} + +	if(pSw = c->switches()->find('i',"ip")) +	{ +		pSw->asString(d->szListenIp); +		if(!(d->szListenIp.contains('.') || d->szListenIp.contains(':'))) +		{ +			// This will magically work with the same buffer as source and dst! +			if(!KviNetUtils::getInterfaceAddress(d->szListenIp,d->szListenIp)) +			{ +				c->error(__tr2qs_ctx("Unable to get address of interface %Q","dcc"),&(d->szListenIp)); +				delete d; +				return false; +			} +		} +	} else { +		QString tmp; +		if(!dcc_kvs_get_listen_ip_address(c,d->console(),tmp)) +		{ +			delete d; +			c->error(__tr2qs_ctx("No suitable interfaces to listen on, use -i","dcc")); +			return false; +		} +		d->szListenIp=tmp; +	} + +	if(pSw = c->switches()->find('p',"port")) +	{ +		pSw->asString(d->szListenPort); // fixme! +	} +	else d->szListenPort = "0"; // any port is ok + +	if(pSw = c->switches()->find('a',"fake-address")) +	{ +		pSw->asString(d->szFakeIp); +	} +	else { +		if(KVI_OPTION_BOOL(KviOption_boolDccSendFakeAddressByDefault)) +		{ +			d->szFakeIp = KVI_OPTION_STRING(KviOption_stringDefaultDccFakeAddress); +			if(d->szFakeIp.isEmpty())KVI_OPTION_BOOL(KviOption_boolDccSendFakeAddressByDefault) = false; +		} +	} + +	if(pSw = c->switches()->find('f',"fake-port")) +	{ +		pSw->asString(d->szFakePort); +	} + +	d->bDoTimeout = (!c->switches()->find('u',"unlimited")); +#ifdef COMPILE_SSL_SUPPORT +	d->bIsSSL = c->switches()->find('s',"ssl"); +#else +	if(c->switches()->find('s',"ssl"))c->warning(__tr2qs_ctx("This executable was built without SSL support, -s switch ignored","dcc")); +#endif + +	return true; +} + + +/* +	@doc: dcc.chat +	@type: +		command +	@title: +		dcc.chat +	@short: +		Starts a DCC Chat connection +	@syntax: +		dcc.chat [-s] [-n] [-c] [-u] [-m[=<boolean>]] [-i=<interface>] [-p=<port>] [-a=<fake address>] [-f=<fake port>] <nickname> +	@switches: +		!sw: -m[=<boolean>] | --minimize[=<boolean>] +		If the -m switch is passed, the default boolCreateMinimizedDccChat option +		is overridden with the <boolean> parameter passed. So actually +		by passing -m=1 will create a minimized DCC send even +		if the [fnc]$option[/fnc](boolCreateMinimizedDccChat) returns false.[br] +		In the same way, by passing -m=0 you will create a non minimized DCC send. +		If no <boolean> value is specified, it defaults to 1.[br] + +		!sw: -n | --no-ctcp +		Do NOT send the CTCP request to the target user, you will have to do it manually, +		or the remote user will have to connect manually (for example by using dcc.chat -c).[br] +		 +		!sw: -c | --connect +		Attempt to CONNECT to the remote host specified as <interface> and <port>, +		instead of listening (active connection instead of a passive one). +		In this case the -i and -p switches are mandatory.[br] +		The 'c' switch takes precedence over 'n' (In fact both should +		be mutually exclusive).[br] +		If the 'c' and 'n' switches are missing, this commands +		needs to be executed in a window that is bound to a connected +		IRC context (you need a third entity to accomplish the negotiation).[br] + +		!sw: -i=<interface> | --ip=<interface> +		Bind the local listening socket to the specified <interface> (which is an IP address, IPv4 or IPv6). +		If this switch is NOT specified, the socket is bound to the interface of +		the current IRC connection (if any) or to "127.0.0.1".[br] +		You can also specify a local interface name to get the address from (this works only for IPv4 interfaces +		since IPv6 ones seems to be unsupported by the system ioctl() calls at the moment (for linux at least)).[br] +		Here go some examples:[br] +		-i=215.243.12.12: this will bind to the IPv4 interface with the specified address.[br] +		-i=3ffe:1001::1: this will bind to the IPv6 interface with the specified address.[br] +		-i=ppp0: this will bind to the IPv4 address of the interface ppp0 (if supported by the underlying system).[br] +		The -i switch parameter may serve also as a target address when the -c switch is used.[br] +		 +		!sw: -p=<port> | --port=<port> +		Bind the local listening socket to the specified <port>. +		If this switch is NOT specified, the port will be a "random" one choosen by the kernel.[br] + +		!sw: -a=<fake address> | --fake-address=<fake address> +		Send the <fake address> as target for the remote client in the requesting CTCP message. +		If this switch is not given, the CTCP will contain the real IP address of the listening +		interface.[br] +		 +		!sw: -f=<fake port> | --fake-port=<fake port> +		Send the <fake port> as target port for the remote client in the requesting CTCP message. +		If this switch is not given, the CTCP will contain the real port of the listening socket. +		[br][br] +		All these switches are meant to allow maximum flexibility of the +		DCC negotiation, earlier KVIrc releases had serious problems +		with firewalled and/or masqueraded machines. With the -a and -f switches +		you can work around it.[br] +		[br] + +		!sw: -u | --unlimited +		If the 'u' switch is given, the connection attempt will +		never time out; this might be useful if you want to leave +		a listening socket for a friend of yours while you are sleeping +		and have the CTCP processing disabled. The 'u' switch works either +		in active and passive mode.[br] + +		!sw: -s | --ssl +		Use a Secure Socket Layer for the transfer; the whole communication will be encrypted +		with a private key algorithm after a public key handshake.[br] +		This option will work only if the KVIrc executable has been compiled with SSL support +		and the remote end supports the SSL protocol too.[br] +		Please note that this will may down the transfer somewhat.[br] +		-s can be combined with -t.[br] +		The CTCP negotiation will use SSEND as parameter (or eventually TSSEND).[br] +		When requesting a SSL based DCC send to someone you probably will need a +		certificate. If you don't have one, create it (for example with CA.pl -newcert) +		and set it in the options dialog. + +		!sw: -z | --zero-port +		Use the 0 port method. This is a dirty hack that allows you to use the CHAT +		protocol with mIrc receiving clients. +	@description: +		Attempts a DCC connection to <nickname>.[br] +		The simplest case "dcc.chat <nickname>" will work just as in all +		the other IRC clients, but this command is really more powerful...[br] +		Before attempting to understand the possibilities of this command, +		be sure to know how [doc:dcc_connection]DCC negotiation and connections[/doc] work. +		If the 'i' switch is specified, the local listening socket +		will be bound to the specified <interface> (which is an IP address, IPv4 or IPv6), +		otherwise it will be bound to the interface of the +		current IRC connection.[br] +		You can also specify a local interface name to get the address from (this works only for IPv4 interfaces +		since IPv6 ones seem to be unsupported by the system ioctl() calls at the moment (in Linux at least)).[br] +		Here are some examples:[br] +		-i=215.243.12.12: This will bind to the IPv4 interface with the specified address.[br] +		-i=3ffe:1001::1: This will bind to the IPv6 interface with the specified address.[br] +		-i=ppp0: This will bind to the IPv4 address of the interface ppp0 (if supported by the underlying system).[br] +		The -i switch parameter may serve also as a target address when the -c switch is used.[br] +		If the 'p' switch is specified, the local listening socket +		will be bound to the <port>, otherwise it will be bound to +		a random port choosen by the kernel.[br] +		If the 'a' switch is specified, the requesting CTCP message +		will contain <fake address> as target for the remote user, +		otherwise the CTCP message will contain the real IP address +		of the listening interface. +		If the 'f' switch is specified, the requesting CTCP message +		will contain <fake port> as target for the remote user, +		otherwise the CTCP message will contain the real port of the +		listening socket. +		All these switches are meant to allow maximum flexibility of the +		DCC negotiation, earlier KVIrc releases had serious problems +		with firewalled and/or masqueraded machines. With the -a and -f switches +		you can workaround it. +		If the 'n' switch is specified, KVIrc will NOT send the CTCP request +		to the target user; you will have to do it manually, or the remote user +		will have to connect manually (for example by using dcc.chat -c). +		If the 'c' switch is specified, KVIrc will attempt to connect +		to the remote host specified as <interface> and <port>, instead +		of listening (active connection instead of a passive one). +		In this case the -i and -p switches are mandatory.[br] +		The 'c' switch takes precedence over 'n' (In fact both should +		be mutually exclusive).[br] +		If the 'c' and 'n' switches are missing, this commands +		needs to be executed in a window that is bound to a connected +		IRC context (you need a third entity to accomplish the negotiation).[br] +		If the 'u' switch is given, the connection attempt will +		never time out; this might be useful if you want to leave +		a listening socket for a friend of yours while you are sleeping +		and have the CTCP processing disabled. The 'u' switch works either +		in active and passive mode.[br] +		If the -m switch is passed, the default boolCreateMinimizedDccChat option +		is overridden with the <boolean> parameter passed. So actually +		by passing -m=1 will create a minimized DCC chat even +		if the [fnc]$option[/fnc](boolCreateMinimizedDccChat) returns false.[br] +		In the same way, by passing -m=0 you will create a non minimized DCC chat. +		If no <boolean> value is specified, it defaults to 1.[br] +		-s will cause the DCC chat to be attempted in Secure Sockets Layer mode: +		the connection will be encrypted with a private key algorithm after a +		public key handshake. -s will work only if the KVIrc executable was compiled +		with SSL support enabled and if the remote end supports it. +		The eventual DCC request will contain the string SCHAT instead of CHAT.[br] +		When requesting a SSL based DCC chat to someone you probably will need a +		certificate. If you don't have one, create it (for example with CA.pl -newcert) +		and set it in the options dialog. +	@examples: +		Simple examples: +		[example] +			# Simple DCC chat to Pragma +			dcc.chat Pragma +			# DCC chat to Pragma, listen on address 127.0.0.1 +			dcc.chat -i=127.0.0.1 Pragma +			# DCC chat to Pragma, listen on address 168.0.0.1 and port 1025 +			dcc.chat -i=168.0.0.1 -p=1025 Pragma +		[/example] +		More tricky ones: +		[example] +			# DCC chat to Pragma, listen on address 127.0.0.1 and port 1080 +			# but tell him to connect to address 212.134.22.11 port 1080 +			dcc.chat -i=127.0.0.1 -p=1080 -a=212.134.22.11 Pragma +			# DCC chat to Pragma, listen on address 127.0.0.1 and port 1080 +			# but tell him to connect to address 212.134.22.11 port 1090 +			dcc.chat -i=127.0.0.1 -p=1080 -a=212.134.22.11 -f=1090 Pragma +		[/example] +		Now run completely out of bounds. Use dcc.chat connections +		to do various things: +		[example] +			# Tricky: simulate a HTTP server +			dcc.chat -n -i=127.0.0.1 -p=80 Netscape +			# Now open http://127.0.0.1 with netscape +			# and type "<html><body>Hello!</body></html>" :) +			# +			# Tricky 2: simulate the ident daemon (if you have none) +			dcc.chat -n -i=127.0.0.1 -p=113 Client +			# +			# Now a self-DCC connection without the IRC negotiation +			# Src: Setup a listening socket awaiting the "Destination" user connection +			dcc.chat -n -i=127.0.0.1 -p=1080 Dst +			# Dst: Connect to the listening socket at addr 127.0.0.1 and port 1080 +			dcc.chat -c -i=127.0.0.1 -p=1080 Src +			# The above example will mess you a bit... +			# Try to guess why in both windows YOU have the same nickname +			# that is probably not Dst nor Src... :) +		[/example] +		Using the shell ftp proggie is too easy: +		we're [b]real hackers[/b] and want to do complicated things... +		[example] +			# Connect to the local ftp server and get the list of contents +			/dcc.chat -c -i=127.0.0.1 -p=21 FtpServer +			# Now login, type in the new window (the following lines are NOT commands): +			USER youruser +			PASS yourpass +			# Now enter passive mode +			PASV +			# And watch the line that you have received...sth like +			# 227 Entering passive mode (127,0,0,1,210,195) +			/dcc.chat -c -i=127.0.0.1 -p=$((210 * 256) + 195) FtpList +			# (Change the port numbers accordingly) +			# And then type in the FtpServer window (this is NOT a KVIrc command): +			LIST +			# Then watch the ls output in the FtpList window. :) +			# In this way you can also read ascii files by ftp: +			# Assume that in the previous ls output you have seen +			# a README file. +			# In the FtpServer window type: +			PASV +			# Watch the message +			# 227 Entering passive mode (127,0,0,1,22,227) +			/dcc.chat -c -i=127.0.0.1 -p=$((22 * 256) + 227) README +			# In the FtpServer window type: +			RETR README +			# And read the file in the README window :) +		[/example] +*/ + +static bool dcc_kvs_cmd_chat(KviKvsModuleCommandCall * c) +{ +	QString szTarget; +	KVSM_PARAMETERS_BEGIN(c) +		KVSM_PARAMETER("target",KVS_PT_NONEMPTYSTRING,0,szTarget) +	KVSM_PARAMETERS_END(c) + +	KviDccDescriptor * d = new KviDccDescriptor(c->window()->console()); + +	d->szNick       = szTarget;    // we always specify the nickname +	d->szUser       = __tr2qs_ctx("unknown","dcc"); // username is always unknown +	d->szHost       = d->szUser;       // host is always unknown + +	if(!dcc_kvs_parse_default_parameters(d,c))return false; +	dcc_module_set_dcc_type(d,"CHAT"); + +	if(c->switches()->find('z',"zero-port")) +	{ +		// we want to have it reversed... add a tag and send out the request +		KviDccZeroPortTag * t = g_pDccBroker->addZeroPortTag(); + +		d->console()->connection()->sendFmtData("PRIVMSG %s :%cDCC %s chat 127.0.0.1 0 %s%c", +			d->console()->connection()->encodeText(d->szNick).data(), +			0x01, +			d->console()->connection()->encodeText(d->szType).data(), +			d->console()->connection()->encodeText(t->m_szTag).data(), +			0x01); + +		return true; +	} + +	if(c->switches()->find('c',"connect")) +	{ +		if(!(c->switches()->find('i',"ip") && c->switches()->find('p',"port"))) +		{ +			delete d; +			c->error(__tr2qs_ctx("-c requires -i and -p","dcc")); +			return false; +		} +		d->szIp         = d->szListenIp; +		d->szPort       = d->szListenPort; +		d->szListenIp   = ""; // useless +		d->szListenPort = ""; // useless +		d->bActive      = true; +	} else { +		d->szIp         = __tr2qs_ctx("unknown","dcc"); +		d->szPort       = d->szIp; +		d->bActive      = false; +		d->bSendRequest = !c->switches()->find('n',"no-ctcp"); +	} +	 +	//c->window()->output(0,"%Q %Q %Q",&(d->szIp),&(d->szPort),&(d->szListenIp)); +	d->triggerCreationEvent(); +	g_pDccBroker->executeChat(0,d); + +	return true; +} + + +/* +	@doc: dcc.send +	@type: +		command +	@title: +		dcc.send +	@short: +		Sends a file +	@syntax: +		dcc.send [-s] [-n] [-c] [-u] [-b] [-g[=<file size>]] [-t] [-m[=<boolean>]] [-i=<interface>] [-p=<port>] [-a=<fake address>] [-f=<fake port>] <nickname> [filename] +	@switches: +		!sw: -g[=<file size>] | --get[=<file size>] +		This switch is a dirty trick, you can use it to receive files from people +		behind a firewall with masquerading enabled.[br] +		It causes the transfer direction to be inverted; your client will receive +		the file from the remote host instead of sending it.[br] +		<file size> is the expected file size in bytes. This parameter can be omitted, +		and in this case the DCC will "blindly" trust the remote end and assume +		that the file has been transferred correctly when the remote end closes the connection.[br] +		If you don't pass the -n option, the remote end will receive an informational DCC RECV request, +		specifying the IP address and the port to connect to.[br] +		-t can be used to prevent sending acknowledges to the remote end, and -u can be used +		to avoid the listening socket to timeout.[br] +		-a and -f can be used as well, but I see no real reason for that...[br] +		Here is an example of usage of this option:[br] +		spion can't accept connections (is behind a firewall with masquerading or some other reason...), +		to his machine.[br] +		spion wants to send the file important.jpg to Pragma.[br] +		spion tells to Pragma that he wants to send him the file and Pragma sets up a connection in the following way:[br] +		[b]dcc.send -g spion important.png[/b][br] +		spion will see the informational DCC RECV request with the IP address and port that Pragma is listening on. +		Assume that the address was 212.212.231.220 and the port 32344.[br] +		spion will then use the following command:[br] +		[b]dcc.send -c -i=212.212.231.220 -p=32344 Pragma /home/spion/important.jpg[/b][br] +		Et voila!..the file is transferring.[br] +		Pragma will see no file progress indication, since the file size is unknown on Pragma's side.[br] +		If spion had specified the file size, Pragma could use -g=<file size> while setting up the connection, +		to be able to see the progress indications.[br] +		If Pragma used the the -n option, the DCC RECV indication wouldn't have been sent, in this case +		Pragma would need to communicate the Ip address and the port "manually" to spion.[br] + +		!sw: -b | --blind +		Assume that no acknowledges are sent. +		Assume that the transfer was successful when the whole file has been sent, +		then close the socket.[br] +		This is called a "blind" DCC send.[br] +		 +		!sw: -t | -tdcc +		Emulate the TDCC protocol: Use the TDCC CTCP message (DCC TSEND) for requesting the connection +		and assume that no acknowledges are sent. Wait for the remote end to close the connection.[br] + +		!sw: -m[=<boolean>] | --minimize[=<boolean>] +		If the -m switch is passed, the default boolCreateMinimizedDccSend option +		is overridden with the <boolean> parameter passed. So actually +		by passing -m=1 will create a minimized DCC send even +		if the [fnc]$option[/fnc](boolCreateMinimizedDccSend) returns false.[br] +		In the same way, by passing -m=0 you will create a non minimized DCC send. +		If no <boolean> value is specified, it defaults to 1.[br] + +		!sw: -n | --no-ctcp +		Do NOT send the CTCP request to the target user, you will have to do it manually, +		or the remote user will have to connect manually (for example by using dcc.recv -c).[br] +		 +		!sw: -c | --connect +		Attempt to CONNECT to the remote host specified as <interface> and <port>, +		instead of listening (active connection instead of a passive one). +		In this case the -i and -p switches are mandatory.[br] +		The 'c' switch takes precedence over 'n' (In fact both should +		be mutually exclusive).[br] +		If the 'c' and 'n' switches are missing, this commands +		needs to be executed in a window that is bound to a connected +		IRC context (you need a third entity to accomplish the negotiation).[br] + +		!sw: -i=<interface> | --ip=<interface> +		Bind the local listening socket to the specified <interface> (which is an IP address, IPv4 or IPv6). +		If this switch is NOT specified, the socket is bound to the interface of +		the current IRC connection (if any) or to "127.0.0.1".[br] +		You can also specify a local interface name to get the address from (this works only for IPv4 interfaces +		since IPv6 ones seems to be unsupported by the system ioctl() calls at the moment (for linux at least)).[br] +		Here go some examples:[br] +		-i=215.243.12.12: this will bind to the IPv4 interface with the specified address.[br] +		-i=3ffe:1001::1: this will bind to the IPv6 interface with the specified address.[br] +		-i=ppp0: this will bind to the IPv4 address of the interface ppp0 (if supported by the underlying system).[br] +		The -i switch parameter may serve also as a target address when the -c switch is used.[br] +		 +		!sw: -p=<port> | --port=<port> +		Bind the local listening socket to the specified <port>. +		If this switch is NOT specified, the port will be a "random" one choosen by the kernel.[br] + +		!sw: -a=<fake address> | --fake-address=<fake address> +		Send the <fake address> as target for the remote client in the requesting CTCP message. +		If this switch is not given, the CTCP will contain the real IP address of the listening +		interface.[br] +		 +		!sw: -f=<fake port> | --fake-port=<fake port> +		Send the <fake port> as target port for the remote client in the requesting CTCP message. +		If this switch is not given, the CTCP will contain the real port of the listening socket. +		[br][br] +		All these switches are meant to allow maximum flexibility of the +		DCC negotiation, earlier KVIrc releases had serious problems +		with firewalled and/or masqueraded machines. With the -a and -f switches +		you can work around it.[br] +		[br] + +		!sw: -u | --unlimited +		If the 'u' switch is given, the connection attempt will +		never time out; this might be useful if you want to leave +		a listening socket for a friend of yours while you are sleeping +		and have the CTCP processing disabled. The 'u' switch works either +		in active and passive mode.[br] + +		!sw: -s | --ssl +		Use a Secure Socket Layer for the transfer; the whole communication will be encrypted +		with a private key algorithm after a public key handshake.[br] +		This option will work only if the KVIrc executable has been compiled with SSL support +		and the remote end supports the SSL protocol too.[br] +		Please note that this will may down the transfer somewhat.[br] +		-s can be combined with -t.[br] +		The CTCP negotiation will use SSEND as parameter (or eventually TSSEND).[br] +		When requesting a SSL based DCC send to someone you probably will need a +		certificate. If you don't have one, create it (for example with CA.pl -newcert) +		and set it in the options dialog. + +	@description: +		Attempts to send the file <filename> to <nickname>.[br] +		If [filename] is specified it must be an absolute file path, +		otherwise a file selection dialog is opened.[br] +		The simplest case "dcc.send <nickname> <filename>" will work just as in all +		the other IRC clients, but this command is really more powerful...[br] +		Before attempting to understand the possibilities of this command, +		be sure to know how a [doc:dcc_connection]DCC negotiation and connection[/doc] works.[br] +		The file will be sent as a sequence of packets which must +		be acknowledged one by one by the active client.[br] +		There is a special option (see $option()) called "fast send" (also known +		as "send ahead") that makes KVIrc avoid to wait for the acknowledge +		of the last packet before sending the next one.[br] +		Anyway, the connection is declared as successful only +		when the whole file (all the packets) has been acknowledged.[br] +		If you need to send a file but you're firewalled, you might take +		a look at the [cmd]dcc.rsend[/cmd] command. It also handles +		the mIrc zero port protocol extension. +	@examples: + +*/ + +//#warning "Example for -r" +//#warning "OPTION FOR NO GUI ? (this is hard...)" +//#warning "When in IPv6 mode, should be able to use the IPv4 binding!" + +static bool dcc_kvs_cmd_send(KviKvsModuleCommandCall * c) +{ +	QString szTarget,szFileName; +	KVSM_PARAMETERS_BEGIN(c) +		KVSM_PARAMETER("target",KVS_PT_NONEMPTYSTRING,0,szTarget) +		KVSM_PARAMETER("file name",KVS_PT_NONEMPTYSTRING,KVS_PF_OPTIONAL,szFileName) +	KVSM_PARAMETERS_END(c) + +	KviDccDescriptor * d = new KviDccDescriptor(c->window()->console()); + +	d->szNick            = szTarget;    // we always specify the nickname + +	QString szTmp; +	KviKvsVariant * pSw = 0; + +	if(pSw = c->switches()->find('g',"get")) +	{ +		d->szFileName = QFileInfo(szFileName).fileName(); + +		if(!pSw->isBoolean()) +		{ +			kvs_int_t iSize; +			if(pSw->asInteger(iSize)) // is an integer +			{ +				pSw->asString(szTmp); +				// avoid sprintf as long as possibile +				d->szFileSize = szTmp; +			} else { +				d->szFileSize = __tr_ctx("<unknown size>","dcc"); +			} +		} +	} else { +		d->szFileName      = szFileName; +		d->szLocalFileName = szFileName; +	} + +	d->szUser            = __tr2qs_ctx("unknown","dcc"); // username is always unknown +	d->szHost            = d->szUser;                 // host is always unknown +	d->bRecvFile         = pSw != 0; +	d->bNoAcks           = c->switches()->find('b',"blind") || c->switches()->find('t',"tdcc"); +	d->bResume           = false; +	d->bAutoAccept       = pSw != 0; +	d->bIsIncomingAvatar = false; + +	if(!dcc_kvs_parse_default_parameters(d,c))return false; + +	if(c->switches()->find('c',"connect")) +	{ +		if(!(c->switches()->find('i',"ip") && c->switches()->find('p',"port"))) +		{ +			delete d; +			c->error(__tr2qs_ctx("-c requires -i and -p","dcc")); +			return false; +		} +		d->szIp         = d->szListenIp; +		d->szPort       = d->szListenPort; +		d->szListenIp   = ""; // useless +		d->szListenPort = ""; // useless +		d->bActive      = true; +	} else { +		d->szIp         = __tr2qs_ctx("unknown","dcc"); +		d->szPort       = d->szIp; +		d->bActive      = false; +		d->bSendRequest = !c->switches()->find('n',"no-ctcp"); +	} + +	if(c->switches()->find('g',"get")) +	{ +		dcc_module_set_dcc_type(d,"RECV"); +		d->triggerCreationEvent(); +		g_pDccBroker->recvFileManage(d); +	} else { +		dcc_module_set_dcc_type(d,"SEND"); +		d->triggerCreationEvent(); +		if(!d->szLocalFileName.isEmpty()) +		{ +			g_pDccBroker->sendFileExecute(0,d); +		} else { +			g_pDccBroker->sendFileManage(d); +		} +	} + +	return true; +} + +/* +	@doc: dcc.recv +	@type: +		command +	@title: +		dcc.recv +	@short: +		Sets up a file receiving connection +	@syntax: +		dcc.recv [-s] [-t] [-u] [-b] [-n] [-c] [-i=<interface>] [-p=<port>] [-m[=<boolean>]] <nickname> <filename> <remote file size> +	@switches: +		!sw: -b | --blind +		Assume that no acknowledges are sent. +		Assume that the transfer was successful when the whole file has been sent, +		then close the socket.[br] +		This is called a "blind" DCC send.[br] +		 +		!sw: -t | -tdcc +		Emulate the TDCC protocol: Use the TDCC CTCP message (DCC TSEND) for requesting the connection +		and assume that no acknowledges are sent. Wait for the remote end to close the connection.[br] + +		!sw: -m[=<boolean>] | --minimize[=<boolean>] +		If the -m switch is passed, the default boolCreateMinimizedDccSend option +		is overridden with the <boolean> parameter passed. So actually +		by passing -m=1 will create a minimized DCC send even +		if the [fnc]$option[/fnc](boolCreateMinimizedDccSend) returns false.[br] +		In the same way, by passing -m=0 you will create a non minimized DCC send. +		If no <boolean> value is specified, it defaults to 1.[br] + +		!sw: -n | --no-ctcp +		Do NOT send the CTCP request to the target user, you will have to do it manually, +		or the remote user will have to connect manually (for example by using dcc.recv -c).[br] +		 +		!sw: -i=<interface> | --ip=<interface> +		Bind the local listening socket to the specified <interface> (which is an IP address, IPv4 or IPv6). +		If this switch is NOT specified, the socket is bound to the interface of +		the current IRC connection (if any) or to "127.0.0.1".[br] +		You can also specify a local interface name to get the address from (this works only for IPv4 interfaces +		since IPv6 ones seems to be unsupported by the system ioctl() calls at the moment (for linux at least)).[br] +		Here go some examples:[br] +		-i=215.243.12.12: this will bind to the IPv4 interface with the specified address.[br] +		-i=3ffe:1001::1: this will bind to the IPv6 interface with the specified address.[br] +		-i=ppp0: this will bind to the IPv4 address of the interface ppp0 (if supported by the underlying system).[br] +		The -i switch parameter may serve also as a target address when the -c switch is used.[br] +		 +		!sw: -p=<port> | --port=<port> +		Bind the local listening socket to the specified <port>. +		If this switch is NOT specified, the port will be a "random" one choosen by the kernel.[br] + +		!sw: -a=<fake address> | --fake-address=<fake address> +		Send the <fake address> as target for the remote client in the requesting CTCP message. +		If this switch is not given, the CTCP will contain the real IP address of the listening +		interface.[br] +		 +		!sw: -f=<fake port> | --fake-port=<fake port> +		Send the <fake port> as target port for the remote client in the requesting CTCP message. +		If this switch is not given, the CTCP will contain the real port of the listening socket. +		[br][br] +		All these switches are meant to allow maximum flexibility of the +		DCC negotiation, earlier KVIrc releases had serious problems +		with firewalled and/or masqueraded machines. With the -a and -f switches +		you can work around it.[br] +		[br] + +		!sw: -u | --unlimited +		If the 'u' switch is given, the connection attempt will +		never time out; this might be useful if you want to leave +		a listening socket for a friend of yours while you are sleeping +		and have the CTCP processing disabled. The 'u' switch works either +		in active and passive mode.[br] + +		!sw: -s | --ssl +		Use a Secure Socket Layer for the transfer; the whole communication will be encrypted +		with a private key algorithm after a public key handshake.[br] +		This option will work only if the KVIrc executable has been compiled with SSL support +		and the remote end supports the SSL protocol too.[br] +		Please note that this will may down the transfer somewhat.[br] +		-s can be combined with -t.[br] +		The CTCP negotiation will use SSEND as parameter (or eventually TSSEND).[br] +		When requesting a SSL based DCC send to someone you probably will need a +		certificate. If you don't have one, create it (for example with CA.pl -newcert) +		and set it in the options dialog. + +		!sw: -c | --connect +		Accepted for compatibility: don't use it! +	@description: +		Sets up a connection ready to receive a file.[br] +		In most 'common' cases you will not need this command, you might want to use [cmd]dcc.get[/cmd] instead.[br] +		This works like dcc.send but has two main differences: The file is INCOMING, and the CTCP sent to the other side +		is a CTCP RECV.[br] +		This command is the counterpart of [cmd]dcc.send[/cmd] and its parameters are exactly the same, so please refer to that +		help page for the full discussion. This help page contains only a brief resume of these parameters.[br] +		The [doc:dcc_connection]dcc documentation[/doc] explains the DCC Recv subprotocol in detail.[br] +	@examples: + +*/ + +//#warning "ENCODE THE CTCP'S!!!!!!!" +//#warning "DOCS FOR dcc.recv (examples!)" + +static bool dcc_kvs_cmd_recv(KviKvsModuleCommandCall * c) +{ +	QString szTarget,szFileName; +	kvs_uint_t uSize; +	KVSM_PARAMETERS_BEGIN(c) +		KVSM_PARAMETER("target",KVS_PT_NONEMPTYSTRING,0,szTarget) +		KVSM_PARAMETER("filename",KVS_PT_NONEMPTYSTRING,0,szFileName) +		KVSM_PARAMETER("size",KVS_PT_UINT,0,uSize) +	KVSM_PARAMETERS_END(c) + +	KviDccDescriptor * d = new KviDccDescriptor(c->window()->console()); +	d->szNick            = szTarget; +	d->szUser            = __tr2qs_ctx("unknown","dcc"); +	d->szHost            = d->szUser; +	d->szIp              = __tr2qs_ctx("unknown","dcc"); +	d->szPort            = d->szIp; + +	// -c is senseless here...but we accept it for compatibility + +	if(!dcc_kvs_parse_default_parameters(d,c))return false; + +	d->szFileName        = szFileName; +	d->szFileSize.setNum(uSize); + +	d->bActive           = false; // we have to listen! +	d->bResume           = false;  +	d->bRecvFile         = true;  // we have to receive the file! +	d->bSendRequest      = !c->switches()->find('n',"no-ctcp"); +	d->bNoAcks           = d->bIsTdcc || c->switches()->find('b',"blind"); +	d->bAutoAccept       = KVI_OPTION_BOOL(KviOption_boolAutoAcceptDccSend); +	d->bIsIncomingAvatar = g_pApp->findPendingAvatarChange(d->console(),d->szNick,d->szFileName); + +	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); + +	return true; +} + +/* +	@doc: dcc.rsend +	@type: +		command +	@title: +		dcc.rsend +	@short: +		Sends a file by using the Reverse DCC Send protocol +	@syntax: +		dcc.rsend [-s] [-t] <nickname> [filename] +	@switches: +		!sw: -t | -tdcc +		Emulate the TDCC protocol. + +		!sw: -s | --ssl +		Use a Secure Socket Layer for the transfer; the whole communication will be encrypted +		with a private key algorithm after a public key handshake.[br] +		This option will work only if the KVIrc executable has been compiled with SSL support +		and the remote end supports the SSL protocol too.[br] +		Please note that this will may down the transfer somewhat.[br] +		-s can be combined with -t.[br] +		The CTCP negotiation will use SSEND as parameter (or eventually TSSEND).[br] +		When requesting a SSL based DCC send to someone you probably will need a +		certificate. If you don't have one, create it (for example with CA.pl -newcert) +		and set it in the options dialog. +		!sw: -z | --zero-port +		Use the 0 port method. This is a dirty hack that allows you to use the RSEND +		protocol with mIrc receiving clients. +	@description: +		Sends a DCC RSEND request to <nickname> notifying him that you want to +		send him the file [filename].[br] +		The remote end may acknowledge the request by sending a DCC RECV request. +		This command effects are similar to [cmd]dcc.send[/cmd], but will work also on machines +		that can't accept incoming connections (firewalling or masquerading problems).[br] +		A 120 seconds file offer is added for the specified file and mask "<nickname>!*@*". +	@examples: + +*/ + +//#warning "ENCODE THE CTCP'S!!!!!!!" +//#warning "DOCS FOR dcc.rsend" + +static bool dcc_kvs_cmd_rsend(KviKvsModuleCommandCall * c) +{ +	QString szTarget,szFileName; +	KVSM_PARAMETERS_BEGIN(c) +		KVSM_PARAMETER("target",KVS_PT_NONEMPTYSTRING,0,szTarget) +		KVSM_PARAMETER("filename",KVS_PT_NONEMPTYSTRING,KVS_PF_OPTIONAL,szFileName) +	KVSM_PARAMETERS_END(c) + +	KVSM_REQUIRE_CONNECTION(c) + +	KviDccDescriptor * d = new KviDccDescriptor(c->window()->console()); +	d->szNick            = szTarget; +	d->szLocalFileName   = szFileName; +	d->bIsTdcc           = c->switches()->find('t',"tdcc"); +#ifdef COMPILE_SSL_SUPPORT +	d->bIsSSL            = c->switches()->find('s',"ssl"); +#else +	if(c->switches()->find('s',"ssl"))c->warning(__tr2qs_ctx("This executable has been built without SSL support, -s switch ignored","dcc")); +#endif //!COMPILE_SSL_SUPPORT + +	if(c->switches()->find('z',"zero-port")) +	{ +		dcc_module_set_dcc_type(d,"SEND"); +		d->setZeroPortRequestTag("nonempty"); // just to tag it +	} else +		dcc_module_set_dcc_type(d,"RSEND"); +	d->triggerCreationEvent(); +	g_pDccBroker->rsendManage(d); + +	return true; +} + + + + +/* +	@doc: dcc.get +	@type: +		command +	@title: +		dcc.get +	@short: +		Requests a file +	@syntax: +		dcc.get [-s] [-t] <nickname> <filename> [filesize] +	@description: +		Sends a CTCP DCC GET to <nickname> requesting the file <filename>. +		The remote end should reply with a DCC SEND request CTCP. +		<filename> must not contain any leading path. +		If the -t switch is given, the message is a DCC TGET, expecting +		a TSEND reply.[br] +		If the -s switch is given, the message will be a DCC SGET, expecting +		a SSEND reply.[br] +		-t and -s can be combined together to obtain a "turbo"+"SSL" extension transfer.[br] +		-s will work only if the KVIrc executable has been compiled with SSL support and +		the remote client supports it.[br] +	@examples: + +*/ + +//#warning "ENCODE THE CTCP'S!!!!!!!" +//#warning "DOCS FOR dcc.get" + +static bool dcc_kvs_cmd_get(KviKvsModuleCommandCall * c) +{ +	QString szTarget,szFileName; +	kvs_uint_t uSize; +	KVSM_PARAMETERS_BEGIN(c) +		KVSM_PARAMETER("target",KVS_PT_NONEMPTYSTRING,0,szTarget) +		KVSM_PARAMETER("filename",KVS_PT_NONEMPTYSTRING,0,szFileName) +		KVSM_PARAMETER("size",KVS_PT_UINT,KVS_PF_OPTIONAL,uSize) +	KVSM_PARAMETERS_END(c) + +	KVSM_REQUIRE_CONNECTION(c) + +	KviQString::cutToLast(szFileName,'/'); + +	if(szFileName.contains(' ')) +	{ +		szFileName.prepend('"'); +		szFileName.append('"'); +	} + +	KviStr szDCC("GET"); +#ifdef COMPILE_SSL_SUPPORT +	if(c->switches()->find('s',"ssl"))szDCC.prepend('S'); +#else +	if(c->switches()->find('s',"ssl"))c->warning(__tr2qs_ctx("This executable has no SSL support, -s switch ignored","dcc")); +#endif +	if(c->switches()->find('t',"tdcc"))szDCC.prepend('T'); + +	if(uSize == 0) +	{ +		c->window()->console()->connection()->sendFmtData("PRIVMSG %s :%cDCC %s %s%c", +			c->window()->console()->connection()->encodeText(szTarget).data(), +			0x01, +			c->window()->console()->connection()->encodeText(szDCC.ptr()).data(), +			c->window()->console()->connection()->encodeText(szFileName).data(), +			0x01); +	} else { +		c->window()->console()->connection()->sendFmtData("PRIVMSG %s :%cDCC %s %s %u%c", +			c->window()->console()->connection()->encodeText(szTarget).data(), +			0x01, +			c->window()->console()->connection()->encodeText(szDCC.ptr()).data(), +			c->window()->console()->connection()->encodeText(szFileName).data(), +			uSize,0x01); +	} + +	return true; +} + + + + +// FIXME: SSL support for DCC VOICE ? (has a sense ?) + +/* +	@doc: dcc.voice +	@type: +		command +	@title: +		dcc.voice +	@short: +		Starts a DCC Voice connection +	@syntax: +		dcc.voice [-g=<codec>] [-n] [-c] [-u] [-h=<sample_rate_in_hz>] [-m[=<boolean>]] [-i=<interface>] [-p=<port>] [-a=<fake address>] [-f=<fake port>] <nickname> +	@switches: +		!sw: -g=<codec> | --codec=<codec> +		Use the codec specified as parameter. +		Actually the supported codecs are "null","adpcm" and "gsm". +		 +		!sw: -h=<rate> | --sample-rate=<rate> +		Use the sample rate specified by <rage>. +		Valid sample rates are 8000, 11025, 22050 and 44100 Hz. +		 +		!sw: -m[=<boolean>] | --minimize[=<boolean>] +		If the -m switch is passed, the default boolCreateMinimizedDccSend option +		is overridden with the <boolean> parameter passed. So actually +		by passing -m=1 will create a minimized DCC send even +		if the [fnc]$option[/fnc](boolCreateMinimizedDccSend) returns false.[br] +		In the same way, by passing -m=0 you will create a non minimized DCC send. +		If no <boolean> value is specified, it defaults to 1.[br] + +		!sw: -n | --no-ctcp +		Do NOT send the CTCP request to the target user, you will have to do it manually, +		or the remote user will have to connect manually (for example by using dcc.recv -c).[br] +		 +		!sw: -c | --connect +		Attempt to CONNECT to the remote host specified as <interface> and <port>, +		instead of listening (active connection instead of a passive one). +		In this case the -i and -p switches are mandatory.[br] +		The 'c' switch takes precedence over 'n' (In fact both should +		be mutually exclusive).[br] +		If the 'c' and 'n' switches are missing, this commands +		needs to be executed in a window that is bound to a connected +		IRC context (you need a third entity to accomplish the negotiation).[br] + +		!sw: -i=<interface> | --ip=<interface> +		Bind the local listening socket to the specified <interface> (which is an IP address, IPv4 or IPv6). +		If this switch is NOT specified, the socket is bound to the interface of +		the current IRC connection (if any) or to "127.0.0.1".[br] +		You can also specify a local interface name to get the address from (this works only for IPv4 interfaces +		since IPv6 ones seems to be unsupported by the system ioctl() calls at the moment (for linux at least)).[br] +		Here go some examples:[br] +		-i=215.243.12.12: this will bind to the IPv4 interface with the specified address.[br] +		-i=3ffe:1001::1: this will bind to the IPv6 interface with the specified address.[br] +		-i=ppp0: this will bind to the IPv4 address of the interface ppp0 (if supported by the underlying system).[br] +		The -i switch parameter may serve also as a target address when the -c switch is used.[br] +		 +		!sw: -p=<port> | --port=<port> +		Bind the local listening socket to the specified <port>. +		If this switch is NOT specified, the port will be a "random" one choosen by the kernel.[br] + +		!sw: -a=<fake address> | --fake-address=<fake address> +		Send the <fake address> as target for the remote client in the requesting CTCP message. +		If this switch is not given, the CTCP will contain the real IP address of the listening +		interface.[br] +		 +		!sw: -f=<fake port> | --fake-port=<fake port> +		Send the <fake port> as target port for the remote client in the requesting CTCP message. +		If this switch is not given, the CTCP will contain the real port of the listening socket. +		[br][br] +		All these switches are meant to allow maximum flexibility of the +		DCC negotiation, earlier KVIrc releases had serious problems +		with firewalled and/or masqueraded machines. With the -a and -f switches +		you can work around it.[br] +		[br] + +		!sw: -u | --unlimited +		If the 'u' switch is given, the connection attempt will +		never time out; this might be useful if you want to leave +		a listening socket for a friend of yours while you are sleeping +		and have the CTCP processing disabled. The 'u' switch works either +		in active and passive mode.[br] + +	@description: +		Attempts a DCC Voice connection to <nickname>.[br] +		The -g option is used to select the GSM codec, available codecs are "gsm", "adpcm" and "null".[br] +		The adpcm codec is the one that was used in previous KVIrc versions, it provides a 1:4 compression rate +		and is designed for 8 KHz audio sampling rate (but will work also with other sampling rates).[br] +		The gsm codec offers 1:10 compression at the cost of some quality and cpu time. If you have a good +		cpu and plan to transmit voice only, use this codec.<br> The null codec +		offers no compression and may be used to transfer plain audio data over a fast connection (usually loopback +		connection or local networks). The null codec with 44100 sampling rate would provide CD quality audio +		streaming, but it is practically not usable (at the time I'm writing) since requires a +		monster bandwidth. If the -g switch is not present, the adpcm codec is used by default (for compatibility reasons).[br] + 		The -h switch is used to select the sampling rate, if not given the sampling rate defaults to 8000 Hz. +		This switch accepts any value, but in fact the soundcards have limits on the values. Typically +		the lowest limit is 5 KHz and the upper limit is 44.1 KHz (but some soundcards support 96 KHz). +		It is also possible that the soundcard can't support a continous range of frequencies and +		will select a discrete closest match instead.[br] +		The "commonly used" sample rates are 8000, 11025, 22050 and 44100 Hz.[br] +		The remaining parameters are equivalent to the ones used in [cmd]dcc.send[/cmd], so please refer to that +		help page for the full discussion. This help page contains only a brief resume of these parameters.[br] +	@examples: +		[example] +			[comment]# Setup a DCC VOICE connection with Pragma (IRC user)[/comment] +			dcc.voice Pragma +			[comment]# Setup a DCC VOICE connection with Pragma and use the gsm codec[/comment] +			dcc.voice -g=gsm Pragma +			[comment]# Setup a DCC VOICE connection with Pragma, use the gsm codec and 22050 Hz sampling rate[/comment] +			dcc.voice -g=gsm -h=22050 Pragma +			[comment]# Setup a listening socket for a DCC VOICE connection on interface 127.0.0.1 and port 8088[/comment] +			dcc.voice -n -i=127.0.0.1 -p=8088 Pippo +			[comment]# Connect to the socket above[/comment] +			dcc.voice -c -i=127.0.0.1 -p=8088 Pluto +			[comment]# Same as above but use the NULL codec with 11025 Hz sampling rate[/comment] +			dcc.voice -g=null -h=11025 -n -i=127.0.0.1 -p=8088 Pippo +			[comment]# Connect to the socket above[/comment] +			dcc.voice -g=null -h=11025 -c -i=127.0.0.1 -p=8088 Pluto +		[/example] +*/ + +static bool dcc_kvs_cmd_voice(KviKvsModuleCommandCall * c) +{ +	QString szTarget; +	KVSM_PARAMETERS_BEGIN(c) +		KVSM_PARAMETER("target",KVS_PT_NONEMPTYSTRING,0,szTarget) +	KVSM_PARAMETERS_END(c) + +#ifdef COMPILE_DISABLE_DCC_VOICE +	c->warning(__tr2qs_ctx("DCC VOICE support not enabled at compilation time","dcc")); +	return true; +#endif + +	KviDccDescriptor * d = new KviDccDescriptor(c->window()->console()); + +	d->szNick       = szTarget;              // we always specify the nickname +	d->szUser       = __tr2qs_ctx("unknown","dcc"); // username is always unknown +	d->szHost       = d->szUser;                 // host is always unknown +	d->iSampleRate  = 8000; + +	if(!dcc_kvs_parse_default_parameters(d,c))return false; + +	if(KviKvsVariant * pSR = c->switches()->find('h',"sample-rate")) +	{ +		kvs_int_t iSR; +		if(!pSR->asInteger(iSR)) +		{ +			c->warning(__tr2qs_ctx("Invalid sample rate specified, defaulting to 8000","dcc")); +			d->iSampleRate = 8000; +		} else { +			d->iSampleRate = iSR; +		} +	} + +	d->szCodec = "adpcm"; + +	if(KviKvsVariant * pCodec = c->switches()->find('g',"codec")) +	{ +		QString szCodec; +		pCodec->asString(szCodec); + +		if(!kvi_dcc_voice_is_valid_codec(szCodec)) +		{ +			c->warning(__tr2qs_ctx("Invalid codec specified, defaulting to 'adpcm'","dcc")); +			d->szCodec = "adpcm"; +		} +	} + +	dcc_module_set_dcc_type(d,"VOICE"); +	if(c->switches()->find('c',"connect")) +	{ +		if(!(c->switches()->find('i',"ip") && c->switches()->find('p',"port"))) +		{ +			delete d; +			c->error(__tr2qs_ctx("-c requires -i and -p","dcc")); +			return false; +		} +		d->szIp         = d->szListenIp; +		d->szPort       = d->szListenPort; +		d->szListenIp   = ""; // useless +		d->szListenPort = ""; // useless +		d->bActive      = true; + +		d->triggerCreationEvent(); +		g_pDccBroker->activeVoiceExecute(0,d); +	} else { +		d->szIp         = __tr2qs_ctx("unknown","dcc"); +		d->szPort       = d->szIp; +		d->bActive      = false; +		d->bSendRequest = !(c->switches()->find('n',"no-ctcp")); + +		d->triggerCreationEvent(); +		g_pDccBroker->passiveVoiceExecute(d); +	} + +	return true; +} + + + + +/* +static bool dcc_module_cmd_canvas(KviModule *m,KviCommand *c) +{ +	ENTER_STACK_FRAME(c,"dcc_module_cmd_canvas"); + +	KviStr target; +	if(!g_pUserParser->parseCmdFinalPart(c,target))return false; + +	if(target.isEmpty())return c->error(KviError_notEnoughParameters,"%s",__tr_ctx("Missing target nickname","dcc")); + +	KviDccDescriptor * d = new KviDccDescriptor(c->window()->console()); + +	d->szNick       = target.ptr();              // we always specify the nickname +	d->szUser       = __tr2qs_ctx("unknown","dcc"); // username is always unknown +	d->szHost       = d->szUser;                 // host is always unknown +*/ +/* +	d->bIsTdcc = c->hasSwitch('t'); + +	d->bOverrideMinimize = c->hasSwitch('m'); + +	if(d->bOverrideMinimize) +	{ +		KviStr tmpVal; +		if(!(c->getSwitchValue('m',tmpVal)))d->bShowMinimized = false; +		else d->bShowMinimized = kvi_strEqualCI(tmpVal.ptr(),"1"); +	} + + +	if(!d->console()) +	{ +		// We don't need a console with -c and -n , otherwise we need it +		if(!(c->hasSwitch('c') || c->hasSwitch('n')))return c->noIrcContext(); +		else d->console() = c->window()->frame()->firstConsole(); +	} + +	__range_valid(d->console()); + +	if(!d->console()->isConnected()) +	{ +		// We don't need a connection with -c and -n , otherwise we need it +		if(!(c->hasSwitch('c') || c->hasSwitch('n')))return c->notConnectedToServer(); +		else { +			// -c or -n , grab a local nick from somewhere +			d->szLocalNick  = KVI_OPTION_STRING(KviOption_stringNickname1); +			d->szLocalNick.stripWhiteSpace(); +			if(d->szLocalNick.isEmpty())d->szLocalNick = KVI_DEFAULT_NICKNAME1; +			d->szLocalUser  = __tr("unknown"); // we can live without it +			d->szLocalHost  = d->szLocalUser;  // we can live without it +		} +	} else { +		// We know everything +		d->szLocalNick  = d->console()->currentNickName(); +		d->szLocalUser  = d->console()->currentUserName(); +		d->szLocalHost  = d->console()->currentLocalHostName(); +	} + + +	if(c->hasSwitch('i')) +	{ +		c->getSwitchValue('i',d->szListenIp); +		if(!(d->szListenIp.contains('.') || d->szListenIp.contains(':'))) +		{ +			// This will magically work with the same buffer as source and dst! +			if(!kvi_getInterfaceAddress(d->szListenIp.ptr(),d->szListenIp)) +			{ +				return c->error(KviError_invalidParameter,__tr("Can't get address of interface %s"),d->szListenIp.ptr()); +			} +		} +	} else { +		if(d->console()->isConnected()) +		{ +			d->console()->socket()->getLocalHostIp(d->szListenIp,d->console()->isIpV6Connection()); +		} else d->szListenIp = "127.0.0.1"; // huh ? :) +	} + +	if(c->hasSwitch('p'))c->getSwitchValue('p',d->szListenPort); +	else d->szListenPort = "0"; // any port is ok + +	if(c->hasSwitch('a'))c->getSwitchValue('a',d->szFakeIp); + +	if(c->hasSwitch('f'))c->getSwitchValue('f',d->szFakePort); + +	d->bDoTimeout = (!c->hasSwitch('u')); + +*/ +/* +	if(!dcc_module_parse_default_parameters(d,c))return false; +	dcc_module_set_dcc_type(d,"VOICE"); + +	if(c->hasSwitch('c')) +	{ +		if(!(c->hasSwitch('i') && c->hasSwitch('p'))) +		{ +			delete d; +			return c->error(KviError_notEnoughParameters,__tr_ctx("-c requires -i and -p","dcc")); +		} +		d->szIp         = d->szListenIp; +		d->szPort       = d->szListenPort; +		d->szListenIp   = ""; // useless +		d->szListenPort = ""; // useless +		d->bActive      = true; + +		d->triggerCreationEvent(); +		g_pDccBroker->activeCanvasExecute(0,d); +	} else { +		d->szIp         = __tr2qs_ctx("unknown","dcc"); +		d->szPort       = d->szIp; +		d->bActive      = false; +		d->bSendRequest = !c->hasSwitch('n'); + +		d->triggerCreationEvent(); +		g_pDccBroker->passiveCanvasExecute(d); +	} + +	return c->leaveStackFrame(); +} +*/ + + + + + +/* +	@doc: dcc_connection +	@type: +		generic +	@title: +		DCC negotiation and connection +	@short: +		Overview of the DCC internals +	@keyterms: +		DCC without IRC +	@body: +		[big]What is DCC?[/big][br] +		'DCC' stands for Direct Client Connection, it is used to exchange data +		directly between two IRC clients (with no IRC server in the middle).[br] +		DCC itself is not a well-defined protocol, but rather a set of +		subprotocols with (more or less) standardized rules.[br] +		Sub-protocols are also (historically) called [b]"DCC types"[/b]; this term often +		leads to confusion and it will become clear later.[br] +		Each subprotocol has two main parts: The [b]DCC negotiation[/b] and the [b]DCC transfer[/b].[br] +		The [b]DCC negotiation[/b] part is used to request the [b]DCC transfer[/b] and define its necessary parameters,[br] +		while the [b]DCC transfer[/b] part is the real data transfer between clients.[br] +		The [b]DCC negotiation[/b] requires a third entity that routes the negotiation data between clients, +		this is usually an IRC server.[br] +		[br] +		[big]DCC Negotiation[/big][br] +		This part of the protocol is the most tricky and difficult one, and is different for almost every DCC subprotocol.[br] +		The "constant" scenario of the negotiation is more or less the following:[br] +		There are two IRC clients connected to the same IRC network and they want to exchange some data in +		a direct client connection.[br] +		Each client knows the other by nickname only (and eventually by the host displayed by the IRC server, +		but this cannot be trusted for several reasons), and can send text messages to each other by using the +		IRC network as data channel.[br] +		To initiate a direct client connection, one of the clients must start listening on some port (this is called [b]passive client[/b]) +		and the other must connect to that port on the first client's machine (this is the [b]active client[/b]).[br] +		Both clients must agree on who is the passive and who is the active client. The active client must also +		know the passive client's IP address and port (in order to be able to contact it).[br] +		Finally, both clients must agree on the transfer type that has to be initiated.[br] +		The negotiation exchanges these informations between clients by using IRC as channel and CTCP messages +		as encoding method.[br] +		An example will make things clearer:[br] +		DCC Chat is the simplest (and most widely implemented) DCC subprotocol: +		it is used to exchange <cr><lf> separated text data between clients.[br] +		Assume that you want to establish a DCC Chat +		connection to 'Sarah' that is actually connected to your IRC network (so +		she/he is an IRC user just like you). +		All you have to do is type sth as "/dcc chat Sarah" in your IRC client. +		The client will setup a listening socket on a random port choosen +		usually by the kernel of your OS. In this case YOU are the [b]passive client[/b], and Sarah is the active one.[br] +		Once the socket is ready to accept connections, +		your client will send a [doc:ctcp_handling]CTCP message[/doc] to Sarah using the IRC connection (and protocol) as channel:[br] +		PRIVMSG Sarah :<0x01>DCC CHAT chat <ip_address> <port><0x01>[br] +		where <ip_address> is the address of the listening socket and <port> +		is the port that it has been bound to (these informations are obtained +		after the socket has been setup). Once Sarah has received the CTCP message, +		and agreed to connect, her (active) client will attempt to connect to the +		specified <ip_address> and <port> (eg. to your listening socket).[br] +		Once the connection has been established, it continues using the +		specific CHAT transfer protocol.[br] +		Some IRC clients allow modifications of this procedure:[br] +		First of all, the port to listen on can be specified by the user +		and not by the kernel; this is useful when the passive client +		is behind a firewall that "shades" some sets of ports. +		The ip address for the listening socket +		can be specified by the user as well (especially when the machine has more than one network interface).[br] +		A more challenging trick is to listen on a specified ip address and port +		and notify different ones to the remote user (eg, <ip_address> and <port> +		parameters of the CTCP message are not the ones that the client is listening on). +		This is especially useful with "transparent proxy" firewalls that +		often are not transparent enough to allow the DCC connections. +		(If you have one of these firewalls you know what I'm talking about, +		otherwise just read on). KVIrc allows to avoid the usage of a third entity +		for the protocol negotiation too. +		You can setup a listening socket on a specified port and ip address +		without notyfying anyone of this. +		You can also manually connect to a specified port and ip address without +		having been notified of a DCC request.[br][br][br] +		Is everything clear ?...I don't think so... my English is really too bad... +		[br] +		[big]DCC Transfer[/big][br] +		The DCC transfer part is different for every DCC subprotocol, but +		it always happens over a direct client to client TCP connection.[br] +		[br] +		[big]DCC Subprotocols[/big][br] +		There are two main standardized DCC subprotocols that are widely implemented in IRC clients: +		[b]DCC Chat[/b] and [b]DCC Send[/b].[br] +		DCC Chat is quite simple and the protocol is more or less completely defined.[br] +		DCC Send is a *real mess*, the original definition was not very flexible +		so many IRC clients tried to enchance both the negotiation and the transfer, leading +		often to incompatible implementations. (I can remember the Turbo File Transfer implemented +		by VIrc, the Send-Ahead enchancement implemented in many clients, the RESUME facility...)[br] +		Many clients introduced new DCC subprotocols with non-standard implementations, +		leading again to client incompatibility.[br] +		Some of the notable subprotocols are DCC Voice, DCC Draw, DCC Whiteboard...[br] +		[br] +		[big]DCC Chat[/big][br] +		This is the simplest and most standardized DCC subprotocol. Almost every IRC client implements it.[br] +		It is used to exchange lines of text between the two clients.[br] +		The negotiation is quite simple, we assume that Client A wants to establish a DCC Chat connection to Client B. +		Client A sets up a listening socket and retrieves its adress (ip address and port).[br] +		Once the socket is ready Client A sends a CTCP request to B, in the following form:[br] +		[b]DCC CHAT chat <ipaddress> <port>[/b][br] +		Where <ipaddress> is a string representing an positive integer that is the A socket's IP address +		in network byte order, and where <port> is a string representing an positive integer that is the +		A socket's port.[br] +		The original purpose of the second "chat" string in the CTCP request is quite obscure, it was probably +		introduced to have the <ipaddress> as second parameter, as in the DCC Send subprotocol.[br] +		Client B receives the CTCP, parses it, eventually asks the user for permission and connects +		to the specified ip address and port. +		The transfer protocol is quite simple, both clients can send text lines separated by <cr><lf> pairs.[br] +		Some clients use only <lf> as line terminator so the general idea is that one of <cr> <cr><lf> or <lf> +		can be used as line terminator.[br] +		As extension to the protocol, KVIrc allows <ipaddress> to be an IPv6 address in the standard hexadecimal +		notation, the connection will be made over the IPv6 protocol in this case (obviously if both clients +		support this feature).[br] +		(It is not clear why the original DCC specification used the unsigned int format instead of a +		standard string representation of the IP address... missing inet_aton() function on the target system?).[br] +		KVIrc adds the Secure Sockets Layer to the DCC Chat protocol. In this case the negotiation string becomes:[br] +		[b]DCC SCHAT chat <ipaddress> <port>[/b][br] +		where "SCHAT" stands for Secure CHAT.[br] The external protocol is exactly the same but is built on top of a Secure Sockets Layer +		implementation (specifically OpenSSL). The connection will be encrypted with a private key algorithm after +		a public key handshake.[br] +		[br] +		[big]DCC Send[/big][br] +		DCC Send is another standard subprotocol. Most clients implement this as well, many have tried +		to enchance it.[br] +		The basic DCC Send protocol allows transferring a file from the requesting client to the receiving client.[br] +		The requesting client (the one that sends the file) is always passive and the receiving client is always active.[br] +		This is a huge protocol limitation since firewalled clients are often unable to accept incoming connections.[br] +		The negotiation protocol is more complex than DCC Chat; we assume that Client A wants to send the file F to Client B.[br] +		Client A sets up a listening socket and retrieves its ip address and port.[br] +		Client A sends a CTCP request to Client B in the following form:[br] +		[b]DCC SEND <filename> <ipaddress> <port> <filesize>[/b][br] +		<ipaddress> and <port> have the same semantics as in the DCC Chat subprotocol.[br] +		<filename> is the name (without path!) of the file to be sent, and <filesize> is (yeah), the file size.[br] +		Client B receives the CTCP, parses it, eventually asks the user for confirmation and connects to the +		specified ip address and port; the transfer then begins.[br] +		Client A sends blocks of data (usually 1-2 KB) and at every block awaits confirmation from the Client B, +		that when receiving a block should reply 4 bytes containing an positive number specifying the total size +		of the file received up to that moment.[br] +		The transmission closes when the last acknowledge is received by Client A.[br] +		The acknowledges were meant to include some sort of coherency check in the transmission, but in fact +		no client can "recover" from an acknowledge error/desync, all of them just close the connection declaring the +		transfer as failed (the situation is even worse in fact, often acknowledge errors aren't even detected!).[br] +		Since the packet-acknowledge round trip eats a lot of time, many clients included +		the "send-ahead" feature; the Client A does NOT wait for the acknowledge of the first packet before sending the second one.[br] +		The acknowledges are still sent, but just a reverse independent stream.[br] This makes the DCC Send considerably faster.[br] +		Since the acknowledge stream has non-zero bandwidth usage, no client can recover from an acknowledge error and +		having them as an independant stream is more or less like having no acknowledges, the "Turbo" ( :) ) extension has been added: +		Client B will send no acknowledges and will just close the connection when he has received all the expected data.[br] +		This makes the DCC Send as fast as FTP transfers.[br] +		The "Turbo" extension is specified during the negotiation phase, bu using TSEND as DCC message type (instead of SEND).[br] +		The "Turbo" extension is not widely implemented.[br] +		Later implementations have added the support for resuming interrupted DCC Send transfers:[br] +		Client A sets up the socket and sends the CTCP request as before.[br] +		If Client B discovers that the file has been partially received in a previous DCC Send session it sends +		a resume request in the following form:[br] +		[b]DCC RESUME <filename> <port> <resume position>[/b][br] +		Where <port> is the <port> sent in the DCC SEND request and <resume position> is the position in the file +		from where the transfer should start.[br] +		Cilent A receives the request, parses it and eventually replies with:[br] +		[b]DCC ACCEPT <filename> <port> <resume position>[/b][br] +		Client B receives the ACCEPT message, connects to Client A and the transfer initiates as before.[br] +		The "Send-ahead" and "Turbo" extensions can obviously be used also in this case (But 'T' is NOT prepended to the RESUME and ACCEPT messages).[br] +		The IPv6 extension can be used also in this subprotocol, so <ipaddress> can be also an IPv6 address in hexadecimal notation.[br] +		KVIrc introduces the SSL extension also to DCC Send. The protocol remains the same again but it is built on top of +		a Secure Sockets Layer implementation just like DCC Chat.[br] +		With SSL the negotiation string becomes:[br] +		[b]DCC SSEND <filename> <ipaddress> <port> <filesize>[/b][br] +		where "SSEND" stands for Secure SEND.[br] +		The "turbo" extension can be combined with the SSL extension too. In this case the second parameter +		of the negotiation string must be "TSSEND" or "STSEND".[br] +		[br] +		[big]DCC Recv[/big][br] +		DCC Recv is the counterpart of DCC Send. This is a KVIrc extension and is not standard yet.[br] +		The purpose of this subprotocol will not be immediately clear, but read on for an explanation.[br] +		It is used to request a file from another client; we assume that Client A knows that Client B has +		a specific file and is able/wants to send it.[br] +		Client A sets up a listening socket, retrieves its address and port and then +		sends a CTCP request to Client B in the following form:[br] +		[b]DCC RECV <filename> <ipaddress> <port> <resume position>[/b][br] +		where <filename> is the name of the requested file without path, <ipaddress> and <port> have the usual meaning and <resume position> +		is the position from that the transfer should start from.[br] +		<ipaddress> can be an IPv6 address as well.[br] +		Client B receives the CTCP message, parses it, looks for the file to send (in some unspecified way) +		and connects to the specified ip address and port. The transfer then begins just as in the DCC send, but in the inverse way: +		Client B sends blocks of data to Client A and Client B sends back acknowledges.[br] +		This subprotocol is useful in transferring data from clients that are behind a firewall and are not able to accept +		incoming connections (this is not possible with a normal DCC Send). In this case the client that receives +		the file is passive and the client that sends it is active (as opposite to DCC Send).[br] +		The "Send ahead" extension can be used also in this case and the "Turbo" extension is activated by prepending a 'T' to the +		DCC message, "TRECV" instead of "RECV". The SSL extension is activated by prepending an 'S' to the +		DCC message, "SRECV", "STRECV" or "TSRECV".[br] +		This subprotocol has an implicit resume capability and thus has no need for RESUME and ACCEPT messages.[br] +		DCC Recv requires the initiating (passive) client to know that the file to be transferred is avaiable on the B's side +		and probably also know the file size. This subprotocol does not specify how this information is obtained, but it +		will become clear soon that it can be obtained either manually (User B can simply tell the info to User A), +		or automatically (as in the DCC Rsend subprotocol (keep reading)).[br] +		[br] +		[big]DCC RSend[/big][br] +		DCC RSend stands for Reverse Send. This is a KVIrc extension to the SEND protocol to allow firewalled clients +		to send files.[br] In fact, this is a "half" subprotocol, since it defines only a part of the DCC negotiation; +		the transfer is defined by another subprotocol (and specifically bu DCC Recv).[br] +		The requesting client (the one that sends the file) is active and the receiving client is passive.[br] +		Assume that Client A wants to send a file to Client B and that Client A cannot accept incoming connections.[br] +		Client A sends a CTCP request to Client B in the following form:[br] +		[b]DCC RSEND <filename> <filesize>[/b][br] +		Client B receives the request, parses it, eventually asks the user for confirmation, sets up a listening socket, retrieves +		its ip address and port and switches to the DCC Recv subprotocol by effectively sending the following CTCP message:[br] +		[b]DCC RECV <filename> <ipaddress> <port> <resume position>[/b][br] +		The rest of the transfer is defined by the DCC Recv subprotocol.[br] +		The "Turbo" extension is again activated by prepending a 'T' to the RSEND string, so the initial CTCP will become:[br] +		[b]DCC TRSEND <filename> <filesize>[/b][br] +		The "SSL" extension is also activated by prepending an 'S' to the RSEND string. It can be again combined +		with the "turbo" extension. The negotiation parameter becomes then "SRSEND","TSRSEND" or "STRSEND".[br] +		Easy, no ? :)[br] +		[br] +		[big]DCC Get[/big][br] +		This is again a "half" subprotocol in fact since it defines only a part of the negotiation for file transfers.[br] +		It is also NON standard, since actually no client except KVIrc implements it (AFAIK).[br] +		DCC Get is used to request a file from a remote client. Assume that Client A wants to request a file from Client B +		(and assume that Client A knows that B has that file and wants to send it).[br] +		Client A sends a CTCP message to Client B in the following form:[br] +		[b]DCC GET <filename>[/b][br] +		Where <filename> is a name of a file without path.[br] +		Client B receives the message, parses it, looks for an association of the <filename> to a real filesystem file +		and starts one of the two DCC File transfer subprotocols, DCC Send or DCC RSend.[br] +		Client B should prefer the DCC Send method and choose DCC RSend only if it is not able to accept incoming connections.[br] +		This subprotocol can be used by firewalled clients that can't accept connections but still want to request a file +		from another client, this one can fail only if both clients are firewalled (in this case no DCC transfer is possible at all).[br] +		This subprotocol also does not need to "magically" know the file size, the size definition +		is found in the subprotocol that the remote client will choose.[br] +		The association of <filename> with a real file on the B's machine is not explicitly defined by the subprotocol; +		KVIrc uses an internal "file-offer" table with a list of files that are available for download.[br] +		The "turbo" and "SSL" extensions are activated as usual, "TGET", "SGET", "TSGET" and "STGET" are supported.[br] +		[br] +		[big]DCC File Transfer[/big][br] +		DCC Send: Send a file, sender is passive, receiver is active (not good for firewalled senders)[br] +		DCC Recv: Receive a file, sender is active, receiver is passive (not good for firewalled receivers)[br] +		DCC RSend: Send a file, sender is active, receiver is passive (not good for firewalled receivers)[br] +		DCC Get: Receive a file, sender is passive if not firewalled, receiver active if sender not firewalled (will fail only if both are firewalled)[br] +		The "turbo" extension disables the stream of acknowledges and is activated by prepending the 'T' character to the DCC subprotocol name[br] +		The "SSL" extension causes a Secure Socket Layer to be used and is activated by prepending the 'S' character to the DCC subprotocol name[br] +		[br] +		[big]DCC Voice[/big][br] +		DCC Voice is a KVIrc extension (there is a Windows client called VIrc that implements such +		a protocol, but it is incompatible with KVIrc).[br] +		DCC Voice allows audio level communication between two clients, the audio stream is compressed +		with a specified codec.[br] +		KVIrc currently supports the ADPCM (core support) and the GSM codec (if the libgsm is available on the target system).[br] +		[b]TODO: Finish the DCC Voice doc :)[/b] +		[big]More tricks[/big][br] +		KVIrc supports another "hack" to the DCC negotiation, it recognizes "XDCC" as +		a DCC negotiation CTCP parameter.[br] +		This can be used to circumvent limitations of some IRC clients (read mIRC) that will not allow +		you to send a /DCC GET since it is an unrecognized DCC type.[br] +		"XDCC" has exactly the same meaning as "DCC" (at least in KVIrc).[br] +*/ + +static KviDccDescriptor * dcc_kvs_find_dcc_descriptor(const kvs_uint_t &uId,KviKvsModuleRunTimeCall * c,bool bWarn = true) +{ +	KviDccDescriptor * dcc = 0; +	if(uId == 0) +	{ +		if(c->window()->inherits("KviDccWindow")) +		{ +			dcc = ((KviDccWindow *)(c->window()))->descriptor(); +		} +		if((!dcc) && bWarn) +			c->warning(__tr2qs_ctx("The current window has no associated DCC session","dcc")); +		return dcc; +	} +	dcc = KviDccDescriptor::find(uId); +	if((!dcc) && bWarn) +		c->warning(__tr2qs_ctx("The specified parameter is not a valid DCC identifier","dcc")); +	return dcc; +} + + +/* +	@doc: dcc.abort +	@type: +		command +	@title: +		dcc.abort +	@short: +		Aborts a dcc session +	@syntax: +		dcc.abort [-q] [dcc_id:uint] +	@description: +		Terminates the Direct Client Connection specified by <dcc_id>.[br] +		If <dcc_id> is omitted then the DCC Session associated +		to the current window is assumed.[br] +		If <dcc_id> is not a valid DCC session identifier (or it is omitted +		and the current window has no associated DCC session) then +		this function doesn't abort anything and prints a warning unless the -q switch is used.[br] +		If <dcc_id> refers to a file transfer then it the transfer is simply +		terminated. If <dcc_id> refers to a dcc chat then the result +		is equivalent to closing the related window.[br] +		See the [module:dcc]dcc module[/module] documentation for more informations.[br] +	@examples: +*/ + +static bool dcc_kvs_cmd_abort(KviKvsModuleCommandCall * c) +{ +	kvs_uint_t uDccId; +	KVSM_PARAMETERS_BEGIN(c) +		KVSM_PARAMETER("dcc_id",KVS_PT_UINT,KVS_PF_OPTIONAL,uDccId) +	KVSM_PARAMETERS_END(c) + +	KviDccDescriptor * dcc = dcc_kvs_find_dcc_descriptor(uDccId,c,!c->switches()->find('q',"quiet")); + +	if(dcc) +	{ +		if(dcc->transfer())dcc->transfer()->abort(); +		else if(dcc->window())dcc->window()->close(); +	} + +	return true; +} + +/* +	@doc: dcc.setBandwidthLimit +	@type: +		command +	@title: +		dcc.setBandwidthLimit +	@short: +		Set the bandwidthlimit of a dcc.send session. +	@syntax: +		dcc.setBandwidthLimit [-q] [dcc_id:uint] +	@description: +		Terminates the Direct Client Connection specified by <dcc_id>.[br] +		If <dcc_id> is omitted then the DCC Session associated +		to the current window is assumed.[br] +		If <dcc_id> is not a valid DCC session identifier (or it is omitted +		and the current window has no associated DCC session) then +		this function  prints a warning unless the -q switch is used.[br] +		If <dcc_id> does not refers to a file transfer a warning will be printing unless the -q switch is used.[br] +		See the [module:dcc]dcc module[/module] documentation for more informations.[br] +	@examples: +*/ +static bool dcc_kvs_cmd_setBandwidthLimit(KviKvsModuleCommandCall * c) +{ +	kvs_uint_t uDccId,uVal; +	KVSM_PARAMETERS_BEGIN(c) +		KVSM_PARAMETER("limit_value",KVS_PT_UINT,0,uVal) +		KVSM_PARAMETER("dcc_id",KVS_PT_UINT,KVS_PF_OPTIONAL,uDccId) +	KVSM_PARAMETERS_END(c) + +	KviDccDescriptor * dcc = dcc_kvs_find_dcc_descriptor(uDccId,c,!c->switches()->find('q',"quiet")); +	if(dcc) +	{ +		if (dcc->transfer())dcc->transfer()->setBandwidthLimit(uVal); +		else if (!c->switches()->find('q',"quiet"))c->warning(__tr2qs_ctx("This DCC session is not a DCC transfer session","dcc")); +	} +	return true; +} + +/* +	@doc: dcc.protocol +	@type: +		function +	@title: +		$dcc.protocol +	@short: +		Returns the protocol of the specified DCC +	@syntax: +		<string> $dcc.protocol +		<string> $dcc.protocol(<dcc_id:uint>) +	@description: +		Returns the string describing the protocol of the +		Direct Client Connection specified by <dcc_id>.[br] +		If <dcc_id> is omitted then the DCC Session associated +		to the current window is assumed.[br] +		If <dcc_id> is not a valid DCC session identifier (or it is omitted +		and the current window has no associated DCC session) then +		this function prints a warning and returns an empty sting.[br] +		See the [module:dcc]dcc module[/module] documentation for more informations.[br] +*/ + +static bool dcc_kvs_fnc_protocol(KviKvsModuleFunctionCall * c) +{ +	kvs_uint_t uDccId; +	KVSM_PARAMETERS_BEGIN(c) +		KVSM_PARAMETER("dcc_id",KVS_PT_UINT,KVS_PF_OPTIONAL,uDccId) +	KVSM_PARAMETERS_END(c) + +	KviDccDescriptor * dcc = dcc_kvs_find_dcc_descriptor(uDccId,c); + +	if(dcc)c->returnValue()->setString(dcc->protocol()); +	return true; +} + + +/* +	@doc: dcc.connectionType +	@type: +		function +	@title: +		$dcc.connectionType +	@short: +		Returns the connection type of the specified DCC +	@syntax: +		<string> $dcc.connectionType +		<string> $dcc.connectionType(<dcc_id:uint>) +	@description: +		Returns the connection type of the specified DCC.[br] +		Returns the string "ACTIVE" for active DCC connections +		and the string "PASSIVE" for passive DCC connections. +		If <dcc_id> is omitted then the DCC Session associated +		to the current window is assumed.[br] +		If <dcc_id> is not a valid DCC session identifier (or it is omitted +		and the current window has no associated DCC session) then +		this function prints a warning and returns an empty sting.[br] +		See the [module:dcc]dcc module[/module] documentation for more informations.[br] +*/ + +static bool dcc_kvs_fnc_connectionType(KviKvsModuleFunctionCall * c) +{ +	kvs_uint_t uDccId; +	KVSM_PARAMETERS_BEGIN(c) +		KVSM_PARAMETER("dcc_id",KVS_PT_UINT,KVS_PF_OPTIONAL,uDccId) +	KVSM_PARAMETERS_END(c) + +	KviDccDescriptor * dcc = dcc_kvs_find_dcc_descriptor(uDccId,c); + +	if(dcc)c->returnValue()->setString(dcc->isActive() ? "ACTIVE" : "PASSIVE"); +	return true; +} + + +/* +	@doc: dcc.isFileTransfer +	@type: +		function +	@title: +		$dcc.isFileTransfer +	@short: +		Checks if a DCC is a file transfer +	@syntax: +		<boolean> $dcc.isFileTransfer +		<boolean> $dcc.isFileTransfer(<dcc_id:uint>) +	@description: +		Returns 1 if the specified Direct Client Connection +		is a file transfer and 0 otherwise.[br] +		If <dcc_id> is omitted then the DCC Session associated +		to the current window is assumed.[br] +		If <dcc_id> is not a valid DCC session identifier (or it is omitted +		and the current window has no associated DCC session) then +		this and returns 0.[br] +		See the [module:dcc]dcc module[/module] documentation for more informations.[br] +*/ + +static bool dcc_kvs_fnc_isFileTransfer(KviKvsModuleFunctionCall * c) +{ +	kvs_uint_t uDccId; +	KVSM_PARAMETERS_BEGIN(c) +		KVSM_PARAMETER("dcc_id",KVS_PT_UINT,KVS_PF_OPTIONAL,uDccId) +	KVSM_PARAMETERS_END(c) + +	KviDccDescriptor * dcc = dcc_kvs_find_dcc_descriptor(uDccId,c,false); + +	if(dcc)c->returnValue()->setBoolean(dcc->isFileTransfer()); +	return true; +} + + +/* +	@doc: dcc.isFileUpload +	@type: +		function +	@title: +		$dcc.isFileUpload +	@short: +		Checks if a DCC is an upload file transfer +	@syntax: +		<boolean> $dcc.isFileUpload +		<boolean> $dcc.isFileUpload(<dcc_id:uint>) +	@description: +		Returns 1 if the specified Direct Client Connection +		is an upload file transfer and 0 otherwise.[br] +		If <dcc_id> is omitted then the DCC Session associated +		to the current window is assumed.[br] +		If <dcc_id> is not a valid DCC session identifier (or it is omitted +		and the current window has no associated DCC session) then +		this function prints a warning and returns 0.[br] +		See the [module:dcc]dcc module[/module] documentation for more informations.[br] +*/ + +static bool dcc_kvs_fnc_isFileUpload(KviKvsModuleFunctionCall * c) +{ +	kvs_uint_t uDccId; +	KVSM_PARAMETERS_BEGIN(c) +		KVSM_PARAMETER("dcc_id",KVS_PT_UINT,KVS_PF_OPTIONAL,uDccId) +	KVSM_PARAMETERS_END(c) + +	KviDccDescriptor * dcc = dcc_kvs_find_dcc_descriptor(uDccId,c); + +	if(dcc)c->returnValue()->setBoolean(dcc->isFileUpload()); +	return true; +} + + +/* +	@doc: dcc.isFileDownload +	@type: +		function +	@title: +		$dcc.isFileDownload +	@short: +		Checks if a DCC is a download file transfer +	@syntax: +		<boolean> $dcc.isFileDownload +		<boolean> $dcc.isFileDownload(<dcc_id:uint>) +	@description: +		Returns 1 if the specified Direct Client Connection +		is a download file transfer and 0 otherwise.[br] +		If <dcc_id> is omitted then the DCC Session associated +		to the current window is assumed.[br] +		If <dcc_id> is not a valid DCC session identifier (or it is omitted +		and the current window has no associated DCC session) then +		this function prints a warning and returns 0.[br] +		See the [module:dcc]dcc module[/module] documentation for more informations.[br] +*/ + +static bool dcc_kvs_fnc_isFileDownload(KviKvsModuleFunctionCall * c) +{ +	kvs_uint_t uDccId; +	KVSM_PARAMETERS_BEGIN(c) +		KVSM_PARAMETER("dcc_id",KVS_PT_UINT,KVS_PF_OPTIONAL,uDccId) +	KVSM_PARAMETERS_END(c) + +	KviDccDescriptor * dcc = dcc_kvs_find_dcc_descriptor(uDccId,c); + +	if(dcc)c->returnValue()->setBoolean(dcc->isFileDownload()); +	return true; +} + + +/* +	@doc: dcc.localNick +	@type: +		function +	@title: +		$dcc.localNick +	@short: +		Returns the local nickname associated to the specified DCC +	@syntax: +		<string> $dcc.localNick +		<string> $dcc.localNick(<dcc_id:uint>) +	@description: +		Returns the local nickname associated to the specified DCC.[br] +		If <dcc_id> is omitted then the DCC Session associated +		to the current window is assumed.[br] +		If <dcc_id> is not a valid DCC session identifier (or it is omitted +		and the current window has no associated DCC session) then +		this function prints a warning and returns an empty sting.[br] +		See the [module:dcc]dcc module[/module] documentation for more informations.[br] +*/ + +static bool dcc_kvs_fnc_localNick(KviKvsModuleFunctionCall * c) +{ +	kvs_uint_t uDccId; +	KVSM_PARAMETERS_BEGIN(c) +		KVSM_PARAMETER("dcc_id",KVS_PT_UINT,KVS_PF_OPTIONAL,uDccId) +	KVSM_PARAMETERS_END(c) + +	KviDccDescriptor * dcc = dcc_kvs_find_dcc_descriptor(uDccId,c); + +	if(dcc)c->returnValue()->setString(dcc->localNick()); +	return true; +} + + +/* +	@doc: dcc.localUser +	@type: +		function +	@title: +		$dcc.localUser +	@short: +		Returns the local username associated to the specified DCC +	@syntax: +		<string> $dcc.localUser +		<string> $dcc.localUser(<dcc_id:uint>) +	@description: +		Returns the local username associated to the specified DCC.[br] +		If <dcc_id> is omitted then the DCC Session associated +		to the current window is assumed.[br] +		If <dcc_id> is not a valid DCC session identifier (or it is omitted +		and the current window has no associated DCC session) then +		this function prints a warning and returns an empty sting.[br] +		See the [module:dcc]dcc module[/module] documentation for more informations.[br] +*/ + +static bool dcc_kvs_fnc_localUser(KviKvsModuleFunctionCall * c) +{ +	kvs_uint_t uDccId; +	KVSM_PARAMETERS_BEGIN(c) +		KVSM_PARAMETER("dcc_id",KVS_PT_UINT,KVS_PF_OPTIONAL,uDccId) +	KVSM_PARAMETERS_END(c) + +	KviDccDescriptor * dcc = dcc_kvs_find_dcc_descriptor(uDccId,c); + +	if(dcc)c->returnValue()->setString(dcc->localUser()); +	return true; +} + +/* +	@doc: dcc.localHost +	@type: +		function +	@title: +		$dcc.localHost +	@short: +		Returns the local hostname associated to the specified DCC +	@syntax: +		<string> $dcc.localHost +		<string> $dcc.localHost(<dcc_id:uint>) +	@description: +		Returns the local hostname associated to the specified DCC.[br] +		If <dcc_id> is omitted then the DCC Session associated +		to the current window is assumed.[br] +		If <dcc_id> is not a valid DCC session identifier (or it is omitted +		and the current window has no associated DCC session) then +		this function prints a warning and returns an empty sting.[br] +		See the [module:dcc]dcc module[/module] documentation for more informations.[br] +*/ + +static bool dcc_kvs_fnc_localHost(KviKvsModuleFunctionCall * c) +{ +	kvs_uint_t uDccId; +	KVSM_PARAMETERS_BEGIN(c) +		KVSM_PARAMETER("dcc_id",KVS_PT_UINT,KVS_PF_OPTIONAL,uDccId) +	KVSM_PARAMETERS_END(c) + +	KviDccDescriptor * dcc = dcc_kvs_find_dcc_descriptor(uDccId,c); + +	if(dcc)c->returnValue()->setString(dcc->localHost()); +	return true; +} + + +/* +	@doc: dcc.localIp +	@type: +		function +	@title: +		$dcc.localIp +	@short: +		Returns the local ip address associated to the specified DCC +	@syntax: +		<string> $dcc.localIp +		<string> $dcc.localIp(<dcc_id:uint>) +	@description: +		Returns the local ip address associated to the specified DCC.[br] +		If <dcc_id> is omitted then the DCC Session associated +		to the current window is assumed.[br] +		If <dcc_id> is not a valid DCC session identifier (or it is omitted +		and the current window has no associated DCC session) then +		this function prints a warning and returns an empty sting.[br] +		See the [module:dcc]dcc module[/module] documentation for more informations.[br] +*/ + +static bool dcc_kvs_fnc_localIp(KviKvsModuleFunctionCall * c) +{ +	kvs_uint_t uDccId; +	KVSM_PARAMETERS_BEGIN(c) +		KVSM_PARAMETER("dcc_id",KVS_PT_UINT,KVS_PF_OPTIONAL,uDccId) +	KVSM_PARAMETERS_END(c) + +	KviDccDescriptor * dcc = dcc_kvs_find_dcc_descriptor(uDccId,c); + +	if(dcc)c->returnValue()->setString(dcc->localIp()); +	return true; +} + + +/* +	@doc: dcc.localPort +	@type: +		function +	@title: +		$dcc.localPort +	@short: +		Returns the local port associated to the specified DCC +	@syntax: +		<integer> $dcc.localPort +		<integer> $dcc.localPort(<dcc_id:uint>) +	@description: +		Returns the local port associated to the specified DCC.[br] +		If <dcc_id> is omitted then the DCC Session associated +		to the current window is assumed.[br] +		If <dcc_id> is not a valid DCC session identifier then +		this function prints a warning and returns an empty sting.[br] +		See the [module:dcc]dcc module[/module] documentation for more informations.[br] +*/ + +static bool dcc_kvs_fnc_localPort(KviKvsModuleFunctionCall * c) +{ +	kvs_uint_t uDccId; +	KVSM_PARAMETERS_BEGIN(c) +		KVSM_PARAMETER("dcc_id",KVS_PT_UINT,KVS_PF_OPTIONAL,uDccId) +	KVSM_PARAMETERS_END(c) + +	KviDccDescriptor * dcc = dcc_kvs_find_dcc_descriptor(uDccId,c); + +	if(dcc)c->returnValue()->setString(dcc->localPort()); +	return true; +} + + +/* +	@doc: dcc.localFileName +	@type: +		function +	@title: +		$dcc.localFileName +	@short: +		Returns the local file name associated to the specified DCC +	@syntax: +		<string> $dcc.localFileName(<dcc_id:uint>) +	@description: +		Returns the local file name associated to the specified DCC.[br] +		If <dcc_id> does not identify a file transfer DCC then this +		function returns an empty string. +		If <dcc_id> is not a valid Direct Client Connection identifier +		then this function prints a warning and returns an empty string. +*/ + +static bool dcc_kvs_fnc_localFileName(KviKvsModuleFunctionCall * c) +{ +	kvs_uint_t uDccId; +	KVSM_PARAMETERS_BEGIN(c) +		KVSM_PARAMETER("dcc_id",KVS_PT_UINT,KVS_PF_OPTIONAL,uDccId) +	KVSM_PARAMETERS_END(c) + +	KviDccDescriptor * dcc = dcc_kvs_find_dcc_descriptor(uDccId,c); + +	if(dcc)c->returnValue()->setString(dcc->localFileName()); +	return true; +} + + +/* +	@doc: dcc.localFileSize +	@type: +		function +	@title: +		$dcc.localFileSize +	@short: +		Returns the local file size associated to the specified DCC +	@syntax: +		<integer> $dcc.localFileSize(<dcc_id:uint>) +	@description: +		Returns the local file size associated to the specified DCC.[br] +		If <dcc_id> does not identify a file transfer DCC then this +		function returns '0'[br] +		If <dcc_id> is not a valid Direct Client Connection identifier +		then this function prints a warning and returns '0'[br] +		In upload transfers the local file size rappresents the +		total size of the file to be transferred. In download transfers +		the local file size is non zero only if the transfer has to resume +		a file already existing on the local disk and it rappresents the +		size of that file (and thus the offset that the transfer started on). +*/ + +static bool dcc_kvs_fnc_localFileSize(KviKvsModuleFunctionCall * c) +{ +	kvs_uint_t uDccId; +	KVSM_PARAMETERS_BEGIN(c) +		KVSM_PARAMETER("dcc_id",KVS_PT_UINT,KVS_PF_OPTIONAL,uDccId) +	KVSM_PARAMETERS_END(c) + +	KviDccDescriptor * dcc = dcc_kvs_find_dcc_descriptor(uDccId,c); + +	if(dcc)c->returnValue()->setString(dcc->localFileSize().isEmpty() ? QString("0") : dcc->localFileSize()); +	return true; +} + + +/* +	@doc: dcc.remoteNick +	@type: +		function +	@title: +		$dcc.remoteNick +	@short: +		Returns the remote nickname associated to the specified DCC +	@syntax: +		<string> $dcc.remoteNick +		<string> $dcc.remoteNick(<dcc_id:uint>) +	@description: +		Returns the remote nickname associated to the specified DCC.[br] +		If <dcc_id> is omitted then the DCC Session associated +		to the current window is assumed.[br] +		If <dcc_id> is not a valid DCC session identifier (or it is omitted +		and the current window has no associated DCC session) then +		this function prints a warning and returns an empty sting.[br] +		See the [module:dcc]dcc module[/module] documentation for more informations.[br] +*/ + +static bool dcc_kvs_fnc_remoteNick(KviKvsModuleFunctionCall * c) +{ +	kvs_uint_t uDccId; +	KVSM_PARAMETERS_BEGIN(c) +		KVSM_PARAMETER("dcc_id",KVS_PT_UINT,KVS_PF_OPTIONAL,uDccId) +	KVSM_PARAMETERS_END(c) + +	KviDccDescriptor * dcc = dcc_kvs_find_dcc_descriptor(uDccId,c); + +	if(dcc)c->returnValue()->setString(dcc->remoteNick()); +	return true; +} + + +/* +	@doc: dcc.remoteUser +	@type: +		function +	@title: +		$dcc.remoteUser +	@short: +		Returns the remote username associated to the specified DCC +	@syntax: +		<string> $dcc.remoteUser +		<string> $dcc.remoteUser(<dcc_id:uint>) +	@description: +		Returns the remote username associated to the specified DCC.[br] +		If <dcc_id> is omitted then the DCC Session associated +		to the current window is assumed.[br] +		If <dcc_id> is not a valid DCC session identifier (or it is omitted +		and the current window has no associated DCC session) then +		this function prints a warning and returns an empty sting.[br] +		See the [module:dcc]dcc module[/module] documentation for more informations.[br] +*/ + +static bool dcc_kvs_fnc_remoteUser(KviKvsModuleFunctionCall * c) +{ +	kvs_uint_t uDccId; +	KVSM_PARAMETERS_BEGIN(c) +		KVSM_PARAMETER("dcc_id",KVS_PT_UINT,KVS_PF_OPTIONAL,uDccId) +	KVSM_PARAMETERS_END(c) + +	KviDccDescriptor * dcc = dcc_kvs_find_dcc_descriptor(uDccId,c); + +	if(dcc)c->returnValue()->setString(dcc->remoteUser()); +	return true; +} + +/* +	@doc: dcc.remoteHost +	@type: +		function +	@title: +		$dcc.remoteHost +	@short: +		Returns the remote hostname associated to the specified DCC +	@syntax: +		<string> $dcc.remoteHost +		<string> $dcc.remoteHost(<dcc_id:uint>) +	@description: +		Returns the remote hostname associated to the specified DCC.[br] +		If <dcc_id> is omitted then the DCC Session associated +		to the current window is assumed.[br] +		If <dcc_id> is not a valid DCC session identifier (or it is omitted +		and the current window has no associated DCC session) then +		this function prints a warning and returns an empty sting.[br] +		See the [module:dcc]dcc module[/module] documentation for more informations.[br] +*/ + +static bool dcc_kvs_fnc_remoteHost(KviKvsModuleFunctionCall * c) +{ +	kvs_uint_t uDccId; +	KVSM_PARAMETERS_BEGIN(c) +		KVSM_PARAMETER("dcc_id",KVS_PT_UINT,KVS_PF_OPTIONAL,uDccId) +	KVSM_PARAMETERS_END(c) + +	KviDccDescriptor * dcc = dcc_kvs_find_dcc_descriptor(uDccId,c); + +	if(dcc)c->returnValue()->setString(dcc->remoteHost()); +	return true; +} + +/* +	@doc: dcc.remoteIp +	@type: +		function +	@title: +		$dcc.remoteIp +	@short: +		Returns the remote ip address associated to the specified DCC +	@syntax: +		<string> $dcc.remoteIp +		<string> $dcc.remoteIp(<dcc_id:uint>) +	@description: +		Returns the remote ip address associated to the specified DCC.[br] +		If <dcc_id> is omitted then the DCC Session associated +		to the current window is assumed.[br] +		If <dcc_id> is not a valid DCC session identifier (or it is omitted +		and the current window has no associated DCC session) then +		this function prints a warning and returns an empty sting.[br] +		See the [module:dcc]dcc module[/module] documentation for more informations.[br] +*/ + +static bool dcc_kvs_fnc_remoteIp(KviKvsModuleFunctionCall * c) +{ +	kvs_uint_t uDccId; +	KVSM_PARAMETERS_BEGIN(c) +		KVSM_PARAMETER("dcc_id",KVS_PT_UINT,KVS_PF_OPTIONAL,uDccId) +	KVSM_PARAMETERS_END(c) + +	KviDccDescriptor * dcc = dcc_kvs_find_dcc_descriptor(uDccId,c); + +	if(dcc)c->returnValue()->setString(dcc->remoteIp()); +	return true; +} + + +/* +	@doc: dcc.remotePort +	@type: +		function +	@title: +		$dcc.remotePort +	@short: +		Returns the remote port associated to the specified DCC +	@syntax: +		<integer> $dcc.remotePort +		<integer> $dcc.remotePort(<dcc_id:uint>) +	@description: +		Returns the remote port associated to the specified DCC.[br] +		If <dcc_id> is omitted then the DCC Session associated +		to the current window is assumed.[br] +		If <dcc_id> is not a valid DCC session identifier (or it is omitted +		and the current window has no associated DCC session) then +		this function prints a warning and returns an empty sting.[br] +		See the [module:dcc]dcc module[/module] documentation for more informations.[br] +*/ + +static bool dcc_kvs_fnc_remotePort(KviKvsModuleFunctionCall * c) +{ +	kvs_uint_t uDccId; +	KVSM_PARAMETERS_BEGIN(c) +		KVSM_PARAMETER("dcc_id",KVS_PT_UINT,KVS_PF_OPTIONAL,uDccId) +	KVSM_PARAMETERS_END(c) + +	KviDccDescriptor * dcc = dcc_kvs_find_dcc_descriptor(uDccId,c); + +	if(dcc)c->returnValue()->setString(dcc->remotePort()); +	return true; +} + + +/* +	@doc: dcc.remoteFileName +	@type: +		function +	@title: +		$dcc.remoteFileName +	@short: +		Returns the remote file name associated to the specified DCC +	@syntax: +		<string> $dcc.remoteFileName(<dcc_id:uint>) +	@description: +		Returns the remote file name associated to the specified DCC.[br] +		If <dcc_id> does not identify a file transfer DCC then this +		function returns an empty string. +		If <dcc_id> is not a valid Direct Client Connection identifier +		then this function prints a warning and returns an empty string. +*/ + +static bool dcc_kvs_fnc_remoteFileName(KviKvsModuleFunctionCall * c) +{ +	kvs_uint_t uDccId; +	KVSM_PARAMETERS_BEGIN(c) +		KVSM_PARAMETER("dcc_id",KVS_PT_UINT,KVS_PF_OPTIONAL,uDccId) +	KVSM_PARAMETERS_END(c) + +	KviDccDescriptor * dcc = dcc_kvs_find_dcc_descriptor(uDccId,c); + +	if(dcc)c->returnValue()->setString(dcc->remoteFileName()); +	return true; +} + + +/* +	@doc: dcc.remoteFileSize +	@type: +		function +	@title: +		$dcc.remoteFileSize +	@short: +		Returns the remote file size associated to the specified DCC +	@syntax: +		<integer> $dcc.remoteFileSize(<dcc_id:uint>) +	@description: +		Returns the remote file size associated to the specified DCC.[br] +		If <dcc_id> does not identify a file transfer DCC then this +		function returns '0'[br] +		If <dcc_id> is not a valid Direct Client Connection identifier +		then this function prints a warning and returns '0'[br] +		In download transfers the remote file size rappresents the +		total size of the file to be transferred (advertished by the remote end).[br] +		In upload transfers the remote file size is non zero only if the +		remote user has issued a resume request and is rappresents the requested offset +		in bytes from which the transfer has started. +*/ + +static bool dcc_kvs_fnc_remoteFileSize(KviKvsModuleFunctionCall * c) +{ +	kvs_uint_t uDccId; +	KVSM_PARAMETERS_BEGIN(c) +		KVSM_PARAMETER("dcc_id",KVS_PT_UINT,KVS_PF_OPTIONAL,uDccId) +	KVSM_PARAMETERS_END(c) + +	KviDccDescriptor * dcc = dcc_kvs_find_dcc_descriptor(uDccId,c); + +	if(dcc)c->returnValue()->setString(dcc->remoteFileSize().isEmpty() ? QString("0") : dcc->remoteFileSize()); +	return true; +} + + +/* +	@doc: dcc.ircContext +	@type: +		function +	@title: +		$dcc.ircContext +	@short: +		Returns the ircContext from which this DCC has originated +	@syntax: +		<integer> $dcc.ircContext +		<integer> $dcc.ircContext(<dcc_id:uint>) +	@description: +		Returns the identifier of the irc context from which +		the specified DCC has been originated.[br] +		When the DCC is not originated from an IRC context +		then this function returns '0' : an invalid irc context id. +		If <dcc_id> is omitted then the DCC Session associated +		to the current window is assumed.[br] +		If <dcc_id> is not a valid DCC session identifier (or it is omitted +		and the current window has no associated DCC session) then +		this function prints a warning and returns 0.[br] +		See the [module:dcc]dcc module[/module] documentation for more informations.[br] +*/ + +static bool dcc_kvs_fnc_ircContext(KviKvsModuleFunctionCall * c) +{ +	kvs_uint_t uDccId; +	KVSM_PARAMETERS_BEGIN(c) +		KVSM_PARAMETER("dcc_id",KVS_PT_UINT,KVS_PF_OPTIONAL,uDccId) +	KVSM_PARAMETERS_END(c) + +	KviDccDescriptor * dcc = dcc_kvs_find_dcc_descriptor(uDccId,c); + +	if(dcc)c->returnValue()->setInteger(dcc->console()->context()->id()); +	return true; +} + + + +/* +	@doc: dcc.transferStatus +	@type: +		function +	@title: +		$dcc.transferStatus +	@short: +		Returns the current status of a dcc file transfer +	@syntax: +		<string> $dcc.transferStatus +		<string> $dcc.transferStatus(<dcc_id:uint>) +	@description: +		Returns the status in the specified DCC session.[br] +		The status is one of the strings "connecting", "transferring", "success" and "failure". +		"success" and "failure" are reported when the transfer is terminated. +		If <dcc_id> is omitted then the DCC Session associated +		to the current window is assumed.[br] +		If <dcc_id> is not a valid DCC session identifier (or it is omitted +		and the current window has no associated DCC session) then +		this function prints a warning and returns an empty sting.[br] +		If the DCC session does not refer to a file transfer then +		this function returns "".[br] +		See the [module:dcc]dcc module[/module] documentation for more informations.[br] +*/ + +static bool dcc_kvs_fnc_transferStatus(KviKvsModuleFunctionCall * c) +{ +	kvs_uint_t uDccId; +	KVSM_PARAMETERS_BEGIN(c) +		KVSM_PARAMETER("dcc_id",KVS_PT_UINT,KVS_PF_OPTIONAL,uDccId) +	KVSM_PARAMETERS_END(c) + +	KviDccDescriptor * dcc = dcc_kvs_find_dcc_descriptor(uDccId,c); + +	if(dcc) +	{ +		if(dcc->transfer()) +		{ +			QString tmp; +			dcc->transfer()->fillStatusString(tmp); +			c->returnValue()->setString(tmp); +		} +	} +	return true; +} + + +/* +	@doc: dcc.transferredBytes +	@type: +		function +	@title: +		$dcc.transferredBytes +	@short: +		Returns the number of transferred bytes in a dcc file transfer +	@syntax: +		<integer> $dcc.transferredBytes +		<integer> $dcc.transferredBytes(<dcc_id:uint>) +	@description: +		Returns the number of transferred bytes in the specified DCC session.[br] +		If <dcc_id> is omitted then the DCC Session associated +		to the current window is assumed.[br] +		If <dcc_id> is not a valid DCC session identifier (or it is omitted +		and the current window has no associated DCC session) then +		this function prints a warning and returns an empty sting.[br] +		If the DCC session does not refer to a file transfer then +		this function returns 0.[br] +		See the [module:dcc]dcc module[/module] documentation for more informations.[br] +*/ + +static bool dcc_kvs_fnc_transferredBytes(KviKvsModuleFunctionCall * c) +{ +	kvs_uint_t uDccId; +	KVSM_PARAMETERS_BEGIN(c) +		KVSM_PARAMETER("dcc_id",KVS_PT_UINT,KVS_PF_OPTIONAL,uDccId) +	KVSM_PARAMETERS_END(c) + +	KviDccDescriptor * dcc = dcc_kvs_find_dcc_descriptor(uDccId,c); + +	if(dcc) +	{ +		if(dcc->transfer()) +		{ +			c->returnValue()->setInteger(dcc->transfer()->transferredBytes()); +		} else { +			c->returnValue()->setInteger(0); +		} +	} +	return true; +} + + + +/* +	@doc: dcc.averageSpeed +	@type: +		function +	@title: +		$dcc.averageSpeed +	@short: +		Returns the average speed of a dcc file transfer +	@syntax: +		$dcc.averageSpeed +		$dcc.averageSpeed(<dcc_id>) +	@description: +		Returns the average speed (in bytes/sec) of the specified DCC session.[br] +		If <dcc_id> is omitted then the DCC Session associated +		to the current window is assumed.[br] +		If <dcc_id> is not a valid DCC session identifier (or it is omitted +		and the current window has no associated DCC session) then +		this function prints a warning and returns an empty sting.[br] +		If the DCC session does not refer to a file transfer then +		this function returns 0.[br] +		See the [module:dcc]dcc module[/module] documentation for more informations.[br] +*/ + +static bool dcc_kvs_fnc_averageSpeed(KviKvsModuleFunctionCall * c) +{ +	kvs_uint_t uDccId; +	KVSM_PARAMETERS_BEGIN(c) +		KVSM_PARAMETER("dcc_id",KVS_PT_UINT,KVS_PF_OPTIONAL,uDccId) +	KVSM_PARAMETERS_END(c) + +	KviDccDescriptor * dcc = dcc_kvs_find_dcc_descriptor(uDccId,c); + +	if(dcc) +	{ +		if(dcc->transfer()) +		{ +			c->returnValue()->setInteger(dcc->transfer()->averageSpeed()); +		} else { +			c->returnValue()->setInteger(0); +		} +	} +	return true; +} + + + +/* +	@doc: dcc.session +	@type: +		function +	@title: +		$dcc.session +	@short: +		Returns the DCC session identifier associated to a window +	@syntax: +		<uint> $dcc.session +		<uint> $dcc.session(<window_id>) +	@description: +		Returns the DCC session identifier associated to the DCC window specified +		by <window_id>. If <window_id> is omitted then the DCC session identifier +		associated to the current window is returned. If the specified window +		has no associated DCC session then a warning is printed and 0 is returned.[br] +*/ + +static bool dcc_kvs_fnc_session(KviKvsModuleFunctionCall * c) +{ +	QString szWinId; +	KVSM_PARAMETERS_BEGIN(c) +		KVSM_PARAMETER("window_id",KVS_PT_STRING,KVS_PF_OPTIONAL,szWinId) +	KVSM_PARAMETERS_END(c) + +	KviDccDescriptor * dcc = 0; +	if(szWinId.isEmpty()) +	{ +		if(c->window()->inherits("KviDccWindow")) +			dcc = ((KviDccWindow *)(c->window()))->descriptor(); +		if(!dcc) +		{ +			c->warning(__tr2qs_ctx("The current window has no associated DCC session","dcc")); +			c->returnValue()->setInteger(0); +		} else { +			c->returnValue()->setInteger(dcc->id()); +		} +		return true; +	} + +	KviWindow * pWnd = g_pApp->findWindow(szWinId); +	if(!pWnd) +	{ +		c->warning(__tr2qs_ctx("The specified window identifier is not valid","dcc")); +		c->returnValue()->setInteger(0); +		return true; +	} + +	if(pWnd->inherits("KviDccWindow")) +		dcc = ((KviDccWindow *)pWnd)->descriptor(); +	if(!dcc) +	{ +		c->warning(__tr2qs_ctx("The current window has no associated DCC session","dcc")); +		c->returnValue()->setInteger(0); +	} else { +		c->returnValue()->setInteger(dcc->id()); +	} +	return true; +} + + +/* +	@doc: dcc.sessionList +	@type: +		function +	@title: +		$dcc.sessionList +	@short: +		List the existing dcc session identifiers +	@syntax: +		<array> $dcc.sessionList +		<array> $dcc.sessionList(<filter:string>) +	@description: +		The first form returns an array with all the currently existing dcc session +		identifiers. The second form returns an array with the session types specified +		in <filter> which may be a combination of the flags 'u' (for file upload),  +		'd' (for file download) and 'c' (for dcc chat). To select all the file transfers +		please use the combination 'ud'.[br] +		See the [module:dcc]dcc module[/module] documentation for more informations.[br] +*/ + +static bool dcc_kvs_fnc_sessionList(KviKvsModuleFunctionCall * c) +{ +	QString szFlags; +	KVSM_PARAMETERS_BEGIN(c) +		KVSM_PARAMETER("filter",KVS_PT_STRING,KVS_PF_OPTIONAL,szFlags) +	KVSM_PARAMETERS_END(c) + +	KviKvsArray * a = new KviKvsArray(); +	c->returnValue()->setArray(a); + +	KviPointerHashTable<int,KviDccDescriptor> * dict = KviDccDescriptor::descriptorDict(); +	if(!dict)return true; + +	KviPointerHashTableIterator<int,KviDccDescriptor> it(*dict); + +	int idx = 0; +	 +	if(szFlags.isEmpty()) +	{ +		// all +		while(KviDccDescriptor * dcc = it.current()) +		{ +			a->set(idx++,new KviKvsVariant((kvs_int_t)(dcc->id()))); +			++it; +		} +	} else { +		bool bWantFileUploads = szFlags.find('u',false) != -1; +		bool bWantFileDownloads = szFlags.contains('d',false) != -1; +		bool bWantChats = szFlags.contains('c',false) != -1; + +		while(KviDccDescriptor * dcc = it.current()) +		{ +			if((dcc->isFileUpload() && bWantFileUploads) || +				(dcc->isFileDownload() && bWantFileDownloads) || +				(dcc->isDccChat() && bWantChats)) +			{ +				a->set(idx++,new KviKvsVariant((kvs_int_t)(dcc->id()))); +			} +			++it; +		} +	} + +	return true; +} + + +/* +	@doc: dcc +	@type: +		module +	@short: +		Direct Client Connections +	@title: +		The DCC module +	@body: +		[big]Overview[/big][br] +		The DCC module handles the Direct Client Connection +		protocol layer and all it's sub-protocols.[br] +		The sub-protocols include the standard CHAT +		the standard SEND and its variants plus several +		KVIrc extensions like RECV,RSEND,GET and VOICE.[br] +		[br] +		[big]Initiating a DCC negotiation[/big][br] +		The following commands initiate a specific DCC session +		with a remote client:[br] +		[cmd]dcc.chat[/cmd][br] +		[cmd]dcc.send[/cmd][br] +		[cmd]dcc.rsend[/cmd][br] +		[cmd]dcc.recv[/cmd][br] +		[cmd]dcc.get[/cmd][br] +		[cmd]dcc.voice[/cmd][br] +		[br] +		[big]Handling the DCC events[/big][br] +		Each DCC session has an associated unique identifier (<dcc_id>).[br] +		You can interact with the session by using several commands +		and functions exported by this module and by passing the above session +		id as parameter.[br] +		The session related commands and functions are the following:[br] +		[fnc]$dcc.sessionList[/fnc][br] +		[fnc]$dcc.protocol[/fnc][br] +		[fnc]$dcc.connectionType[/fnc][br] +		[fnc]$dcc.transferStatus[/fnc][br] +		[fnc]$dcc.isFileTransfer[/fnc][br] +		[fnc]$dcc.isFileUpload[/fnc][br] +		[fnc]$dcc.isFileDownload[/fnc][br] +		[fnc]$dcc.localNick[/fnc][br] +		[fnc]$dcc.localUser[/fnc][br] +		[fnc]$dcc.localHost[/fnc][br] +		[fnc]$dcc.localIp[/fnc][br] +		[fnc]$dcc.localPort[/fnc][br] +		[fnc]$dcc.localFileName[/fnc][br] +		[fnc]$dcc.localFileSize[/fnc][br] +		[fnc]$dcc.remoteNick[/fnc][br] +		[fnc]$dcc.remoteUser[/fnc][br] +		[fnc]$dcc.remoteHost[/fnc][br] +		[fnc]$dcc.remoteIp[/fnc][br] +		[fnc]$dcc.remotePort[/fnc][br] +		[fnc]$dcc.remoteFileName[/fnc][br] +		[fnc]$dcc.remoteFileSize[/fnc][br] +		[fnc]$dcc.ircContext[/fnc][br] +		[fnc]$dcc.session[/fnc][br] +*/ + + +static bool dcc_module_init(KviModule * m) +{ +	g_pDccBroker = new KviDccBroker(); + +	KVSM_REGISTER_SIMPLE_COMMAND(m,"send",dcc_kvs_cmd_send); +	KVSM_REGISTER_SIMPLE_COMMAND(m,"chat",dcc_kvs_cmd_chat); +	KVSM_REGISTER_SIMPLE_COMMAND(m,"voice",dcc_kvs_cmd_voice); +	KVSM_REGISTER_SIMPLE_COMMAND(m,"recv",dcc_kvs_cmd_recv); +	KVSM_REGISTER_SIMPLE_COMMAND(m,"rsend",dcc_kvs_cmd_rsend); +	KVSM_REGISTER_SIMPLE_COMMAND(m,"get",dcc_kvs_cmd_get); +	KVSM_REGISTER_SIMPLE_COMMAND(m,"abort",dcc_kvs_cmd_abort); +	KVSM_REGISTER_SIMPLE_COMMAND(m,"setBandwidthLimit",dcc_kvs_cmd_setBandwidthLimit); + + +	// FIXME: file upload / download state ? + +	KVSM_REGISTER_FUNCTION(m,"transferStatus",dcc_kvs_fnc_transferStatus); +	KVSM_REGISTER_FUNCTION(m,"protocol",dcc_kvs_fnc_protocol); +	KVSM_REGISTER_FUNCTION(m,"connectionType",dcc_kvs_fnc_connectionType); +	KVSM_REGISTER_FUNCTION(m,"isFileTransfer",dcc_kvs_fnc_isFileTransfer); +	KVSM_REGISTER_FUNCTION(m,"isFileUpload",dcc_kvs_fnc_isFileUpload); +	KVSM_REGISTER_FUNCTION(m,"isFileDownload",dcc_kvs_fnc_isFileDownload); +	KVSM_REGISTER_FUNCTION(m,"localNick",dcc_kvs_fnc_localNick); +	KVSM_REGISTER_FUNCTION(m,"localUser",dcc_kvs_fnc_localUser); +	KVSM_REGISTER_FUNCTION(m,"localHost",dcc_kvs_fnc_localHost); +	KVSM_REGISTER_FUNCTION(m,"localIp",dcc_kvs_fnc_localIp); +	KVSM_REGISTER_FUNCTION(m,"localPort",dcc_kvs_fnc_localPort); +	KVSM_REGISTER_FUNCTION(m,"localFileName",dcc_kvs_fnc_localFileName); +	KVSM_REGISTER_FUNCTION(m,"localFileSize",dcc_kvs_fnc_localFileSize); +	KVSM_REGISTER_FUNCTION(m,"remoteNick",dcc_kvs_fnc_remoteNick); +	KVSM_REGISTER_FUNCTION(m,"remoteUser",dcc_kvs_fnc_remoteUser); +	KVSM_REGISTER_FUNCTION(m,"remoteHost",dcc_kvs_fnc_remoteHost); +	KVSM_REGISTER_FUNCTION(m,"remoteIp",dcc_kvs_fnc_remoteIp); +	KVSM_REGISTER_FUNCTION(m,"remotePort",dcc_kvs_fnc_remotePort); +	KVSM_REGISTER_FUNCTION(m,"remoteFileName",dcc_kvs_fnc_remoteFileName); +	KVSM_REGISTER_FUNCTION(m,"remoteFileSize",dcc_kvs_fnc_remoteFileSize); +	KVSM_REGISTER_FUNCTION(m,"averageSpeed",dcc_kvs_fnc_averageSpeed); +	KVSM_REGISTER_FUNCTION(m,"transferredBytes",dcc_kvs_fnc_transferredBytes); +	KVSM_REGISTER_FUNCTION(m,"ircContext",dcc_kvs_fnc_ircContext); +	KVSM_REGISTER_FUNCTION(m,"session",dcc_kvs_fnc_session); +	KVSM_REGISTER_FUNCTION(m,"sessionList",dcc_kvs_fnc_sessionList); + +	return true; +} + +static bool dcc_module_cleanup(KviModule *m) +{ +	delete g_pDccBroker; +	g_pDccBroker = 0; +#ifdef COMPILE_USE_GSM +	kvi_gsm_codec_done(); +#endif + +	return true; +} + +static bool dcc_module_can_unload(KviModule *m) +{ +	return g_pDccBroker ? g_pDccBroker->canUnload() : true; +} + +KVIRC_MODULE( +	"Dcc", +	"1.0.0", +	"Copyright (C) 2000-2004:\n" \ +	"  Szymon Stefanek (pragma at kvirc dot net)\n", +	"DCC extension for KVIrc\n", +	dcc_module_init, +	dcc_module_can_unload, +	0, +	dcc_module_cleanup +) | 
