Do yourself a big favor. Replace QTMMSENDMAIL and SNDDST.
At the recent COMMON Conference in Reno, I went to just about all of the sessions related to PHP, Zend Framework, and MySQL. I got a lot of good information and made a bunch of new friends. Most of the PHP and MySQL sessions were standing-room-only, which was great to see. Yet I ran into too many people who were still on the fence, stalling rather than embracing these powerful new tools. This article is a direct response to those people.
Since I was a boy, I have been told to always use the right tool for the job. Today, that advice rings at least as true as it did when I was a boy rebuilding car engines with my dad. As a developer, using the right tool means knowing the strengths of each language in your toolbox. RPG does a lot of things extremely well, but email is not one of them. On the other hand, PHP handles email very well. The Zend_Mail class in Zend Framework handles it even better still. For example, if you are sending emails with one or more attachments, Zend_Mail offers a vastly simpler solution than SNDDST or QTMMSENDMAIL. Let's see how it works.
The first thing you'll need to know is how to use PHP-CLI, the PHP Command Line Interface. PHP-CLI is simply a way to run a PHP script from a command line--in this case, the QShell (QSH) command line on the IBM i (iSeries, AS/400, et al). The example below shows how you might execute a PHP script from the QSH command line:
php-cli /www/zendcore/htdocs/scriptName.php
PHP-CLI is the "command" to execute. If your PHP server were running on a Windows machine, the command would be PHP-CLI.exe. The path and filename that follow identify the PHP script to be executed. Any parameters to be passed to the script would be separated by a blank space and would follow the script filename and path. See the example below:
php-cli /www/zendcore/htdocs/scriptName.php "parm 1" "parm 2" 3
In the example, the first two parameters are enclosed in quotes because they contain embedded blanks. We will use PHP-CLI to execute our email script directly from an RPG program by using the QSH command via the QCMDEXC command interface. But we are getting ahead of ourselves.
The next thing we will do is write a PHP script that will accept up to six parameters and then use those parameters to send an email. The code for this script is shown below:
<?php
/**********************************************************************
* Send Email script
*
* This script will accept parameters from the command line and
* use those parameters to build and send an email.
*
* @author Jeff Olen - Olen Business Consulting, Inc
*
*
* @param String argument value 1 - Subject text for the email
* @param String argument value 2 - Email recipient addresses
* comma separated
* @param String argument value 3 - Email attachment
* filename(s)/path(s) comma separated. Leave
* blank if no attachments.
* @param String argument value 4 - This is the body of the
* email and must be normal text not HTML.
* @param String argument value 5 - Email carbon copy (CC)
* addresses comma separated. Leave blank if no
* attachments.
* @param String argument value 6 - Email blind carbon copy
* (BCC) addresses comma separated. Leave blank if
* no attachments.
*
*
********************************************************************
*/
// set error display and reporting level
// (remove after test and debugging!!)
ini_set("display_errors",1 );
error_reporting(E_ALL);
// load parameters to variables
$subject = $_SERVER['argv'][1];
$toEmail = $_SERVER['argv'][2];
$attachments = $_SERVER['argv'][3];
$body = $_SERVER['argv'][4];
if (isset($_SERVER['argv'][5])) {
$ccEmail = $_SERVER['argv'][5];
var_dump($ccEmail);
}
if (isset($_SERVER['argv'][6])) {
$bccEmail = $_SERVER['argv'][6];
}
// load the mail class
require_once('Zend/Mail.php');
require_once 'Zend/Mail/Transport/Smtp.php';
// set the transport to SMTP
$tr = new Zend_Mail_Transport_Smtp('localhost');
Zend_Mail::setDefaultTransport($tr);
// create the mail object
if (!($mail = new Zend_Mail())) {
die("failed to create Zend_Mail object. ");
}
// set the email text
$mail->setBodyText($body);
// set the subject
$mail->setSubject($subject);
// set sender
$mail->setFrom(
// add the TO email address(es)
$toAddresses = explode(',',$toEmail);
foreach ($toAddresses as $address) {
$mail->addTo(trim($address));
}
// add the CC email address(es) (if any)
if (isset($ccEmail)) {
$toAddresses = explode(',',$ccEmail);
foreach ($toAddresses as $address) {
$mail->addCc(trim($address));
}
}
// add the BCC email address(es) (if any)
if (isset($bccEmail)) {
$toAddresses = explode(',',$bccEmail);
foreach ($toAddresses as $address) {
$mail->addBcc(trim($address));
}
}
// add the attachments (if any)
if (strlen(trim($attachments)) > 0) {
$attachFiles = explode(',',$attachments);
foreach ($attachFiles as $Filename) {
$fileContents = file_get_contents(trim($Filename));
$at = $mail->createAttachment($fileContents);
$at->filename = trim($Filename);
}
}
// send the email
$mail->send();
?>
This code is fairly straightforward. The parameters to be passed are clearly defined in the PHPDoc comments at the top. Since most of the parameters can have embedded blanks, we will want to enclose them in quotes when we paste together the command.
We could examine each piece of the code and describe the purpose of it. However, in the interest keeping this article to a reasonable size, I'll leave you to decipher most of it your self. The sole exception is the following code:
// set the transport to SMTP
$tr = new Zend_Mail_Transport_Smtp('localhost');
Zend_Mail::setDefaultTransport($tr);
In the code above, we are using some object-oriented (OO) coding techniques to set the default email transport to SMTP and set the SMTP server to localhost. If you are unfamiliar with OO, you are welcome to use this code without understanding exactly how it does what it does. As a friend once told me, "You can use object-oriented techniques in your PHP code, but you don't have to." And it's true. You can use completely procedural PHP without any trouble whatsoever. Then, when you want to, you can start using object-oriented. No problem.
The reason we need to set the default email transport is that by default the Zend_Mail class will attempt to use the UNIX-based SENDMAIL command to send email. Since the IBM i doesn't usually have SENDMAIL installed, we are overriding the default setting and forcing it to use the standard IBM i TCP SMTP server. This means that you will need to have the IBM i TCP SMTP server running and correctly configured in order for this script to function correctly. A simple test to determine if your IBM i is correctly configured is to attempt to send an email using SNDDST. If it works, then all is well.
Once you have the PHP script written, you need to write the RPG program or service program that you will call to send the email(s). I have created a standalone program to show you how this works, but it could easily be converted to a service program procedure. The code is shown below:
H OPTION(*NODEBUGIO:*SRCSTMT) DFTACTGRP(*NO) actgrp(*caller)
// ----------------------------------------------------------------------
// Program name: EMAILSRVC
// Description : Provides email service via PHP/Zend_Mail
// ----------------------------------------------------------------------
// ----------------------------------------------------------------------
// Parameters:
//
// inSubject - string to be used as the subject of the email.
// inToAddresses - String of email addresses separated by commas
// to be used as the TO: email addresses.
// inAttachFiles - String of IFS files (with path) to be attached
// to the email as attachments.
// inBodyText - String of plain text to be used as the body of
// the email.
// inCCAddresses - (optional) String of email addresses separated by
// commas to be used as the CC: email addresses.
// inToAddresses - (optional) String of email addresses separated by
// commas to be used as the BCC: email addresses.
//
// ----------------------------------------------------------------------
// Modifications:
// ----------------------------------------------------------------------
// 05/02/09 Jeff Olen - Olen Business Consulting, Inc.
// initial version
// ----------------------------------------------------------------------
d EMailSrvc PR extpgm('EMAILSRVC')
d inSubject 125a const
d inToAddresses...
d 32767a options(*varsize) const
d inAttachFiles...
d 32767a options(*varsize) const
d inBodyText 32767a options(*varsize) const
d inCCAddresses...
d 32767a options(*nopass:*varsize) const
d inBCCAddresses...
d 32767a options(*nopass:*varsize) const
d EMailSrvc PI
d inSubject 125a const
d inToAddresses...
d 32767a options(*varsize) const
d inAttachFiles...
d 32767a options(*varsize) const
d inBodyText 32767a options(*varsize) const
d inCCAddresses...
d 32767a options(*nopass:*varsize) const
d inBCCAddresses...
d 32767a options(*nopass:*varsize) const
// -----------------------------------------------------------------------
// External procedure prototypes
// -----------------------------------------------------------------------
d runCommand pr extpgm('QCMDEXC')
d cmdString 32767a options(*varsize) const
d cmdLength 15p 5 const
// -----------------------------------------------------------------------
// Global variables
// -----------------------------------------------------------------------
d cmdString s 32767a varying
d wkToAddresses s 32767a
d wkAttachFiles s 32767a
d wkBodyText s 32767a
d wkCCAddresses s 32767a
d wkBCCAddresses s 32767a
d PHP_CLI_PATH C '/usr/local/Zend/Core/bin/php-cli'
d ZEND_MAIL_SCRIPT_PATH...
d C '/www/zendcore/htdocs/jeffo/misc_scr-
d ipts/TestZendMail.php'
d SINGLE_QUOTE C ''''
d DBL_QUOTE C ''''''
/free
// -----------------------------------------------------------------------
// Mainline
// -----------------------------------------------------------------------
wkToAddresses = %subst(inToAddresses:1:%len(%trim(inToAddresses)));
wkAttachFiles = %subst(inAttachFiles:1:%len(%trim(inAttachFiles)));
wkBodyText = %subst(inBodyText:1:%len(%trim(inBodyText)));
if %parms >= 5;
wkCCAddresses = %subst(inCCAddresses:1:%len(%trim(inCCAddresses)));
endif;
if %parms >= 6;
wkBCCAddresses = %subst(inBCCAddresses:1:%len(%trim(inBCCAddresses)));
endif;
cmdString = 'QSH CMD(' + SINGLE_QUOTE +
DBL_QUOTE + PHP_CLI_PATH + DBL_QUOTE + ' ' +
DBL_QUOTE + ZEND_MAIL_SCRIPT_PATH + DBL_QUOTE + ' ' +
DBL_QUOTE + %trim(inSubject) + DBL_QUOTE + ' ' +
DBL_QUOTE + %trim(wkToAddresses) + DBL_QUOTE + ' ' +
DBL_QUOTE + %trim(wkAttachFiles) + DBL_QUOTE + ' ' +
DBL_QUOTE + %trim(wkBodyText) + DBL_QUOTE;
if %parms >= 5;
cmdString += ' ' + DBL_QUOTE + %trim(wkCCAddresses) + DBL_QUOTE;
endif;
if %parms >= 6;
cmdString += ' ' + DBL_QUOTE + %trim(wkBCCAddresses) + DBL_QUOTE;
endif;
cmdString += SINGLE_QUOTE + ')';
callp runCommand ( cmdString : %len(%trim(cmdString)) );
*inlr = *on;
return;
/end-free
Since I am guessing that you are already an RPG master (you are, aren't you?), I am not going to review the code in detail. I encourage you to pick it apart yourself.
So with this RPG code in place, all you need to do is format your parameters as described in the RPG program (or the PHPDoc comments in the PHP script) and call the EMAILSRVC program. Please note that you cannot call the EMAILSRVC program from the command line because of the size of the parameters. It can only be called from another program. You could, however, code a command to run it as the command processing program (CPP). The PHP script will attach the specified attachments from the IFS, converting them to BASE64 as necessary, and then send the email. Anyone who has ever spent the time to decipher and use QTMMSENDMAIL will join me in celebrating the simplicity of this solution.
Last but not least, I'll leave you with a CLLE program that actually calls the EMAILSRVC program with parameters specified.
PGM
DCL VAR(&SUBJECT) TYPE(*CHAR) LEN(125) +
VALUE('Test subject')
DCL VAR(&ToAddrs) TYPE(*CHAR) LEN(32767) +
VALUE(
DCL VAR(&CCAddrs) TYPE(*CHAR) LEN(32767) +
VALUE(
DCL VAR(&BCCAddrs) TYPE(*CHAR) LEN(32767) +
VALUE(
DCL VAR(&Attach) TYPE(*CHAR) LEN(32767) +
VALUE('/home/jolen/DNC_2009_05_1.txt.zip, +
/home/jolen/TechTip_2009_05.doc')
DCL VAR(&Body) TYPE(*CHAR) LEN(32767) +
.+... 2 ...+... 3 ...+... 4 ...+... 5 ...+... 6 ...+...
VALUE('Here's your DNC zip file and the +
Techtip you asked for.')
CALL PGM(EMAILSRVC) parm(&subject &ToAddrs +
&Attach &Body &ccAddrs &bccAddrs)
ENDPGM
Enjoy!
LATEST COMMENTS
MC Press Online