using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Security.Permissions;
using System.Threading;
using System.IO;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
namespace FTP
{
public class FTPFactory
{
private X509CertificateCollection _clientCertificates = new X509CertificateCollection();
private string remoteHost,remotePath,remoteUser,remotePass,mes;
private int remotePort,bytes;
private Socket clientSocket;
private int retValue;
private Boolean debug;
private Boolean logined;
private string reply;
private bool useStream;
private bool isUpload;
private Stream stream = null;
private Stream stream2 = null;
private static int BLOCK_SIZE = 1024;
Byte[] buffer = new Byte[BLOCK_SIZE];
Encoding ASCII = Encoding.Default;
public FTPFactory()
{
remoteHost = string.Empty ;
remotePath = ".";
remoteUser = string.Empty;
remotePass = string.Empty;
remotePort = 21;
debug = false;
logined = false;
}
///
/// Set the name of the FTP server to connect to.
///
/// Server name
public void setRemoteHost(string remoteHost)
{
this.remoteHost = remoteHost;
}
public void setUseStream(bool value)
{
this.useStream = value;
}
///
/// Return the name of the current FTP server.
///
/// Server name
public string getRemoteHost()
{
return remoteHost;
}
///
/// Set the port number to use for FTP.
///
/// Port number
public void setRemotePort(int remotePort)
{
this.remotePort = remotePort;
}
///
/// Return the current port number.
///
/// Current port number
public int getRemotePort()
{
return remotePort;
}
///
/// Set the remote directory path.
///
/// The remote directory path
public void setRemotePath(string remotePath)
{
this.remotePath = remotePath;
}
///
/// Return the current remote directory path.
///
/// The current remote directory path.
public string getRemotePath()
{
return remotePath;
}
///
/// Set the user name to use for logging into the remote server.
///
/// Username
public void setRemoteUser(string remoteUser)
{
this.remoteUser = remoteUser;
}
///
/// Set the password to user for logging into the remote server.
///
/// Password
public void setRemotePass(string remotePass)
{
this.remotePass = remotePass;
}
///
/// Return the size of a file.
///
///
///
public long getFileSize(string fileName)
{
if(!logined)
{
login();
}
sendCommand("SIZE " + fileName);
long size=0;
if(retValue == 213)
{
size = Int64.Parse(reply.Substring(4));
}
else
{
throw new IOException(reply.Substring(4));
}
return size;
}
public void login()
{
//SslStream sslStream = new SslStream(client.GetStream(), false);
try
{
if (clientSocket==null || clientSocket.Connected == false)
this.loginWithoutUser();
}
catch (Exception)
{
throw new IOException("Couldn't connect to remote server");
}
if (debug)
Console.WriteLine("USER " + remoteUser);
sendCommand("USER " + remoteUser);
if (!(retValue == 331 || retValue == 230))
{
cleanup();
throw new IOException(reply.Substring(4));
}
if (retValue != 230)
{
if (debug)
Console.WriteLine("PASS xxx");
sendCommand("PASS " + remotePass);
if (!(retValue == 230 || retValue == 202))
{
cleanup();
throw new IOException(reply.Substring(4));
}
}
logined = true;
Console.WriteLine("Connected to " + remoteHost);
chdir(remotePath);
}
///
/// Login to the remote server.
///
public void loginWithoutUser()
{
// SslStream sslStream = new SslStream(client.GetStream(), false);
clientSocket = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
IPEndPoint ep = new IPEndPoint(Dns.Resolve(remoteHost).AddressList[0], remotePort);
try
{
clientSocket.Connect(ep);
getSslStream();
useStream = true;
//InitStreams();
}
catch(Exception)
{
throw new IOException("Couldn't connect to remote server");
}
readReply();
if(retValue != 220)
{
close();
throw new IOException(reply.Substring(4));
}
}
private static bool OnCertificateValidation(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors)
{
/* Console.WriteLine("Server Certificate Issued To: {0}", certificate.GetName());
Console.WriteLine("Server Certificate Issued By: {0}", certificate.GetIssuerName());
// Return true if there are no policy errors
// The certificate can also be manually verified to
//make sure it meets your specific // policies by
// interrogating the x509Certificate object.
if (errors != SslPolicyErrors.None)
{
Console.WriteLine("Server Certificate Validation Error");
Console.WriteLine(errors.ToString());
return false;
}
else
{
Console.WriteLine("No Certificate Validation Errors");
return true;
}*/
return true; //所有證書都合法
}
private void showCertificateInfo(X509Certificate remoteCertificate, bool verbose)
{
Console.WriteLine("Certficate Information for:/n{0}/n", remoteCertificate.GetName());
Console.WriteLine("Valid From: /n{0}", remoteCertificate.GetEffectiveDateString());
Console.WriteLine("Valid To: /n{0}", remoteCertificate.GetExpirationDateString());
Console.WriteLine("Certificate Format: /n{0}/n", remoteCertificate.GetFormat());
Console.WriteLine("Issuer Name: /n{0}", remoteCertificate.GetIssuerName());
if (verbose)
{
Console.WriteLine("Serial Number: /n{0}", remoteCertificate.GetSerialNumberString());
Console.WriteLine("Hash: /n{0}", remoteCertificate.GetCertHashString());
Console.WriteLine("Key Algorithm: /n{0}", remoteCertificate.GetKeyAlgorithm());
Console.WriteLine("Key Algorithm Parameters: /n{0}", remoteCertificate.GetKeyAlgorithmParametersString());
Console.WriteLine("Public Key: /n{0}", remoteCertificate.GetPublicKeyString());
}
}
private void showSslInfo(string serverName, SslStream sslStream, bool verbose)
{
showCertificateInfo(sslStream.RemoteCertificate, verbose);
Console.WriteLine("/n/nSSL Connect Report for : {0}/n", serverName);
Console.WriteLine("Is Authenticated: {0}", sslStream.IsAuthenticated);
Console.WriteLine("Is Encrypted: {0}", sslStream.IsEncrypted);
Console.WriteLine("Is Signed: {0}", sslStream.IsSigned);
Console.WriteLine("Is Mutually Authenticated: {0}/n", sslStream.IsMutuallyAuthenticated);
Console.WriteLine("Hash Algorithm: {0}", sslStream.HashAlgorithm);
Console.WriteLine("Hash Strength: {0}", sslStream.HashStrength);
Console.WriteLine("Cipher Algorithm: {0}", sslStream.CipherAlgorithm);
Console.WriteLine("Cipher Strength: {0}/n", sslStream.CipherStrength);
Console.WriteLine("Key Exchange Algorithm: {0}", sslStream.KeyExchangeAlgorithm);
Console.WriteLine("Key Exchange Strength: {0}/n", sslStream.KeyExchangeStrength);
Console.WriteLine("SSL Protocol: {0}", sslStream.SslProtocol);
}
public void getSslStream()
{
this.getSslStream(clientSocket);
}
public void getSslStream(Socket Csocket)
{
//SslStream sslStream = new SslStream(client.GetStream(), false);
//RemoteCertificateValidationCallback callback = new RemoteCertificateValidationCallback(OnCertificateValidation);
//SslStream _sslStream = new SslStream(new NetworkStream(Csocket));//,new RemoteCertificateValidationCallback(ValidateServerCertificate), null);
ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(OnCertificateValidation);
SslStream _sslStream = new SslStream(new NetworkStream(Csocket), false, new RemoteCertificateValidationCallback(OnCertificateValidation));
try
{
_sslStream.AuthenticateAsClient(
remoteHost,
_clientCertificates,
System.Security.Authentication.SslProtocols.Ssl3 | System.Security.Authentication.SslProtocols.Tls,
true);
if (_sslStream.IsAuthenticated)
if (isUpload)
stream2 = _sslStream;
else
stream = _sslStream;
}
catch (Exception ex)
{
throw new IOException(ex.StackTrace);
}
showSslInfo(remoteHost, _sslStream, true);
// readReply();
}
///
/// If the value of mode is true, set binary mode for downloads.
/// Else, set Ascii mode.
///
///
public void setBinaryMode(Boolean mode)
{
if(mode)
{
sendCommand("TYPE I");
}
else
{
sendCommand("TYPE A");
}
if (retValue != 200)
{
throw new IOException(reply.Substring(4));
}
}
///
/// Upload a file.
///
///
///
/// Secure Upload a file and set the resume flag.
///
///
///
public void uploadSecure(string fileName, Boolean resume)
{
sendCommand("PBSZ 0");
sendCommand("PROT P");
/* sendCommand("PASV");
if (retValue != 227)
{
throw new IOException(reply.Substring(4));
}*/
if (!logined)
{
login();
}
Socket cSocket = createDataSocket();
long offset = 0;
if (resume)
{
try
{
setBinaryMode(true);
offset = getFileSize(fileName);
}
catch (Exception)
{
offset = 0;
}
}
if (offset > 0)
{
sendCommand("REST " + offset);
if (retValue != 350)
{
//throw new IOException(reply.Substring(4));
//Remote server may not support resuming.
offset = 0;
}
}
sendCommand("STOR " + Path.GetFileName(fileName)); //必需先進行傳輸通道的打開再進行sslstream的連接
if (!(retValue == 125 || retValue == 150))
{
throw new IOException(reply.Substring(4));
}
isUpload=true;
this.getSslStream(cSocket);
FileStream input = File.OpenRead(fileName);
byte[] bufferFile = new byte[input.Length];
input.Read(bufferFile, 0, bufferFile.Length);
input.Close();
if (offset != 0)
{
if (debug)
{
Console.WriteLine("seeking to " + offset);
}
input.Seek(offset, SeekOrigin.Begin);
}
Console.WriteLine("Uploading file " + fileName + " to " + remotePath);
if (cSocket.Connected)
{
this.stream2.Write(bufferFile, 0, bufferFile.Length);
Console.WriteLine("File Upload");
}
this.stream2.Close();
if (cSocket.Connected)
{
cSocket.Close();
}
readReply();
if (!(retValue == 226 || retValue == 250))
{
throw new IOException(reply.Substring(4));
}
}
///
/// Delete a file from the remote FTP server.
///
///
public void deleteRemoteFile(string fileName)
{
if(!logined)
{
login();
}
sendCommand("DELE "+fileName);
if(retValue != 250)
{
throw new IOException(reply.Substring(4));
}
}
///
/// Rename a file on the remote FTP server.
///
///
///
public void renameRemoteFile(string oldFileName,string newFileName)
{
if(!logined)
{
login();
}
sendCommand("RNFR "+oldFileName);
if(retValue != 350)
{
throw new IOException(reply.Substring(4));
}
// known problem
// rnto will not take care of existing file.
// i.e. It will overwrite if newFileName exist
sendCommand("RNTO "+newFileName);
if(retValue != 250)
{
throw new IOException(reply.Substring(4));
}
}
///
/// Create a directory on the remote FTP server.
///
///
public void mkdir(string dirName)
{
if(!logined)
{
login();
}
sendCommand("MKD "+dirName);
if(retValue != 257)
{
throw new IOException(reply.Substring(4));
}
}
///
/// Delete a directory on the remote FTP server.
///
///
public void rmdir(string dirName)
{
if(!logined)
{
login();
}
sendCommand("RMD "+dirName);
if(retValue != 250)
{
throw new IOException(reply.Substring(4));
}
}
///
/// Change the current working directory on the remote FTP server.
///
///
public void chdir(string dirName)
{
if(dirName.Equals("."))
{
return;
}
if(!logined)
{
login();
}
sendCommand("CWD "+dirName);
if(retValue != 250)
{
throw new IOException(reply.Substring(4));
}
this.remotePath = dirName;
Console.WriteLine("Current directory is "+remotePath);
}
///
/// Close the FTP connection.
///
public void close()
{
if( clientSocket != null )
{
sendCommand("QUIT");
}
cleanup();
Console.WriteLine("Closing...");
}
///
/// Set debug mode.
///
///
public void setDebug(Boolean debug)
{
this.debug = debug;
}
private void readReply()
{
if (useStream)
reply=ResponseMsg();
else
{
mes = "";
reply = readLine();
retValue = Int32.Parse(reply.Substring(0, 3));
}
}
private void cleanup()
{
if(clientSocket!=null)
{
clientSocket.Close();
clientSocket = null;
}
logined = false;
}
private string readLine()
{
string line = null;
while(true)
{
if (useStream)
bytes = stream.Read(buffer, buffer.Length, 0);
else
bytes = clientSocket.Receive(buffer, buffer.Length, 0);
//line = reader.ReadLine();
/*mes = line;
if (mes.Length > 0)
break;*/
mes += ASCII.GetString(buffer, 0, bytes);
if(bytes < buffer.Length)
{
break;
}
}
char[] seperator = {'/n'};
string[] mess = mes.Split(seperator);
if(mes.Length > 2)
{
mes = mess[mess.Length-2];
}
else
{
mes = mess[0];
}
if(!mes.Substring(3,1).Equals(" "))
{
return readLine();
}
if(debug)
{
for(int k=0;k < mess.Length-1;k++)
{
Console.WriteLine(mess[k]);
}
}
return mes;
}
private void WriteMsg(string message)
{
Encoding en = Encoding.Default; //解決中文不能識別問題
//System.Text.UTF32Encoding en = new UTF32Encoding();
// System.Text.UTF8Encoding en = new UTF8Encoding();
//System.Text.ASCIIEncoding en = new System.Text.ASCIIEncoding();
byte[] WriteBuffer = new byte[1024];
WriteBuffer = en.GetBytes(message);
stream.Write(WriteBuffer, 0, WriteBuffer.Length);
Console.WriteLine(" WRITE:" + message);
}
private string ResponseMsg()
{
Encoding enc = Encoding.Default; //解決中文不能識別問題
//System.Text.UTF8Encoding enc = new UTF8Encoding();
//System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
byte[] serverbuff = new Byte[1024];
int count = 0;
while (true)
{
byte[] buff = new Byte[2];
int bytes = stream.Read(buff, 0, 1);
if (bytes == 1)
{
serverbuff[count] = buff[0];
count++;
if (buff[0] == '/n')
{
break;
}
}
else
{
break;
};
};
string retval = enc.GetString(serverbuff, 0, count);
Console.WriteLine(" READ:" + retval);
retValue = Int32.Parse(retval.Substring(0, 3));
return retval;
}
public void sendCommand(String command )
{
Byte[] cmdBytes = Encoding.ASCII.GetBytes((command+"/r/n").ToCharArray());
if (useStream)
{
WriteMsg(command + "/r/n");
//WriteMsg(command);
}
else
clientSocket.Send(cmdBytes, cmdBytes.Length, 0);
//writer.Write(command);
readReply();
}
public Socket createDataSocket()
{
sendCommand("PASV");
if (retValue != 227)
{
throw new IOException(reply.Substring(4));
}
int index1 = reply.IndexOf('(');
int index2 = reply.IndexOf(')');
string ipData = reply.Substring(index1+1,index2-index1-1);
int[] parts = new int[6];
int len = ipData.Length;
int partCount = 0;
string buf="";
for (int i = 0; i < len && partCount <= 6; i++)
{
char ch = Char.Parse(ipData.Substring(i,1));
if (Char.IsDigit(ch))
buf+=ch;
else if (ch != ',')
{
throw new IOException("Malformed PASV reply: " + reply);
}
if (ch == ',' || i+1 == len)
{
try
{
parts[partCount++] = Int32.Parse(buf);
buf="";
}
catch (Exception)
{
throw new IOException("Malformed PASV reply: " + reply);
}
}
}
string ipAddress = parts[0] + "."+ parts[1]+ "." +
parts[2] + "." + parts[3];
int port = (parts[4] << 8) + parts[5];
//int port = 21;
Socket s = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
IPEndPoint ep = new IPEndPoint(Dns.Resolve(ipAddress).AddressList[0], port);
try
{
s.Connect(ep);
}
catch(Exception )
{
throw new IOException("Can't connect to remoteserver");
}
return s;
}
}
}
以下是測試代碼:
static void Main(string[] args)
{
try
{
FTPFactory ftp = new FTPFactory();
ftp.setDebug(true);
ftp.setRemoteHost(Settings.Default.TargetFtpSource);
//Connect to SSL Port (990)
ftp.setRemotePort(990);
ftp.loginWithoutUser();
// string cmd = "AUTH SSL";
// ftp.sendCommand(cmd);
//Create SSL Stream
//ftp.getSslStream();
//ftp.setUseStream(true);
//Login FTP Secure
ftp.setRemoteUser(Settings.Default.TargetFtpSecureUser);
ftp.setRemotePass(Settings.Default.TargetFtpSecurePass);
ftp.login();
//Set ASCII Mode
ftp.setBinaryMode(true);
ftp.uploadSecure(@"C:/BLOB34_do.pdf", false);
ftp.close();
Console.Read();
}
catch (Exception e)
{
Console.WriteLine("Caught Error :" + e.Message);
Console.Read();
}
}