FTP PeopleCode

FTP PeopleCode

I was recently working on an interface that needed to pull files from the vendor via FTP peoplecode. More specifically, I needed to retrieve an unknown number of ASCII files from a vendor’s server, with no specific naming convention.

I have done this before using a ‘call system’ command to kick off a bat script that would handle staging the files. This worked pretty good, and I eventually got the bat script to perform fairly decent error handling. But this interface was being written in App Engine. well more accurately, a PeopleCode program kicked off by an App Engine shell (I still hate writing App Engine programs the “PeopleSoft recommended” way).

PeopleCode does provide the GetAttachment function, but this seemed quite limiting in that it required me knowing the4name of the file I wanted to retrieve. I needed something more flexible. Searching the internet, I found a Slerp article by David Jen that explains how to use PeopleCode’s external library declaration to utilize the FTP functionality in the wininet.dll library. This was great, but it still required me to know the name of the file I wanted to ‘get’. But, at least, I now had another road to pursue. After understanding more of what was available to me in the wininet.dll and playing around with different configurations, I came up with the following:

/************************* DECLARE EXTERNAL FUNCTIONS *************************/
Declare Function GetLastError Library “kernel32”
() Returns long As number;

Declare Function FormatMessageA Library “kernel32”
(long Value As number, long Value As number, long Value As number, long Value As number, string Ref As string, long Value As number, long Value As number) Returns long As number;

Declare Function InternetOpenA Library “wininet.dll”
(string Value As string, long Value As number, string Value As string, string Value As string, long Value As number) Returns long As number;

Declare Function InternetConnectA Library “wininet.dll”
(long Value As number, string Value As string, integer Value As number, string Value As string, string Value As string, long Value As number, long Value As number, long Value As number) Returns long As number;

Declare Function FtpSetCurrentDirectoryA Library “wininet.dll”
(long Value As number, string Value As string) Returns long As number;

Declare Function FtpGetFileA Library “wininet.dll”
(long Value As number, string Value As string, string Value As string, long Value As number, long Value As number, long Value As number, long Value As number) Returns boolean;

Declare Function FtpDeleteFileA Library “wininet.dll”
(long Value As number, string Value As string) Returns boolean;

Declare Function FtpCommandA Library “wininet.dll”
(long Value As number, long Value As number, long Value As number, string Value As string, long Value As number, long Ref As number) Returns boolean;

Declare Function InternetReadFile Library “wininet.dll”
(long Value As number, string Ref As string, integer Value As number, integer Ref As number) Returns boolean;

Declare Function InternetCloseHandle Library “wininet.dll”
(long Value As number) Returns integer As number;

/*********************** END DECLARE EXTERNAL FUNCTIONS ***********************/

/* Program Variable Declarations */
Local array of string &gFileList;
Local number &gHostOpen, &gHostConnect;
Local string &gHostFileName;
Local number &gReturnCode;
Local boolean &gReturnStatus;

Function GetNTMessage(&inReturnCode As number) Returns string;
Local number &l_MsgLength;
Local string &l_ReturnMsg;

&l_MsgLength = FormatMessageA(4096, 0, &inReturnCode, 0, &l_ReturnMsg, 256, 0);
If &l_MsgLength > 0 Then
Return &l_ReturnMsg;
Else
Return “”;
End-If;
End-Function;

Function FTPOpenHostConnection(&inLogFile As File) Returns number;
Local number &l_OPEN_PRECONFIG = 0;
Local number &l_Handle;

/* Open connection to host */
&l_Handle = InternetOpenA(“Peoplecode FTP”, &l_OPEN_PRECONFIG, “”, “”, 0);
If &l_Handle = 0 Then
&inLogFile.WriteLine(“FTP ERROR: Unable to open Internet connection.”);
End-If;
Return &l_Handle;
End-Function;

Function FTPConnectToHost(&inLogFile As File, &inHandleOpen As number, &inHostSystem As string, &inHostDirectory As string, &inHostFTPAccount As string, &inHostPassword As string) Returns number;
Local number &l_FTP_PORT = 21;
Local number &l_INET_FTP = 1;
Local number &l_INET_PASSIVE = 134217728;

Local number &l_HostDirSet, &l_Handle;
Local number &l_ReturnCode;

/* Login to Host */
&l_Handle = InternetConnectA(&inHandleOpen, &inHostSystem, &l_FTP_PORT, &inHostFTPAccount, &inHostPassword, &l_INET_FTP, &l_INET_PASSIVE, 0);

If &l_Handle = 0 Then
&inLogFile.WriteLine(“FTP ERROR: Unable to open connection to ” | &inHostSystem | ” !!!”);
Return 0;
End-If;

/* Change remote directory */
If All(&inHostDirectory) Then /* If host directory passed, set it. */
&l_HostDirSet = FtpSetCurrentDirectoryA(&l_Handle, &inHostDirectory);
Else
&l_HostDirSet = 1;
End-If;

If &l_HostDirSet = 0 Then
&inLogFile.WriteLine(“FTP ERROR: Unable to set directory to ” | &inHostDirectory | ” !!!”);
&l_ReturnCode = InternetCloseHandle(&l_Handle);
Return 0;
End-If;

Return &l_Handle;
End-Function;

Function FTPGetFileList(&inLogFile As File, &inHandleConnect As number) Returns array of string;
Local string &l_FTP_CMD = “NLST”;
Local number &l_ASCII = 1;

Local array of string &l_FileList = CreateArrayRept(“”, 0);

Local number &l_FTPHandle;
Local string &l_Out, &l_Text_Buffer;
Local number &l_Bytes = 100;
Local number &l_Bytes_Read;
Local boolean &l_ReturnStatus;
Local number &l_ReturnCode;

&l_ReturnStatus = FtpCommandA(&inHandleConnect, 1, &l_ASCII, &l_FTP_CMD, 0, &l_FTPHandle);
If &l_FTPHandle = 0 Then
&inLogFile.WriteLine(“FTP ERROR: Unable to get directory !!!”);
Return &l_FileList;
Else
&inLogFile.WriteLine(“Getting List of files from host.”);
&l_ReturnStatus = InternetReadFile(&l_FTPHandle, &l_Text_Buffer, &l_Bytes, &l_Bytes_Read);
While &l_Bytes_Read > 0
&l_Out = &l_Out | &l_Text_Buffer;
&l_ReturnStatus = InternetReadFile(&l_FTPHandle, &l_Text_Buffer, &l_Bytes, &l_Bytes_Read);
End-While;
&l_ReturnCode = InternetCloseHandle(&l_FTPHandle);
End-If;
&l_FileList = Split(&l_Out, Char(13) | Char(10));
&inLogFile.WriteLine(String(&l_FileList.Len) | ” file(s) found.”);
Return &l_FileList;
End-Function;

Function FTPGetFile(&inLogFile As File, &inHandleConnect As number, &inHostFileName As string, &inLocalDirectory) Returns boolean;
Local number &l_FTP_ASCII = 1;
Local string &l_LocalFileName;
Local number &l_ReturnCode;
Local string &l_ReturnMsg;
Local boolean &l_GetFile;

/* Get (Session, local file, remote file, failexist, flags&attibutes, flags, context */
&inLogFile.WriteLine(“Getting ” | &inHostFileName | “…”);
&l_LocalFileName = &inLocalDirectory | &inHostFileName;
&l_GetFile = FtpGetFileA(&inHandleConnect, &inHostFileName, &l_LocalFileName, 0, 0, &l_FTP_ASCII, 0);

If Not &l_GetFile Then
&l_ReturnCode = GetLastError();
&l_ReturnMsg = GetNTMessage(&l_ReturnCode);
&inLogFile.WriteLine(“FTP ERROR: ” | String(&l_ReturnCode) | ” : ” | &l_ReturnMsg);
&inLogFile.WriteLine(“FTP ERROR: Unable to retrieve file !!!”);
Return False;
End-If;

/* Sucessful Get */
&inLogFile.WriteLine(” Get Sucessful.”);
Return True;
End-Function;

Function FTPDeleteFile(&inLogFile As File, &inHandleConnect As number, &inHostFileName As string) Returns boolean;
Local boolean &l_DeleteFile;
Local number &l_ReturnCode;
Local string &l_ReturnMsg;

&l_DeleteFile = FtpDeleteFileA(&inHandleConnect, &inHostFileName);

If Not &l_DeleteFile Then
&l_ReturnCode = GetLastError();
&l_ReturnMsg = GetNTMessage(&l_ReturnCode);
&inLogFile.WriteLine(” FTP ERROR: ” | String(&l_ReturnCode) | ” : ” | &l_ReturnMsg);
&inLogFile.WriteLine(” FTP ERROR: Unable to delete file.”);
Return False;
End-If;

/* SUCCESS */
&inLogFile.WriteLine(” File Deleted from HRSmart server.”);
Return True;
End-Function;

/*****************************************************************************/
/* MAIN PROGRAM
/*****************************************************************************/

/* Log File */
Local File &gLogFile;

/* Host information */
Local string &HOSTSERVER = “www.example.com”;
Local string &HOSTACCOUNT = “usename”;
Local string &HOSTPASSWORD = “password”;
Local string &HOSTDIRECTORY = “”;
Local string &FILE_MASK = “%”;

/* A File Object &gLogFile needs to have already been open prior to calling this routine. */
/* Log information is written to this file */

&gHostOpen = FTPOpenHostConnection(&gLogFile);
If &gHostOpen > 0 Then
&gHostConnect = FTPConnectToHost(&gLogFile, &gHostOpen, &HOSTSERVER, &HOSTDIRECTORY, &HOSTACCOUNT, &HOSTPASSWORD);
If &gHostConnect > 0 Then
&gFileList = FTPGetFileList(&gLogFile, &gHostConnect);
While &gFileList.Len > 0
&gHostFileName = &gFileList.Shift();
If DBPatternMatch(&gHostFileName, &FILE_MASK, False) Then
If FTPGetFile(&gLogFile, &gHostConnect, &gHostFileName, &LOCALDIRECTORY) Then
&gReturnStatus = FTPDeleteFile(&gLogFile, &gHostConnect, &gHostFileName);
End-If;
End-If;
End-While;
&gReturnCode = InternetCloseHandle(&gHostConnect);
&gReturnCode = InternetCloseHandle(&gHostOpen);
Else
&gReturnCode = InternetCloseHandle(&gHostOpen);
End-If;
End-If;

One thing to note… it seems that PeopleCode can only use DLL functions when they return datatypes that are understood by PeopleCode (strings, numbers, boolean). In trying to find a way to search the FTP directory, I came across a couple functions named FindFirstFile and FindNextFile. Unfortunately, these returned a pointer to a pointer to a WIN32_FIND_DATA structure. I could not figure out a way to use these. As a result, I had to take a slightly longer approach using the FTPCommand function.

Credit :http://ideatec.blogspot.com/2005/03/ftp-in-peoplecode.html

Posted in Peoplecode.