NetTalk FTP
Introduction
FTP is one of the oldest protocols on the internet. It is used to transfer files between and FTP Client and an FTP Server. NetTalk
includes an FTP Client Class which allows you to build FTP Client functionality into your Clarion program.
FTP is different to most other protocols because it involves two connections between the server and the client. The first connection
is known as the Control Connection and commands, and responses, flow on this connection. However when data needs to flow, a second
connection, known as the Data Connection is created. The actual file then travels on the Data connection.
Originally the data connection was opened by the server connecting to the client. While this worked well at the beginnings
of the internet, it requires special network configuration to allow this to happen. As the internet matured this reverse-connection
because a security hazard and most companies don't support it.
In order to extend the life of the FTP protocol a new approach, allowing the client to create the data connection to the server,
was invented. This approach is known as Passive mode. In builds before 8.29 the default for the object was Active mode. From build 8.29
the default is passive mode.
As security became a higher and higher priority the need to make the FTP
connection secure became important. The protocol was thus further extended
to add support for SSL (FTPS and FTPES) and support for SSH (SFTP). NetTalk
supports FTP over SSL (FTPS and FTPES) but does not support FTP over SSH.
NetTalk provides two classes which are used in the FTP support. The
NetFtpClientControl class handles the control connection, and the
NetFtpClientData class handles the data connection.
The FTP client
is always a Window procedure, because NetTalk communications are
asynchronous and thus need an ACCEPT loop, which in turn needs a window
structure. If the procedure is completely automatic then the window itself
can be hidden from the user.
Jump Start
This section gets you going as quickly as possible to add FTP functionality to your application.
- Make sure the Activate NetTalk Global extension has been added to the
application.
- Make sure the Activate StringTheory Global Extension has been added to
the application.
- Check out the ftpDemo app in the \examples\nettalk\ftp\FTP
Functions folder. This contains some example procedures which you may
be able to use in your application either "as is" or with modifications. See
the Examples section below for more on this example.
- If you want to construct an FTP procedure of your own, then the
following basic steps are pretty much required;
- Create a procedure based on the Window template
- Add a NetTalk Object Extension to the procedure, Object Name
ThisFtpControl and Base Class
NetFTPClientControl. On the Settings tab set
the Data Object to ThisFTPData
- Add a NetTalk Object Extension to the procedure, Object Name
ThisFtpData and Base Class
NetFTPDataControl. On the Settings tab set
the Control Object to ThisFTPControl
- Either set the Passive mode setting on the ThisFTPControl Settings
tab, or, in embed code, set the
thisFtpControl.PassiveMode property before the call to
ThisFTPControl.init. If this step is omitted
then the connection will default to Passive mode.
- Add some trigger event to the window (usually a button). Add code to
this button which primes all the required parameters (see Before Connecting below) and then calls
the first command you wish to execute (see Commands
below.)
- IIn the ThisFTPControl.Done method, add
code to execute when a command completes. (See Done below.)
- IIn the ThisFTPControl.ErrorTrap method
add code to deal with errors - often culminating in a
Post(Event:CloseWindow).
Before Init
The fundamental way the object works changes when it is in passive mode or active mode. Thus it is necessary to set the
PassiveMode property before the control object .INIT method is called.
The other properties can be set later, before the first command, but the
PassiveMode property must be set very early. The default value is 1, so if
this property is not set, or is set too late, then the FTP object will
assume that passive mode is required.
Before Connecting
As with most connections you connect to a server by specifying a host name and port number. If the server is not in passive
mode you should also set that and if the server is to connect over SSL then that needs to be set as well.
The server address is placed in the ServerHost
property. The server
port number is placed in the ControlPort property.
If the server supports
passive mode then the PassiveMode property is set to
True. This is the
default, so you don't have to set it. If the server does not support passive
mode then you need to set this property to False.
If you wish to
connect over SSL then call the SetFtpType method with one of the possible equates;
Ftp:NoSSL
Ftp:ImplicitSSLControl
Ftp:ImplicitSSL
Ftp:ExplicitSSL
The SetFTPType method takes the Data connection object as the second
parameter.
FTP is an authenticated protocol, meaning that a user name and password are required before you can start
transferring files.
Some servers allow the use of a "generic" login, called an anonymous login, where you can use some documented login and password,
however it's important to note that even these sites do require a login and password to be passed to the server.
The user login is placed in the property called
User, and the password is placed in the
property called Password. If these change
during a connection then set the property
OpenNewControlConnection to force the current control connection to
close and a new one to be opened with the next command.
If you are
connecting with SSL then all the normal SSL properties apply to both the
control connection and the data connection.
Example
This is an example of setting all the above settings before the Control
object is Initialized. In the code below thisFtpControl is the control
object, and thisFtpData is the data object.
thisFtpControl.serverHost
= 'ftp.capesoft.com'
thisFtpControl.ControlPort = 21
thisFtpControl.PassiveMode = true
thisFtpControl.SetFtpType(FTP:ImplicitSSL,thisFtpData)
thisFtpControl.User = 'someone'
thisFtpControl.Password = '1234'
thisFtpControl.openNewControlConnection = true
FtpControl.SSLCertificateOptions.CertificateFile =
''
FtpControl.SSLCertificateOptions.PrivateKeyFile = ''
FtpControl.SSLCertificateOptions.DontVerifyRemoteCertificateCommonName =
true
FtpControl.SSLCertificateOptions.DontVerifyRemoteCertificateWithCARoot =
true
FtpControl.SSLCertificateOptions.CARootFile = 'CA_Roots.pem'
FtpData.SSLCertificateOptions.CertificateFile = ''
FtpData.SSLCertificateOptions.PrivateKeyFile = ''
FtpData.SSLCertificateOptions.DontVerifyRemoteCertificateCommonName = true
FtpData.SSLCertificateOptions.DontVerifyRemoteCertificateWithCARoot = true
FtpData.SSLCertificateOptions.CARootFile = 'CA_Roots.pem'
Connecting
There is no need to explicitly connect to the server. The object will automatically connect as required when
a command is executed.
Control Template
From version 8.29 the control template has been deprecated. It still exists, and still works, but is not a
recommended approach
for use anymore. See the
JumpStart section for more information on adding FTP procedures to your application.
Commands
FFTP is designed to be a remote-file-system, meaning that the files are
organized with directories, and sub-directories and so on.
While you will primarily be sending and receiving files there are a number of commands you can use to navigate and manage the server.
The commands supported by the FTP object are listed here;
FTP Command | NetTalk Method | Comments |
abor | Aborting() | Sends an Abort command to the server.
This terminates any file being uploaded or downloaded. The
CommandQueue is emptied. No call to
Done or ErrorTrap
for the existing, or waiting commands will be called. A call to
Done with the _Command
property set to aborting will be made instead. |
appe | AppendToFile(LocalFileName, RemoteFileName) | Similar
to the PutFile, but does an append instead of an overwrite. It transfers the
local file specified by LocalFileName from the local machine to the FTP
Server, and stores it as RemoteFileName. If a file with the same path and
name as RemoteFileName already exists, the
transferred data is appended to the end of the existing file. If no such
file exists, a new file is created. Will either call Done or ErrorTrap or success or failure. |
cdup | ChangeDirUp() | Changes the remote current working
directory to one level higher in the directory tree. Will either call Done or ErrorTrap or success or failure. |
cwd | ChangeDir(DirectoryName) | Changes the remote current
working directory to DirectoryName. Will either call
Done or
ErrorTrap or success or failure. |
dele | DeleteFile(RemoteFileName) |
Deletes the remote file specified by
RemoteFileName. Will either call
Done or ErrorTrap or success or failure. |
list | GetDirListing(DirectoryName) | Retrieves a list of
contents of the remote directory specified by DirectoryName
and stores
them in self.DirListingQ property. To get contents of the current working
directory, pass an empty string as the parameter. For more information on
the DirListingQ property see Directory Listings
below.
By setting the
FullListing property to false, you will receive a list of file or directory
names only. When FullListing is true
(the default), full information on the files will be
retrieved.
Note that DirectoryName can also contain a remote file name.
In this case, current information on the specific file will be returned.
Note: Many FTP Servers are case sensitive
Examples:
GetDirListing ('') GetDirListing ('mydirectory')
GetDirListing ('myfile.exe') GetDirListing ('m*')
GetDirListing ('m*.exe')
Will either call
Done or ErrorTrap on success or failure.
|
size | GetSize(RemoteFileName) | Gets the size of a specific
file on the server. The result is placed in the
ExpectedFileSize property. Note that not all servers support this
call - if it fails ErrorTrap will not be called.
If it does fail then the code will check the current
DirListingQ to get a size from there. In all cases the
Done method is called once the command has
completed. |
mkd | MakeDir(DirectoryName) | Pass the name of the new
remote directory to be created as a parameter. Will either call
Done or ErrorTrap or success or failure. |
noop | Noop() | Not usually called by the program. Used
internally. NOOP is a non-operational command designed to do nothing. It is
useful for preventing a FTP Control Connection from closing. |
pwd | GetCurrentDir() | Returns the current directory name
on the server. The reply is placed in the CurrentDirectory
property. Will either call
Done or ErrorTrap or success or failure. |
quit | Quit() | This method will allow for all the data
transfer in process to complete and then disconnect you from the FTP Server.
Will either call
Done or ErrorTrap on
success or failure. |
retr | GetRemoteFile(RemoteFileName,LocalFileName) | Transfers the
remote file specified by RemoteFileName from the FTP Server to the local
machine and stores it in LocalFileName. If a file with the same path and
name as LocalFileName already exists, it will be overwritten by the received
file. A new file is created, otherwise.
Will either call Done or
ErrorTrap or success or failure. |
rnfr / rnto | Rename(RemoteName, NewName) | Renames the
remote file specified by RemoteName to
NewName. This method can also be
used to rename a directory. Will either call Done or
ErrorTrap or
success or failure. |
rmd | RemoveDir(directory) | Removes the remote directory
specified by DirectoryName. Will either call
Done or ErrorTrap on
success or failure. |
stop | Stop() | Immediately terminates the data connection. |
stor | PutFile(LocalFileName,RemoteFileName) | Transfers the
local file specified by LocalFileName from the local machine to the FTP
Server, and stores it as RemoteFileName. If a file with the same path and
name as RemoteFileName already exists, it will
be overwritten by the file being transferred. If the file does not already
exist, then it is created. Will either call
Done or ErrorTrap on
success or failure. |
syst | System | Gets a OS type from the server. the result
is placed in the SystemType property. |
type | SetType(Type) | The type is either A for ascii, or I
for binary. Not usually called by the program, is handled internally. Will
either call
Done or ErrorTrap on
success or failure. |
Properties
A number of properties affect how the FTP Client will behave. Other
properties are the result of commands.
Property | Description |
BinaryTransfer | If set to true then all file transfers will be in
binary mode. If false transfers will be in ASCII mode. The default is binary
mode, and there is almost never a requirement to transfer in ASCII mode. |
BytesLeftToReceive | When receiving a file, contains the amount of
the file which still has to arrive. |
BytesLeftToSend | When sending a file, contains the number of
bytes that have not been sent yet. |
_Command | Contains the command (as called in the command list)
which has just completed. Typically used in the Done or ErrorTrap methods.
Is also set at the start of the calls to PutFile or GetRemoteFile. Note that
this property is always in lower case. |
CurrentDirectory | Set after a call to the
GetCurrentDir command. |
DirListingQ | A queue containing the remote directory contents
after a call to GetDirListing. See
Directory Listings for more information. |
ExpectedFileSize | The size of the file being retrieved. Populated
by a call to GetSize, which in turn will use the
existing DirListingQ property if the server does not support the SIZE
command. |
ForwardSlashes | UNIX operating systems use the forward slash (/)
instead of the backslash (\) when separating directories and file names.
Since FTP originated on UNIX most FTP servers (even when running on windows)
follow this convention. If this property is true then all backslashes in
remote file name parameters will automatically be converted to forward
slashes before executing the command. |
PassiveMode | This needs to be set before the object
INIT method is called. (In other words, it's
called much earlier than any other setting). The default value is true. |
ProgressControl | The Use Equate of a progress bar control on the
window. Can be used to display the progress of file uploads and downloads.
See Progress Control for more information. |
SystemType | A text string containing the System Type as
identified by the server. A command to fetch this is called automatically
when logging into the server. See System. |
| |
Done
As motioned earlier the communications between the server and the client is
asynchronous. This means that you send a command, but you cannot immediately
test for the response to that command. Rather when the command completes
successfully the .Done method will be called. So if you wish to add more
commands at this point then you can do so.
Inside the done method you
can test the ._command property which will return the name of the command
which just completed. This property is in lower case, and matches the
NetTalk Method Name in the table above.
If the command is
unsuccessful then the .ErrorTrap method is called instead of the
.Done
method.
Example
case
self._command
of 'makedir'
self.changedir(somedir)
end
Important: Some commands that you
call will generate other commands internally. For example when doing a
Putfile a SetType
command is executed first. In this case you'll get a call to
Done first for SetType
and then later on for PutFile. So your
code in the Done method should be aware that
more calls to Done may be called than commands
you have executed.
ErrorTrap
The ErrorTrap method is called when an error occurs. when this happens the
Done method will not be called.
Directory Listings
The format of the directory listing is not specified by the protocol. So the
exact format of the text which is returned depends on the specific server,
and specific OS of the server. NetTalk does a reasonable job of deformating
this reply and placing the result in a property called .DirListingQ.
The Queue is declared as follows;
Net:FTPDirListingQType Queue, Type
Name
string(FILE:MAXFILENAME)
Shortname
string(13)
Date
long
Time
long
Size
long
Attrib
byte
NoOfLinks
long
Owner
string(50)
GroupOwnership
string(50)
AccessPermissions
string(50)
end
In your program you can declare your own queue of this
type and as long as it has the same structure, you can set the object to use
your queue rather than the built-in one. For example, in the program
code;
MyQueue
Queue
Name
string(FILE:MAXFILENAME)
Shortname
string(13)
Date
long
Time
long
Size
long
Attrib
byte
NoOfLinks
long
Owner
string(50)
GroupOwnership
string(50)
AccessPermissions
string(50)
end
thisFtpControl.DirListingQ &= MyQueue
This means that the call to GetDirListing will result in your queue
being automatically populated with the result. Since the queue is declared
in your procedure it is also easy to assign it to a LIST control on the
window.
Unknown format of directory listing
If the object is
unable to interpret the reply from the server then an error will be
generated "Unknown format of directory listing". It is quite unusual
to find a server that uses a format that NetTalk can't figure it out.
If you don't want this error to be called then put the following code in
the virtual method _FigureOutDirFormat() after
the parent call:
if self._DirListingFormat = 0
self._DirListingFormat = 255
end
You will need to figure
out the directory listing yourself in this case. The way that it works is
that the _ProcessGetDirListing() method is
called when data is received. If there is not enough information to figure
out the format then the _DirFormatSet property
is set to zero and the data is stored.
When there is sufficient
information from the server the _DirFormatSet
property is set to 1 and _FigureOutDirFormat()
is called. If this method manages to figure out the directory format then it
sets the _DirListingFormat property, otherwise
it sets it to zero.
This is what is happening in this case - enough
data has been received to figure out the format, but the format itself is
unknown. In order to add support for the custom format add code to the
_FigureOutDirFormat() embed to process the
directory listing after the parent call (the parent method could not figure
it out, which is what caused the error).
The _FigureOutDirFormat()
method is passed a string which contains the directory list (what you would
see printed out in a terminal if you were connecting to the FTP server on
the command line and the LIST command was issued to the server).
NetTalk allows you to process the listing and set the _DirListingFormat
to a number between 200 and 255, which means that it is in a custom format.
You then need to process this string in the _FillDirListingQ_FormatCustom()
virtual method that NetTalk provides for you.
It might be helpful to
have a look at the source code for the methods such as
_FillDirListingQ_Format01() to see how they process the string into
the queue and then write your own
FillDirListingQ_FormatCustom() to process your specific server's
response. You might find that it is quite close to one of the currently
supported formats. The full source code for the FTP objects is in the
NetFtp.clw and NetFtp.inc
files in your \Clarion\Accessory\Libsrc\win
folder.
Progress Control
SomSome of the operations, specifically
PutFile and GetRemoteFile
can take some time to complete. The class has a property called
.ProgressControl which can be set to the use
equate of a progress bar on the screen. If this property is set then the
control (ie a progress bar) will be updated as the file upload, or download,
happens.
For example
thisFtpControl.ProgressControl = ?Progress1
Three additional
properties are available if you wish to display the actual values. They are;
.ExpectedFileSize
.BytesLeftToSend
.BytesLeftToReceive
Examples
NetDemo.App
In \Examples\NetTalk\Demo folder.
This example
contains a window procedure called TestFTPClient. It is a highly visual
example of the FTP Client class which allows you to navigate an FTP server,
experiment with settings, and so on. It is an excellent way to get a feel
for FTP, and to test the behavior against a specific FTP server.
FTPDemo.App
In
\Examples\NetTalk\FTP\ABC folder.
This application contains examples of various "stand-alone" functions
that you might want to use in your application. These functions can be
imported into your own app for your own use. Current functions are;
function | use |
FtpFile | Write a file to an FTP server, or read a file from
an FTP server. Note that this function does one specific task - it's not
a good starting point if you need to perform a number of tasks against
the FTP server. |
FtpDirectory | Write all the files in a directory to the FTP
server, or read all the files from a directory on the FTP server. |
GetFileOverFTP | An example of calling the FtpFile function to
fetch a specific file from a specific server. |
GetDirectoryOverFTP | An example of calling the FtpDirectory
function to fetch a specific directory from a specific server. |
SendFileOverFTP | An example of calling the Ftpfile function
to write a specific file to a specific server. |
SendDirectoryOverFTP | An example of calling the FtpDirectory
function to write a specific directory to a specific server. |
| |
FTPLegacy.App
In \Examples\NetTalk\FTP\Legacy folder.
Same as FtpDemo.App above, but based on legacy template chain.
FAQ
1. How do I upload multiple files using FTP?
You can call
.PutFile multiple times. The commands will be
queued and executed one at a time.
[End of this document]