Project

General

Profile

Email Send

Starting in FWD trunk revision 11187, FWD provides a handle-based resource that can be easily used for sending emails via SMTP. This revision was released as FWD v3.2 in August 2018. The original task for developing this feature was #3343. It has useful information about the implementation as well as details of mock/test environments.

Introduction

Included with FWD is a set of Java classes that make it easy to send emails via SMTP. The following features are available:

  • Connection via 4 possible modes: UNENCRYPTED, STARTTLS_WHEN_AVAILABLE, STARTTLS_REQUIRED or SSL.
  • Specify the SMTP host (IP address or hostname) and optionally the port.
  • No authentication or authentication with userid/password.
  • Main contents of the email can be plain text, HTML or both.
  • Unlimited TO, CC and BCC recipients.
  • Control over FROM and REPLY-TO addresses.
  • Attach local files or files specified by URL.
  • Embed images in HTML using a local file or an image via URL.

The backing support for this is implemented using the Apache Commons Email project. This in turn uses the standard JavaMail API that is part of the J2EE platform. No extra installation of software is needed. The supporting libraries are automatically pulled in to the installation by the FWD build process.

The current implementation is done as a 4GL handle-based resource. This is a short term approach. When the OO 4GL support is available, it will be possible to directly instantiate and access Java objects. Using that facility, the handle approach will be deprecated (and eventually removed) leaving behind a cleaner OO approach (and no need for extra 4GL language syntax).

4GL API Details

This is an implementation of the SMTP-EMAIL extension to the 4GL. The created object is a handle based resource with attributes and methods that can be used to define the email's state and to send the email. When the email is sent, the send will occur on the client.

This is a FWD-specific set of functionality. It does not exist and will not run in the Progress 4GL (e.g. OpenEdge).

To create an SMTP-EMAIL resource use the following syntax:

CREATE SMTP-EMAIL handle [ASSIGN { ATTR_OR_METHOD = value ...}].

The following list of attributes are available:

Attribute Data Type Required Description
connection-type character Y Must be "UNENCRYPTED", "STARTTLS_WHEN_AVAILABLE", "STARTTLS_REQUIRED" or "SSL".
smtp-from character Y This is the FROM address (the email address of the sender).
smtp-host character Y This is the SMTP server's IP address or hostname.
smtp-html character N If set, this is the content of the HTML body.
smtp-port character N Defaults to 25 for UNENCRYPTED, 587 for STARTTLS_* and 465 for SSL.
smtp-reply-to character N Optional REPLY-TO address.
smtp-subject character Y This is the subject line.
smtp-text character N If set, this is the content of the HTML body.
smtp-user character N If both the user and the password are NOT set, then no authentication will be attempted. If set, this is the userid for authentication with the SMTP server.
smtp-password character N If both the user and the password are NOT set, then no authentication will be attempted. If set, this is the password for authentication with the SMTP server.
smtp-validate logical N Defaults to TRUE. If FALSE, the SMTP server's SSL certificate will be treated as trusted. Ignored for UNENCRYPTED mode.

The following list of methods are available:

Method Return Type Return Meaning Required Description
add-to-address(character addr) logical TRUE on success. N This adds a TO address to the mail. At least one recipient is needed.
add-cc-address(character addr) logical TRUE on success. N This adds a CC address to the mail. At least one recipient is needed.
add-bcc-address(character addr) logical TRUE on success. N This adds a BCC address to the mail. At least one recipient is needed.
attach-file(character filename) logical TRUE on success. N This attaches a local file to the email. The file must exist and be accessible to the user.
attach-url(character url) logical TRUE on success. N This attaches a resource specified by URL to the email. The resource will be downloaded when the email is sent. The URL must exist and be accessible to the user.
clear-attachments-list() logical TRUE on success. N Deletes everything in the attachments list.
clear-bcc-list() logical TRUE on success. N Deletes everything in the BCC address list.
clear-cc-list() logical TRUE on success. N Deletes everything in the CC address list.
clear-embedded-image-list() logical TRUE on success. N Deletes everything in the embedded image list.
clear-to-list() logical TRUE on success. N Deletes everything in the TO address list.
embed-file(character filename) character CONTENT-ID N Adds a local file to the embedded image list and returns the content ID to be substituted into the IMG tag of the HTML body. The file must exist and be accessible to the user.
embed-url(character filename) character CONTENT-ID N Adds a URL resource to the embedded image list and returns the content ID to be substituted into the IMG tag of the HTML body. The resource will be downloaded when the email is sent. The URL must exist and be accessible to the user.
get-attachments-list() character[] The list as an array. N Returns the attachments list as an array.
get-bcc-list() character[] The list as an array. N Returns the BCC address list as an array.
get-cc-list() character[] The list as an array. N Returns the CC address list as an array.
get-embedded-image-list() character[] The list as an array. N Returns the embedded image list as an array.
get-to-list() character[] The list as an array. N Returns the TO address list as an array.
send() logical TRUE on success. Y Sends the email as previously defined. You can call this more than once (possibly changing the state of the handle each time). Every time this is called it will attempt to send the defined email. This may raise an ERROR condition if there are any problems during send.

At the end of lifetime of the object, it should be deleted, as usual with:

DELETE OBJECT handle.

Example 4GL Programs

All of these programs are designed for use in ChUI. They have not been implemented or testing in GUI.

Simple Text Email

This code allows sending a text-only email with no attachments.


def var c-type as character format "x(25)" 
   label "Connection Type" 
   view-as combo-box
      list-items "UNENCRYPTED", "STARTTLS_WHEN_AVAILABLE", "STARTTLS_REQUIRED", "SSL" 
      size-chars 25 by 5.

def var text-body as char
   view-as editor inner-chars 70 inner-lines 10 scrollbar-vertical.

def var host      as char format "x(65)"  init "localhost" label "Host".
def var port      as int  format "99999"  init ?           label "Port".
def var s-user    as char format "x(25)"  init ?           label "User".
def var pw        as char format "x(25)"  init ?           label "Password".
def var from-addr as char format "x(65)"                   label "From".
def var reply-to  as char format "x(65)"                   label "Reply-To".
def var to-addr   as char format "x(65)"                   label "To".
def var subject   as char format "x(65)"                   label "Subject".
def var c-valid   as log                  init true        label "Validate".

def var mailer as handle.
def var again  as log init true.

repeat while again:

   update c-type                                                                                       skip
          host            help "Type hostname or IP address."                                          skip
          port            help "Type port number or leave blank for default port."                     skip
          s-user          help "Userid for SMTP server authentication or leave blank for no auth."     skip
          pw              help "Password for SMTP server authentication or leave blank for no auth."   skip
          c-valid         help "Use 'no' to disable server certificate validation."                    skip
          from-addr                                                                                    skip
          reply-to                                                                                     skip
          to-addr                                                                                      skip
          subject                                                                                      skip
          text-body       help "Type the body of the email, to be sent as plain text."  no-label
          with frame compose side-labels title "Send a Text Email with No Attachments".

   create smtp-email mailer
      assign connection-type = c-type
             smtp-host       = host
             smtp-port       = string(port)
             smtp-user       = s-user 
             smtp-password   = pw
             smtp-from       = from-addr
             smtp-reply-to   = reply-to
             smtp-subject    = subject 
             smtp-text       = text-body
             smtp-validate   = c-valid.

   mailer:add-to-address(to-addr).
   mailer:send().

   again = false.
   message "Send another email?" update again format "Y/N".
end.

This is an screen shot of a possible result:

HTML Email

This allows sending an HTML email with any number of attachments. Please note that the HTML itself cannot have embedded images (in this sample, see the sample below for the embedded images support).


def var c-type as character format "x(25)" 
   label "Connection Type" 
   view-as combo-box
      list-items "UNENCRYPTED", "STARTTLS_WHEN_AVAILABLE", "STARTTLS_REQUIRED", "SSL" 
      size-chars 25 by 5.

def var text-body as char
   view-as editor inner-chars 70 inner-lines 10 scrollbar-vertical.

def var html-body as char
   view-as editor inner-chars 70 inner-lines 10 scrollbar-vertical.

def var host      as char format "x(65)"  init "localhost" label "Host".
def var port      as int  format "99999"  init ?           label "Port".
def var s-user    as char format "x(25)"  init ?           label "User".
def var pw        as char format "x(25)"  init ?           label "Password".
def var from-addr as char format "x(65)"                   label "From".
def var reply-to  as char format "x(65)"                   label "Reply-To".
def var to-addr   as char format "x(65)"                   label "To".
def var subject   as char format "x(65)"                   label "Subject".
def var c-valid   as log                  init true        label "Validate".

def var cc-list         as char extent 10  init ?.
def var bcc-list        as char extent 10  init ?.
def var att-file-list   as char extent 10  init ?.
def var att-url-list    as char extent 10  init ?.

def button cc-button             label "Add CC".
def button bcc-button            label "Add BCC".
def button attach-file-button    label "Attach File".
def button attach-url-button     label "Attach URL".

def var mailer as handle.
def var again  as log init true.

def frame compose
       c-type                                                                                       skip
       host            help "Type hostname or IP address."                                          skip
       port            help "Type port number or leave blank for default port."                     skip
       s-user          help "Userid for SMTP server authentication or leave blank for no auth."     skip
       pw              help "Password for SMTP server authentication or leave blank for no auth."   skip
       c-valid         help "Use 'no' to disable server certificate validation."                    skip
       from-addr                                                                                    skip
       reply-to                                                                                     skip
       to-addr                                                                                      skip
       subject                                                                                      skip
       text-body       help "Type the plain text body of the email."  no-label                      skip
       html-body       help "Type the HTML body of the email."        no-label                      skip
       cc-button         
       bcc-button        
       attach-file-button
       attach-url-button 
       with side-labels title "Send an HTML Email and Attachments".

function add-to-list returns integer (input txt as character, input-output list as character extent 10):

   return -1.
end.

on choose of cc-button
do:
   message "Add CC Address" update addr as char format "x(60)".

   def var i as int.

   do i = 1 to extent(cc-list):
      if cc-list[i] eq ? then
      do:
         cc-list[i] = addr.
         return.
      end.
   end.

   message "Only 10 CC addresses are supported at this time.".
end.

on choose of bcc-button
do:
   message "Add BCC Address" update addr as char format "x(60)".

   def var i as int.

   do i = 1 to extent(bcc-list):
      if bcc-list[i] eq ? then
      do:
         bcc-list[i] = addr.
         return.
      end.
   end.

   message "Only 10 BCC addresses are supported at this time.".
end.

on choose of attach-file-button
do:
   message "File to Attach" update file as char format "x(65)".
   file = search(file).
   if file eq ? then
      message "File does not exist!" view-as alert-box error.
   else
   do:
      def var i as int.

      do i = 1 to extent(att-file-list):
         if att-file-list[i] eq ? then
         do:
            att-file-list[i] = file.
            return.
         end.
      end.

      message "Only 10 file attachments are supported at this time.".
   end.
end.

on choose of attach-url-button
do:
   message "Resource URL to Attach" update url as char format "x(65)".

   def var i as int.

   do i = 1 to extent(att-url-list):
      if att-url-list[i] eq ? then
      do:
         att-url-list[i] = url.
         return.
      end.
   end.

   message "Only 10 URL attachments are supported at this time.".
end.

def var i as int.

repeat while again:

   display c-type
           host
           port
           s-user
           pw
           c-valid
           from-addr
           reply-to
           to-addr
           subject
           text-body
           html-body
           with frame compose.

   enable all with frame compose.
   wait-for 'go' of frame compose.

   assign c-type
          host
          port
          s-user
          pw
          c-valid
          from-addr
          reply-to
          to-addr
          subject
          text-body
          html-body.

   create smtp-email mailer
      assign connection-type = c-type
             smtp-host       = host
             smtp-port       = string(port)
             smtp-user       = s-user 
             smtp-password   = pw
             smtp-from       = from-addr
             smtp-reply-to   = reply-to
             smtp-subject    = subject 
             smtp-text       = text-body
             smtp-html       = html-body
             smtp-validate   = c-valid.

   mailer:add-to-address(to-addr).

   do i = 1 to extent(cc-list):
      if cc-list[i] ne ? then
         mailer:add-cc-address(cc-list[i]).
   end.
   do i = 1 to extent(bcc-list):
      if bcc-list[i] ne ? then
         mailer:add-bcc-address(bcc-list[i]).
   end.
   do i = 1 to extent(att-file-list):
      if att-file-list[i] ne ? then
         mailer:attach-file(att-file-list[i]).
   end.
   do i = 1 to extent(att-url-list):
      if att-url-list[i] ne ? then
         mailer:attach-url(att-url-list[i]).
   end.

   mailer:send().

   again = false.
   message "Send another email?" update again format "Y/N".
end.

This is an screen shot of a possible result:

HTML Email with Embedded Images

This sends an HTML email with two embedded images (one via a local file and the other via URL).


def var c-type as character format "x(25)" 
   label "Connection Type" 
   view-as combo-box
      list-items "UNENCRYPTED", "STARTTLS_WHEN_AVAILABLE", "STARTTLS_REQUIRED", "SSL" 
      size-chars 25 by 5.

def var host      as char format "x(65)"  init "localhost" label "Host".
def var port      as int  format "99999"  init ?           label "Port".
def var s-user    as char format "x(25)"  init ?           label "User".
def var pw        as char format "x(25)"  init ?           label "Password".
def var from-addr as char format "x(65)"                   label "From".
def var reply-to  as char format "x(65)"                   label "Reply-To".
def var to-addr   as char format "x(65)"                   label "To".
def var subject   as char format "x(65)"                   label "Subject".
def var c-valid   as log                  init true        label "Validate".
def var filename  as char format "x(65)"                   label "File to Embed".
def var url       as char format "x(65)"                   label "URL to Embed".

def var html-body as char.
def var cid1 as char.
def var cid2 as char.

def var mailer as handle.
def var again  as log init true.

repeat while again:

   update c-type                                                                                       skip
          host            help "Type hostname or IP address."                                          skip
          port            help "Type port number or leave blank for default port."                     skip
          s-user          help "Userid for SMTP server authentication or leave blank for no auth."     skip
          pw              help "Password for SMTP server authentication or leave blank for no auth."   skip
          c-valid         help "Use 'no' to disable server certificate validation."                    skip
          from-addr                                                                                    skip
          reply-to                                                                                     skip
          to-addr                                                                                      skip
          subject                                                                                      skip
          filename     validate(search(filename) ne ?, "Filename must exist on the local system!")     skip
          url
          with frame compose side-labels title "HTML Email with Embedded Images".

   create smtp-email mailer
      assign connection-type = c-type
             smtp-host       = host
             smtp-port       = string(port)
             smtp-user       = s-user 
             smtp-password   = pw
             smtp-from       = from-addr
             smtp-reply-to   = reply-to
             smtp-subject    = subject 
             smtp-validate   = c-valid.

   mailer:add-to-address(to-addr).

   cid1 = mailer:embed-image-by-file(filename).
   cid2 = mailer:embed-image-by-url(url).

   html-body = "<html><body><img src=\"cid:" + cid1 + "\"><p>This is text in between two images.<p><img src=\"cid:" + cid2 +"\"></body></html>".
   mailer:smtp-html = html-body.

   mailer:send().

   again = false.
   message "Send another email?" update again format "Y/N".
end.

This is an screen shot of a possible result:

Java Implementation Details

The primary class that provides the handle based resource is com.goldencode.p2j.email.SmtpEmail. This class has Java methods that match each 4GL attribute and method:

4GL Attribute Java Method(s)
connection-type getConnectionType(), setConnectionType(character), setConnectionType(String)
smtp-from getSmtpFrom(), setSmtpFrom(character), setSmtpFrom(String)
smtp-host getSmtpHost(), setSmtpHost(character), setSmtpHost(String)
smtp-html getSmtpHtmlBody(), setSmtpHtmlBody(character), setSmtpHtmlBody(String)
smtp-port getSmtpPort(), setSmtpPort(character), setSmtpPort(String)
smtp-reply-to getSmtpReplyTo(), setSmtpReplyTo(character), setSmtpReplyTo(String)
smtp-subject getSmtpSubject(), setSmtpSubject(character), setSmtpSubject(String)
smtp-text getSmtpTextBody(), setSmtpTextBody(character), setSmtpTextBody(String)
smtp-user getSmtpUser(), setSmtpUser(character), setSmtpUser(String)
smtp-password getSmtpPassword(), setSmtpPassword(character), setSmtpPassword(String)
smtp-validate isSmtpValidate(), setSmtpValidate(character), setSmtpValidate(String)
4GL Method Java Method(s)
add-to-address(character addr) addToAddress(character), addToAddress(String)
add-cc-address(character addr) addCcAddress(character), addCcAddress(String)
add-bcc-address(character addr) addBccAddress(character), addBccAddress(String)
attach-file(character filename) attachFile(character), attachFile(String)
attach-url(character url) attachURL(character), attachURL(String)
clear-attachments-list() clearAttachmentsList()
clear-bcc-list() clearBccAddressList()
clear-cc-list() clearCcAddressList()
clear-embedded-image-list() clearEmdeddedImageList()
clear-to-list() clearToAddressList()
embed-file(character filename) embedFile(character), embedFile(String)
embed-url(character filename) embedURL(character), embedURL(String)
get-attachments-list() getAttachmentsList()
get-bcc-list() getBccAddressList()
get-cc-list() getCcAddressList()
get-embedded-image-list() getEmbeddedImageList()
get-to-list() getToAddressList()
send() send()

The Java method names were deliberately mapped directly to the keywords that were added to the language. When OO 4GL support is available, these keywords will be removed and the SmtpEmail class will be used directly from 4GL code. When that happens, the method names will be simplified to take advantage of the natural namespace that occurs by using an OO approach.

Under the covers, the code in SmtpEmail uses the com.goldencode.email.EmailDefinition and com.goldencode.email.Emailer classes, which in turn use the Apache Commons Email project.

Please see #3343 for more implementation details.


© 2004-2017 Golden Code Development Corporation. ALL RIGHTS RESERVED.

html_email_with_attachments.png (27.7 KB) Greg Shah, 11/01/2017 05:58 PM

html_email_with_embedded_images.png (32.9 KB) Greg Shah, 11/01/2017 05:58 PM

simple_text_email.png (17.1 KB) Greg Shah, 11/01/2017 05:59 PM