SFTP Connenction manager d365fo x++

SFTP Connenction manager d365fo x++

using System.IO;
using System.Collections;
using Renci.SshNet;
using Renci.SshNet.Sftp;//RESCP.ssh

class DSAPSFTPConnectionManager
{
    /// <summary>
    /// Test SFTP connection
    /// </summary>
    public static boolean testConnection(DSAPSFTPProfiles _profile)
    {
        boolean isConnected = false;
        System.Exception ex;
        DSAPSFTPProfiles profileUpdate;

        try
        {
            SftpClient sftp = new SftpClient(
                _profile.HostName,
                _profile.Port,
                _profile.UserName,
                _profile.Password
            );
            sftp.Connect();

            if (sftp.IsConnected)
            {
                info(strFmt("SFTP Authentication successful to %1:%2", _profile.HostName, _profile.Port));
                isConnected = true;

                // Update connection status using a separate select forupdate
                ttsbegin;
                select forupdate profileUpdate
                    where profileUpdate.RecId == _profile.RecId;
                if (profileUpdate)
                {
                    profileUpdate.ConnectionStatus = DSAPSFTPConnectionStatus::Valid;
                    profileUpdate.LastValidatedBy = curUserId();
                    profileUpdate.LastValidatedDateTime = DateTimeUtil::utcNow();
                    profileUpdate.update();
                }
                ttscommit;
            }

            sftp.Disconnect();
            sftp.Dispose();
        }
        catch (Exception::CLRError)
        {
            ex = CLRInterop::getLastException();
            while (ex)
            {
                warning(strFmt("Exception: %1", ex.get_Message()));
                ex = ex.get_InnerException();
            }

            // Update connection status on failure
            try
            {
                ttsbegin;
                select forupdate profileUpdate
                    where profileUpdate.RecId == _profile.RecId;
                if (profileUpdate)
                {
                    profileUpdate.ConnectionStatus = DSAPSFTPConnectionStatus::Invalid;
                    profileUpdate.LastValidatedBy = curUserId();
                    profileUpdate.LastValidatedDateTime = DateTimeUtil::utcNow();
                    profileUpdate.update();
                }
                ttscommit;
            }
            catch
            {
                // If update fails, just log it
                warning("Failed to update profile connection status");
            }
        }

        return isConnected;
    }

    /// <summary>
    /// Upload file to SFTP server - CORRECTED VERSION
    /// </summary>
    public static void uploadFile(DSAPSFTPProfiles _profile, str localFilePath)
    {
        System.Exception ex;

        try
        {
            // Initialize SFTP client
            Renci.SshNet.SftpClient sftp = new Renci.SshNet.SftpClient(
                _profile.HostName,
                _profile.Port,
                _profile.UserName,
                _profile.Password
            );
            
            sftp.Connect();

            if (!sftp.IsConnected)
            {
                throw new System.Exception(strFmt("Failed to connect to %1:%2", _profile.HostName, _profile.Port));
            }

            info(strFmt("Connected to SFTP server: %1:%2", _profile.HostName, _profile.Port));
            
            // Get the working directory first
            str workingDirectory = sftp.WorkingDirectory;
            info(strFmt("Current working directory: %1", workingDirectory));

            // Determine remote folder - use lowercase 'out' as shown in FileZilla
            str remoteFolder;
            
            if (_profile.FolderOUT && strLen(_profile.FolderOUT) > 0)
            {
                remoteFolder = _profile.FolderOUT;
                // Remove any leading slashes or backslashes
                while (strLen(remoteFolder) > 0 && (strStartsWith(remoteFolder, "/") || strStartsWith(remoteFolder, "\\")))
                {
                    remoteFolder = subStr(remoteFolder, 2, strLen(remoteFolder) - 1);
                }
            }
            else
            {
                // Default to 'out' folder (lowercase as seen in FileZilla)
                remoteFolder = "out";
            }

            // Build the full remote folder path
            // Try to use relative path from working directory
            str remoteFolderPath = remoteFolder;
            
            info(strFmt("Checking if folder exists: %1", remoteFolderPath));
            
            // Check if folder exists, if not try to create it
            try
            {
                if (!sftp.Exists(remoteFolderPath))
                {
                    info(strFmt("Folder does not exist, creating: %1", remoteFolderPath));
                    sftp.CreateDirectory(remoteFolderPath);
                }
                else
                {
                    info(strFmt("Folder exists: %1", remoteFolderPath));
                }
            }
            catch
            {
                // If checking/creating folder fails, try to continue anyway
                warning(strFmt("Could not verify/create folder: %1. Attempting upload anyway.", remoteFolderPath));
            }

            // Get just the filename from the local path
            str fileName = System.IO.Path::GetFileName(localFilePath);
            
            // Build remote file path - use forward slash as path separator for SFTP
            str remoteFilePath = strFmt("%1/%2", remoteFolderPath, fileName);
            
            info(strFmt("Uploading file from: %1", localFilePath));
            info(strFmt("Uploading file to: %1", remoteFilePath));

            // Upload file
            System.IO.FileStream fs = null;
            try
            {
                fs = new System.IO.FileStream(localFilePath, System.IO.FileMode::Open, System.IO.FileAccess::Read);
                
                // Try upload with the constructed path
                sftp.UploadFile(fs, remoteFilePath, true, null);
                
                info(strFmt("File uploaded successfully: %1 → %2", fileName, remoteFilePath));
            }
            catch (Exception::CLRError)
            {
                // If first attempt fails, try with just filename in working directory
                warning(strFmt("Failed to upload to %1, trying working directory", remoteFilePath));
                
                if (fs)
                {
                    fs.Close();
                    fs = new System.IO.FileStream(localFilePath, System.IO.FileMode::Open, System.IO.FileAccess::Read);
                }
                
                // Try uploading to just the filename (current directory)
                sftp.UploadFile(fs, fileName, true, null);
                info(strFmt("File uploaded to working directory: %1", fileName));
            }
            finally
            {
                if (fs)
                {
                    fs.Close();
                    fs.Dispose();
                }
            }

            sftp.Disconnect();
            sftp.Dispose();
        }
        catch (Exception::CLRError)
        {
            ex = CLRInterop::getLastException();
            str errorMessage = "";
            while (ex)
            {
                errorMessage = ex.get_Message();
                error(strFmt("Upload failed: %1", errorMessage));
                ex = ex.get_InnerException();
            }
            // Provide more helpful error message
            if (strScan(errorMessage, "permission", 1, strLen(errorMessage)) > 0)
            {
                error("Permission denied. Check that the SFTP user has write access to the target folder.");
            }
            else if (strScan(errorMessage, "not found", 1, strLen(errorMessage)) > 0 ||
                     strScan(errorMessage, "does not exist", 1, strLen(errorMessage)) > 0)
            {
                error("Target folder not found. Verify the FolderOUT path in the SFTP Profile configuration.");
            }
            throw;
        }
    }

    /// <summary>
    /// Read remote file list from SFTP server using Renci.SshNet
    /// </summary>
    public static List getRemoteFileList(DSAPSFTPProfiles _profile, boolean useInFolder = false)
    {
        List fileList = new List(Types::String);
        System.Exception ex;

        try
        {
            str host = _profile.HostName;
            int port = _profile.Port;
            str username = _profile.UserName;
            str password = _profile.Password;

            str remoteFolder = useInFolder
                ? (strLen(_profile.FolderIN) ? _profile.FolderIN : "/IN")
                : (strLen(_profile.FolderOUT) ? _profile.FolderOUT : "/OUT");

            Renci.SshNet.SftpClient sftpClient = new Renci.SshNet.SftpClient(host, port, username, password);
            sftpClient.Connect();

            System.Collections.IEnumerable files = sftpClient.ListDirectory(remoteFolder, null);
            System.Collections.IEnumerator enumerator = files.GetEnumerator();

            while (enumerator.MoveNext())
            {
                Renci.SshNet.Sftp.SftpFile file = enumerator.get_Current();
                if (!file.get_IsDirectory() && file.get_Name() != "." && file.get_Name() != "..")
                {
                    // Add full path for deletion or download
                    fileList.addEnd(file.get_FullName());
                }
            }

            sftpClient.Disconnect();
        }
        catch (Exception::CLRError)
        {
            ex = CLRInterop::getLastException();
            warning(strFmt("Error listing remote files: %1", ex.get_Message()));
        }

        return fileList;
    }

    /// <summary>
    /// Delete file from SFTP server
    /// </summary>
    public static void DeleteRemoteFiles(DSAPSFTPProfiles _profile, List fileList)
    {
        System.Exception ex;

        if (fileList.elements() == 0)
        {
            info("No files provided for deletion.");
            return;
        }

        try
        {
            str host = _profile.HostName;
            int port = _profile.Port;
            str username = _profile.UserName;
            str password = _profile.Password;

            Renci.SshNet.SftpClient sftpClient = new Renci.SshNet.SftpClient(host, port, username, password);
            sftpClient.Connect();

            if (!sftpClient.IsConnected)
            {
                warning("SFTP connection failed. Cannot delete files.");
                return;
            }

            ListEnumerator le = fileList.getEnumerator();
            while (le.moveNext())
            {
                str remoteFileFullPath = le.current(); // full path now
                try
                {
                    if (sftpClient.Exists(remoteFileFullPath))
                    {
                        sftpClient.DeleteFile(remoteFileFullPath);
                        info(strFmt("Deleted file: %1", remoteFileFullPath));
                    }
                    else
                    {
                        warning(strFmt("File not found on server: %1", remoteFileFullPath));
                    }
                }
                catch (Exception::CLRError)
                {
                    ex = CLRInterop::getLastException();
                    warning(strFmt("Failed to delete file %1: %2", remoteFileFullPath, ex.get_Message()));
                }
            }

            sftpClient.Disconnect();
            info("Delete operation completed.");
        }
        catch (Exception::CLRError)
        {
            ex = CLRInterop::getLastException();
            warning(strFmt("SFTP delete operation failed: %1", ex.get_Message()));
        }
    }

    public static void MoveFileOutToIn(DSAPSFTPProfiles _profile)
    {
        System.Exception ex;

        try
        {
            str host = _profile.HostName;
            int port = _profile.Port;
            str username = _profile.UserName;
            str password = _profile.Password;

            str sourceFolder = strLen(_profile.FolderOUT) ? _profile.FolderOUT : "/OUT";
            str destinationFolder = strLen(_profile.FolderIN) ? _profile.FolderIN : "/IN";

            Renci.SshNet.SftpClient sftpClient = new Renci.SshNet.SftpClient(host, port, username, password);
            sftpClient.Connect();

            if (!sftpClient.IsConnected)
            {
                warning("SFTP connection failed.");
                return;
            }

            System.Collections.IEnumerable files = sftpClient.ListDirectory(sourceFolder, null);
            System.Collections.IEnumerator enumerator = files.GetEnumerator();

            int movedCount = 0;
            int errorCount = 0;

            while (enumerator.MoveNext())
            {
                Renci.SshNet.Sftp.SftpFile file = enumerator.get_Current();

                // Skip directories and system files
                if (!file.get_IsDirectory() && file.get_Name() != "." && file.get_Name() != "..")
                {
                    str sourceFilePath = sourceFolder + "/" + file.get_Name();
                    str destinationFilePath = destinationFolder + "/" + file.get_Name();

                    try
                    {
                        sftpClient.RenameFile(sourceFilePath, destinationFilePath);
                        movedCount++;
                        info(strFmt("File moved successfully: %1 → %2", sourceFilePath, destinationFilePath));
                    }
                    catch (Exception::CLRError)
                    {
                        ex = CLRInterop::getLastException();
                        warning(strFmt("Failed to move file %1: %2", sourceFilePath, ex.get_Message()));
                        errorCount++;
                    }
                }
            }

            sftpClient.Disconnect();

            if (movedCount > 0 && errorCount == 0)
                info(strFmt("Successfully moved %1 files from OUT to IN folder.", movedCount));
            else if (movedCount > 0 && errorCount > 0)
                warning(strFmt("Moved %1 files, %2 errors occurred.", movedCount, errorCount));
            else
                info("No files found to move in OUT folder.");
        }
        catch (Exception::CLRError)
        {
            ex = CLRInterop::getLastException();
            warning(strFmt("Error moving files to IN folder: %1", ex.get_Message()));
        }
    }

    public static void MoveFileInToOut(DSAPSFTPProfiles _profile)
    {
        System.Exception ex;

        try
        {
            str host = _profile.HostName;
            int port = _profile.Port;
            str username = _profile.UserName;
            str password = _profile.Password;

            str sourceFolder = strLen(_profile.FolderIN) ? _profile.FolderIN : "/IN";
            str destinationFolder = strLen(_profile.FolderOUT) ? _profile.FolderOUT : "/OUT";

            Renci.SshNet.SftpClient sftpClient = new Renci.SshNet.SftpClient(host, port, username, password);
            sftpClient.Connect();

            if (!sftpClient.IsConnected)
            {
                warning("SFTP connection failed.");
                return;
            }

            System.Collections.IEnumerable files = sftpClient.ListDirectory(sourceFolder, null);
            System.Collections.IEnumerator enumerator = files.GetEnumerator();

            int movedCount = 0;
            int errorCount = 0;

            while (enumerator.MoveNext())
            {
                Renci.SshNet.Sftp.SftpFile file = enumerator.get_Current();

                // Skip directories and system files
                if (!file.get_IsDirectory() && file.get_Name() != "." && file.get_Name() != "..")
                {
                    str sourceFilePath = sourceFolder + "/" + file.get_Name();
                    str destinationFilePath = destinationFolder + "/" + file.get_Name();

                    try
                    {
                        sftpClient.RenameFile(sourceFilePath, destinationFilePath);
                        movedCount++;
                        info(strFmt("File moved successfully: %1 → %2", sourceFilePath, destinationFilePath));
                    }
                    catch (Exception::CLRError)
                    {
                        ex = CLRInterop::getLastException();
                        warning(strFmt("Failed to move file %1: %2", sourceFilePath, ex.get_Message()));
                        errorCount++;
                    }
                }
            }

            sftpClient.Disconnect();

            if (movedCount > 0 && errorCount == 0)
                info(strFmt("Successfully moved %1 files from IN to OUT folder.", movedCount));
            else if (movedCount > 0 && errorCount > 0)
                warning(strFmt("Moved %1 files, %2 errors occurred.", movedCount, errorCount));
            else
                info("No files found to move in IN folder.");
        }
        catch (Exception::CLRError)
        {
            ex = CLRInterop::getLastException();
            warning(strFmt("Error moving files from IN to OUT folder: %1", ex.get_Message()));
        }
    }

    /// <summary>
    /// Download file from SFTP server using Renci.SshNet
    /// </summary>
    public static void downloadFile(DSAPSFTPProfiles _profile, str remoteFileName, str localFolder)
    {
        System.Exception ex;
 
        if (!System.IO.Directory::Exists(localFolder))
        {
            warning(strFmt("Local folder does not exist: %1", localFolder));
            return;
        }
 
        try
        {
            str host = _profile.HostName;
            int port = _profile.Port;
            str username = _profile.UserName;
            str password = _profile.Password;
 
            // Fix remote folder path
            str remoteFolder = strLen(_profile.FolderIN) ? _profile.FolderIN : "/";
            if (!strEndsWith(remoteFolder, "/"))
          remoteFolder += "/";
 
            // Use full path only if needed
            str remoteFilePath = strStartsWith(remoteFileName, "/") ? remoteFileName : remoteFolder + remoteFileName;
            str fileName = System.IO.Path::GetFileName(remoteFileName);
            str localFilePath = System.IO.Path::Combine(localFolder, fileName);
 
            info(strFmt("Attempting download: %1 → %2", remoteFilePath, localFilePath));
 
            Renci.SshNet.SftpClient sftpClient = new Renci.SshNet.SftpClient(host, port, username, password);
            sftpClient.Connect();
 
            if (!sftpClient.IsConnected)
            {
                warning("SFTP connection failed.");
                return;
            }
 
            // Check if file exists on server
            if (!sftpClient.Exists(remoteFilePath))
            {
                warning(strFmt("Remote file not found: %1", remoteFilePath));
                sftpClient.Disconnect();
                return;
            }
 
            // Download file
            System.IO.FileStream fileStream = new System.IO.FileStream(localFilePath, System.IO.FileMode::Create);
            sftpClient.DownloadFile(remoteFilePath, fileStream, null);
            fileStream.Close();
            sftpClient.Disconnect();
 
            info(strFmt("File downloaded successfully: %1 → %2", remoteFilePath, localFilePath));
        }
        catch (Exception::CLRError)
        {
            System.Exception clrEx = CLRInterop::getLastException();
            System.Exception innerEx = clrEx.get_InnerException();
            str message = clrEx.get_Message();
 
            if (innerEx)
          message += " | Inner Exception: " + innerEx.get_Message();
 
            warning(strFmt("Download failed: %1", message));
        }
    }

}

Comments