From e078a2887cf2dc61da49ce302324ff1485f95af9 Mon Sep 17 00:00:00 2001 From: Ian C Date: Mon, 1 Oct 2012 20:59:12 +0000 Subject: Added non-working code for an attempt at FTP --- Noddybox.WindowsPhone.FTP/FTPClient.cs | 828 +++++++++++++++++++++++++++++++++ 1 file changed, 828 insertions(+) create mode 100644 Noddybox.WindowsPhone.FTP/FTPClient.cs (limited to 'Noddybox.WindowsPhone.FTP/FTPClient.cs') diff --git a/Noddybox.WindowsPhone.FTP/FTPClient.cs b/Noddybox.WindowsPhone.FTP/FTPClient.cs new file mode 100644 index 0000000..1ae13ce --- /dev/null +++ b/Noddybox.WindowsPhone.FTP/FTPClient.cs @@ -0,0 +1,828 @@ +// This file is part of the Noddybox.WindowsPhone C# suite. +// +// Noddybox.Emulation is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Noddybox.Emulation is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Noddybox.Emulation. If not, see . +// +// Copyright (c) 2012 Ian Cowburn +// +using System; +using System.Text; +using System.IO; +using System.Net; +using System.Net.Sockets; +using System.Collections.Generic; + +namespace Noddybox.WindowsPhone.Net +{ + /// + /// Provides a simple FTP client interface. Currently only supports IP4. + /// + public class FTPClient + { + // ------------------------------------------------- + // - PUBLIC MEMBERS - + // ------------------------------------------------- + + /// + /// Transfer type + /// + public enum TransferType + { + /// + /// ASCII Data + /// + ASCII, + + /// + /// Binary (image) Data + /// + Binary + }; + + /// + /// Default constructor + /// + public FTPClient(string server_address) + { + passive = false; + connected = true; + welcome = ""; + system = "unknown"; + type = TransferType.Binary; + + passiveAddress = IPAddress.Loopback; + + lastCode=-1; + + try + { + encode = new ASCIIEncoding(); + + server = new ServerDialog(); + + ServerDialog.Reply r; + + commandClient = new TcpClient(server_address,21); + command = commandClient.GetStream(); + + r = BareResult(); + + if (r.Code != 220) + { + connected=false; + commandClient=null; + command=null; + } + } + catch (SocketException e) + { + connected=false; + } + } + + /// + /// Sets timeouts for the command connection + /// + public void SetTimeouts(int recv_timeout, + int send_timeout) + { + recvTimeout=recv_timeout; + sendTimeout=send_timeout; + + if (connected) + { + commandClient.ReceiveTimeout=recv_timeout; + commandClient.SendTimeout=send_timeout; + } + } + + /// + /// Set/get whether to use passive mode + /// + public bool Passive + { + get {return passive;} + + set + { + passive=value; + } + } + + /// + /// Set/get the IP address to offer in non-passive mode + /// + public IPAddress NonPassiveIP + { + get {return passiveAddress;} + + set + { + passiveAddress=value; + } + } + + /// + /// Logon to the FTP server + /// + public bool Logon(string username,string password) + { + ServerDialog.Reply r; + + r=SendCommand("USER "+username); + + if (r.Code==331) + { + r=SendCommand("PASS "+password); + } + + if (r.Code==230) + { + SendCommand("SYST"); + } + else + { + return false; + } + + return true; + } + + /// + /// Whether connected to the FTP server + /// + public bool Connected + { + get {return connected;} + } + + /// + /// The welcome text after logging on + /// + public string Welcome + { + get {return welcome;} + } + + /// + /// The system type. Note the entire system response is + /// returned. If you want just the first word, string.split() + /// it. + /// + public string System + { + get {return system;} + } + + /// + /// The transfer type + /// + public TransferType Type + { + get + { + return type; + } + + set + { + type=value; + + switch(type) + { + case TransferType.ASCII: + SendCommand("TYPE A"); + break; + + case TransferType.Binary: + SendCommand("TYPE I"); + break; + } + } + } + + /// + /// Get the current working directory + /// + public string Pwd + { + get + { + string pwd=""; + + ServerDialog.Reply r=SendCommand("PWD"); + + if (r.Code==257) + { + pwd=ParseQuote(r.Text); + } + + return pwd; + } + } + + /// + /// Change directory to the parent directory + /// + public bool CDUp() + { + return SendCommand("CDUP").Code==250; + } + + /// + /// Change directory + /// + public bool CD(string dir) + { + return SendCommand("CWD "+dir).Code==250; + } + + /// + /// Get a list of the filenames in the current directory + /// + public StringCollection Dir() + { + StringCollection l=new StringCollection(); + ServerDialog.Reply r; + TransferType old_type=type; + + PrepareData(); + + Type=TransferType.ASCII; + + r=SendCommand("NLST"); + + if (r.Code==150 || r.Code==125) + { + OpenData(); + + string s=GetDataString(); + string fn=""; + + foreach (char c in s) + { + if (c=='\n') + { + l.Add(fn.TrimEnd(null)); + fn=""; + } + else + { + fn+=c; + } + } + + CloseData(); + } + + BareResult(); + + Type=old_type; + + return l; + } + + /// + /// Get a detailed list of the filenames in the current directory. + /// The list returned is made up of FTPFile classes. + /// + public FTPFileCollection DirEx() + { + StringCollection l=Dir(); + FTPFileCollection al=new FTPFileCollection(); + + foreach(string s in l) + { + FTPFile.FileType type=FTPFile.FileType.File; + ulong size; + + // See if it's a directory by seeing if CD works + // + if (CD(s)) + { + type=FTPFile.FileType.Dir; + size=0; + CDUp(); + } + else + { + ServerDialog.Reply r; + + // Get the file size + // + r=SendCommand("SIZE "+s); + + if (r.Code==213) + { + size=Convert.ToUInt64(r.Text); + } + else + { + size=UInt64.MaxValue; + } + } + + al.Add(new FTPFile(s,type,size)); + } + + al.Sort(); + + return al; + } + + /// + /// Delete a specified file + /// + public bool Delete(string name) + { + return SendCommand("DELE "+name).Code==250; + } + + /// + /// Delete a specified directory + /// + public bool Rmdir(string name) + { + return SendCommand("RMD "+name).Code==250; + } + + /// + /// Create a specified directory + /// + public bool Mkdir(string name) + { + return SendCommand("MKD "+name).Code==257; + } + + /// + /// Rename a specified file + /// + public bool Rename(string old_name, string new_name) + { + ServerDialog.Reply r; + + r=SendCommand("RNFR "+old_name); + + if (r.Code!=350) + { + return false; + } + + r=SendCommand("RNTO "+new_name); + + if (r.Code!=250) + { + return false; + } + + return true; + } + + /// + /// Gets a file as a byte array + /// + public byte[] Get(string name) + { + ArrayList al=new ArrayList(); + + PrepareData(); + + ServerDialog.Reply r=SendCommand("RETR "+name); + + if (r.Code==150 || r.Code==125) + { + OpenData(); + GetData(al); + CloseData(); + } + else + { + return null; + } + + BareResult(); + + // Surely there must be a better way... + // + int count=al.Count; + byte[] file=new byte[count]; + + for(int f=0;f + /// Gets a file and stores it in a local file + /// + public bool Get(string name, string local_name) + { + byte[] data=Get(name); + + if (data==null) + { + return false; + } + + FileStream file=new FileStream(local_name,FileMode.Create); + BinaryWriter bin=new BinaryWriter(file); + + bin.Write(data); + + bin.Close(); + + return true; + } + + /// + /// Puts a file as a byte array + /// + public bool Put(string name, byte[] data) + { + PrepareData(); + + ServerDialog.Reply r=SendCommand("STOR "+name); + + if (r.Code==150 || r.Code==125) + { + OpenData(); + PutData(data); + CloseData(); + } + else + { + return false; + } + + r=BareResult(); + + return r.Code==226; + } + + /// + /// Puts a file and from a local file + /// + public bool Put(string name, string local_name) + { + if (!File.Exists(local_name)) + { + return false; + } + + FileInfo info=new FileInfo(local_name); + + FileStream file=new FileStream(local_name,FileMode.Open,FileAccess.Read); + BinaryReader bin=new BinaryReader(file); + + byte[] data=bin.ReadBytes((int)info.Length); + bin.Close(); + + return Put(name,data); + } + + /// + /// The last code returned by the server (see RFC 959) + /// + public int LastCode + { + get {return lastCode;} + } + + /// + /// Quit and disconnect from the server. + /// + public void Quit() + { + if (connected) + { + SendCommand("QUIT"); + command.Close(); + } + } + + // ------------------------------------------------- + // - PRIVATE MEMBERS - + // ------------------------------------------------- + + private bool connected; + private TcpClient commandClient; + private Socket data; + private Socket nonPassive; + private IPEndPoint dataAddr; + private NetworkStream command; + private string welcome; + private string system; + private int lastCode; + private ServerDialog server; + private int recvTimeout; + private int sendTimeout; + private ASCIIEncoding encode; + private bool passive; + private TransferType type; + private IPAddress passiveAddress; + + private string ParseQuote(string s) + { + string r=""; + bool in_quote=false; + + foreach (char c in s) + { + if (c=='\"') + { + in_quote=!in_quote; + } + else + { + if (in_quote) + { + r+=c; + } + } + } + + return r; + } + + private IPEndPoint ParseAddress(string s) + { + string r=""; + bool in_quote=false; + + foreach (char c in s) + { + if (c=='(' || c==')') + { + in_quote=!in_quote; + } + else + { + if (in_quote) + { + r+=c; + } + } + } + + char[] sep=new char[1] {','}; + string[] arg=r.Split(sep); + + if (arg.Length!=6) + { + throw new NetException(NetException.Type.FTP_BadReplyAddress); + } + + int port=Convert.ToInt32(arg[4])*256+Convert.ToInt32(arg[5]); + + IPEndPoint ip=new IPEndPoint(IPAddress.Parse(arg[0] + "." + + arg[1] + "." + + arg[2] + "." + + arg[3]), + port); + + return ip; + } + + private void PrepareData() + { + if (passive) + { + ServerDialog.Reply r=SendCommand("PASV"); + + if (r.Code!=227) + { + throw new NetException(NetException.Type.FTP_NoPassive); + } + + dataAddr=ParseAddress(r.Text); + + DebugOut("PASV told to use " + dataAddr.ToString()); + + data=new Socket(AddressFamily.InterNetwork, + SocketType.Stream, + ProtocolType.Tcp); + + data.Connect(dataAddr); + } + else + { + if (nonPassive==null) + { + try + { + nonPassive=new Socket(AddressFamily.InterNetwork, + SocketType.Stream, + ProtocolType.Tcp); + + if (passiveAddress==IPAddress.None) + { + IPAddress[] host=Dns.Resolve(Dns.GetHostName()).AddressList; + + if (host.Length==0) + { + throw new NetException(NetException.Type.ALL_NoLocalIP); + } + + passiveAddress=host[0]; + } + + IPEndPoint ep=new IPEndPoint(passiveAddress,0); + + nonPassive.Bind(ep); + + ep=(IPEndPoint)nonPassive.LocalEndPoint; + + byte[] a=ep.Address.GetAddressBytes(); + int p=ep.Port; + + int p1=p>>8; + int p2=p&0xff; + + string portCmd="PORT " + + a[0].ToString() + "," + + a[1].ToString() + "," + + a[2].ToString() + "," + + a[3].ToString() + "," + + p1.ToString() + "," + + p2.ToString(); + + ServerDialog.Reply r=SendCommand(portCmd); + + if (r.Code!=200) + { + nonPassive.Close(); + nonPassive=null; + throw new NetException + (NetException.Type.FTP_NonPassiveProblem); + } + + nonPassive.Listen(5); + } + catch (SocketException e) + { + nonPassive=null; + throw e; + } + } + } + } + + private void OpenData() + { + if (passive) + { + // Connection already created by PrepareData() + } + else + { + // TODO : This could actually block indefinitely + // + data=nonPassive.Accept(); + + if (data==null) + { + throw new NetException + (NetException.Type.FTP_NonPassiveProblem); + } + } + } + + private void CloseData() + { + DebugOut("Closing data connection"); + + data.Shutdown(SocketShutdown.Both); + data.Close(); + data=null; + + if (!passive) + { + DebugOut("Closing non-passive listener"); + + nonPassive.Close(); + nonPassive=null; + } + } + + private string GetDataString() + { + byte[] buff=new byte[1024]; + string result=""; + + while(true) + { + int len=data.Receive(buff,buff.Length,SocketFlags.None); + + DebugOut("Read " + len.ToString() + " bytes from data connection"); + + if (len==0) + { + break; + } + + result+=encode.GetString(buff,0,len); + } + + DebugOut("Read string '" + result + "' data connection"); + + return result; + } + + private void GetData(ArrayList al) + { + byte[] buff=new byte[1024]; + + while(true) + { + int len=data.Receive(buff,buff.Length,SocketFlags.None); + + DebugOut("Read " + len.ToString() + " bytes from data connection"); + + if (len==0) + { + break; + } + + // This whole idea needs sorting - this will be painful + // + for(int f=0;f-1) + { + lastCode=r.Code; + } + + switch(r.Code) + { + case 215: + system = r.Text; + break; + + case 230: + welcome = r.Text; + break; + + default: + break; + } + } + } +} -- cgit v1.2.3