Wednesday, January 05, 2011

Running FTP.EXE from C#

NOTE: Ultimately, I didn't go with this solution.

Since I need to log on to various FTP servers with various security configurations and FTP.EXE seams to figure out these configurations and the free FTP libraries I’ve played with can’t I have decided to wrap FTP.EXE with C#.

Telling it what to do

I wanted to avoid sending my commands to FTP.EXE via command files because I would have to dynamically write text files and make sure that they are secure. System.Diagnostics.Process allows me to send commands to its StandardInput stream, but I can’t figure out how to make it accept passwords. To solve these problems, I log in using command files (which I can secure and only allow myself and the program to see, and then write the rest of the commands to the StandardInput stream.

The Command File

The command file needs to reside in a directory with no spaces and the file needs to be saved using ANSI encoding. Here I open a server and login, the rest is up to the commands that I send to the stream.

open localhost
username
password

Sample Application

This simple console application demonstrates using the command file to log in and StandardInput to send a List command (and Quit).

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;

namespace DemoFtp
{
    class Program
    {
        private const string FTP_EXE_LOCATION = @"C:\Windows\System32\ftp.exe";
        private const string DEFAULT_COMMAND_FILE_LOCATION = @"C:\temp\localhost.txt";

        static void Main(string[] args)
        {
            try
            {
                string commandFileLocation = getCommandFileLocation(args);

                var commands = new List<string>();
                commands.Add("ls -la");
                commands.Add("quit");   // Don't forget to log out!
                string output = RunWithResponses(
                    FTP_EXE_LOCATION, 
                    string.Format("-s:{0}", commandFileLocation), 
                    commands);
                Console.WriteLine(output);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
            Console.ReadKey();
        }

        private static string getCommandFileLocation(string[] args)
        {
            string commandFileLocation;
            if (args.Length > 0)
                commandFileLocation = args[0];
            else
                commandFileLocation = DEFAULT_COMMAND_FILE_LOCATION;
            return commandFileLocation;
        }

        public static string RunWithResponses(string exeName, string argsLine,
            List<string> commands)
        {
            StreamReader outputStream = StreamReader.Null;
            StreamReader errorStream = StreamReader.Null;
            StreamWriter inputStream = StreamWriter.Null;
            string output = string.Empty;
            try
            {
                // Setup the process
                Process newProcess = new Process();
                newProcess.StartInfo.FileName = exeName;
                newProcess.StartInfo.Arguments = argsLine;
                newProcess.StartInfo.UseShellExecute = false;
                newProcess.StartInfo.CreateNoWindow = true;
                newProcess.StartInfo.RedirectStandardInput = true;
                newProcess.StartInfo.RedirectStandardOutput = true;
                newProcess.StartInfo.RedirectStandardError = true;
                newProcess.Start();
                inputStream = newProcess.StandardInput;
                outputStream = newProcess.StandardOutput;
                errorStream = newProcess.StandardError;
                inputStream.AutoFlush = true;

                // Issue My commands
                foreach (string command in commands)
                {
                    inputStream.WriteLine(command);
                }

                // Get the rsults and wait for it to end
                output = outputStream.ReadToEnd();
                newProcess.WaitForExit();
            }
            finally
            {
                // Clean up
                if (outputStream != StreamReader.Null)
                    outputStream.Close();
                if (errorStream != StreamReader.Null)
                    errorStream.Close();
                if (inputStream != StreamWriter.Null)
                    inputStream.Close();
            }
            return output;
        }
    }
}

3 comments:

Anonymous said...

Thank you!!! Very useful stuff.

Jack Stephens said...

Thanks, this is probably the most popular post on my modest little blog.

Anonymous said...

Great post Jack!
Thank you so much,