/* Copyright (c) 2006, J.P. Trosclair * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are permitted * provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, this list of conditions and * the following disclaimer. * * Redistributions in binary form must reproduce the above copyright notice, this list of conditions * and the following disclaimer in the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * Based on FTPFactory.cs code, pretty much a complete re-write with FTPFactory.cs * as a reference. * *********************** * Authors of this code: *********************** * J.P. Trosclair (jptrosclair@judelawfirm.com) * Filipe Madureira (filipe_madureira@hotmail.com) * Carlo M. Andreoli (cmandreoli@numericaprogetti.it) * Sloan Holliday (sloan@ipass.net) * Johann Pascher (johann.pascher@gmail.com) * *********************** * FTPFactory.cs was written by Jaimon Mathew (jaimonmathew@rediffmail.com) * and modified by Dan Rolander (Dan.Rolander@marriott.com), * Johann Pascher (johann.pascher@gmail.com) Remove most unneeded code to adapt. *********************** * * ** DO NOT ** contact the authors of FTPFactory.cs about problems with this code. It * is not their responsibility. * * Any bug fixes or additions to the code will be properly credited to the author. * * * All calls to the ftplib functions should be: * * try * { * // ftplib function call * } * catch(Exception ex) * { * // error handeler * } * * If you add to the code please make use of OpenDataSocket(), CloseDataSocket(), and * ReadResponse() appropriately. See the comments above each for info about using them. * * The Fail() function terminates the entire connection. Only call it on critical errors. * Non critical errors should NOT close the connection. * All errors should throw an exception of type Exception with the response string from * the server as the message. */ //#define FTP_DEBUG using System; using System.IO; using System.Net; using System.Net.Sockets; using System.Text; using System.Text.RegularExpressions; using System.Collections; using System.Diagnostics; namespace ftplib { public class FTP { #region Public Variables /// /// IP address or hostname to connect to /// public string server; /// /// Username to login as /// public string user; /// /// Password for account /// public string pass; /// /// Port number the FTP server is listening on /// public int port; /// /// Data Port number the FTP server is listening on /// public int dataport; /// /// Data server IP number the FTP server is listening on /// public string dataserver; /// /// The timeout (miliseconds) for waiting on data to arrive /// public int timeout; public string messages; // server messages public string responseStr; // server response if the user wants it. public int response; //response Number #endregion #region Private Variables private long bytes_total; // upload/download info if the user wants it. private long file_size; // gets set when an upload or download takes place private Socket main_sock; private Socket data_sock; private IPEndPoint main_ipEndPoint; private IPEndPoint data_ipEndPoint; private FileStream file; private string bucket = ""; #endregion /// /// Connection status to the server /// public bool IsConnected { get { if (main_sock != null) return main_sock.Connected; return false; } } /// /// Returns true if the message buffer has data in it /// public bool MessagesAvailable { get { if(messages.Length > 0) return true; return false; } } /// /// Server messages if any, buffer is cleared after you access this property /// public string Messages { get { string tmp = messages; messages = ""; return tmp; } } /// /// The response string from the last issued command /// public string ResponseString { get { return responseStr; } } /// /// The response number from the last issued command /// public int Response { get { return response; } } /// /// The total number of bytes sent/recieved in a transfer /// public long BytesTotal { get { return bytes_total; } } /// /// The size of the file being downloaded/uploaded (Can possibly be 0 if no size is available) /// public long FileSize { get { return file_size; } } /// /// Connect to a ftp server /// /// IP of the server to connect to public void Connect(string server) { this.server = server; if (main_sock != null) if (main_sock.Connected) return; main_sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //main_ipEndPoint = new IPEndPoint(Dns.GetHostByName(server).AddressList[0], port); main_ipEndPoint = new IPEndPoint(IPAddress.Parse(server), port); try { main_sock.Connect(main_ipEndPoint); } catch (Exception ex) { throw new Exception(ex.Message); } } /// /// Upload the file, to be used in a loop until file is completely uploaded /// /// Bytes sent public long DoUpload1460() { Byte[] bytes = new Byte[1460]; long bytes_got; try { bytes_got = file.Read(bytes, 0, bytes.Length); bytes_total += bytes_got; if (bytes_got > 0) data_sock.Send(bytes, (int)bytes_got, 0); if (bytes_got <= 0) { // the upload is complete or an error occured file.Close(); file = null; CloseDataSocket(); ReadResponse(); switch (response) { case 226: //transfere complette case 250: break; default: throw new Exception(responseStr); } } } catch (Exception ex) { file.Close(); file = null; CloseDataSocket(); ReadResponse(); throw ex; } return bytes_got; } /// /// Open an upload file with resume support /// public void OpenUploadFile(string filename) { try { file = new FileStream(filename, FileMode.Open); } catch (Exception ex) { file = null; throw new Exception(ex.Message); } file_size = file.Length; } public bool SendCommand(string command) { try { Byte[] cmd = Encoding.ASCII.GetBytes((command + "\r\n").ToCharArray()); main_sock.Send(cmd, cmd.Length, 0); } catch (Exception) { return false; } return true; } // Any time a command is sent, use ReadResponse() to get the response from the server. // The variable responseStr holds the entire string. // The variable response holds the response number. // The variable messages holds multilines. public String ReadResponse() { if (main_sock != null) { string buf = ""; messages = ""; while (true) { //-> buf = GetLineFromBucket(); int i; if ((i = bucket.IndexOf('\n')) < 0) { while (i < 0) { //->FillBucket(); Byte[] bytes = new Byte[512]; long bytesgot; //+++++++++++++++++++++++++++++++++++++++++++ while (main_sock.Available < 1) ; //+++++++++++++++++++++++++++++++++++++++++++ while (main_sock.Available > 0) { bytesgot = main_sock.Receive(bytes, 512, 0); bucket += Encoding.ASCII.GetString(bytes, 0, (int)bytesgot); }//<-FillBucket(); i = bucket.IndexOf('\n'); } } buf = bucket.Substring(0, i); bucket = bucket.Substring(i + 1); //<-buf = GetLineFromBucket(); #if (FTP_DEBUG) Debug.WriteLine("buf:" + buf); #endif responseStr = buf; // get line if (Regex.Match(buf, "^[0-9]+ ").Success) { response = int.Parse(buf.Substring(0, 3)); //get 3 digit code break; } else messages += buf; // get multilines into messages } return responseStr; } return "No main Socket!"; } public void GetDataPortFormResponseString() { string[] pasv; if (response != 227) Fail(); try { int i1, i2; i1 = responseStr.IndexOf('(') + 1; i2 = responseStr.IndexOf(')') - i1; pasv = responseStr.Substring(i1, i2).Split(','); } catch (Exception) { Disconnect(); throw new Exception("Malformed PASV response: " + responseStr); } if (pasv.Length < 6) { Disconnect(); throw new Exception("Malformed PASV response: " + responseStr); } dataserver = String.Format("{0}.{1}.{2}.{3}", pasv[0], pasv[1], pasv[2], pasv[3]); dataport = (int.Parse(pasv[4]) << 8) + int.Parse(pasv[5]); } // if you add code that needs a data socket, // Send the PASV command first and GetDataPortFormResponseString(). // opens the appropriate data socket. // The socket variable is Socket data_socket. Once you // are done with it, be sure to call CloseDataSocket() public void OpenDataSocket() { try { #if (FTP_DEBUG) Debug.WriteLine("Data socket: {0}:{1}", dataserver, dataport); #endif CloseDataSocket(); #if (FTP_DEBUG) Debug.WriteLine("Creating socket..."); #endif data_sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); #if (FTP_DEBUG) Debug.WriteLine("Resolving host"); #endif //data_ipEndPoint = new IPEndPoint(Dns.GetHostByName(server).AddressList[0], dataport); data_ipEndPoint = new IPEndPoint(IPAddress.Parse(server), dataport); #if (FTP_DEBUG) Debug.WriteLine("Connecting.."); #endif data_sock.Connect(data_ipEndPoint); #if (FTP_DEBUG) Debug.WriteLine("Connected."); #endif } catch(Exception ex) { throw new Exception("Failed to connect for data transfer: " + ex.Message); } } public void CheckStor() { switch (response) { case 125: case 150: break; default: file.Close(); file = null; throw new Exception(responseStr); } } public bool CheckSocetAvalabel() { System.Threading.Thread.Sleep(500); if (main_sock == null) return true; if (main_sock.Available < 1) return false; return true; } public void Disconnect() //Send QUIT and close data soket main soket and file { CloseDataSocket(); if (main_sock != null) { if (main_sock.Connected) { SendCommand("QUIT"); CloseMainSocket(); } } if (file != null) file.Close(); file = null; } public void CloseMainSocket() //close main channel socket { #if (FTP_DEBUG) Debug.WriteLine("Attempting to close main socket..."); #endif if (main_sock != null) { if (main_sock.Connected) { #if (FTP_DEBUG) Debug.WriteLine("Closing main socket!"); #endif main_sock.Close(); #if (FTP_DEBUG) Debug.WriteLine("Main channel socket closed!"); #endif } main_sock = null; } main_ipEndPoint = null; } public void CloseDataSocket() //close data channel socket { #if (FTP_DEBUG) Debug.WriteLine("Attempting to close data channel socket..."); #endif if (data_sock != null) { if (data_sock.Connected) { #if (FTP_DEBUG) Debug.WriteLine("Closing data channel socket!"); #endif data_sock.Close(); #if (FTP_DEBUG) Debug.WriteLine("Data channel socket closed!"); #endif } data_sock = null; } data_ipEndPoint = null; } public void Fail() { Disconnect(); //throw new Exception(responseStr); } } }