summaryrefslogtreecommitdiff
path: root/Noddybox.WindowsPhone.FTP
diff options
context:
space:
mode:
Diffstat (limited to 'Noddybox.WindowsPhone.FTP')
-rw-r--r--Noddybox.WindowsPhone.FTP/FTPClient.cs828
-rw-r--r--Noddybox.WindowsPhone.FTP/FTPFile.cs125
-rw-r--r--Noddybox.WindowsPhone.FTP/FTPFileCollection.cs145
-rw-r--r--Noddybox.WindowsPhone.FTP/NetException.cs84
-rw-r--r--Noddybox.WindowsPhone.FTP/Noddybox.WindowsPhone.FTP.csproj69
-rw-r--r--Noddybox.WindowsPhone.FTP/Properties/AssemblyInfo.cs37
-rw-r--r--Noddybox.WindowsPhone.FTP/ServerDialog.cs178
7 files changed, 1466 insertions, 0 deletions
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 <http://www.gnu.org/licenses/>.
+//
+// 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
+{
+ /// <summary>
+ /// Provides a simple FTP client interface. Currently only supports IP4.
+ /// </summary>
+ public class FTPClient
+ {
+ // -------------------------------------------------
+ // - PUBLIC MEMBERS -
+ // -------------------------------------------------
+
+ /// <summary>
+ /// Transfer type
+ /// </summary>
+ public enum TransferType
+ {
+ /// <summary>
+ /// ASCII Data
+ /// </summary>
+ ASCII,
+
+ /// <summary>
+ /// Binary (image) Data
+ /// </summary>
+ Binary
+ };
+
+ /// <summary>
+ /// Default constructor
+ /// </summary>
+ 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;
+ }
+ }
+
+ /// <summary>
+ /// Sets timeouts for the command connection
+ /// </summary>
+ 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;
+ }
+ }
+
+ /// <summary>
+ /// Set/get whether to use passive mode
+ /// </summary>
+ public bool Passive
+ {
+ get {return passive;}
+
+ set
+ {
+ passive=value;
+ }
+ }
+
+ /// <summary>
+ /// Set/get the IP address to offer in non-passive mode
+ /// </summary>
+ public IPAddress NonPassiveIP
+ {
+ get {return passiveAddress;}
+
+ set
+ {
+ passiveAddress=value;
+ }
+ }
+
+ /// <summary>
+ /// Logon to the FTP server
+ /// </summary>
+ 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;
+ }
+
+ /// <summary>
+ /// Whether connected to the FTP server
+ /// </summary>
+ public bool Connected
+ {
+ get {return connected;}
+ }
+
+ /// <summary>
+ /// The welcome text after logging on
+ /// </summary>
+ public string Welcome
+ {
+ get {return welcome;}
+ }
+
+ /// <summary>
+ /// The system type. Note the entire system response is
+ /// returned. If you want just the first word, string.split()
+ /// it.
+ /// </summary>
+ public string System
+ {
+ get {return system;}
+ }
+
+ /// <summary>
+ /// The transfer type
+ /// </summary>
+ 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;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Get the current working directory
+ /// </summary>
+ public string Pwd
+ {
+ get
+ {
+ string pwd="";
+
+ ServerDialog.Reply r=SendCommand("PWD");
+
+ if (r.Code==257)
+ {
+ pwd=ParseQuote(r.Text);
+ }
+
+ return pwd;
+ }
+ }
+
+ /// <summary>
+ /// Change directory to the parent directory
+ /// </summary>
+ public bool CDUp()
+ {
+ return SendCommand("CDUP").Code==250;
+ }
+
+ /// <summary>
+ /// Change directory
+ /// </summary>
+ public bool CD(string dir)
+ {
+ return SendCommand("CWD "+dir).Code==250;
+ }
+
+ /// <summary>
+ /// Get a list of the filenames in the current directory
+ /// </summary>
+ 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;
+ }
+
+ /// <summary>
+ /// Get a detailed list of the filenames in the current directory.
+ /// The list returned is made up of FTPFile classes.
+ /// </summary>
+ 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;
+ }
+
+ /// <summary>
+ /// Delete a specified file
+ /// </summary>
+ public bool Delete(string name)
+ {
+ return SendCommand("DELE "+name).Code==250;
+ }
+
+ /// <summary>
+ /// Delete a specified directory
+ /// </summary>
+ public bool Rmdir(string name)
+ {
+ return SendCommand("RMD "+name).Code==250;
+ }
+
+ /// <summary>
+ /// Create a specified directory
+ /// </summary>
+ public bool Mkdir(string name)
+ {
+ return SendCommand("MKD "+name).Code==257;
+ }
+
+ /// <summary>
+ /// Rename a specified file
+ /// </summary>
+ 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;
+ }
+
+ /// <summary>
+ /// Gets a file as a byte array
+ /// </summary>
+ 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<count;f++)
+ {
+ file[f]=(byte)al[f];
+ }
+
+ return file;
+ }
+
+ /// <summary>
+ /// Gets a file and stores it in a local file
+ /// </summary>
+ 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;
+ }
+
+ /// <summary>
+ /// Puts a file as a byte array
+ /// </summary>
+ 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;
+ }
+
+ /// <summary>
+ /// Puts a file and from a local file
+ /// </summary>
+ 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);
+ }
+
+ /// <summary>
+ /// The last code returned by the server (see RFC 959)
+ /// </summary>
+ public int LastCode
+ {
+ get {return lastCode;}
+ }
+
+ /// <summary>
+ /// Quit and disconnect from the server.
+ /// </summary>
+ 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<len;f++)
+ {
+ al.Add(buff[f]);
+ }
+ }
+ }
+
+ private void PutData(byte[] data)
+ {
+ int len=0;
+
+ while(len<data.Length)
+ {
+ len+=data.Send(data,len,data.Length-len,SocketFlags.None);
+ }
+ }
+
+ private ServerDialog.Reply SendCommand(string command)
+ {
+ ServerDialog.Reply r=new ServerDialog.Reply();
+
+ command+="\r\n";
+
+ if (connected)
+ {
+ r=server.Send(command,command);
+ StoreResult(r);
+ }
+
+ return r;
+ }
+
+ private ServerDialog.Reply BareResult()
+ {
+ ServerDialog.Reply r=new ServerDialog.Reply();
+
+ if (connected)
+ {
+ r=server.Send(command,null);
+ StoreResult(r);
+ }
+
+ return r;
+ }
+
+ private void StoreResult(ServerDialog.Reply r)
+ {
+ if (r.Code>-1)
+ {
+ lastCode=r.Code;
+ }
+
+ switch(r.Code)
+ {
+ case 215:
+ system = r.Text;
+ break;
+
+ case 230:
+ welcome = r.Text;
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+}
diff --git a/Noddybox.WindowsPhone.FTP/FTPFile.cs b/Noddybox.WindowsPhone.FTP/FTPFile.cs
new file mode 100644
index 0000000..61ab481
--- /dev/null
+++ b/Noddybox.WindowsPhone.FTP/FTPFile.cs
@@ -0,0 +1,125 @@
+// 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 <http://www.gnu.org/licenses/>.
+//
+// Copyright (c) 2012 Ian Cowburn
+//
+using System;
+
+namespace Noddybox.WindowsPhone.Net
+{
+ using System;
+
+ /// <summary>
+ /// Provides the file info type returned by FTPClient
+ /// </summary>
+ public class FTPFile : IComparable
+ {
+ // -------------------------------------------------
+ // - PUBLIC MEMBERS -
+ // -------------------------------------------------
+
+ /// <summary>
+ /// File type
+ /// </summary>
+ public enum FileType
+ {
+ /// <summary>
+ /// Directory
+ /// </summary>
+ Dir,
+
+ /// <summary>
+ /// File
+ /// </summary>
+ File
+ };
+
+ /// <summary>
+ /// Constructor
+ /// </summary>
+ public FTPFile(string name, FileType type, ulong size)
+ {
+ m_type=type;
+ m_name=name;
+ m_size=size;
+ }
+
+ /// <summary>
+ /// The name of the file
+ /// </summary>
+ public string Name
+ {
+ get {return m_name;}
+ }
+
+ /// <summary>
+ /// The type of the file
+ /// </summary>
+ public FileType Type
+ {
+ get {return m_type;}
+ }
+
+ /// <summary>
+ /// The size of the file (returns 0 for directories)
+ /// </summary>
+ public ulong Size
+ {
+ get
+ {
+ if (m_type==FileType.Dir)
+ {
+ return 0;
+ }
+ else
+ {
+ return m_size;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Compares instance
+ /// </summary>
+ int IComparable.CompareTo(object o)
+ {
+ if (o is FTPFile)
+ {
+ FTPFile f=(FTPFile)o;
+
+ if (m_type==FileType.Dir && f.m_type!=FileType.Dir)
+ {
+ return -1;
+ }
+
+ if (m_type==FileType.File && f.m_type!=FileType.File)
+ {
+ return 1;
+ }
+
+ return m_name.CompareTo(f.m_name);
+ }
+
+ throw new ArgumentException("object not an FTPFile");
+ }
+
+ // -------------------------------------------------
+ // - PRIVATE MEMBERS -
+ // -------------------------------------------------
+ private FileType m_type;
+ private ulong m_size;
+ private string m_name;
+ }
+}
diff --git a/Noddybox.WindowsPhone.FTP/FTPFileCollection.cs b/Noddybox.WindowsPhone.FTP/FTPFileCollection.cs
new file mode 100644
index 0000000..1f8d4cb
--- /dev/null
+++ b/Noddybox.WindowsPhone.FTP/FTPFileCollection.cs
@@ -0,0 +1,145 @@
+// 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 <http://www.gnu.org/licenses/>.
+//
+// Copyright (c) 2012 Ian Cowburn
+//
+using System;
+using System.Collections.Generic;
+
+namespace Noddybox.WindowsPhone.Net
+{
+ /// <summary>
+ /// Collection of FTPFile objects
+ /// </summary>
+ public class FTPFileCollection : IEnumerable<FTPFile>
+ {
+ // -------------------------------------------------
+ // - PUBLIC MEMBERS -
+ // -------------------------------------------------
+
+ /// <summary>
+ /// Default constructor - initializes all fields to default values
+ /// </summary>
+ public FTPFileCollection()
+ {
+ m_list = new List<FTPFile>();
+ }
+
+ /// <summary>
+ /// Number of files in the collection
+ /// </summary>
+ public int Count
+ {
+ get {return m_list.Count;}
+ }
+
+ /// <summary>
+ /// Add a file to the collection
+ /// </summary>
+ public void Add(FTPFile f)
+ {
+ m_list.Add(f);
+ }
+
+ /// <summary>
+ /// Get a file from the collection
+ /// </summary>
+ public FTPFile Get(int index)
+ {
+ return (FTPFile)m_list[index];
+ }
+
+ /// <summary>
+ /// Sort the collection
+ /// </summary>
+ public void Sort()
+ {
+ m_list.Sort();
+ }
+
+ /// <summary>
+ /// Get iterator
+ /// </summary>
+ public Enum GetEnumerator()
+ {
+ return new Enum(this);
+ }
+
+ /// <summary>
+ /// Get iterator
+ /// </summary>
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+
+ /// <summary>
+ /// Class for enumeration
+ /// </summary>
+ public class Enum : IEnumerator<FTPFile>
+ {
+ /// <summary>
+ /// Default constructor - initializes all fields to default values
+ /// </summary>
+ public Enum(FTPFileCollection c)
+ {
+ m_index=-1;
+ m_col=c;
+ }
+
+ /// <summary>
+ /// See IEnumerator
+ /// </summary>
+ public bool MoveNext()
+ {
+ m_index++;
+ return m_index<m_col.Count;
+ }
+
+ /// <summary>
+ /// See IEnumerator
+ /// </summary>
+ public FTPFile Current
+ {
+ get {return m_col.Get(m_index);}
+ }
+
+ /// <summary>
+ /// See IEnumerator
+ /// </summary>
+ public void Reset()
+ {
+ m_index=-1;
+ }
+
+ /// <summary>
+ /// See IEnumerator
+ /// </summary>
+ object IEnumerator<FTPFile>.Current
+ {
+ get {return Current;}
+ }
+
+ private int m_index;
+ private FTPFileCollection m_col;
+ }
+
+ // -------------------------------------------------
+ // - PRIVATE MEMBERS -
+ // -------------------------------------------------
+
+ private List<FTPFile> m_list;
+ }
+}
diff --git a/Noddybox.WindowsPhone.FTP/NetException.cs b/Noddybox.WindowsPhone.FTP/NetException.cs
new file mode 100644
index 0000000..119c241
--- /dev/null
+++ b/Noddybox.WindowsPhone.FTP/NetException.cs
@@ -0,0 +1,84 @@
+// 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 <http://www.gnu.org/licenses/>.
+//
+// Copyright (c) 2012 Ian Cowburn
+//
+using System;
+
+namespace Noddybox.WindowsPhone.Net
+{
+ /// <summary>
+ /// Exception raised for internal problems.
+ /// </summary>
+ public class NetException : System.Exception
+ {
+ /// <summary>
+ /// The types of error that can be raised
+ /// </summary>
+ public enum Type
+ {
+ /// <summary>
+ /// A server replied with no code in the first reply line
+ /// </summary>
+ ALL_NoReplyCode,
+
+ /// <summary>
+ /// The local IP address could not be found (note that all
+ /// the routines use the first available IP when onew must
+ /// be chosen).
+ /// </summary>
+ ALL_NoLocalIP,
+
+ /// <summary>
+ /// An uncompleted feature was used
+ /// </summary>
+ ALL_NotImplemented,
+
+ /// <summary>
+ /// The FTP server does not support passive connections
+ /// </summary>
+ FTP_NoPassive,
+
+ /// <summary>
+ /// The FTP server had a problem accepting our local port
+ /// info
+ /// </summary>
+ FTP_NonPassiveProblem,
+
+ /// <summary>
+ /// Given a bad address by the server
+ /// </summary>
+ FTP_BadReplyAddress
+ };
+
+ /// <summary>
+ /// Default constructor - initializes all fields to default values
+ /// </summary>
+ public NetException(Type e)
+ {
+ m_code=e;
+ }
+
+ /// <summary>
+ /// Gets the cause of the error
+ /// </summary>
+ public Type Error
+ {
+ get {return m_code;}
+ }
+
+ private Type m_code;
+ }
+}
diff --git a/Noddybox.WindowsPhone.FTP/Noddybox.WindowsPhone.FTP.csproj b/Noddybox.WindowsPhone.FTP/Noddybox.WindowsPhone.FTP.csproj
new file mode 100644
index 0000000..0c9453b
--- /dev/null
+++ b/Noddybox.WindowsPhone.FTP/Noddybox.WindowsPhone.FTP.csproj
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProductVersion>10.0.20506</ProductVersion>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{31A42B81-8DF6-41FD-AB0A-7F90388DFB7A}</ProjectGuid>
+ <ProjectTypeGuids>{C089C8C0-30E0-4E22-80C0-CE093F111A43};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>
+ <OutputType>Library</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>Noddybox.WindowsPhone.FTP</RootNamespace>
+ <AssemblyName>Noddybox.WindowsPhone.FTP</AssemblyName>
+ <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
+ <SilverlightVersion>$(TargetFrameworkVersion)</SilverlightVersion>
+ <TargetFrameworkProfile>WindowsPhone71</TargetFrameworkProfile>
+ <TargetFrameworkIdentifier>Silverlight</TargetFrameworkIdentifier>
+ <SilverlightApplication>false</SilverlightApplication>
+ <ValidateXaml>true</ValidateXaml>
+ <ThrowErrorsInValidation>true</ThrowErrorsInValidation>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>Bin\Debug</OutputPath>
+ <DefineConstants>DEBUG;TRACE;SILVERLIGHT;WINDOWS_PHONE</DefineConstants>
+ <NoStdLib>true</NoStdLib>
+ <NoConfig>true</NoConfig>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>Bin\Release</OutputPath>
+ <DefineConstants>TRACE;SILVERLIGHT;WINDOWS_PHONE</DefineConstants>
+ <NoStdLib>true</NoStdLib>
+ <NoConfig>true</NoConfig>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="System.Windows" />
+ <Reference Include="system" />
+ <Reference Include="System.Core" />
+ <Reference Include="System.Xml" />
+ <Reference Include="System.Net" />
+ <Reference Include="mscorlib.extensions" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="FTPClient.cs" />
+ <Compile Include="FTPFile.cs" />
+ <Compile Include="FTPFileCollection.cs" />
+ <Compile Include="NetException.cs" />
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ <Compile Include="ServerDialog.cs" />
+ </ItemGroup>
+ <Import Project="$(MSBuildExtensionsPath)\Microsoft\Silverlight for Phone\$(TargetFrameworkVersion)\Microsoft.Silverlight.$(TargetFrameworkProfile).Overrides.targets" />
+ <Import Project="$(MSBuildExtensionsPath)\Microsoft\Silverlight for Phone\$(TargetFrameworkVersion)\Microsoft.Silverlight.CSharp.targets" />
+ <ProjectExtensions />
+ <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
+ Other similar extension points exist, see Microsoft.Common.targets.
+ <Target Name="BeforeBuild">
+ </Target>
+ <Target Name="AfterBuild">
+ </Target>
+ -->
+</Project> \ No newline at end of file
diff --git a/Noddybox.WindowsPhone.FTP/Properties/AssemblyInfo.cs b/Noddybox.WindowsPhone.FTP/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..158de02
--- /dev/null
+++ b/Noddybox.WindowsPhone.FTP/Properties/AssemblyInfo.cs
@@ -0,0 +1,37 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Resources;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("Noddybox.WindowsPhone.FTP")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("Noddybox.WindowsPhone.FTP")]
+[assembly: AssemblyCopyright("Copyright © 2012")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("3e489c9b-66bf-41c9-bfe5-54cdf00d2afe")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Revision and Build Numbers
+// by using the '*' as shown below:
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
+[assembly: NeutralResourcesLanguageAttribute("en-US")]
diff --git a/Noddybox.WindowsPhone.FTP/ServerDialog.cs b/Noddybox.WindowsPhone.FTP/ServerDialog.cs
new file mode 100644
index 0000000..4291afb
--- /dev/null
+++ b/Noddybox.WindowsPhone.FTP/ServerDialog.cs
@@ -0,0 +1,178 @@
+// 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 <http://www.gnu.org/licenses/>.
+//
+// Copyright (c) 2012 Ian Cowburn
+//
+using System;
+using System.Net;
+using System.Net.Sockets;
+using System.IO;
+using System.Text;
+using System.Text.RegularExpressions;
+
+namespace Noddybox.WindowsPhone.Net
+{
+ /// <summary>
+ /// Provides a wrapper around a stream to interact with servers
+ /// </summary>
+ public class ServerDialog
+ {
+ // -------------------------------------------------
+ // - PUBLIC MEMBERS -
+ // -------------------------------------------------
+
+ /// <summary>
+ /// Used for returns from the server
+ /// </summary>
+ public class Reply
+ {
+ /// <summary>
+ /// Default constructor - initializes all fields to default values
+ /// </summary>
+ public Reply()
+ {
+ m_code=-1;
+ m_text="";
+ }
+
+ /// <summary>
+ /// The code returned by the server
+ /// </summary>
+ public int Code
+ {
+ get {return m_code;}
+ set {m_code=value;}
+ }
+
+ /// <summary>
+ /// The text returned by the server
+ /// </summary>
+ public string Text
+ {
+ get {return m_text;}
+ set {m_text=value;}
+ }
+
+ private int m_code;
+ private string m_text;
+
+ }
+
+ /// <summary>
+ /// Default constructor - initializes all fields to default values
+ /// </summary>
+ public ServerDialog()
+ {
+ m_encode=new System.Text.ASCIIEncoding();
+ m_code=new Regex("^[0-9][0-9][0-9]");
+ m_code_end=new Regex("^[0-9][0-9][0-9][^-]");
+
+ }
+
+ /// <summary>
+ /// Sends a command to the server and returns the reply
+ /// </summary>
+ public Reply Send(Stream str, string command)
+ {
+ Reply r=new Reply();
+
+ if (command!=null)
+ {
+ byte[] b=ToBytes(command);
+
+ str.Write(b,0,b.Length);
+ }
+
+ GetResult(str,r);
+
+ return r;
+ }
+
+ // -------------------------------------------------
+ // - PRIVATE MEMBERS -
+ // -------------------------------------------------
+
+ private ASCIIEncoding m_encode;
+ private Regex m_code;
+ private Regex m_code_end;
+
+
+
+ private byte[] ToBytes(string s)
+ {
+ return m_encode.GetBytes(s);
+ }
+
+ private string GetLine(Stream str)
+ {
+ byte[] b=new byte[1];
+ int c;
+ string r="";
+
+ while((c=str.ReadByte())!=-1)
+ {
+ b[0]=(byte)c;
+ r+=m_encode.GetString(b);
+
+ if (c==10)
+ {
+ break;
+ }
+ }
+
+ return r;
+ }
+
+ private void GetResult(Stream str, Reply r)
+ {
+ string s;
+ string all="";
+ bool first_line=true;
+
+ do
+ {
+ s=GetLine(str);
+
+ if (first_line)
+ {
+ if (m_code.IsMatch(s))
+ {
+ r.Code=Convert.ToInt32(s.Substring(0,3));
+ }
+ else
+ {
+ throw new NetException(NetException.Type.ALL_NoReplyCode);
+ }
+
+ first_line=false;
+ }
+
+ if (m_code.IsMatch(s))
+ {
+ all+=s.Substring(4);
+ }
+ else
+ {
+ all+=s;
+ }
+
+ } while (!m_code_end.IsMatch(s));
+
+ all=all.TrimEnd(null);
+ r.Text=all;
+ }
+
+ }
+}