You are on page 1of 13

H BNDDIR('QC2LE') OPTION(*NODEBUGIO:*SRCSTMT) H DFTACTGRP(*NO) ACTGRP(*NEW) ************************************************************** ** FTPSNDFILE - (c) 2005 - Robert Cozzi, Jr. ** All rights reserved.

** Software is provided "as is" for illustrative/example ** purposes only. No warranty is expressed or implied and ** none is given. ** Permission to reference in other software is granted ** with the following conditions: ** (1) No money is charge is exchanged in any way. ** (2) This notice along with the copyright notification is ** remains in any distribution of this software. ** (3) The right to reproduce this software for publication ** purposes is expressly denied. Instead, please reference ** the original source code via a URL link. ************************************************************** ** ** ************************************************************** ** This source is set up to run on OS/400 V5R1 and was ** later modified so that it would compile and run on V4R4. ** Conditional compiling is used so that you can take ** advantage of the OS/400 V5 features in RPG IV such as ** qualified data structures and the LIKEDS keyword. ** ** To Compile this source member, you must first create ** a source file name QFTPSRC with a record length ** of at least 150 bytes, as follows: ** ** CRTSRCPF QGPL/QFTPSRC RCDLEN(150) ** ** USAGE NOTES: This source file receives the FTP scripts ** that are generated by the program. ************************************************************** FQFTPSRC UF A E /IF DEFINED(*V5R1M0) F /ENDIF DISK USROPN RENAME(QFTPSRC:FTPSRCREC) EXTFILE(szFTPSRC) EXTMBR(szFTPSrcMbr)

** Input parameter list. ** Although not strictly required, this program is ** normally called as the CPP of a command definition. ** These parameters are set up for such a call. D FtpSndFile PR D RemoteIP 128A D LocalFile 20A D LocalMbr 10A D RemoteFile 20A D RemoteMbr 10A D bReplace 1N D RemoteUser 64A D RemotePWD 64A D TransferMode 10A D ftpSrcFile 20A D ftpSrcMbr 10A D ftplogFile 20A D ftplogMbr 10A D bFtpDspLog 1N


FtpSndFile RemoteIP LocalFile LocalMbr RemoteFile RemoteMbr bReplace RemoteUser RemotePWD TransferMode ftpSrcFile ftpSrcMbr ftplogFile ftplogMbr bFtpDspLog

PI 128A 20A 10A 20A 10A 1N 64A 64A 10A 20A 10A 20A 10A 1N

** This /INCLUDEs (or /COPYs) are required. ** If you do not have QSYSINC library installed ** on your system, the program will not compile. ** Also, even though SEU does not recognize /INCLUDE ** directives, they will compile on OS/400 V4R5 and later. /INCLUDE QSYSINC/QRPGLESRC,QUSRMBRD /INCLUDE QSYSINC/QRPGLESRC,QUSROBJD /INCLUDE QSYSINC/QRPGLESRC,QUSEC ** Normally, you would call the RPG xTools ** to remove an unwanted info/diag message ** but to keep this routine ** independent, we call the OS/400 QMHRMVPM API. ** Remove Message from Program Queue API D QmhRmvPM PR ExtPgm('QMHRMVPM') D CallStackEntry 64A Const OPTIONS(*VARSIZE) D CallStackCount 10I 0 Const D MsgKey 4A Const D MsgToRemove 10A Const /IF NOT DEFINED(*V5R1M0) D ApiErrorDS Like(QUSEC) /ELSE D ApiErrorDS LikeDS(QUSEC) /ENDIF ** Retrieve member description QRtvMbrD PR ExtPgm('QUSRMBRD') szRecvBuffer 32766A Options(*VARSIZE) nLenRecvBuf 10I 0 Const Format 8A Const FileName 20A Const MbrName 10A Const bOvrProc 1A Const /IF DEFINED(*V5R1M0) D apierror LikeDS(QUSEC) OPTIONS(*NOPASS) D bFindMbr 1A Const OPTIONS(*NOPASS) D/ELSE D apierror Like(QUSEC) OPTIONS(*NOPASS) D/ENDIF D D D D D D D ** The OS/400 QUSROBJD API is used to get the library ** name for an unqualified object. For example: ** *LIBL/MYOBJ would be returned with QGPL as the

** name of the library containing the object. D QRtvObjD PR ExtPgm('QUSROBJD') D rtnData 32766A OPTIONS(*VARSIZE) D nRtnDataLen 10I 0 Const D Format 8A Const D QualObj 20A Const D ObjType 10A Const /IF DEFINED(*V5R1M0) D apierror LikeDS(QUSEC) D/ELSE D apierror Like(QUSEC) D/ENDIF ** The RPG xTools C runtime prototype is used ** to run CL commands (FTP commands in this case). D system PR 10I 0 ExtProc('system') D szCmd * Value OPTIONS(*STRING) ** Normally, the RPG xTools WrtJobLog() ** subprocedure would be called. Instead ** we call the OS/400 Unix-API Qp0zLprintF() ** to accomplish a similar result. D Qp0zLprintf PR 10I 0 ExtProc('Qp0zLprintf') D szOutputString... D * Value OPTIONS(*STRING) D * Value OPTIONS(*STRING:*NOPASS) ** JobLog() is a wrapper for the Qp0zLprintf() Unix-API. D JobLog PR D szMsg 1024A Const VARYING ** If we're using V5R1 or later, then declare the ** data structures used by the APIs as Qualified ** data structures based on data structure templates. ** Otherwise, just use the LIKE keyword to create ** large field names that are moved back and forth ** between the QSYSINC DS and the fields. /IF DEFINED(*V5R1M0) D MbrDesc DS LikeDS(QUSM0100) D ObjDesc DS LikeDS(QUSD0100) D APIError DS LikeDS(QUSEC) D/ELSE D MbrDesc S Like(QUSM0100) D ObjDesc S Like(QUSD0100) D APIError S Like(QUSEC) D/ENDIF D D D D PSDS JobName USRPRF JobNbr ** Defaults and DFTFTPSrc DFTFTPSrcMbr APPEND REPLACE SDS 10A 10A 6A Constants C C C C Overlay(PSDS:244) Overlay(PSDS:254) Overlay(PSDS:264)


Const('QTEMP/QFTPSRC') Const('*') Const('APPEND') Const('(Replace')

D GENERICMBR D D D D D D D D D D D D D D D D D szFTPSrc szFtpSrcMbr szFTPLog szFTPLogMbr bNoLog bDspLog bAppend szReplace bGeneric

C S S S S S S S S S 21A 10A 21A 10A 1N 1N 1N 10A 1N

Const('GENERIC') Inz(DftFTPSRC) Inz('SENDSCR') Inz('QTEMP/QFTPLOG') Inz('FTPLOG') Inz(*OFF) Inz(*OFF) Inz(*OFF) Varying Inz(*OFF)

** Local OS/400 library/file/member name Local_File DS lclFile 10A lclLib 10A LclMbr 10A ** Remote OS/400 library/file/member name Remote_File DS RmtFile 10A RmtLib 10A RmtMbr 10A

** Remote IP or domain name, user ID and password D** Remote_Location... D RL DS D RmtIP 128A D RmtUser 64A D RmtPwd 64A ** Transfer mode BINARY | ASCII D TFRMode 10A D D D D D D D D Inz('BINARY')

** FTP Script Source file, library and member name. Ftp_SrcFile DS SrcFile 10A SrcLib 10A SrcMbr 10A ** FTP log file, library and member name. Ftp_LogFile DS LogFile 10A LogLib 10A LogMbr 10A ** Long variables to hold CL and FTP command strings. szSndFile S 128A Varying szRmtFile S 128A Varying ovrFTPSrc S 128A Varying ovrFTPLog S 128A Varying szOvrdbf S 128A Varying


** ADDPFM is used to add/clear source file ** members in the FTP script source file. D ADDPFM S 256A Varying D FtpCmd S 256A Varying ** 8 "digit" date in character format

D YYMD S ** RPGIV-version of "UDATE"; I ** initialized to "today" (the D Today S D D D D D C

8A true date data-type system's run date). D Inz(*SYS) DATFMT(*ISO)

** Remove "Buffer Overflow" msg when opening Source file CallStkE s 32A CallStkCnt s 10I 0 MsgKey s 4A MsgToRmv s 10A MsgAPIErr s Inz(*ALLX'00') LIKE(QUSEC) ** End Reove "Buffer Overflow" msg eval *INLR = *ON

** NOTE: Parms are expected to be passed in through a ** CL command interface. They are declared on the ** "procedure" interface statements. C if %Parms >= 1 C eval RmtIP = RemoteIP C endif C C C C C C C C C C C C C C C C C C C C C C C C C C C C if eval endif if eval endif if eval endif if eval endif ** Replace(*YES|*NO) if eval endif if eval endif if if eval else eval endif endif if eval endif %Parms >= 2 Local_File = LocalFile %Parms >= 3 lclMbr = LocalMbr %Parms >= 4 Remote_File = RemoteFile %Parms >= 5 RmtMbr = RemoteMbr

%Parms >= 6 bAppend = NOT bReplace %Parms >= 7 RmtUser = RemoteUser %Parms >= 8 %subst(RemotePWD:1:3) = '*US' RmtPWD = RmtUser RmtPWD = RemotePwd

%Parms >= 9 TfrMode = TransferMode

** FTP Script source file and library name


if eval eval endif if eval eval endif

%Parms >= 10 ftp_SrcFile = ftpSrcFile szFtpSrc = %TrimR(srcLib) + '/' + %TrimR(srcFile) %Parms >= 11 szFtpSrcMbr= FtpSrcMbr srcMbr = ftpSrcMbr

** FTP log file and library name if %Parms >= 12 if ftpLogFile = *BLANKS or ftpLogFile = '*NONE' or ftpLogFile = '*STDIO' eval bNoLog = *ON else eval bNoLog = *OFF endif ** If FTPLOG(*SRCFILE) is specified, then use the same ** file and library name as the source file, otherwise ** use the specific FTPLOG entry. C if %subst(ftpLogFile:1:4) = '*SRC' C eval ftp_LogFile = ftpSrcFile C else C eval ftp_LogFile = ftpLogFile C endif C C C C C C C C eval endif if eval eval endif %Parms >= 13 and bNoLog = *OFF szFtpLogMbr= FtpLogMbr logMbr = ftpLogMbr szFTPLog = %TrimR(LogLib) + '/' + %TrimR(logFile)

** Display FTP log after FTP Send finishes? ** NOTE: DSPLOG(*STDIO) causes the internal FTP ** standard output log to be displayed. C if %Parms >= 14 C if bFtpDspLog = *OFF C or ftpLogFile = '*NONE' C or ftpLogFile = *BLANKS C or ftpLogFile = '*STDIO' C eval bDspLog = *OFF C else C eval bDspLog = *ON C endif C endif ** If no sendfile member name is specified, use the ** send file's name as the member name. C if lclMbr = *Blanks or lclMbr = '*FILE' C eval lclMbr = lclFile C endif

** If the member name is *ALL, *FIRST or *LAST, then ** translate that value to the real member name. ** This is done by calling the QUSRMBRD API. C if lclMbr = '*ALL' C eval lclMbr = '*' C endif C if lclMbr = '*FIRST' C or lclMbr = '*LAST' /IF DEFINED(*V5R1M0) C clear MbrDesc C clear ApiError C eval ApiError.QUSBPRV = %size(ApiError) /ELSE C clear QUSEC C clear QUSM0100 C eval QUSBPRV = %size(ApiError) C eval ApiError = QUSEC C eval MbrDesc = QUSM0100 /ENDIF ** Get the member description, and hence, it's actual name. ** (i.e., convert *LAST or *FIRST into a real member name). C CallP QRtvMbrD(MbrDesc:%size(mbrDesc): C 'MBRD0100':Local_File: C lclMbr:'0': ApiError) ** Everything go okay? ** then extract the real member name. /IF DEFINED(*V5R1M0) C if ApiError.QUSBAVL = 0 C eval lclMbr = MbrDesc.QUSMN02 /ELSE C eval QUSEC = ApiError C if QUSBAVL = 0 C eval QUSM0100 = MbrDesc C eval lclMbr = QUSMN02 /ENDIF C else ** If the RTVMBRD failed, use the file name as the member name. C eval lclMbr = lclFile C endif C endif ** If *LIBL or blanks is used for the library name, ** on the Local File, then use QUSROBJD to find the real ** library name. C if LclLib = *Blanks or C %Subst(LclLib:1:1) = '*' /IF DEFINED(*V5R1M0) clear clear eval /ELSE C clear C clear C eval C eval C eval /ENDIF C C C ObjDesc ApiError ApiError.QUSBPRV = %size(ApiError) QUSEC QUSD0100 QUSBPRV = %size(ApiError) ApiError = QUSEC ObjDesc = QUSD0100


** Call QUSROBJD to get the library name of the file being sent. CallP QRtvObjD(ObjDesc : %size(ObjDesc) : 'OBJD0100': local_File : '*FILE': ApiError) ApiError.QUSBAVL = 0 ObjDesc.QUSRL01 <> *BLANKS lclLib = %TrimR(ObjDesc.QUSRL01)

/IF DEFINED(*V5R1M0) if if eval endif endif /ELSE C eval C if C eval C if C eval C endif C endif /ENDIF C endif

QUSEC = ApiError QUSBAVL = 0 QUSD0100 = ObjDesc QUSRL01 <> *BLANKS lclLib = %TrimR(QUSRL01)

** FIX: Moved RMTFILE(*FROMFILE) logic to after *LIBL translation. If TOFILE(*FROMFILE) is specified, copy the file name. If the TOFILE's library is blank or *LIBL (expected) then also copy the FROMFILE's library name to the TOFILE's library name. C if %subst(RmtFile:1:5) = '*FROM' C eval RmtFile = LclFile C endif ** NOTE: Can't use *LIBL or *CURLIB as the ** target/remote file's library name. C if RmtLib = *BLANKS C or %subst(RmtLib:1:1) = '*' C eval RmtLib = LclLib C endif ** If no remote member name is specified, use the file name. ** NOTE: We can't use *FIRST or *LAST for the remote ** file since we can't run QUSRMBRD over that file. C if RmtMbr = *Blanks C or RmtMbr = '*FILE' C or RmtMbr = '*RMTFILE' C eval RmtMbr = RmtFile C else C if RmtMbr = '*FROMMBR' C eval RmtMbr = LclMbr C else C if RmtMbr = '*FROMFILE' C eval RmtMbr = LclFile C endif C endif C endif ** FIX: End-Fix ** Build the FTP string containing the lib/file/mbr to send. C C eval szSndFile = '/qsys.lib' + '/' + %TrimR(lclLib) + '.lib' + ** ** ** **

C C ** ** ** ** ** C C C C C C C

'/' + %TrimR(lclFile) + '.file' + '/' + %TrimR(lclMbr) + '.mbr' Build the remote file name If a generic name, such as AP* or *ALL, such as * is passed in, use the generic member name as the local name. Then we also have to do a CD (change directory) on the remote system to send the generic members. if %scan('*':lclMbr) > 0 eval lclMbr = GENERICMBR eval bGeneric = *ON else eval bGeneric = *OFF endif

if NOT bGeneric ** Regular member name? C eval szRmtFile = '/qsys.lib' + C '/' + %TrimR(RmtLib) + '.lib' + C '/' + %TrimR(RmtFile) + '.file' + C '/' + %TrimR(RmtMbr) + '.mbr' C else ** When sending a generic member name, then we use szRmtFile ** as the "current directory" not as the target file/member name. ** Since no member name is needed, only lib/file is specified. C eval szRmtFile = '/qsys.lib' + C '/' + %TrimR(RmtLib) + '.lib' + C '/' + %TrimR(RmtFile) + '.file' C endif ** Translate special member identifiers to the actual mbr name. ** Script source member if eval eval endif ** Log member C if C eval C eval C endif C C C C FtpSrcMbr = '*FROMMBR' szFtpSrcMbr = LclMbr SrcMbr = LclMbr FtpLogMbr = '*FROMMBR' szFtpLogMbr = LclMbr logMbr = LclMbr

** If the caller specified SRCMBR(*GEN) then create ** a source member name based on today's date. C if szFtpSrcMbr = '*GEN' ** Get today's date as YYYYMMDD. C *ISO0 MOVE TODAY YYMD ** The member named is: FSyyyymmdd C eval FTPSRCMBR = 'FS' + YYMD C endif ** ** ** ** Attempt to create the source file for the FTP Script. If its already there, there's not problem with trying to create it again... the (e) on the CALLP will swallow the "already exists" error.


if szFTPSrc = dftFTPSRC callp(e) system('CRTSRCPF ' + szFTPSrc + ' RCDLEN(152)') endif ** Add and clear the FTP Script source member eval AddPFM = 'ADDPFM FILE(' + %TrimR(szFTPSRC) + ') ' + 'MBR(' + %TrimR(szFTPSRCMbr) + ') ' + 'SRCTYPE(FTPSCRIPT)' callp(e) system(AddPFM) eval AddPfm = 'CLRPFM FILE(' + %TrimR(szFTPSRC) + ') ' + 'MBR(' + %TrimR(szFTPSRCMbr) + ') ' callp(e) system(AddPFM) ** Add and/or clear the FTP Log source member, if requested. if %subst(logFile:1:1) <> '*' and logFile <> *BLANKS eval AddPfm = 'ADDPFM FILE(' + %TrimR(szFtpLog) + ') ' + 'MBR(' + %TrimR(szFTPLogMbr) + ') ' + 'SRCTYPE(FTPLOG)' callp(e) system(AddPFM) eval AddPfm = 'CLRPFM FILE(' + %TrimR(szFTPLog) + ') ' + 'MBR(' + %TrimR(szFTPLogMbr) + ')' callp(e) system(AddPFM) endif


/IF NOT DEFINED(*V5R1M0) ** Override to the new script source member. ** We do this only when compiling on an OS/400 Ver 4 system. C eval OVRFTPSRC = 'OVRDBF FILE(QFTPSRC) ' + C 'TOFILE(' + %TrimR(szFTPSrc) + ') ' + C 'MBR(' + %TrimR(szFTPSRCMbr) + ') ' C callp(e) system(OVRFTPSRC) C/ENDIF ** Open and build the FTP INPUT Script Open QFTPSrc if NOT %OPEN(QFTPSRC) callp Joblog('Source file for FTP script + failed to open. FTP cancelled.') return endif ** Remove the "Buffer overflow" message C eval QUSBPRV = %size(ApiError) C eval ApiError = QUSEC C Callp QMHRMVPM('*':0:' ':'*NEW':ApiError) C C C C C C ** User ID & PWD C C C C if eval endif ** If PWD(*USER) is specified, make the PWD RmtUser = '*CURRENT' or %subst(RmtUser:1:3) = '*US' RmtUser = USRPRF


** the same as the user profile. if %Subst(RmtPWD:1:3) = '*US' eval RmtPWD = RmtUser endif ** Send the FTP user ID and password to the remote FTP server. eval srcdta = %Trim(RmtUser) + ' ' + %Trim(RmtPWD) Write FTPSrcRec ** Change the transfer mode to BINARY or ASCII. eval srcdta = %Trim(TFRMode) Write FtpSrcRec

** Change the Name Format to 1. ** NOTE: This may cause the remote location to send a 501 error, ** but that's okay. C eval srcdta = 'NAMEFMT 1' C Write FtpSrcRec ** If sending a bunch of members (generic or *ALL) then ** issue the CD (change directory) command on the remote server. C if bGeneric C eval srcDta = 'CD ' + %TrimR(szRmtFile) C Write FtpSrcRec ** Generic/Multi-member MPUT C eval srcdta = 'MPUT ' + %TrimR(szSndFile) C Write FtpSrcRec C C C C C C C C C C C C C C C ** Sending a Single member? Use the PUT or APPEND command. else if bAppend eval srcdta = 'APPEND ' + %TrimR(szSndFile) + ' ' + %TrimR(szRmtFile) else eval srcdta = 'PUT ' + %TrimR(szSndFile) + ' ' + %TrimR(szRmtFile) endif Write FtpSrcRec endif ** Say goodbye to the FTP server. eval srcdta = 'QUIT' Write FtpSrcRec Close QFTPSrc ************************************************************** ** At this point, the FTP script has been created and should be ** stored in the source file, library and member specified. ** If debugging, use Debug Shift+F9 to open a command-line ** and then use SEU or DSPPFM to view/review the FTP script. ************************************************************** ************************************************************** ** Prepare the FTP CL command by overriding the FTP input ** to the script that we just created. **************************************************************



szOvrdbf = 'OVRDBF FILE(INPUT) ' + ' TOFILE(' + %TrimR(szFtpSrc) + ')' + ' MBR(' + %TrimR(szFTPSRCMBR) + ')' + ' OVRSCOPE(*JOB) ' callp(e) system(szOvrdbf)

************************************************************** ** If an FTP log is requested, override the output to ** the FTP log file, library and member. ** NOTE: If LOG(*NONE) is specified, the log is overriden ** to a dummy file in QTEMP that is not displayed. ** This is done so that the STDIO log that is ** normally generated by FTP is not displayed. ************************************************************** C if logFile = '*NONE' or bNoLog = *ON C eval szOvrdbf = 'OVRDBF FILE(OUTPUT) ' + C 'TOFILE(QTEMP/QFTPNULL) ' + C 'MBR(NONE) ' + C 'OVRSCOPE(*JOB) ' C callp(e) system(szOvrdbf) C else C if %subst(logFile:1:1) <> '*' C and logFile <> *BLANK C and bNoLog = *OFF C eval szOvrdbf = 'OVRDBF FILE(OUTPUT) ' + C 'TOFILE(' + %TrimR(szFtpLog) + ') ' + C 'MBR(' + %TrimR(szFTPLogMbr) + ') ' + C 'OVRSCOPE(*JOB) ' C callp(e) system(szOvrdbf) C endif C endif ** Evoke FTP to send the file to the remote ** location using the FTP script we just created. C eval FtpCmd = 'FTP ' + C '''' + %TRIM(RmtIP) + '''' ** Run the FTP command. C callp(e) system(FtpCmd) ** Now go back and obscure the remote user's password open(e) QFTPSRC if %OPEN(QFTPSRC) ** Remove the "Buffer overflow" message C eval QUSBPRV = %size(ApiError) C eval ApiError = QUSEC C Callp QMHRMVPM('*':0:' ':'*NEW':ApiError) C C C C C C C C C C ** Obscure the remote user's password in the FTP script source member if NOT %Error read FTPSRCREC eval srcdta = %Trim(RmtUser) + ' ' + '********' update FTPSRCREC endif endif callp(e) system(' DLTOVR FILE(INPUT) LVL(*JOB) ') callp(e) system(' DLTOVR FILE(OUTPUT) LVL(*JOB) ') ** If the end-user requested that the FTP log be displayed, ** and an FTP log outfile was specified, then display it


** using DSPPFM. You could change this to DSPF also. if %subst(logFile:1:1) <> '*' and logFile <> *BLANK if NOT bNoLog callp(e) system(' DSPPFM FILE(' + szFtpLog + ')' + ' MBR(' + szFtpLogMbr + ')' + ' FROMRCD(*END) ' ) endif endif ENDPGM TAG return

****************************************************** ** Write an impromptu message to the joblog ** ****************************************************** P JobLog B D JobLog PI D szMsg 1024A Const VARYING C callp Qp0zLprintf(szMsg + X'25') P JobLog E