1 -----------------------------------------------------------------------------
2 -- Unified SMTP/FTP subsystem
5 -----------------------------------------------------------------------------
7 -----------------------------------------------------------------------------
8 -- Declare module and import dependencies
9 -----------------------------------------------------------------------------
11 local string = require("string")
12 local socket
= require("socket")
13 local ltn12
= require("ltn12")
16 -----------------------------------------------------------------------------
18 -----------------------------------------------------------------------------
21 -----------------------------------------------------------------------------
23 -----------------------------------------------------------------------------
24 -- gets server reply (works for SMTP and FTP)
25 local function get_reply(c
)
26 local code
, current
, sep
27 local line
, err
= c
:receive()
29 if err
then return nil, err
end
30 code
, sep
= socket
.skip(2, string.find(line
, "^(%d%d%d)(.?)"))
31 if not code
then return nil, "invalid server reply" end
32 if sep
== "-" then -- reply is multiline
34 line
, err
= c
:receive()
35 if err
then return nil, err
end
36 current
, sep
= socket
.skip(2, string.find(line
, "^(%d%d%d)(.?)"))
37 reply
= reply
.. "\n" .. line
38 -- reply ends with same code
39 until code
== current
and sep
== " "
44 -- metatable for sock object
45 local metat
= { __index
= {} }
47 function metat
.__index
:check(ok
)
48 local code
, reply
= get_reply(self
.c
)
49 if not code
then return nil, reply
end
50 if base
.type(ok
) ~= "function" then
51 if base
.type(ok
) == "table" then
52 for i
, v
in base
.ipairs(ok
) do
53 if string.find(code
, v
) then
54 return base
.tonumber(code
), reply
59 if string.find(code
, ok
) then return base
.tonumber(code
), reply
60 else return nil, reply
end
62 else return ok(base
.tonumber(code
), reply
) end
65 function metat
.__index
:command(cmd
, arg
)
66 cmd
= string.upper(cmd
)
68 return self
.c
:send(cmd
.. " " .. arg
.. "\r\n")
70 return self
.c
:send(cmd
.. "\r\n")
74 function metat
.__index
:sink(snk
, pat
)
75 local chunk
, err
= c
:receive(pat
)
76 return snk(chunk
, err
)
79 function metat
.__index
:send(data
)
80 return self
.c
:send(data
)
83 function metat
.__index
:receive(pat
)
84 return self
.c
:receive(pat
)
87 function metat
.__index
:getfd()
91 function metat
.__index
:dirty()
95 function metat
.__index
:getcontrol()
99 function metat
.__index
:source(source
, step
)
100 local sink
= socket
.sink("keep-open", self
.c
)
101 local ret
, err
= ltn12
.pump
.all(source
, sink
, step
or ltn12
.pump
.step
)
105 -- closes the underlying c
106 function metat
.__index
:close()
111 -- connect with server and return c object
112 function connect(host
, port
, timeout
, create
)
113 local c
, e
= (create
or socket
.tcp
)()
114 if not c
then return nil, e
end
115 c
:settimeout(timeout
or TIMEOUT
)
116 local r
, e
= c
:connect(host
, port
)
121 return base
.setmetatable({c
= c
}, metat
)