Convert a job's spool files to PDF and email

This program will process specified spool files from a user’s job, convert them to PDF format, then email them to the user.

Command features:

  • Supports all spool files or a specific spool file name for a given job. Converting a specific spool file number is not implemented.
  • Supports up to 20 attachments per email. (This limitation documented in Brad Stone’s MAILTOOL. I am unsure if this is also a limit in the AS/400 SMTP server.)
  • Generates as many emails as needed, denoted by “part 2”, “part 3”, etc. if more than 20 attachments are found.
  • By default, excludes job logs and debugging output from being converted, but can be included by specifying incjoblog(*yes) or incdebug(*yes).

Requirements for use:

  • A tool to convert spool files to PDF. This program uses Brad Stone’s Spooled File Tools.
  • A tool to send email, with attachments. This program uses Brad Stone’s eMail Tool.
  • The SMTP server must be configured and enabled on the AS/400. If you use another mail transfer agent (MTA), such as a Microsoft Exchange server, the SMTP server must be configured to relay messages to the MTA server.
  • This program was written on an AS/400 running OS/400 version V4R5. The program should be forward compatible to versions V5R1 and V5R2.

This program shows the proper way to process output generated by OS/400’s list APIs.

On my production system, these programs reside in library TOOLS, with the source residing in library TOOLSSRC. Change the library names to those applicable to your system.

Command

This is the front end command. Adjust the dft() keywords for the parameters fromem and fromname to match your AS/400’s email address and screen name, respectively.


cmd prompt('Convert job to PDF and Email')
parm kwd(to) type(*char) case(*mixed) min(1) max(5) len(200) +
   prompt('To email address' 3)
parm kwd(file) type(*name) len(10) prompt('Spool file' 1) dft(*all) +
   spcval((*all))
parm kwd(job) type(qual3) prompt('Job' 2) sngval((*)) dft(*)
parm kwd(subject) type(*char) len(100) case(*mixed) +
   prompt('Subject' 4)
parm kwd(body) type(*char) len(500) case(*mixed) prompt('Body' 5)
parm kwd(fromem) type(*char) case(*mixed) len(200) +
   dft('youras400@your.domain.com') +
   prompt('From email address' 6) pmtctl(*pmtrqs)
parm kwd(fromname) type(*char) case(*mixed) len(200) +
   dft('Your AS/400') +
   prompt('From email name' 7) pmtctl(*pmtrqs)
parm kwd(incjoblog) type(*char) len(4) dft(*no) values(*yes *no) +
   rstd(*yes) prompt('Include job log, if any' 8) pmtctl(*pmtrqs)
parm kwd(incdebug) type(*char) len(4) dft(*no) values(*yes *no) +
   rstd(*yes) prompt('Include debug, if any' 9) pmtctl(*pmtrqs)

qual3: qual type(*name) len(10) dft(*)
      qual type(*name) len(10) prompt('User')
      qual type(*char) len(6) range(000000 999999) prompt('Number')

Command processing program

Command processing program for the CVTPDFEM command. In the “Your processing here” section of the program, you must change the command to convert the spooled file to PDF if you’re not using Brad Stone’s SPLTOOL. Also, you must adjust the command in the mailit subroutine if you’re not using Brad Stone’s MAILTOOL.


     h dftactgrp(*no) actgrp(*caller)

      /copy toolssrc/apiibm,apisplf
      /copy toolssrc/apiibm,apimisc
      /copy toolssrc/formats,openlist
      /copy toolssrc/formats,errorcode
      /copy toolssrc/apiibm,qcmdexc

      *-- Parameters
     d into            ds
     d  intonbr                       5i 0
     d  intoaddr1                   200a
     d  intoaddr2                   200a
     d  intoaddr3                   200a
     d  intoaddr4                   200a
     d  intoaddr5                   200a
     d infile          s             10a
     d injob           ds            26
     d  injobname                    10a
     d  injobuser                    10a
     d  injobnbr                      6a
     d insubject       s            100a
     d inbody          s            500a
     d infromaddr      s            200a
     d infromname      s            200a
     d inincjoblog     s              4a
     d inincdebug      s              4a

      *-- Constants
     d ALL             c                   '*ALL'
     d ASCENDING       c                   '1'
     d ATTMAX          c                   20
     d BUILDSYNCH      c                   -1
     d COMPLETE        c                   'C'
     d DEBUGLOG        c                   'QPPGMDMP  '
     d ENTRIESPERPASS  c                   200
     d HEXZERO         c                   x'00'
     d JOBLOG          c                   'QPJOBLOG  '
     d NO              c                   '*NO '
     d PARTIAL         c                   'P'
     d QUOTE           c                   ''''
     d SIGNEDBINARY    c                   0
     d STMFLOC         c                   '/tmppdf/'
     d YES             c                   '*YES'

      *-- Variables
     d addresses       s                   inz like(intoaddr1) dim(5)
     d command         s           2000a   inz varying
     d currentrec      s              5  0 inz
     d filename        s             40a   inz
     d listhandle      s              4a   inz
     d nextlistrec     s             10i 0 inz
     d part            s              5  0 inz
     d pdflist         s            100a   inz dim(ATTMAX)
     d pdfnbr          s              5  0 inz
     d pdftomail       s              5  0 inz
     d processed       s             11  0 inz
     d receiver        s          32767a   inz
     d recstart        s              5  0 inz
     d recstoprocess   s             11  0 inz
     d x               s              5  0 inz

     d filterinfods    ds
     d  finumusers                   10i 0 inz
     d  fi1user                      10a   inz
     d  fi1userrsv                    2a   inz
     d  finumoutq                    10i 0 inz
     d  fi1outqname                  10a   inz
     d  fi1outqlib                   10a   inz
     d  fiformtype                   10a   inz
     d  fiuserdata                   10a   inz
     d  finumsts                     10i 0 inz
     d  fi1sts                       10a   inz
     d  fi1stsrsv                     2a   inz
     d  finumdev                     10i 0 inz
     d  fi1devname                   10a   inz
     d  fi1devnamersv                 2a   inz

     d sortinfods      ds
     d  sinumkey                     10i 0 inz
     d  si1kfstart                   10i 0 inz
     d  si1kflen                     10i 0 inz
     d  si1kftype                     5i 0 inz
     d  si1sort                       1a   inz
     d  si1reserv                     1a   inz

      *-- Lists
     c     *entry        plist
     c                   parm                    into
     c                   parm                    infile
     c                   parm                    injob
     c                   parm                    insubject
     c                   parm                    inbody
     c                   parm                    infromaddr
     c                   parm                    infromname
     c                   parm                    inincjoblog
     c                   parm                    inincdebug

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

     c                   eval      pdfnbr = 1

     c                   eval      part = 1

     c                   eval      addresses(1) = intoaddr1
     c                   eval      addresses(2) = intoaddr2
     c                   eval      addresses(3) = intoaddr3
     c                   eval      addresses(4) = intoaddr4
     c                   eval      addresses(5) = intoaddr5

      *-- Set up filtering: everything
     c                   eval      fiformtype = ALL
     c                   eval      fiuserdata = ALL
     c                   eval      finumsts = 1
     c                   eval      fi1sts = ALL
     c                   eval      finumdev = 1
     c                   eval      fi1devname = ALL
     c                   eval      finumusers = 1
     c                   eval      fi1user = ALL
     c                   eval      finumoutq = 1
     c                   eval      fi1outqname = ALL
     c                   eval      fi1outqlib = *blanks

      *-- Set up sorting: sort by spool file number
     c                   eval      sinumkey = 1
     c                   eval      si1kfstart = 37
     c                   eval      si1kflen = 4
     c                   eval      si1kftype = SIGNEDBINARY
     c                   eval      si1sort = ASCENDING
     c                   eval      si1reserv = HEXZERO

      *-- Open a list of spool files for the specified job, sorted by the
      *-- spool file number.
     c                   clear                   receiver
     c                   clear                   openlist
     c                   clear                   errc0100
     c                   callp(e)  OpenListSPLF( receiver : %len(receiver) :
     c                             openlist : BUILDSYNCH : sortinfods :
     c                             filterinfods : injob : 'OSPL0100' :
     c                             errc0100 )

      *-- Read through list, building a PDF for each file found (excluding
      *-- joblogs and program dumps if necessary).
     c                   eval      listhandle = olreqhdl
     c                   eval      recstoprocess = oltotrec
     c                   eval      nextlistrec = 1
     c                   eval      processed = 0

      *-- Odds are the job will not have more spool files than is in the
      *-- receiver variable, but process correctly anyway.
     c                   dow       processed < recstoprocess
     c                   if        not ( olinfcpl = PARTIAL or
     c                             olinfcpl = COMPLETE )
     c                   leave
     c                   endif
     c                   eval      nextlistrec = nextlistrec + olrecret

      *-- Read through the receiver variable's list, map to OSPL0100 data
      *-- structure and process
     c                   for       currentrec = 1 to olrecret
     c                   eval      recstart = (( currentrec - 1 ) * olrcdlen )
     c                             + 1
     c                   eval      ospl0100 = %subst( receiver : recstart :
     c                             olrcdlen )

      *-- Your processing here
     c                   if        pdfnbr <= %elem(pdflist) and
     c                             ospl01sts <> '*FINISHED ' and
     c                              ((infile = ALL and
     c                               ((ospl01splfnam = JOBLOG and
     c                                 inincjoblog = YES) or
     c                                (ospl01splfnam = DEBUGLOG and
     c                                 inincdebug = YES))) or
     c                              (infile = ALL and
     c                               ospl01splfnam <> DEBUGLOG and
     c                               ospl01splfnam <> JOBLOG) or
     c                              (infile = ospl01splfnam and
     c                               infile <> DEBUGLOG and infile <> JOBLOG) or
     c                              (infile = ospl01splfnam and
     c                               infile = DEBUGLOG and inincdebug = YES) or
     c                              (infile = ospl01splfnam and
     c                               infile = JOBLOG and inincjoblog = YES))
     c                   eval      filename = %trim(ospl01splfnam) + '.' +
     c                             %trim(ospl01jobnbr) + '.' +
     c                             %trim(%editc(ospl01splfnbr:'Z'))
     c                   eval      pdflist(pdfnbr) = filename
     c                   eval      command = 'spl2stmfb +
     c                             outq(' + %trim(ospl01outqlib)+'/'+
     c                             %trim(ospl01outq) + ') +
     c                             file(' + %trim(ospl01splfnam) + ') +
     c                             user(' + %trim(ospl01usernam) + ') +
     c                             job(' + %trim(ospl01jobnam) + ') +
     c                             jobnum(' + %trim(ospl01jobnbr) + ') +
     c                             splnum(' + %trim(%editc(ospl01splfnbr:'Z')) +
     c                             ') +
     c                             usrd(' + QUOTE + %trim(ospl01usrdta) +
     c                             QUOTE + ') +
     c                             frmt(' + %trim(ospl01frmtyp) + ') +
     c                             dir(' + QUOTE + %trim(STMFLOC) + QUOTE +
     c                             ') +
     c                             ext(pdf) +
     c                             onlyf(*yes) +
     c                             fname(' + QUOTE + %trim(filename) + QUOTE +
     c                             ') +
     c                             rplf(*yes)'
     c                   callp(e)  execcmd(command : %len(command))
     c                   eval      pdfnbr = pdfnbr + 1
     c                   endif
     c                   if        pdfnbr > %elem(pdflist)
     c                   exsr      mailit
     c                   endif
      *-- Your processing here

     c                   eval      processed = processed + 1
     c                   endfor

      *-- Get more entries if necessary (more than the receiver could hold)
     c                   if        olinfcpl = PARTIAL or
     c                             ( olinfcpl = COMPLETE and
     c                             processed < recstoprocess )
     c                   clear                   errc0100
     c                   callp(e)  GetListEntries( receiver : %len(receiver) :
     c                             listhandle : openlist : ENTRIESPERPASS :
     c                             nextlistrec : errc0100 )
     c                   endif
     c                   enddo

      *-- Email the PDFs.
     c                   exsr      mailit

     c                   eval      *inlr = *on
     c                   return

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

     c     mailit        begsr
     c                   eval      pdftomail = 0
     c                   for       x = 1 to %elem(pdflist)
     c                   if        pdflist(x) <> *blanks
     c                   eval      pdftomail = pdftomail + 1
     c                   endif
     c                   endfor

     c                   if        pdftomail > 0
     c                   eval      command = 'mailtool +
     c                             toaddr('
     c                   for       x = 1 to intonbr
     c                   eval      command = command + QUOTE +
     c                             %trim(addresses(x)) + QUOTE + ' '
     c                   endfor
     c                   eval      command = command + ') +
     c                             fromaddr(' + QUOTE + %trim(infromaddr) +
     c                             QUOTE + ') +
     c                             fromname(' + QUOTE + %trim(infromname) +
     c                             QUOTE + ') '
     c                   if        part > 1
     c                   eval      command = command + 'subject(' +
     c                             QUOTE + %trim(insubject) + ' - part ' +
     c                             %trim(%editc(part:'Z')) + QUOTE + ') '
     c                   else
     c                   eval      command = command + 'subject(' +
     c                             QUOTE + %trim(insubject) + QUOTE + ') '
     c                   endif
     c                   eval      command = command +
     c                             'message(' + QUOTE + %trim(inbody) + QUOTE +
     c                             ') +
     c                             attach('
     c                   for       x = 1 to %elem(pdflist)
     c                   if        pdflist(x) <> *blanks
     c                   eval      command = command + QUOTE + %trim(STMFLOC) +
     c                             %trim(pdflist(x)) + '.pdf' + QUOTE + ' '
     c                   endif
     c                   endfor
     c                   eval      command = command + ')'
     c                   callp(e)  execcmd(command : %len(command))
     c                   endif

     c                   clear                   pdflist
     c                   eval      pdfnbr = 1
     c                   eval      part = part + 1
     c                   endsr

APIs used

List APIs


      *-------------------------------------------------------------------------
      *-- Close List API
      *-- (QGYCLST)

      *-- Version V4R3

     d CloseList       pr                  extpgm('QGY/QGYCLST')
     d  ReqHandle                     4a   const                                Request handle
     d  ErrorCode                 32767a   options(*varsize)                    Error Code

      *-- REQUEST HANDLE
      *-- The handle to the list that is to be closed. The handle is generated
      *-- by one of the following APIs:
      *--  * Open List of Job Log Messages (QGYOLJBL)
      *--  * Open List of Messages (QGYOLMSG)
      *--  * Open List of Objects (QGYOLOBJ)
      *--  * Open List of Printers (QGYRPRTL)
      *--  * Open List of Spooled Files (QGYOLSPL)

      *-- ERROR CODE
      *-- The structure in which to return error information.

      *-------------------------------------------------------------------------
      *-- Get List Entries
      *-- (QGYGTLE)

      *-- Version V4R3

     d GetListEntries  pr                  extpgm('QGY/QGYGTLE')
     d  Receiver                  32767a   options(*varsize)
     d  ReceiverLen                  10i 0 const
     d  ReqHandle                     4a   const
     d  ListInfo                     80a
     d  NbrRecRtn                    10i 0 const
     d  StartRcd                     10i 0 const
     d  ErrorCode                 32767a   options(*varsize)

      *-- RECEIVER VARIABLE
      *-- The receiver variable that receives the information requested. You
      *-- can specify the size of the area to be smaller than the format
      *-- requested as long as you specify the length parameter correctly. As
      *-- a result, the API returns only the data that the area can hold.

      *-- LENGTH OF RECEIVER VARIABLE
      *-- The length of the receiver variable provided. The length of receiver
      *-- variable parameter may be specified up to the size of the receiver
      *-- variable specified in the user program. If the length of receiver
      *-- variable parameter specified is larger than the allocated size of the
      *-- receiver variable specified in the user programs, the results are
      *-- not predictable. The minimum length is 8 bytes.

      *-- REQUEST HANDLE
      *-- The handle of the request. The value of this determines from which
      *-- user space to retrieve the information.

      *-- LIST INFORMATION
      *-- Information about the list from which entries are being returned.

      *-- NUMBER OF RECORDS TO RETURN
      *-- The number of records in the list (starting with the record indicated
      *-- in the starting record parameter) to take from the user space and put
      *-- into the receiver variable. This value must be greater than or equal
      *-- to zero.
      *-- If the value zero is specified, then only the list information is
      *-- returned and no actual list entries are returned.

      *-- STARTING RECORD
      *-- The entry in the list that will be the first entry to by put into
      *-- the receiver variable. The value must be greater than zero or one of
      *-- the special values of 0 or -1.
      *-- The special value of 0 indicates that the list information should be
      *-- returned to the caller immediately. The special value 0 is only
      *-- allowed when the number of records to return parameter is zero.
      *-- The special value of -1 indicates that the whole list should be
      *-- built before the list information is returned to the caller.

      *-- ERROR CODE
      *-- The structure in which to return error information.


Spooled file APIs


      *-------------------------------------------------------------------------
      *-- Open List of Spooled Files
      *-- (QGYOLSPL)

      *-- Version V4R3

     d OpenListSPLF    pr                  extpgm('QGY/QGYOLSPL')
     d  Receiver                  32767a   options(*varsize)
     d  ReceiverLen                  10i 0 const
     d  ListInfo                     80a
     d  NumRecsReturn                10i 0 const
     d  SortInfo                  32767a   const options(*varsize)
     d  FilterInfo                32767a   const options(*varsize)
     d  QualJob                      26a   const
     d  Format                        8a   const
     d  ErrorCode                 32767a   options(*varsize)

      *-- Format OSPL0100 - Basic information

     d ospl0100        ds
     d  ospl01splfnam                10a                                        Spool file name
     d  ospl01jobnam                 10a                                        Job name
     d  ospl01usernam                10a                                        User name
     d  ospl01jobnbr                  6a                                        Job number
     d  ospl01splfnbr                10i 0                                      Spool file number
     d  ospl01totpag                 10i 0                                      Total pages
     d  ospl01curpag                 10i 0                                      Current page
     d  ospl01copprt                 10i 0                                      Copies left to print
     d  ospl01outq                   10a                                        Output queue
     d  ospl01outqlib                10a                                        Output queue library
     d  ospl01usrdta                 10a                                        User specified data
     d  ospl01sts                    10a                                        Status
     d  ospl01frmtyp                 10a                                        Form type
     d  ospl01pty                     2a                                        Priority
     d  ospl01intjobi                16a                                        Internal job ident
     d  ospl01intspli                16a                                        Internal splf ident
     d  ospl01devtyp                 10a                                        Device type
     d  ospl01reserv1                14a                                        Reserved

Execute command API


     d ExecCmd         pr                  extpgm('QCMDEXC')
     d  CmdString                  3000a   const options(*varsize)
     d  CmdStringLen                 15p 5 const
     d  CmdOption                     3a   const options(*nopass)

Error code formats


      *-- These formats are for return error codes from API calls. Most of the
      *-- time, the error code format used will be ERRC0100.

     d errc0100        ds
     d  errc01bytpro                 10i 0                                      Bytes provided
     d  errc01bytava                 10i 0                                      Bytes available
     d  errc01excid                   7a                                        Exception ID
     d  errc01resaaa                  1a                                        Reserved
     d  errc01excdta                 40a                                        Exception data

     d errc0200        ds
     d  errc02key                    10i 0                                      Key
     d  errc02bytpro                 10i 0                                      Bytes provided
     d  errc02bytava                 10i 0                                      Bytes available
     d  errc02excid                   7a                                        Exception ID
     d  errc02resaaa                  1a                                        Reserved
     d  errc02ccsid                  10i 0                                      CCSID of data
     d  errc02excoff                 10i 0                                      Exception offset
     d  errc02exclen                 10i 0                                      Exception length
     d  errc02excdta              32767a                                        Exception data

Open list API


      *-- Format of open list information

      *-- As of V4R3.

     d openlist        ds
     d  oltotrec                     10i 0                                      Total records
     d  olrecret                     10i 0                                      Records returned
     d  olreqhdl                      4a                                        Request handle
     d  olrcdlen                     10i 0                                      Record length
     d  olinfcpl                      1a                                        Information complete
     d  oldtcrt                      13a                                        Date/time created
     d  ollststs                      1a                                        List status
     d  olreserv1                     1a                                        Reserved
     d  olleninfret                  10i 0                                      Len of info returned
     d  olfirstrec                   10i 0                                      First rec in rcv var
     d  olreserv2                    40a                                        Reserved

      *-- Field descriptions

      *-- TOTAL RECORDS
      *-- The total number of records available in the list.

      *-- RECORDS RETURNED
      *-- The number of records that are returned in the receiver variable.
      *-- This number is the smallest of the following values:
      *--  * The number of records that will fit into the receiver variable.
      *--  * The number of records in the list.
      *--  * The number of records that are requested.

      *-- REQUEST HANDLE
      *-- The handle of the request that can be used for subsequent requests of
      *-- information from the list. The handle is valid until the Close List
      *-- (QGYCLST) is called to close the list, or until the job ends.

      *-- RECORD LENGTH
      *-- The length of each record of information returned. For variable
      *-- length records, this value is zero. For variable length records, you
      *-- can obtain the length of individual records from the records
      *-- themselves.

      *-- INFORMATION COMPLETE INDICATOR
      *-- WHether all requested information has been suppled. Possible values
      *-- follow:
      *--  C Complete and accurate information. All of the requested records
      *--    have been returned to the receiver variable.
      *--  I Incomplete information. An interruption causes the receiver
      *--    variable to contain incomplete information.
      *--  P Partial and accurate information. Partial information is returned
      *--    when the receiver variable is full and not all of the records
      *--    requested are returned.

      *-- DATE AND TIME CREATED
      *-- The date and time when the list was created. The 13 characters are:
      *--  1    Century, where 0 indicates years 19xx and 1 indicates years
      *--       20xx.
      *--  2-7  The date, in YYMMDD (year, month, day) format.
      *--  8-13 The time of day, in HHMMSS (hours, minutes, seconds) format.

      *-- LIST STATUS INDICATOR
      *-- The status of building the list. Possible values follow:
      *--  0 The building of the list is pending.
      *--  1 The list is in the process of being built.
      *--  2 The list has been completely built.
      *--  3 An error occurred when building the list. The next call to the
      *--    Get List Entries (QGYGTLE) API will cause the error to be signaled
      *--    to the caller of the QGYGTLE API.
      *--  4 The list is primed and reasy to be built. The list will be built
      *--    asynchronously by a server job, but the server job has not
      *--    necessarily started building the list yet.

      *-- LENGTH OF INFORMATION RETURNED
      *-- The size, in bytes, of the information that is returned in the
      *-- receiver variable.

      *-- FIRST RECORD IN RECEIVER VARIABLE
      *-- The number of the first record returned in the receiver variable.


Revision history:

  • Version 1.2, 24 May 2006, Hanna Goodbar.
    Removed command screen image.
    Change parameters for job and debug logs from exclude to include.
  • Version 1.1, 11 Sep 2002, Hanna Goodbar.
    Added command screen image.
  • Version 1.0, 10 Sep 2002, Hanna Goodbar.
    Initial version. Thanks to Brad Stone for creating the shareware tools SPLTOOL and MAILTOOL. Visit his website at http://www.bvstools.com/.