aboutsummaryrefslogtreecommitdiffstats
path: root/src/irc/dcc.lua
blob: f227d4b15736c7918a460d7ce6f27a53e738dc86 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
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
-- }}}
-- }}}