mime.lua
Verified Featured
Added by iNTERFACEWARE
Sends MIME-encoded email attachments using the SMTP protocol. A wrapper around net.smtp.send.
Source Code
-- The mime module -- Copyright (c) 2011-2015 iNTERFACEWARE Inc. ALL RIGHTS RESERVED -- iNTERFACEWARE permits you to use, modify, and distribute this file in -- accordance with the terms of the iNTERFACEWARE license agreement -- accompanying the software in which it is used. -- Basic SMTP/MIME module for sending MIME formatted attachments via -- SMTP. -- -- An attempt is made to format the MIME parts with the correct headers, -- and pathnames that represent non-plain-text data are Base64 encoded -- when constructing the part for that attachment. -- -- SMTP/MIME is a large and complicated standard; only part of those -- standards are supported here. The assumption is that most mailers -- and mail transfer agents will do their best to handle inconsistencies. -- -- Example usage: -- -- local Results = mime.send{ -- server='smtp://mysmtp.com:25', username='john', password='password', -- from='john@smith.com', to={'john@smith.com', 'jane@smith.com'}, -- header={['Subject']='Test Subject'}, body='Test Email Body', use_ssl='try', -- attachments={'/home/jsmith/pictures/test.jpeg'}, -- } local mime = {} -- Common file extensions and the corresponding MIME sub-type we will probably encounter. Add more as necessary. local MIMEtypes = { ['pdf'] = 'application/pdf', ['jpeg'] = 'image/jpeg', ['jpg'] = 'image/jpeg', ['gif'] = 'image/gif', ['png'] = 'image/png', ['zip'] = 'application/zip', ['gzip'] = 'application/gzip', ['tiff'] = 'image/tiff', ['html'] = 'text/html', ['htm'] = 'text/html', ['mpeg'] = 'video/mpeg', ['mp4'] = 'video/mp4', ['txt'] = 'text/plain', ['exe'] = 'application/plain', ['js'] = 'application/javascript', } local function getContentType(FileExtension) return MIMEtypes[FileExtension] or 'application/unknown' end -- Most mailers support UTF-8 local defaultCharset = 'utf8' -- Read the passed in filespec into a local variable. function mime.readFile(Filename) local F = assert(io.open(Filename, "rb")) local Data = F:read("*a") F:close() return Data end -- Base64 encode the content passed in. Break the encoded data into reasonable lengths per RFC2821 and friends. -- Most mail clients can handle about 990-1000 - Maxline can be reduced down to 72 for debugging. local function Base64Encode(Content) local Lines = {''} local Encoded = filter.base64.enc(Content) local Maxl = 990 - 2 -- Less 2 for the trailing CRLF pair local Len = #Encoded local Start = 1 local Lineend = Start + Maxl while Start < Len do Lines[#Lines+1] = Encoded:sub(Start,Start+Maxl) Start = Start + Maxl + 1 end trace(Lines) return table.concat(Lines, '\r\n') end local function AddAttachments(FileList, partBoundary) local Body = '' for _, FilePath in ipairs(FileList) do local path, Filename, Extension = FilePath:match("(.-)([^\\/]-%.?([^%.\\/]*))$") -- Get the (best guess) content-type and file contents. -- Cook the contents into Base64 if necessary. local contentType = getContentType(Extension) local Content = mime.readFile(FilePath) local isBinary = Content:find("[^\f\n\r\t\032-\128]"); if isBinary then Content = Base64Encode(Content) end local Charset = isBinary and 'B' or defaultCharset -- We could use "quoted-printable" to make sure we handle -- occasional non-7-bit text data, but then we'd have to break -- the passed-in data into max 76 char lines. We don't really -- want to munge the original data that much. Defaulting to -- 7bit should work in most cases, and supporting quoted-printable -- makes things pretty complicated (and increases the message -- size even more.) local ContentTransferEncoding = isBinary and 'Content-Transfer-Encoding: base64' or '' -- Concatenate the current chunk onto the entire body. Body = Body..'\r\n\r\n' ..partBoundary..'\r\n' ..'Content-Type: '..contentType..'; charset="'..Charset..'"; name="'..Filename..'"\r\n' ..'Content-ID: <'..Filename..'>\r\n' ..'Content-Disposition: attachment; filename="'..Filename..'"\r\n' ..ContentTransferEncoding..'\r\n' ..Content..'\r\n' end return Body end -- Similar to net.smtp.send with a single additional required parameter -- of an array of local absolute filenames to add to the message -- body as attachments. -- -- An attempt is made to add the attachment parts with the right -- MIME-related headers. function mime.send(args) local server = args.server local to = args.to local from = args.from local header = args.header local body = args.body local attachments = args.attachments local username = args.username local password = args.password local timeout = args.timeout local use_ssl = args.use_ssl local live = args.live local debug = args.debug local entity_type = args.entity_type or 'text/plain' -- Blanket non-optional parameter enforcement. if server == nil or to == nil or from == nil or header == nil or body == nil or attachments == nil then error("Missing required parameter.", 2) end header['Date'] = os.ts.date("!%a, %d %b %Y %H:%M:%S GMT"); -- Create a unique ID to use for multi-part boundaries. local boundaryID = util.guid(128) if debug then -- debug hook boundaryID = 'xyzzy_0123456789_xyzzy' end local partBoundary = '--' .. boundaryID local endBoundary = '--' .. boundaryID .. '--' -- Append our headers, set up the multi-part message. header['MIME-Version'] = '1.0' header['Content-Type'] = 'multipart/mixed; boundary=' .. boundaryID -- Preload the body part. local msgBody = partBoundary..'\r\n' ..'Content-Type: '..entity_type..'; charset="'..defaultCharset..'"\r\n' ..'\r\n'..body msgBody = msgBody..AddAttachments(attachments, partBoundary) msgBody = msgBody..'\r\n'..endBoundary trace(msgBody) -- Send the message via net.smtp.send() net.smtp.send{ server = server, to = to, from = from, header = header, body = msgBody, username = username, password = password, timeout = timeout, use_ssl = use_ssl, live = live, debug = debug } -- Debug hook if debug then return msgBody, header end end local mimehelp = { Title="mime.send"; Usage="mime.send{server=<value> [, username=<value>] [, ...]}", SummaryLine="Sends an email using the SMTP protocol.", Desc=[[Sends an email using the SMTP protocol. A wrapper around net.smtp.send. Accepts the same parameters as net.smtp.send, with an additional "attachments" parameter. ]]; ["Returns"] = { {Desc="Normally nothing. If the debug flag is set to true then the email body and email header is returned <u>string</u>."}, }; ParameterTable= true, Parameters= { {attachments= {Desc='A table of absolute filenames to be attached to the email <u>table</u>.'}}, }; Examples={ [[local Results = mime.send{ server='smtp://mysmtp.com:25', username='john', password='password', from='john@smith.com', to={'john@smith.com', 'jane@smith.com'}, header={['Subject']='Test Subject'}, body='Test Email Body', use_ssl='try', attachments={'/home/jsmith/pictures/test.jpeg'}, }]], }; SeeAlso={ { Title="net.smtp - in our code repository", Link="http://help.interfaceware.com/api/#net_smtp_send" }, { Title="Mime email client ", Link="http://help.interfaceware.com/category/building-interfaces/repositories/builtin-iguanatools" } } } help.set{input_function=mime.send, help_data=mimehelp} return mime
Description
Sends MIME-encoded email attachments using the SMTP protocol. A wrapper around net.smtp.send.
Usage Details
The mime.lua module contains a function mime.send()
that sends MIME-encoded email attachments using the SMTP protocol. The mime.send() is a wrapper around net.smtp.send()
with an added “attachments” parameter.
How to use mime.lua:
- Call mime.send() using your smtp parameters
Here is sample code for main():
local mime = require 'mime' function main() -- Set up the parameters to be used with sending an email local smtpparams={ header = {To = 'sales@interfaceware.com'; From = '<your email name>'; Date = 'Thu, 23 Aug 2001 21:27:04 -0400'; Subject = 'Email from Iguana';}, username = '<your mail username>', password = '<your mail password>', server = '<your smtp mail server name>', -- note that the "to" param is actually what is used to send the email, -- the entries in header are only used for display. -- For instance, to do Bcc, add the address in the 'to' parameter but -- omit it from the header. to = {'sales@interfaceware.com','admin@interfaceware.com'}, from = 'Test User ', body = 'This is the test body of the email', use_ssl = 'try', attachments = {'test.txt'}, -- attach "test.txt" located in the Iguana install directory --live = true -- uncomment to run in the editor } mime.send(smtpparams) end
More Information