 |
BorlandTalk.com Borland discussion newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
Hans Frandsen Guest
|
Posted: Thu Oct 16, 2003 11:30 pm Post subject: Example of IdFTPServer.OnChangeDirectory and relative paths |
|
|
Hello
I managed to get OnChangeDirectory to work for my FTP Server using full
paths, but I cannot figure out to make a version that supports relative
paths also. Can anyone help me out please?
My code so far:
procedure TForm1.IdFTPServer1ChangeDirectory(ASender: TIdFTPServerThread;
var VDirectory: String);
var
s : string;
begin
// showmessage (processpath (FTPHomeDirectory, s, ''));
// GetDir(0, s); { 0 = Current drive }
// if (s = FTPHomeDirectory) and (POS ('..', VDirectory) = 0) then
// begin
if FTPRelativePath and (POS ('..', VDirectory) = 0) then
ChDir(FTPHomeDirectory+VDirectory)
else
ChDir(VDirectory);
GetDir(0, s); { 0 = Current drive }
// end;
if FTPRelativePath then
S := ExtractRelativePath (FTPHomeDirectory, S);
VDirectory := S;
end;
Thanks,
Hans
|
|
| Back to top |
|
 |
Remy Lebeau (TeamB) Guest
|
Posted: Fri Oct 17, 2003 2:01 am Post subject: Re: Example of IdFTPServer.OnChangeDirectory and relative pa |
|
|
"Hans Frandsen" <nospam (AT) nospam (DOT) com> wrote
| Quote: | I managed to get OnChangeDirectory to work for my FTP Server
using full paths, but I cannot figure out to make a version that
supports relative paths also. Can anyone help me out please?
|
TIdFTPServer already keeps track of the current full path (and home
directory) for you. That is what the CurrentDir (and HomeDir) property of
TIdFTPServerThread is for. Paths need to be processed in relation to that
property. If the client sends a full path, then you update the VDirectory
parameter with that new path. However, if the client sends a relative path,
then you simply append/trim the CurrentDir using the VDirectory as needed
and then assign the new value to VDirectory.
Is there any particular reason why you are using ChDir() and GetDir()?
Those affect the current working directory for the entire process as a
whole, whereas TIdFTPServer supports multiple client connections at a time
and OnChangeDirectory is triggered on a per-client basis. You should not be
modifying the process's working directory for any good reason. If you are
using the working directory in your other code, then chances are your other
code is not designed very well to begin with, as there are better ways to
work with paths without altering the working directory at all.
Now, with that said, once you take the ChDir/GetDir() out of the picture,
TIdFTPServer already has adequate native parsing of both absolute and
relative paths, so in theory you could just get rid of the OnChangeDirectory
handler altogether, as long as you make sure to update the rest of your code
to use the TIdFTPServerThread CurrentDir property instead of GetDir() when
needed.
However, if you must parse it manually, then you could use something like
the following (untested). This is just a basic implementation, the
TIdFTPServer's native parsing is much more thorough:
procedure TForm1.IdFTPServer1ChangeDirectory(ASender:
TIdFTPServerThread; var VDirectory: String);
var
s : string;
pos: Integer;
begin
if IsDelimiter('/', VDirectory, 1) then
begin
// make sure you set the HomeDir property when the client first
connects
s := ASender.HomeDir;
VDirectory.Delete(1, 1);
end
else
begin
s := ASender.CurrentDir;
end;
// adjust this to append the actual path
// separator that you are really using
if not IsDelimiter('/', s, Length(s)) then s := s + '';
if Copy(1, 2, VDirectory) = '..' then
begin
pos := LastDelimiter('/', s);
if pos > 1 then
begin
if (pos <> 3) or ((pos = 3) and (s[2] <> #5 ) then
begin
s := Copy(1, pos-1, s);
end;
end;
end
// do any other custom parsing as needed
VDirectory := s;
end;
Gambit
|
|
| Back to top |
|
 |
Hans Frandsen Guest
|
Posted: Fri Oct 17, 2003 2:05 pm Post subject: Re: Example of IdFTPServer.OnChangeDirectory and relative pa |
|
|
Thanks for the answer Remy.
I have been unsuccessfull in finding any examples of how to use the
FTPServer, so
I had to figure something out myself.
I don't need to use chdir and getdir. This is just something I tried to get
it working.
It was having problems finding the files. Of course multible connections
should work
also, so I need to get rid of them.
You say I can get rid of the OnChangeDirectory event altgether, but if I
don't
implement it I cannot change path at all. Maybe is is because of my
OnListDirectory
and OnRetrieveFile implementations which are not too good either.
procedure TForm1.IdFTPServer1RetrieveFile(ASender: TIdFTPServerThread; const
AFileName: String; var VStream: TStream);
begin
if FileExists (ExtractFileName(NormalizeFileName(AFileName))) then
VStream :=
TFileStream.Create(ExtractFileName(NormalizeFileName(AFileName)),
fmShareDenyNone)
else
VStream := nil;
end;
procedure TForm1.IdFTPServer1ListDirectory(ASender: TIdFTPServerThread;
const APath: String; ADirectoryListing: TIdFTPListItems);
var
d : TIdFTPListItem;
sr: TSearchRec;
FileAttrs: Integer;
begin
FileAttrs := faAnyFile;
if FindFirst('*', FileAttrs, sr) = 0 then
begin
repeat
// if (sr.Attr and FileAttrs) = sr.Attr then
// begin
d := ADirectoryListing.Add;
d.Size := sr.Size; // doesnt work for files over 2 gb!
d.ModifiedDate := 0;
d.FileName := sr.Name;
if (sr.Attr and faDirectory) = faDirectory then
d.ItemType := ditDirectory
else
d.ItemType := ditFile;
// end;
until FindNext(sr) <> 0;
FindClose(sr);
end;
// showmessage ('connected: path:'+extractfilepath(HomeDirectory)+apath);
end;
If you can supply examples of best use of these to get it working either
with relative
paths or not, dependant on user setting or a setting in my app, it would be
great.
I should be able to browse and download files like I am now. That would be
great
Thanks for your help,
Hans F
"Remy Lebeau (TeamB)" <gambit47.no.spam (AT) no (DOT) spam.yahoo.com> wrote
| Quote: |
"Hans Frandsen" <nospam (AT) nospam (DOT) com> wrote in message
news:3f8f2a05 (AT) newsgroups (DOT) borland.com...
I managed to get OnChangeDirectory to work for my FTP Server
using full paths, but I cannot figure out to make a version that
supports relative paths also. Can anyone help me out please?
TIdFTPServer already keeps track of the current full path (and home
directory) for you. That is what the CurrentDir (and HomeDir) property of
TIdFTPServerThread is for. Paths need to be processed in relation to that
property. If the client sends a full path, then you update the VDirectory
parameter with that new path. However, if the client sends a relative
path,
then you simply append/trim the CurrentDir using the VDirectory as needed
and then assign the new value to VDirectory.
My code so far
Is there any particular reason why you are using ChDir() and GetDir()?
Those affect the current working directory for the entire process as a
whole, whereas TIdFTPServer supports multiple client connections at a time
and OnChangeDirectory is triggered on a per-client basis. You should not
be
modifying the process's working directory for any good reason. If you are
using the working directory in your other code, then chances are your
other
code is not designed very well to begin with, as there are better ways to
work with paths without altering the working directory at all.
Now, with that said, once you take the ChDir/GetDir() out of the picture,
TIdFTPServer already has adequate native parsing of both absolute and
relative paths, so in theory you could just get rid of the
OnChangeDirectory
handler altogether, as long as you make sure to update the rest of your
code
to use the TIdFTPServerThread CurrentDir property instead of GetDir() when
needed.
However, if you must parse it manually, then you could use something like
the following (untested). This is just a basic implementation, the
TIdFTPServer's native parsing is much more thorough:
procedure TForm1.IdFTPServer1ChangeDirectory(ASender:
TIdFTPServerThread; var VDirectory: String);
var
s : string;
pos: Integer;
begin
if IsDelimiter('/', VDirectory, 1) then
begin
// make sure you set the HomeDir property when the client
first
connects
s := ASender.HomeDir;
VDirectory.Delete(1, 1);
end
else
begin
s := ASender.CurrentDir;
end;
// adjust this to append the actual path
// separator that you are really using
if not IsDelimiter('/', s, Length(s)) then s := s + '';
if Copy(1, 2, VDirectory) = '..' then
begin
pos := LastDelimiter('/', s);
if pos > 1 then
begin
if (pos <> 3) or ((pos = 3) and (s[2] <> #5 ) then
begin
s := Copy(1, pos-1, s);
end;
end;
end
// do any other custom parsing as needed
VDirectory := s;
end;
Gambit
|
|
|
| Back to top |
|
 |
Remy Lebeau (TeamB) Guest
|
Posted: Fri Oct 17, 2003 5:21 pm Post subject: Re: Example of IdFTPServer.OnChangeDirectory and relative pa |
|
|
"Hans Frandsen" <nospam (AT) nospam (DOT) com> wrote
| Quote: | You say I can get rid of the OnChangeDirectory event altgether,
but if I don't implement it I cannot change path at all.
|
Yes, you can, because TIdFTPServer already does its own pre-processing of
paths before triggering the OnChangeDirectory event, and stores those
processing results in the thread's CurrentDir property. The
OnChangeDirectory event simply allows you to alter the CurrentDir further,
if needed, for example to implement virtual folders. But it is not a
required event to handle for every single request in general. That is why I
also mentioned in my previous reply that you should be changing the rest of
your code outside of the OnChangeDirectory event to use the thread's
CurrentDir property when you need to access a file/folder, since that
property is always up-to-date with the user's navigation requests.
| Quote: | if FileExists (ExtractFileName(NormalizeFileName(AFileName))) then
|
You should not be using ExtractFileName() at all. AFileName is already a
full path, just use it as-is:
procedure TForm1.IdFTPServer1RetrieveFile(ASender: TIdFTPServerThread;
const AFileName: String; var VStream: TStream);
begin
if FileExists(AFileName) then VStream :=
TFileStream.Create(AFileName, fmOpenRead | fmShareDenyWrite);
end;
| Quote: | if FindFirst('*', FileAttrs, sr) = 0 then
|
You should be specifying a full path to FindFirst(), ie:
sPath := IncludeTrailingBackslash(APath);
if FindFirst(sPath + '*', FileAttrs, sr) = 0 then
//...
| Quote: | // if (sr.Attr and FileAttrs) = sr.Attr then
// begin
|
You should be testing for "." and ".." before adding the item to the
listing.
Gambit
|
|
| Back to top |
|
 |
Hans Frandsen Guest
|
Posted: Fri Oct 17, 2003 9:43 pm Post subject: Re: Example of IdFTPServer.OnChangeDirectory and relative pa |
|
|
Hello again
Once again thank you very much for answering!
In OnListDirectory APath might have this format: "..directoryname/" or
"../" which is weird. Why
does it do that?
I still don't see how I can controll relative path contra full path eg:
c:windowssystem32
or if homedir was and relative path c:windows
system32
I get some weird results in FlashFXP ftp client.
You should never be able to browse backward longer than to homedir.
Also setting homedir after login does not seem to work.
Here is my code so far:
procedure TForm1.IdFTPServer1ListDirectory(ASender: TIdFTPServerThread;
const APath: String; ADirectoryListing: TIdFTPListItems);
var
d : TIdFTPListItem;
sr: TSearchRec;
FileAttrs: Integer;
begin
FileAttrs := faAnyFile;
if FindFirst(IncludeTrailingBackslash(APath)+'*', FileAttrs, sr) = 0 then
begin
repeat
if (sr.Name <> '.') and (sr.Name <> '..') then
begin
d := ADirectoryListing.Add;
d.Size := sr.Size; // doesnt work for files over 2 gb!?
d.ModifiedDate := 0; // todo
d.FileName := sr.Name;
if (sr.Attr and faDirectory) = faDirectory then
d.ItemType := ditDirectory
else
d.ItemType := ditFile;
end;
until FindNext(sr) <> 0;
FindClose(sr);
end;
end;
procedure TForm1.IdFTPServer1ChangeDirectory(ASender: TIdFTPServerThread;
var VDirectory: String);
begin
// do nothing
end;
procedure TForm1.IdFTPServer1RetrieveFile(ASender: TIdFTPServerThread; const
AFileName: String; var VStream: TStream);
begin
if FileExists(AFileName) then
VStream := TFileStream.Create(AFileName, fmOpenRead + fmShareDenyWrite)
else
VStream := nil;
end;
procedure TForm1.IdFTPServer1AfterUserLogin(ASender: TIdFTPServerThread);
begin
ASender.HomeDir := FTPHomeDirectory;
end;
I guess it's almost working now. I hope you can help me with the last
questions.
Thanks,
Hans F.
|
|
| Back to top |
|
 |
Remy Lebeau (TeamB) Guest
|
Posted: Fri Oct 17, 2003 10:30 pm Post subject: Re: Example of IdFTPServer.OnChangeDirectory and relative pa |
|
|
"Hans Frandsen" <nospam (AT) nospam (DOT) com> wrote
| Quote: | In OnListDirectory APath might have this format:
"..directoryname/" or "../" which is weird. Why
does it do that?
|
Because TIdFTPServer defaults to using "/" instead of "" for path
separators, and IncludeTrailingBackslash() only looks for "". TIdFTPServer
will only use "" if its EmulateSystem property is set to ftpsDOS.
Otherwise, you'll have to replace the call to IncludeTrailingBackslahs() to
append "/" manually when needed. Alternatively, use StringReplace() to
replace all "/" with "".
| Quote: | I still don't see how I can controll relative path contra full path eg:
c:windowssystem32
or if homedir was and relative path c:windows
system32
|
Set the thread's HomeDir property to "c:windows" when the client initially
connects, and then simply append the HomeDir to the front of the specified
path if its first character is set to "/" or "".
| Quote: | You should never be able to browse backward longer than to homedir.
|
By default, the CurrentDir property is expressed as a path relative to "/"or
"". When the user tries to navigate past "/", obviously it can't, there's
nothing left of the path. It is your responsibility to match "/" to the
HomeDir when needed.
| Quote: | Also setting homedir after login does not seem to work.
|
TIdFTPServer itself does not actually use HomeDir for anything, it is for
your own use.
| Quote: | Here is my code so far:
|
Try this instead:
// this function may need a more thorough implementation
function RelativeToAbsolutePath(const ABase: String; const APath:
string): string;
begin
Result := StringReplace(APath, '/', '', [rfReplaceAll]);
if IsPathDelimiter(Result, 1) then Result :=
IncludeTrailingBackslash(ABase) + Copy(2, Length(Result), Result);
Result := IncludeTrailingBackslash(Result);
end;
procedure TForm1.IdFTPServer1ListDirectory(ASender: TIdFTPServerThread;
const APath: String; ADirectoryListing: TIdFTPListItems);
var
d : TIdFTPListItem;
sr: TSearchRec;
FileAttrs: Integer;
sPath: string;
begin
sPath := RelativeToAbsolutePath(ASender.HomeDir, APath);
if FindFirst(sPath + '*', faAnyFile, sr) = 0 then
begin
repeat
if (sr.Name <> '.') and (sr.Name <> '..') then
begin
d := ADirectoryListing.Add;
d.Size := sr.Size;
d.ModifiedDate := FileTimeToDateTime(sr.Time);
d.FileName := sr.Name;
if (sr.Attr and faDirectory) = faDirectory then
d.ItemType := ditDirectory
else
d.ItemType := ditFile;
end;
until FindNext(sr) <> 0;
FindClose(sr);
end;
end;
procedure TForm1.IdFTPServer1RetrieveFile(ASender: TIdFTPServerThread;
const AFileName: String; var VStream: TStream);
var
sFileName: string;
begin
sFileName := RelativeToAbsolutePath(ASender.HomeDir, AFileName);
if FileExists(sFileName) then VStream :=
TFileStream.Create(sFileName, fmOpenRead or fmShareDenyWrite);
end;
procedure TForm1.IdFTPServer1AfterUserLogin(ASender:
TIdFTPServerThread);
begin
ASender.HomeDir := FTPHomeDirectory;
end;
Gambit
|
|
| Back to top |
|
 |
Hans Frandsen Guest
|
Posted: Sat Oct 18, 2003 1:03 pm Post subject: Re: Example of IdFTPServer.OnChangeDirectory and relative pa |
|
|
Hello Remy
EmulateSystem is set to ftpsDOS, which is default, but still the forward
slashes appears
in the paths!? Don't know if this is a bug or what, but like in your
RelativeToAbsolutePath
I fixed it with my NormalizeFileName.
I tried using your code, but I get pretty much same results regarding
relative path, but worse
when regarding browsing :(
At first when I connect to ftp server with flashfxp the folder is displayed
as blank.
If I enter a directory, it is displayed as "..folder name", strange thing
is that it has
added ".." infront. Browsing downward through the foldertree works fine,
but if I go back
one folder, I end up in root ".." and not just one folder up. Also it
doesn't start in homedir
if set, it starts in root folder of drive where projekt is located.
FileTimeToDateTime cannot be found on my system. Is this misspelled or
something?
I think all the problems are related to the ".." which added, plus it ends
up having many
backslashes in arow after forward slashes are changed. Some times as much as
4 or 5.
I hope you can help me with these last details.
Thanks,
Hans
|
|
| Back to top |
|
 |
Remy Lebeau (TeamB) Guest
|
Posted: Mon Oct 20, 2003 3:22 am Post subject: Re: Example of IdFTPServer.OnChangeDirectory and relative pa |
|
|
"Hans Frandsen" <nospam (AT) nospam (DOT) com> wrote
| Quote: | EmulateSystem is set to ftpsDOS, which is default,
but still the forward slashes appears in the paths!?
|
I do not see how that is possible. TIdFTPServer is specifically coded to
always look for ftpsDOS whenever it manipulates paths so that it can use ""
instead of "/".
| Quote: | At first when I connect to ftp server with flashfxp the
folder is displayed as blank.
|
Again, not possible, at least from the FTPServer's perspective. The path is
always initialized as "/", so if you are getting a blank path, then you must
be setting the path to a blank value yourself.
| Quote: | FileTimeToDateTime cannot be found on my system. Is this
misspelled or something?
|
It should have been FileDateToDateTime(), sorry.
Gambit
|
|
| Back to top |
|
 |
Remy Lebeau (TeamB) Guest
|
Posted: Mon Oct 20, 2003 4:06 am Post subject: Re: Example of IdFTPServer.OnChangeDirectory and relative pa |
|
|
"Remy Lebeau (TeamB)" <gambit47.no.spam (AT) no (DOT) spam.yahoo.com> wrote
| Quote: | Again, not possible, at least from the FTPServer's perspective.
The path is always initialized as "/", so if you are getting a blank
path, then you must be setting the path to a blank value yourself.
|
To expand on this, my testings with TIdFTPServer tonight suggest that you
need to set the CurrentDir property yourself on initial client connection,
such as in the OnAfterUserLogin event, otherwise the path will be displayed
as blank in the client. I do not know why, considering that the CurrentDir
is initialized to "/" upon connection.
Gambit
|
|
| Back to top |
|
 |
Hans Frandsen Guest
|
Posted: Mon Oct 20, 2003 8:41 am Post subject: Re: Example of IdFTPServer.OnChangeDirectory and relative pa |
|
|
I think I have it all working now.
The mix of backslash and forwardslash is still here, but I replace them
all and remove duplicates so there are only one left at a time.
I'm pretty sure this is a problem with Indy 9 and not my code, but I
can live with it and code should still work if it was fixed in 9 or the new
10 when done.
Thanks alot for all the help! Apreciate it!
Hans
"Remy Lebeau (TeamB)" <gambit47.no.spam (AT) no (DOT) spam.yahoo.com> wrote
| Quote: |
"Remy Lebeau (TeamB)" <gambit47.no.spam (AT) no (DOT) spam.yahoo.com> wrote in
message
news:3f93545c (AT) newsgroups (DOT) borland.com...
Again, not possible, at least from the FTPServer's perspective.
The path is always initialized as "/", so if you are getting a blank
path, then you must be setting the path to a blank value yourself.
To expand on this, my testings with TIdFTPServer tonight suggest that you
need to set the CurrentDir property yourself on initial client connection,
such as in the OnAfterUserLogin event, otherwise the path will be
displayed
as blank in the client. I do not know why, considering that the
CurrentDir
is initialized to "/" upon connection.
Gambit
|
|
|
| Back to top |
|
 |
Remy Lebeau (TeamB) Guest
|
Posted: Mon Oct 20, 2003 10:01 am Post subject: Re: Example of IdFTPServer.OnChangeDirectory and relative pa |
|
|
"Hans Frandsen" <nospam (AT) nospam (DOT) com> wrote
| Quote: | The mix of backslash and forwardslash is still here
snip |
Please put together a sample application, complete with functional event
handlers and property setup, that reproduces the problem(s) you are seeing,
and then post the project to the .attachments group so people can study it
to find out where the problems are.
Gambit
|
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|