25
Thu, Apr
1 New Articles

Has Your System i Emailed Someone Today?

RPG
Typography
  • Smaller Small Medium Big Bigger
  • Default Helvetica Segoe Georgia Times

Depending on how much functionality you need, you have a variety of options for sending email from your System i.

 

Did you know your System i can send email messages? In this article, I'll demonstrate how you can easily integrate email into your existing programs, allowing them to send an email message as simply as (or more simply than) they produce a report.

Four Solutions for One Problem

The four email-sending procedures presented here were created over a couple of years, according to the company's needs. I'll start with the simplest solution--sending short, text-only email--and move up to the most complex--sending formatted text and several types of attachments (text files, Excel spreadsheets, and PDF files) to multiple addresses.

 

The SndEmail procedure (Figure 1) allows you to send an email message to a maximum of three addresses.

 

     *-------------------------------------------------------------------------*

     *   Send E-Mail Messag

     *-------------------------------------------------------------------------

     PSndEMail         B                   EXPOR

     D                 PI            

     * Input Parm

     DP_SendTo                       75   Valu

     DP_CopyTo                       75   Valu

     DP_BlindCopyTo                 75   Valu

     DP_Subject                     30   Valu

     DP_Body                       200   Valu

 

     /FRE

 

       // Set the e-mail destination strin

       // (A)

     SetDestStr(P_SendTo : P_CopyTo : P_BlindCopyTo : P_DestStr)

 

       // Send messag

       // (B)

       P_Cmdlin = 'SNDDST TYPE(*LMSG)

                   + ' TOINTNET(' + %Trim(P_DestStr) + ')

                   + ' DSTD(' + '''' + %Trim(P_Subject) + '''' + ')

                   + ' LONGMSG(' + '''' + %Trim(P_Body) + '''' + ')

                   + ' PTY(*HIGH)'

       // (C)

       W_ErrorDS = ExecCmd (%Trim(P_CmdLin))

 

       // If the mail was sent (no error message id), return *Of

       If W_ErrorDS.APIMsgId = *Blanks

           Return *Off

       Else

           Return *On

       Endif

 

     /END-FRE

 

     PSndEMail         E

 

Figure 1: Use SndEmail to send email to up to three addresses.

 

The message is constrained by the subject and body lengths (30 and 200 characters, respectively) and doesn't allow any attachments. This might suffice if all you need is to alert a user with a short and simple message.

 

How does it work? Well, this procedure is actually a programmer-friendly mask to the SNDDST CL command: the procedure's parameters are transformed to fit the command's keywords. The P_SendTo, P_CopyTo, and P_BlindCopyTo parameters are rearranged by the SetDestStr procedure into the destination string that will fit the command's TOINTNET keyword (see A). The P_Subject and P_Body parameters are used in the DSTD and LMSG keywords, respectively (see B). When the CL command is built, the ExecCmd procedure is used to execute it (see C).

 

Sounds simple, doesn't it? Too simple? You need to send a message and an output file? Use SndFileByMail (Figure 2).

 

     *-------------------------------------------------------------------------*

     *   Send File By Mai

     *-------------------------------------------------------------------------

     PSndFileByMail   B                   EXPOR

     D                 PI            

     * Input Parm

     DP_SendTo                       75   Valu

     DP_CopyTo                       75   Valu

     DP_BlindCopyTo                 75   Valu

     DP_Body                       200   Valu

     DP_FileName                     10   Valu

     DP_DocName                     12   Valu

     DP_FolderName                   10   Valu

     DP_Format                       10   Valu

 

     /FRE

 

       // Set the e-mail destination strin

       // (A)

       SetDestStr(P_SendTo : P_CopyTo : P_BlindCopyTo : P_DestStr)

 

       // Retrieve the field separator for CSV forma

       // (B)

         In DaFldSep

         W_FldSep = DaFldSep

 

       // Set attachment (either copy PF to folder, or use existing doc in folder

       // (C)

       If P_DocName = *Blanks

           P_DocName = %Trim(P_FileName) + '.' + %Trim(P_Format)

       EndIf

       If P_FileName <> *Blanks

           Select

             When P_Format = 'TXT'

                 P_Cmdlin = 'CPYTOPCD FROMFILE(' + %Trim(P_FileName) + ')

                             + ' TOFLR(' + %Trim(P_FolderName) + ')

                             + ' TODOC(' + '''' + %Trim(P_DocName) + '''' + ')

                            + ' REPLACE(*YES)'

 

             When P_Format = 'CSV'

             P_Cmdlin = 'CPYTOIMPF FROMFILE(' + %Trim(P_FileName) + ')

                         + ' TOSTMF(' + '''' + 'QDLS/

                         + %Trim(P_FolderName) + '/' + %Trim(P_DocName) + '''

                         + ') MBROPT(*REPLACE) STMFCODPAG(437)

                         + ' RCDDLM(*CRLF) STRDLM(' + '''' + ' ' + '''' + ')

                         + ' FLDDLM(' + '''' + W_FldSep + '''' + ')'

             Other

                Return *On

           EndSl

           W_ErrorDS = ExecCmd (%Trim(P_CmdLin))

 

       // If an error occurred (error message id is not blank), return *O

       // (D)

           If W_ErrorDS.APIMsgId <> *Blanks

             Return *On

          EndIf

 

       // Send messag

       // (E)

           P_Cmdlin = 'SNDDST TYPE(*DOC)'

                       ' TOINTNET(' + %Trim(P_DestStr) + ')

                       + ' DSTD(' + '''' + 'DESC' + '''' + ')

                       + ' MSG(' + '''' + %Trim(P_Body) + '''' + ')

                       + ' DOC(' + '''' + %Trim(P_DocName) + '''' + ')

                       + ' FLR(' + %Trim(P_FolderName) + ') PTY(*HIGH)'

           W_ErrorDS = ExecCmd (%Trim(P_CmdLin))

 

       // If the mail was sent (no error message id), return *Of

           If W_ErrorDS.APIMsgId = *Blanks

             Return *Off

           Else

             Return *On

           Endif

       Endif

 

     /END-FRE

 

     PSndFileByMail   E

 

Figure 2: Use SndFileByMail to send a message and an output file.

 

This procedure can be used to send an output file generated by your program in text or comma-separated values (CSV) format to the same three addresses as the previous procedure. Again, the procedure is a mask to the SNDDST CL command: the destination addresses are treated the same way as previously (see A), but the similarity to the first procedure ends here. The CSV conversion functionality requires a separator, usually a comma. Since this separator can vary in different countries or Excel versions, it's stored in a data area (DAFLDSEP). Because of this, the procedure's next step is retrieving the separator character (see B).

 

The attachment type, passed in parameter P_Format, determines the type of conversion that will be performed. For the text format, the procedure uses the CPYTOPCD CL command, while the CPYTOIMPF command is used to generate the CSV format (see C). If the conversion fails, the procedure ends, returning *On. This is the way to tell the calling program that something went wrong and the email was not sent (see D). Finally, the CL command string is formed and executed (see E). Notice that the P_Body is now placed in the MSG keyword and there is no P_Subject parameter. This happens because we are now using a different type of distribution that forces us to do these changes (see the SNDDST CL command explanation).

 

What if all you really need is to send a spool file (some sort of business report or purchase order) via email to a supplier or client, without any need for fancy subject or message text because the attachment says it all? In that case, use SndSplfByMail (Figure 3).

 

     *-------------------------------------------------------------------------*

     *   Send Spoolfile By Mail

     *-------------------------------------------------------------------------*

     PSndSplfByMail   B                   EXPORT

     D                 PI             N

     * Input Parms

     DP_SplfName                     10   Value

     DP_SendTo                       75   Value

 

     DI_ProdSys       S               N   Inz(*Off)

 

     /FREE

 

       // (A)

       // Check if this is the production system.

       // If it's not, e-mails won't be sent out of the Company

       I_ProdSys = ProductionSys;

 

       // Retrieve the PDF transform printer name

       In DaPdfPrt;

       W_PdfPrt = DaPdfPrt;

 

       // * Get the user's e-mail address (if internal)

       // (B)

       P_SendTo = RtvEmailAddr(P_SendTo);

       // If this it not the production system

       // and the destination mail is not from the company, send it.

       // (C)

       If Not I_ProdSys And Not InternalAddr(P_SendTo);

           Return *On;

       EndIf;

 

     // Change spoolfile attributes and redirect to PDF transform printer

       // (D)

       P_Cmdlin = 'CHGSPLFA FILE(' + %Trim(P_SplfName) + ') SPLNBR(*LAST)'

                   + ' OUTQ(*LIBL/' + %Trim(W_PdfPrt) + ')'

                   + ' USRDFNDTA(' + ''''

                   + 'MAILTAG(' + %Trim(P_SendTo) + ')' + '''' + ')';

       W_ErrorDS = ExecCmd (%Trim(P_CmdLin));

 

       // If the mail was sent (no error message id), return *Off

       If W_ErrorDS.APIMsgId = *Blanks;

           Return *Off;

       Else;

           Return *On;

       Endif;

 

     /END-FREE

 

     PSndSplfByMail   E

 

Figure 3: Use SndSplfByMail to send only a spool file.

 

This very simple procedure takes advantage of the PDF conversion functionality provided by the InfoPrint Server licensed program. IBM's Printing Redbook explains how to install and configure this functionality, so I won't waste your time with that here; I'll just explain how it works. The conversion is performed by a virtual printer that also sends the generated PDF file via email to the address you specify in the USRDFNDTA keyword of the CHGSPLFA CL command.

 

Let's go over the code. In the beginning of the procedure (see A), the necessary data is gathered: system type (development, test, or production) and PDF conversion printer are retrieved. In the next step (see B), the P_SendTo parameter is checked and converted to an Internet email address if necessary (read about procedure RtvEmailAddr in the "Other Procedures" sidebar at the end of this article). In C, the address is checked (to prevent sending test data to the "outside world") using the system type information and the destination address (the procedure InternalAddr is also explained in the "Other Procedures" sidebar). Next, in D, the conversion and distribution are performed in one single step: the spool file name (P_SplfName), PDF conversion printer (W_PdfPrtf), and destination address (P_SendTo) are used in the CHGSPLFA CL command to send the spool file to the "printer" that sends it to its destination.

 

Be aware that there are some factors to take into account: complex printer files (with lines, boxes, or extensive formatting) might not be properly converted or, even worse, not converted at all. Generate the spool file and use the CHGSPLFA command or option 2 in the WRKSPLF command to test it before you use this procedure.

 

The procedure requires the InfoPrint Server licensed program to work; without it, it's useless.

The conversion is performed by a printer that, even though is a virtual one, might be waiting for a reply to a message or stopped.

Mime & Mail

Eventually, the business started asking for more than these procedures could supply. So we went online looking for a solution and found it in IBM. Not IBM Rochester, as one would expect, but IBM Italy. Confused? It's quite simple: Giovanni B. Perotti, a former IBM Italy developer, created a set of procedures that provide a usable front-end to the QtmmSendMail API, a not-user-friendly-at-all API to send email. This application, named Mime & Mail, is free and open source. It can be downloaded from Giovanni's Web site. You can find out more about this application in "You've Got Mail," an introductory article by Susan Gantner and Jon Paris in the October 2004 edition of IBM Systems Magazine. We used it to create the email sending procedure that I'll present now. The requirement was to enable programs to send several types of output in different formats to multiple destinations.

 

Procedure SndMulFiles (Figure 4) provides a programmer-friendly interface to the Mime & Mail procedures.

 

     *-------------------------------------------------------------------------*

     *   Send Multiple files in an E-Mail Message

     *-------------------------------------------------------------------------*

     PSndMulFiles     B                  EXPORT

     DSndMulFiles     PI             1

     * Input Parms

     D P_SName                       50a   Value

     D P_SEmail                     50a   Value

     D P_MailTo                     200   Value

     D P_MailCC                     200   Value

     D P_MailBcc                   200   Value

     D P_Spool                       10   Value

     D                                     Dim(50)

     D P_ImbAtt                       1   Value

     D                                     Dim(50)

     D P_File                       10   Value

     D                                     Dim(50)

     D P_Lib                         10   Value

     D                                     Dim(50)

     D P_FmtFile                     1   Value

    D                                     Dim(50)

     D P_NameFile                   12   Value                                 File Name

     D                                     Dim(50)

     D P_IncFlds                     1   Value                                Include field names

     D*                                                                         0 - Don't include

     D*                                                                         1 - Include

     D                                    Dim(50)

     D P_Zip                         12   Value                                 Zipped file name

     D                                     Dim(50)

     D P_MailSubject                 70a   Value

     D P_MailBody                   500   Value

     D P_PathFile                   60   Value Options(*NoPass)

     D                                     Dim(20)

 

 

     D P_ImbAtt1       S             10i 0 Dim(51) Inz

 

     D FromFName       S           512   Inz(*Blanks)

     D FromFNameA     S           512   Dim(200) Inz

     D SplName         S             10   Inz(*Blanks)

     D ImbAtt         S             10i 0 Inz(*Zeros)

     D ToAddrArr       S           256   Dim(1000) Inz

     D ToDistArr       S             10i 0 Dim(1000) Inz

     D ToNameArr       S             50   Dim(1000) Inz

     D ReplyTo         S             50   Inz(*Blanks)

 

     D W_Order         S             3 0 Inz(*Zeros)

     D W_ToDist       S             1 0 Inz(*Zeros)

     D W_Zip          S             12   Inz(*Blanks)

     D W_File         S             10   Inz(*Blanks)

     D W_FileSav       S             10   Inz(*Blanks)

     D W_Lib           S             10   Inz(*Blanks)

     D W_FromMember   S             10   Inz(*Blanks)

     D W_ToFile       S             64   Inz(*Blanks)

     D W_ToDir         S           128   Inz(*Blanks)

     D W_To           S             1   Inz(*Blanks)

 

     D W_Len50         S             5 0 Inz(*Zeros)

     D W_Len51         S              5 0 Inz(*Zeros)

     D W_Len           S             5 0 Inz(*Zeros)

     D W_Len1         S             5 0 Inz(*Zeros)

     D W_Len2         S             5 0 Inz(*Zeros)

     D W_Res           S             50   Inz(*Blanks)

     D Wx             S             5 0 Inz(*Zeros)

     D Wy             S             5 0 Inz(*Zeros)

     D WxSav           S             5 0 Inz(*Zeros)

     D Wn             S             5 0 Inz(*Zeros)

     D W_LibSav       S             10   Inz(*Blanks)

     D W_LibAux       S             10   Inz(*Blanks)

     D W_String       S           1000   Inz(*Blanks)

     D W_Pos           S             9 0 Inz(*Zeros)

     D W_MbrOpt       S             11   Inz(*Blanks)

     D W_LenT          s             4 0 Inz(*Zeros)

     D W_LenTSav       s             4 0 Inz(*Zeros)

     D W_CtlPdf       s             1   Inz(*Blanks)

     D W_LstEMail     S             50   Inz(*Blanks)

 

     /FREE

 

       // Inicialize variables

         Exsr Init;

 

       // (A)

       // ***************

       // PREPARE TO SEND

       // ***************

 

       // Prepare e-mail destinations array

         ExSr DstMails;

 

       // Import Message Body

         ExSr ImpBody;

 

       // Process Spool files to be sent

         ExSr TrSpool;

 

       // Process files to be sent

         ExSr ProccFiles;

 

       // Add the files to the IFS

         ExSr AddIfsFiles;

 

       // (B)

       // ***********

       // SEND E-MAIL

       // ***********

       // Invoke the MMailSender procedure to send e-mail

         ExSr SendMMAIL;

 

       // (C)

       // ********

       // CLEAN UP

       // ********

 

       // Remove the PDF files created for the message and unlock PDF creation

         ExSr RmvPdfs;

 

       // Remove the spool file generated for the message body

         ExSr RmvSpl;

 

 

       // *************

       // RETURN STATUS

       // *************

 

       // Return: *On means error

         If P_Error = '2';

           Return *On;

         Else;

            Return *Off;

         EndIf;

 

       //-----------------------------------------------------------------------*

       // Process spool files to be sent

       //-----------------------------------------------------------------------*

         BegSr Init;

 

       // Retrieve the field separator for XLS format

         In DaFldSep;

         W_FldSep = DaFldSep;

 

       // Read the Address of the Mail Log

       // (Used to check if the mail is being correctly sent)

         In DaMailLog;

         W_MailLog = DaMailLog;

 

       // Retrieve the path for file conversion (pdf, xls, etc.)

         In DaCnvPath;

         W_ToDir = DaCnvPath;

 

       // Check if this is the production system.

       // If it's not, e-mails won't be sent out of the Company

        I_ProdSys = ProductionSys;

 

       // Set the priority to HIGH

         P_Prio = 2;

 

       // Set the importance to MEDIUM

         P_Impo = 1;

 

         EndSr;

       //-----------------------------------------------------------------------*

       // Process spool files to be sent

       //-----------------------------------------------------------------------*

         BegSr TrSpool;

 

           Wx = 1;

       // Fill the spool file array (used by the mail sending program)

           Dow P_Spool(Wx) <> *Blanks Or %Elem(P_Spool) = Wx;

       // If the format is PDF, copy file to the IFS

               If P_ImbAtt(Wx) = '3';

       // Retrieve the PDF file name (Sequence Number) to be used

                 ExSr RtvSeqNbr;

       // Perform PDF conversion

                 P_Cmdlin = 'MMAIL/CVTSPLFPDF SPLF(' + %Trim(P_Spool(WX)) + ')'

                             + ' TOPDF(' + '''' + %Trim(P_Path) +''''+ ')';

                 W_ErrorDS = ExecCmd (%Trim(P_CmdLin));

       // If an error occured, return *ON. Continue otherwise

                 If W_ErrorDS.APIMsgId <> *Blanks;

                     Return *On;

                 Endif;

 

       // Add the file to the Files To Sent array

       // Save the index

                 WxSav = Wx;

       // Look up the last array entry

                 Wx = 1;

                 Dow FromFNameA(Wx) <> *Blanks;

                     Wx = Wx + 1;

                 EndDo;

                 FromFNameA(Wx) = %Trim(P_Path);

                 Wx = WxSav;

                  Wx = Wx + 1;

                 Iter;

               EndIf;

               P_SplName(Wx) = P_Spool(Wx);

               P_ImbAtt1(Wx) = %Int(P_ImbAtt(Wx));

               Wx = Wx + 1;

           EndDo;

 

       // Look up the last array entry, to add the body

           Wx = 1;

           Dow P_SplName(Wx) <> *Blanks;

               Wx = Wx + 1;

           EndDo;

       // Add the body

           If P_MailBody <> *Blanks;

               P_SplName(Wx) = 'MAILBODYSP';

           EndIf;

 

         EndSr;

       //-----------------------------------------------------------------------*

       // Add the body to the message

       //-----------------------------------------------------------------------*

         BegSr ImpBody;

 

       // If the message has a body, print it to a spool file and add it as

       // an embeded attachment

           If P_MailBody = *Blanks;

               LeaveSr;

           EndIf;

 

           Open MailBodySp;

 

           W_Len = 1;

           W_Len2 = 1;

           Dow W_Len > *Zero;

               W_Len = %Scan(':/P' : P_MailBody : W_Len+1);

               If W_Len = *Zero;

                 Body1 = %Trim(%Subst(P_MailBody : W_Len2 : 500 - W_Len2));

                 Write Body;

                 Leave;

               EndIf;

               If W_Len2 > 1;

                 Body1 = %Subst(P_MailBody : W_Len2 : W_Len - W_Len2);

               Else;

                 Body1 = %Subst(P_MailBody : W_Len2 : W_Len - 1);

               EndIf;

               W_Len2 = W_Len + 3;

              Write Body;

           EndDo;

 

           Close MailBodySp;

 

         EndSr;

       //-----------------------------------------------------------------------*

       // Remove the spool file created for the body

       //-----------------------------------------------------------------------*

         BegSr RmvSpl;

 

       // Delete the body spool file

           If P_MailBody = *Blanks;

               LeaveSr;

           EndIf;

           P_Cmdlin = 'DLTSPLF FILE(MAILBODYSP) SPLNBR(*LAST)';

           W_ErrorDS = ExecCmd (%Trim(P_CmdLin));

 

       // Clear the spool file array

           P_SplName = *Blanks;

 

         EndSr;

 

       //-----------------------------------------------------------------------*

       // Add the files to the IFS

       //-----------------------------------------------------------------------*

         BegSr AddIfsFiles;

 

           Wx = 1;

           Wy = 1;

 

       // Look up the last filled array position

           Dow FromFNameA(Wx) <> *Blanks And %Elem(FromFNameA) < Wx;

               Wx = Wx + 1;

           EndDo;

 

       // Add the files to be sent to the IFS to the proper array

           Dow P_PathFile(Wy) <> *Blanks And %Elem(FromFNameA) > Wx

               And %Elem(P_PathFile) > Wy And %Parms >= 16;

 

               FromFNameA(Wx) = P_PathFile(Wy);

               Wx = Wx + 1;

               Wy = Wy + 1;

 

           EndDo;

 

           EndSr;

       //----------------------------------------------------------------------*

       // Process files to be sent

       //_---------------------------------------------------------------------*

         BegSr ProccFiles;

 

           Wx = 1;

           Dow P_FmtFile(Wx) <> *Blanks;

               Select;

       // If the format is XLS

                  When P_FmtFile(Wx) = 'X' Or P_FmtFile(Wx) = 'T';

                     W_To = P_FmtFile(Wx);

       // Prepare the parameters for the conversion procedure

                     W_File = P_File(Wx);

                     W_Lib = P_Lib(Wx);

                    W_FromMember = '*FIRST';

       // If the file name is not filled, use the physical file name

                     If P_NameFile(Wx) = *Blanks;

                       W_ToFile = P_File(Wx);

                     Else;

                       W_ToFile = %Trim(P_NameFile(Wx));

                     EndIf;

 

                     If P_FmtFile(Wx) = 'X';

       // If the field names should be included in the output file

                         If P_IncFlds(Wx) = '1';

       // Write the field names as the file's first record

                         ExSr RtvFlds;

       // Copy the physical file to the IFS as XLS file

       // Copy the field names from the file in QTEMP

                         W_MbrOpt = '*REPLACE';

                         W_LibSav = W_Lib;

                         W_FileSav = W_File;

                         W_Lib = 'QTEMP';

                         W_File = 'PFFFD';

       // Convert files to XLS format

                         CopyToXls(W_File : W_Lib : W_FromMember :

                                  W_ToFile : W_ToDir : W_To : W_MbrOpt);

       // Duplicate the file to QTEMP, to put the field names in

                         P_Cmdlin = 'DLTF (QTEMP/PFFFD)';

                         W_ErrorDS = ExecCmd (%Trim(P_CmdLin));

       // Add the file with the field names to the previous file

                         W_MbrOpt = '*ADD';

                         W_Lib = W_LibSav;

                         W_File = W_FileSav;

                     Else;

                         W_LibSav = W_Lib;

                         W_FileSav = W_File;

                         W_MbrOpt = '*REPLACE';

                     EndIf;

                   Else;

                     W_MbrOpt = '*REPLACE';

                   EndIf;

 

       // Convert files to XLS format

                   CopyToXls(W_File : W_Lib : W_FromMember :

                             W_ToFile : W_ToDir : W_To : W_MbrOpt);

 

       // If the output file should be zipped

                   If P_Zip(Wx) <> *Blanks;

                      ExSr TrZip;

                   Else;

       // Otherwise attach it as it is

                       FromFNameA(Wx) = %Trim(W_ToDir) + '/' + %Trim(W_ToFile);

                   EndIf;

                 Other;

                   Return *On;

              EndSl;

               Wx = Wx + 1;

           EndDo;

 

         EndSr;

 

       //----------------------------------------------------------------------*

       // Zip files

       //----------------------------------------------------------------------*

         BegSr TrZip;

 

           If P_Zip(Wx) = *Blanks;

               LeaveSr;

           EndIf;

 

       // Replace the file extension by ".zip"

           W_Len = *Zeros;

           W_Len = %Scan('.':P_NameFile(Wx));

           If W_Len > *Zero;

               W_Zip = %SubSt(P_NameFile(Wx):1:W_Len);

           Else;

               W_Zip = P_NameFile(Wx);

               W_Len = 8;

           EndIf;

           W_Zip = %Replace('zip' : W_Zip : W_Len + 1 : 3);

           P_Cmdlin = 'PAEZIP/PAEZIP OPTION(*ZIP)'

                       + ' ZIPFILE(' + ''''+ %Trim(W_ToDir) + '/'

                       + %Trim(W_Zip) + '''' + ')'

                       + ' FILE(' + '''' + %Trim(W_ToDir) + '/'

                       + %Trim(P_NameFile(Wx)) + '''' + ')';

           W_ErrorDS = ExecCmd (%Trim(P_CmdLin));

 

       // Add the zip file to the Files to be Sent array

           FromFNameA(Wx) = %Trim(W_ToDir) + '/' + %Trim(W_Zip);

 

         EndSr;

 

 

       //----------------------------------------------------------------------*

       // Prepare e-mail destinations array

       //----------------------------------------------------------------------*

         BegSr DstMails;

 

           Wx = 1;

 

       // To

           Dow P_MailTo <> *Blanks;

              W_ToDist = *Zero;

               W_Len = %Scan(',' : P_MailTo : 1);

               If W_Len = *Zeros;

                 ToAddrArr(Wx)= %Trim(P_MailTo);

 

       // Retrieve mail address (internet e-mail or internal user)

                 ExSr RtvMail;

                 Wx = Wx + 1;

 

       // Don't send e-mails to the outside world from the test system

                 If Not I_ProdSys And Not InternalAddr (P_MailTo);

                     Wx = Wx - 1;

                     ToAddrArr(Wx)= *Blanks;

                     ToNameArr(Wx)= *Blanks;

                 EndIf;

                 Leave;

               EndIf;

               ToAddrArr(Wx)= %Trim(%Subst(P_MailTo : 1 : W_Len-1));

 

       // Remove the address added to the array from the string

              P_MailTo = %Replace(' ' : P_Mailto : 1 : W_Len);

 

       // Retrieve mail address (internet e-mail or internal user)

               ExSr RtvMail;

               Wx = Wx + 1;

 

       // If this it not the production system

       // and the destination mail is not from the company, don't send it

               If Not I_ProdSys And Not InternalAddr (P_MailTo);

                 Wx = Wx - 1;

                 ToAddrArr(Wx)= *Blanks;

                 ToNameArr(Wx)= *Blanks;

               EndIf;

         EndDo;

 

       // Cc

           Dow P_MailCc <> *Blanks;

               W_ToDist = 1;

               W_Len = %Scan(',':P_MailCc:1);

               If W_Len = *Zeros;

                 ToAddrArr(Wx)= %Trim(P_MailCc);

 

       // Retrieve mail address (internet e-mail or internal user)

                 ExSr RtvMail;

                 Wx = Wx + 1;

 

       // If this it not the production system

       // and the destination mail is not from the company, don't send it

                 If Not I_ProdSys And Not InternalAddr (P_MailCc);

                     Wx = Wx - 1;

                     ToAddrArr(Wx)= *Blanks;

                     ToNameArr(Wx)= *Blanks;

                 EndIf;

                 Leave;

               EndIf;

               ToAddrArr(Wx)= %Trim(%Subst(P_MailCc : 1 : W_Len-1));

 

       // Remove the address added to the array from the string

               P_MailCc = %Replace(' ':P_MailCc:1:W_Len);

 

       // Retrieve mail address (internet e-mail or internal user)

               ExSr RtvMail;

 

       // If this it not the production system

       // and the destination mail is not from the company, don't send it

               If Not I_ProdSys And Not InternalAddr (P_MailCc);

                 Wx = Wx - 1;

                 ToAddrArr(Wx)= *Blanks;

                 ToNameArr(Wx)= *Blanks;

               EndIf;

               Wx = Wx + 1;

           EndDo;

 

       // Bcc

       // BlindCopy - Send to the Mail Log mailbox

           If W_MailLog <> *Blanks;

               W_ToDist = 2;

               If P_MailBcc <> *Blanks;

                 P_MailBcc = %Trim(P_MailBcc) + ', ' + %Trim(W_MailLog);

               Else;

                 P_MailBcc = %Trim(P_MailBcc) + %Trim(W_MailLog);

               EndIf;

           EndIf;

 

           Dow P_MailBcc <> *Blanks;

               W_ToDist = 2;

               W_Len = %Scan(',' : P_MailBcc : 1);

               If W_Len = *Zeros;

                 ToAddrArr(Wx)= %Trim(P_MailBcc);

 

       // Retrieve mail address (internet e-mail or internal user)

                 ExSr RtvMail;

                 Wx = Wx + 1;

 

       // If this it not the production system

       // and the destination mail is not from the company, don't send it

                 If Not I_ProdSys And Not InternalAddr (P_MailBcc);

                     Wx = Wx - 1;

                     ToAddrArr(Wx)= *Blanks;

                     ToNameArr(Wx)= *Blanks;

                 EndIf;

                 Leave;

               EndIf;

               ToAddrArr(Wx)= %Trim(%Subst(P_MailBcc : 1 : W_Len-1));

 

       // Remove the address added to the array from the string

               P_MailBcc = %Replace(' ' : P_MailBcc : 1 : W_Len);

 

       // Retrieve mail address (internet e-mail or internal user)

               ExSr RtvMail;

 

       // If this it not the production system

       // and the destination mail is not from the company, don't send it

               If Not I_ProdSys And Not InternalAddr (P_MailBcc);

                 Wx = Wx - 1;

                 ToAddrArr(Wx)= *Blanks;

                  ToNameArr(Wx)= *Blanks;

               EndIf;

               Wx = Wx + 1;

           EndDo;

 

         EndSr;

       //----------------------------------------------------------------------*

       // Retrieve the user's email address

       // look it up in the group and user tables

       //----------------------------------------------------------------------*

         BegSr RtvMail;

 

       // If it's a group

           If %Scan('@':ToAddrArr(Wx)) = *Zero;

               P_Group = ToAddrArr(Wx);

             ToAddrArr(Wx) = *Blanks;

               P_Email = *Blanks;

               P_Eof = *Off;

               Dow P_Eof = *Off;

                 RtvGrpAddrs(P_Group : P_Email : P_Emails : P_Names : P_Eof);

       // Move the email names and addresses fields to the proper arrays

                 Wn = 1;

                 Dow Wn < 11;

                     If P_Emails(Wn) = *Blanks;

                       Leave;

                     EndIf;

                     ToAddrArr(Wx) = P_Emails(Wn);

                    ToNameArr(Wx) = P_Names(Wn);

                     ToDistArr(Wx) = W_ToDist;

                     W_LstEmail = P_Emails(Wn);

                     Wn = Wn + 1;

                     Wx = Wx + 1;

                 EndDo;

       // Re-call the Retrieve address procedure from the last address used,

       // because the procedure returns blocks of 10 addresses

                 P_Email = W_LstEmail;

               EndDo;

                 Wx = Wx - 1;

 

       // If it's not a group, check the address associated to the user

               Else;

                 P_Email = ToAddrArr(Wx);

                 P_Name = ToNameArr(Wx);

                 RtvEmailName(P_Email);

               EndIf;

 

         EndSr;

       //----------------------------------------------------------------------*

       // Retrieve the field names to include in the IFS' file header

       //----------------------------------------------------------------------*

         BegSR RtvFlds;

 

       // Inicialize fields

           W_LenTSav = *Zeros;

           W_LenT   = *Zeros;

           W_Len50   = *Zeros;

           W_Len51   = *Zeros;

 

       // Create a file in QTEMP with the field definition

           P_Cmdlin = 'DSPFFD FILE(' + %Trim(W_File) + ')'

                       + ' OUTPUT(*OUTFILE) '

                       + ' OUTFILE(QTEMP/PFEMP) '

                       + 'OUTMBR(*FIRST *REPLACE)';

           W_ErrorDS = ExecCmd (%Trim(P_CmdLin));

       // Read the field sizes

           Open PfEmp;

           Read QWHDRFFD;

       // Add the field sizes and for each field add 1, because of the comma

           W_Pos = *Zeros;

           Dow Not %Eof(PfEmp);

               W_Pos = 1;

               If W_LenTSav <> *Zeros;

                 W_LenTSav = W_LenT;

             Else;

                 W_LenTSav = 1;

               EndIf;

               If WHFLDD = *Zeros;

                 W_LenT = W_LenT + W_Pos + WHFLDB;

               Else;

                 W_LenT = W_LenT + W_Pos + WHFLDD;

               EndIf;

              If W_LenTSav <> 1;

                 W_Len50 = W_LenTSav + 1;

               Else;

                 W_Len50 = 1;

               EndIf;

               W_Len51 = W_LenT - W_LenTSav - 1;

               If W_Len51 > 10;

                 W_Len51 = 10;

              EndIf;

               W_String = %Replace(%Subst(WHFLDI : 1 : W_Len51) :

                                   W_String : W_Len50 : W_Len51);

       // If the format is XLS, use the user-defined field separator

               If P_FmtFile(Wx) = 'X';

                 W_String = %Replace(W_FldSep : W_String : W_LenT : 1);

               Else;

                 W_String = %Replace(' ' : W_String : W_LenT : 1);

               EndIf;

               Read QWHDRFFD;

           EndDo;

           Close PfEmp;

 

       // Duplicate the file to QTEMP to add the field names

           P_Cmdlin = 'CRTPF (QTEMP/PFFFD) RCDLEN(1000)';

           W_ErrorDS = ExecCmd (%Trim(P_CmdLin));

 

       // Put QTEMP at the beginning of the library list

         P_CmdLin = 'ADDLIBLE (QTEMP)';

         W_ErrorDS = ExecCmd(P_CmdLin);

 

       // Write the records to the file in QTEMP

           Open Pfffd;

           FD_Pfffd = %Trim(W_String);

           Write PfffdR;

           Close Pfffd;

 

         EndSr;

       //-----------------------------------------------------------------------*

       // Retrieve the PDF file name (Sequence Number) to be used

       //-----------------------------------------------------------------------*

         BegSr RtvSeqNbr;

 

       // Use the Pdf Lock Data Area to prevent document number duplication

           If W_CtlPdf <> '1';

               In(E) *Lock DaPdfLck;

               Dow %Error = *On;

                 In(E) *Lock DaPdfLck;

               EndDo;

           EndIf;

 

       // Retrieve the sequence number to be used

           W_CtlPdf = '1';

           W_Order = 1;

           Dow W_Order < 999;

               P_Path = %Trim(W_ToDir) + 'PDF' + %Char(W_Order) + '.pdf';

               W_Order = W_Order + 1;

               ChkObjInIFS (P_Path : P_Response);

               If P_Response = C_NotExists;

                 Leave;

               EndIf;

           EndDo;

 

         EndSr;

       //-----------------------------------------------------------------------*

       // Invoke the MMAILSND procedure to send e-mail

       //-----------------------------------------------------------------------*

         BegSr SendMMail;

         // Add the Mail processing library

           P_CmdLin = 'ADDLIBLE (MMAIL)';

           W_ErrorDS = ExecCmd(P_CmdLin);

 

 

         // Call the mail sending program

           MMailSender(P_Error : P_SName : P_SEmail : ToNameArr : ToAddrArr :

                       ToDistArr : ReplyTo : P_MailSubject : P_Impo :

                       P_Prio : FromFNameA : P_SplName : P_ImbAtt1);

 

         // Remove the Mail processing library

           P_CmdLin = 'RMVLIBLE (MMAIL)';

           W_ErrorDS = ExecCmd(P_CmdLin);

 

         EndSr;

       //-----------------------------------------------------------------------*

       // Remove the PDF files created for the message and unlock PDF creation

       //-----------------------------------------------------------------------*

         BegSr RmvPdfs;

 

           If W_CtlPdf = '1';

               P_CmdLin = 'RMVLNK OBJLNK('

                           + '''' + %Trim(W_ToDir) + 'PDF*.pdf' + '''' + ')';

               W_ErrorDs = ExecCmd(P_CmdLin);

               Out DAPdfLck;

               W_CtlPdf = *Blanks;

           EndIf;

 

         EndSr;

 

     /END-FREE

     PSndMulFiles     E

 

Figure 4: SndMulFiles provides a programmer-friendly interface to the Mime & Mail procedures.

 

SndMulFiles accepts as parameters spool files that can be converted to text or PDF format, physical files that can be transformed into text or Excel files, other files that already exist in your IFS, and a wide range of destinations (internal user IDs, mailing groups, and individual email addresses retrieved from a sort of address book or from the production data). But let's begin with the procedure's parameters:

 

  • P_SName is the sender's name. You might use this to enable your program to send emails under a department's name, such as Accounting, Customer Service, etc.
  • P_SEmail is the sender's address. This will be the "Reply To" address.
  • P_MailTo is the "To" field, where you can specify user IDs, groups, and names (from the address group) or Internet email addresses.
  • P_MailCC is the same as previous for the "Cc" field.
  • P_MailBcc is the same as previous for the "Bcc" field.
  • P_Spool is an array that can contain up to 50 spool files to convert and attach to the email message.
  • P_ImbAtt specifies the conversion to perform in each of the spool files; 1 means imbed in the body, 2 means attach to the body, 3 means convert to PDF and attach to the body.
  • P_File is an array that can contain up to 10 physical file names to convert and attach to the email message.
  • P_Lib is an array that contains the library of each physical file mentioned in the previous parameter.
  • P_FmtFile specifies the conversion to perform in each of the physical files contained in the P_File array: X means Excel, T means text format.
  • P_NameFile is an array that contains the name of each of the converted physical files
  • P_IncFlds is an array that indicates whether the column names should be included in each of the converted files: 0 means don't include, 1 means include.
  • P_Zip is an array that contains the zipped file name of each of the converted files; if an array position is not blank, it means that the corresponding file in array P_File will be zipped and attached to the message with the name specified in the P_Zip array position
  • P_MailSubject is the "Subject" field.
  • P_MailBody is the "Body" field.
  • P_PathFile is an optional parameter to specify additional files to attach that already exist in the IFS.

 

SndMulFiles is divided in three parts: preparation, execution, and cleanup.

 

Preparation (see A) begins with the setup of the email destinations array (in routine DstMails, which uses parameters P_MailTo, P_MailCC, and P_MailBcc parameters to fill the ToAddrArr, ToNameArr, and ToDistArr arrays). Note that the distribution type (To, Cc, or Bcc) is set in variable W_ToDist and passed to the ToDistArray in routine RtvMail. This routine retrieves the address or addresses (in case of a group) from the content of each of the ToDistArr array entries.

 

Since the entry parameters mentioned before can contain an Internet email address, a group name, a user name, or a system user ID, two conversions (procedures RtvGrpAddr and RtvEmailName, explained in the "Other Procedures" sidebar) are necessary to ensure that the name and address arrays are properly filled.

 

The next step is moving the P_Body parameter to the appropriate place within the email message. Routine ImpBody prints the P_Body parameter to a spool file and routine TrSpool converts it into the message body. This conversion is necessary because in Mime & Mail the body might contain formatting (we don't actually use it, except for the new line, ":/P" special notation that you'll find in the ImpBody routine), and this is the easiest way to ensure that nothing gets lost. The before-mentioned TrSpool routine not only imbeds the body in the message, but also treats any spool files indicated in the P_Spool parameter. This operation consists of converting the spool file to PDF (when indicated in the respective array entry of parameter P_ImbAtt) via the CVTSPLFPDF command provided by Mime & Mail and filling the FromFNameA, P_SplName, and P_ImbAtt1 arrays with the IFS file name, spool file name, and attachment type information, respectively. The next step is attaching any physical files specified in parameter P_File in routine ProccFiles. The resulting output files are then placed in array FromFNameA. There are several options that are driven by P_FmtFile (convert to Excel or Text format), P_IncFlds (include headers in the Excel format), and P_Zip (compress the output file).

 

The preparation stage ends with the AddIFSFiles routine, which adds to the FromFNameA array any other files (specified in the optional parameter P_PathFile) that already exist in the IFS.

 

The execution stage (see B) is simply the invocation of the Mime & Mail procedure wrapper (MMailSender) that sends the message. This procedure is explained in the "Other Procedures" sidebar.

 

Finally, the cleanup (see C) consists of two routines that remove the spool and PDF files (named RmvSpl and RmvPDFs, respectively) generated by the SndMulFiles procedure.

Requirements

In order to take full advantage of these procedures, you need to have several programs installed. The table below indicates the correspondence between procedures and required software:

 

Required Programs

Procedure

Additional Software Required

SndEmail

None

SndSplfByMail

InfoPrint Server Licensed Program

SndFileByMail

None

SndMulFiles*

Mime&Mail and PAEZIP

 

* SndMulFiles will work without PAEZIP if you always leave parameter P_Zip blank.

 

Keep in mind that you also need to configure your System i in order to be able to send email. Read the sidebar at the end of this article on that subject. Also, SndMulFiles was built using Mime & Mail's version dated April 26, 2005. You might have to perform minor adjustments to parameter sizes and such when you download and install the current version of the application from Giovanni's Web site.

 

In conclusion, I hope this downloadable code will help you fight, in your own "battleground," the idea that System i is an old-fashioned, antique, good-for-nothing server!

 

Other Procedures

For the email sending stuff to work, quite a few other procedures are necessary. They are presented here, divided into three groups: email address-related procedures (service program ADDRESS), Mime & Mail sending procedures (service program MMAILSND), and miscellaneous procedures (service program MISC).

Email Address-Related Procedures

RtvGrpAddrs

This procedure retrieves the email addresses that belong to the group specified in parameter P_Group. It can return up to 10 email addresses and email names in parameters P_Emails and P_Names, respectively. The addresses are retrieved from file LFEGRP01, a logical file over PFEGRP (E-Mail Groups file). The names are retrieved via procedure RtvEMailName, explained next. Even though the procedure's arrays return only 10 addresses at a time, there is a way to read a group with more than 10 members: if parameter P_Email is filled, the group's file will be read from that address on.

RtvEMailName

This procedure returns the name associated with the email address passed in parameter P_Email. It can be either the name found for the email address in the LFEMAIL02 file or a logical file over PFEMAIL (E-Mail Addresses file), or it can be composed from the left part (before the @ sign) of the P_Email parameter.

RtvEMailAddr

This procedure retrieves the email address associated with the name passed in parameter P_Name. If the parameter does not contain an Internet email address already, it is looked up in the email addresses file via P_Name, and, if it is not found, RtvSmtpAddr procedure (explained next) is used to find it in the system's directory.

RtvSmtpAddr

This procedure retrieves the email address from the system directory for the specified user ID. It was created by Carsten Flensburg and published in the Think400 site. I've converted it to free-format and performed minor changes. Here's the author's description of the procedure:

"Searches system directory based directory on input search criteria(s) and returns the requested user information for the found entries.

 

Sequence of events:

1. The API input parameters are initialized

2. The search directory API is called

3. If an error occurs while calling the API or no entry is found blanks are returned to the caller

4. If an entry is found the requested SMTP-address is retrieved, formatted and returned to the caller

 

Parameters:

P_User (INPUT) - User-id of the directory entry searched. Determined by the presence of the second parameter this can be both a user profile name and the first part of the system directory entry user identifier. The special value *CURRENT will be replaced by the job's current user profile name.

 

P_Addr (INPUT) - The address qualifier of the directory entry searched.

 

Return-value (OUTPUT) - The formatted SMTP-address of the system directory entry specified by the input parameter(s). If no matching entry was found or an error occurred blanks are returned to the caller.

InternalAddr

This is a rather useful yet simple procedure. It returns *On if the email address passed in the P_Address parameter belongs to the company and *Off otherwise. The check is performed based on the email domain name of the company, defined in data area DaMailDmn. Function ScanForString is used to search for the domain in the P_Address parameter.

Mime & Mail Sending Procedures

MMailSender

This procedure invokes all the necessary Mime & Mail APIs to create and send an email message, based on the following input parameters:

  • P_Error--Error indicator; '2' means error
  • P_SName--Sender's name
  • P_SEmail--Sender's email address
  • ToNameArr--Array containing the email names of the message's destinations
  • ToAddrArr--Array containing the email addresses of the message's destinations
  • ToDistArr--Array containing the type of destination (To, Cc, or Bcc) for each entry of the previous arrays
  • P_ReplyTo--Reply To address
  • P_MailSubject--Subject
  • P_Impo--Importance
  • P_Prio--Priority
  • FromFNameA--Array containing IFS file names to attach (spool files, physical files, and other IFS files)
  • P_SplName--Array containing the names of the spool files to be sent as attachments to the message
  • P_ImbAtt--Array containing the treatment of each of the previous array's entries

Since this procedure uses Mime & Mail APIs, please refer to the application's documentation

for more information. However, there are some details worth mentioning:

  • The DaMimeLck data area is used to prevent a problem with the sending process--some sort of temporary file duplication that causes messages to get mixed up. It's possible that the current Mime & Mail version no longer requires this "trick" to work properly, but the one that we have installed does.
  • It is assumed, in this procedure, that the spool file that is supposed to be attached to the message is the last generated.

Miscellaneous Procedures

ExecCmd

This very handy procedure is used to replace the call to the QCMDEXC API. It was created by the bright guys of the Free RPG Tools Web site, and it consists of the invocation of the QCAPCMD API with a programmer-friendly interface. The main advantage over QCMDEXC is that the command is validated before execution, thus ensuring the call won't end in error. If there is an error (usually CPF something), it is returned in ApiError.ApiMsgID. I've initialized some variables early in the code to prevent a bug that occurred when the procedure was invoked multiple times within the same program.

 

ScanForString

This simple yet extremely useful procedure returns *On if the value passed in the ScanString parameter is found in the Text parameter. It can also convert both parameters to uppercase, a kind of "ignore case search" if the CvtUpC parameter contains Y, and return the position in the Text parameter, where the ScanString parameter begins, if a receiving variable is provided for the fourth parameter (P_Position).

 

CvtLwcUpc

Another simple yet extremely useful procedure. Using the %XLATE BIF and the W_Up and W_Lo work variables, it converts the TextLower parameter to uppercase. Note that the content of these variables must be adjusted to use your language's special characters in uppercase and lowercase, respectively.

 

ProductionSys

If you have more than one System i, you're probably familiar with those annoying situations in which, for some reason, someone does something in the production system, mistakenly thinking it's the development or test system. This procedure returns *On if the data area DASYSTYPE is set to P (Production). We use it in the email procedures to avoid having test messages get sent outside the company.

Configuration

The following explanation was adapted from an article written by Steve Miall, on October 4, 2000, from company Genesisv:

 

Requirements

 

1. OS/400 release 4.2 or higher

 

2. An SMTP gateway somewhere in your network

 

Setup

 

1. Get the IP address of the email (SMTP) gateway. This is either a PC or network server on your network, or it is the server at your local ISP.

 

2. Make sure this SMTP gateway is in your System i hosts table:

   ADDTCPHTE INTNETADR('192.168.1.50') HOSTNAME('SMTP')

 

   If the Internet address is already in the table, make a note of its host table name and use it from here on instead of "SMTP."

 

3. Change the SMTP attributes:

   CHGSMTPA AUTOSTART(*YES) MAILROUTER('SMTP') FIREWALL(*YES)

 

4. Add your email users to the SMTP alias names system table via CFGTCPSMTP and option 1. These are the users you want to be able to send emails. This is necessary for the email to have a decent-looking "from" name and for the email replies to get to a real email account.

 

If you don't add an alias table, your email will be sent from This email address is being protected from spambots. You need JavaScript enabled to view it., where the USER and ADDR correspond to your user ID and address, and DOMAIN.COM is the domain name of the System i.

 

Use WRKDIRE *ALL to see the user names registered in the directory. Using F19, you'll be able to specify the SMTP use ID and domain to use. I highly recommend that you fill these parameters for all your users, since procedure RtvSmtpAddr will use this information.

 

To review or change the domain name of the system, use the CHGTCPDMN command (prompt command with F4).

  

The alias table consists of user ID, address, SMTP user ID, and SMTP domain. For example, this is my entry:

 

USERID MYUSER

ADDRESS GVUK

SMTP user name support

SMTP Domain GENESISV.COM

With this entry, my mail is sent from This email address is being protected from spambots. You need JavaScript enabled to view it.. But without it, it is sent from This email address is being protected from spambots. You need JavaScript enabled to view it.:

 

MYUSER=User

GVUK=Address

GVUK=Host name

GENESISV.COM=Domain

 

5. Add a generic user in the directory to route email. Either use WRKDIRE and take the add option, or use the following:

 

   ADDDIRE (INTERNET SMTPRTE) USRD('Internet generic user') +

     SYSNAME(TCPIP) NETUSRID(*USRID)                     +

     MSFSRVLVL(*USRIDX) PREFADR(NETUSRID *IBM ATCONTXT)

 

6. Add a distribution queue:

 

   ADDDSTQ DSTQ(QSMTPQ) RMTLOCNAME(TCPIP) DSTQTYPE(*RPDS)

 

7. Add a routing table entry, via CFGDSTSRV then option 2:

 

                           Add Routing Table Entry                        

Type choices, press Enter. (At least one queue name is required.)          

System name/Group . .   TCPIP                                            

Description . . . . .   TCP/IP Routing                                  

Service level:                                                            

   Fast:                                                                  

     Queue name . . . .   QSMTPQ           Distribution queue name        

     Maximum hops . . .   *DFT             Number of hops, *DFT          

   Status:                                                                

     Queue name . . . .   QSMTPQ                                          

     Maximum hops . . .   *DFT                                            

   Data high:                                                              

     Queue name . . . .   QSMTPQ                                          

     Maximum hops . . .   *DFT                                            

   Data low:                                                              

     Queue name . . . .   QSMTPQ                                          

     Maximum hops . . .   *DFT                                            

F3=Exit     F12=Cancel                                                    

 

8. Change the distribution attributes:

 

   CHGDSTA KEEPRCP(*BCC) USEMSFLCL(*NO) SMTPRTE(INTERNET SMTPRTE)

 

9. There can be an authority problem, which can cause the MSF facility to die. This will cause all emailing and all QSNADS message-sending to stop until MSF is restarted (ENDMSF and STRMSF). To get around it, grant the following authorities:

 

   GRTOBJAUT OBJ(QSYS/QZMFARSV) OBJTYPE(*PGM) USER(QTCP) AUT(*USE)

   GRTOBJAUT OBJ(QSYS/QZMFARSV) OBJTYPE(*PGM) USER(QMSF) AUT(*USE)

   GRTOBJAUT OBJ(QSYS/QZMFASCR) OBJTYPE(*PGM) USER(QTCP) AUT(*USE)

   GRTOBJAUT OBJ(QSYS/QZMFASCR) OBJTYPE(*PGM) USER(QMSF) AUT(*USE)

   GRTOBJAUT OBJ(QSYS/QZMFACHG) OBJTYPE(*PGM) USER(QTCP) AUT(*USE)

   GRTOBJAUT OBJ(QSYS/QZMFACHG) OBJTYPE(*PGM) USER(QMSF) AUT(*USE)

 GRTOBJAUT OBJ(QSYS/QZMFACRT) OBJTYPE(*PGM) USER(QTCP) AUT(*USE)

   GRTOBJAUT OBJ(QSYS/QZMFACRT) OBJTYPE(*PGM) USER(QMSF) AUT(*USE)

 

10. Start the server:

   STRTCPSVR *SMTP

 

11. Test to see if it sends:

   SNDDST TYPE(*LMSG) TOUSRID((INTERNET SMTPRTE)) +

           DSTD('E-mail')                         +

           TOINTNET((This email address is being protected from spambots. You need JavaScript enabled to view it.'))           +

           SUBJECT('This is the subject')         +

           LONGMSG('This is the message')

 

Then, go to your mailbox and see if you have received the message.

 

12. To send from a QDLS document, use this code:

 

   SNDDST TYPE(*FILE) TOUSRID((INTERNET SMTPRTE)) +

         TOINTNET((This email address is being protected from spambots. You need JavaScript enabled to view it.'))        +

         DSTD('Email')                           +

         MSG('This is a test')                  +

         DOC(document.TXT) FLR(folder)

 

The subject will be the document description; the SUBJECT parameter is accepted but ignored. The MSG parameter is optional. You cannot have a long message with an attached document, and you cannot attach two documents.

 

13. If you have problems and need additional information on setting up TCP/IP and other related issues, try going to www.easy400.net/tcpcfgs.

 

Rafael Victoria-Pereira

Rafael Victória-Pereira has more than 20 years of IBM i experience as a programmer, analyst, and manager. Over that period, he has been an active voice in the IBM i community, encouraging and helping programmers transition to ILE and free-format RPG. Rafael has written more than 100 technical articles about topics ranging from interfaces (the topic for his first book, Flexible Input, Dazzling Output with IBM i) to modern RPG and SQL in his popular RPG Academy and SQL 101 series on mcpressonline.com and in his books Evolve Your RPG Coding and SQL for IBM i: A Database Modernization Guide. Rafael writes in an easy-to-read, practical style that is highly popular with his audience of IBM technology professionals.

Rafael is the Deputy IT Director - Infrastructures and Services at the Luis Simões Group in Portugal. His areas of expertise include programming in the IBM i native languages (RPG, CL, and DB2 SQL) and in "modern" programming languages, such as Java, C#, and Python, as well as project management and consultancy.


MC Press books written by Rafael Victória-Pereira available now on the MC Press Bookstore.

Evolve Your RPG Coding: Move from OPM to ILE...and Beyond Evolve Your RPG Coding: Move from OPM to ILE...and Beyond
Transition to modern RPG programming with this step-by-step guide through ILE and free-format RPG, SQL, and modernization techniques.
List Price $79.95

Now On Sale

Flexible Input, Dazzling Output with IBM i Flexible Input, Dazzling Output with IBM i
Uncover easier, more flexible ways to get data into your system, plus some methods for exporting and presenting the vital business data it contains.
List Price $79.95

Now On Sale

SQL for IBM i: A Database Modernization Guide SQL for IBM i: A Database Modernization Guide
Learn how to use SQL’s capabilities to modernize and enhance your IBM i database.
List Price $79.95

Now On Sale

BLOG COMMENTS POWERED BY DISQUS

LATEST COMMENTS

Support MC Press Online

$0.00 Raised:
$

Book Reviews

Resource Center

  • SB Profound WC 5536 Have you been wondering about Node.js? Our free Node.js Webinar Series takes you from total beginner to creating a fully-functional IBM i Node.js business application. You can find Part 1 here. In Part 2 of our free Node.js Webinar Series, Brian May teaches you the different tooling options available for writing code, debugging, and using Git for version control. Brian will briefly discuss the different tools available, and demonstrate his preferred setup for Node development on IBM i or any platform. Attend this webinar to learn:

  • SB Profound WP 5539More than ever, there is a demand for IT to deliver innovation. Your IBM i has been an essential part of your business operations for years. However, your organization may struggle to maintain the current system and implement new projects. The thousands of customers we've worked with and surveyed state that expectations regarding the digital footprint and vision of the company are not aligned with the current IT environment.

  • SB HelpSystems ROBOT Generic IBM announced the E1080 servers using the latest Power10 processor in September 2021. The most powerful processor from IBM to date, Power10 is designed to handle the demands of doing business in today’s high-tech atmosphere, including running cloud applications, supporting big data, and managing AI workloads. But what does Power10 mean for your data center? In this recorded webinar, IBMers Dan Sundt and Dylan Boday join IBM Power Champion Tom Huntington for a discussion on why Power10 technology is the right strategic investment if you run IBM i, AIX, or Linux. In this action-packed hour, Tom will share trends from the IBM i and AIX user communities while Dan and Dylan dive into the tech specs for key hardware, including:

  • Magic MarkTRY the one package that solves all your document design and printing challenges on all your platforms. Produce bar code labels, electronic forms, ad hoc reports, and RFID tags – without programming! MarkMagic is the only document design and print solution that combines report writing, WYSIWYG label and forms design, and conditional printing in one integrated product. Make sure your data survives when catastrophe hits. Request your trial now!  Request Now.

  • SB HelpSystems ROBOT GenericForms of ransomware has been around for over 30 years, and with more and more organizations suffering attacks each year, it continues to endure. What has made ransomware such a durable threat and what is the best way to combat it? In order to prevent ransomware, organizations must first understand how it works.

  • SB HelpSystems ROBOT GenericIT security is a top priority for businesses around the world, but most IBM i pros don’t know where to begin—and most cybersecurity experts don’t know IBM i. In this session, Robin Tatam explores the business impact of lax IBM i security, the top vulnerabilities putting IBM i at risk, and the steps you can take to protect your organization. If you’re looking to avoid unexpected downtime or corrupted data, you don’t want to miss this session.

  • SB HelpSystems ROBOT GenericCan you trust all of your users all of the time? A typical end user receives 16 malicious emails each month, but only 17 percent of these phishing campaigns are reported to IT. Once an attack is underway, most organizations won’t discover the breach until six months later. A staggering amount of damage can occur in that time. Despite these risks, 93 percent of organizations are leaving their IBM i systems vulnerable to cybercrime. In this on-demand webinar, IBM i security experts Robin Tatam and Sandi Moore will reveal:

  • FORTRA Disaster protection is vital to every business. Yet, it often consists of patched together procedures that are prone to error. From automatic backups to data encryption to media management, Robot automates the routine (yet often complex) tasks of iSeries backup and recovery, saving you time and money and making the process safer and more reliable. Automate your backups with the Robot Backup and Recovery Solution. Key features include:

  • FORTRAManaging messages on your IBM i can be more than a full-time job if you have to do it manually. Messages need a response and resources must be monitored—often over multiple systems and across platforms. How can you be sure you won’t miss important system events? Automate your message center with the Robot Message Management Solution. Key features include:

  • FORTRAThe thought of printing, distributing, and storing iSeries reports manually may reduce you to tears. Paper and labor costs associated with report generation can spiral out of control. Mountains of paper threaten to swamp your files. Robot automates report bursting, distribution, bundling, and archiving, and offers secure, selective online report viewing. Manage your reports with the Robot Report Management Solution. Key features include:

  • FORTRAFor over 30 years, Robot has been a leader in systems management for IBM i. With batch job creation and scheduling at its core, the Robot Job Scheduling Solution reduces the opportunity for human error and helps you maintain service levels, automating even the biggest, most complex runbooks. Manage your job schedule with the Robot Job Scheduling Solution. Key features include:

  • LANSA Business users want new applications now. Market and regulatory pressures require faster application updates and delivery into production. Your IBM i developers may be approaching retirement, and you see no sure way to fill their positions with experienced developers. In addition, you may be caught between maintaining your existing applications and the uncertainty of moving to something new.

  • LANSAWhen it comes to creating your business applications, there are hundreds of coding platforms and programming languages to choose from. These options range from very complex traditional programming languages to Low-Code platforms where sometimes no traditional coding experience is needed. Download our whitepaper, The Power of Writing Code in a Low-Code Solution, and:

  • LANSASupply Chain is becoming increasingly complex and unpredictable. From raw materials for manufacturing to food supply chains, the journey from source to production to delivery to consumers is marred with inefficiencies, manual processes, shortages, recalls, counterfeits, and scandals. In this webinar, we discuss how:

  • The MC Resource Centers bring you the widest selection of white papers, trial software, and on-demand webcasts for you to choose from. >> Review the list of White Papers, Trial Software or On-Demand Webcast at the MC Press Resource Center. >> Add the items to yru Cart and complet he checkout process and submit

  • Profound Logic Have you been wondering about Node.js? Our free Node.js Webinar Series takes you from total beginner to creating a fully-functional IBM i Node.js business application.

  • SB Profound WC 5536Join us for this hour-long webcast that will explore:

  • Fortra IT managers hoping to find new IBM i talent are discovering that the pool of experienced RPG programmers and operators or administrators with intimate knowledge of the operating system and the applications that run on it is small. This begs the question: How will you manage the platform that supports such a big part of your business? This guide offers strategies and software suggestions to help you plan IT staffing and resources and smooth the transition after your AS/400 talent retires. Read on to learn: