ftp connection in VB.NET

S

Schoo

I have a directory on a server called "content" and my goal is to return a
list of files/dirs (I need to differentiate between the two in the returned
data) from that directory in code. I determined that I can't use a static
directory to do this (the DIR commad) so FTP seems to be the option I want.
I set up IIS on the content server to 'open up' the content folder with no
security (for now) and I can open up IE6 on a workstation and access the
file structure by typeing ftp://<IP Address> into the address line. So, I
know the security allows IE to get in.

Next, I read KB#812404 and created a test project, importing ftp.vb and
ftpwebrequest.vb into the project as a class library. I have stepped
through the process in debug and I am able to follow it as it moves into the
classes so I know I am referencing the classes properly in code. Also, the
Output window is filling with data (much of it I don't understand), so it
appears to be working with the parameters. The problem is that I am not
able to get a list of the files in the directory.

Can anyone tell me what I might be doing wrong? Has anyone had this problem
with FTP connections in .NET before? Any suggestions or pointers to better
VB.NET examples would be appreciated.

Scott

PS. To recreate what I am doing, import the classes from the KB article
into a library, reference it in your main web project and add the following
(simplified) code to a form:

=================================================================
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles MyBase.Load
Dim creator As FtpRequestCreator = New FtpRequestCreator
WebRequest.RegisterPrefix("ftp:", creator)
End Sub

Private Sub cmdFTP_Click(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles cmdFTP.Click
txtContents.Text = fetch(txtFTP.Text)
End Sub

Private Function fetch(ByVal uri As String) As String
Dim req As WebRequest
Dim resp As WebResponse

req = WebRequest.Create(uri)
resp = req.GetResponse

Dim strTest(1) As String
strTest(0) = "-u" & uri
strTest(1) = "-mdir"
Main(strTest)

Dim reader As IO.StreamReader
reader = New IO.StreamReader(resp.GetResponseStream())

Dim retval As String
retval = reader.ReadToEnd()
reader.Close()

End Function
=================================================================
 
P

Peter Huang

Hi Schoo,

Because the sample in the KB is just for demostration purpose,all the
method about the ftp command is being encapsulated in the ftpwebrequest
class, it is not easy to achieve your goal without making change to the
class.

I think the easiest and simplest method to do your job is to make some mere
change as below.
1. Add a property in the ftpwebrequest class
Private _dir As String
Public Property CurrentDirectory() As String
Get
Return _dir
End Get
Set(ByVal Value As String)
_dir = Value
End Set
End Property

2. Change the GetFtpResponse method.
Private Function GetFtpResponse() As WebResponse

<code snippet>
Dim resp_desc As ResponseDescription = ReceiveCommandResponse()

If Not resp_desc.PositiveCompletion Then
Throw New ApplicationException("Data negotiation failed:" +
vbNewLine + m_sbControlSocketLog.ToString())
End If

If m_szServerMethod = "PWD" Then
m_szCmdParameter = Nothing
End If
' this will let us enter the directory CurrentDirectory,
If Not CurrentDirectory = "" Then
SendCommand("CWD", "\" + CurrentDirectory)
resp_desc = ReceiveCommandResponse()
End If
'this will let the dir command
If m_szServerMethod = "LIST" Then
SendCommand(m_szServerMethod, ".")
Else
SendCommand(m_szServerMethod, m_szCmdParameter)
End If
</code snippet>


And then you can list the content in the directory "directoryname" as below.

Imports System.Net
Imports System.IO
Module Module1
Public Sub Main()

Dim Creator As FtpRequestCreator = New FtpRequestCreator
WebRequest.RegisterPrefix("ftp:", Creator)
Dim w As WebRequest = WebRequest.Create("ftp://servername")
w.Method = "dir"
Dim ftp As FtpWebRequest
Try
ftp = Convert.ChangeType(w, GetType(FtpWebRequest))
Catch
ftp = Nothing
End Try
ftp.CurrentDirectory = "directoryname"
Dim r As WebResponse
r = CType(ftp.GetResponse(), FtpWebResponse)
Dim respstream As Stream = r.GetResponseStream()
If (respstream.CanRead) Then
Dim rdr As StreamReader = New StreamReader(respstream)
Dim resp As String = rdr.ReadToEnd()
rdr.Close()
Console.WriteLine(resp)
End If
End Sub
End Module

You may have a try and let me know the result.

Best regards,

Peter Huang
Microsoft Online Partner Support

Get Secure! - www.microsoft.com/security
This posting is provided "AS IS" with no warranties, and confers no rights.
 
S

Schoo

Peter,

Thank you for your input. I changed the code as you recommended and noted
the changes in the original code. Unfortunately I am still not able to get
the directory list returned. I have a question: I expect to see a section
of code that loops through the directories/files and returns them, but I
don't see that. I really expected this to be a little easier than this.
All that needs to be done is to make the connection and have it return the
directories in an array or something. I expect this to work like the DIR
command on file access. Instead, it is difficult even to step through the
code because the classes are large and complex. Studying the code, I can't
figure out where I pick up the directory/file names and their
types.

Anyway, I thought the following output would be useful in diagnosing the
issue we are working on (I had to replace the IP address with another for
security but the format is the same):
===================================================================
FtpWebRequest::ctor(ftp://128.1.1.1/)
USER anonymous

331 Anonymous access allowed, send identity (e-mail name) as password.

___ RESPONSE IS COMPLETE
PASS User@

230 Anonymous user logged in.

___ RESPONSE IS COMPLETE
TYPE I

200 Type set to I.

___ RESPONSE IS COMPLETE
/

500 ' /': command not understood

___ RESPONSE IS COMPLETE
FtpWebRequest::ctor(ftp://128.1.1.1/)
USER anonymous

331 Anonymous access allowed, send identity (e-mail name) as password.

___ RESPONSE IS COMPLETE
PASS User@

230 Anonymous user logged in.

___ RESPONSE IS COMPLETE
PORT 128,5,252,30,17,251

200 PORT command successful.

___ RESPONSE IS COMPLETE
TYPE I

200 Type set to I.

___ RESPONSE IS COMPLETE
CWD \directoryname

550 /directoryname: The system cannot find the file specified.

___ RESPONSE IS COMPLETE
LIST .

150 Opening BINARY mode data connection for /bin/ls.

___ RESPONSE IS COMPLETE
226 Transfer complete.

___ RESPONSE IS COMPLETE
==========================================================

It still seems to be trying to access a file instead of a directory.
Perhaps this is the problem?
Please let me know what you think.

Schoo
 
F

feroze

Can you give more data about what you were expecting to see, and what you
exactly received in the response ?

Also it would help to see the verb you were sending.

thanks

feroze.
 
P

Peter Huang

Hi Schoo,

The return string from the ftp sample is an string similar as below.
10-12-98 05:38AM 65536 aspftp.dll
06-15-04 12:53AM <DIR> mailroot

<DIR> means the mailroot is a directory.

So we have to parse the return string to judge if the mailroot is a
directory.
If it is a directory, we may need to call the FtpRequst again
and set the ftp.CurrentDirectory = "mailroot"
so the ftp client will first enter the \mailroot and then list the content
under the mailroot.

The directoryname is for demostration purpose, we may need to change
according to your senario.
ftp.CurrentDirectory = "directoryname"

e.g. if we want to list the content of the \mailroot
then we can set the ftp.CurrentDirectory = "mailroot" and then set
the Method ="dir" to list the content under the mailroot.
if we want to list the content of the \mailroot\test
we can set the ftp.CurrentDirectory = "mailroot\test" and so on....


Or do you have any concern that set the remote pc's directory as mapped
network driver.
e.g.
net use Z: \\remoteserver\test

so that now we can use the z:\ just as it was in the local machine.

Best regards,

Peter Huang
Microsoft Online Partner Support

Get Secure! - www.microsoft.com/security
This posting is provided "AS IS" with no warranties, and confers no rights.
 
S

Schoo

feroze,

Thank you for your interest, I will give you more detail. Please see my
original message for more background, but I will expand on that here.

I have a test form (FTPTest.aspx) with 2 textbox controls and a button.
The goal is to have the user click the button and the program passes the ftp
address from the first textbox to get a list of directories/files on the ftp
site. Here is the code that runs on the test page when the user clicks the
button:
====================================================
Private Function fetch1(ByVal uri As String) As String
Dim req As WebRequest
Dim resp As WebResponse

req = WebRequest.Create(uri)
resp = req.GetResponse

Dim strTest(1) As String
strTest(0) = "-u" & uri
strTest(1) = "-mdir"
Main(strTest)

Dim reader As IO.StreamReader
reader = New IO.StreamReader(resp.GetResponseStream())

Dim retval As String
retval = reader.ReadToEnd()
reader.Close()

Return retval
End Function
====================================================
From here the code uses the 2 classes that I downloaded from KB#812404:
ftp.vb and ftpwebrequest.vb. Please download them for more information. I
made the changes suggested by Peter Huang in this thread on 6/15/04 which
were changes to ftpwebrequest.vb, but I still get a blank string returned.
Please see the output text which I get from running the code as it stands
right now. I included this output in the message you responded to
(6/15/04). I should also mention that I am able to step through all of the
code and it appears that it is stepping through the classes appropriately,
so I know they are referenced properly.

My goal is to get a string returned with a list of all the files/directories
in the ftp default directory, which I can then parse. At this point I would
be happy to put in a test URI just to get a list. I have been using
ftp.lucasarts.com as a test.

I hope this helps. Maybe you see something we don't! :) Please let me
know.

Schoo
 
S

Schoo

Peter,

OK, I understand that we are looking for a string to parse. I can deal with
that when we get it (that makes sense now).

As far as setting ftp.currentdirectory, I would be happy at this point to
just read the default directory. I tried setting this value to "", "/", "\"
and remarking out the line alltogether, but none of these worked. The
procedure is returning a blank string.

Did you learn anything from the output I sent on 6/15/04?

Please refer to my response in this sting to feroze for a detail on how I am
using the code.

Please let me know what you think the problem is.

Schoo
 
P

Peter Huang

Hi Schoo,

From the information you post before, it seems that we can not enter the
directory named "directoryname"

CWD \directoryname

550 /directoryname: The system cannot find the file specified.

In the ftp session, "cwd \directoryname" means "cd \directoryname" in the
command dos prompt.
As I said before, I use the directoryname for test only, the directory may
not exist on your ftp server's root directory.
e.g.
If I am sure there is a mailroot directory in the root directory on my ftp
server, then the cwd \directoryname will success, and now current directory
will be \mailroot.

Also because the difference between different ftp server, so you may also
try to run the command as cwd /directoryname.
So now we can try to change the code below
' this will let us enter the directory CurrentDirectory,
If Not CurrentDirectory = "" Then
SendCommand("CWD", "\" + CurrentDirectory)
resp_desc = ReceiveCommandResponse()
End If
AS
' this will let us enter the directory CurrentDirectory,
If Not CurrentDirectory = "" Then
SendCommand("CWD", "/" + CurrentDirectory)
resp_desc = ReceiveCommandResponse()
End If


In the main function, we can change the CurrentDirectory as below
ftp.CurrentDirectory = "subdir1/subdir2"

So that now the cwd command will execute as below
cwd /subdir1/subdir2

For detailed information, I think you may need to look into the ftp RFC doc
for reference about the ftp command.
FILE TRANSFER PROTOCOL (FTP)
ftp://ftp.rfc-editor.org/in-notes/rfc959.txt

Best regards,

Peter Huang
Microsoft Online Partner Support

Get Secure! - www.microsoft.com/security
This posting is provided "AS IS" with no warranties, and confers no rights.
 
S

Schoo

Peter,
I appologize if there was a miscommunication... I know this thread is
getting long, so let me reiterate the issue, taking a step back to explain
where I am.

I downloaded the samples off the Microsoft KB#812404, so if you have not
already done so, please start with those. I then modified the files as you
recommended on 6/15/04. In the ftp.vb file (from the KB example) there is a
variable to define ftp.currentdirectory. This appears to be the location of
your "directoryname" variable. As I have stated, I have been changing this
to different values including "", "/" and "\", in an attempt to access a
directory listing of the base directory. None of these are working. I am
attaching an updated output file to the end of this message for your review
as the other one is old. Please let me know if you can tell me anything
based on this output..

The goal here is to return a list of all the files and directories in an FTP
site on one of my servers. I can get the file list by typing ftp://<IP
Address> into the address bar in IE6, so I know the site is configured
properly. What I need now is to get the list of directories and files
through code.

Scott

Here is the output (IP Address changed for security reasons):
=======================================================
USER anonymous

331 Anonymous access allowed, send identity (e-mail name) as password.

___ RESPONSE IS COMPLETE
PASS User@

230 Anonymous user logged in.

___ RESPONSE IS COMPLETE
TYPE I

200 Type set to I.

___ RESPONSE IS COMPLETE
/

500 ' /': command not understood

___ RESPONSE IS COMPLETE
FtpWebRequest::ctor(ftp://1.5.1.1/)
USER anonymous

331 Anonymous access allowed, send identity (e-mail name) as password.

___ RESPONSE IS COMPLETE
PASS User@

230 Anonymous user logged in.

___ RESPONSE IS COMPLETE
PORT 128,5,252,30,14,42

200 PORT command successful.

___ RESPONSE IS COMPLETE
TYPE I

200 Type set to I.

___ RESPONSE IS COMPLETE
LIST .

150 Opening BINARY mode data connection for /bin/ls.

___ RESPONSE IS COMPLETE
226 Transfer complete.

___ RESPONSE IS COMPLETE
=======================================================
 
P

Peter Huang

Hi Scott,

From the log you have post, I can not figure out what is wrong with your
senario.
The log just said that we have logged onto the ftp server and then run the
port and type ftp command, you may refer to the link I have referred to
before about the rfc of ftp protocol. And if we try to run the LIST .
command(which means will list the content in the current directory, but the
list command will need another connection to accept the data return from
the server. So the result means 1. the current directory is empty, or we
did not get the response from the other connection.

Since the ftpclient sample in the KB article is just for demostration, to
reach your goal, we need to do a large change.
I have found another ftp client with source code, which is easier to use.
You may take a look and let me know the result.
VB.NET FTP Client
http://www.freevbcode.com/ShowCode.Asp?ID=4655

e.g. we can reuse the ftp class as below.
Module Module1
Public Sub Main()
Dim cFtp As New cFTP("<servername>", 21)
cFtp.IdVerify("anonymous", "IUser@")
'list the content in the root directory
Console.WriteLine(cFtp.DirList("/"))
'list the contect in the mailroot directory under the root directory
Console.WriteLine(cFtp.DirList("/mailroot"))
'close the ftp connection.
cFtp.Close()
End Sub
End Module


Best regards,

Peter Huang
Microsoft Online Partner Support

Get Secure! - www.microsoft.com/security
This posting is provided "AS IS" with no warranties, and confers no rights.
 
S

Schoo

Peter,

I have downloaded the sample you gave and IT WORKS! So, I am going to take
some time to analyse that and try to attach it to my application. If I have
any other problems, I will start a new thread. Thanks for the link, I think
I am on the right path now. :)

Scott
 
S

Schoo

Actually Peter... if you don't mind, I have been going through the working
code from the link you provided (from freevbcode.com) and I have some
questions.

It appears that the MainForm_Load procedure in Main.vb is simply trying to
see if it can make a connection to the default parameters stored in txt
files on the C: drive. At this point the form does not show a connection
and you still have to connect to the server you want from that point. So,
why does it try to make the connection? Why not just set up the oPM and
oCnM objects and stop?

Scott
 
P

Peter Huang

Hi Schoo,

I think it due to how the programmer designer his program.
From his code, it seems that the ReadFromFile will populate the saved ftp
list into the mCnList, and then DefaultConnection function will find the
default connection in the mCnList and at the time the program did not try
to connect to the server.

If you still have any concern on this issue, please feel free to let me
know.

Best regards,

Peter Huang
Microsoft Online Partner Support

Get Secure! - www.microsoft.com/security
This posting is provided "AS IS" with no warranties, and confers no rights.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top