using System;
using System.Net;
using System.IO;
using System.Text;
using System.Net.Sockets;
using System.Diagnostics;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Messaging;
namespace SharpDownload{
/// <summary>
/// Abstract protocol client class
/// </summary>
public abstract class ProtocolClient
{
/// <summary>
/// Protocol exception class
/// </summary>
public class ProtocolException : Exception
{
public ProtocolException(string message) : base(message){}
public ProtocolException(string message, Exception innerException) : base(message,innerException){}
}
public class ProtocolCommandException : ApplicationException
{
private FileDownload.FileDownloadCommandEnum command = FileDownload.FileDownloadCommandEnum.NoCommand;
public ProtocolCommandException(FileDownload.FileDownloadCommandEnum Command)
{
command = Command;
}
public FileDownload.FileDownloadCommandEnum Command
{
get
{
return command;
}
}
}
public enum DownloadResult
{
Error,
Paused,
Cancelled,
Finished
}
protected static int BUFFER_SIZE = 512;
protected static Encoding ASCII = Encoding.ASCII;
protected bool verboseDebugging = false;
// defaults
protected string server = string.Empty;
protected string remotePath = string.Empty;
protected string username = string.Empty;
protected string password = string.Empty;
protected string message = null;
protected string result = null;
protected string[] fullMessage = null;
protected int port = 0;
protected int bytes = 0;
protected int resultCode = 0;
protected bool loggedin = false;
protected bool binMode = false;
protected Byte[] buffer = new Byte[BUFFER_SIZE];
protected Socket clientSocket = null;
protected int timeoutSeconds = 60;
private string MODULE_NAME = "ProtocolClient";
/// <summary>
/// Default constructor
/// </summary>
public ProtocolClient()
{
}
/// <summary>
/// Overloaded constructor
/// </summary>
/// <param name="server"></param>
/// <param name="username"></param>
/// <param name="password"></param>
public ProtocolClient(string server, string username, string password)
{
this.Server = server;
this.Username = username;
this.Password = password;
}
/// <summary>
/// Overloaded constructor
/// </summary>
/// <param name="server"></param>
/// <param name="username"></param>
/// <param name="password"></param>
/// <param name="timeoutSeconds"></param>
/// <param name="port"></param>
public ProtocolClient(string server, string username, string password, int timeoutSeconds, int port)
{
this.server = server;
this.username = username;
this.password = password;
this.timeoutSeconds = timeoutSeconds;
this.port = port;
}
/// <summary>
/// Simple factory patter
/// Returns the appropriate protocol client dependent on the protocol passed
/// </summary>
/// <param name="protocol">type of protocol client to get</param>
/// <returns>protocol client sub-class</returns>
public static ProtocolClient GetProtocolClient(string protocol)
{
switch (protocol)
{
case "ftp://":
return new FtpClient();
case "http://":
return new HttpClient();
default:
throw new ApplicationException("Unknown protocol: " + protocol + ".");
}
}
/// <summary>
/// Return a new protocol client
/// </summary>
/// <param name="protocol">url to check</param>
/// <param name="downloadInfo">url to check</param>
/// <returns>ProtocolClient sub-class</returns>
public static ProtocolClient GetProtocolClient(string protocol, DownloadInfo downloadInfo)
{
ProtocolClient client = ProtocolClient.GetProtocolClient(protocol);
client.Server = downloadInfo.ServerAddress;
client.Port = downloadInfo.Port;
return client;
}
/// <summary>
/// Return a new protocol client
/// </summary>
/// <param name="downloadInfo">Download info</param>
/// <returns>ProtocolClient sub-class</returns>
public static ProtocolClient GetProtocolClient(DownloadInfo downloadInfo)
{
ProtocolClient client = ProtocolClient.GetProtocolClient(downloadInfo.Protocol);
client.Server = downloadInfo.ServerAddress;
client.Port = downloadInfo.Port;
client.Username = downloadInfo.UserName;
client.Password = downloadInfo.Password;
return client;
}
/// <summary>
/// Display all communications to the debug log
/// </summary>
public bool VerboseDebugging
{
get
{
return this.verboseDebugging;
}
set
{
this.verboseDebugging = value;
}
}
/// <summary>
/// Remote server port.
/// </summary>
public int Port
{
get
{
return this.port;
}
set
{
this.port = value;
}
}
/// <summary>
/// Timeout waiting for a response from server, in seconds.
/// </summary>
public int Timeout
{
get
{
return this.timeoutSeconds;
}
set
{
this.timeoutSeconds = value;
}
}
/// <summary>
/// Gets and Sets the name of the server.
/// </summary>
/// <returns></returns>
public string Server
{
get
{
return this.server;
}
set
{
this.server = value;
}
}
/// <summary>
/// Gets and Sets the port number.
/// </summary>
public int RemotePort
{
get
{
return this.port;
}
set
{
this.port = value;
}
}
/// <summary>
/// GetS and Sets the remote directory.
/// </summary>
public string RemotePath
{
get
{
return this.remotePath;
}
set
{
this.remotePath = value;
}
}
/// <summary>
/// Gets and Sets the username.
/// </summary>
public string Username
{
get
{
return this.username;
}
set
{
this.username = value;
}
}
/// <summary>
/// Gets and Set the password.
/// </summary>
public string Password
{
get
{
return this.password;
}
set
{
this.password = value;
}
}
/// <summary>
/// Return whether session is logged in
/// </summary>
public bool IsLoggedIn
{
get
{
return loggedin;
}
}
/// <summary>
/// Copy a larger array's elements to a smaller array.
/// Elements out of the smaller arrays bounds are ignored.
/// </summary>
/// <param name="bigArray">The larger array</param>
/// <param name="elementsToCopy">Number of elements to copy to the smaller array</param>
/// <returns>Smaller array</returns>
protected byte[] CopyToSmallArray(byte[] bigArray, int elementsToCopy)
{
Byte[] smallArray = new Byte[elementsToCopy-1];
for (int i=0;i<elementsToCopy-1;i++)
{
smallArray[i] = bigArray[i];
}
return smallArray;
}
/// <summary>
/// Copy a larger array's elements to a smaller array.
/// Elements out of the smaller arrays bounds are ignored.
/// </summary>
/// <param name="bigArray">The larger array</param>
/// <param name="startIndex">Number of elements to copy to the smaller array</param>
/// <param name="elementsToCopy">Number of elements to copy to the smaller array</param>
/// <returns>Smaller array</returns>
protected byte[] CopyToSmallArray(byte[] bigArray, int elementsToCopy, int startIndex)
{
Byte[] smallArray = new Byte[elementsToCopy];
for (int i=startIndex;i<bigArray.Length;i++)
{
smallArray[i-startIndex] = bigArray[i];
}
return smallArray;
}
/// <summary>
/// Overloaded function - creates a tcp data socket for the data download
/// </summary>
/// <returns>Connected socket</returns>
protected Socket createDataSocket()
{
return createDataSocket(this.Server, this.Port);
}
/// <summary>
/// Overloaded function - creates a tcp data socket for the data download
/// </summary>
/// <returns>Connected socket</returns>
protected Socket createDataSocket(string ipAddress, int port)
{
Socket socket = null;
IPHostEntry iphe = null;
// Try and open a connection to the server
try
{
iphe = Dns.Resolve(ipAddress);
// Loop through the AddressList to obtain the supported AddressFamily. This is to avoid
// an exception to be thrown if the host IP Address is not compatible with the address family
// (typical in the IPv6 case).
foreach(IPAddress ipad in iphe.AddressList)
{
IPEndPoint ipe = new IPEndPoint(ipad, port);
Socket tmpS =
new Socket(ipe.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
tmpS.Connect(ipe);
if(tmpS.Connected)
{
socket = tmpS;
break;
}
else
continue;
}
}
catch(Exception ex)
{
// doubtful
if ( this.clientSocket != null && this.clientSocket.Connected ) this.clientSocket.Close();
throw new ProtocolException("Couldn't connect to remote server",ex);
}
// Check if we have a valid connection
if (socket==null)
{
throw new ProtocolException("Couldn't connect to remote server");
}
Debug.WriteLine("Connected to " + this.server, MODULE_NAME );
return socket;
}
/// <summary>
/// Gets a file stream to write downloaded output to.
/// </summary>
/// <param name="locFileName">local file name to create</param>
/// <param name="remFileName">remote file name, used for local file name when no local name provided</param>
/// <returns>open FileStream object</returns>
protected FileStream GetLocalFileStream(string locFileName, string remFileName)
{
// Check if the local filename was passed
if (locFileName.Equals(""))
{
// Set local filename to name of remote file
locFileName = remFileName;
}
// Check if the file already exists
FileStream output = null;
if ( !File.Exists(locFileName) )
{
// Create a new file
output = File.Create(locFileName);
}
else
{
// Append output to the existing file
output = new FileStream(locFileName, FileMode.Open);
}
return output;
}
/// <summary>
/// Resets the download buffer
/// </summary>
public void ResetBuffer()
{
this.buffer = new Byte[BUFFER_SIZE];
}
public abstract FileDownload.FileDownloadCommandEnum Command
{
get;
set;
}
public abstract bool CanResume();
public abstract bool CanResume(string remFileName);
public abstract void Close();
public abstract void Login();
public abstract long GetFileSize(string remFileName);
public abstract void StopDownload();
public abstract DownloadResult Download(string remFileName);
public abstract DownloadResult Download(string remFileName, bool resume);
public abstract DownloadResult Download(string remFileName, string locFileName);
public abstract DownloadResult Download(string remFileName, string locFileName, bool resume);
public abstract DownloadResult Download(string remFileName, string locFileName, bool resume, long byteOffSet);
public abstract DownloadResult Download(string remFileName, string locFileName, bool resume, long byteOffSet, long byteStopDownload);
public abstract DownloadResult Download(string remFileName, string locFileName, bool resume, long byteOffSet, long byteStopDownload, long totalFileSize);
public abstract DownloadResult Download(string remFileName, string locFileName, bool resume, long byteOffSet, long byteStopDownload, long totalFileSize, bool isLastPart);
#region DownloadStarted Event
/// <summary>
/// Download started event arguments
/// </summary>
public class DownloadStartedEventArgs : EventArgs
{
public DownloadStartedEventArgs ()
{
}
}
/// <summary>
/// Delegate for Download Started Event
/// </summary>
public delegate void DownloadStartedEventHandler(ProtocolClient sender, DownloadStartedEventArgs e);
/// <summary>
/// The Download Started Event is raised when the download starts
/// </summary>
public event DownloadStartedEventHandler DownloadStartedEvent;
/// <summary>
/// OnDownloadStartedEvent
/// </summary>
/// <param name="e">DownloadStartedEventArgs</param>
protected virtual void OnDownloadStartedEvent(DownloadStartedEventArgs e)
{
if (DownloadStartedEvent != null)
{
DownloadStartedEvent(this, e);
}
}
#endregion
#region DataReceived Event
/// <summary>
/// Data received event arguments
/// </summary>
public class DataReceivedEventArgs : EventArgs
{
private readonly long totalDataReceived;
/// <summary>
/// Default constructor
/// </summary>
/// <param name="totalData">Total amount of data in bytes received</param>
public DataReceivedEventArgs (long totalData)
{
this.totalDataReceived = totalData;
}
/// <summary>
/// Read-only param to get the total number of bytes received
/// </summary>
public long TotalDataReceived
{
get
{
return totalDataReceived;
}
}
}
/// <summary>
/// Delegate for Data Received Event
/// </summary>
public delegate void DataReceivedEventHandler(ProtocolClient sender, DataReceivedEventArgs e);
/// <summary>
/// The Data Received Event is raised when data is received in the download module.
/// </summary>
public event DataReceivedEventHandler DataReceivedEvent;
/// <summary>
/// OnDataReceivedEvent
/// </summary>
/// <param name="e">DataReceivedEventArgs</param>
protected virtual void OnDataReceivedEvent(DataReceivedEventArgs e)
{
if (DataReceivedEvent != null)
{
DataReceivedEvent(this, e);
}
}
#endregion
#region DownloadStopped Event
/// <summary>
/// Data received event arguments
/// </summary>
public class DownloadStoppedEventArgs : EventArgs
{
private readonly long totalDownloadStopped;
/// <summary>
/// Default constructor
/// </summary>
/// <param name="totalData">Total amount of data in bytes received</param>
public DownloadStoppedEventArgs (long totalData)
{
this.totalDownloadStopped = totalData;
}
/// <summary>
/// Read-only param to get the total number of bytes received
/// </summary>
public long TotalDownloadStopped
{
get
{
return totalDownloadStopped;
}
}
}
/// <summary>
/// Delegate for Data Received Event
/// </summary>
public delegate void DownloadStoppedEventHandler(ProtocolClient sender, DownloadStoppedEventArgs e);
/// <summary>
/// The Data Received Event is raised when data is received in the download module.
/// </summary>
public event DownloadStoppedEventHandler DownloadStoppedEvent;
/// <summary>
/// OnDownloadStoppedEvent
/// </summary>
/// <param name="e">DownloadStoppedEventArgs</param>
protected virtual void OnDownloadStoppedEvent(DownloadStoppedEventArgs e)
{
if (DownloadStoppedEvent != null)
{
DownloadStoppedEvent(this, e);
}
}
#endregion
#region ResumeSupported Event
/// <summary>
/// Resume supported event arguments
/// </summary>
public class ResumeSupportedEventArgs : EventArgs
{
private readonly bool isResumeSupported = false;
/// <summary>
/// Default constructor
/// </summary>
/// <param name="resumeSupported">Is resume supported by the server</param>
public ResumeSupportedEventArgs (bool resumeSupported)
{
this.isResumeSupported = resumeSupported;
}
/// <summary>
/// Read-only param to return whether resume is supported
/// </summary>
public bool IsResumeSupported
{
get
{
return isResumeSupported;
}
}
}
/// <summary>
/// Delegate for Resume Supported event
/// </summary>
public delegate void ResumeSupportedEventHandler(ProtocolClient sender, ResumeSupportedEventArgs e);
/// <summary>
/// The Resume Supported Event is raised when the connection discovers if resume is supported by the server
/// </summary>
public event ResumeSupportedEventHandler ResumeSupportedEvent;
/// <summary>
/// OnResumeSupportedEvent
/// </summary>
/// <param name="e">ResumeSupportedEventArgs</param>
protected virtual void OnResumeSupportedEvent(ResumeSupportedEventArgs e)
{
if (ResumeSupportedEvent != null)
{
ResumeSupportedEvent(this, e);
}
}
#endregion
#region Async methods
private delegate void LoginCallback();
/// <summary>
/// Asynchronous begin login method
/// </summary>
/// <param name="callback"></param>
/// <returns></returns>
public System.IAsyncResult BeginLogin( System.AsyncCallback callback )
{
LoginCallback protocolCallback = new LoginCallback( this.Login);
return protocolCallback.BeginInvoke(callback, null);
}
private delegate void CloseCallback();
/// <summary>
/// Asynchronous begin close method
/// </summary>
/// <param name="callback"></param>
/// <returns></returns>
public System.IAsyncResult BeginClose( System.AsyncCallback callback )
{
CloseCallback protocolCallback = new CloseCallback( this.Close);
return protocolCallback.BeginInvoke(callback, null);
}
private delegate Int64 GetFileSizeCallback(String fileName);
/// <summary>
/// Asynchronous get filesize method
/// </summary>
/// <param name="fileName"></param>
/// <param name="callback"></param>
/// <returns></returns>
public System.IAsyncResult BeginGetFileSize( String fileName, System.AsyncCallback callback )
{
GetFileSizeCallback protocolCallback = new GetFileSizeCallback(this.GetFileSize);
return protocolCallback.BeginInvoke(fileName, callback, null);
}
protected delegate DownloadResult DownloadCallback(String remFileName);
/// <summary>
/// Asynchronous begin download method
/// </summary>
/// <param name="remFileName"></param>
/// <param name="callback"></param>
/// <returns></returns>
public System.IAsyncResult BeginDownload( String remFileName, System.AsyncCallback callback )
{
DownloadCallback protocolCallback = new DownloadCallback(this.Download);
return protocolCallback.BeginInvoke(remFileName, callback, null);
}
protected delegate DownloadResult DownloadFileNameResumeCallback(String remFileName,Boolean resume);
/// <summary>
/// Asynchronous begin download method
/// </summary>
/// <param name="remFileName"></param>
/// <param name="resume"></param>
/// <param name="callback"></param>
/// <returns></returns>
public System.IAsyncResult BeginDownload( String remFileName,Boolean resume, System.AsyncCallback callback )
{
DownloadFileNameResumeCallback protocolCallback = new DownloadFileNameResumeCallback(this.Download);
return protocolCallback.BeginInvoke(remFileName, resume, callback, null);
}
protected delegate DownloadResult DownloadFileNameFileNameCallback(String remFileName,String locFileName);
/// <summary>
/// Asynchronous begin download method
/// </summary>
/// <param name="remFileName"></param>
/// <param name="locFileName"></param>
/// <param name="callback"></param>
/// <returns></returns>
public System.IAsyncResult BeginDownload( String remFileName,String locFileName, System.AsyncCallback callback )
{
DownloadFileNameFileNameCallback protocolCallback = new DownloadFileNameFileNameCallback(this.Download);
return protocolCallback.BeginInvoke(remFileName, locFileName, callback, null);
}
protected delegate DownloadResult DownloadFileNameFileNameResumeCallback(String remFileName,String locFileName,Boolean resume);
/// <summary>
/// Asynchronous begin download method
/// </summary>
/// <param name="remFileName"></param>
/// <param name="locFileName"></param>
/// <param name="resume"></param>
/// <param name="callback"></param>
/// <returns></returns>
public System.IAsyncResult BeginDownload( String remFileName,String locFileName,Boolean resume, System.AsyncCallback callback )
{
DownloadFileNameFileNameResumeCallback protocolCallback = new DownloadFileNameFileNameResumeCallback(this.Download);
return protocolCallback.BeginInvoke(remFileName, locFileName, resume, callback, null);
}
protected delegate DownloadResult DownloadFileNameFileNameResumeOffsetStopAtCallback(string remFileName, string locFileName, Boolean resume, long byteOffset, long byteStopDownload);
/// <summary>
/// Asynchronous begin download method
/// </summary>
/// <param name="remFileName"></param>
/// <param name="locFileName"></param>
/// <param name="resume"></param>
/// <param name="byteOffset"></param>
/// <param name="byteStopDownload"></param>
/// <param name="callback"></param>
/// <returns></returns>
public System.IAsyncResult BeginDownload( String remFileName, String locFileName, Boolean resume, long byteOffset, long byteStopDownload, System.AsyncCallback callback )
{
DownloadFileNameFileNameResumeOffsetStopAtCallback protocolCallback = new DownloadFileNameFileNameResumeOffsetStopAtCallback(this.Download);
return protocolCallback.BeginInvoke(remFileName, locFileName, resume, byteOffset, byteStopDownload, callback, null);
}
protected delegate DownloadResult DownloadFileNameFileNameResumeOffsetStopAtFileSizeCallback(string remFileName, string locFileName, Boolean resume, long byteOffset, long byteStopDownload, long totalFileSize);
/// <summary>
/// Asynchronous begin download method
/// </summary>
/// <param name="remFileName"></param>
/// <param name="locFileName"></param>
/// <param name="resume"></param>
/// <param name="byteOffset"></param>
/// <param name="byteStopDownload"></param>
/// <param name="isLastPart"></param>
/// <param name="callback"></param>
/// <returns></returns>
public System.IAsyncResult BeginDownload( String remFileName, String locFileName, Boolean resume, long byteOffset, long byteStopDownload, long totalFileSize, System.AsyncCallback callback )
{
DownloadFileNameFileNameResumeOffsetStopAtFileSizeCallback protocolCallback = new DownloadFileNameFileNameResumeOffsetStopAtFileSizeCallback(this.Download);
return protocolCallback.BeginInvoke(remFileName, locFileName, resume, byteOffset, byteStopDownload, totalFileSize, callback, null);
}
public delegate DownloadResult DownloadFileNameFileNameResumeOffsetStopAtFileSizeLastPartCallback(string remFileName, string locFileName, Boolean resume, long byteOffset, long byteStopDownload, long totalFileSize, bool isLastPart);
/// <summary>
/// Asynchronous begin download method
/// </summary>
/// <param name="remFileName"></param>
/// <param name="locFileName"></param>
/// <param name="resume"></param>
/// <param name="byteOffset"></param>
/// <param name="byteStopDownload"></param>
/// <param name="totalFileSize"></param>
/// <param name="isLastPart"></param>
/// <param name="callback"></param>
/// <returns></returns>
public System.IAsyncResult BeginDownload( String remFileName, String locFileName, Boolean resume, long byteOffset, long byteStopDownload, long totalFileSize, bool isLastPart, System.AsyncCallback callback )
{
DownloadFileNameFileNameResumeOffsetStopAtFileSizeLastPartCallback protocolCallback = new DownloadFileNameFileNameResumeOffsetStopAtFileSizeLastPartCallback(this.Download);
return protocolCallback.BeginInvoke(remFileName, locFileName, resume, byteOffset, byteStopDownload, totalFileSize, isLastPart, callback, null);
}
#endregion
}
}
|