Imports System.IO
Imports System.Net
Imports System.Net.Sockets
Imports System.Windows.Forms
Imports System.Text.Encoding
 
Module Module1
    '--- Our arguments from the Command line
    Dim FileName As String
    Dim FilePath As String
    Dim Server As String
    Dim Port As Integer = 21
    Dim SaveFilePath As String
    Dim SaveFileName As String
    Dim UserID As String
    Dim Password As String
    Dim BufferSize As Integer = 4096
    Dim OutputFile As String
    Dim RcvdByteCount As Long = 0
    Dim QuietMode As Boolean = False
 

    '--- The FTP Control Channel
    '--- Used for sending commands & receiving responses
    Dim ftpc As Socket
    Dim bufc(128) As Byte
 
    '--- The FTP Data Channel
    '--- Used for receiving the file
    Dim ftpd As Socket
    Dim bufd() As Byte

    Sub
Main(ByVal args() As String)
        If args.Length = 0 Or (args.Length > 0 AndAlso args(0).IndexOf("?") >= 0) Then
            ShowHelp()
            Exit Sub
        End If
 
        GetArgs(args)
        If FileName = "" Or Server = "" Or UserID = "" Or Password = "" Then
            ShowHelp()
            Exit Sub
        End If
 
        If SaveFileName = "" Then SaveFileName = FileName
        If SaveFilePath = "" Then
            OutputFile = SaveFileName
        Else
            OutputFile = SaveFilePath & "\" & SaveFileName
            OutputFile = OutputFile.Replace("\\", "\")
        End If
 
        ReDim bufd(BufferSize)
        ConnectToServer(Server)
        Application.Run()
 
    End Sub
 
    Sub ShowHelp()
        Console.WriteLine(vbCrLf & _
                "Downloads the specified file from an FTP server." & vbCrLf & _
                "Usage: GETFTP {[-fp FilePath] -fn FileName -s Server [-p Port]" & vbCrLf & _
                "               [-sfp SaveFilePath] [-sfn SaveFileName] [-bs BufferSize]" & vbCrLf & _
                "                -uid UserID -pwd Password [-q True|False]}" & vbCrLf & vbCrLf & _
                "                -q = Quiet Mode")
    End Sub
 
    Sub GetArgs(ByVal args() As String)
        For i As Integer = 0 To args.Length - 2
            Select Case args(i)
                Case "-fp" : FilePath = args(i + 1)
                Case "-fn" : FileName = args(i + 1)
                Case "-s" : Server = args(i + 1)
                Case "-sfp" : SaveFilePath = args(i + 1)
                Case "-sfn" : SaveFileName = args(i + 1)
                Case "-bs" : BufferSize = args(i + 1)
                Case "-uid" : UserID = args(i + 1)
                Case "-pwd" : Password = args(i + 1)
                Case "-p" : Port = args(i + 1)
                Case "-q" : QuietMode = CType(args(i + 1), Boolean)
            End Select
        Next
    End Sub
 
    Sub ConnectToServer(ByVal Server As String)
        ftpc = New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
        Dim ipe As New IPEndPoint(Dns.Resolve(Server).AddressList(0), Port)
        ftpc.BeginConnect(ipe, AddressOf ConnectedCallback, Nothing)
    End Sub
 
    Sub ConnectedCallback(ByVal ar As IAsyncResult)
        ftpc.EndConnect(ar)
        ftpc.BeginReceive(bufc, 0, bufc.Length, SocketFlags.None, AddressOf ReceiveCallBack, bufc)
    End Sub
 
    Sub ReceiveCallBack(ByVal ar As IAsyncResult)
        bufc = CType(ar.AsyncState, Byte())
        Dim numbytes As Int32
        numbytes = ftpc.EndReceive(ar)
        'See what the server just told us.
        ParseResponse(System.Text.Encoding.ASCII.GetString(bufc, 0, numbytes))
        'Start receiving again...
        ftpc.BeginReceive(bufc, 0, bufc.Length, SocketFlags.None, AddressOf ReceiveCallBack, bufc)
    End Sub
 
    Sub ParseResponse(ByVal msg As String)
        Dim snd() As Byte
        If QuietMode = False Then Console.WriteLine(msg)
        Select Case msg.Substring(0, 3)
            Case "220"  'Connected
                snd = ASCII.GetBytes("USER " & UserID & vbCrLf)
            Case "331"  'Password Required for user
                snd = ASCII.GetBytes("PASS " & Password & vbCrLf)
            Case "530"  'User cannot log in
                If QuietMode = True Then Console.WriteLine(msg)
                Application.Exit()
            Case "230"  'User logged in
                snd = ASCII.GetBytes("CWD " & FilePath & vbCrLf)
            Case "250"  'CWD command successful
                snd = ASCII.GetBytes("PASV" & vbCrLf)
            Case "550"  'The system cannot find the file specified
                If QuietMode = True Then Console.WriteLine(msg)
                Application.Exit()
            Case "227"  'Entering Passive Mode (a1,a2,a3,a4,p1,p2).
                ConnectToDataChannel(msg)
                snd = ASCII.GetBytes("TYPE I" & vbCrLf)
            Case "200"  'Type set to I
                snd = ASCII.GetBytes("RETR " & FileName & vbCrLf)
            Case "226"  'Data transfer complete
                Application.Exit()
            Case "500"  'Invalid number of parameters
                If QuietMode = True Then Console.WriteLine(msg)
                Application.Exit()
        End Select
        If Not snd Is Nothing Then ftpc.Send(snd)
    End Sub
 
    Function GetDataEndPoint(ByVal rsp As String) As IPEndPoint
        rsp = rsp.Substring(rsp.IndexOf("(") + 1)
        rsp = rsp.Substring(0, rsp.IndexOf(")"))
        Dim dat() As String = rsp.Split(",")
        Dim ipe As New IPEndPoint(Dns.Resolve(dat(0) & "." & dat(1) & "." & dat(2) & "." & dat(3)).AddressList(0), _
                                  (CType(dat(4), Integer) * 256) + (CType(dat(5), Integer)))
        Return ipe
    End Function
 
    Sub ConnectToDataChannel(ByVal rsp As String)
        ftpd = New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
        ftpd.BeginConnect(GetDataEndPoint(rsp), AddressOf DataConnectedCallBack, Nothing)
    End Sub
 
    Sub DataConnectedCallBack(ByVal ar As IAsyncResult)
        ftpd.EndConnect(ar)
        'Create the file
        CreateFile()
        ftpd.BeginReceive(bufd, 0, bufd.Length, SocketFlags.None, AddressOf DataReceiveCallBack, bufd)
        If QuietMode = False Then Console.WriteLine("Connected to the data channel." & vbCrLf)
    End Sub
 
    Sub DataReceiveCallBack(ByVal ar As IAsyncResult)
        bufd = CType(ar.AsyncState, Byte())
        Dim numbytes As Int32
        numbytes = ftpd.EndReceive(ar)
        RcvdByteCount += numbytes
        If QuietMode = False Then Console.WriteLine(RcvdByteCount & " bytes received")
        If numbytes = 0 Then
            'Close the data channel
            ftpd.Close()
            If QuietMode = False Then Console.WriteLine("Done with data file" & vbCrLf)
            Exit Sub
        End If
        'Write the bytes to the file
        AppendFile(numbytes)
        'Start receiving again...
        ftpd.BeginReceive(bufd, 0, bufd.Length, SocketFlags.None, AddressOf DataReceiveCallBack, bufd)
    End Sub
 
    Sub CreateFile()
        Dim fs As New FileStream(OutputFile, FileMode.Create)
        fs.Close()
    End Sub
 
    Sub AppendFile(ByVal numbytes As Integer)
        Dim fs As New FileStream(OutputFile, FileMode.Append)
        fs.Write(bufd, 0, numbytes)
        fs.Close()
    End Sub
 
End
Module