Function FTP_Services(@Service, @Params)
/***********************************************************************************************************************

    This program is proprietary and is not to be used by or disclosed to others, nor is it to be copied without written
    permission from SRP Computer Solutions, Inc.

    Name        :   FTP_Services

    Description :   Handler program for all FTP services.

    Notes       :   See the following URLs for a list of FTP/SFTP commands:
                    - http://www.nsftools.com/tips/MSFTP.htm
                    - https://the.earth.li/~sgtatham/putty/0.70/puttydoc.txt

    Parameters  :
        Service         [in] -- Name of the service being requested
        Param1-10   [in/out] -- Additional request parameter holders
        Response       [out] -- Response to be sent back to the Controller (MCP) or requesting procedure

    Metadata    :

    History     :   (Date, Initials, Notes)
        07/10/18    dmb     Original programmer.
        11/13/24    djm     Add ListDirectory service.
        01/08/24    djm     Changed ListDirectory to preserve host error message and set Error_Services.

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

#pragma precomp SRP_PreCompiler

$insert LOGICAL
$insert SERVICE_SETUP
$insert FTP_REQUESTS_EQUATES
$insert NOTIFICATION_EQUATES
Equ     CRLF$ to \0D0A\
Equ     Tab$  to Char(9)

Declare function    FTP_Services, Memory_Services, Logging_Services, SRP_Path, SRP_Decode, RTI_CreateGUID
Declare function    Environment_Services, Database_Services, Utility, SRP_Run_Command
Declare subroutine  FTP_Services, Memory_Services, Logging_Services, SRP_Run_Command, GetTempPath, GetTempFileName
Declare subroutine  Database_Services, RList, Obj_Notes, Shipment_Services

LogPath         = Environment_Services('GetApplicationRootPath') : '\LogFiles\FTP'
LogDate         = Oconv(Date(), 'D4/')
LogTime         = Oconv(Time(), 'MTS')
LogFileName     = LogDate[7, 4] : '-' : LogDate[1, 2] : '-' : LogDate[4, 2] : '.log'
Headers         = 'Logging DTM' : @FM : 'Activity' : @FM : 'Host' : @FM : 'FTP Request ID or Local/Remote File' : @FM : 'Remote Directory' : @FM : 'Error'
ColumnWidths    = 20 : @FM : 20 : @FM : 30 : @FM : 75 : @FM : 50 : @FM : 100
objLog          = Logging_Services('NewLog', LogPath, LogFileName, CRLF$, ' ', Headers, ColumnWidths, False$, False$)
LoggingDTM      = LogDate : ' ' : LogTime   ; // Logging DTM

GoToService else
    Error_Services('Add', Service : ' is not a valid service request within the ' : ServiceModule : ' module.')
end

Return Response OR ''


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Service Parameter Options
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Options BOOLEAN         = True$, False$
Options HOSTS           = '10.131.104.18', 'ftp3.vis.com.tw', 'ftp1.towersemi.com', '10.72.176.48', '172.23.28.185', '172.28.150.80'
Options COMMANDS        = 'put', 'append', 'get'
Options SERVERS         = 'MESSA005', 'MESSA01EC'


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Services
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

//----------------------------------------------------------------------------------------------------------------------
// SendFile
//
// Host             - FTP server name or IP. - [Required]
// LocalFile        - Local file to send to the FTP server. - [Required if EncodedData is empty]
// EncodedData      - Base64 encoded to send to the FTP server. - [Required if LocalFile is empty]
// Username         - Username to authenticate against the FTP server. - [Optional]
// Password         - Password to authenticate against the FTP server. - [Optional]
// LocalDirectory   - Required working directory of the local computer. Used with the "lcd" FTP command. - [Optional]
// RemoteDirectory  - Required working directory of the remote computer. Used with the "cd' FTP command. - [Optional]
// RemoteFile       - Remote file that will be created on the FTP server. - [Optional]
// ScriptPath       - Local directory and/or file name to use for the script file. - [Optional]
// DeleteScript     - Boolean flag to indicate if the script file should be deleted after being ran. - [Optional]
// SSH              - Boolean flag to indicate if SSH (Secure FTP) is required. Default is False. - [Optional]
//
// Sends the indicated local file or encoded data to the indicated host.
//----------------------------------------------------------------------------------------------------------------------
Service SendFile(Host=HOSTS, LocalFile, EncodedData, Username, Password, LocalDirectory, RemoteDirectory, RemoteFile, ScriptPath, DeleteScript=BOOLEAN, SSH=BOOLEAN)
    
    LogData     = ''
    LogData<1>  = LoggingDTM
    LogData<2>  = 'Start ' : Service
    LogData<3>  = Host
    LogData<4>  = LocalFile
    LogData<5>  = RemoteDirectory
    Logging_Services('AppendLog', objLog, LogData, @RM, @FM)
    
    If (Host NE '') AND ((LocalFile NE '') OR (EncodedData NE '')) then
        If LocalFile EQ '' then
            // Data to send to the FTP server is coming in as Base64 string rather than a path to a local file. Decode the
            // string and create a temp file.
            LocalFile   = FTP_Services('ConvertEncodedData', EncodedData)
        end
        ScriptPath  = FTP_Services('CreateScript', ScriptPath, Host, LocalFile, Username, Password, LocalDirectory, RemoteDirectory, RemoteFile, 'put', SSH)
        If Error_Services('NoError') then
            FTP_Services('RunScript', ScriptPath, Host, Username, Password, LocalFile, RemoteDirectory, DeleteScript, SSH)
        end
        If EncodedData NE '' then
            OSDelete LocalFile
        end
    end else
        Error_Services('Add', 'Host, LocalFile, or EncodedData argument was missing from the ' : Service : ' service.')
    end
    
    ServiceError    = Error_Services('GetMessage')
    LogData<1>      = LoggingDTM
    LogData<2>      = 'Stop  ' : Service
    LogData<3>      = Host
    LogData<4>      = LocalFile
    LogData<5>      = RemoteDirectory
    LogData<6>      = Error_Services('GetMessage')
    Logging_Services('AppendLog', objLog, LogData, @RM, @FM)
    Error_Services('Set', ServiceError)
    
end service


//----------------------------------------------------------------------------------------------------------------------
// AppendFile
//
// Host             - FTP server name or IP. - [Required]
// LocalFile        - Local file to send to the FTP server. - [Required if EncodedData is empty]
// EncodedData      - Base64 encoded to send to the FTP server. - [Required if LocalFile is empty]
// Username         - Username to authenticate against the FTP server. - [Optional]
// Password         - Password to authenticate against the FTP server. - [Optional]
// LocalDirectory   - Required working directory of the local computer. Used with the "lcd" FTP command. - [Optional]
// RemoteDirectory  - Required working directory of the remote computer. Used with the "cd' FTP command. - [Optional]
// RemoteFile       - Remote file that will be appended on the FTP server. - [Optional]
// ScriptPath       - Local directory and/or file name to use for the script file. - [Optional]
// DeleteScript     - Boolean flag to indicate if the script file should be deleted after being ran. - [Optional]
// SSH              - Boolean flag to indicate if SSH (Secure FTP) is required. Default is False. - [Optional]
//
// Appends the indicated local file or encoded data to the indicated host.
//----------------------------------------------------------------------------------------------------------------------
Service AppendFile(Host=HOSTS, LocalFile, EncodedData, Username, Password, LocalDirectory, RemoteDirectory, RemoteFile, ScriptPath, DeleteScript=BOOLEAN, SSH=BOOLEAN)
    
    LogData     = ''
    LogData<1>  = LoggingDTM
    LogData<2>  = 'Start ' : Service
    LogData<3>  = Host
    LogData<4>  = LocalFile
    LogData<5>  = RemoteDirectory
    Logging_Services('AppendLog', objLog, LogData, @RM, @FM)
    
    If (Host NE '') AND ((LocalFile NE '') OR (EncodedData NE '')) then
        If LocalFile EQ '' then
            // Data to send to the FTP server is coming in as Base64 string rather than a path to a local file. Decode the
            // string and create a temp file.
            LocalFile   = FTP_Services('ConvertEncodedData', EncodedData)
        end
        If SSH EQ True$ then
            // SFTP does not support the append command. To simulate, the file must first be retrieved using the get
            // command and then appended locally. The resulting file is then sent back using the put command.
            TempFile    = FTP_Services('GetTempFile')
            If TempFile NE '' then
                // Use a temp file to hold the file from the host.
                TempFileName    = SRP_Path('GetFilename', TempFile)
                TempFilePath    = SRP_Path('GetDirectory', TempFile)
                // The name of the remote file is indicated in the append command or it simply defaults to the local file.
                If RemoteFile NE '' then
                    TempRemoteFile  = RemoteFile
                end else
                    TempRemoteFile  = LocalFile
                end
                // Perform the get command. This will put any remote file retrieved into the temp file location.
                FTP_Services('GetFile', Host, TempRemoteFile, TempFileName, Username, Password, TempFilePath, RemoteDirectory, '', True$, SSH)
                If Error_Services('NoError') then
                    // Read the remote file and the local file data and append.
                    OSRead OrigFile from TempFile else OrigFile = ''
                    OSRead AppendFile from LocalFile else AppendFile = ''
                    OrigFile   := AppendFile
                    OSWrite OrigFile to LocalFile
                    // Delete the temp file.
                    OSDelete TempFile
                    // Put the newly appended local file to the host server.
                    ScriptPath  = FTP_Services('CreateScript', ScriptPath, Host, LocalFile, Username, Password, LocalDirectory, RemoteDirectory, RemoteFile, 'put', SSH)
                    If Error_Services('NoError') then
                        FTP_Services('RunScript', ScriptPath, Host, Username, Password, LocalFile, RemoteDirectory, DeleteScript, SSH)
                    end
                end
            end else
                Error_Services('Add', 'Error getting a temp file in the ' : Service : ' service.')
            end
        end else
            ScriptPath  = FTP_Services('CreateScript', ScriptPath, Host, LocalFile, Username, Password, LocalDirectory, RemoteDirectory, RemoteFile, 'append', SSH)
            If Error_Services('NoError') then
                FTP_Services('RunScript', ScriptPath, Host, Username, Password, LocalFile, RemoteDirectory, DeleteScript, SSH)
            end
        end
        If EncodedData NE '' then
            OSDelete LocalFile
        end
    end else
        Error_Services('Add', 'Host, LocalFile, or EncodedData argument was missing from the ' : Service : ' service.')
    end
    
    ServiceError    = Error_Services('GetMessage')
    LogData<1>      = LoggingDTM
    LogData<2>      = 'Stop  ' : Service
    LogData<3>      = Host
    LogData<4>      = LocalFile
    LogData<5>      = RemoteDirectory
    LogData<6>      = Error_Services('GetMessage')
    Logging_Services('AppendLog', objLog, LogData, @RM, @FM)
    Error_Services('Set', ServiceError)
    
end service


//----------------------------------------------------------------------------------------------------------------------
// GetFile
//
// Host             - FTP server name or IP. - [Required]
// RemoteFile       - Remote file that will be retrieved from the FTP server. - [Required]
// LocalFile        - Local file to direct the remote file to. - [Optional]
// Username         - Username to authenticate against the FTP server. - [Optional]
// Password         - Password to authenticate against the FTP server. - [Optional]
// LocalDirectory   - Required working directory of the local computer. Used with the "lcd" FTP command. - [Optional]
// RemoteDirectory  - Required working directory of the remote computer. Used with the "cd' FTP command. - [Optional]
// ScriptPath       - Local directory and/or file name to use for the script file. - [Optional]
// DeleteScript     - Boolean flag to indicate if the script file should be deleted after being ran. - [Optional]
// SSH              - Boolean flag to indicate if SSH (Secure FTP) is required. Default is False. - [Optional]
//
// Gets the indicated remote file from the indicated host.
//----------------------------------------------------------------------------------------------------------------------
Service GetFile(Host=HOSTS, RemoteFile, LocalFile, Username, Password, LocalDirectory, RemoteDirectory, ScriptPath, DeleteScript=BOOLEAN, SSH=BOOLEAN)
    
    LogData     = ''
    LogData<1>  = LoggingDTM
    LogData<2>  = 'Start ' : Service
    LogData<3>  = Host
    LogData<4>  = LocalFile
    LogData<5>  = RemoteDirectory
    Logging_Services('AppendLog', objLog, LogData, @RM, @FM)
    
    If (Host NE '') AND (RemoteFile NE '') then
        ScriptPath  = FTP_Services('CreateScript', ScriptPath, Host, LocalFile, Username, Password, LocalDirectory, RemoteDirectory, RemoteFile, 'get', SSH)
        If Error_Services('NoError') then
            FTP_Services('RunScript', ScriptPath, Host, Username, Password, LocalFile, RemoteDirectory, DeleteScript, SSH)
        end
    end else
        Error_Services('Add', 'Host or RemoteFile argument was missing from the ' : Service : ' service.')
    end
    
    ServiceError    = Error_Services('GetMessage')
    LogData<1>      = LoggingDTM
    LogData<2>      = 'Stop  ' : Service
    LogData<3>      = Host
    LogData<4>      = LocalFile
    LogData<5>      = RemoteDirectory
    LogData<6>      = Error_Services('GetMessage')
    Logging_Services('AppendLog', objLog, LogData, @RM, @FM)
    Error_Services('Set', ServiceError)
    
end service


//----------------------------------------------------------------------------------------------------------------------
// DeleteFile
//
// Host             - FTP server name or IP. - [Required]
// RemoteFile       - Remote file that will be retrieved from the FTP server. - [Required]
// Username         - Username to authenticate against the FTP server. - [Optional]
// Password         - Password to authenticate against the FTP server. - [Optional]
// RemoteDirectory  - Required working directory of the remote computer. Used with the "cd' FTP command. - [Optional]
// ScriptPath       - Local directory and/or file name to use for the script file. - [Optional]
// DeleteScript     - Boolean flag to indicate if the script file should be deleted after being ran. - [Optional]
// SSH              - Boolean flag to indicate if SSH (Secure FTP) is required. Default is False. - [Optional]
//
// Deletes the indicated remote file from the indicated host.
//----------------------------------------------------------------------------------------------------------------------
Service DeleteFile(Host=HOSTS, RemoteFile, Username, Password, RemoteDirectory, ScriptPath, DeleteScript=BOOLEAN, SSH=BOOLEAN)
    
    LogData     = ''
    LogData<1>  = LoggingDTM
    LogData<2>  = 'Start ' : Service
    LogData<3>  = Host
    LogData<4>  = RemoteFile
    LogData<5>  = RemoteDirectory
    Logging_Services('AppendLog', objLog, LogData, @RM, @FM)
    
    If (Host NE '') AND (RemoteFile NE '') then
        ScriptPath  = FTP_Services('CreateScript', ScriptPath, Host, '', Username, Password, '', RemoteDirectory, RemoteFile, 'delete', SSH)
        If Error_Services('NoError') then
            FTP_Services('RunScript', ScriptPath, Host, Username, Password, LocalFile, RemoteDirectory, DeleteScript, SSH)
        end
    end else
        Error_Services('Add', 'Host or RemoteFile argument was missing from the ' : Service : ' service.')
    end
    
    ServiceError    = Error_Services('GetMessage')
    LogData<1>      = LoggingDTM
    LogData<2>      = 'Stop  ' : Service
    LogData<3>      = Host
    LogData<4>      = RemoteFile
    LogData<5>      = RemoteDirectory
    LogData<6>      = Error_Services('GetMessage')
    Logging_Services('AppendLog', objLog, LogData, @RM, @FM)
    Error_Services('Set', ServiceError)
    
end service


//----------------------------------------------------------------------------------------------------------------------
// RunScript
//
// ScriptPath       - Local directory and/or file name to use for the script file. - [Required]
// Host             - FTP server name or IP. Used only for error reporting. - [Optional]
// Username         - Username to authenticate against the FTP server. - [Optional]
// Password         - Password to authenticate against the FTP server. - [Optional]
// LocalFile        - Local file to direct the remote file to. Used only for error reporting. - [Optional]
// RemoteDirectory  - Required working directory of the remote computer. Used with the "cd' FTP command. Used only for
//                    error reporting. - [Optional]
// DeleteScript     - Boolean flag to indicate if the script file should be deleted after being ran. Used only for error
//                    reporting. - [Optional]
// SSH              - Boolean flag to indicate if SSH (Secure FTP) is required. Default is False. - [Optional]
//
// Runs the indicated FTP script.
//----------------------------------------------------------------------------------------------------------------------
Service RunScript(ScriptPath, Host, Username, Password, LocalFile, RemoteDirectory, DeleteScript, SSH)
    
    If ScriptPath NE '' then
        Output  = 'VAR'     ; // Use when running in production.
        *        Output  = 'DOSOPEN' ; // Use only for debugging the console.
        If SSH EQ True$ then
            Command     = 'psftp'
            If Username NE '' then
                If Host NE '' then Command := ' ' : Username : '@' : Host
            end else
                If Host NE '' then Command := ' ' : Host
            end
            If Password NE '' then
                Command    := ' -pw ' : Password
            end
            Command    := ' -b ' : ScriptPath
        end else
            Command     = 'ftp -i -n -s:' : ScriptPath
        end
        SRP_Run_Command(Command, Output, '', '')
        If DeleteScript EQ True$ then
            OSDelete ScriptPath
        end
        
        If Output NE 'DOSOPEN' then
            Swap \0D0A\ with @FM in Output
            Begin Case
                Case IndexC(Output, 'Connection timed out', 1)                          ; Error_Services('Add', 'Error in ' : Service : ' service. Connection timed out: ' : Quote(Host) : '.')
                Case IndexC(Output, 'Connection refused', 1)                            ; Error_Services('Add', 'Error in ' : Service : ' service. Connection refused at host: ' : Quote(Host) : '.')
                Case IndexC(Output, 'Server unexpectedly closed network connection', 1) ; Error_Services('Add', 'Error in ' : Service : ' service. Connection refused at host: ' : Quote(Host) : '.')
                Case IndexC(Output, 'File not found', 1)                                ; Error_Services('Add', 'Error in ' : Service : ' service. Local file not found: ' : Quote(LocalFile) : '.')
                Case IndexC(Output, 'User cannot log in', 1)                            ; Error_Services('Add', 'Error in ' : Service : ' service. Username or password invalid.')
                Case IndexC(Output, 'The system cannot find the file specified', 1)     ; Error_Services('Add', 'Error in ' : Service : ' service. Remote file cannot be found: ' : Quote(RemoteDirectory) : '.')
                Case IndexC(Output, 'No such file or folder', 1)                        ; Error_Services('Add', 'Error in ' : Service : ' service. Remote file cannot be found: ' : Quote(RemoteDirectory) : '.')
                Case IndexC(Output, 'open for read: failure', 1)                        ; Error_Services('Add', 'Error in ' : Service : ' service. Remote file cannot be found: ' : Quote(RemoteDirectory) : '.')
                Case IndexC(Output, 'unrecognised option', 1)                           ; Error_Services('Add', 'Error in ' : Service : ' service. Unrecognised option.')
                Case IndexC(Output, 'The parameter is incorrect', 1)                    ; Error_Services('Add', 'Error in ' : Service : ' service. A parameter is incorrect.')
                Case IndexC(Output, 'failure', 1)                                       ; Error_Services('Add', 'Error in ' : Service : ' service. Unknown failure.')
                Case IndexC(Output, 'unable to open', 1)                                ; Error_Services('Add', 'Error in ' : Service : ' service. Local file not found: ' : Quote(LocalFile) : '.')
                Case IndexC(Output, 'FATAL ERROR', 1)                                   ; Error_Services('Add', 'Error in ' : Service : ' service. FATAL ERROR: ' : Quote(LocalFile) : '.')
                Case IndexC(Output, 'Access Denied', 1)                                 ; Error_Services('Add', 'Error in ' : Service : ' service. FATAL ERROR: ' : Quote(LocalFile) : '.')
            End Case
        end
    end else
        Error_Services('Add', 'ScriptPath argument was missing from the ' : Service : ' service.')
    end
    
end service


//----------------------------------------------------------------------------------------------------------------------
// CreateScript
//
// ScriptPath       - Local directory and/or file name to use for the script file. - [Optional]
// Host             - FTP server name or IP. - [Optional]
// LocalFile        - Local file to send to the FTP server. - [Optional]
// Username         - Username to authenticate against the FTP server. - [Optional]
// Password         - Password to authenticate against the FTP server. - [Optional]
// LocalDirectory   - Required working directory of the local computer. Used with the "lcd" FTP command. - [Optional]
// RemoteDirectory  - Required working directory of the remote computer. Used with the "cd' FTP command. - [Optional]
// RemoteFile       - Remote file that will be created on the FTP server. - [Optional]
// Command          - FTP file command. - [Optional]
// SSH              - Boolean flag to indicate if SSH (Secure FTP) is required. Default is False. - [Optional]
//
// Creates a script file based on the indicated arguments. This will be writen to the resulting script path.
//----------------------------------------------------------------------------------------------------------------------
Service CreateScript(ScriptPath, Host, LocalFile, Username, Password, LocalDirectory, RemoteDirectory, RemoteFile, Command=COMMANDS, SSH)
    
    // First, create the script list of commands based on the indicated arguments.
    Script  = ''
    
    If SSH EQ True$ then
        // PSFTP requires credentials to be entered in the command line rather than in the script.
    end else
        If Host NE '' then  Script := 'open ' : Host : @FM
        If Username NE '' then
            Script := 'user' : @FM
            Script := Username : @FM
        end
        If Password NE '' then
            Script := Password : @FM
        end
    end
    If LocalDirectory NE '' then
        Script := 'lcd ' : LocalDirectory : @FM
    end
    If RemoteDirectory NE '' then
        Script := 'cd ' : RemoteDirectory : @FM
    end
    Begin Case
        Case Command _EQC 'put' OR Command _EQC 'append'
            If SSH EQ True$ then
                Script := 'put'
                If Command _EQC 'append' then
                    Script := ' -append'
                end
                Script := ' ' : LocalFile
            end else
                Script := command : ' ' : LocalFile
            end
            If RemoteFile NE '' then
                Script := ' ' : RemoteFile
            end
        Case Command _EQC 'get'
            Script := command : ' ' : RemoteFile
            If LocalFile NE '' then
                Script := ' ' : LocalFile
            end
        Case Command _EQC 'delete'
            If SSH EQ True$ then
                Script := 'del ' : RemoteFile
            end else
                Script := command : ' ' : RemoteFile
            end
        Case Command _EQC 'list'
            Script := 'ls '
            If RemoteFile NE '' then
                Script := ' ' : RemoteFile
            end
            
    End Case
    Script := @FM
    Script := 'quit'
    Swap @FM with CRLF$ in Script    
    
    // Second, validate the script path. Use the specific directory and/or filename provided in the ScriptPath argument
    // if possible. Otherwise use temp names.
    ScriptDirectory = ''
    ScriptFile      = ''
    If ScriptPath NE '' then
        If SRP_Path('IsDirectory', ScriptPath) then
            // Directory is valid, which means no filename.
            ScriptDirectory = ScriptPath
        end else
            // Check to see if there is a directory with a filename.
            ScriptDirectory = SRP_Path('GetDirectory', ScriptPath)
            If SRP_Path('IsDirectory', ScriptDirectory) then
                ScriptFile  = SRP_Path('GetFilename', ScriptPath)
            end else
                ScriptDirectory = ''
                ScriptFile      = ScriptPath
            end
        end
    end
    If ScriptDirectory EQ '' then
        ScriptDirectory = Str(\00\, 1024)
        GetTempPath(Len(ScriptDirectory), ScriptDirectory)
        Convert \00\ to '' in ScriptDirectory
    end
    If ScriptFile EQ '' then
        ScriptFile  = Str(\00\, 1024)
        GetTempFileName(ScriptDirectory : \00\, \00\, 0, ScriptFile)
        OSDelete ScriptFile
        ScriptPath  = SRP_Path('RenameExtension', ScriptFile, 'scr')
    end else
        ScriptPath  = SRP_Path('Combine', ScriptDirectory, ScriptFile)
    end
    
    // Finally, create the script file in the indicated ScriptPath.
    Status()    = 0
    OSWrite Script to ScriptPath
    If Status() then
        Error_Services('Add', 'Error creating ' : ScriptPath : ' in the ' : Service : ' service. Description: ' : Field('Bad OS filename.,Access denied by operating system.,Disk or directory full.,File does not exist.,Unknown error.,Attempt to write to a read-only file.', ',', Status()))
    end
    
    Response    = ScriptPath
    
end service


//----------------------------------------------------------------------------------------------------------------------
// ConvertEncodedData
//
// EncodedData      - Base64 encoded data. - [Required]
//
// Converts Base64 encoded data to plaint text and writes to a temporary file.
//----------------------------------------------------------------------------------------------------------------------
Service ConvertEncodedData(EncodedData)
    
    LocalFile   = ''
    
    If EncodedData NE '' then
        Data    = SRP_Decode(EncodedData, 'BASE64')
        If Data NE '' then
            LocalFile   = FTP_Services('GetTempFile')
            If LocalFile NE '' then
                OSWrite Data to LocalFile
                If Status() then
                    Error_Services('Add', 'Error creating ' : LocalFile : ' in the ' : Service : ' service. Description: ' : Field('Bad OS filename.,Access denied by operating system.,Disk or directory full.,File does not exist.,Unknown error.,Attempt to write to a read-only file.', ',', Status()))
                end
            end else
                Error_Services('Add', 'Error getting a temp file in the ' : Service : ' service.')
            end
        end else
            Error_Services('Add', 'Error decoding the EncodedData argument in the ' : Service : ' service.')
        end
    end else
        Error_Services('Add', 'EncodedData argument was missing from the ' : Service : ' service.')
    end
    
    Response    = LocalFile
    
end service


//----------------------------------------------------------------------------------------------------------------------
// GetTempFile
//
// Returns the path to a newly created temporary file.
//----------------------------------------------------------------------------------------------------------------------
Service GetTempFile()
    
    TempFile    = ''
    
    TempDirectory   = Str(\00\, 1024)
    GetTempPath(Len(TempDirectory), TempDirectory)
    Convert \00\ to '' in TempDirectory
    TempFile        = Str(\00\, 1024)
    GetTempFileName(TempDirectory : \00\, \00\, 0, TempFile)
    
    Response    = TempFile
    
end service


//----------------------------------------------------------------------------------------------------------------------
// PostRequest
//
// Command          - FTP file command. - [Required]
// Host             - FTP server name or IP. - [Required]
// Server           - Name of the local server responsible for processing this FTP request. - [Required]
// LocalFile        - Local file to send to the FTP server. - [Required if EncodedData is empty]
// EncodedData      - Base64 encoded to send to the FTP server. - [Required if LocalFile is empty]
// Username         - Username to authenticate against the FTP server. - [Optional]
// Password         - Password to authenticate against the FTP server. - [Optional]
// LocalDirectory   - Required working directory of the local computer. Used with the "lcd" FTP command. - [Optional]
// RemoteDirectory  - Required working directory of the remote computer. Used with the "cd' FTP command. - [Optional]
// RemoteFile       - Remote file that will be created on the FTP server. - [Optional]
// ScriptPath       - Local directory and/or file name to use for the script file. - [Optional]
// DeleteScript     - Boolean flag to indicate if the script file should be deleted after being ran. - [Optional]
// SSH              - Boolean flag to indicate if SSH (Secure FTP) is required. Default is False. - [Optional]
// ProcessDirectory - Local direcotry to move the local file into after it has been sent to the FTP server. - [Optional]
// NumberAttempts   - Number of attempts that should be made to process this request before quitting. Default is 1.
//                    - [Optional]
// DeleteLocalFile  - Boolean flag to indicate if the local file should be deleted after the request has been processed.
//                    Default is False. - [Optional]
//
// Posts the FTP request into the FTP_REQUESTS database table for future processing.
//----------------------------------------------------------------------------------------------------------------------
Service PostRequest(Command=COMMANDS, Host=HOSTS, Server=SERVERS, LocalFile, EncodedData, Username, Password, LocalDirectory, RemoteDirectory, RemoteFile, ScriptPath, DeleteScript=BOOLEAN, SSH=BOOLEAN, ProcessDirectory, NumberAttempts, DeleteLocalFile)
    
    LogData     = ''
    LogData<1>  = LoggingDTM
    LogData<2>  = 'Start ' : Service
    LogData<3>  = Host
    LogData<4>  = Command : ' ' : LocalFile
    LogData<5>  = RemoteDirectory
    Logging_Services('AppendLog', objLog, LogData, @RM, @FM)
    
    FTPRequestID    = ''
    
    If (Command NE '') AND (Host NE '') AND (Server NE '') AND ((LocalFile NE '') OR (EncodedData NE '')) then
        RequestDate     = Date()
        RequestTime     = Time()
        FTPRequestID    = RequestDate : '-' : RequestTime : '-'
        SeqCounter      = 1 ; // Start the counter always at 1
        Loop
            HaveLock    = Database_Services('GetKeyIDLock', 'FTP_REQUESTS', FTPRequestID : SeqCounter, False$)
            If HaveLock EQ True$ then
                FTPRequest  = Database_Services('ReadDataRow', 'FTP_REQUESTS', FTPRequestID : SeqCounter)
                If Error_Services('NoError') then
                    // FTP request already exists. Unlock the Key ID and attempt the next sequential counter.
                    Database_Services('ReleaseKeyIDLock', 'FTP_REQUESTS', FTPRequestID : SeqCounter)
                    HaveLock    = False$
                end else
                    // Clear the error as this means the FTP request does not exist.
                    Error_Services('Clear')
                end
            end
        Until HaveLock OR Error_Services('HasError')
            SeqCounter += 1
        Repeat
        If Error_Services('NoError') then
            FTPRequestID                                   := SeqCounter
            FTPRequestRow                                   = ''
            FTPRequestRow<FTP_REQUESTS.REQUEST_DATE$>       = RequestDate
            FTPRequestRow<FTP_REQUESTS.REQUEST_TIME$>       = RequestTime
            FTPRequestRow<FTP_REQUESTS.FTP_COMMAND$>        = Command
            FTPRequestRow<FTP_REQUESTS.HOST$>               = Host
            FTPRequestRow<FTP_REQUESTS.LOCAL_FILE$>         = LocalFile
            FTPRequestRow<FTP_REQUESTS.ENCODED_DATA$>       = EncodedData
            FTPRequestRow<FTP_REQUESTS.USERNAME$>           = Username
            FTPRequestRow<FTP_REQUESTS.PASSWORD$>           = Password
            FTPRequestRow<FTP_REQUESTS.LOCAL_DIRECTORY$>    = LocalDirectory
            FTPRequestRow<FTP_REQUESTS.REMOTE_DIRECTORY$>   = RemoteDirectory
            FTPRequestRow<FTP_REQUESTS.REMOTE_FILE$>        = RemoteFile
            FTPRequestRow<FTP_REQUESTS.SCRIPT_PATH$>        = ScriptPath
            FTPRequestRow<FTP_REQUESTS.DELETE_SCRIPT$>      = DeleteScript
            FTPRequestRow<FTP_REQUESTS.SSH$>                = SSH
            FTPRequestRow<FTP_REQUESTS.PROCESS_DIRECTORY$>  = ProcessDirectory
            FTPRequestRow<FTP_REQUESTS.SERVER$>             = Server
            If NumberAttempts LE 0 then NumberAttempts = 1
            FTPRequestRow<FTP_REQUESTS.NUMBER_OF_ATTEMPTS$> = NumberAttempts
            If DeleteLocalFile NE True$ then DeleteLocalFile = False$
            FTPRequestRow<FTP_REQUESTS.DELETE_LOCAL_FILE$>  = DeleteLocalFile
            Database_Services('WriteDataRow', 'FTP_REQUESTS', FTPRequestID, FTPRequestRow, True$, False$, True$)
            If Error_Services('NoError') then
                Database_Services('ReleaseKeyIDLock', 'FTP_REQUESTS', FTPRequestID)        
            end
        end
    end else
        Error_Services('Add', 'Command, Host, Server, LocalFile, or EncodedData argument was missing from the ' : Service : ' service.')
    end
    
    ServiceError    = Error_Services('GetMessage')
    LogData<1>      = LoggingDTM
    LogData<2>      = 'Stop  ' : Service
    LogData<3>      = Host
    LogData<4>      = Command : ' ' : LocalFile : ' : ' : FTPRequestID
    LogData<5>      = RemoteDirectory
    LogData<6>      = Error_Services('GetMessage')
    Logging_Services('AppendLog', objLog, LogData, @RM, @FM)
    Error_Services('Set', ServiceError)
    
    Response        = FTPRequestID
    
end service


//----------------------------------------------------------------------------------------------------------------------
// ProcessRequest
//
// FTPRequestID - Key ID to the FTP_REQUESTS database table. - [Required]
//
// Processes the FTP request specified by the FTPRequestID.
//----------------------------------------------------------------------------------------------------------------------
Service ProcessRequest(FTPRequestID)
    
    If (FTPRequestID NE '') then
        FTPRequestRow   = Database_Services('ReadDataRow', 'FTP_REQUESTS', FTPRequestID)
        If Error_Services('NoError') then
            Server              = FTPRequestRow<FTP_REQUESTS.SERVER$>
            *            If Server EQ 'MESSA01EC' THEN Debug
            ThisServer          = Environment_Services('GetServer')
            If Server _EQC ThisServer then
                // This is the designated server to process this request.
                LogData     = ''
                LogData<1>  = LoggingDTM
                LogData<2>  = 'Start ' : Service
                LogData<3>  = ''
                LogData<4>  = FTPRequestID
                LogData<5>  = ''
                Logging_Services('AppendLog', objLog, LogData, @RM, @FM)
                
                RequestDate         = FTPRequestRow<FTP_REQUESTS.REQUEST_DATE$>
                RequestTime         = FTPRequestRow<FTP_REQUESTS.REQUEST_TIME$>
                Command             = FTPRequestRow<FTP_REQUESTS.FTP_COMMAND$>
                Host                = FTPRequestRow<FTP_REQUESTS.HOST$>
                LocalFile           = FTPRequestRow<FTP_REQUESTS.LOCAL_FILE$>
                EncodedData         = FTPRequestRow<FTP_REQUESTS.ENCODED_DATA$>
                Username            = FTPRequestRow<FTP_REQUESTS.USERNAME$>
                Password            = FTPRequestRow<FTP_REQUESTS.PASSWORD$>
                LocalDirectory      = FTPRequestRow<FTP_REQUESTS.LOCAL_DIRECTORY$>
                RemoteDirectory     = FTPRequestRow<FTP_REQUESTS.REMOTE_DIRECTORY$>
                RemoteFile          = FTPRequestRow<FTP_REQUESTS.REMOTE_FILE$>
                ScriptPath          = FTPRequestRow<FTP_REQUESTS.SCRIPT_PATH$>
                DeleteScript        = FTPRequestRow<FTP_REQUESTS.DELETE_SCRIPT$>
                SSH                 = FTPRequestRow<FTP_REQUESTS.SSH$>
                ProcessDirectory    = FTPRequestRow<FTP_REQUESTS.PROCESS_DIRECTORY$>
                DeleteLocalFile     = FTPRequestRow<FTP_REQUESTS.DELETE_LOCAL_FILE$>
                Begin Case
                    Case Command _EQC 'put'
                        FTP_Services('SendFile', Host, LocalFile, EncodedData, Username, Password, LocalDirectory, RemoteDirectory, RemoteFile, ScriptPath, DeleteScript, SSH)
                    Case Command _EQC 'append'
                        FTP_Services('AppendFile', Host, LocalFile, EncodedData, Username, Password, LocalDirectory, RemoteDirectory, RemoteFile, ScriptPath, DeleteScript, SSH)
                    Case Command _EQC 'get'
                        FTP_Services('GetFile', Host, RemoteFile, LocalFile, Username, Password, LocalDirectory, RemoteDirectory, ScriptPath, DeleteScript, SSH)
                    Case Command _EQC 'delete'
                        FTP_Services('DeleteFile', Host, RemoteFile, Username, Password, RemoteDirectory, ScriptPath, DeleteScript, SSH)
                End Case
                
                If Error_Services('NoError') then
                    Result  = 'Success'
                    Shipment_Services('ClearMONACritical', 'FILE_TRANSMISSION')
                    If (ProcessDirectory NE '') AND (LocalFile NE '') then
                        // Move the local file to the process directory.
                        ProcessFile = SRP_Path('Combine', ProcessDirectory, SRP_Path('GetFilename', LocalFile))
                        // Work around for the Combine service. This has been fixed, but not yet installed at Infineon.
                        If ProcessFile[1, 1] EQ '\' And ProcessFile[1, 2] NE '\' then ProcessFile = '\' : ProcessFile
                        Success     = Utility('COPYFILE', LocalFile, ProcessFile)
                        If (Success EQ True$) AND (DeleteLocalFile EQ True$) then
                            OSDelete LocalFile
                        end
                    end else
                        If DeleteLocalFile EQ True$ then
                            OSDelete LocalFile
                        end
                    end
                end else
                    Result     = Error_Services('GetMessage')
                    Shipment_Services('SetMONACritical', 'FILE_TRANSMISSION', Result)
                    Recipients = XLATE('NOTIFICATION','COC_DELIVERY',NOTIFICATION_USER_ID$,'X')
                    SendFrom		= 'System'       
                    Subject		    = 'COC FTP Delivery Failue.'
                    AttachWindow	= ''
                    AttachKey		= ''
                    SendToGroup		= ''
                    MessageParms = Recipients:@RM:SendFrom:@RM:Subject:@RM:Result:@RM:AttachWindow:@RM:AttachKey:@RM:SendToGroup
                    obj_Notes('Create',MessageParms)
                end
                
                // Update the FTP request with the latest attempt details.
                AttemptCount    = FTPRequestRow<FTP_REQUESTS.ATTEMPT_COUNT$>
                AttemptCount   += 1
                FTPRequestRow<FTP_REQUESTS.ATTEMPT_COUNT$>              = AttemptCount
                FTPRequestRow<FTP_REQUESTS.ATTEMPT_DATE$, AttemptCount> = Date()
                FTPRequestRow<FTP_REQUESTS.ATTEMPT_TIME$, AttemptCount> = Time()
                FTPRequestRow<FTP_REQUESTS.RESULT$, AttemptCount>       = Result
                Database_Services('WriteDataRow', 'FTP_REQUESTS', FTPRequestID, FTPRequestRow, True$, False$, True$)
                
                LogData<1>      = LoggingDTM
                LogData<2>      = 'Stop  ' : Service
                LogData<3>      = ''
                LogData<4>      = FTPRequestID
                LogData<5>      = ''
                LogData<6>      = Result
                Logging_Services('AppendLog', objLog, LogData, @RM, @FM)
                Error_Services('Set', ServiceError)
            end
        end
    end else
        Error_Services('Add', 'FTPRequestID argument was missing from the ' : Service : ' service.')
    end
    
end service


//----------------------------------------------------------------------------------------------------------------------
// ProcessRequests
//
// Processeses the pending FTP requests.
//----------------------------------------------------------------------------------------------------------------------
Service ProcessRequests()
    hSysLists   = Database_Services('GetTableHandle', 'SYSLISTS')
    Lock hSysLists, ServiceKeyID then
        rv              = Set_Status(0)
        RowCount        = 0
        // 11/02/18 - djs
        // Use new select statement when deploying new OBJ_SAP build, which uses FTP_SERVICES
        // to ensure that we process the FTP requests in chronological order.
        SelectStatement = 'SELECT FTP_REQUESTS WITH STATUS EQ "Pending" BY REQUEST_ID'
        *SelectStatement = 'SELECT FTP_REQUESTS WITH STATUS EQ "Pending"'
        RList(SelectStatement, 5)
        If (@List_Active EQ 3) AND (@RecCount GT 0) then
            EOF     = False$
            Loop
                ReadNext FTPRequestID else EOF = True$
            Until EOF EQ True$
                FTP_Services('ProcessRequest', FTPRequestID)
            Repeat
        end
        ClearSelect 0
        Unlock hSysLists, ServiceKeyID else Null
    end
    
end service

//----------------------------------------------------------------------------------------------------------------------
// ListDirectory
//
// Host             - FTP server name or IP. - [Required]
// Query            - Specify wildcard query. - [Optional]
// Username         - Username to authenticate against the FTP server. - [Optional]
// Password         - Password to authenticate against the FTP server. - [Optional]
// RemoteDirectory  - Required working directory of the remote computer. Used with the "cd' FTP command. - [Optional]
// ScriptPath       - Local directory and/or file name to use for the script file. - [Optional]
// DeleteScript     - Boolean flag to indicate if the script file should be deleted after being ran. - [Optional]
// SSH              - Boolean flag to indicate if SSH (Secure FTP) is required. Default is False. - [Optional]
// OutputPath       - Path to output file list to file. - [Optional]
//
// Gets the indicated remote file from the indicated host. Functions as 'mls' command.
//----------------------------------------------------------------------------------------------------------------------
Service ListDirectory(Host=HOSTS, Username, Password, RemoteDirectory, Query, ScriptPath, DeleteScript=BOOLEAN, SSH=BOOLEAN, OutputPath)
    
    LogData     = ''
    LogData<1>  = LoggingDTM
    LogData<2>  = 'Start ' : Service
    LogData<3>  = Host
    LogData<4>  = ''
    LogData<5>  = RemoteDirectory
    Logging_Services('AppendLog', objLog, LogData, @RM, @FM)
    
    If (Host NE '') AND (RemoteDirectory NE '') then
        ScriptPath  = FTP_Services('CreateScript', ScriptPath, Host, '', Username, Password, '', RemoteDirectory, Query, 'list', SSH)
        If Error_Services('NoError') then
            If SSH EQ True$ then
                Command     = 'psftp'
                If Username NE '' then
                    If Host NE '' then Command := ' ' : Username : '@' : Host
                end else
                    If Host NE '' then Command := ' ' : Host
                end
                If Password NE '' then
                    Command    := ' -pw ' : Password
                end
                Command    := ' -b ' : ScriptPath
            end else
                Command     = 'ftp -i -n -s:' : ScriptPath
            end
            DirectoryList = SRP_Run_Command(Command, 'VAR')
            
            If Count(DirectoryList, 'ftpadmin') NE 0 then
                Gosub LsToMls
            end else
               Error_Services('Add', DirectoryList)
            end
            Response = DirectoryList
            If OutputPath NE '' then
                OutputDirectoryList = DirectoryList
                Swap @FM with CRLF$ in OutputDirectoryList
                OSWrite OutputDirectoryList to OutputPath
            end
        end
    end else
        Error_Services('Add', 'Host or RemoteFile argument was missing from the ' : Service : ' service.')
    end
    
    If DeleteScript EQ True$ then
        OSDelete ScriptPath
    end
    
    ServiceError    = Error_Services('GetMessage')
    LogData<1>      = LoggingDTM
    LogData<2>      = 'Stop  ' : Service
    LogData<3>      = Host
    LogData<4>      = ''
    LogData<5>      = RemoteDirectory
    LogData<6>      = Error_Services('GetMessage')
    Logging_Services('AppendLog', objLog, LogData, @RM, @FM)
    Error_Services('Set', ServiceError)
    
end service



////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Internal GoSubs
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

//----------------------------------------------------------------------------------------------------------------------
// LsToMls 
//
// Parse output of ls command to remove directory paths and other unneccessary metadata. Functions as mls command. 
//----------------------------------------------------------------------------------------------------------------------
LsToMls:
    
    OldDirectoryList = DirectoryList
    DirectoryList = ''
    // Skip the first period because it is related to username.
    FileCount = Count(OldDirectoryList, '.') - 1
    // Index starts at 5 because that CRLF$ Field is the first containing a file name.
    FieldIndex = 5
    
    For FileIndex = 1 to FileCount
        FileName = Field(OldDirectoryList, CRLF$, FieldIndex)
        SpaceCount = DCount(FileName, ' ')
        FileName = Field(FileName, ' ', SpaceCount)
        DirectoryList<-1> = FileName
        FieldIndex += 1
    Next FileIndex
    
return





