135 lines
		
	
	
		
			3.7 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
		
		
			
		
	
	
			135 lines
		
	
	
		
			3.7 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
| 
								 | 
							
								-----------------------------------------------------------------------------
							 | 
						||
| 
								 | 
							
								-- 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:getpeername()
							 | 
						||
| 
								 | 
							
								    return self.c:getpeername()
							 | 
						||
| 
								 | 
							
								end
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function metat.__index:getsockname()
							 | 
						||
| 
								 | 
							
								    return self.c:getpeername()
							 | 
						||
| 
								 | 
							
								end
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								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 = self.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
							 |