|
To test this application (and the project), you need an FTP server. If you are connected to the Internet, you can use a number of FTP servers that allow anonymous access. These servers normally allow you to download files but not upload files. Alternatively, you can use a local FTP server found in Windows 2000 Server. The following sections start with installing and configuring an FTP server and then continue with a simple FTP client application.
If you have access to Windows 2000 Server, you are in luck. One of the programs you can install is an FTP server. If it is already installed, you can configure it through the Internet Service Manager whose applet can be found in Administrative Tools in the Control Panel. If it is not yet installed, it is now time to do so.
To install the FTP Server in Windows 2000 Server, follow these steps:
Double-click Add/Remove Programs from Control Panel.
Click the Add/Remove Windows Components button on the left side of the page. The Windows Components Wizard will display. One of the items displayed in the Components list is Internet Information Services (IIS). Click this item.
Click the Details button. The Internet Information Services dialog box shows.
Check the File Transfer Protocol (FTP) Server subcomponent.
Click the OK button and follow the instructions to install. You will be asked to insert the Windows 2000 Server installation CD.
The main task in the configuration is to map the directory that will become the root of the FTP server. In addition, you can also set the session timeout and the directory list style. Any user who has access to the Windows 2000 server will have the same access to the FTP server.
To configure the FTP server, follow these steps:
Double-click Administrative Tools from Control Panel.
Double-click the Internet Service Manager icon. You should be able to see the Default FTP Site icon under the machine name.
Right-click the Default FTP Site icon and click Properties. The Default FTP Site Properties dialog box will display.
Click the Home Directory tab, as shown in Figure 5-1.
In the FTP Site Directory section, browse to the directory that you want to be the root of the FTP server.
In the FTP Site Directory section, make sure that the Read and Write check boxes are selected.
Select UNIX in the Directory Listing Style section.
Click the Apply button and then the OK button.
Figure 5-1: The Home Directory tab of the Default FTP Site Properties dialog box
The NETFTP console application is a simple FTP client application that is similar to the ftp.exe program you can find in Unix/Linux or Windows. It consists of a class, NETFTP, that you can find in the Listings/Ch05/Other/NETFTP.vb file. The main purpose of this small application is to show how to use the System.Net.Sockets.Socket class to connect to an FTP server and do file transfer; it does not worry too much about error handling. After understanding this application, you can understand the project more easily.
To compile this program, type the following command in the command prompt:
vbc -r:System.dll FTPNET.vb
The result is an executable called FTPNET.exe.
The first thing to do to use this program is connect to an FTP server by typing the following:
NETFTP <server> <user name> <password>
If the connection attempt was successful, a message such as the following will display in the console.
Connected. Waiting for reply... 220 bulbul Microsoft FTP Service (Version 5.0). USER Administrator 331 Password required for Administrator. PASS 230 User Administrator logged in. 215 Windows_NT version 5.0 200 Type set to A.
If the connection failed, an error message will display.
Once connected, you will see the NETFTP> prompt. You can type one of the following commands in this prompt: CWD, DELE, LIST, PWD, QUIT, RETR, STOR. Upon execution, the server sends a reply that will display on the console. Other than that, the program outputs the "Command Invalid" message.
Upon successful execution of a command, the program displays the NETFTP> prompt again, indicating it is ready to accept a new command.
The valid commands are explained in the following sections.
This command causes the program to send a CWD command to the FTP server. This command changes the remote working directory. The syntax of this command is as follows:
CWD directory
where directory is the name of the directory to which you want to change. For example, to change the working directory to the /files/program directory, type the following:
CWD /files/program
You can also pass a relative path as the argument. To change to the parent directory of the current directory, type the following:
CWD ..
If the command successfully executes, the server sends the following message, which displays on the console:
250 CWD command successful.
If it cannot find the destination directory, the server sends the following message:
550 /prog/images: The system cannot find the file specified.
This command causes the application to send a DELE command to the connected FTP server. The DELE command deletes a file in the server. The syntax of this command is as follows:
DELE pathToFileToDelete
This command does not take an argument. It causes the application to send a LIST command to the server. The LIST command displays the content of the current directory. For example, the following is the output of the LIST directory:
227 Entering Passive Mode (127,0,0,1,6,77). 125 Data connection already open; Transfer starting. 226 Transfer complete. drwxrwxrwx 1 owner group 0 Jul 28 21:30 April2001 -rwxrwxrwx 1 owner group 344064 Jul 29 13:25 Chapter5.doc -rwxrwxrwx 1 owner group 12118 Jul 29 13:25 complete.wav -rwxrwxrwx 1 owner group 12118 Jul 30 10:05 complete2.wav -rwxrwxrwx 1 owner group 1050 Jul 29 13:25 Folder.gif -rwxrwxrwx 1 owner group 53248 Jul 28 20:08 FTPClient.exe drwxrwxrwx 1 owner group 0 Jul 28 20:21 June 2002 -rwxrwxrwx 1 owner group 9216 Jul 30 10:02 NETFTP.exe -rwxrwxrwx 1 owner group 12077 Jul 30 10:02 NETFTP.vb drwxrwxrwx 1 owner group 0 Jul 30 10:05 New Folder drwxrwxrwx 1 owner group 0 Jul 26 20:09 New Folder (2) -rwxrwxrwx 1 owner group 132717 Jul 29 13:24 rfc959.txt
Note that this directory listing is in the Unix style. An FTP server can choose to use another style.
The PWD command causes the application to send a PWD command to the FTP server. The PWD command displays the current directory. This command does not take an argument. On successful execution, this is an example of what the server may send:
257 "/" is current directory.
The QUIT command causes the application to send a QUIT command to the FTP server. The QUIT command causes the connection to be closed. This command does not take an argument.
The RETR command causes the application to send a RETR command to the FTP server. The RETR command downloads a file from the server. The syntax of this command is as follows:
RETR pathToFileToDownload
The STOR command causes the application to send a STOR command to the FTP server. The STOR command uploads a file in the local directory. The syntax of this command is as follows:
STOR pathToFileToUpload
The NETFTP program comprises one class: NETFTP. The following sections describe the NETFTP class.
The following is the declarations part of the NETFTP class:
Private port As Integer = 21 Private controlSocket As _ New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp) Private dataSocket As Socket Private serverAddress As String Public replyMessage As String Public replyCode As String
Note that there are two Socket object references used in this class: control-Socket and dataSocket.
The following are the methods in the NETFTP class.
The Connect method connects to an FTP server (see Listing 5-1).
Listing 5-1: The Connect Method
Public Sub Connect(ByVal server As String) Try controlSocket.Connect(New IPEndPoint(Dns.Resolve(server).AddressList(0), port)) Catch e As Exception Console.WriteLine(e.ToString()) Return End Try If controlSocket.Connected Then Console.WriteLine("Connected. Waiting for reply...") GetResponse() Else Console.WriteLine("Couldn't connect.") End If End Sub
The Connect method uses the controlSocket's Connect method to connect to an FTP server:
Try controlSocket.Connect(New IPEndPoint(Dns.Resolve(server).AddressList(0), port)) Catch e As Exception Console.WriteLine(e.ToString()) Return End Try
If the connection attempt is successful, the controlSocket's Connected property will be set to True. In this case, a message prints on the console and the GetResponse method is invoked:
If controlSocket.Connected Then Console.WriteLine("Connected. Waiting for reply...") GetResponse()
Otherwise, a "Couldn't connect" message displays on the console:
Else Console.WriteLine("Couldn't connect.") End If
The PassiveData method sends the PASV command to the connected FTP server to make the server become passive (see Listing 5-2).
Listing 5-2: The PassiveData Method
Private Sub PassiveDataConnection() SendCommand("PASV" & ControlChars.CrLf) GetResponse() Dim addr As String = replyMessage addr = addr.Substring(addr.IndexOf("("c) + 1, _ addr.IndexOf(")"c) - addr.IndexOf("("c) - 1) Dim address As String() = addr.Split(","c) Dim ip As String = address(0) & "." & address(1) & "." & address(2) & "." & _ address(3) Dim port As Integer = Convert.ToInt32(address(4)) * 256 + _ Convert.ToInt32(address(5)) dataSocket = New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp) dataSocket.Connect(New IPEndPoint(IPAddress.Parse(ip), port)) End Sub
If the PASV command successfully executes at the server, the server sends the address and port number of the data connection for the client to connect. Therefore, on a successful execution of PASV, the server replies by sending a string of the following format:
227 Entering Passive Mode (a1,a2,a3,a4,p1,p2).
where a1, a2, a3, and a4 are parts of an IP address in dotted-quad notation, and p1 and p2 signify the port number. Consequently, you can obtain the IP address using the following lines:
Dim addr As String = replyMessage addr = addr.Substring(addr.IndexOf("("c) + 1, addr.IndexOf(")"c) - addr.IndexOf("("c) - 1) Dim address As String() = addr.Split(","c) Dim ip As String = address(0) & "." & address(1) & "." & address(2) & "." & address(3)
You obtain the port number by multiplying p1 with 256 and adding the result to p2:
Dim port As Integer = Convert.ToInt32(address(4)) * 256 + Convert.ToInt32(address(5))
Then, you instantiate a data socket and invoke its Connect method:
dataSocket = New Socket(AddressFamily.InterNetwork, SocketType.Stream, _ ProtocolType.Tcp) dataSocket.Connect(New IPEndPoint(IPAddress.Parse(ip), port))
After the Connect method is called, the data socket is readily available to the method that invokes the PassiveData method.
The GetResponse method receives the byte stream from the connected FTP server. This method is invoked immediately after an FTP command is sent to the server (see Listing 5-3).
Listing 5-3: The GetResponse Method
Private Sub GetResponse() ' this method listens for the server response and receives all bytes ' sent by the server ' A server response can be single line or multiline. ' If the fourth byte of the first line is a hyphen, then it is ' multiline. If multiline, waits until the line that starts with the ' response code (the first three bytes of the first line). Dim bytes(511) As Byte ' an array of 512 bytes Dim receivedByteCount As Integer Dim response As String = "" ' get the first line receivedByteCount = controlSocket.Receive(bytes) response = Encoding.ASCII.GetString(bytes, 0, receivedByteCount) Dim multiline As Boolean = (response.Chars(3) = "-"c) If multiline Then If response.Length > 3 Then replyCode = response.Substring(0, 3) End If Dim line As String = "" Dim lastLineReached As Boolean = False While Not lastLineReached receivedByteCount = controlSocket.Receive(bytes) line = Encoding.ASCII.GetString(bytes, 0, receivedByteCount) response += line If line.IndexOf(ControlChars.CrLf & replyCode & " ") <> -1 Then lastLineReached = True End If If lastLineReached Then 'just wait until CRLF is reached While Not line.EndsWith(ControlChars.CrLf) receivedByteCount = controlSocket.Receive(bytes) line = Encoding.ASCII.GetString(bytes, 0, receivedByteCount) response += line End While End If End While Else While receivedByteCount = bytes.Length And _ Not response.EndsWith(ControlChars.CrLf) receivedByteCount = controlSocket.Receive(bytes) response += Encoding.ASCII.GetString(bytes, 0, receivedByteCount) End While End If Console.WriteLine() Console.Write(response) If response.Length > 3 Then replyCode = response.Substring(0, 3) replyMessage = response.Substring(3, response.Length - 3) Else replyCode = "" replyMessage = "Unexpected Error has occurred." End If End Sub
The first thing this method does is to check the fourth byte of the reply to determine whether the server reply is a single-line or a multiline response. The fourth byte in a single-line response is a space, whereas it will be a hyphen in a multiline response:
Dim bytes(511) As Byte ' an array of 512 bytes Dim receivedByteCount As Integer Dim response As String = "" ' get the first line receivedByteCount = controlSocket.Receive(bytes) response = Encoding.ASCII.GetString(bytes, 0, receivedByteCount) Dim multiline As Boolean = (response.Chars(3) = "-"c)
If the response is multilined, the GetResponse method reads the reply code (the first three digits of the first line) and keeps reading the response in a While loop until the last line is reached. The last line is a line that starts with a reply code:
If multiline Then If response.Length > 3 Then replyCode = response.Substring(0, 3) End If Dim line As String = "" Dim lastLineReached As Boolean = False While Not lastLineReached receivedByteCount = controlSocket.Receive(bytes) line = Encoding.ASCII.GetString(bytes, 0, receivedByteCount) response += line If line.IndexOf(ControlChars.CrLf & replyCode & " ") <> -1 Then lastLineReached = True End If
When the last line is reached, it continues reading until the last byte is received:
If lastLineReached Then 'just wait until CRLF is reached While Not line.EndsWith(ControlChars.CrLf) receivedByteCount = controlSocket.Receive(bytes) line = Encoding.ASCII.GetString(bytes, 0, receivedByteCount) response += line End While
If it is a single-line response, it just continues reading until the last byte in the stream is received:
Else While receivedByteCount = bytes.Length And _ Not response.EndsWith(ControlChars.CrLf) receivedByteCount = controlSocket.Receive(bytes) response += Encoding.ASCII.GetString(bytes, 0, receivedByteCount) End While
The response will then be sent to the console:
Console.WriteLine() Console.Write(response)
Next, replyCode and replyMessage are assigned the reply code and message of the server response, respectively:
replyCode = response.Substring(0, 3) replyMessage = response.Substring(3, response.Length - 3)
The Login method accepts a username and a password that will be sent as the user's credential to log in to the connected FTP server (see Listing 5-4).
Listing 5-4: The Login Method
Public Function Login(ByVal userName As String, ByVal password As String) As String If controlSocket.Connected Then ' Sending user name Dim command As String command = "USER " & userName & ControlChars.CrLf Console.WriteLine(command) SendCommand(command) GetResponse() ' Sending password command = "PASS " & password & ControlChars.CrLf Console.Write("PASS") 'do not display password SendCommand(command) GetResponse() Return replyCode Else Console.Write("Login failed because no connection is available") End If Return "" End Function
The Login method sends the USER and PASS commands in sequence. It returns the three-digit reply code. Login is successful only if the three-digit code is 230.
The SendCommand method sends a command to the connected FTP server (see Listing 5-5).
Listing 5-5: The SendCommand Method
Private Sub SendCommand(ByVal command As String) Try controlSocket.Send(Encoding.ASCII.GetBytes(command), command.Length, 0) Catch End Try End Sub
The SendCWDCommand method uses the SendCommand method to send a CWD command to the connected FTP server (see Listing 5-6).
Listing 5-6: The SendCWDCommand Method
Public Sub SendCWDCommand(ByVal path As String) SendCommand("CWD " & path & ControlChars.CrLf) GetResponse() End Sub
The SendDELECommand method sends a DELE command to the connected FTP server using the SendCommand method (see Listing 5-7).
Listing 5-7: The SendDELECommand Method
Public Sub SendDELECommand(ByVal filename As String) SendCommand("DELE " & filename & ControlChars.CrLf) GetResponse() End Sub
The SendLISTCommand method sends a LIST command to the connected FTP server and displays the returned directory list (see Listing 5-8).
Listing 5-8: The SendLISTCommand Method
Public Sub SendLISTCommand() PassiveDataConnection() SendCommand("LIST" & ControlChars.CrLf) GetResponse() Dim byteReceivedCount As Integer Dim msg As New StringBuilder(2048) Dim bytes(511) As Byte Do byteReceivedCount = _ dataSocket.Receive(bytes, bytes.Length, SocketFlags.None) msg.Append(Encoding.ASCII.GetString(bytes, 0, byteReceivedCount)) Loop Until byteReceivedCount = 0 Console.WriteLine(msg.ToString()) 'because the 226 response might be sent 'before the data connection finishes, only try to get "completion message" 'if it's not yet sent If replyMessage.IndexOf("226 ") = -1 Then GetResponse() End If End Sub
The SendLISTCommand method starts by calling the PassiveDataConnection and sends the LIST command:
PassiveDataConnection() SendCommand("LIST" & ControlChars.CrLf) GetResponse()
The data socket instantiated in the PassiveDataConnection method then reads the data from the server:
Dim byteReceivedCount As Integer Dim msg As New StringBuilder(2048) Dim bytes(511) As Byte Do byteReceivedCount = _ dataSocket.Receive(bytes, bytes.Length, SocketFlags.None) msg.Append(Encoding.ASCII.GetString(bytes, 0, byteReceivedCount)) Loop Until byteReceivedCount = 0
The data then displays on the console:
Console.WriteLine(msg.ToString())
Upon sending the directory list, the server should send the 226 reply code indicating the transfer completion. However, in my testing, the 226 reply code might be sent before the data is received. Therefore, you call the GetResponse method only if the 226 reply code has not been sent:
If replyMessage.IndexOf("226 ") = -1 Then GetResponse() End If
The SendMKCommand method sends an MKD command to the connected FTP server (see Listing 5-9).
Listing 5-9: The SendMKDCommand Method
Public Sub SendMKDCommand(ByVal dir As String) SendCommand("MKD " & dir & ControlChars.CrLf) GetResponse() End Sub
The SendPWDCommand method sends a PWD command to the connected FTP server (see Listing 5-10).
Listing 5-10: The SendPWDCommand Method
Public Sub SendPWDCommand() SendCommand("PWD" & ControlChars.CrLf) GetResponse() End Sub
The SendRMDCommand method sends a RMD command to the connected FTP server (see Listing 5-11).
Listing 5-11: The SendRMDCommand Method
Public Sub SendRMDCommand(ByVal dir As String) SendCommand("RMD " & dir & ControlChars.CrLf) GetResponse() End Sub
The SendQUITCommand method sends a QUIT command to the connected FTP server (see Listing 5-12). After the response is received, it calls the Shutdown and Close methods of the control socket.
Listing 5-12: The SendQUITCommand Method
Public Sub SendQUITCommand() SendCommand("QUIT" & ControlChars.CrLf) GetResponse() controlSocket.Shutdown(SocketShutdown.Both) controlSocket.Close() End Sub
The SendRETRCommand method downloads a file in the connected FTP server (see Listing 5-13).
Listing 5-13: The SendRETRCommand Method
Public Sub SendRETRCommand(ByVal filename As String) Dim f As FileStream = File.Create(filename) SendTYPECommand("I") PassiveDataConnection() SendCommand("RETR " & filename & ControlChars.CrLf) GetResponse() Dim byteReceivedCount As Integer Dim totalByteReceived As Integer = 0 Dim bytes(511) As Byte Do byteReceivedCount = _ dataSocket.Receive(bytes, bytes.Length, SocketFlags.None) totalByteReceived += byteReceivedCount f.Write(bytes, 0, byteReceivedCount) Loop Until byteReceivedCount = 0 f.Close() 'because the 226 response might be sent 'before the data connection finishes, only try to get "completion message" 'if it's not yet sent If replyMessage.IndexOf("226 ") = -1 Then GetResponse() End If SendTYPECommand("A") End Sub
The method starts by creating a file in the current local directory:
Dim f As FileStream = File.Create(filename)
It then changes the transmission mode to image (binary) by calling the Send-TYPECommand method, passing "I" to the method:
SendTYPECommand("I")
Next, it calls the PassiveDataConnection method to get a data socket for the data transmission and sends a RETR command to the server:
PassiveDataConnection() SendCommand("RETR " & filename & ControlChars.CrLf) GetResponse()
The data socket created in the PassiveDataConnection method is then used to read the data stream. The incoming stream is written to the file created at the beginning of this method:
Dim byteReceivedCount As Integer Dim totalByteReceived As Integer = 0 Dim bytes(511) As Byte Do byteReceivedCount = _ dataSocket.Receive(bytes, bytes.Length, SocketFlags.None) totalByteReceived += byteReceivedCount f.Write(bytes, 0, byteReceivedCount) Loop Until byteReceivedCount = 0
Next, the file closes:
f.Close()
After the data transmission completes, the server closes the data connection and sends a 226 transfer completion code through the control connection. However, the 226 reply code might be sent before all the data is received. Therefore, you call the GetResponse method only if the 226 reply code has not been sent:
If replyMessage.IndexOf("226 ") = -1 Then GetResponse() End If
Finally, it changes the mode back to ASCII:
SendTYPECommand("A")
The SendSTORCommand method uploads a file in the connected FTP server (see Listing 5-14).
Listing 5-14: The SendSTORCommand Method
Public Sub SendSTORCommand(ByVal filename As String) Dim f As FileStream = File.Open(filename, FileMode.Open) SendTYPECommand("I") PassiveDataConnection() SendCommand("STOR " & filename & ControlChars.CrLf) GetResponse() Dim byteReadCount As Integer Dim totalByteSent As Integer Dim bytes(511) As Byte Do byteReadCount = f.Read(bytes, 0, bytes.Length) If byteReadCount <> 0 Then dataSocket.Send(bytes, byteReadCount, SocketFlags.None) totalByteSent += byteReadCount End If Loop Until byteReadCount = 0 dataSocket.Shutdown(SocketShutdown.Both) dataSocket.Close() f.Close() GetResponse() SendTYPECommand("A") End Sub
The method starts by opening the file to upload to the connected FTP server:
Dim f As FileStream = File.Open(filename, FileMode.Open)
It then changes the transmission mode to image (binary) and calls the PassiveDataConnection to obtain a data socket for the file transmission:
SendTYPECommand("I") PassiveDataConnection()
Then it sends a STOR command to indicate to the server that it is going to send a file to that server:
SendCommand("STOR " & filename & ControlChars.CrLf) GetResponse()
The data socket created by the PassiveDataConnection transfers the file.
Dim byteReadCount As Integer Dim totalByteSent As Integer Dim bytes(511) As Byte Do byteReadCount = f.Read(bytes, 0, bytes.Length) If byteReadCount <> 0 Then dataSocket.Send(bytes, byteReadCount, SocketFlags.None) totalByteSent += byteReadCount End If Loop Until byteReadCount = 0
After the file transfer completes, the data socket closes:
dataSocket.Shutdown(SocketShutdown.Both) dataSocket.Close()
Then, the file closes and the mode switches back to ASCII:
f.Close() GetResponse() SendTYPECommand("A")
The SendSYSTCommand method sends a SYST method to the connected FTP server (see Listing 5-15).
Listing 5-15: The SendSYSTCommand Method
Public Sub SendSYSTCommand() SendCommand("SYST" & ControlChars.CrLf) GetResponse() End Sub
You use the SendTYPECommand method to change the transmission mode from the client to the server (see Listing 5-16).
Listing 5-16: The SendTYPECommand Method
Public Sub SendTYPECommand(ByVal type As String) SendCommand("TYPE " & type & ControlChars.CrLf) GetResponse() End Sub
The Main static method is the entry point of the program. It does the following:
Ensures that the program is invoked using the correct number of arguments.
Controls the program flow with a While loop so that the user can enter one FTP command after the execution of another.
Invokes the correct method upon receiving a valid user input.
Displays an error message on receiving an invalid user input.
Listing 5-17 shows the Main method.
Listing 5-17: The Main Method
Public Shared Sub Main(ByVal args As String()) If args.Length <> 3 Then Console.WriteLine("usage: NETFTP server username password") Else Dim ftp As New NETFTP() ftp.Connect(args(0)) Dim replyCode As String = ftp.Login(args(1), args(2)) If replyCode.Equals("230") Then 'login successful, allow user to type in commands ftp.SendSYSTCommand() ftp.SendTYPECommand("A") Dim command As String = "" Try While Not command.ToUpper.Equals("QUIT") Console.Write("NETFTP>") command = Console.ReadLine().Trim() If command.ToUpper.Equals("PWD") Then ftp.SendPWDCommand() ElseIf command.ToUpper.StartsWith("CWD") Then If command.Length > 3 Then Dim path As String = command.Substring(4).Trim() If path.Equals("") Then Console.WriteLine("Please specify the directory to change to") Else ftp.SendCWDCommand(path) End If End If ElseIf command.ToUpper.StartsWith("DELE") Then If command.Length > 4 Then Dim path As String = command.Substring(5).Trim() If path.Equals("") Then Console.WriteLine("Please specify the file to delete") Else ftp.SendDELECommand(path) End If End If ElseIf command.ToUpper.Equals("LIST") Then ftp.SendLISTCommand() ElseIf command.ToUpper.StartsWith("MKD") Then If command.Length > 3 Then Dim dir As String = command.Substring(4).Trim() If dir.Equals("") Then Console.WriteLine("Please specify the name of the directory to create") Else ftp.SendMKDCommand(dir) End If End If ElseIf command.ToUpper.Equals("QUIT") Then ElseIf command.ToUpper.StartsWith("RMD") Then If command.Length > 3 Then Dim dir As String = command.Substring(4).Trim() If dir.Equals("") Then Console.WriteLine("Please specify the name of the directory to delete") Else ftp.SendRMDCommand(dir) End If End If ElseIf command.ToUpper.StartsWith("RETR") Then If command.Length > 4 Then Dim filename As String = command.Substring(5).Trim() If filename.Equals("") Then Console.WriteLine("Please specify a file to retrieve") Else ftp.SendRETRCommand(filename) End If End If ElseIf command.ToUpper.StartsWith("STOR") Then If command.Length > 4 Then Dim filename As String = command.Substring(5).Trim() If filename.Equals("") Then Console.WriteLine("Please specify a file to store") Else ftp.SendSTORCommand(filename) End If End If Else Console.WriteLine("Invalid command.") End If End While ftp.SendQUITCommand() Catch e As Exception Console.WriteLine(e.ToString()) End Try Console.WriteLine("Thank you for using NETFTP.") Else ftp.SendQUITCommand() Console.WriteLine("Login failed. Please try again.") End If End If End Sub
The Main method starts by checking that the user passes three arguments: server, username, and password. If the number of arguments is not three, it prints the following message on the console:
usage: NETFTP server username password
If the number of arguments is correct, the Main method instantiates a NETFTP object and uses the first argument (server) to connect to the remote server by calling the Connect method:
ftp.Connect(args(0))
Once connected, the Main method uses the second and third argument to log in by calling the Login method:
Dim replyCode As String = ftp.Login(args(1), args(2))
The remote server returning a reply code of 230 indicates a successful login. If login was successful, it calls the SendSYSTCommand and SendTYPECommand methods. The SendSYSTCommand method prints the server's operating system information, and the SendTYPECommand method is called by passing "A" as the argument. This in effect changes the transfer mode to ASCII:
If replyCode.Equals("230") Then 'login successful, allow user to type in commands ftp.SendSYSTCommand() ftp.SendTYPECommand("A")
After the user is successfully logged in, the program enters a While loop. Inside the While loop is the code that gets the user's input and sends the corresponding FTP command. Control exits the While loop when the user types QUIT:
Dim command As String = "" Try While Not command.ToUpper.Equals("QUIT")
Inside the While loop, the Main method first displays the NETFTP> prompt and uses the Console class's ReadLine to read the user input:
Console.Write("NETFTP>") command = Console.ReadLine().Trim()
Then, it goes through a series of If commands to execute code based on the command entered by the user. Valid commands are PWD, CWD, DELE, LIST, MKD, QUIT, RMD, RETR, and STOR.
If the command equals PWD, the SendPWDCommand method is called:
If command.ToUpper.Equals("PWD") Then ftp.SendPWDCommand()
If the command is CWD, it ensures that there is a parameter, a path, after the CWD command and that the parameter does not consist of spaces only. If the path looks valid, it calls the SendCWDCommand method. Otherwise, it prints a warning on the console:
ElseIf command.ToUpper.StartsWith("CWD") Then If command.Length > 3 Then Dim path As String = command.Substring(4).Trim() If path.Equals("") Then Console.WriteLine("Please specify the directory to change to") Else ftp.SendCWDCommand(path) End If End If
If the command is DELE, it makes sure there is a valid parameter, a filename. If the command has a valid parameter, it invokes the SendDELECommand method. Otherwise, it prints a warning to the user:
ElseIf command.ToUpper.StartsWith("DELE") Then If command.Length > 4 Then Dim path As String = command.Substring(5).Trim() If path.Equals("") Then Console.WriteLine("Please specify the file to delete") Else ftp.SendDELECommand(path) End If End If
If the command equals LIST, it invokes the SendLISTCommand method:
ElseIf command.ToUpper.Equals("LIST") Then ftp.SendLISTCommand()
If the command starts with MKD and there is a valid parameter (a directory name), it calls the SendMKDCommand method. Otherwise, a warning displays on the console:
ElseIf command.ToUpper.StartsWith("MKD") Then If command.Length > 3 Then Dim dir As String = command.Substring(4).Trim() If dir.Equals("") Then Console.WriteLine("Please specify the name of the directory to create") Else ftp.SendMKDCommand(dir) End If End If
If the command equals QUIT, it does not do anything as this will be captured by the conditional statement in the While loop:
ElseIf command.ToUpper.Equals("QUIT") Then
If the command starts with RMD and there is a valid parameter (a directory name to remove), it invokes the SendRMDCommand method. Otherwise, a warning displays on the console:
ElseIf command.ToUpper.StartsWith("RMD") Then If command.Length > 3 Then Dim dir As String = command.Substring(4).Trim() If dir.Equals("") Then Console.WriteLine("Please specify the name of the directory to delete") Else ftp.SendRMDCommand(dir) End If End If
If the command starts with RETR and there is a parameter (a filename), the SendRETRCommand method is invoked. Otherwise, it prints a warning on the console:
ElseIf command.ToUpper.StartsWith("RETR") Then If command.Length > 4 Then Dim filename As String = command.Substring(5).Trim() If filename.Equals("") Then Console.WriteLine("Please specify a file to retrieve") Else ftp.SendRETRCommand(filename) End If End If
If the command starts with STOR and there is a parameter (a filename), it invokes the SendSTORCommand method. Otherwise, it prints a warning on the console:
ElseIf command.ToUpper.StartsWith("STOR") Then If command.Length > 4 Then Dim filename As String = command.Substring(5).Trim() If filename.Equals("") Then Console.WriteLine("Please specify a file to store") Else ftp.SendSTORCommand(filename) End If End If
Finally, if the command is none of the valid commands, it simply prints "Invalid command" on the console:
Else Console.WriteLine("Invalid command.") End If
When the user types QUIT, the control exits the While loop, and the SendQUITCommand method is invoked:
ftp.SendQUITCommand()
Before closing itself, the program prints a thank you message.
Console.WriteLine("Thank you for using NETFTP.")
|