luasocket/src/tp.lua

112 lines
3.5 KiB
Lua
Raw Normal View History

-----------------------------------------------------------------------------
-- Unified SMTP/FTP subsystem
-- LuaSocket toolkit.
-- Author: Diego Nehab
-- Conforming to: RFC 2616, LTN7
-- RCS ID: $Id$
-----------------------------------------------------------------------------
-- make sure LuaSocket is loaded
if not LUASOCKET_LIBNAME then error('module requires LuaSocket') end
-- get LuaSocket namespace
local socket = _G[LUASOCKET_LIBNAME]
if not socket then error('module requires LuaSocket') end
-- create namespace inside LuaSocket namespace
socket.tp = socket.tp or {}
-- make all module globals fall into namespace
setmetatable(socket.tp, { __index = _G })
setfenv(1, socket.tp)
TIMEOUT = 60
-- tries to get a pattern from the server and closes socket on error
local function try_receiving(sock, pattern)
local data, message = sock:receive(pattern)
if not data then sock:close() end
return data, message
end
-- tries to send data to server and closes socket on error
local function try_sending(sock, data)
local sent, message = sock:send(data)
if not sent then sock:close() end
return sent, message
end
-- gets server reply
local function get_reply(sock)
local code, current, separator, _
local line, message = try_receiving(sock)
local reply = line
if message then return nil, message end
_, _, code, separator = string.find(line, "^(%d%d%d)(.?)")
if not code then return nil, "invalid server reply" end
if separator == "-" then -- reply is multiline
repeat
line, message = try_receiving(sock)
if message then return nil, message end
_,_, current, separator = string.find(line, "^(%d%d%d)(.)")
if not current or not separator then
return nil, "invalid server reply"
end
reply = reply .. "\n" .. line
-- reply ends with same code
until code == current and separator == " "
end
return code, reply
end
-- metatable for sock object
local metatable = { __index = {} }
-- execute the "check" instr
function metatable.__index:check(ok)
local code, reply = get_reply(self.sock)
if not code then return nil, reply end
if type(ok) ~= "function" then
if type(ok) ~= "table" then ok = {ok} end
for i, v in ipairs(ok) do
if string.find(code, v) then return code, reply end
end
return nil, reply
else return ok(code, reply) end
end
function metatable.__index:cmdchk(cmd, arg, ok)
local code, err = self:command(cmd, arg)
if not code then return nil, err end
return self:check(ok)
end
-- execute the "command" instr
function metatable.__index:command(cmd, arg)
if arg then return try_sending(self.sock, cmd .. " " .. arg.. "\r\n")
return try_sending(self.sock, cmd .. "\r\n") end
end
function metatable.__index:sink(snk, pat)
local chunk, err = sock:receive(pat)
return snk(chunk, err)
end
function metatable.__index:source(src, instr)
while true do
local chunk, err = src()
if not chunk then return not err, err end
local ret, err = try_sending(self.sock, chunk)
if not ret then return nil, err end
end
end
-- closes the underlying sock
function metatable.__index:close()
self.sock:close()
end
-- connect with server and return sock object
function connect(host, port)
local sock, err = socket.connect(host, port)
if not sock then return nil, message end
sock:settimeout(TIMEOUT)
return setmetatable({sock = sock}, metatable)
end