aboutsummaryrefslogtreecommitdiff
path: root/src/tp.lua
diff options
context:
space:
mode:
Diffstat (limited to 'src/tp.lua')
-rw-r--r--src/tp.lua111
1 files changed, 111 insertions, 0 deletions
diff --git a/src/tp.lua b/src/tp.lua
new file mode 100644
index 0000000..d8dabc0
--- /dev/null
+++ b/src/tp.lua
@@ -0,0 +1,111 @@
1-----------------------------------------------------------------------------
2-- Unified SMTP/FTP subsystem
3-- LuaSocket toolkit.
4-- Author: Diego Nehab
5-- Conforming to: RFC 2616, LTN7
6-- RCS ID: $Id$
7-----------------------------------------------------------------------------
8-- make sure LuaSocket is loaded
9if not LUASOCKET_LIBNAME then error('module requires LuaSocket') end
10-- get LuaSocket namespace
11local socket = _G[LUASOCKET_LIBNAME]
12if not socket then error('module requires LuaSocket') end
13-- create namespace inside LuaSocket namespace
14socket.tp = socket.tp or {}
15-- make all module globals fall into namespace
16setmetatable(socket.tp, { __index = _G })
17setfenv(1, socket.tp)
18
19TIMEOUT = 60
20
21-- tries to get a pattern from the server and closes socket on error
22local function try_receiving(sock, pattern)
23 local data, message = sock:receive(pattern)
24 if not data then sock:close() end
25 return data, message
26end
27
28-- tries to send data to server and closes socket on error
29local function try_sending(sock, data)
30 local sent, message = sock:send(data)
31 if not sent then sock:close() end
32 return sent, message
33end
34
35-- gets server reply
36local function get_reply(sock)
37 local code, current, separator, _
38 local line, message = try_receiving(sock)
39 local reply = line
40 if message then return nil, message end
41 _, _, code, separator = string.find(line, "^(%d%d%d)(.?)")
42 if not code then return nil, "invalid server reply" end
43 if separator == "-" then -- reply is multiline
44 repeat
45 line, message = try_receiving(sock)
46 if message then return nil, message end
47 _,_, current, separator = string.find(line, "^(%d%d%d)(.)")
48 if not current or not separator then
49 return nil, "invalid server reply"
50 end
51 reply = reply .. "\n" .. line
52 -- reply ends with same code
53 until code == current and separator == " "
54 end
55 return code, reply
56end
57
58-- metatable for sock object
59local metatable = { __index = {} }
60
61-- execute the "check" instr
62function metatable.__index:check(ok)
63 local code, reply = get_reply(self.sock)
64 if not code then return nil, reply end
65 if type(ok) ~= "function" then
66 if type(ok) ~= "table" then ok = {ok} end
67 for i, v in ipairs(ok) do
68 if string.find(code, v) then return code, reply end
69 end
70 return nil, reply
71 else return ok(code, reply) end
72end
73
74function metatable.__index:cmdchk(cmd, arg, ok)
75 local code, err = self:command(cmd, arg)
76 if not code then return nil, err end
77 return self:check(ok)
78end
79
80-- execute the "command" instr
81function metatable.__index:command(cmd, arg)
82 if arg then return try_sending(self.sock, cmd .. " " .. arg.. "\r\n")
83 return try_sending(self.sock, cmd .. "\r\n") end
84end
85
86function metatable.__index:sink(snk, pat)
87 local chunk, err = sock:receive(pat)
88 return snk(chunk, err)
89end
90
91function metatable.__index:source(src, instr)
92 while true do
93 local chunk, err = src()
94 if not chunk then return not err, err end
95 local ret, err = try_sending(self.sock, chunk)
96 if not ret then return nil, err end
97 end
98end
99
100-- closes the underlying sock
101function metatable.__index:close()
102 self.sock:close()
103end
104
105-- connect with server and return sock object
106function connect(host, port)
107 local sock, err = socket.connect(host, port)
108 if not sock then return nil, message end
109 sock:settimeout(TIMEOUT)
110 return setmetatable({sock = sock}, metatable)
111end