Send file FTP via .Net Sockets

Here is a Class I found on the Net.


Imports System 
Imports System.Net 
Imports System.Net.Sockets 
Imports System.Text 
Imports System.IO 
Imports System.Security.Cryptography 
Imports Microsoft.VisualBasic 

'/// <summary>Implements a class that can download file over an FTP connection.</summary> 
'/// <remarks>In the current implementation, the DownloadLength property will always be negative.</remarks> 
Public Class FtpDownloader 
Inherits FileDownloader 
    '/// <summary>Constructs a new FtpDownloader object and initializes the port to 21.</summary> 
    Public Sub New() 
        Port = 21 
    End Sub 
    '/// <summary>Starts downloading a file.</summary> 
    '/// <exceptions cref="DownloadException">Thrown when an error occured while downloading the file.</exceptions> 
    Public Overrides Sub FetchFile() 
        If IsConnected Then Throw New DownloadException("This object is already downloading something.") 
        Dim Buffer(1023) as Byte 
        Dim Ret as Integer 
        'Initialize the class 
        m_DownloadLength = -1 
        'Initialize the things we're going to use 
        ClearBuffer() 
        OpenFile() 
        'Connect to the server 
        Connect() 
        Logon() 
        'Download the file 
        SendCommand("TYPE I") 
        If LastResponseType = 4 OrElse LastResponseType = 5 Then 
            Disconnect() 
            Throw New DownloadException("Server rejected binary transfer.") 
        End If 
        'Create a new data socket 
        DataSocket = New Socket(AddressFamily.Unspecified, SocketType.Stream, ProtocolType.Tcp) 
        If Passive Then 
            'Get the server's IP address and port 
            SendCommand("PASV") 
            If LastResponseType = 4 OrElse LastResponseType = 5 Then     
                Disconnect() 
                Throw New DownloadException("Server rejected passive request.") 
            End If 
            'Try to connect to that 
            Try 
                DataSocket.Connect(ParsePassiveReply(LastResponse)) 
            Catch 
                Disconnect() 
                Throw New DownloadException("Cannot connect to specified endpoint.") 
            End Try 
        Else 
            'Let's listen on a local port 
            DataSocket.Bind(New IPEndPoint(Dns.Resolve(Dns.GetHostName()).AddressList(0), 0)) 
            DataSocket.Listen(10) 
            Dim LocalEP as IPEndPoint = CType(DataSocket.LocalEndPoint, IPEndPoint) 
            'Let the server know which port we're listening on 
            SendCommand("PORT " + CType(LocalEP, IPEndPoint).Address.ToString().Replace(".", ",") + "," + Math.Floor(LocalEP.Port / 256).ToString + "," + (LocalEP.Port Mod 256).ToString) 
            If LastResponseType = 4 OrElse LastResponseType = 5 Then 
                Disconnect() 
                DataSocket.Close() 
                Throw New DownloadException("Server rejected PORT command.") 
            End If 
        End If 
        'Resume 
        If UseResume AndAlso ResumeFrom > 0 Then 
            SendCommand("REST " + ResumeFrom.ToString) 
            If LastResponseType = 4 OrElse LastResponseType = 5 Then 
                Disconnect() 
                DataSocket.Close() 
                Throw New DownloadException("Server doesn't support resume.") 
            End If 
        End If 
        'Ask the server to send us the file 
        SendCommand("RETR " + RequestedFile) 
        If LastResponseType = 4 OrElse LastResponseType = 5 Then 
            Disconnect() 
            DataSocket.Close() 
            Throw New DownloadException("Download request failed.") 
        End If 
        'Download all the bytes 
        DownloadData() 
        CloseFile() 
        'Disconnect from the server 
        SendCommand("QUIT") 
        Disconnect() 
    End Sub 
    '/// <summary>Logs the user on to the server.</summary> 
    '/// <exceptions cref="DownloadException">Thrown when there was an error whil logging on.</exceptions> 
    Private Sub Logon() 
        WaitForResponse() 
        If LastResponseType = 4 OrElse LastResponseType = 5 Then 
            Disconnect() 
            Throw New DownloadException("Server rejected connection.") 
        End If 
        SendCommand("USER " & Username) 
        If LastResponseType = 4 OrElse LastResponseType = 5 Then 
            Disconnect() 
            Throw New DownloadException("Server rejected username.") 
        End If 
        SendCommand("PASS " & Password) 
        If LastResponseType = 4 OrElse LastResponseType = 5 Then 
            Disconnect() 
            Throw New DownloadException("Server rejected username/password combination.") 
        End If 
    End Sub 
    '/// <summary>Parses the reply that's received from the remote host after a PASV command has been sent.</summary> 
    '/// <param name="Reply">The reply from the server.</param> 
    '/// <exceptions cref="DownloadException">Thrown when the server's reply is invalid.</exceptions> 
    '/// <returns>Returns the IPEndPoint that's parsed from the input data</returns> 
    Private Function ParsePassiveReply(Reply as String) as IPEndPoint 
        Dim BeginPos as Integer = Reply.IndexOf("(") 
        Dim EndPos as Integer = Reply.IndexOf(")", BeginPos + 1) 
        Try 
            If BeginPos > 0 And EndPos > 0 Then 
                Dim Output() as String = Reply.Substring(BeginPos + 1, EndPos - BeginPos - 1).Split(","c) 
                If Output.Length() = 6 Then 
                    ParsePassiveReply = New IPEndPoint(IPAddress.Parse(Output(0) + "." + Output(1) + "." + Output(2) + "." + Output(3)), Integer.Parse(Output(4)) * 256 + Integer.Parse(Output(5))) 
                End If 
            End If 
        Catch 
            Throw New DownloadException("Invalid PASV reply from server.") 
        End Try 
    End Function 
    '/// <summary>Downloads the data from the remote host.</summary> 
    '/// <exceptions cref="DownloadException">Thrown when there was an error while downloading the data.</exceptions> 
    Private Sub DownloadData() 
        Dim Ret as Integer = 0 
        Dim Buffer(1024) as Byte 
        Dim theSocket as Socket 
        Try 
            If Passive Then 
                theSocket = DataSocket 
            Else 
                theSocket = DataSocket.Accept() 
            End If 
            'Receive the reply 
            Ret = theSocket.Receive(Buffer) 
            While Ret <> 0 AndAlso IsConnected 
                HandleBytes(Buffer, Ret) 
                If ReceivedBytes = MaxDownload Then Exit While 
                If theSocket.Connected Then 
                    Ret = theSocket.Receive(Buffer) 
                Else 
                    Ret = 0 
                End If 
            End While 
        Catch 
            Throw New DownloadException("An error occured while downloading the data from the remote host.") 
        End Try 
        theSocket.Close() 
        If Not Passive Then DataSocket.Close() 
        WaitForResponse() 
        If LastResponseType = 4 OrElse LastResponseType = 5 Then 
            Disconnect() 
            Throw New DownloadException("Error whle downloading the file.") 
        End If 
    End Sub 
    '/// <summary>Waits until the server sends a reply.</summary> 
    '/// <exceptions cref="DownloadException">Thrown when there was an error while waiting for a reply.</exceptions> 
    Private Sub WaitForResponse() 
        Dim TempBuffer(1023) as Byte 
        Dim RetBytes as Integer 
        Dim RetString as String 
        Try 
            Do 
                RetBytes = DownloadSocket.Receive(tempBuffer) 
                RetString &= Encoding.ASCII.GetString(TempBuffer, 0, RetBytes) 
            Loop Until IsValidResponse(RetString) 
            If RetString.Length >= 1 Then 
                m_LastResponse = retString 
                m_LastResponseType = Integer.Parse(retString.Substring(0, 1)) 
            Else 
                m_LastResponse = "" 
                m_LastResponseType = 5 'Unrecoverable error 
            End If 
        Catch 
            If IsConnected Then Throw New DownloadException("Error while reading from command stream.") 
        End Try 
    End Sub 
    '/// <summary>Determines whether a string returned from an FTP server is a valid reply string.</summary> 
    '/// <returns>True if it is a valid reply string, False otherwise.</returns> 
    Private Function IsValidResponse(Input as string)as Boolean 
        Dim Lines() as String = Input.Split(ControlChars.Lf) 
        If Lines.Length > 1 Then 
            Try 
                If Lines(Lines.Length - 2).Replace(ControlChars.Cr, "").Substring(3, 1).Equals(" ") Then Return True 
            Catch 
                Return False 
            End Try 
        End If 
        Return False 
    End Function 
    '/// <summary>Sends a command to the remote host.</summary> 
    '/// <exceptions cref="DownloadException">Thrown when there was an error sending the command.</exceptions> 
    Private Sub SendCommand(Command as String) 
        Try 
            DownloadSocket.Send(Encoding.ASCII.GetBytes(Command & ControlChars.CrLf)) 
            WaitForResponse() 
        Catch 
            If IsConnected Then Throw New DownloadException("Error while writing to command stream.") 
        End Try 
    End Sub 
    '/// <summary>Disconnects the object from the remote host.</summary> 
    Public Shadows Sub Disconnect() 
        If Not DataSocket Is Nothing Then DataSocket.Close() 
        MyBase.Disconnect() 
    End Sub 
    '/// <summary>Downloads the secified file.</summary> 
    '/// <param name="File">The file to download.<param> 
    '/// <exceptions cref="ArgumentException">Thrown when the specified parameter is invalid.</exceptions> 
    '/// <remarks> 
    '/// The following type of input is accepted: 
    '/// ftp://123.1.2.3/thedir/thefile.ext 
    '/// ftp://123.1.2.3:123/thedir/thefile.ext 
    '/// ftp://username@123.1.2.3/thedir/thefile.ext 
    '/// ftp://username@123.1.2.3:123/thedir/thefile.ext 
    '/// ftp://usernameassword@123.1.2.3/thedir/thefile.ext 
    '/// ftp://usernameassword@123.1.2.3:123/thedir/thefile.ext 
    '/// All the above combinations without the 'ftp://' 
    '/// </remarks> 
    Public Overrides Sub FetchFileString(File as String) 
        ParseURL(File) 
        FetchFile() 
    End Sub 
    '/// <summary>Parses a FTP string.</summary> 
    '/// <param name="URL">The URL to parse.<param> 
    '/// <exceptions cref="ArgumentException">Thrown when the specified parameter is invalid.</exceptions> 
    Private Sub ParseURL(URL as String) 
        URL = URL.Trim 
        Dim Protocol as Integer = URL.IndexOf("://") 
        Dim StartHost as Integer, EndHost as Integer 
        If Protocol > 0 Then 
            If Not URL.Substring(0, 6).ToLower.Equals("ftp://") Then 
                Throw New ArgumentException() 
            End If 
            StartHost = 6 
        Else 
            StartHost = 0 
        End If 
        EndHost = URL.IndexOf("/", StartHost) 
        Dim UrlHost as String = URL.Substring(StartHost, EndHost - StartHost) 
        Dim Parts() as String = UrlHost.Split("@"c) 
        Dim UserPass() as String 
        Dim ServerPort() as String 
        If Parts.Length = 1 Then 
            UserPass = New String() {Username, Password} 
            ServerPort = Parts(0).Split(":"c) 
        ElseIf Parts.Length = 2 Then 
            UserPass = Parts(0).Split(":"c) 
            ServerPort = Parts(1).Split(":"c) 
        Else 
            Throw New ArgumentException() 
        End If 
        Protocol = Integer.Parse(ServerPort(1)) 
        If Protocol <= 0 Then Throw New ArgumentException() 
        Port = Protocol 
        Host = ServerPort(0) 
        Username = UserPass(0) 
        Password = UserPass(1) 
        RequestedFile = URL.Substring(EndHost) 
    End Sub 
    '/// <summary>Holds the last response type sent by the server.</summary> 
    '/// <value>The last response type sent by the server.</value> 
    Private ReadOnly Property LastResponseType() as Integer 
        Get 
            Return m_LastResponseType 
        End Get 
    End Property 
    '/// <summary>Holds the last response sent by the server.</summary> 
    '/// <value>The last response sent by the server.</value> 
    Private ReadOnly Property LastResponse() as String 
        Get 
            Return m_LastResponse 
        End Get 
    End Property 
    '/// <summary>Gets of sets the data socket.</summary> 
    '/// <value>The data socket.</value> 
    Private Property DataSocket() as Socket 
        Get 
             Return m_DataSocket 
        End Get 
        Set(Value as Socket) 
            m_DataSocket = Value 
        End Set 
    End Property 
    '/// <summary>Gets or sets a value that specifies whether or not to use passive transfers.</summary> 
    '/// <value>A value that specifies whether or not to use passive transfers.</value> 
    Public Property Passive() as Boolean 
        Get 
            Return m_Passive 
        End Get 
        Set(Value as Boolean) 
            m_Passive = Value 
        End Set 
    End Property 
    'Private variables 
    Private m_LastResponseType as Integer = 0 
    Private m_LastResponse as String = "" 
    Private m_Passive as Boolean = True 
    Private m_DataSocket as Socket 
End Class 

From Here