- SMTP Configuration Checklist
- Configuration of the IBM i SMTP Client to Relay Email to Office365 and Gmail
- How To Migrate SMTP on IBM i from *SDD to *SMTP/*SMTPMSF
- Configuring TLS Between IBM i and Remote Mail Router WITHOUT Authentication
- How To Configure the SMTP Client To Use SMTP Authentication with a SMTP Relay
A blog about IBM i (AS/400), MQ and other things developers or Admins need to know.
星期一, 10月 21, 2024
IBM i SMTP
星期四, 11月 09, 2023
2015-06-05 如何監控系統中執行狀態為 MSGW 的工作,並傳送訊息至 MSGQ 或 Email?(Command CHKJOBMSGW -- Check Job Message Wait with QGYOLJOB Open list of jobs API)
如何監控系統中執行狀態為 MSGW 的工作,並傳送訊息至 MSGQ 或 Email?(Command CHKJOBMSGW -- Check Job Message Wait with QGYOLJOB Open list of jobs API)
File : QRPGLESRC
Member: CHKJOBMSGW
Usage : CRTBNDRPG PGM(CHKJOBMSGW)
**
** Program . . : CHKJOBMSGW
** Description : Check job msgw and send msgw info to msgq or mail
** Author . . : Vengoal Chang
** Published . : AS400ePaper
** Date . . . : June 5, 2015
**
**
** Program summary
** ---------------
**
** Work management APIs:
** QGYOLJOB Open list of jobs Lists jobs on the system based on
** the specified selection criteria.
**
** Optionally a sort order for the
** returned jobs can be specified -
** in this case the jobname first
**
** QWVRCSTK Retrieve Call Stack Lists the program call stack for
** the specified job or thread.
** The current invocation level is
** returned first.
**
** Message handling API:
** QMHSNDM Send message Sends a message to the specified
** non-program message queue - here
** an informational message is sent
** to the current user running this
** program.
**
** QMHRTVM Retrieve Message Retrieves the message description
** of a predefined message.
**
** Open list APIs:
** QGYGTLE Get list entries To retrieve open lists entries
** from an already open list the
** QGYGTLE (Get List Entries) API
** is available.
**
** QGYCLST Close list This API closes the previously
** opened list identified by the
** request handle parameter.
** Storage allocated is freed.
**
** MI builtins:
** _MEMMOVE Copy memory Copies a string from one pointer
** specified location to another.
**
** Unix Type - Signal APIs:
** Sleep Suspends program processing for
** the specified number of seconds.
**
**
** Sequence of events:
** 1. The list jobs API input parameters are initialized
**
** 2. The open list of jobs API is called
**
** 3. For each job get key data
** 101 Active job status
** 305 Current user profile
** 1307 Message reply
** 1308 Message key, when active job waiting for a message
** 1309 Message queue name - qualified, when active job waiting for a message
** 1906 Subsystem description name - qualified
**
** List job name and check whether job waiting message or not.
** If job wait for a reply to a message, send message to user
** which run the CHKJOBMSGW program.
**
** 4. The job list resources are cleaned up.
**
** 5. Sleep wakeup interval, then go to step 2. lsit msgw job
**
**
**
** Programmer's notes:
** To retrieve another job's call stack *JOBCTL special authority is
** required.
**
**
** Compile options:
**
** CrtBndPgm Pgm( CHKJOBMSGW )
**
**-- Control spec: -----------------------------------------------------**
H Option( *SrcStmt ) DecEdit( *JobRun ) BndDir( 'QC2LE' )
H DftActGrp(*NO) Debug
**-- System information: -----------------------------------------------**
D PgmSts SDs
D PsPgmNam *Proc
D PsSts 5a Overlay( PgmSts: 11 )
D PsCurJob 10a Overlay( PgmSts: 244 )
D PsUsrPrf 10a Overlay( PgmSts: 254 )
D PsJobNbr 6a Overlay( PgmSts: 264 )
D PsCurUsr 10a Overlay( PgmSts: 358 )
**-- API error data structure: -----------------------------------------**
D ApiError Ds
D AeBytPrv 10i 0 Inz( %Size( ApiError ))
D AeBytAvl 10i 0
D AeExcpId 7a
D 1a
D AeExcpDta 128a
**-- API parameters: ---------------------------------------------------**
D JlRtnRcdNbr s 10i 0 Inz( 1 )
D JlNbrFldRtn s 10i 0 Inz( %Elem( JlKeyFld ))
D JlKeyFld s 10i 0 Dim( 6 )
**-- Job information OLJB0300:
D*JlJobInf0300 Ds 512
D* JbJobId 26a
D* JbJobUsd 10a Overlay( JbJobId: 1 )
D* JbUsrUsd 10a Overlay( JbJobId: *Next )
D* JbNbrUsd 6a Overlay( JbJobId: *Next )
D* JbActSts 4a
D* JbJobTyp 1a
D* JbJobSubTyp 1a
D* JbDtaLen 10i 0
D* 4a
D* JbDta 256a
**-- Job information OLJB0200:
D JlJobInf Ds 512
D JbJobId 26a
D JbJobUsd 10a Overlay( JbJobId: 1 )
D JbUsrUsd 10a Overlay( JbJobId: *Next )
D JbNbrUsd 6a Overlay( JbJobId: *Next )
D JbJobIntId 16a
D JbJobSts 10a
D JbJobTyp 1a
D JbJobSubTyp 1a
D 2a
D JbJobInfoSts 1a
D 3a
D JbDta 256a
**-- Key information:
D JlKeyInf Ds
D KiFldNbrRtn 10i 0
D KiKeyInf 20a Dim( %Elem( JlKeyFld ))
D KiFldInfLen 10i 0 Overlay( KiKeyInf : 1 )
D KiKeyFld 10i 0 Overlay( KiKeyInf : 5 )
D KiDtaTyp 1a Overlay( KiKeyInf : 9 )
D 3a Overlay( KiKeyInf : 10 )
D KiDtaLen 10i 0 Overlay( KiKeyInf : 13 )
D KiDtaOfs 10i 0 Overlay( KiKeyInf : 17 )
**-- Sort information:
D JlSrtInf Ds
D SiNbrKeys 10i 0 Inz( 1 )
D SiSrtInf 12a Dim( 10 )
D SiKeyFldOfs 10i 0 Overlay( SiSrtInf : 1 )
D SiKeyFldLen 10i 0 Overlay( SiSrtInf : 5 )
D SiKeyFldTyp 5i 0 Overlay( SiSrtInf : 9 )
D SiSrtOrd 1a Overlay( SiSrtInf : 11 )
D SiRsv 1a Overlay( SiSrtInf : 12 )
**-- List information:
D JlLstInf Ds
D LiRcdNbrTot 10i 0
D LiRcdNbrRtn 10i 0
D LiHandle 4a
D LiRcdLen 10i 0
D LiInfSts 1a
D LiDts 13a
D LiLstSts 1a
D 1a
D LiInfLen 10i 0
D LiRcd1 10i 0
D 40a
**-- Selection information:
D JlSltInf Ds
D SiJobNam 10a Inz( '*ALL' )
D SiUsrNam 10a Inz( '*ALL' )
D SiJobNbr 6a Inz( '*ALL' )
D* SiJobTyp 1a Inz( 'I' )
D SiJobTyp 1a Inz( '*' )
D 1a
D SiOfsPriSts 10i 0 Inz(108 )
D SiNbrPriSts 10i 0 Inz( 1 )
D SiOfsActSts 10i 0 Inz(118 )
D SiNbrActSts 10i 0 Inz( 1 )
D SiOfsJbqSts 10i 0 Inz(126 )
D SiNbrJbqSts 10i 0 Inz( 0 )
D SiOfsJbqNam 10i 0 Inz(136 )
D SiNbrJbqNam 10i 0 Inz( 0 )
D SiOfsCurUsr 10i 0 Inz(156 )
D SiNbrCurUsr 10i 0 Inz( 0 )
D SiOfsSvrTyp 10i 0 Inz(166 )
D SiNbrSvrTyp 10i 0 Inz( 0 )
D SiOfsActSbs 10i 0 Inz(196 )
D SiNbrActSbs 10i 0 Inz( 0 )
D SiOfsMemPol 10i 0 Inz(206 )
D SiNbrMemPol 10i 0 Inz( 0 )
D SiOfsJobTypE 10i 0 Inz(210 )
D SiNbrJobTypE 10i 0 Inz( 0 )
D SiOfsQualJob 10i 0 Inz(214 )
D SiNbrQualJob 10i 0 Inz( 0 )
**
D SiPriSts 10a Dim( 1 )
D SiActSts 4a Dim( 2 )
D SiJbqSts 10a Dim( 1 )
D SiJbqNam 20a Dim( 1 )
D SiCurUsr 10a Dim( 1 )
D SiSvrTyp 30a Dim( 1 )
D SiActSbs 10a Dim( 1 )
D SiMemPol 10i 0 Dim( 1 )
D SiJobTypEn 10i 0 Dim( 1 )
D SiQualJob 26 Dim( 1 )
**-- Job information key fields:
D JbKeyDta Ds
D JbCurUSr 10
D JbMsgRpy 1
D JbMsgKeyRpy 4
D JbMsgQRpy 20
D JbQualSbs 20
**-- General return data:
D JlGenDta Ds
D GdBytRtn 10i 0
D GdBytAvl 10i 0
D GdElpTim 20u 0
D 16a
**-- API constants: ----------------------------------------------------**
D JOB_RESET_STAT c '1'
D JOB_KEEP_STAT c '0'
**-- Open list of jobs: ------------------------------------------------**
D LstJobs Pr ExtPgm( 'QGYOLJOB' )
D LjRcvVar 65535a Options( *VarSize )
D LjRcvVarLen 10i 0 Const
D LjFmtNam 8a Const
D LjRcvVarDfn 65535a Options( *VarSize )
D LjRcvDfnLen 10i 0 Const
D LjLstInf 80a
D LjNbrRcdRtn 10i 0 Const
D LjSrtInf 1024a Const Options( *VarSize )
D LjJobSltInf 1024a Const Options( *VarSize )
D LjJobSltLen 10i 0 Const
D LjNbrFldRtn 10i 0 Const
D LjKeyFldRtn 10i 0 Const Options( *VarSize ) Dim( 32 )
D LjError 1024a Options( *VarSize )
**
D LjJobSltFmt 8a Const Options( *NoPass )
**
D LjResStc 1a Const Options( *NoPass )
D LjGenRtnDta 32a Options( *NoPass: *VarSize )
D LjGenRtnDtaLn 10i 0 Const Options( *NoPass )
**-- Get list entry: ---------------------------------------------------**
D GetLstEnt Pr ExtPgm( 'QGYGTLE' )
D GlRcvVar 65535a Options( *VarSize )
D GlRcvVarLen 10i 0 Const
D GlHandle 4a Const
D GlLstInf 80a
D GlNbrRcdRtn 10i 0 Const
D GlRtnRcdNbr 10i 0 Const
D GlError 1024a Options( *VarSize )
**-- Close list: -------------------------------------------------------**
D CloseLst Pr ExtPgm( 'QGYCLST' )
D ClHandle 4a Const
D ClError 1024a Options( *VarSize )
**-- Send message: -----------------------------------------------------**
D SndMsg Pr ExtPgm( 'QMHSNDM' )
D SmMsgId 7a Const
D SmMsgFq 20a Const
D SmMsgDta 512a Const Options( *VarSize )
D SmMsgDtaLen 10i 0 Const
D SmMsgTyp 10a Const
D SmMsgQq 1000a Const Options( *VarSize )
D SmMsgQnbr 10i 0 Const
D SmMsgQrpy 20a Const
D SmMsgKey 4a
D SmError 10i 0 Const
**
D SmCcsId 10i 0 Const Options( *NoPass )
**-- Copy memory: ------------------------------------------------------**
D memcpy Pr * ExtProc( '_MEMMOVE' )
D outmem * Value
D inpmem * Value
D memsiz 10u 0 Value
**-- Delay job: --------------------------------------------------------**
D sleep Pr 10i 0 ExtProc( 'sleep' )
D seconds 10u 0 Value
**-- Get top stack entry: ----------------------------------------------**
D GetTopStkE Pr 20a
D GtJobId 26a Const
*** Prototypes for external subprocedures:
D Cmd PR ExtPgm('QCMDEXC')
D command 4096A OPTIONS(*VARSIZE) CONST
D length 15P 5 const
D tmpnam PR * extproc('_C_IFS_tmpnam')
D string 39A options(*omit)
D filename S 40A Varying
D fd S 10I 0
D CRLF S 2 INZ(X'0D25')
**-- Receive non-program message:
D RcvMsg Pr ExtPgm( 'QMHRCVM' )
D RcvVar 65535a Options( *VarSize )
D RcvVarLen 10i 0 Const
D FmtNam 10a Const
D MsgQueQ 20a Const
D MsgTyp 10a Const
D MsgKey 4a Const
D Wait 10i 0 Const
D MsgAct 10a Const
D Error 32767a Options( *VarSize )
D CcsId 10i 0 Const Options( *NoPass )
D AlwDftRpyRjt 10i 0 Const Options( *NoPass )
**-- Message information structure:
D RCVM0200 Ds Qualified
D BytRtn 10i 0
D BytAvl 10i 0
D MsgSev 10i 0
D MsgId 7a
D MsgTyp 2s 0
D MsgKey 4a
D MsgFilNam 10a
D MsgFilLib_s 10a
D MsgFilLib_u 10a
D SndJobNam 10a
D SndJobUsr 10a
D SndJobNbr 6a
D SndPgmNam 12a
D 4a
D DatSnt 7s 0
D TimSnt 6s 0
D TimSntMs 6s 0
D SndUsrPrf 10a
D 1a
D CcsIdStsTxt 10i 0
D CcsIdStsDta 10i 0
D AlrOpt 9a
D CcsIdMsgTxt 10i 0
D CcsIdMsgDta 10i 0
D DtaLenRtn 10i 0
D DtaLenAvl 10i 0
D MsgLenRtn 10i 0
D MsgLenAvl 10i 0
D HlpLenRtn 10i 0
D HlpLenAvl 10i 0
D VarDta 32767a
**
**
D MsqNam_q Ds
D ObjNam 10a
D LibNam 10a
D DateSentC S 19
D DateSent Ds 13
D DateSnt 7s 0
D TimeSnt 6s 0
D SentYY 2 overlay(DateSent:2)
D SentMM 2 overlay(DateSent:4)
D SentDD 2 overlay(DateSent:6)
D SentHH 2 overlay(DateSent:8)
D SentMin 2 overlay(DateSent:10)
D Sentss 2 overlay(DateSent:12)
**-- Global variables: -------------------------------------------------**
D PgmNam s 20a
D MsgDta s 256a Varying
D MsgKey s 4a
D MsgTyp s 10a
D Subject s 60a
D PgmJob s 100a
D MsgTxt s 1024a Varying
D MsgHlpTxt s 1024a Varying
D SecLvlMsgTxt s 2048a Varying
D SecLvl s 4096a Varying
D MailText s 2048a Varying
D tempText s 2048a
D Ix s 5i 0
D Offset s 5i 0
D Pos s 5i 0
D CmdStr S 4096
D QualMsgQ S 20
D SecLvlFmt s 78a Dim( 64 )
D MsgTxtFmt s 78a Dim( 64 )
D NbrLinMsg s 5i 0
D NbrLinSec s 5i 0
D LinIdx s 5i 0
D SysDts s z
D Idx s 5i 0
D IfsCcsid s 10U 0
**-- Global constants:
D OFS_MSGDTA c 16
D JOB_CCSID c 0
D RPY_SENT c '2'
D TYP_INQ c 5
D TYP_RPY c 21
D MSG_TXT c '&N Message . . . . : '
D RPY_DTA c 'Reply . . . . . : '
D NO_WAIT c 0
D KEEP_STS c '*SAME'
D NULL c ''
D WAIT_MAX c -1
D OLD_STS c '*OLD'
D QUOTE C X'7D'
D* Flags for use in open()
D O_RDWR C 4
D* Create File if not exist
D O_CREAT C 8
D* Exclusively create
D O_EXCL C 16
D* Assign a CCSID
D O_CCSID C 32
D* Truncate File to 0 bytes
D O_TRUNC C 64
D* Writing Only
D O_WRONLY C 2
D O_TEXTDATA C 16777216
D* Note: O_TEXT_CREAT requires all of the following flags to work:
D* O_CREAT+O_TEXTDATA+(O_CODEPAGE or O_CCSID)
D O_TEXT_CREAT C 33554432
D* owner authority
D S_IRUSR C 256
D S_IWUSR C 128
D*--------------------------------------------------------------------
D* Open a File
D open PR 10I 0 ExtProc('open')
D filename * value options(*string)
D openflags 10I 0 value
D mode 10U 0 value options(*nopass)
D codepage 10U 0 value options(*nopass)
D txtcreatid 10U 0 value options(*nopass)
D*--------------------------------------------------------------------
D* Close a file
D close PR 10I 0 ExtProc('close')
D handle 10I 0 value
D*--------------------------------------------------------------------
D* Remove Link to File.
D unlink PR 10I 0 ExtProc('unlink')
D path * Value options(*string)
D*--------------------------------------------------------------------
D* Write to a file
D write PR 10I 0 ExtProc('write')
D handle 10I 0 value
D buffer * value
D bytes 10U 0 value
**-- Retrieve message:
D RtvMsg Pr ExtPgm( 'QMHRTVM' )
D RcvVar 32767a Options( *VarSize )
D RcvVarLen 10i 0 Const
D FmtNam 10a Const
D MsgId 7a Const
D MsgFil_q 20a Const
D MsgDta 512a Const Options( *VarSize )
D MsgDtaLen 10i 0 Const
D RplSubVal 10a Const
D RtnFmtChr 10a Const
D Error 32767a Options( *VarSize )
D RtvOpt 10a Const Options( *NoPass )
D CvtCcsId 10i 0 Const Options( *NoPass )
D RplCcsId 10i 0 Const Options( *NoPass )
**-- Retrieve second level help text:
D RtvSecLvl Pr 4096a Varying
D PxMsgFil_q 20a Const
D PxMsgId 7a Const
D PxMsgDta 4096a Const Varying
**-- Format message string:
D FmtMsgStr Pr 78a Dim( 64 )
D PxMsgStr 4096a Value Varying
D PxNbrLin 5i 0
**-- Format message line:
D FmtMsgLin Pr 78a
D PxMsgLin 82a Const Varying
D PxPrcLen 5i 0
D PxInzInd n Options( *NoPass )
**-- Find format instruction:
D FndFmtIns Pr 5u 0
D PxMsgLin 82a Const Varying
D PxOffSet 5u 0 Const Options( *NoPass )
**-- Get indent positions:
D GetIndPos Pr 5u 0
D PxFmtIns 2a Const
D PxInxNxt 5u 0
**-- Scan reverse:
D ScanR Pr 5u 0
D PxArg 128a Const Varying
D PxString 4096a Const Varying
D PxOfs 5u 0 Const Options( *NoPass )
**-- Check AS400 Object exist ?
D As400ObjFound PR N
D QlObjName 20 Value
D ObjType 10 Value
**-- Send escape message:
D SndEscMsg Pr 10i 0
D PxMsgId 7a Const
D PxMsgF 10a Const
D PxMsgDta 512a Const Varying
**-- Send program message:
D SndPgmMsg Pr ExtPgm( 'QMHSNDPM' )
D MsgId 7a Const
D MsgFq 20a Const
D MsgDta 128a Const
D MsgDtaLen 10i 0 Const
D MsgTyp 10a Const
D CalStkE 10a Const Options( *VarSize )
D CalStkCtr 10i 0 Const
D MsgKey 4a
D Error 1024a Options( *VarSize )
**
**-- Mainline: ---------------------------------------------------------**
**
C *Entry Plist
C Parm WakeUp 5 0
C Parm Mail 4
C Parm MsgQ 4
C Parm ToMail 32
C IfsCcsid Parm CltCCSID 5 0
C Parm ToMsgQ 20
C If MsgQ = '*YES'
C CallP As400ObjFound( ToMsgQ : '*MSGQ' )
C EndIf
C Eval SiPriSts(1) = '*ACTIVE'
C Eval SiActSts(1) = 'MSGW'
**-- Job information return fields:
C Eval JlKeyFld(1) = 101
C Eval JlKeyFld(2) = 305
C Eval JlKeyFld(3) = 1307
C Eval JlKeyFld(4) = 1308
C Eval JlKeyFld(5) = 1309
C Eval JlKeyFld(6) = 1906
**
**-- Sort field specification:
C Eval SiNbrKeys = 1
C Eval SiKeyFldOfs(1) = 1
C Eval SiKeyFldLen(1) = 10
C Eval SiKeyFldTyp(1) = 4
C Eval SiSrtOrd(1) = '1'
C Eval SiRsv(1) = x'00'
**
C DoW 1 = 1
**
**-- Retrieve job list:
C CallP LstJobs( JlJobInf
C : %Size( JlJobInf )
C : 'OLJB0200'
C : JlKeyInf
C : %Size( JlKeyInf )
C : JlLstInf
C : 1
C : JlSrtInf
C : JlSltInf
C : %Size( JlSltInf )
C : JlNbrFldRtn
C : JlKeyFld
C : ApiError
C : 'OLJS0200'
C : JOB_KEEP_STAT
C : JlGenDta
C : %Size( JlGenDta )
C )
**
C If AeBytAvl = *Zero
**
C DoW LiLstSts <> '2' Or
C LiRcdNbrTot > JlRtnRcdNbr
**
C ExSr GetKeyDta
**
C Eval JlRtnRcdNbr = JlRtnRcdNbr + 1
**
C CallP GetLstEnt( JlJobInf
C : %Size( JlJobInf )
C : LiHandle
C : JlLstInf
C : 1
C : JlRtnRcdNbr
C : ApiError
C )
**
C If AeBytAvl > *Zero
C Leave
C EndIf
**
C EndDo
**
C CallP CloseLst( LiHandle
C : ApiError
C )
**
**-- Wait 10 seconds:
C CallP sleep( WakeUp )
**
C Reset JlLstInf
C Eval JlRtnRcdNbr = 1
C Else
C Leave
C EndIf
C EndDo
C If AeBytAvl > *Zero
C CallP SndEscMsg( AeExcpId
C : 'QCPFMSG'
C : %Subst( AeExcpDta: 1: AeBytAvl- 16 )
C )
C EndIf
**
C Eval *InLr = *On
**
C Return
**
**-- Get KEY data: -----------------------------------------------------**
C GetKeyDta BegSr
**
C Clear JbKeyDta
**
C For Ix = 1 To KiFldNbrRtn
**
C Select
C When KiKeyFld(Ix) = 305
C CallP memcpy( %Addr( JbCurUsr )
C : %Addr( JlJobInf ) +
C KiDtaOfs(Ix)
C : KiDtaLen(Ix)
C )
**
C When KiKeyFld(Ix) = 1307
C CallP memcpy( %Addr( JbMsgRpy )
C : %Addr( JlJobInf ) +
C KiDtaOfs(Ix)
C : KiDtaLen(Ix)
C )
**
C When KiKeyFld(Ix) = 1308
C CallP memcpy( %Addr( JbMsgKeyRpy )
C : %Addr( JlJobInf ) +
C KiDtaOfs(Ix)
C : KiDtaLen(Ix)
C )
**
C When KiKeyFld(Ix) = 1309
C CallP memcpy( %Addr( JbMsgQRpy )
C : %Addr( JlJobInf ) +
C KiDtaOfs(Ix)
C : KiDtaLen(Ix)
C )
**
C When KiKeyFld(Ix) = 1906
C CallP memcpy( %Addr( JbQualSbs )
C : %Addr( JlJobInf ) +
C KiDtaOfs(Ix)
C : KiDtaLen(Ix)
C )
C EndSl
C EndFor
**
C If JbMsgRpy = '1'
C Eval PgmNam = GetTopStkE( JbJobId )
C If MsgQ = '*YES'
C Exsr SndCmpMsg
C EndIf
C If Mail = '*YES'
C Exsr GetMsgTxt
C Exsr SndMailAtch
C EndIf
C EndIf
**
C EndSr
**-- Get Message Text: -------------------------------------------------**
C GetMsgTxt BegSr
**
C Eval MsgTyp = '*INQ'
C Eval MsgKey = JbMsgKeyRpy
C Eval MsqNam_q= JbMsgQRpy
C CallP RcvMsg( RCVM0200
C : %Size( RCVM0200 )
C : 'RCVM0200'
C : MsqNam_q
C : MsgTyp
C : MsgKey
C : NO_WAIT
C : OLD_STS
C : ApiError
C : JOB_CCSID
C )
C If RCVM0200.MsgFilLib_u=*Blanks
C eval RCVM0200.MsgFilLib_u=RCVM0200.MsgFilLib_s
C EndIf
C Eval MsgDta = %Subst( RCVM0200.VarDta
C : 1
C : RCVM0200.DtaLenRtn
C )
C eval MsgTxt = %Subst( RCVM0200.VarDta
C : RCVM0200.DtaLenRtn + 1
C : RCVM0200.MsgLenRtn
C )
C If RCVM0200.MsgId = *Blanks
C eval MsgTxt = MsgDta
C Else
C eval SecLvl = RtvSecLvl( RCVM0200.MsgFilNam +
C RCVM0200.MsgFilLib_u
C : RCVM0200.MsgId
C : MsgDta
C )
C eval SecLvlFmt=FmtMsgStr( SecLvl: NbrLinSec )
C eval SecLvlMsgTxt = *Blanks
C For idx = 1 to 64
C If %len(%trimr(SecLvlFmt(idx))) > 0
C If %Subst(SecLvlFmt(idx):1:2) <> 'Ca' and
C %Subst(SecLvlFmt(idx):1:2) <> 'Re'
C eval %Subst(SecLvlFmt(idx):1:2) = ' '
C EndIf
C eval SecLvlMsgTxt = %trimr(SecLvlMsgTxt) +
C SecLvlFmt(idx) + CRLF
C Else
C leave
C EndIf
C EndFor
C EndIf
C
C EndSr
**-- Send completion message: ------------------------------------------**
C SndCmpMsg BegSr
**
C Eval QualMsgQ = PsCurUsr + '*LIBL'
C If MsgQ <> *Blanks
C Eval QualMsgQ = ToMsgQ
C EndIf
C Eval MsgDta = 'Program ' +
C %Trim(%SubSt(PgmNam:11:10)) +
C '/' +
C %Trim(%SubSt(PgmNam: 1:10)) +
C ' in job ' +
C %Trim( JbNbrUsd ) + '/' +
C %Trim( JbUsrUsd ) + '/' +
C %Trim( JbJobUsd ) +
C ' waiting for a message' +
C ' in Msgq ' +
C %Trim(%SubSt(JbMsgQRpy:11:10))+
C '/' +
C %Trim(%SubSt(JbMsgQRpy:1:10))+
C '.'
**
C CallP(e) SndMsg( *Blanks
C : *Blanks
C : MsgDta
C : %Len( MsgDta )
C : '*COMP'
C : QualMsgQ
C : 1
C : *Blanks
C : MsgKey
C : 0
C )
**
C EndSr
**-- Send mail message with attach file: -------------------------------**
c SndMailAtch BegSr
c Eval DateSnt = RCVM0200.DatSnt
c Eval TimeSnt = RCVM0200.TimSnt
c Eval DateSentC = '20' + SentYY + '-' +
c SentMM + '-' + SentDD + '-' +
c SentHH + ':' + SentMIN+ ':' +
c SentSS
C Eval PgmJob = 'Program ' +
C %Trim(%SubSt(PgmNam:11:10)) +
C '/' +
C %Trim(%SubSt(PgmNam: 1:10)) +
C ' error in job ' +
C %Trim( JbNbrUsd ) + '/' +
C %Trim( JbUsrUsd ) + '/' +
C %Trim( JbJobUsd )
C Eval MailText =
C %trim(PgmJob) + CRLF +
C 'Time sent: '+DateSentc+' '+
C CRLF +
C 'Message ID: ' +
C RCVM0200.MsgId + CRLF +
C 'Message: ' + %trim(MsgTxt) +
C CRLF + CRLF
C* Eval filename = %str(tmpnam(*OMIT))
C Eval filename = '/tmp/' +
C 'Error_'+ %trim(JbJobUsd) +
C '_' + %trim(JbUsrUsd) +
C '_' + %trim(JbNbrUsd) +
C '.TXT'
C callp unlink(filename)
C
C Eval fd = open( filename
C : O_CREAT + O_CCSID+ O_WRONLY
C + O_TEXT_CREAT + O_TEXTDATA
C : S_IRUSR + S_IWUSR
C : IfsCcsid
C : 0 )
C
C if fd = -1
C* open failed, check errno for the reason why.
C endif
C callp write(fd: %addr(MailText)+2
C : %len(%trim(MailText)))
C
C If %len(SecLvlMsgTxt) > 0
C callp write(fd: %addr(SecLvlMsgTxt)+2
C : %len(%trim(SecLvlMsgTxt)))
C EndIf
C callp close(fd)
C
C Eval Subject = 'AS400 program ' +
C %Trim(%SubSt(PgmNam:11:10)) +
C '/' +
C %Trim(%SubSt(PgmNam: 1:10)) +
C ' exception occurred.'
C EVAL cmdSTR = 'SNDSMTPEMM RCP(' +
C %trim(ToMail) +
C ') SUBJECT(' + QUOTE +
C %trim(Subject) +
C QUOTE + ') NOTE(' +
C QUOTE +
C %trim( MailText) +
C QUOTE + ') ATTACH(' +
C QUOTE +
C %trim(filename) +
C QUOTE + ')'
C* ' CONTENT(*HTML)'
C callp(e) Cmd(%trim(cmdStr) : %len(%trim(cmdstr)))
C callp unlink(filename)
c EndSr
**-- Get top stack entry: ----------------------------------------------**
P GetTopStkE B Export
D Pi 20a
D GtJobId 26a Const
**-- API parameters:
D CsRcvVar Ds
D CsBytRtn 10i 0
D CsBytAvl 10i 0
D CsNbrStkE 10i 0
D CsOfsStkE 10i 0
D CsNbrEntRtn 10i 0
D CsThrId 8a
D CsInfSts 1a
D CsCalStk 32767a
**
D CsCalStkE Ds Based( pCalStkE )
D CsStkEntLen 10i 0
D CsOfsStmIds 10i 0
D CsNbrStmIds 10i 0
D CsOfsPrcNam 10i 0
D CsLenPrcNam 10i 0
D CsRqsLvl 10i 0
D CsPgmNam 10a
D CsPgmLib 10a
D CsMiInst 10i 0
D CsModNam 10a
D CsModLib 10a
D CsCtlBdy 1a
D CsRsv 3a
D CsActGrpNbr 10u 0
D CsActGrpNam 10a
D CsAddInf 4096a
**
D CsStmIds 10a Dim( 16 )
D CsPrcNam 512a
**
D CsJobId Ds
D JiJobId 26a
D JiJobNam 10a Overlay( JiJobId: 1 )
D JiUsrNam 10a Overlay( JiJobId: *Next )
D JiJobNbr 6a Overlay( JiJobId: *Next )
D JiIntId 16a
D JiRsv 2a Inz( *Allx'00' )
D JiThrInd 10i 0 Inz( 2 )
D JiThrId 8a Inz( *Allx'00' )
**-- Retrieve call stack:
D RtvCalStk Pr ExtPgm( 'QWVRCSTK' )
D RcRcvVar 32767a
D RcRcvVarLen 10i 0 Const
D RcRcvInfFmt 8a Const
D RcJobId 56a Const
D RcJobIdFmt 8a Const
D RcError 32767a Options( *VarSize )
**
D EntNbr s 5u 0
**-- Get stack entries: ------------------------------------------------**
**
C Eval JiJobId = GtJobId
**
C CallP RtvCalStk( CsRcvVar
C : %Size( CsRcvVar )
C : 'CSTK0100'
C : CsJobId
C : 'JIDF0100'
C : ApiError
C )
**
C If AeBytAvl = *Zero
C Eval pCalStkE = %Addr( CsRcvVar ) + CsOfsStkE
**
C For EntNbr = 1 to CsNbrEntRtn
**
C* If EntNbr = 1
**
C Eval CsStmIds = *Blanks
C Eval CsPrcNam = *Blanks
**
C If CsOfsStmIds > *Zero
C CallP MemCpy( %Addr( CsStmIds )
C : %Addr( CsCalStkE ) +
C CsOfsStmIds
C : CsNbrStmIds * %Size( CsStmIds )
C )
C EndIf
**
C If CsOfsPrcNam > *Zero
C CallP MemCpy( %Addr( CsPrcNam )
C : %Addr( CsCalStkE ) +
C CsOfsPrcNam
C : CsLenPrcNam
C )
C EndIf
**
C* Leave
C* EndIf
C If %SubSt(CsPgmNam:1:1) <> 'Q'
C Leave
C EndIf
**
C If EntNbr < CsNbrEntRtn
C Eval pCalStkE = PCalStkE + CsStkEntLen
C EndIf
C EndFor
**
C Return CsPgmNam + CsPgmLib
**
C Else
C Return *Blanks
C EndIf
**
P GetTopStkE E
**-- Retrieve second level help text:
P RtvSecLvl B
D Pi 4096a Varying
D PxMsgFil_q 20a Const
D PxMsgId 7a Const
D PxMsgDta 4096a Const Varying
**-- Message information structure:
D RTVM0100 Ds Qualified
D BytRtn 10i 0
D BytAvl 10i 0
D RtnMsgLen 10i 0
D RtnMsgAvl 10i 0
D RtnHlpLen 10i 0
D RtnHlpAvl 10i 0
D Msg 4096a
**
D RPL_SUB_VAL c '*YES'
D INC_FMT_CTL c '*YES'
/Free
RtvMsg( RTVM0100
: %Size( RTVM0100 )
: 'RTVM0100'
: PxMsgId
: PxMsgFil_q
: PxMsgDta
: %Len( PxMsgDta )
: RPL_SUB_VAL
: INC_FMT_CTL
: ApiError
);
If AeBytAvl > *Zero;
Return NULL;
Else;
Return %Subst( RTVM0100.Msg
: RTVM0100.RtnMsgLen + 1
: RTVM0100.RtnHlpLen
);
EndIf;
/End-Free
P RtvSecLvl E
**-- Format message string:
P FmtMsgStr B
D Pi 78a Dim( 64 )
D PxMsgStr 4096a Value Varying
D PxNbrLin 5i 0
**-- Local variables:
D InzInd s n Inz( *On )
D LinIdx s 5i 0
D PrcLen s 5i 0
D StrPos s 10i 0 Inz( 1 )
D RtnFmt s 78a Dim( 64 )
/Free
If PxMsgStr > *Blanks;
For LinIdx = 1 To %Elem( RtnFmt );
RtnFmt(LinIdx) = FmtMsgLin( %TrimL( PxMsgStr ): PrcLen: InzInd );
If %Len( %TrimL( PxMsgStr )) > PrcLen;
PxMsgStr = %Subst( %TrimL( PxMsgStr ): PrcLen + 1 );
Else;
Leave;
EndIf;
If PxMsgStr = *Blanks;
Leave;
EndIf;
EndFor;
EndIf;
PxNbrLin = LinIdx;
Return RtnFmt;
/End-Free
P FmtMsgStr E
**-- Format message line:
P FmtMsgLin B
D Pi 78a
D PxMsgLin 82a Const Varying
D PxPrcLen 5i 0
D PxInzInd n Options( *NoPass )
**-- Local variables:
D IndPos s 5u 0 Inz( 1 ) Static
D IndNxt s 5u 0 Inz( 1 ) Static
D StrLen s 5u 0
D FmtPos s 5u 0
D FmtIns s 2a
D FmtBeg s n
D LeadBlk s 10i 0
**
D MsgLin Ds Qualified
D LstPos 1a Overlay( MsgLin: 78 )
D NxtPos 1a Overlay( MsgLin: 79 )
/Free
If %Parms = 3 And PxInzInd = *On;
Reset IndPos;
Reset IndNxt;
PxInzInd = *Off;
EndIf;
FmtBeg = *Off;
FmtPos = FndFmtIns( PxMsgLin );
If FmtPos > *Zero;
FmtIns = %Subst( PxMsgLin: FmtPos: 2 );
If FmtPos = 1;
FmtBeg = *On;
IndPos = GetIndPos( FmtIns: IndNxt );
FmtPos = FndFmtIns( PxMsgLin: 4 );
If FmtPos > *Zero;
%Subst( MsgLin: IndPos ) = %Subst( PxMsgLin: 4: FmtPos - 4 );
Else;
%Subst( MsgLin: IndPos ) = %Subst( PxMsgLin: 4 );
EndIf;
Else;
%Subst( MsgLin: IndPos ) = %Subst( PxMsgLin: 1: FmtPos - 1 );
EndIf;
Else;
%Subst( MsgLin: IndPos ) = %Subst( PxMsgLin: 1 );
EndIf;
LeadBlk = %Check( ' ': MsgLin );
If LeadBlk > *Zero;
LeadBlk -= IndPos;
EndIf;
IndPos = IndNxt;
If MsgLin.LstPos = *Blank Or MsgLin.NxtPos = *Blank;
StrLen = %Size( MsgLin ) - 1;
Else;
StrLen = ScanR( ' ': MsgLin ) - 1;
If StrLen = *Zero;
StrLen = %Size( MsgLin ) - 1;
EndIf;
EndIf;
MsgLin = %Subst( MsgLin: 1: StrLen );
If FmtBeg = *On;
PxPrcLen = %Len( %Trim( MsgLin )) + 3 + LeadBlk;
Else;
PxPrcLen = %Len( %Trim( MsgLin )) + 1 + LeadBlk;
EndIf;
Return MsgLin;
/End-Free
P FmtMsgLin E
**-- Find format instruction:
P FndFmtIns B
D Pi 5u 0
D PxMsgLin 82a Const Varying
D PxOffSet 5u 0 Const Options( *NoPass )
**-- Local variables:
D FmtPosN s 5u 0
D FmtPosP s 5u 0
D FmtPosB s 5u 0
D OffSet s 5u 0
/Free
If %Parms = 2;
OffSet = PxOffSet;
Else;
OffSet = 1;
EndIf;
FmtPosN = %Scan( '&N': PxMsgLin: OffSet );
FmtPosP = %Scan( '&P': PxMsgLin: OffSet );
FmtPosB = %Scan( '&B': PxMsgLin: OffSet );
If FmtPosN = *Zero;
FmtPosN = *HiVal;
EndIf;
If FmtPosP = *Zero;
FmtPosP = *HiVal;
EndIf;
If FmtPosB = *Zero;
FmtPosB = *HiVal;
EndIf;
Select;
When FmtPosN < FmtPosP And
FmtPosN < FmtPosB;
Return FmtPosN;
When FmtPosP < FmtPosN And
FmtPosP < FmtPosB;
Return FmtPosP;
When FmtPosB < FmtPosN And
FmtPosB < FmtPosP;
Return FmtPosB;
EndSl;
Return *Zero;
/End-Free
P FndFmtIns E
**-- Get inden positions:
P GetIndPos B
D Pi 5u 0
D PxFmtIns 2a Const
D PxIndNxt 5u 0
/Free
Select;
When PxFmtIns = '&N';
PxIndNxt = 3;
Return 1;
When PxFmtIns = '&P';
PxIndNxt = 3;
Return 5;
When PxFmtIns = '&B';
PxIndNxt = 5;
Return 3;
Other;
PxIndNxt = 0;
Return 0;
EndSl;
/End-Free
P GetIndPos E
**-- Scan reverse:
P ScanR B
D Pi 5u 0
D PxArg 128a Const Varying
D PxString 4096a Const Varying
D PxOfs 5u 0 Const Options( *NoPass )
**
D Pos s 5u 0
D Ofs s 5u 0
/Free
If %Parms = 3;
Ofs = PxOfs - %Len( PxArg ) + 1;
Else;
Ofs = %Len( PxString ) - %Len( PxArg ) + 1;
EndIf;
If Ofs > %Len( PxString );
Pos = %Len( PxString ) + 1;
Else;
For Pos = Ofs DownTo 1;
If %SubSt( PxString: Pos: %Len( PxArg )) = PxArg;
Leave;
EndIf;
EndFor;
EndIf;
Return Pos;
/End-Free
P ScanR E
** As400ObjFound - Attempts to locate an AS/400 object
P As400ObjFound B
D As400ObjFound PI N
D QlObjName 20 Value
D ObjType 10 Value
* Local variables and prototypes
D RtvObjDesc PR ExtPgm( 'QUSROBJD' )
D RcvrVar 8
D LenRcvrVar 10I 0 Const
D FmtName 8 Const
D QlObjName 20 Const
D ObjType 10 Const
D ApiErrInf Like( ApiError )
D Rcvr S 8
* Invoke the QUSROBJD API to attempt to locate the object
C CallP RtvObjDesc( Rcvr:
C %Size( Rcvr ):
C 'OBJD0100':
C QlObjName:
C ObjType:
C ApiError )
* If the API returns any error at all, I assume we were unable to
* locate the object.
C If AeBytAvl > *Zero
C CallP SndEscMsg( AeExcpId
C : 'QCPFMSG'
C : %Subst( AeExcpDta: 1: AeBytAvl- 16 )
C )
C Return *Off
C Else
C Return *On
C EndIf
P As400ObjFound E
**-- Send escape message: ----------------------------------------------**
P SndEscMsg B
D Pi 10i 0
D PxMsgId 7a Const
D PxMsgF 10a Const
D PxMsgDta 512a Const Varying
**
D MsgKey s 4a
D MsgFQual s 20a
C If %SubSt(PxMsgId: 1: 3) = 'GUI'
C eval %SubSt(MsgFQual : 1: 10) = 'QGUIMSG'
C Else
C eval %SubSt(MsgFQual : 1: 10) = PxMsgF
C EndIf
C eval %SubSt(MsgFQual :11: 10) = '*LIBL'
C Callp SndPgmMsg( PxMsgId
C : MsgFQual
C : PxMsgDta
C : %Len( PxMsgDta )
C : '*ESCAPE'
C : '*PGMBDY'
C : 1
C : MsgKey
C : ApiError
C )
C If AeBytAvl > *Zero
C Return -1
C
C Else
C Return 0
C EndIf
P SndEscMsg E
File : QCMDSRC
Member: CHKJOBMSGW
Usage : CrtCmd Cmd( CHKJOBMSGW )
Pgm( CHKJOBMSGW )
SrcFile( YourSourceFile )
/* =============================================================== */
/* = Command....... ChkJobMsgw = */
/* = CPP........... ChkJobMsgw RPGLE = */
/* = Description... Check job message waiting and send = */
/* = notification to msgq or detail info to mail = */
/* = or both = */
/* = = */
/* = CrtCmd Cmd( ChkJobMsgw ) = */
/* = Pgm( ChkJobMsgw ) = */
/* = SrcFile( YourSourceFile ) = */
/* =============================================================== */
/* = Date : 2015/06/05 = */
/* = Author: Vengoal Chang = */
/* =============================================================== */
/* = Usage: = */
/* = Notification send to MsgQ QSYSOPR : = */
/* = SBMJOB CMD(CHKJOBMSGW WAKEUP(600)) = */
/* = JOB(CHKJOBMSGW) = */
/* = JOBQ(QSYSNOMAX) = */
/* = = */
/* = Notification send to Mail with attachement by SNDSMTPEMM = */
/* = SBMJOB CMD(CHKJOBMSGW WAKEUP(600) = */
/* = MAIL(*YES) = */
/* = MSGQ(*NO ) = */
/* = TOMAIL('user@domain') = */
/* = CLTCCSID(819) = */
/* = ) = */
/* = JOB(CHKJOBMSGW) = */
/* = JOBQ(QSYSNOMAX) = */
/* = = */
/* =============================================================== */
Cmd Prompt('Check Job Message Wait')
Parm WAKEUP *Dec (5 0) +
Dft(300) +
Prompt('Wakeup interval for the batch')
Parm MAIL *Char 4 +
Dft(*NO) +
Values(*YES *NO) +
Rstd(*YES) +
Prompt('Send to mail')
Parm MSGQ *Char 4 +
Dft(*YES) +
Values(*YES *NO) +
Rstd(*YES) +
Prompt('Send to MsgQ')
Parm TOMAIL *Char 32 +
PmtCtl(IFMAIL) +
Prompt('Notification send to email')
Parm CLTCCSID *Dec (5 0) +
PmtCtl(IFMAIL) +
Dft(950) +
Prompt('Client ccsid for mail used')
Parm TOMSGQ +
Type( Qual2) +
PmtCtl(IFMSGQ) +
Prompt('Notification send to MsgQ')
QUAL2: Qual Type(*NAME) +
Dft(QSYSOPR) +
Expr(*YES)
Qual Type(*NAME) +
Dft(*LIBL) +
SpcVal((*LIBL) (*CURLIB)) +
Expr(*YES) +
Prompt('Library')
IFMAIL: PmtCtl Ctl(MAIL) Cond((*EQ *YES))
IFMSGQ: PmtCtl Ctl(MSGQ) Cond((*EQ *YES))
Dep Ctl(&MAIL *EQ *YES) +
Parm((&TOMAIL *NE ' ')) +
NbrTrue(*EQ 1)
Dep Ctl(&MAIL *EQ *NO) +
Parm((&MSGQ *NE *NO)) +
NbrTrue(*EQ 1)
參照: Open List of Jobs (QGYOLJOB) API
星期一, 11月 06, 2023
2003-11-20 如何直接使用 QtmmSendMail API 傳送 E-amil ?(Command SNDEMAIL)
如何直接使用 QtmmSendMail API 傳送 E-amil ?(Command SNDEMAIL)
要利用 AS/400 傳送 EMAIL,我於先前電子報是利用 SNDDST 指令來完成,但此指令僅能
附加一個檔案為附件,而且傳送對象無法向一般 email 軟體,指定副本(cc :copy to) 或 密件副本 (bcc),
但 AS/400 另提供一個 API QtmmSendMail
可以達成上述需求。
要使用此工具前須先執行(只要設定一次即可),
1. CHGSMTPA MAILROUTER(other smtp server IP)
2. 要啟動 SMTP server(STRTCPSVR *SMTP)
File : QRPGLESRC
Member: SNDEMAILR
Type : RPGLE
Usage : CRTRPGMOD lib/SNDEMAILR SRCFILE(srclib/srcfile)
CRTPGM lib/SNDEMAILR MODULE(lib/SNDEMAILR) BNDSRVPGM(QTCP/QTMMSNDM)
H*****************************************************************
H* Program description
H*
H* This program will send a MIME e-mail, with optional attachments.
H*
H* To create this program, issue the following:
H* CRTRPGMOD lib/SNDEMAILR SRCFILE(srclib/srcfile)
H* CRTPGM lib/SNDEMAILR MODULE(lib/SNDEMAILR) BNDSRVPGM(QTCP/QTMMSNDM)
H*
H* November 2003
H* Author: Vengoal Chang
H*****************************************************************
H debug BNDDIR('QC2LE')
H OPTION(*SRCSTMT:*NODEBUGIO)
D* IFS Prototype
D*****************************************************************
*-- GetErrNo ---- Get error number ----------------------------------
* extern int * __errno(void);
D @__errno PR * ExtProc('__errno')
*-- StrError ---- Get error text ------------------------------------
* char *strerror(int errnum);
D strerror PR * ExtProc('strerror')
D errnum 10I 0 value
D perror PR ExtProc('perror')
D comment * value options(*string)
D errno PR 10I 0
D die PR
D peMsg 256A const
D err S 10I 0
D*** open an IFS file
Dopen PR 10I 0 EXTPROC('open')
D filename * VALUE OPTIONS(*STRING)
D openflags 10I 0 VALUE
D mode 10U 0 VALUE OPTIONS(*NOPASS)
D codepage 10U 0 VALUE OPTIONS(*NOPASS)
D*** read an IFS file
Dread PR 10I 0 EXTPROC('read')
D filehandle 10I 0 VALUE
D datareceived * VALUE
D nbytes 10U 0 VALUE
D*** write to an IFS file
Dwrite PR 10I 0 EXTPROC('write')
D filehandle 10I 0 VALUE
D datatowrite * VALUE
D nbytes 10U 0 VALUE
D*** close an IFS file
Dclose PR 10I 0 EXTPROC('close')
D filehandle 10I 0 VALUE
* stat()--Get File Information ...........................................
Dstat PR 10I 0 EXTPROC('stat')
D path * VALUE
D buf * VALUE
*
Dencodemailaddr PR 10I 0
D mail_addr 256 VALUE
D mail_desc 50 VALUE
D outstr 9999
Dto950 PR 10I 0
D ebcdic 9999 VALUE
D c950 9999
Dsmtphead PR 10I 0
D instr 9999 VALUE
D outstr 9999
DBencode PR 3P 0
D ascii 256 VALUE
D buflen 3P 0 VALUE
D newbuf 256
Dbase64e PR 4
D inchr 3 VALUE
D translate PR ExtPgm('QDCXLATE')
D length 5P 0 const
D data 32766A options(*varsize)
D table 10A const
* Program status structure
D PsDs SDS
D PsProc *PROC
D PsSts *STATUS
D PsSrcLineNo 21 28
D PsExcpType 40 42
D PsExcpNum 43 46
D PsMsgId 40 46
D PsPgmLib 81 90
D PsLstFileErr 175 184
D PsJobName 244 253
D PsUsrId 254 263
D PsJobNum 270 275
D PsPgmName 334 343
D PsModName 344 353
D*****************************************************************
D* IFS CONSTANTS
D*****************************************************************
D*** File Access Modes for open()
D O_RDONLY S 10I 0 INZ(1)
D O_WRONLY S 10I 0 INZ(2)
D O_RDWR S 10I 0 INZ(4)
D*** oflag Values for open()
D O_CREAT S 10I 0 INZ(8)
D O_EXCL S 10I 0 INZ(16)
D O_TRUNC S 10I 0 INZ(64)
D*** File Status Flags for open() and fcntl()
D O_NONBLOCK S 10I 0 INZ(128)
D O_APPEND S 10I 0 INZ(256)
D*** oflag Share Mode Values for open()
D O_SHARE_NONE S 10I 0 INZ(2000000)
D O_SHARE_RDONLY S 10I 0 INZ(0200000)
D O_SHARE_RDWR S 10I 0 INZ(1000000)
D O_SHARE_WRONLY S 10I 0 INZ(0400000)
D*** file permissions
D S_IRUSR S 10I 0 INZ(256)
D S_IWUSR S 10I 0 INZ(128)
D S_IXUSR S 10I 0 INZ(64)
D S_IRWXU S 10I 0 INZ(448)
D S_IRGRP S 10I 0 INZ(32)
D S_IWGRP S 10I 0 INZ(16)
D S_IXGRP S 10I 0 INZ(8)
D S_IRWXG S 10I 0 INZ(56)
D S_IROTH S 10I 0 INZ(4)
D S_IWOTH S 10I 0 INZ(2)
D S_IXOTH S 10I 0 INZ(1)
D S_IRWXO S 10I 0 INZ(7)
D*** misc
D O_TEXTDATA S 10I 0 INZ(16777216)
D O_CODEPAGE S 10I 0 INZ(8388608)
D O_CCSID C 32
D*****************************************************************
D* DATA DEFINITIONS
D*****************************************************************
D*** Miscellaneous data declarations
D FileName S 255A
D FileLen S 9B 0
D Originator S 255A
D OriginName S 80A
D OriginLen S 9B 0
D CPFNumber S Like(MSGID)
D Subject S 256A
D Message S 512A
D AttachName S 256A
D*AsciiCodePage S 10U 0 INZ(819)
D AsciiCodePage S 10U 0 INZ(950)
D EbcdicCodePage S 10U 0 INZ(937)
D OutAddrArr s 256a dim(1000)
D OutDistArr s 10i 0 dim(1000)
D ToAddrArr s 256a dim(1000)
D ToNameArr s 50a dim(1000)
D CcAddrArr s 256a dim(1000)
D CcNameArr s 50a dim(1000)
D bCcAddrArr s 256a dim(1000)
D bCcNameArr s 50a dim(1000)
D Importnc s 4a
D Priority s 4a
D Sensitiv s 4a
D Receipt s 4a
D***
D Addressee S Like(Address)
D AddresseeName S Like(Address)
D Recipients s 280 dim(1000)
D TotalRecp S 9B 0
D***
D FileDesc S 10I 0
D Flags S 10I 0
D BytesWrt S 10I 0
D Data S 9999A
D Data1 S 9999A
D Data2 S 9999A
D InData S 9999A
D AttachDesc S 10I 0
D BodyDesc S 10I 0
D atcfd S 10I 0
D BytesRead S 10I 0
D DataRead S 9899A
D EOR S 2A Inz(X'0D25')
D CRLF S 2 INZ(X'0D0A')
D Null S 1A Inz(X'00')
D FullName S 512A
D ReturnInt S 10I 0
D Pos S 5U 0
D SavePos S Like(Pos)
D*** Data structure of recipient info.
D Recipient DS
D OffSet 1 4B 0
D AddrLen 5 8B 0
D Format 9 16
D DistrType 17 20B 0
D Reserved 21 24B 0
D Address 25 280
D*** MIME Header fields
D MSender S 256A
D MReceipt S 256A
D MImportnc S 256A
D MPriority S 256A
D MDateTime S 256A
D MFrom S 256A
D MMimeVer S 256A
D MTo S 9999A
D MCc S 9999A
D MBcc S 9999A
D MSubject S 256A
D MBoundary S 256A Inz('--PART.BOUNDARY.1')
D*** Array of Receiption address
D ToReceiptions DS
D to 1 9302
D NbrofTo 5U 0 OVERLAY(to : 1)
D to_replacem 5U 0 OVERLAY(to : 3) DIM(30)
D CcReceiptions DS
D cc 1 9302
D Nbrofcc 5U 0 OVERLAY(cc : 1)
D cc_replacem 5U 0 OVERLAY(cc : 3) DIM(30)
D BccReceiptions DS
D bcc 1 9302
D Nbrofbcc 5U 0 OVERLAY(bcc : 1)
D bcc_replacem 5U 0 OVERLAY(bcc : 3) DIM(30)
D RcptNA DS 306
D RcptName 50 overlay(RcptNA : 1)
D RcptAddr 256 overlay(RcptNA : 51)
DTxtF ds
D TxtFile 10a
D TxtFLib 10a
D TxtFMbr s 10a
D*** Array of file attachments
D Attachment DS
D NbrFiles 1 2B 0
D AttachFile 256A Dim(30)
D*** API error info
D APIError DS
D BytesProvided 10I 0 Inz( %Size( APIError ) )
D BytesAvail 10I 0 Inz( *Zero )
D MsgID 7 Inz( *Blanks )
D 1 Inz( X'00' )
D MsgDta 256 Inz( *Blanks )
D*** Constants
D AtS S 1a inz(X'7C')
D DTo C Const(0)
D DCC C Const(1)
D DBCC C Const(2)
D MsgSize C Const(%Len(Message))
*=============================================
* MISCELLANEOUS
D EmailAddr s 255a
D r s 10i 0
D outstr s 9999a
* string character set
Dcharset S 16 Inz('Big5')
* current time for temp file name (store mail message)
Dcurtime S Z
* last folding position in encoded string
Dfold S 3P 0
*
* SBCS character tables *****************************************
*
* US-ASCII (ANSI X3.4-1986) characters (95)
Da_c S 95
Da_x C X'202122232425262728292A2B2C2D2E2F-
* sp ! " # $ % & ' ( ) * + , - . /
D 303132333435363738393A3B3C3D3E3F-
* 0 1 2 3 4 5 6 7 8 9 : ; < = > ?
D 404142434445464748494A4B4C4D4E4F-
* @ A B C D E F G H I J K L M N O
D 505152535455565758595A5B5C5D5E5F-
* P Q R S T U V W X Y Z [ \ ] ^ _
D 606162636465666768696A6B6C6D6E6F-
* ` a b c d e f g h i j k l m n o
D 707172737475767778797A7B7C7D7E'
* p q r s t u v w x y z { | } ?
*
Dtoblank S 95 INZ(*ALL' ')
*
* 'safe' ASCII chars other than especials[RFC2047 p3] and
* quoted chars for EBCDIC gateway [RFC2045 p20]) (69)
* (These characters are also invariant EBCDIC characters)
Da_s_c C ' %&''*+-0123456789-
D ABCDEFGHIJKLMNOPQRSTUVWXYZ-
D abcdefghijklmnopqrstuvwxyz'
*
Da_s_x C X'202526272A2B2D30313233343536373839-
* sp % & ' * + - 0 1 2 3 4 5 6 7 8 9
D 4142434445464748494A4B4C4D-
* A B C D E F G H I J K L M
D 4E4F505152535455565758595A-
* N O P Q R S T U V W X Y Z
D 6162636465666768696A6B6C6D-
* a b c d e f g h i j k l m
D 6E6F707172737475767778797A'
* n o p q r s t u v w x y z
Da_c_c S 98
Da_c_x S 98
* ISO-2022-JP escape sequences
DG0ascii C X'1B2842'
DG0roman C X'1B284A'
DG0kana C X'1B2849'
DG0k78 C X'1B2440'
DG0k83 C X'1B2442'
* especial characters (RFC2047 section 2)
Despecials C X'28293C3E402C3B3A222F5B5D3F2E3D'
* ( ) < > @ , ; : " / [ ] ? . =
*****************************************************************
* base64 encode (attachment file) ........................................
Db64chrDS DS
D b64i 1 3
D b64i1 1 1
D b64i2 2 2
D b64i3 3 3
Db64apDS DS
D b64ap 1 8
D b64ap1 1 2U 0
D b64ap1L 2 2
D b64ap2 3 4U 0
D b64ap2L 4 4
D b64ap3 5 6U 0
D b64ap3L 6 6
D b64ap4 7 8U 0
D b64ap4L 8 8
*
Db64a C X'4142434445464748494A4B4C4D4E4F-
* A B C D E F G H I J K L M N O
D 505152535455565758595A-
* P Q R S T U V W X Y Z
D 6162636465666768696A6B6C6D6E6F-
* a b c d e f g h i j k l m n o
D 707172737475767778797A-
* p q r s t u v w x y z
D 303132333435363738392B2F'
* 0 1 2 3 4 5 6 7 8 9 + /
*****************************************************************
* Loop control
DI S 9P 0
DJ S 9P 0
DK S 9P 0
* write buffer for tmpf
Dtmpfwb S 512
Dtmpfwbb64 S 3900
Dtmpfwblen S 10I 0
* total size of tmpf
Dtmpf_size S 10I 0
* read buffer for attachment file
Datcfrb S 2850
Datcfrblen S 10I 0
*****************************************************************
* attachment size array
Datc_st_size S LIKE(st_size) DIM(30)
* structure stat ........................................ QSYSINC/SYS.STAT
Dstatinfo DS
* Data types in () are defined at QSYSINC/SYS.TYPES
* File mode (typedef unsigned int mode_t;)
D st_mode 10U 0
* File serial number (typedef unsigned int ino_t;)
D st_ino 10U 0
* Number of links (typedef unsigned short nlink_t;)
D*st_nlink 5U 0
D st_nlink 10U 0
* User ID of the owner of file (typedef unsigned int uid_t;)
D st_uid 10U 0
* Group ID of the group of file (typedef unsigned int gid_t;)
D st_gid 10U 0
* For regular files, the file size in bytes (typedef int off_t;)
D st_size 10I 0
* Time of last access (typedef long int time_t;)
D st_atime 10I 0
* Time of last data modification typedef (long int time_t;)
D st_mtime 10I 0
* Time of last file status change (typedef long int time_t;)
D st_ctime 10I 0
* ID of device containing file (typedef unsigned int dev_t;)
D st_dev 10U 0
* Size of a block of the file (typedef unsigned int size_t;)
D st_blksize 10U 0
* Allocation size of the file unsigned long
D st_allocsize 10U 0
* AS/400 object type (typedef char qp0l_objtype_t[11];)
D st_objtype 11
* Object data codepage unsigned short
D st_codepage 5U 0
* reserved - must be 0x00's char[62]
D st_reserved1 62 INZ(*ALLX'00')
* File serial number generation id unsigned int
D st_ino_gen_id 10U 0
*
D*****************************************************************
C* MAIN LINE CALCULATIONS
C*****************************************************************
C*** Entry Parms
C *ENTRY PLIST
C Parm Originator
C Parm ToReceiptions
C Parm OriginName
C Parm CcReceiptions
C Parm BCcReceiptions
C Parm Attachment
C Parm Subject
C Parm Message
C parm TxtF
C parm TxtFMbr
C parm Importnc
C parm Priority
C parm Receipt
C PARM tmpdir 64
C Exsr #INIT
* check sender
C Eval EmailAddr = Originator
C Exsr ChkEmail
* check receiption
C Exsr ChkRcpt
C*** Initialize error structure
C Eval BytesProvided = 0
C*** Initialize values
C Eval OriginLen = %Len(%Trimr(Originator))
C Eval Format = 'ADDR0100'
C Eval Reserved = 0
* Fill in the "Recipients" array
C do 1000 z
C if OutAddrArr(z) = ' '
C leave
C endif
C eval Address = OutAddrArr(z)
* check recipient's e-mail
C eval EMailAddr = Address
C exsr ChkEMail
C eval AddrLen = %len(%trimr(Address))
C eval DistrType = OutDistArr(z)
C if OutAddrArr(z+1) <> ' '
C eval OffSet= 280
C else
C eval OffSet=0
C endif
C eval Recipients(z) = recipient
C enddo
* Total number of recipients
C eval TotalRecp = z -1
C*** Write MIME file
C Exsr WriteHdr
C*** Call API to send e-mail
C CallB 'QtmmSendMail'
C Parm FileName
C Parm FileLen
C Parm Originator
C Parm OriginLen
C Parm Recipients
C Parm TotalRecp
C Parm APIError
C* MSGID dsply
c* dump
C*** Return to caller
C Exit Tag
C Eval *InLr = *On
C Return
*****************************************************************
* Initialize routine
C #INIT BEGSR
* US ASCII character set
* X'A2' = Cent sign, X'A3' = Pound sign, X'A5' = Yen sign
C MOVE *BLANKS a_c_256 256
C* EVAL rc = iconvw(a_x + X'A2A3A5' + NULL : a_c_256)
C Eval a_c_256 = a_x + X'A2A3A5' + NULL
c callp Translate(%len(%trim(a_c_256)):
c a_c_256 : 'QTCPEBC')
* print EBCDIC internal table
C MOVEL a_c_256 a_c_c
C MOVEL a_c_c a_c
C EVAL a_c_x = a_x + X'0D0A' + X'1B'
* check body text file
C If %trim(txtFile) <> '*NONE'
C Eval FullName = '/QSYS.LIB/' +
C %trim(TxtFLib) + '.LIB/' +
C %trim(TxtFile) + '.FILE/' +
C %trim(TxtFmbr) + '.MBR' + NULL
C Eval BodyDesc = open(%trimr(FullName)
C : O_RDONLY)
C If BodyDesc < 0
c eval err = errno
c callp die(%str(strerror(err)) + ' ' + fullname)
C return
C Else
C Eval ReturnInt = close(BodyDesc)
C EndIf
C EndIf
* check attachement
C If Nbrfiles > *ZERO
C and AttachFile(1) <> '*NONE'
C DO NbrFiles I
C EVAL fullname = %TRIM(AttachFile(I)) + NULL
C IF -1 = stat(%ADDR(fullname) : %ADDR(statinfo))
c eval err = errno
c callp die(%str(strerror(err)) + ' ' + fullname)
C return
C ENDIF
* store st_size to array
C EVAL atc_st_size(I) = st_size
* path must be stream file or doc
C IF (%SUBST(st_objtype : 1 : 10) <> '*STMF') AND
C (%SUBST(st_objtype : 1 : 10) <> '*DOC') AND
C (%SUBST(st_objtype : 1 : 10) <> '*DSTMF')
c callp die(%TRIM(AttachFile(I))+ ' ' +
c 'not a STMF or DOC type.')
C return
C ENDIF
* file size is 0
C IF st_size = 0
C ENDIF
C ENDDO
C EndIf
*
C ENDSR
C*****************************************************************
C* Write header portion of file
C*****************************************************************
CSR WriteHdr Begsr
C*** Open file
* open work file to write mail message
C TIME curtime
C MOVE curtime curtimec 26
C EVAL FileName = %TRIM(tmpdir) + '/SNDEMAIL_' +
C %TRIM(PsJobNum) + '-' +
C %TRIM(PsUsrid) + '-' +
C %TRIM(PsJobName) + '_' +
C %SUBST(curtimec : 1 : 23) + '.TXT'
C Eval FileLen = %Len(%Trimr(FileName))
C Eval FullName = %TRIMR(FileName)
C Eval Flags = O_CREAT + O_WRONLY + O_TRUNC +
C O_CCSID
C Eval FileDesc = open(%trimr(FullName)
C : Flags
C : S_IRWXU + S_IROTH
C : AsciiCodePage)
C Move *BLANKS tmpstr 45
C Eval ReturnInt = close(FileDesc)
C Eval FileDesc = open(%trimr(FullName)
C : O_RDWR + O_CCSID
C : S_IRWXU + S_IROTH
C : AsciiCodePage)
C* eval err = errno
C* eval tmpstr = %str(strerror(err))
C* dsply tmpstr
C* 'open0' dsply FileDesc
C*** Build MIME header fields
C If OriginName <> *BLANKS
C Eval rtnlen = smtphead(OriginName : outstr)
C Eval MSender =
C 'Sender: "' +
C %subst(outstr : 1 : rtnlen) +
C '"' + Originator
C Else
C Eval MSender =
C 'Sender: ' + Originator
C EndIf
C Eval MDateTime =
C 'Date: '
C eval rtnlen =
C encodemailaddr(Originator :
C OriginName : outstr)
C Eval MFrom =
C 'From: ' + %subst(outstr : 1 : rtnlen)
C Eval MMimeVer =
C 'MIME-Version: 1.0'
* Create Mto, Mcc, Mbcc mail string
C Exsr Crtdistr
C If Subject <> *Blanks
C eval InData = Subject
C callp smtphead(InData : outstr)
C Eval MSubject =
C 'Subject: ' + %trim(outstr)
C Else
C Eval MSubject =
C 'Subject: '
C Endif
C
C If Message <> *Blanks
C eval InData = Message
C eval outstr = *blanks
C callp to950(InData : outstr)
C eval Message = outstr
C endIf
* Add receipt notification, if requested so
C if Receipt = '*YES'
C eval MReceipt =
C 'Disposition-Notification-To: ' +
C %trim(Msender) + EOR
C endif
* Add the Importance header
C exsr SetImpo
* Add the Priority header
C exsr SetPrio
C Eval Data1 = %Trimr(MSender) +
C EOR +
C %Trimr(MDateTime) +
C EOR +
C %Trimr(MFrom) +
C EOR +
C %Trimr(MMimeVer) +
C EOR +
C %Trimr(MTo) +
C %Trim(MCc) +
C %Trim(MBCc) +
C EOR +
C %trimr(MReceipt) +
C %Trimr(MSubject) +
C EOR +
C %Trimr(MImportnc) +
C EOR +
C %Trimr(MPriority) +
C EOR +
C 'Content-Type: multipart/mixed; boundary=' +
C '"' + %Trimr(MBoundary) + '"' +
C EOR +
C EOR +
C 'This is a multi-part message in MIME ' +
C 'format.' + EOR + EOR +
C '--' + %Trimr(MBoundary) +
C EOR +
C* 'Content-Type: text/plain; charset=us-ascii'+
C 'Content-Type: text/plain; charset='+
C %trim(charset)+
C EOR +
C 'Content-Transfer-Encoding: 7bit' +
C EOR + EOR
C Eval Data2 =
C* %Trimr(Message) +
C EOR + EOR + EOR + EOR +
C '--' + %Trimr(MBoundary)
C*** Add attachment file(s) if requested
C If NbrFiles > *Zero
C and AttachFile(1) <> '*NONE'
C Exsr WriteHead
C Do NbrFiles Z 4 0
C Clear SavePos
C Eval Pos = %Scan('/':AttachFile(Z):1)
C Dow Pos > *Zero
C Eval SavePos = Pos
C Eval Pos = %Scan('/':AttachFile(Z):Pos+1)
C Enddo
C If SavePos <> *Zero
C Eval AttachName = %Subst(AttachFile(Z):SavePos+1)
C Else
C Eval AttachName = AttachFile(Z)
C Endif
C Eval Data = EOR +
C 'Content-Type: application/octet' +
C '-stream; name="' +
C %Trimr(AttachName) + '"' +
C EOR +
C* 'Content-Disposition: inline; filename="' +
C 'Content-Disposition: attachment;' +
C ' filename="' +
C %Trimr(AttachName) + '"' +
C EOR +
C* 'Content-Transfer-Encoding: 7bit' +
C 'Content-Transfer-Encoding: base64' +
C EOR + EOR
C* Write attached file heading
C Exsr WriteAttachHd
C*** Open file and write to MIME file
C Eval FullName = %TRIMR(AttachFile(Z)) + Null
C Exsr #WATC
C If Z >= NbrFiles
C Eval Data = EOR +
C '--' + %Trimr(MBoundary) + '--' +
C EOR + EOR
C Else
C Eval Data = EOR +
C '--' + %Trimr(MBoundary)
C Endif
C Exsr WriteAttachHd
C Enddo
C Else
C*** Write end of MIME file for e-mail w/ no attachment
C Exsr WriteHead
C Endif
C*** Close file
C Eval ReturnInt = close(FileDesc)
C***
C Endsr
C*****************************************************************
C* Check Receiption Addr
C*****************************************************************
CSR ChkRcpt Begsr
C*** Add receiption to arrary
C z-add 1 x
C If Nbrofto > *Zero
C Z-add 1 Z
C Do Nbrofto Z
C Eval RcptNA =
C %SUBST(to : to_replacem(Z) + 3 : 306)
c Eval EMailAddr = RcptAddr
C Exsr ChkEmail
c Eval ToAddrArr(z) = RcptAddr
C Eval ToNameArr(z) = RcptName
c Eval OutAddrArr(x) = RcptAddr
c Eval OutDistArr(x) = DTO
C Eval x = x + 1
C EndDo
C EndIf
C If Nbrofcc > *Zero
C Z-add 1 Z
C Do Nbrofcc Z
C Eval RcptNA =
C %SUBST(cc : cc_replacem(Z) + 3 : 306)
c Eval EMailAddr = RcptAddr
C Exsr ChkEmail
c Eval CcAddrArr(z) = RcptAddr
C Eval CcNameArr(z) = RcptName
c Eval OutAddrArr(x) = RcptAddr
c Eval OutDistArr(x) = DCC
C Eval x = x + 1
C EndDo
C EndIf
C If Nbrofbcc > *Zero
C Z-add 1 Z
C Do Nbrofbcc Z
C Eval RcptNA =
C %SUBST(bcc : bcc_replacem(Z) + 3 : 306)
c Eval EMailAddr = RcptAddr
C Exsr ChkEmail
c Eval BCcAddrArr(z) = RcptAddr
C Eval BCcNameArr(z) = RcptName
c Eval OutAddrArr(x) = RcptAddr
c Eval OutDistArr(x) = DBCC
C Eval x = x + 1
C EndDo
C EndIf
CSR EndSr
C*****************************************************************
C* Create Distribution
C*****************************************************************
CSR CrtDistr Begsr
* Process the "To"-s
C z-add 0 NumberOf 4 0
C z-add 0 x 4 0
C 1 DO 1000 x
C if ToNameArr(x) = ' '
C leave
C endif
C eval NumberOf = NumberOf +1
C Z-add 0 rtnlen 10 0
C eval rtnlen =
C encodemailaddr(ToAddrArr(x):
C ToNameArr(x): outstr)
C if NumberOf = 1
C eval MTo = 'To: ' + %subst(outstr:1 : rtnlen)
C else
C eval MTo = %trimr(Mto) +
C ' ,' + %subst(outstr:1 : rtnlen)
C endif
C ENDDO
* Process the "Cc"-s
C z-add 0 NumberOf 4 0
C z-add 0 x 4 0
C 1 DO 1000 x
C if CcNameArr(x) = ' '
C leave
C endif
C eval NumberOf = NumberOf +1
C Z-add 0 rtnlen 10 0
C eval rtnlen =
C encodemailaddr(CcAddrArr(x):
C CcNameArr(x): outstr)
C if NumberOf = 1
C eval MCc = 'cc: ' + %subst(outstr:1 : rtnlen)
C else
C eval MCc = %trimr(MCc) +
C ' ,' + %subst(outstr:1 : rtnlen)
C endif
C ENDDO
* Process the "BCc"-s
C z-add 0 NumberOf 4 0
C z-add 0 x 4 0
C 1 DO 1000 x
C if BCcNameArr(x) = ' '
C leave
C endif
C eval NumberOf = NumberOf +1
C Z-add 0 rtnlen 10 0
C eval rtnlen =
C encodemailaddr(BCcAddrArr(x):
C BCcNameArr(x): outstr)
C if NumberOf = 1
C eval MBCc= 'bcc: '+ %subst(outstr:1 : rtnlen)
C else
C eval MBCc = %trimr(MBCc) +
C ' ,' + %subst(outstr:1 : rtnlen)
C endif
C ENDDO
C If %len(%trim(MCc)) > 0
C eval MTo = %trim(MTo) + EOR
C EndIf
C If %len(%trim(MBCc)) > 0
C eval MCc = %trim(MCc) + EOR
C EndIf
CSR EndSr
*=====================================================================
* Set importance
*=====================================================================
C SetImpo begsr
C eval MImportnc = *Blanks
C select
C when Importnc = '*LOW'
C eval MImportnc = 'Importance: low'
C when Importnc = '*MED'
C eval MImportnc = 'Importance: medium'
C when Importnc = '*HIG'
C eval MImportnc = 'Importance: high'
C endsl
C endsr
*=====================================================================
* Set priority
*=====================================================================
C SetPrio begsr
C eval MPriority = *blanks
C select
C when Priority = '*NUR'
C eval MPriority = 'Priority: non-urgent'
C when Priority = '*NRM'
C eval MPriority = 'Priority: normal'
C when Priority = '*URG'
C eval MPriority ='Priority: urgent'
C endsl
C endsr
*****************************************************************
* write message text to temp file
C #WBODY BEGSR
*
C Eval FullName = '/QSYS.LIB/' +
C %trim(TxtFLib) + '.LIB/' +
C %trim(TxtFile) + '.FILE/' +
C %trim(TxtFmbr) + '.MBR' + NULL
C
C Eval BodyDesc = open(%trimr(FullName)
C : O_RDONLY)
C If BodyDesc < 0
c eval err = errno
c callp die(%str(strerror(err)) + ' ' + fullname)
C return
C EndIf
C* append CRLF to Message after
C If %len(%trim(Message)) > 0
C Eval BytesWrt = write(FileDesc
C : %ADDR(CRLF)
C : 2)
C EndIf
C*** Read from file and write to MIME file
C Eval BytesRead = read(BodyDesc
C : %Addr(DataRead)
C : 80)
C Dow BytesRead > 0
C Eval InData = %Subst(DataRead:1:BytesRead) +
C EOR
C eval outstr = *blanks
C Eval rtnlen = to950(InData : outstr)
C Eval BytesWrt = write(FileDesc
C : %ADDR(outstr)
C : rtnlen)
C Eval BytesRead = read(BodyDesc
C : %Addr(DataRead)
C : 80)
C Enddo
C Eval ReturnInt = close(BodyDesc)
*
C ENDSR
C*****************************************************************
C* Write head
C*****************************************************************
CSR WriteHead Begsr
C* conver ebcdic to ascii
C Move *Blanks DataEnd 6
C Eval DataEnd = '--' + EOR + EOR
C Z-add 0 Data1Len 5 0
C Z-add 0 Data2Len 5 0
C Eval Data1len = %len(%trimr(Data1))
C Eval Data2len = %len(%trimr(Data2))
C* Eval %SubSt(Data2 : Data2Len+1 : 6) =
C* DataEnd
C* Eval Data2len = Data2len + 6
C a_c:a_x XLATE Data1 Data1
C EOR:CRLF XLATE Data1 Data1
C a_c:a_x XLATE Data2 Data2
C EOR:CRLF XLATE Data2 Data2
C a_c:a_x XLATE DataEnd DataEnd
C EOR:CRLF XLATE DataEnd DataEnd
C Eval BytesWrt = write(FileDesc
C : %ADDR(Data1)
C : Data1len)
C Eval BytesWrt = write(FileDesc
C : %ADDR(Message)
C : %len(%trim(Message)))
C If %trim(TxtFile) <> '*NONE'
C Exsr #WBODY
C EndIf
C Eval BytesWrt = write(FileDesc
C : %ADDR(Data2)
C : Data2len)
C If NbrFiles > *Zero
C and AttachFile(1) = '*NONE'
C Eval BytesWrt = write(FileDesc
C : %ADDR(DataEnd)
C : %len(%trim(DataEnd)))
C EndIf
C* Exsr WriteFile
CSR EndSr
C*****************************************************************
C* Write Attach head
C*****************************************************************
CSR WriteattachHd Begsr
C Z-add 0 Data1Len
C Eval Data1len = %len(%trimr(Data))
C a_c:a_x XLATE Data Data
C EOR:CRLF XLATE Data Data
C Eval BytesWrt = write(FileDesc
C : %ADDR(Data)
C : Data1Len)
CSR EndSr
C*****************************************************************
CSR WriteFile Begsr
C*** Write to file
C Eval BytesWrt = write(FileDesc
C : %ADDR(Data)
C : %LEN(%TRIMR(Data)))
C***
C Endsr
*===========================
* If at sign not found in e-mail address, generate an escape message
C ChkEMail begsr
C eval r = %scan(AtS:EMailAddr)
C if r = 0
C eval MsgDta = '"' + AtS +
C '" not found in"' +
C %trim(EMailAddr) + '".'
c callp die(MsgDta)
C return
C endif
C endsr
*****************************************************************
* encode attachment file and write to temp file
*****************************************************************
C #WATC BEGSR
* open attachment file
C EVAL atcfd = open(%ADDR(FullName) : 1)
*
C Z-ADD 0 rtotal 9 0
| C Z-ADD 0 inscrlf 3 0
* read 2850 byte -> base64 -> write 3900 byte
* 2850 byte (before encode) = 57(chars/line) * 50(line)
* 57 -base64encode-> * 4/3 + CRLF = 78byte
* 78 * 50 = 3900 byte (after encode)
*
* read stream file
C DO atc_st_size(Z)J
C EVAL atcfrblen = read(atcfd : %ADDR(atcfrb) :
C 2850)
C IF atcfrblen = -1
C ENDIF
* end of file
C IF atcfrblen = 0
C LEAVE
C ENDIF
C Z-ADD atcfrblen b64instrlen 9 0
* accumulate read bytes
C ADD atcfrblen rtotal
* adjust to miltiply of 3 if less than 2850 bytes read
C IF atcfrblen < 2850
* don't use %DIV/%REM for V4R2 or earlier version
C atcfrblen DIV 3 b64count 9 0
C MVR b64mod 1 0
C 3 SUB b64mod b64pad 1 0
C IF b64mod > 0
C EVAL %SUBST(atcfrb : atcfrblen + 1 : b64pad)
C = X'0000'
C ADD b64pad b64instrlen
C ENDIF
C ENDIF
* Base64 encode. should be faster than procedure call...
C Z-ADD 1 tmpfwblen
C DO b64instrlen K
C EVAL b64i = %SUBST(atcfrb : K : 3)
C MOVE *ALLX'00' b64ap
* 1st byte of outchr
C MOVE b64i1 b64ap1L
C DIV 4 b64ap1
* 2nd
C TESTB '6' b64i1 20
C 20 BITON '2' b64ap2L
C TESTB '7' b64i1 20
C 20 BITON '3' b64ap2L
C TESTB '0' b64i2 20
C 20 BITON '4' b64ap2L
C TESTB '1' b64i2 20
C 20 BITON '5' b64ap2L
C TESTB '2' b64i2 20
C 20 BITON '6' b64ap2L
C TESTB '3' b64i2 20
C 20 BITON '7' b64ap2L
* 3rd
C TESTB '4' b64i2 20
C 20 BITON '2' b64ap3L
C TESTB '5' b64i2 20
C 20 BITON '3' b64ap3L
C TESTB '6' b64i2 20
C 20 BITON '4' b64ap3L
C TESTB '7' b64i2 20
C 20 BITON '5' b64ap3L
C TESTB '0' b64i3 20
C 20 BITON '6' b64ap3L
C TESTB '1' b64i3 20
C 20 BITON '7' b64ap3L
* 4th
C BITOFF '01' b64i3
C MOVE b64i3 b64ap4L
*
C EVAL %SUBST(tmpfwbb64 : tmpfwblen : 4) =
C %SUBST(b64a : b64ap1 + 1 : 1) +
C %SUBST(b64a : b64ap2 + 1 : 1) +
C %SUBST(b64a : b64ap3 + 1 : 1) +
C %SUBST(b64a : b64ap4 + 1 : 1)
C ADD 4 tmpfwblen
* append CRLF in every 19 encodes (57->76byte)
C ADD 1 inscrlf
C IF inscrlf = 19
C EVAL %SUBST(tmpfwbb64 : tmpfwblen : 2) = CRLF
C ADD 2 tmpfwblen
C Z-ADD 0 inscrlf
C ENDIF
C ENDDO 3
*
C EVAL tmpfwblen = tmpfwblen - 1
* adjust last line
C IF atcfrblen < 2850
* remove appended CRLF
C IF inscrlf = 0
C SUB 2 tmpfwblen
C ENDIF
* adjust '='
C IF b64mod > 0
C EVAL %SUBST(tmpfwbb64 : tmpfwblen - b64pad + 1 :
C b64pad) = X'3D3D'
C ENDIF
* add CRLF
C EVAL %SUBST(tmpfwbb64 : tmpfwblen + 1 : 2) = CRLF
C ADD 2 tmpfwblen
C ENDIF
* accumulate total bytes written
C ADD tmpfwblen wtotal 9 0
* write to temp file
C EVAL BytesWrt = write(FileDesc : %ADDR(tmpfwbb64)
C : tmpfwblen)
C IF BytesWrt = -1
C ENDIF
* write operation not complete
C IF BytesWrt <> tmpfwblen
C ENDIF
* accumulate total bytes written to temp file
C EVAL tmpf_size = tmpf_size + BytesWrt
* no more data to read
C IF atcfrblen < 2850
C LEAVE
C ENDIF
*
C ENDDO 2850
* close attachment file
C IF -1 = close(atcfd)
C ENDIF
* compare read total with file size
C IF rtotal <> atc_st_size(I)
C ENDIF
*
C ENDSR
*****************************************************************
* encode mail address and description
* return : length of outstr
* < 0 return code form procedure 'smtphead'
* mtype : 'From:', 'To:', 'cc:', 'bcc:', 'Reply-To:' I
* mail_addr : mail address I
* mail_desc : mail address description I
* outstr : encoded string O
*
Pencodemailaddr B
Dencodemailaddr PI 10I 0
D mail_addr 256 VALUE
D mail_desc 50 VALUE
D outstr 9999
*
Doutstrlen S 3P 0
*
* mail adderss description is blank
C EVAL outstrlen = smtphead(%TRIM(mail_desc) :
C outstr)
C IF outstrlen < 0
C RETURN outstrlen
C ENDIF
C EVAL outstr = ' "' +
C %SUBST(outstr : 1 : outstrlen) + '"'
C Eval outstrlen = outstrlen + 3
* folding
C IF outstrlen - fold + 8 > 65
C EVAL %SUBST(outstr : outstrlen + 1 : 2) = CRLF
C EVAL outstrlen = outstrlen + 2
C ENDIF
* append mail address
C EVAL outstr = %SUBST(outstr : 1 : outstrlen) +
C ' <' + %TRIM(mail_addr) + '>'
C EVAL outstrlen = outstrlen +
C %LEN(%TRIM(mail_addr)) + 3
*
C RETURN outstrlen
Pencodemailaddr E
*****************************************************************
* Generate SMTP mail header
* return : length of outstr
* 0 nothing to process
* -1, -2, -3, -4 return code from other procedures
* -5 invalid character set (should not happen though)
* instr : input string (EBCDIC) I
* outstr : encoded string (EBCDIC) O
* (dftjobccsid : CCSID of instr )R
* (charset : 'US-ASCII' or 'US-ASCII-NONSAFE' or 'ISO-8859-1' )M
* ( or 'ISO-2022-JP' (Japanese) )
* (fold : last folding position of encoded string )M
*
Psmtphead B
Dsmtphead PI 10I 0
D instr 9999 VALUE
D outstr 9999
*
Dinstrlen S 3P 0
Dascii S 9999
Drc S 3P 0
*
C EVAL instrlen = %LEN(%TRIMR(instr))
C IF instrlen = 0
C RETURN 0
C ENDIF
* encode string
C SELECT
* Plain ASCII
C WHEN charset = 'US-ASCII'
C EVAL outstr = instr
C RETURN %LEN(%TRIM(instr))
C WHEN charset = 'Big5'
* convert jobccsid -> 950
C EVAL rc = to950(instr : ascii)
C IF rc < 0
C RETURN rc
C ENDIF
C RETURN Bencode(ascii : rc : outstr)
*
C ENDSL
*
C RETURN -5
*
Psmtphead E
*****************************************************************
* Convert EBCDIC string to Big5
*
* return : length of Big5 string
* 0 no graphic character found
* -1 iconv error (->950)
* ebcdic : ebcdic representation of original string I
* c950 : Big5 string O
*
Pto950 B
Dto950 PI 10I 0
D ebcdic 9999 VALUE
D c950 9999
*
D***
D QDCXLATE PR ExtPgm('QDCXLATE')
D CvtDtaLen 5 0
D CvtDta 10
D SBCSTabNam 10
D SBCSTabLib 10
D OutputDta 10
D Outbuflen 5 0
D Outcvtlen 5 0
D DBCSID 10
D ShiftInOut 1
D CvtType 10
D
D CvtDtaLen S 5 0
D CvtDta S 9999
D SBCSTabNam S 10
D SBCSTabLib S 10
D OutputDta S 9999
D Outbuflen S 5 0
D Outcvtlen S 5 0
D DBCSID S 10 inz('*BG5')
D ShiftInOut S 1
D CvtType S 10 inz('*EA')
Debcdic_len S 3P 0
Dc950_len S 10I 0
Dc950_chr S 1
* no character other than space (X'40') found
C Eval CvtType = '*EA'
C Eval DBCSID = '*BG5'
C Eval Outbuflen = 9999
C EVAL CvtDtaLen = %LEN(%TRIMR(ebcdic))
C IF CvtDtaLen = 0
C RETURN 0
C ENDIF
C Eval CvtDta = ebcdic
* convert to 950(Big5)
C CallP QDCXLATE ( CvtDtaLen :
C CvtDta :
C SbcsTabnam :
C SbcsTabLib :
C OutputDta :
C OutBufLen :
C Outcvtlen :
C DBCSID :
C ShiftInOut :
C CvtType )
C EVAL c950_len = Outcvtlen
C IF c950_len < 0
C RETURN -1
C ENDIF
C Eval c950 = %subst(OutputDta :1 : Outcvtlen)
*
C RETURN c950_len
Pto950 E
C*****************************************************************
* 'B' encode for DBCS mail header
* return : length of newbuf
* -4 especials found <- 2002-05-06 out of use
* ascii : input string I
* buflen : length of input string I
* newbuf : output (converted) string O
* (structured : string is in structured field of mail header )R
* (charset : 'US-ASCII' or 'US-ASCII-NONSAFE' or 'ISO-8859-1' )R
* (fold : > 0 if folding occured )M
*
*****************************************************************
PBencode B
DBencode PI 3P 0
D ascii 256 VALUE
D buflen 3P 0 VALUE
D newbuf 256
*
Dbufpos S 3P 0
Dcslen S 3P 0
Dchr S 1
Desc S 3 INZ(G0ascii)
Dline S 44
Dlinel S 2P 0
*
C EVAL cslen = %LEN(%TRIM(charset))
C EVAL newbuf = '=?' + %TRIM(charset) + '?B?'
C EVAL bufpos = cslen + 6
C EVAL fold = 0
*
1 C DO buflen I 3 0
| C EVAL chr = %SUBST(ascii : I : 1)
* escape char
2 C IF chr = X'1B'
| C EVAL esc = %SUBST(ascii : I : 3)
C EVAL I = I + 2
C EVAL linel = linel + 3
* normal char
E C ELSE
* DBCS
3 C IF esc = G0k78 or esc = G0k83
| C EVAL I = I + 1
| C EVAL linel = linel + 2
* SBCS
E C ELSE
| C EVAL linel = linel + 1
3 C ENDIF
2 C ENDIF
* Base64 encode when line legnth exceeds 35 bytes or end of string
2 C IF (linel > 35) or (I >= buflen)
| C EVAL line = %SUBST(ascii : I - linel + 1 : linel)
* add ascii escape sequence if not end as SBCS
3 C IF esc <> G0ascii and esc <> G0roman
| C EVAL %SUBST(line : linel + 1 : 3) = G0ascii
| C EVAL linel = linel + 3
3 C ENDIF
* adjust to multiple of 3 for base64 encode
C linel DIV 3 b64count 3 0
C MVR b64mod 3 0
C 3 SUB b64mod b64pad 3 0
3 C IF b64mod > 0
| C EVAL %SUBST(line : linel + 1 : b64pad) = X'0000'
| C EVAL linel = linel + b64pad
3 C ENDIF
* Base64 encode (3 to 4)
3 C DO linel J 3 0
| C EVAL %SUBST(newbuf : bufpos : 4) =
C base64e(%SUBST(line : J : 3))
| C EVAL bufpos = bufpos + 4
3 C ENDDO 3
C Z-ADD 0 linel
* Pad '='
3 C IF b64mod > 0
C EVAL %SUBST(newbuf : bufpos - b64pad : b64pad) =
C '=='
C ENDIF
* end of input string or maximum line length
3 C IF (I >= buflen) or (bufpos > 180)
| C EVAL %SUBST(newbuf : bufpos : 2) = '?='
| C EVAL bufpos = bufpos + 2
<- C LEAVE
| * folding
E C ELSE
| C EVAL %SUBST(newbuf : bufpos : cslen + 10) =
C '?=' + X'0D0A' + ' =?' +
C %TRIM(charset) + '?B?'
C EVAL fold = bufpos + 6
C EVAL bufpos = bufpos + cslen + 10
* add ascii escape sequence if not end as SBCS
4 C IF esc <> G0ascii and esc <> G0roman
| C EVAL %SUBST(newbuf : bufpos : 4) = base64e(esc)
| C EVAL bufpos = bufpos + 4
|4 C ENDIF
3 C ENDIF
| *
|2 C ENDIF
1 C ENDDO
*
C RETURN bufpos - 1
*****************************************************************
PBencode E
* Base64 encode (3 to 4)
* inchr : 3 bytes string to convert I
* return : Converted character (ASCII)
*
*****************************************************************
Pbase64e B
Dbase64e PI 4
D inchr 3 VALUE
*
Dchrs DS
D i1 1 1
D i2 2 2
D i3 3 3
Dap1DS DS
D ap1 1 2U 0 INZ(0)
D ap1L 2 2
Dap2DS DS
D ap2 1 2U 0 INZ(0)
D ap2L 2 2
Dap3DS DS
D ap3 1 2U 0 INZ(0)
D ap3L 2 2
Dap4DS DS
D ap4 1 2U 0 INZ(0)
D ap4L 2 2
*
Db64e C 'ABCDEFGHIJKLMNOPQRSTUVWXYZ-
D abcdefghijklmnopqrstuvwxyz-
D 0123456789+/'
C MOVE inchr chrs
* 1st byte of outchr
C MOVE i1 ap1L
C DIV 4 ap1
* 2nd
C TESTB '6' i1 20
C 20 BITON '2' ap2L
C TESTB '7' i1 20
C 20 BITON '3' ap2L
C TESTB '0' i2 20
C 20 BITON '4' ap2L
C TESTB '1' i2 20
C 20 BITON '5' ap2L
C TESTB '2' i2 20
C 20 BITON '6' ap2L
C TESTB '3' i2 20
C 20 BITON '7' ap2L
* 3rd
C TESTB '4' i2 20
C 20 BITON '2' ap3L
C TESTB '5' i2 20
C 20 BITON '3' ap3L
C TESTB '6' i2 20
C 20 BITON '4' ap3L
C TESTB '7' i2 20
C 20 BITON '5' ap3L
C TESTB '0' i3 20
C 20 BITON '6' ap3L
C TESTB '1' i3 20
C 20 BITON '7' ap3L
* 4th
C BITOFF '01' i3
C MOVE i3 ap4L
*
C RETURN %SUBST(b64e : ap1 + 1 : 1) +
C %SUBST(b64e : ap2 + 1 : 1) +
C %SUBST(b64e : ap3 + 1 : 1) +
C %SUBST(b64e : ap4 + 1 : 1)
*
Pbase64e E
*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
* This ends this program abnormally, and sends back an escape.
* message explaining the failure.
*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
P die B
D die PI
D peMsg 256A const
D SndPgmMsg PR ExtPgm('QMHSNDPM')
D MessageID 7A Const
D QualMsgF 20A Const
D MsgData 256A Const
D MsgDtaLen 10I 0 Const
D MsgType 10A Const
D CallStkEnt 10A Const
D CallStkCnt 10I 0 Const
D MessageKey 4A
D ErrorCode 32766A options(*varsize)
D dsEC DS
D dsECBytesP 1 4I 0 INZ(256)
D dsECBytesA 5 8I 0 INZ(0)
D dsECMsgID 9 15
D dsECReserv 16 16
D dsECMsgDta 17 256
D wwMsgLen S 10I 0
D wwTheKey S 4A
c eval wwMsgLen = %len(%trimr(peMsg))
c if wwMsgLen<1
c return
c endif
c callp SndPgmMsg('CPF9897': 'QCPFMSG *LIBL':
c peMsg: wwMsgLen: '*ESCAPE':
c '*PGMBDY': 1: wwTheKey: dsEC)
c return
P E
*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
* This procedure return call socket C API errno
*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
P errno B
D errno PI 10I 0
D p_errno S *
D wwreturn S 10I 0 based(p_errno)
C eval p_errno = @__errno
c return wwreturn
P E
File : QCMDSRC
Member: SNDEMAIL
Type : CMD
Usage : CRTCMD lib/SNDEMAIL SRCFILE(srclib/srcfile) PGM(lib/SNDEMAILR)
SNDEMAIL SENDERNAME('Sender name') SENDERADDR(name@company.com.tw)
TO(('Receipt 中文 ' name@company.com.tw)) ATTACHMENT('/tmp/qcustcdt.txt') SUBJECT(' 主旨 ') TXTF(QGPL/QDDSSRC) TXTMBR(QDSIGNON)
如果要匯出 AS/400 資料庫使用 CPYTOIMPF 指令. 例如: CPYTOIMPF FROMFILE(QIWS/QCUSTCDT) TOSTMF('/tmp/qcustcdt.txt') STMFCODP
AG(*PCASCII) RCDDLM(*CRLF) DTAFMT(*FIXED)
本指令只支援 IFS 目錄下的附件,並不支援 /QSYS.LIB/library.lib/...。
/*********************************************************************/
/* To create this command, issue the following: */
/* CRTCMD lib/SNDEMAIL SRCFILE(srclib/srcfile) PGM(lib/SNDEMAILR) */
/* November 2003 */
/* Author: Vengoal Chang */
/*********************************************************************/
CMD PROMPT('Send an E-mail Message')
PARM KWD(SENDERADDR) TYPE(*PNAME) LEN(255) MIN(1) +
EXPR(*YES) PROMPT('Sender email address' 2)
PARM KWD(TO) TYPE(TO) MIN(1) MAX(30) +
PROMPT('Recipient' 3)
PARM KWD(SENDERNAME) TYPE(*CHAR) LEN(256) +
DFT(*NONE) SPCVAL((*NONE '')) EXPR(*YES) +
PROMPT('Sender name' 1)
PARM KWD(CC) TYPE(CC) MAX(30) PROMPT('CC' 4)
PARM KWD(BCC) TYPE(BCC) MAX(30) PROMPT('BCC' 5)
PARM KWD(ATTACHMENT) TYPE(*PNAME) LEN(256) +
DFT(*NONE) SNGVAL((*NONE)) MAX(30) +
EXPR(*YES) PROMPT('File attachment' 6)
PARM KWD(SUBJECT) TYPE(*CHAR) LEN(256) DFT(*NONE) +
SPCVAL((*NONE '')) EXPR(*YES) +
PROMPT('Subject' 7)
PARM KWD(MESSAGE) TYPE(*CHAR) LEN(512) +
DFT(*NONE) SPCVAL((*NONE '')) EXPR(*YES) +
PROMPT('Message' 8)
PARM KWD(TXTF) TYPE(QUAL1) PROMPT('Text source +
file')
QUAL1: QUAL TYPE(*NAME) LEN(10) DFT(*NONE) SPCVAL((*NONE +
*NONE))
QUAL TYPE(*NAME) LEN(10) DFT(*LIBL) +
SPCVAL((*LIBL) (*CURLIB)) PROMPT('Library')
PARM KWD(TXTMBR) TYPE(*NAME) LEN(10) PROMPT('Text +
source member')
PARM KWD(IMPORTNC) TYPE(*CHAR) LEN(4) RSTD(*YES) +
DFT(*MED) VALUES(*LOW *MED *HIG) +
PROMPT('Importance' )
PARM KWD(PRIORITY) TYPE(*CHAR) LEN(4) RSTD(*YES) +
DFT(*NRM) VALUES(*NUR *NRM *URG) +
PROMPT('Priority' )
PARM KWD(RECEIPT) TYPE(*CHAR) LEN(4) RSTD(*YES) +
DFT(*NO) VALUES(*NO *YES) PROMPT('Return +
receipt' )
PARM KWD(TMPDIR) TYPE(*PNAME) LEN(64) DFT('/TMP') +
PMTCTL(*PMTRQS) +
PROMPT('Work directory')
TO: ELEM TYPE(*CHAR) LEN(50) EXPR(*YES) +
PROMPT('Name')
ELEM TYPE(*CHAR) LEN(256) EXPR(*YES) +
PROMPT('Mail address')
CC: ELEM TYPE(*CHAR) LEN(50) MIN(1) EXPR(*YES) +
PROMPT('Name')
ELEM TYPE(*CHAR) LEN(256) MIN(1) EXPR(*YES) +
PROMPT('Mail address')
BCC: ELEM TYPE(*CHAR) LEN(50) MIN(1) EXPR(*YES) +
PROMPT('Name')
ELEM TYPE(*CHAR) LEN(256) MIN(1) EXPR(*YES) +
PROMPT('Mail address')
訂閱:
文章 (Atom)