aboutsummaryrefslogtreecommitdiffstats
path: root/src/irc/dcc.lua
diff options
context:
space:
mode:
Diffstat (limited to 'src/irc/dcc.lua')
-rw-r--r--src/irc/dcc.lua122
1 files changed, 122 insertions, 0 deletions
diff --git a/src/irc/dcc.lua b/src/irc/dcc.lua
new file mode 100644
index 0000000..f227d4b
--- /dev/null
+++ b/src/irc/dcc.lua
@@ -0,0 +1,122 @@
+-- initialization {{{
+local base = _G
+local irc = require 'irc'
+local irc_debug = require 'irc.debug'
+local misc = require 'irc.misc'
+local socket = require 'socket'
+local coroutine = require 'coroutine'
+local io = require 'io'
+local string = require 'string'
+-- }}}
+
+module 'irc.dcc'
+
+-- defaults {{{
+FIRST_PORT = 1028
+LAST_PORT = 5000
+-- }}}
+
+-- private functions {{{
+-- send_file {{{
+local function send_file(sock, file, size, packet_size)
+ local bytes = 0
+ while true do
+ local packet = file:read(packet_size)
+ if not packet then break end
+ bytes = bytes + packet:len()
+ local index = 1
+ while true do
+ sock:send(packet, index)
+ local new_bytes = misc.int_to_str(sock:receive(4))
+ if new_bytes ~= bytes then
+ index = packet_size - bytes + new_bytes + 1
+ else
+ break
+ end
+ end
+ if bytes >= size then break end
+ coroutine.yield(true)
+ end
+ file:close()
+ sock:close()
+ irc._unregister_socket(sock, 'w')
+ return true
+end
+-- }}}
+
+-- handle_connect {{{
+local function handle_connect(ssock, file, size, packet_size)
+ packet_size = packet_size or 1024
+ local sock = ssock:accept()
+ sock:settimeout(0.1)
+ ssock:close()
+ irc._unregister_socket(ssock, 'r')
+ irc._register_socket(sock, 'w',
+ coroutine.wrap(function(sock)
+ return send_file(sock, file, size, packet_size)
+ end))
+ return true
+end
+-- }}}
+
+-- accept_file {{{
+local function accept_file(sock, file, size, packet_size)
+ local bytes = 0
+ while true do
+ local packet, err, partial_packet = sock:receive(packet_size)
+ if not packet and err == "timeout" then packet = partial_packet end
+ if not packet then break end
+ if packet:len() == 0 then break end
+ bytes = bytes + packet:len()
+ sock:send(misc.str_to_int(bytes))
+ file:write(packet)
+ coroutine.yield(true)
+ end
+ file:close()
+ sock:close()
+ irc._unregister_socket(sock, 'r')
+ return true
+end
+-- }}}
+-- }}}
+
+-- public functions {{{
+-- send {{{
+function send(nick, filename, port)
+ port = port or FIRST_PORT
+ local sock = base.assert(socket.tcp())
+ repeat
+ err, msg = sock:bind('*', port)
+ port = port + 1
+ until msg ~= "address already in use" and port <= LAST_PORT + 1
+ base.assert(err, msg)
+ base.assert(sock:listen(1))
+ local ip = misc.ip_str_to_int(irc.get_ip())
+ local file = base.assert(io.open(filename))
+ local size = file:seek("end")
+ file:seek("set")
+ irc._register_socket(sock, 'r',
+ coroutine.wrap(function(sock)
+ return handle_connect(sock, file, size)
+ end))
+ filename = misc.basename(filename)
+ if filename:find(" ") then filename = '"' .. filename .. '"' end
+ irc.send("PRIVMSG", nick, {"DCC SEND " .. filename .. " " ..
+ ip .. " " .. port - 1 .. " " .. size})
+end
+-- }}}
+
+-- accept {{{
+function accept(filename, address, port, size, packet_size)
+ packet_size = packet_size or 1024
+ local sock = base.assert(socket.tcp())
+ base.assert(sock:connect(misc.ip_int_to_str(address), port))
+ sock:settimeout(0.1)
+ local file = base.assert(io.open(misc.get_unique_filename(filename), "w"))
+ irc._register_socket(sock, 'r',
+ coroutine.wrap(function(sock)
+ return accept_file(sock, file, size, packet_size)
+ end))
+end
+-- }}}
+-- }}}