128 lines
		
	
	
		
			3.6 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
		
		
			
		
	
	
			128 lines
		
	
	
		
			3.6 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
|  | R"-++**++-(
 | ||
|  | ----------------------------------------------------------------------------- | ||
|  | -- Unified SMTP/FTP subsystem | ||
|  | -- LuaSocket toolkit. | ||
|  | -- Author: Diego Nehab | ||
|  | ----------------------------------------------------------------------------- | ||
|  | 
 | ||
|  | ----------------------------------------------------------------------------- | ||
|  | -- Declare module and import dependencies | ||
|  | ----------------------------------------------------------------------------- | ||
|  | local base = _G | ||
|  | local string = require("string") | ||
|  | local socket = require("socket") | ||
|  | local ltn12 = require("ltn12") | ||
|  | 
 | ||
|  | socket.tp = {} | ||
|  | local _M = socket.tp | ||
|  | 
 | ||
|  | ----------------------------------------------------------------------------- | ||
|  | -- Program constants | ||
|  | ----------------------------------------------------------------------------- | ||
|  | _M.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) | ||
|  |     cmd = string.upper(cmd) | ||
|  |     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 _M.connect(host, port, timeout, create) | ||
|  |     local c, e = (create or socket.tcp)() | ||
|  |     if not c then return nil, e end | ||
|  |     c:settimeout(timeout or _M.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 | ||
|  | 
 | ||
|  | return _M | ||
|  | )-++**++-";
 |