X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Firc%2Fdcc.lua;h=be8892b02ac85f80a671674104058746baeef64a;hb=21a59a8e39d7f1457dae23485515fbdadc8f92b8;hp=45eb446c81adf5fa9de619d778966e61323a0907;hpb=43ca8ab69a3a948add1f5d7f29c869e82924d160;p=luairc.git diff --git a/src/irc/dcc.lua b/src/irc/dcc.lua index 45eb446..be8892b 100644 --- a/src/irc/dcc.lua +++ b/src/irc/dcc.lua @@ -3,6 +3,8 @@ -- initialization {{{ local base = _G local irc = require 'irc' +local ctcp = require 'irc.ctcp' +local c = ctcp._ctcp_quote local irc_debug = require 'irc.debug' local misc = require 'irc.misc' local socket = require 'socket' @@ -22,18 +24,23 @@ LAST_PORT = 5000 -- }}} -- private functions {{{ +-- debug_dcc {{{ +-- +-- Prints a debug message about DCC events similar to irc.debug.warn, etc. +-- @param msg Debug message +local function debug_dcc(msg) + irc_debug._message("DCC", msg, "\027[0;32m") +end +-- }}} + -- send_file {{{ --- TODO: no reason to be sending the size parameter all over the place when we --- only need it in this function. also, should probably seek to the beginning --- of the file before sending it. -- -- Sends a file to a remote user, after that user has accepted our DCC SEND -- invitation -- @param sock Socket to send the file on -- @param file Lua file object corresponding to the file we want to send --- @param size Size of the file to send -- @param packet_size Size of the packets to send the file in -local function send_file(sock, file, size, packet_size) +local function send_file(sock, file, packet_size) local bytes = 0 while true do local packet = file:read(packet_size) @@ -41,17 +48,30 @@ local function send_file(sock, file, size, packet_size) bytes = bytes + packet:len() local index = 1 while true do + local skip = false 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 + local new_bytes, err = sock:receive(4) + if not new_bytes then + if err == "timeout" then + skip = true + else + irc_debug._warn(err) + break + end else - break + new_bytes = misc._int_to_str(new_bytes) + end + if not skip then + if new_bytes ~= bytes then + index = packet_size - bytes + new_bytes + 1 + else + break + end end end - if bytes >= size then break end coroutine.yield(true) end + debug_dcc("File completely sent") file:close() sock:close() irc._unregister_socket(sock, 'w') @@ -66,17 +86,17 @@ end -- that we can send data on -- @param ssock Server socket that the remote user connected to -- @param file Lua file object corresponding to the file we want to send --- @param size Size of the file to send -- @param packet_size Size of the packets to send the file in -local function handle_connect(ssock, file, size, packet_size) +local function handle_connect(ssock, file, packet_size) + debug_dcc("Offer accepted, beginning to send") 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) + coroutine.wrap(function(s) + return send_file(s, file, packet_size) end)) return true end @@ -87,9 +107,8 @@ end -- Accepts a file from a remote user which has offered it to us. -- @param sock Socket to receive the file on -- @param file Lua file object corresponding to the file we want to save --- @param size Size of the file we are receiving -- @param packet_size Size of the packets to receive the file in -local function accept_file(sock, file, size, packet_size) +local function accept_file(sock, file, packet_size) local bytes = 0 while true do local packet, err, partial_packet = sock:receive(packet_size) @@ -97,10 +116,11 @@ local function accept_file(sock, file, size, packet_size) if not packet then break end if packet:len() == 0 then break end bytes = bytes + packet:len() - sock:send(misc.str_to_int(bytes)) + sock:send(misc._str_to_int(bytes)) file:write(packet) coroutine.yield(true) end + debug_dcc("File completely received") file:close() sock:close() irc._unregister_socket(sock, 'r') @@ -109,6 +129,31 @@ end -- }}} -- }}} +-- internal functions {{{ +-- _accept {{{ +-- +-- Accepts a file offer from a remote user. Called when the on_dcc callback +-- retuns true. +-- @param filename Name to save the file as +-- @param address IP address of the remote user in low level int form +-- @param port Port to connect to at the remote user +-- @param packet_size Size of the packets the remote user will be sending +function _accept(filename, address, port, packet_size) + debug_dcc("Accepting a DCC SEND request from " .. + misc._ip_int_to_str(address) .. ":" .. port) + 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(s) + return accept_file(s, file, packet_size) + end)) +end +-- }}} +-- }}} + -- public functions {{{ -- send {{{ --- @@ -120,48 +165,27 @@ end -- above) function send(nick, filename, port) port = port or FIRST_PORT - local sock = base.assert(socket.tcp()) + local sock repeat + sock = base.assert(socket.tcp()) 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 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) + coroutine.wrap(function(s) + return handle_connect(s, file) end)) - filename = misc.basename(filename) + filename = misc._basename(filename) if filename:find(" ") then filename = '"' .. filename .. '"' end - irc.send("PRIVMSG", nick, {"DCC SEND " .. filename .. " " .. - ip .. " " .. port - 1 .. " " .. size}) -end --- }}} - --- accept {{{ --- TODO: this shouldn't be a public function --- --- Accepts a file offer from a remote user. Called when the on_dcc callback --- retuns true. --- @param filename Name to save the file as --- @param address IP address of the remote user --- @param port Port to connect to at the remote user --- @param size Size of the file that the remote user is offering --- @param packet_size Size of the packets the remote user will be sending -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)) + debug_dcc("Offering " .. filename .. " to " .. nick .. " from " .. + irc.get_ip() .. ":" .. port - 1) + irc.send("PRIVMSG", nick, c("DCC", "SEND", filename, ip, port - 1, size)) end -- }}} -- }}}