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_REQUIREDorSSL. - 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:

Diagnosis Tip¶
If you can't easily setup a Java debugging environment with the full FWD runtime, you can use the command line program com.goldencode.email.Emailer to test your inputs.
The syntax:
Syntax: java -classpath p2j.jar com.goldencode.email.Emailer <mode> <host> <port> <userid> <password> <from> <to> <subject> <text>
This command line program has a debug flag enabled so that the Apache Commons Email code will put verbose output to the terminal. That should let you experiment to test your inputs and confirm that the result will work.
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-2025 Golden Code Development Corporation. ALL RIGHTS RESERVED.