Submit
Path:
~
/
/
usr
/
share
/
lua
/
5.4
/
socket
/
File Content:
smtp.lua
----------------------------------------------------------------------------- -- SMTP client support for the Lua language. -- LuaSocket toolkit. -- Author: Diego Nehab ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- -- Declare module and import dependencies ----------------------------------------------------------------------------- local base = _G local coroutine = require("coroutine") local string = require("string") local math = require("math") local os = require("os") local socket = require("socket") local tp = require("socket.tp") local ltn12 = require("ltn12") local headers = require("socket.headers") local mime = require("mime") socket.smtp = {} local _M = socket.smtp ----------------------------------------------------------------------------- -- Program constants ----------------------------------------------------------------------------- -- timeout for connection _M.TIMEOUT = 60 -- default server used to send e-mails _M.SERVER = "localhost" -- default port _M.PORT = 25 -- domain used in HELO command and default sendmail -- If we are under a CGI, try to get from environment _M.DOMAIN = os.getenv("SERVER_NAME") or "localhost" -- default time zone (means we don't know) _M.ZONE = "-0000" --------------------------------------------------------------------------- -- Low level SMTP API ----------------------------------------------------------------------------- local metat = { __index = {} } function metat.__index:greet(domain) self.try(self.tp:check("2..")) self.try(self.tp:command("EHLO", domain or _M.DOMAIN)) return socket.skip(1, self.try(self.tp:check("2.."))) end function metat.__index:mail(from) self.try(self.tp:command("MAIL", "FROM:" .. from)) return self.try(self.tp:check("2..")) end function metat.__index:rcpt(to) self.try(self.tp:command("RCPT", "TO:" .. to)) return self.try(self.tp:check("2..")) end function metat.__index:data(src, step) self.try(self.tp:command("DATA")) self.try(self.tp:check("3..")) self.try(self.tp:source(src, step)) self.try(self.tp:send("\r\n.\r\n")) return self.try(self.tp:check("2..")) end function metat.__index:quit() self.try(self.tp:command("QUIT")) return self.try(self.tp:check("2..")) end function metat.__index:close() return self.tp:close() end function metat.__index:login(user, password) self.try(self.tp:command("AUTH", "LOGIN")) self.try(self.tp:check("3..")) self.try(self.tp:send(mime.b64(user) .. "\r\n")) self.try(self.tp:check("3..")) self.try(self.tp:send(mime.b64(password) .. "\r\n")) return self.try(self.tp:check("2..")) end function metat.__index:plain(user, password) local auth = "PLAIN " .. mime.b64("\0" .. user .. "\0" .. password) self.try(self.tp:command("AUTH", auth)) return self.try(self.tp:check("2..")) end function metat.__index:auth(user, password, ext) if not user or not password then return 1 end if string.find(ext, "AUTH[^\n]+LOGIN") then return self:login(user, password) elseif string.find(ext, "AUTH[^\n]+PLAIN") then return self:plain(user, password) else self.try(nil, "authentication not supported") end end -- send message or throw an exception function metat.__index:send(mailt) self:mail(mailt.from) if base.type(mailt.rcpt) == "table" then for i,v in base.ipairs(mailt.rcpt) do self:rcpt(v) end else self:rcpt(mailt.rcpt) end self:data(ltn12.source.chain(mailt.source, mime.stuff()), mailt.step) end function _M.open(server, port, create) local tp = socket.try(tp.connect(server or _M.SERVER, port or _M.PORT, _M.TIMEOUT, create)) local s = base.setmetatable({tp = tp}, metat) -- make sure tp is closed if we get an exception s.try = socket.newtry(function() s:close() end) return s end -- convert headers to lowercase local function lower_headers(headers) local lower = {} for i,v in base.pairs(headers or lower) do lower[string.lower(i)] = v end return lower end --------------------------------------------------------------------------- -- Multipart message source ----------------------------------------------------------------------------- -- returns a hopefully unique mime boundary local seqno = 0 local function newboundary() seqno = seqno + 1 return string.format('%s%05d==%05u', os.date('%d%m%Y%H%M%S'), math.random(0, 99999), seqno) end -- send_message forward declaration local send_message -- yield the headers all at once, it's faster local function send_headers(tosend) local canonic = headers.canonic local h = "\r\n" for f,v in base.pairs(tosend) do h = (canonic[f] or f) .. ': ' .. v .. "\r\n" .. h end coroutine.yield(h) end -- yield multipart message body from a multipart message table local function send_multipart(mesgt) -- make sure we have our boundary and send headers local bd = newboundary() local headers = lower_headers(mesgt.headers or {}) headers['content-type'] = headers['content-type'] or 'multipart/mixed' headers['content-type'] = headers['content-type'] .. '; boundary="' .. bd .. '"' send_headers(headers) -- send preamble if mesgt.body.preamble then coroutine.yield(mesgt.body.preamble) coroutine.yield("\r\n") end -- send each part separated by a boundary for i, m in base.ipairs(mesgt.body) do coroutine.yield("\r\n--" .. bd .. "\r\n") send_message(m) end -- send last boundary coroutine.yield("\r\n--" .. bd .. "--\r\n\r\n") -- send epilogue if mesgt.body.epilogue then coroutine.yield(mesgt.body.epilogue) coroutine.yield("\r\n") end end -- yield message body from a source local function send_source(mesgt) -- make sure we have a content-type local headers = lower_headers(mesgt.headers or {}) headers['content-type'] = headers['content-type'] or 'text/plain; charset="iso-8859-1"' send_headers(headers) -- send body from source while true do local chunk, err = mesgt.body() if err then coroutine.yield(nil, err) elseif chunk then coroutine.yield(chunk) else break end end end -- yield message body from a string local function send_string(mesgt) -- make sure we have a content-type local headers = lower_headers(mesgt.headers or {}) headers['content-type'] = headers['content-type'] or 'text/plain; charset="iso-8859-1"' send_headers(headers) -- send body from string coroutine.yield(mesgt.body) end -- message source function send_message(mesgt) if base.type(mesgt.body) == "table" then send_multipart(mesgt) elseif base.type(mesgt.body) == "function" then send_source(mesgt) else send_string(mesgt) end end -- set defaul headers local function adjust_headers(mesgt) local lower = lower_headers(mesgt.headers) lower["date"] = lower["date"] or os.date("!%a, %d %b %Y %H:%M:%S ") .. (mesgt.zone or _M.ZONE) lower["x-mailer"] = lower["x-mailer"] or socket._VERSION -- this can't be overriden lower["mime-version"] = "1.0" return lower end function _M.message(mesgt) mesgt.headers = adjust_headers(mesgt) -- create and return message source local co = coroutine.create(function() send_message(mesgt) end) return function() local ret, a, b = coroutine.resume(co) if ret then return a, b else return nil, a end end end --------------------------------------------------------------------------- -- High level SMTP API ----------------------------------------------------------------------------- _M.send = socket.protect(function(mailt) local s = _M.open(mailt.server, mailt.port, mailt.create) local ext = s:greet(mailt.domain) s:auth(mailt.user, mailt.password, ext) s:send(mailt) s:quit() return s:close() end) return _M
Submit
FILE
FOLDER
Name
Size
Permission
Action
ftp.lua
10606 bytes
0644
headers.lua
3698 bytes
0644
http.lua
14659 bytes
0644
smtp.lua
8074 bytes
0644
tp.lua
3766 bytes
0644
url.lua
11669 bytes
0644
N4ST4R_ID | Naxtarrr