September 2013

Volume 28 Number 9

Windows Phone - Adding FTP Support in Windows Phone 8

By Uday Gupta

FTP is one of the most widely used protocols for sharing files. It’s used to hand off files to clients, to distribute installation files to consumers, to provide access to otherwise inaccessible parts of the file system for security reasons in enterprise environments, and for a host of other scenarios. With all the emphasis on the cloud, it might seem counterintuitive today to continue to rely on FTP. But if you want to have direct control of your files while providing easy access to them, FTP can still be the way to go.

The addition of socket support in Windows Phone enabled the devices to communicate with a multitude of different services, making users feel more connected and available than ever. However, in Windows Phone (as well as Windows 8), it may be up to the developer to provide his own implementation of a service. One feature that’s still missing in Windows Phone is support for FTP—there are no direct APIs available for leveraging FTP services in a Windows Store app. From an enterprise perspective, this makes it rather difficult for employees to access files over their phones.

This article is about providing support for FTP on Windows Phone 8 by creating an FTP library and a simple FTP client application that will run on a Windows Phone device. FTP is a well-established, well-documented protocol (see RFC 959 at bit.ly/fB5ezA). RFC 959 describes the specification, its functionality and architecture, and its commands with their responses. I’m not going to describe every feature and command of FTP, but I hope to provide a head start to help you create your own FTP implementation. Note that the feature I’m going to discuss is not a Microsoft implementation and may not necessarily be approved.

FTP Channels

FTP consists of two channels, a command channel and a data channel. The command channel is created when a client connects to an FTP server, and it’s used to transmit all commands and responses to and from the FTP server. The command channel remains open until the client disconnects, a connection-idle timeout takes place, or an error occurs in the FTP server or command channel.

The data channel is used to transmit data to and from the FTP server. This channel is temporary in nature—once the data transfer completes, the channel is disconnected. For every data transmission, a new data channel is established.

An FTP connection can be either active or passive. In active mode, the FTP client sends the data channel information (the data port number) to which the file transfer will be sent. In passive mode, the client asks the server to create a data channel on its end and provide the socket address and port information so the client can connect to that data channel and begin the file-transfer operation.

Passive mode is useful when the FTP client doesn’t want to manage data ports or data channels on its end. While developing the FTP client for Windows Phone, I’ll switch over to passive mode. That is, before beginning a file-transfer operation, I’ll ask the FTP server to create and open a data channel and send me its socket endpoint information so that when I connect, data transfer begins. I’ll discuss how to use passive mode later in the article.

Getting Started

This article won’t cover all of the commands mentioned in RFC 959. I’ll focus instead on the basic commands that are required to build a minimal, working FTP client, including those involved in the connect-disconnect procedure, authentication, browsing the file system, and uploading and downloading files.

To get started, I’ll create a Visual Studio solution that will contain two projects, a Windows Phone Class Library project for the FTP library and a Windows Phone App project to contain code for the UX and for using the FTP library.

To create the solution, open Visual Studio, create a Windows Phone Class Library project and name it WinPhoneFtp.FtpService.

Now add a Windows Phone App project and name it WinPhone­-Ftp.UserExperience. This will con­tain the UI for the test app.

WinPhoneFtp.FtpService is the FTP client library that will be referenced in the WinPhoneFtp.UserExperience UX project in order to use FTP services. The FTP library uses an event-based, async/await pattern. Each operation will be called asynchronously in a background task and, upon completion, an event will be raised to provide notification in the UI. The FTP client library contains various asynchronous methods for each FTP operation (the supported commands) and each asynchronous method is mapped to a certain event that will be raised when the asynchronous method completes. Figure 1 depicts the mapping of asynchronous methods and events.

Figure 1 Asynchronous Methods and Associated Events

Method (with Parameters) Associated Events
Constructor(System.String IPAddress, System.Windows.Threading.Dispatcher UIDispatcher) No associated event
ConnectAsync FtpConnected
DisconnectAsync FtpDisconnected

AuthenticateAsync

AuthenticateAsync(System.String Username, System.String Password)

Success - FtpAuthenticationSucceeded

Failure - FtpAuthenticationFailed

GetPresentWorkingDirectoryAsync FtpDirectoryListed
ChangeWorkingDirectoryAsync

Success - FtpDirectoryChangedSucceeded

Failure - FtpDirectoryChangedFailed

GetDirectoryListingAsync FtpDirectoryListed
UploadFileAsync(System.IO.Stream LocalFileStream, System.String RemoteFilename)

Success - FtpFileUploadSucceeded

Failure - FtpFileUploadFailed

Progress - FtpFileTransferProgressed

DownloadFileAsync(System.IO.Stream LocalFileStream, System.String RemoteFilename)

Success - FtpFileDownloadSucceeded

Failure - FtpFileDownloadFailed

Progress - FtpFileTransferProgressed

The application’s UI consists of two textboxes and two buttons. One textbox and button will be used for accepting the FTP server IP address and connecting to it. The other textbox and button will accept FTP commands from a user and send them to the FTP server.

The TCP Socket Wrapper

Before starting on the FTP client, I created a wrapper named TcpClientSocket for the Windows.Networking.Sockets.StreamSocket class (see bit.ly/15fmqhK). This wrapper will provide certain event notifications based on various socket operations. The notifications I’m interested in are SocketConnected, DataReceived, ErrorOccured and SocketClosed. When the StreamSocket object connects to the remote endpoint, the SocketConnected event will be raised. Similarly, on receiving data over the socket, the Data­Received event will be raised, along with the buffer containing data as event arguments. If an error occurs during socket operation, it’s the ErrorOccured event that does the notification. And, finally, when the socket is closed (by either a remote or local host), the SocketClosed event is raised, with the reason for the closing in its event argument. I won’t go into detail about how the socket wrapper is implemented, but you can download the source code for this article and see its implementation (msdn.com/magazine/msdnmag0913).

FTP Connect and Disconnect

When the FTP client first connects to the FTP server, it establishes a command channel with the server over which commands and their responses are shared. Typically, FTP uses port 21, but for security reasons or other factors, a different port number may be configured. Before a user can start sharing files, he needs to authenticate to the server, typically with a username and password. If the authentication is successful, the server will respond (in my case): 220 Microsoft FTP Service.

Before connecting to the FTP server, I’ll create an object named FtpClient based on my FTP client library class, along with event handlers for all the events described earlier:

FtpClient ftpClient = null;
async private void btnLogin_Tap(object sender,
  System.Windows.Input.GestureEventArgs e)
{
  ftpClient = new FtpClient(txtIp.Text, this.Dispatcher);
  // Add event-handlers to various events of ftpClient object
  await ftpClient.ConnectAsync();
}

Once the object is created, and various FTP client event handlers are added, I’ll call the ConnectAsync method of the FtpClient object. Here’s how ConnectAsync works:

public async Task ConnectAsync()
{
  if (!IsConnected)
  {
    logger.AddLog("FTP Command Channel Initailized");
    await FtpCommandSocket.PrepareSocket();
  }
}

ConnectAsync will connect to the FTP server as a socket-connect operation. When the FTP client successfully connects to the FTP server, the FtpConnected event is raised to notify the client that it’s connected to the FTP server:

async void ftpClient_FtpConnected(object sender, EventArgs e)
{
  // Handle the FtpConnected event to show some prompt to
  // user or call AuthenticateAsync to authenticate user
  // automatically once connected
}

Figure 2 shows what the app will look like when a user has successfully connected to the FTP server.

Connecting and Authenticating to the FTP Server
Figure 2 Connecting and Authenticating to the FTP Server

Note that when you connect to other FTP servers, the text you see may be altogether different.

There are three ways to become disconnected from an FTP server:

  • The user sends a QUIT command to the server to deliberately close the connection.
  • After being idle for a specified time, the FTP server closes the connection.
  • A socket error or some internal error may occur on one or both ends.

To deliberately disconnect via the QUIT command, I issue QUIT in the command’s textbox and the Send Command button’s tap event handler calls the DisconnectAsync method:

async private void btnFtp_Tap(object sender, 
    System.Windows.Input.GestureEventArgs e)
{
  ...
  if (txtCmd.Text.Equals("QUIT"))
  {
    logger.Logs.Clear();
    await ftpClient.DisconnectAsync();
    return;
  }
  ...
}

This is how DisconnectAsync sends the command to the FTP server:

public async Task DisconnectAsync()
{
  ftpCommand = FtpCommand.Logout;
  await FtpCommandSocket.SendData("QUIT\r\n");
}

Once the connection is closed, the FtpDisconnected event is raised, so that the client can handle the Disconnect event and gracefully release the resources, with the result shown in Figure 3:

void ftpClient_FtpDisconnected(object sender, 
    FtpDisconnectedEventArgs e)
{
  // Handle FtpDisconnected event to show some prompt
  // or message to user or release
}

Disconnecting from the FTP Server
Figure 3 Disconnecting from the FTP Server

In any of the scenarios mentioned, the FTP client is disconnected from the FTP server and any ongoing FTP file-transfer operations will also abort.

FTP Authentication

Before starting any FTP file operation, the user needs to authenticate to the FTP server. There are two types of users: anonymous and non-anonymous (those with credentials). When the FTP server can be accessed by anybody, users are authenticated as anonymous. For them, the username is “anonymous” and the password can be any text, formatted as an e-mail address. For non-anonymous FTP, users have to provide valid credentials. Before implementing any authentication scheme, you need to know which type of authentication is enabled on the server.

Authentication takes place via two FTP commands, USER and PASS. The USER command is used to send the user’s identity, that is, the username. The PASS command is used to provide the password. Although it’s good practice to provide the user’s e-mail address, any text formatted as an e-mail address will work:

[FTP Client]: USER anonymous
[FTP Server:] 331 Anonymous access allowed, send identity (e-mail name) as password
[FTP Client]: PASS m@m.com
[FTP Server]: 230 User logged in

The AuthenticateAsync method has two overloads. The first overload will authenticate the user with default credentials, which are generally used for anonymous authentication. The second overload takes a username and password for authentication to the FTP server.

To authenticate the user automatically as an anonymous user, I call AuthenticateAsync when the FtpConnected event is received:

async void ftpClient_FtpConnected(object sender, 
    EventArgs e)
{
  await (sender as FtpClient).AuthenticateAsync();
}

Internally, AuthenticateAsync calls the other overload with default credentials:

public async Task AuthenticateAsync()
{
  await AuthenticateAsync("anonymous", m@m.com);
}

The AuthenticateAsync overload issues the USER command to the FTP server along with the username received from the parameter:

public async Task AuthenticateAsync(String Username, 
    String Password)
{
  ftpCommand = FtpCommand.Username;
  this.Username = Username;
  this.Password = Password;
  logger.AddLog(String.Format("FTPClient -> USER {0}\r\n", 
    Username));
  await FtpCommandSocket.SendData(String.Format("USER {0}\r\n", 
    Username));
}

When the response to the USER command is received, the PASS command is issued along with the password received from the parameter (see Figure 4).

Figure 4 Sending the Password with PASS, After the USER Command

async void FtpClientSocket_DataReceived(object sender, 
    DataReceivedEventArgs e)
{
  String Response = System.Text.Encoding.UTF8.GetString(
    e.GetData(), 0, e.GetData().Length);
  logger.AddLog(String.Format("FTPServer -> {0}", Response));
    switch (ftpPassiveOperation)
    {
      ...
      case FtpPassiveOperation.None:
        switch (ftpCommand)
        {
          case FtpCommand.Username:
          if (Response.StartsWith("501"))
          {
            IsBusy = false;
            RaiseFtpAuthenticationFailedEvent();
            break;
          }
          this.ftpCommand = FtpCommand.Password;
          logger.AddLog(String.Format(
            "FTPClient -> PASS {0}\r\n", this.Password));
          await FtpCommandSocket.SendData(
            String.Format("PASS {0}\r\n", this.Password));
          break;
          ...
        }
    }
    ...
  }

If the authentication is successful, the FtpAuthenticationSucceeded event is raised; if not, the FtpAuthenticationFailed event is raised:

void ftpClient_FtpAuthenticationFailed(object sender, 
    EventArgs e)
{
  logger.AddLog("Authentication failed");
}
void ftpClient_FtpAuthenticationSucceeded(object sender, 
    EventArgs e)
{
  logger.AddLog("Authentication succeeded");
}

Directory Browsing

FTP provides support for directory browsing, but this depends on how the FTP client is written to display directory information. If the FTP client is a GUI, the information may be shown as a directory tree. A console client, in contrast, may just display the directory listing on the screen.

Because there’s no mechanism provided to let you know which directory you’re in, FTP provides a command to show the present working directory (the current directory). Sending the PWD command to the FTP server over the command channel gives you the whole path, from the root to the current directory. When a user is authenticated, he lands in either the root directory or the directory configured for him in the FTP server configuration.

You can issue the PWD command by calling GetPresentWorkingDirectoryAsync. To get the current directory, I issue PWD in the command textbox and call GetPresentWorkingDirectoryAsync in the Send Command button’s tap event:

async private void btnFtp_Tap(object sender,
  System.Windows.Input.GestureEventArgs e)
{
  ...
  if (txtCmd.Text.Equals("PWD"))
  {
    logger.Logs.Clear();
    await ftpClient.GetPresentWorkingDirectoryAsync();
    return;
  }
  ...
}

Internally, GetPresentWorkingDirectoryAsync sends PWD to the FTP server over the socket:

public async Task GetPresentWorkingDirectoryAsync()
{
  if (!IsBusy)
  {
    ftpCommand = FtpCommand.PresentWorkingDirectory;
    logger.AddLog("FTPClient -> PWD\r\n");
    await FtpCommandSocket.SendData("PWD\r\n");
  }
}

When this procedure is successful, the FTP server sends the response with the path of the present working directory, the FTP client is notified and the FtpPresentWorkingDirectoryReceived event is raised. Using the event arguments, the client can get the information about the path of the present working directory (as shown in Figure 5):

void ftpClient_FtpPresentWorkingDirectoryReceived(object sender,
  FtpPresentWorkingDirectoryEventArgs e)
{
  // Handle PresentWorkingDirectoryReceived event to show some
  // prompt or message to user
}

Getting the Present Working Directory from the FTP Server
Figure 5 Getting the Present Working Directory from the FTP Server

To change to some other directory, the FTP client can use the Change Working Directory (CWD) command. Note that CWD only works to change to a subdirectory in the current working directory or to its parent directory. When the user is in the root directory, he can’t navigate backward and CWD will throw an error in response.

To change the working directory, I issue the CWD command in the command textbox, and in the Send Command button’s tap event I call the ChangeWorkingDirectoryAsync method:

async private void btnFtp_Tap(object sender,
  System.Windows.Input.GestureEventArgs e)
{
  ...
  if (txtCmd.Text.StartsWith("CWD"))
  {
    logger.Logs.Clear();
    await ftpClient.ChangeWorkingDirectoryAsync(
      txtCmd.Text.Split(new char[] { ' ' },
      StringSplitOptions.RemoveEmptyEntries)[1]);
    return;
  }
  ...
}

Internally, ChangeWorkingDirectoryAsync issues the CWD command to the FTP server:

public async Task ChangeWorkingDirectoryAsync(String RemoteDirectory)
{
  if (!IsBusy)
  {
    this.RemoteDirectory = RemoteDirectory;
    ftpCommand = FtpCommand.ChangeWorkingDirectory;
    logger.AddLog(String.Format("FTPClient -> CWD {0}\r\n", 
        RemoteDirectory));
    await FtpCommandSocket.SendData(String.Format("CWD {0}\r\n", 
        RemoteDirectory));
  }
}

If the user wants to navigate backward, he can send double dots “..” as a parameter to this procedure. When the change is successful, the client is notified and the FtpDirectoryChangedSucceeded event is raised, which can be seen in Figure 6. If CWD fails and sends an error in response, the client is notified of that failure and the FtpDirectoryChangedFailed event is raised:

void ftpClient_FtpDirectoryChangedSucceded(object sender,
  FtpDirectoryChangedEventArgs e)
{
  // Handle DirectoryChangedSucceeded event to show
  // some prompt or message to user
}
void ftpClient_FtpDirectoryChangedFailed(object sender,
  FtpDirectoryChangedEventArgs e)
{
  // Handle DirectoryChangedFailed event to show
  // some prompt or message to user
}

Changing Working Directory on the FTP Server
Figure 6 Changing Working Directory on the FTP Server

Adding Support for Passive Commands

Now it’s time to provide support for passive commands, because I want the FTP server to manage all the data connections for transferring data to and from the server. In passive mode, any command that needs to transfer data must use a data connection initiated by the FTP server. The FTP server will create a data connection and send the socket information for that data connection so the client can connect to it and perform some data-transfer operations.

Passive mode is temporary in nature. Before sending a command to the FTP server that will send or receive data in response, the client has to tell the server to go into passive mode—and it must do so for every command sent. In a nutshell, for every data transfer, two commands are always fired—passive and then the command itself.

The FTP server is told to prepare for passive mode via the PASV command. In response, it sends the IP address and port number of the data connection in an encoded format. The response is decoded and the client then prepares the data connection with the FTP server using the decoded IP address and port number. Once the data operation is complete, this data connection is closed and dissolved, so that it can’t be used again. This happens every time the PASV command is sent to the FTP server.

Decoding the PASV Command Response The response to the PASV command looks like this:

227 Entering Passive Mode (192,168,33,238,255,167)

The response has data channel information contained within it. Based on this response, I have to formulate the socket address—the IP address and data port the FTP server is using for the data-transfer operation. Here are the steps to calculate the IP address and port, as shown in Figure 7:

  • The first four integer groups form the IPV4 address of the FTP server; that is, 192.168.33.238.
  • The remaining integers comprise the data port. You left-shift the fifth integer group with 8 and then perform Bitwise OR operation with the sixth integer group. The final value gives you the port number where the data channel will be available.

Passive Command Request and Reply
Figure 7 Passive Command Request and Reply

Figure 8 shows the code that parses the response and extracts the IP address and port number of the data channel’s endpoint. The method PrepareDataChannelAsync receives the raw FTP response for the PASV command. Sometimes the response string can be changed on the FTP server to include some other parameters, but essentially the response sends just the IP address and port number.

Figure 8 Parsing the Data Channel Endpoint

private async Task PrepareDataChannelAsync(String ChannelInfo)
{
  ChannelInfo = ChannelInfo.Remove(0, "227 Entering Passive Mode".Length);
  // Configure the IP Address
  String[] Splits = ChannelInfo.Substring(ChannelInfo.IndexOf("(") + 1,
    ChannelInfo.Length - ChannelInfo.IndexOf("(") - 5).Split(
      new char[] { ',', ' ', },
  StringSplitOptions.RemoveEmptyEntries);
  String Ipaddr = String.Join(".", Splits, 0, 4);
  // Calculate the Data Port
  Int32 port = Convert.ToInt32(Splits[4]);
  port = ((port << 8) | Convert.ToInt32(Splits[5]));
  logger.AddLog(String.Format(
    "FTP Data Channel IPAddress: {0}, Port: {1}", Ipaddr, port));
  // Create data channel here with extracted IP Address and Port number
  logger.AddLog("FTP Data Channel connected");
}

More Commands: List, Type, STOR and RETR

Listing the Directory Contents To list the contents of the current working directory, I send the LIST command over the command channel to the FTP server. However, the FTP server will send its response to this command over the data channel, so I must first send the PASV command in order to create the data connection over which the LIST command’s response will be sent. The directory listing format will be based on the OS on which the FTP server is installed. That is, if the FTP server OS is Windows, the directory listing will be received in the Windows directory listing format, and if the OS is Unix-based, the directory listing will be received in Unix format.

To list the directory contents of the present working directory, I issue the LIST command in the command textbox and in the Send Command button’s tap event, I call the GetDirectoryListingAsync method:

async private void btnFtp_Tap(object sender,
  System.Windows.Input.GestureEventArgs e)
{
  ...
  if (txtCmd.Text.Equals("LIST"))
  {
    logger.Logs.Clear();
    await ftpClient.GetDirectoryListingAsync();
    return;
  }
  ...
}

Internally, GetDirectoryListingAsync sends the PASV command to the FTP server over the socket:

public async Task GetDirectoryListingAsync()
{
  if (!IsBusy)
  {
    fileListingData = null;
    IsBusy = true;
    ftpCommand = FtpCommand.Passive;
    logger.AddLog("FTPClient -> PASV\r\n");
    await FtpCommandSocket.SendData("PASV\r\n");
  }
}

Once the endpoint information is received for the PASV command, the data connection is created and the LIST command is then issued to the FTP server, as shown in Figure 9.

Figure 9 Processing the List Command in the Socket’s DataReceived Event

async void FtpClientSocket_DataReceived(object sender, D
    ataReceivedEventArgs e)
{
  String Response = System.Text.Encoding.UTF8.GetString(
    e.GetData(), 0, e.GetData().Length);
  ...
  IsBusy = true;
  DataReader dataReader = new DataReader(
    FtpDataChannel.InputStream);
  dataReader.InputStreamOptions = InputStreamOptions.Partial;
  fileListingData = new List<byte>();
  while (!(await dataReader.LoadAsync(1024)).Equals(0))
  {
    fileListingData.AddRange(dataReader.DetachBuffer().ToArray());
  }
  dataReader.Dispose();
  dataReader = null;
  FtpDataChannel.Dispose();
  FtpDataChannel = null;
  String listingData = System.Text.Encoding.UTF8.GetString(
    fileListingData.ToArray(), 0, fileListingData.ToArray().Length);
  String[] listings = listingData.Split(
    new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
  List<String> Filenames = new List<String>();
  List<String> Directories = new List<String>();
  foreach (String listing in listings)
  {
    if (listing.StartsWith("drwx") || listing.Contains("<DIR>"))
    {
      Directories.Add(listing.Split(new char[] { ' ' }).Last());
    }
    else
    {
      Filenames.Add(listing.Split(new char[] { ' ' }).Last());
    }
  }
  RaiseFtpDirectoryListedEvent(Directories.ToArray(),
    Filenames.ToArray());
  ...
  fileListingData = new List<byte>();
  ftpPassiveOperation = FtpPassiveOperation.ListDirectory;
  logger.AddLog("FTPClient -> LIST\r\n");
  await FtpCommandSocket.SendData("LIST\r\n");
  ...
}

When the directory listing is received over the data channel, the FtpDirectoryListed event is raised with the list of files and directories in its event arguments (Figure 10 shows sending the PASV and LIST commands, and Figure 11 displays the output of directory listing):

void ftpClient_FtpDirectoryListed(object sender, 
    FtpDirectoryListedEventArgs e)
{
  foreach (String filename in e.GetFilenames())
  {
    // Handle the name of filenames in current working directory
  }
  foreach (String directory in e.GetDirectories())
  {
    // Handle the name of directories in current working directory
  }
}

Sending LIST Command to the FTP Server
Figure 10 Sending LIST Command to the FTP Server

Displaying FileSystem Objects in Response to LIST Command
Figure 11 Displaying FileSystem Objects in Response to LIST Command

Describing the Data Format The TYPE command is used to describe the data format in which the file’s data will be received or sent. It doesn’t require PASV mode. I use the “I” format with the TYPE command to signify an Image-type data transmission. TYPE is generally used when storing or retrieving files over a data connection. This tells the FTP server that the transmission will happen in binary mode rather than in textual or some data-structural mode. For other modes that can be used with the TYPE command, refer to RC 959 for FTP.

Storing a File on the FTP Server To store the contents of a file (located on a device) to an FTP server, I send the STOR command along with the name of the file (and extension) the FTP server will use to create and save the file. The transmission of the file contents will be performed on a data connection so, before sending STOR, I’ll query the endpoint details from the FTP server using the PASV command. Once the endpoint is received, I’ll send STOR, and when I receive the response to it, I’ll send the file contents in binary form to the FTP server over the data connection.

The STOR command can be sent by calling the UploadFileAsync method, as shown in Figure 12. This method takes two parameters—the Stream object of the local file and the name of the file on the FTP server as a String.

Figure 12 The UploadFileAsync Method

async private void btnFtp_Tap(object sender, 
    System.Windows.Input.GestureEventArgs e)
{
  ...
  if (txtCmd.Text.StartsWith("STOR"))
  {
    logger.Logs.Clear();
    String Filename = txtCmd.Text.Split(new char[] { ' ', '/' },      
      StringSplitOptions.RemoveEmptyEntries).Last();
    StorageFile file =
      await Windows.Storage.ApplicationData.Current.LocalFolder.GetFileAsync(
      txtCmd.Text.Split(new char[] { ' ' },
      StringSplitOptions.RemoveEmptyEntries)[1]);
    await ftpClient.UploadFileAsync(await file.OpenStreamForReadAsync(),
      "video2.mp4");
    return;
  }
  ...
}

Internally, the UploadFileAsync method issues a PASV command to the FTP server to fetch the data channel endpoint information:

public async Task UploadFileAsync(
  System.IO.Stream LocalFileStream, String RemoteFilename)
{
  if (!IsBusy)
  {
    ftpFileInfo = null;
    IsBusy = true;
    ftpFileInfo = new FtpFileOperationInfo(LocalFileStream, 
        RemoteFilename, true);
    ftpCommand = FtpCommand.Type;
    logger.AddLog("FTPClient -> TYPE I\r\n");
    await FtpCommandSocket.SendData("TYPE I\r\n");
  }
}

As shown in Figure 13, in the PASV command’s response within the DataReceived event, the STOR command is issued after the data channel is created and opened; the data transmission then starts from client to server.

Figure 13 Processing the STOR Command in the Socket’s DataReceived Event

async void FtpClientSocket_DataReceived(
  object sender, DataReceivedEventArgs e)
{
  String Response = System.Text.Encoding.UTF8.GetString(
    e.GetData(), 0, e.GetData().Length);
  ...
IsBusy = true;
  DataWriter dataWriter = new DataWriter(
    FtpDataChannel.OutputStream);
  byte[] data = new byte[32768];
  while (!(await ftpFileInfo.LocalFileStream.ReadAsync(
    data, 0, data.Length)).Equals(0))
  {
    dataWriter.WriteBytes(data);
    await dataWriter.StoreAsync();
    RaiseFtpFileTransferProgressedEvent(
      Convert.ToUInt32(data.Length), true);
  }
  await dataWriter.FlushAsync();
  dataWriter.Dispose();
  dataWriter = null;
  FtpDataChannel.Dispose();
  FtpDataChannel = null;
  ...
  await PrepareDataChannelAsync(Response);
  ftpPassiveOperation = FtpPassiveOperation.FileUpload;
  logger.AddLog(String.Format("FTPClient -> STOR {0}\r\n",
    ftpFileInfo.RemoteFile));
  await FtpCommandSocket.SendData(String.Format("STOR {0}\r\n",
    ftpFileInfo.RemoteFile));
  ...
}

While the file is uploading, the client is notified of the upload progress via FtpFileTransferProgressed:

void ftpClient_FtpFileTransferProgressed(
  object sender, FtpFileTransferProgressedEventArgs e)
{
  // Update the UI with some progressive information
  // or use this to update progress bar
}

If the file-uploading operation completes successfully, the FtpFile­UploadSucceeded event is raised. If not, the FtpFileUploadFailed event is raised with the failure reason in its argument:

void ftpClient_FtpFileUploadSucceeded(
  object sender, FtpFileTransferEventArgs e)
{
  // Handle UploadSucceeded Event to show some
  // prompt or message to user
}
void ftpClient_FtpFileUploadFailed (
  object sender, FtpFileTransferEventArgs e)
{
  // Handle UploadFailed Event to show some
  // prompt or message to user
}

Figure 14 and Figure 15 display the process of uploading a file to the FTP server.

Uploading a File to the FTP Server
Figure 14 Uploading a File to the FTP Server

Successful File Upload
Figure 15 Successful File Upload

Retrieving a File from an FTP ServerTo retrieve the contents of a file from an FTP server, I send the RETR command along with the name of the file and its extension to the FTP server (assuming the file is on the server in the current directory). The transmission of the file contents will use a data connection, so, before sending RETR, I’ll query the endpoint details from the FTP server using the PASV command. Once the endpoint is received, I’ll send the RETR command, and after receiving the response to it, I’ll retrieve the file contents in binary form from the FTP server over the data connection.

As shown in Figure 16, the RETR command can be sent by calling the DownloadFileAsync method. This method takes two parameters—the Stream object of the local file for saving the content and the name of the file on the FTP server as a String.

Figure 16 The DownloadFileAsync Method

async private void btnFtp_Tap(
  object sender, System.Windows.Input.GestureEventArgs e)
{
  ...
  if (txtCmd.Text.StartsWith("RETR"))
  {
    logger.Logs.Clear();
    String Filename = txtCmd.Text.Split(new char[] { ' ', '/' },
      StringSplitOptions.RemoveEmptyEntries).Last();
    StorageFile file =
      await Windows.Storage.ApplicationData.Current.LocalFolder.
        CreateFileAsync(
      txtCmd.Text.Split(new char[] { ' ' }, StringSplitOptions.
        RemoveEmptyEntries)[1],
      CreationCollisionOption.ReplaceExisting);
    await ftpClient.DownloadFileAsync(
      await file.OpenStreamForWriteAsync(), Filename);
    return;
  }
  ...
}

Internally, the DownloadFileAsync method issues a PASV command to the FTP server to fetch the data channel’s endpoint information:

public async Task DownloadFileAsync(
  System.IO.Stream LocalFileStream, String RemoteFilename)
{
  if (!IsBusy)
  {
    ftpFileInfo = null;
    IsBusy = true;
    ftpFileInfo = new FtpFileOperationInfo(
      LocalFileStream, RemoteFilename, false);
    ftpCommand = FtpCommand.Type;
    logger.AddLog("FTPClient -> TYPE I\r\n");
    await FtpCommandSocket.SendData("TYPE I\r\n");
  }
}

As shown in Figure 17, in the PASV command’s response inside the DataReceived event, the RETR command is issued after the data channel is created and opened; then the data transmission starts, running from server to client.

Figure 17 Processing the RETR Command in the Socket’s DataReceived Event

async void FtpClientSocket_DataReceived(
  object sender, DataReceivedEventArgs e)
{
  String Response = System.Text.Encoding.UTF8.GetString(
    e.GetData(), 0, e.GetData().Length);
  ...
  IsBusy = true;
  DataReader dataReader = new DataReader(FtpDataChannel.InputStream);
  dataReader.InputStreamOptions = InputStreamOptions.Partial;
  while (!(await dataReader.LoadAsync(32768)).Equals(0))
  {
    IBuffer databuffer = dataReader.DetachBuffer();
    RaiseFtpFileTransferProgressedEvent(databuffer.Length, false);
    await ftpFileInfo.LocalFileStream.WriteAsync(
      databuffer.ToArray(), 0, Convert.ToInt32  (databuffer.Length));
  }
  await ftpFileInfo.LocalFileStream.FlushAsync();
  dataReader.Dispose();
  dataReader = null;
  FtpDataChannel.Dispose();
  FtpDataChannel = null;
  ...
  await PrepareDataChannelAsync(Response);
  ftpPassiveOperation = FtpPassiveOperation.FileDownload;
  logger.AddLog(String.Format("FTPClient -> RETR {0}\r\n",
    ftpFileInfo.RemoteFile));
  await FtpCommandSocket.SendData(String.Format("RETR {0}\r\n",
    ftpFileInfo.RemoteFile));
  ...
  }
}

While the file is downloading, the client is notified of the download progress via FtpFileTransferProgressed:

void ftpClient_FtpFileTransferProgressed(object sender,
  FtpFileTransferProgressedEventArgs e)
{
  // Update the UI with some progressive information or use
  // this to update progress bar
}

If the file downloading operation completes successfully, the FtpFileDownloadSucceeded event is raised. If not, the FtpFileDownloadFailed event is raised with the failure reason in its argument:

void ftpClient_FtpFileDownloadSucceeded(object sender,
  FtpFileTransferFailedEventArgs e)
{
  // Handle DownloadSucceeded event to show some prompt
  // or message to user
}
void ftpClient_FtpFileDownloadFailed(object sender,
  FtpFileTransferFailedEventArgs e)
{
  // Handle UploadFailed Event to show some prompt
  // or message to user
}

Figure 18 and Figure 19 display the process of downloading a file from the FTP server.

Downloading a File from the FTP Server
Figure 18 Downloading a File from the FTP Server

Successful File Download
Figure 19 Successful File Download

Wrapping Up

Note that you can provide a URI association to an app implementing FTP, so that other apps can also access the FTP service and request data from the FTP server. You’ll find more information about this on the Windows Phone Dev Center page, “Auto-launching apps using file and URI associations for Windows Phone 8,” at bit.ly/XeAaZ8, and sample code at bit.ly/15x4O0y.

The code I’ve written for my FTP library and client app for Windows Phone is fully supported on Windows 8.x, as I haven’t used any API that isn’t compatible with Windows 8.x. You can either recompile the code for Windows 8.x, or put it in a Portable Class Library (PCL) that can target both platforms.


Uday Gupta is senior engineer - product development for Symphony Teleca Corp. (India) Pvt Ltd. He has experience in many .NET technologies, especially in Windows Presentation Foundation (WPF), Silverlight, Windows Phone and Windows 8. Most of his time is spent in coding, gaming, learning new things and helping others.

THANKS to the following technical experts for reviewing this article: Tony Champion (Champion DS) and Andy Wigley (Microsoft)
Tony Champion is president of Champion DS, is a Microsoft MVP, and active in the community as a speaker, blogger, and author. He maintains a blog at tonychampion.net and can be reached via e-mail at tony@tonychampion.net.

Andy Wigley is a Technical Evangelist working for Microsoft UK. Andy is well-known for the popular Windows Phone JumpStart videos which are available on channel9.msdn.com and is a regular speaker at major conferences such as Tech Ed.