FtpWebRequest
Although the S3 fees are not high, if you already have access to an FTP site, you may prefer to host your files there. But beware! Unless your FTP site is running over SSL, your FTP credentials (user ID and password) are transmitted over the Internet in plaintext, and can be intercepted and abused by bad guys. This vulnerability applies to every program that uses the FTP protocol, not just the sample app. You can see this vulnerability with Packetyzer, a freely available network analysis program (http://www.networkchemistry.com/products/packetyzer.php). Start a network capture in Packetyzer. Then use Synchronize Files to download a file from an FTP site. You'll see a trace like the following in Packetyzer:
USER fredjones ..UR..331 Password required for fredjones. PASS fredspassword ...e..230 User fredjones logged in.
Bad guys monitoring packets on any of the machines through which the transfer travels will be able to capture your credentials. Scary! Although the sample app can't prevent this vulnerability, it encrypts your files before uploading them to the FTP site, and decrypts them only after they've been downloaded to your machine. Consequently, there's little risk that bad guys could decrypt your files by intercepting them during transfers, or by downloading them directly from your FTP site.
.NET 2.0 provides native FTP access with FtpWebRequest, but this class is little more than a thin wrapper over the FTP protocol. See FTPDataTransfer for examples of using the FtpWebRequest to synchronize files between the user's machine and an FTP site. FTPDataTransfer.GetFtpWebRequest (Listing One) returns an FtpWebRequest object. The KeepAlive property is set to False to ensure that the connection to the FTP server is closed after every FTP operation. If this isn't done, some FTP operations may fail, depending on the FTP command that was previously executed, and the server's FTP software. Although the KeepAlive = false setting allows arbitrary command sequences to succeed, creating a new FTP connection for each operation is slow. Furthermore, your FTP credentials will be sent to establish each new connection. GetFtpWebRequest includes a Method parameter that specifies the FTP operation that will be performed. There are several FTP operations including UploadFile, DownloadFile, DeleteFile, ListDirectory, and so on. Finally, the UseBinary property should be set to True. Otherwise, binary files will not be correctly transferred.
... class FTPDataTransfer : DataTransfer { ... private FtpWebRequest GetFtpWebRequest(string FTPAddress, string Method) { FtpWebRequest ftpWebRequest = null; try { ftpWebRequest = (FtpWebRequest)WebRequest.Create(FTPAddress); // Start a new FTP session. ftpWebRequest.KeepAlive = false; // Data will be transferred in binary format. ftpWebRequest.UseBinary = true; ftpWebRequest.Credentials = new NetworkCredential(userID, password); ftpWebRequest.Method = Method; } catch (UriFormatException ex) { // Display error message if FTP address is invalid. MessageBox.Show(ex.Message, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error); } return ftpWebRequest; } ... protected override byte[] DownloadBuffer(string BucketName, string FileName) { WebResponse response = null; // Convert bucket and filename into legal FTP folder and filenames. BucketName = EncodeName(BucketName); FileName = EncodeName(FileName); byte[] result = null; try { string ftpAddress = ftpSite + "/" + ftpHomeDirectory + "/" + BucketName + "/" + FileName; FtpWebRequest ftpWebRequest = GetFtpWebRequest(ftpAddress, WebRequestMethods.Ftp.DownloadFile); response = ftpWebRequest.GetResponse(); // Prepare to read the file from the response stream. Stream responseStream = response.GetResponseStream(); BinaryReader binaryReader = new BinaryReader(responseStream); const int bufferSize = 1024; byte[] buffer; List<byte> bufferList = new List<byte>(); // Read the file, one buffer at a time. do { buffer = binaryReader.ReadBytes(bufferSize); if (buffer.Length > 0) { bufferList.AddRange(buffer); } } while (buffer.Length == bufferSize); result = bufferList.ToArray(); binaryReader.Close(); } catch (WebException) { // A WebException will be thrown if file does not exist on server. } finally { if (response != null) { response.Close(); } } return result; } ... }
FTPTransfer.DownloadBuffer calls GetFtpWebRequest to download a file's contents (Listing One). First the EncodeName method is called to convert the bucket name and file name into characters that FTP supports. Then GetFtpWebRequest is called with the FTP address of the file that will be downloaded, and the DownloadFile method. GetResponse is called to access the FtpWebRequest's response stream. The contents of the file are then read from the stream using a BinaryReader. After the stream is closed, the file's contents are returned. When a file is uploaded to an FTP site, its contents are written to the FtpWebRequest's request stream (see FTPTransfer.UploadBuffer for details).