summaryrefslogtreecommitdiffstats
path: root/src/modules/dcc/libkvidcc.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/modules/dcc/libkvidcc.cpp')
-rw-r--r--src/modules/dcc/libkvidcc.cpp2766
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 00000000..717f568d
--- /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 (&lt;dcc_id&gt;).[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
+)