| ----------------------------------------------------------------------------- |
| -- Unified SMTP/FTP subsystem |
| -- LuaSocket toolkit. |
| -- Author: Diego Nehab |
| -- RCS ID: $Id: tp.lua,v 1.22 2006/03/14 09:04:15 diego Exp $ |
| ----------------------------------------------------------------------------- |
| |
| ----------------------------------------------------------------------------- |
| -- Declare module and import dependencies |
| ----------------------------------------------------------------------------- |
| local base = _G |
| local string = require("string") |
| local socket = require("socket") |
| local ltn12 = require("ltn12") |
| module("socket.tp") |
| |
| ----------------------------------------------------------------------------- |
| -- Program constants |
| ----------------------------------------------------------------------------- |
| TIMEOUT = 60 |
| |
| ----------------------------------------------------------------------------- |
| -- Implementation |
| ----------------------------------------------------------------------------- |
| -- gets server reply (works for SMTP and FTP) |
| local function get_reply(c) |
| local code, current, sep |
| local line, err = c:receive() |
| local reply = line |
| if err then return nil, err end |
| code, sep = socket.skip(2, string.find(line, "^(%d%d%d)(.?)")) |
| if not code then return nil, "invalid server reply" end |
| if sep == "-" then -- reply is multiline |
| repeat |
| line, err = c:receive() |
| if err then return nil, err end |
| current, sep = socket.skip(2, string.find(line, "^(%d%d%d)(.?)")) |
| reply = reply .. "\n" .. line |
| -- reply ends with same code |
| until code == current and sep == " " |
| end |
| return code, reply |
| end |
| |
| -- metatable for sock object |
| local metat = { __index = {} } |
| |
| function metat.__index:check(ok) |
| local code, reply = get_reply(self.c) |
| if not code then return nil, reply end |
| if base.type(ok) ~= "function" then |
| if base.type(ok) == "table" then |
| for i, v in base.ipairs(ok) do |
| if string.find(code, v) then |
| return base.tonumber(code), reply |
| end |
| end |
| return nil, reply |
| else |
| if string.find(code, ok) then return base.tonumber(code), reply |
| else return nil, reply end |
| end |
| else return ok(base.tonumber(code), reply) end |
| end |
| |
| function metat.__index:command(cmd, arg) |
| if arg then |
| return self.c:send(cmd .. " " .. arg.. "\r\n") |
| else |
| return self.c:send(cmd .. "\r\n") |
| end |
| end |
| |
| function metat.__index:sink(snk, pat) |
| local chunk, err = c:receive(pat) |
| return snk(chunk, err) |
| end |
| |
| function metat.__index:send(data) |
| return self.c:send(data) |
| end |
| |
| function metat.__index:receive(pat) |
| return self.c:receive(pat) |
| end |
| |
| function metat.__index:getfd() |
| return self.c:getfd() |
| end |
| |
| function metat.__index:dirty() |
| return self.c:dirty() |
| end |
| |
| function metat.__index:getcontrol() |
| return self.c |
| end |
| |
| function metat.__index:source(source, step) |
| local sink = socket.sink("keep-open", self.c) |
| local ret, err = ltn12.pump.all(source, sink, step or ltn12.pump.step) |
| return ret, err |
| end |
| |
| -- closes the underlying c |
| function metat.__index:close() |
| self.c:close() |
| return 1 |
| end |
| |
| -- connect with server and return c object |
| function connect(host, port, timeout, create) |
| local c, e = (create or socket.tcp)() |
| if not c then return nil, e end |
| c:settimeout(timeout or TIMEOUT) |
| local r, e = c:connect(host, port) |
| if not r then |
| c:close() |
| return nil, e |
| end |
| return base.setmetatable({c = c}, metat) |
| end |
| |