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
        
    