The Powernet FTP server is a multi-threaded server that can accept multiple FTP connections. The model used is described in SERVERS.FTH. The code for the command channel is based on the Telnet code.
The objectives of the design are to use "not a lot" of RAM, and to keep the code size down by not providing many bells and whistles. A side effect of the low RAM usage is that directory listings are rather slow. Initially, the intention was only to support the requirements of RFC959, the primary FTP specification. The only client that actually works fully in this mode is WS_FTP. In order to support clients that are not so forgiving, a few extra commands are supported. The FTP server has been tested with:
The use of the FATfiler file system code is assumed. Because this is not a fully thread-safe file system and only has a single "working directory" for all threads, this FTP server is not suitable for use with FTP clients that assume a Unix-style operating system. Such clients include the FTP client built into Finder on Mac OS X. FileZilla works well on the Mac, as well as on Linux and Windows.
struct /FTPdata \ -- n
The standard service data structure is extended for FTP.
: cleanFTP \ *sv -- *sv
Clean up an FTP service block before it is released.
: my_ftpState \ -- addr
Holds the state of the FTP system.
: my_ftpPassive? \ -- addr
Holds the passive flag.
: my_ftpBinary? \ -- addr
Holds the binary flag.
: my_ftpQuit? \ -- addr
Holds the QUIT flag.
: my_ftpDataIP \ -- addr
Holds the IP adddress of the data channel.
: my_ftpDataPort \ -- addr
Holds the port number of the data channel.
: my_ftpDataSock \ -- addr
Holds the socket handle of the data channel.
: my_ftpDataState \ -- addr
Holds the state number of the data channel.
: my_ftpDataFile \ -- addr
Holds the handle of the file being read/written.
: my_ftpLine? \ -- addr
Holds true if a complete line can be processed.
: my_ftpLineLen \ -- addr
Holds the current length of the line excluding terminators.
: my_ftpSrc$ \ -- addr
Buffer that holds a source file path as a counted string.
: my_ftpDest$ \ -- addr
Buffer that holds a destination file path as a counted string.
: my_ftpBuff \ -- addr
Buffer that holds FTP data for transfer.
The FTP server establishes its own generic I/O for the data and command channels. The data channel operations are written for minimum RAM usage and use the sockets for buffering.
: checkSocket \ hs -- ior
The ior is returned non-zero if the socket is invalid
or not in TCPS_ESTABLISHED state.
: sockKey? \ hs -- #chars
Return the number of available characters from the socket.
If an error has occurred, 1 is returned.
: sockKey \ hs -- char
Return a character from a socket. On a socket error, an LF
is returned.
: sockType \ caddr len hs --
Send a string/buffer to the socket.
: sockEmit \ char hs --
Send a character to the socket.
: sockCr \ hs --
Send a CR/LF pair to the socket.
The following five words are used to provide generic I/O on the FTP data channel.
: FTPdataKey ( -- char ) my_ftpDataSock @ sockKey ; : FTPdataKey? ( -- flag ) my_ftpDataSock @ sockKey? ; : FTPdataEmit ( char -- ) my_ftpDataSock @ sockEmit ; : FTPdataType ( caddr len -- ) my_ftpDataSock @ sockType ; : FTPdataCr ( -- ) my_ftpDataSock @ sockCr ;
create ConFTPdata \ -- addr ; OUT managed by upper driver
Function despatch table for FTP data channel I/O.
OUT
is managed by the upper level driver.
: [FTPdataIo \ -- ; R: -- ipvec opvec
Redirects console I/O to the FTP data channel.
Use in the form:
[FTPdataIo ... io]
create ConsoleFTP \ -- addr ; OUT managed by upper driver
Function despatch table for FTP command channel I/O.
OUT
is managed by the upper level driver.
: Init-ConsoleFTP \ --
Initialise the command channel. Note that the
FTP socket must have been set up and and the private
service area initialised.
: FTPio \ --
Select the FTP command channel as the console.
: checkFTP \ -- ior
Return non-zero if there is an error in the FTP command
channel.
: -ftpLine \ --
Reset the FTP line input.
: +ftpLine \ --
Mark that a complete line is available.
: ftpBS \ --
The backspace operation for input.
: +ftpCmdChar \ char --
Add the character to the command line being assembled.
: ?FTPacceptable \ --
Process the next service input character on the command channel.
Input is terminated by LF, and CR is ignored. This satisfies
the requirements of DOS, Windows, Unices and the TCP/IP NVT
(Network Virtual Terminal).
1 value FTPdiags? \ -- n
Set this non-zero to get diagnostic information.
: [ftp ( -- ) FTPdiags? if [io consoleio decimal ;
A COMPILER
macro used to surround debug code, and
terminated by FTP]
.
[FTP ." debug message" FTP]
: ftp] ( -- ) io] endif ;
A COMPILER
macro that terminates an [FTP ... FTP]
structure.
: .fdLine \ caddr len --
Display FTP text with leading CR on Forth console. If
FTPdiags?
is set to zero, no action is taken.
Each line of the display is "sort of" in Unix ls format.
---------- 1 owner group 1803128 Jul 10 10:18 ls-lR.Z
d--------- 1 owner group 0 May 9 19:45 Softlib
create months \ -- addr
String containing 3 character text for the months.
: .ftpDate \ --
Display the current directory entry's date in the form:
Mmm dd yyyy
e.g.
Apr 30 2012
: .ftpDirLine \ --
Display a directory entry in FTP format. FTP clients get the
size information from this format.
: .ftpFile \ --
List the file data for the last file found.
: .ftpdir \ --
Display a list of files in FTP format.
: .ftpDirNlst \ --
Display a list of file names in FTP NLST format.
: .ftpResp \ caddr len --
Send the string plus a CR/LF pair to the command socket and
optionally to the console.
: .ftp150 \ --
Return status 150 - about to transfer data
: .ftp200 \ --
Return status 200 - good command.
: .ftp202 \ --
Return status 202 - not needed
: .ftp211 \ --
Return status 211 - not available
: .ftp230 \ --
Return status 230 - user logged in.
: .ftp250 \ --
Return status 250 - good file command.
: .ftp226 \ --
Return status 226 - transfer successful.
: .ftp331 \ --
Return status 331 - password needed
: .ftp350 \ --
Return status 350 - need more info.
: .ftp425 \ --
Return status 425 - can't make data connection.
: .ftp426 \ --
Return status 426 - aborted.
: .ftp450 \ --
Return FTP error code 450 - action not taken.
: .ftp451 \ --
Return FTP error code 451.
: .ftp502 \ --
Return FTP error code 502 command not implemented.
: .ftp504 \ --
Return FTP error code 504.
: .ftp550 \ --
Return FTP error code 550.
: [sm \ -- 0
Starts the definition of a state machine's states.
: smState \ n -- n+1
Defines the next state as an EQU
and increments the
state number.
: sm] \ n --
Finishes the state machine and defines an equate of the
number of states.
The FTP data transfer state machine.
[sm smState ftpDtIdle \ No data transfer in progress smState ftpListening \ PASV mode, wait connection smState ftpConnected \ wait transfer command smState ftpReadSendData \ RETRieve smState ftpRecvWriteData \ STORe smState ftpErrorState \ it's bad if we get here! sm] #FTPsm \ -- n
Select the the FTP data socket states.
: goFTPidle \ -- ftpDtIdle my_ftpDataState ! ; : goFTPlistening \ -- ftpListening my_ftpDataState ! ; : goFTPconnected \ -- ftpConnected my_ftpDataState ! ; : goFTPReadSend \ -- ftpReadSendData my_ftpDataState ! ; : goFTPRecvWrite \ -- ftpRecvWriteData my_ftpDataState ! ;
: umin \ u1 u2 -- u1|u2
Minimum of two unsigned values.
: closeDataFile \ --
Perform an emergency close of the data file if it is open.
: hasConn? \ hs -- ior true | 0
Return true and an ior if the socket has an error or a
completed connection. For a good connection, ior is
non-zero.
: newDataSocket \ -- hs|0
Create a new FTP data socket and set it to listen.
The socket handle is also stored in the FTP service
structure.
: discDataSocket \ --
If open, flush the socket transmit data and disconnect the
FTP data socket. This is a graceful close unless the socket
has failed.
: closeDataSocket \ --
If open, close the FTP data socket. This is not a graceful close.
: termDataStream \ --
Gracefully shut down a STREAM mode data transfer and return
to FTPDTIDLE state.
: closeDataStream \ --
Hurriedly shut down a STREAM mode data transfer and return
to FTPDTIDLE state.
: CheckFTPdata \ -- ior
Return non-zero if the FTP data socket is in error.
: FTPdataFailed? \ -- ior
Return non-zero if the FTP data socket is not in established
state. If not established, the data socket is closed and we
return to FTPDTIDLE state.
: doDTidle \ --
The action in FTPDTIDLE state. Check the data socket. If it
fails, close it.
: doDTlistening \ --
The action in FTPLISTENING state.
: doDTconnected \ --
The action in FTPCONNECTED state.
: doDTReadSend \ --
The action in FTPREADSENDDATA state.
: doDTRecvWrite \ --
The action in FTPRECVWRITEDATA state.
: doDTerror \ --
The action in FTPERRORSTATE state.
create FTPdataActions \ -- addr
A table of xts corresponding to the FTP data channel state.
: doFTPdata \ --
We have a data socket. Execute the action for the state.
: waitDataConnected \ -- ior
Wait for a passive mode data connection to be made. Return
non-zero on failure.
: waitDataEstablished \ -- ior
Wait for a passive mode data connection to get to TCP state
TCPS_ESTABLISHED
. Return non-zero on failure.
An FTP command exists on a single line. The first token identifies the command. Each command identifier is a Forth word which parses any more data needed by the command.
: parse-name \ "text" -- caddr len
Return the next space-delimited string from the input stream.
: parse-path \ "<pathname>" -- caddr len
Return the next space-delimited path name from the input
stream. Clip it to MAX_PATH 1-
bytes.
: getSrcParam \ --
Read the parameter and store it as the source name.
: getDestParam \ --
Read the parameter and store it as the destination name.
: getSrcDir \ --
Read the parameter and store it as the source name. If the
name ends in a '/' character, remove it. Some FTP clients
terminate directory names with a '/', which can confuse
the FAT file system.
: SrcParam \ -- addr len
Return the string saved as the source parameter.
: FTPannounce \ --
Issues FTP signon message.
: +decByte \ u caddr len -- u' caddr' len'
Accumulate the next byte of a comma separated set of numbers,
e.g. 1,25,33,4. The accumulator u is shifted left by
8 bits and the next number in the text is added. The updated
accumulator and remaining text are returned.
: .decByte \ u -- u'
Display the top byte of u as a decimal number and shift
it left by 8 bits.
: .comma \ --
Display a comma.
: FTPabort426 \ -- 426
Close the data socket and data file and command with a 426
response.
: setDataSock \ -- ior
Depending on the mode, check or establish a data connection.
Return non-zero on error.
: .quo ( -- ) [char] " emit ;
Display a double quotes.
: .pwdResp \ --
Display the PWD response with no CR/LF.
: .257SrcResp \ --
A 257 response with the source buffer
Access to FTP is provided through the FTP verbs (words)
USER
PASS
and optionally ACCT
.
defer doFTPuser \ --
Process the FTP USER command. The default accepts any
user. Once the user and password have been confirmed,
the ftpState
variable can be set to 1.
defer doFTPpass \ --
Process the FTP PASS command. The default accepts any
password. Once the user and password have been confirmed,
the ftpState
variable can be set to 1.
defer doFTPacct \ --
Process the FTP ACCT command. The default accepts any
password. Once the user and password have been confirmed,
the ftpState
variable can be set to 1.
: (FTPuser) \ --
The default action performed for the USER command. All users
are accepted.
: (FTPpass) \ --
The default action performed for the PASS command. All passwords
are accepted.
: (FTPacct) \ --
The default action performed for the ACCT command. All accounts
are accepted.
FTP commands all start with a command name ("verb" in FTP
parlance). Each is implemented as a Forth word in the FTPvoc
vocabulary.
vocabulary FTPvoc \ --
Vocabulary holding FTP commands for execution.
' FTPvoc >body @ constant FTPwid \ -- x
Wordlist holding FTP commands for execution.
: FTPinterpret \ --
Interpret the current line containing an FTP command.
also FTPvoc definitions
Start of FTP command definitions.
: USER \ -- ; USER <name>
Handle the USER command.
: PASS \ -- ; PASS <password>
Handle the PASS command.
: ACCT \ -- ; ACCT <password>
Handle the ACCT command.
: SYST \ -- ; SYST
Handle the SYST command.
: STAT \ -- ; STAT
Process the STAT command. Commands with parameters are
rejected.
: HELP \ -- ; HELP [<param]
The HELP command just describes the system.
: NOOP \ -- ; NOOP
The NOOP command just returns good.
: STRU \ -- ; STRU F
Obsolete command - we just accept F.
: MODE \ -- ; MODE S
Obsolete command - we just accept S.
: TYPE \ -- ; TYPE <params>
Process the TYPE command. Valid parameters are
A, A N, I, L 8
: QUIT \ -- ; QUIT
Process the QUIT command.
: ABOR \ -- ; ABOR
Process the ABORt command.
: PORT \ -- ; PORT a1,a2,a3,a4,ph,pl
Process the PORT command. The IP address and port
are saved in my_ftpDataIP
and my_ftpDataPort
.
: PASV \ -- ; PASV
Process the PASV command. RFC 0959 does not document PASV
well. See http://cr.yp.to/ftp/retr.html
for more details.
A successful response is of the form:
227 Entering Passive Mode (a1,a2,a3,a4,ph,pl)
: RETR \ -- ; RETR <filepathname>
Process the RETRieve command. Starts transmission of a server
file to the FTP client.
: CWD \ -- ; CWD dirpath
Process the CWD command.
: XCWD CWD ; \ -- ; XCWD dirpath
Process the XCWD command.
: PWD \ -- ; CWD dirpath
Process the PWD command.
: XPWD PWD ; \ -- ; XCWD dirpath
Process the XPWD command.
: LIST \ -- ; LIST <filespec/dirspec>
Process the LIST command. The parameter is optional.
If there is no parameter, the current directory is
listed. If the parameter is a file, the details of
that file are listed. If the parameter is a directory,
the files and directories are listed in "sort of" the Unix
ls format.
: NLST \ -- ; LIST <filespec/dirspec>
Process the LIST command. The parameter is optional.
If there is no parameter, the current directory is
listed. If the parameter is a directory, only the files
are listed by returning one name per line.
: RNFR \ -- ; RNFR <filespec>
Process the RNFR command, checking that the file exists.
: RNTO \ -- ; RNTO <filespec>
Process RNTO, the second part of the file rename operation.
: DELE \ -- ; DELE <filespec>
Process the file delete command.
: MKD \ -- ; MKD <pathspec>
Process the make directory command.
: RMD \ -- ; RMD <pathspec>
Process the delete directory command.
previous definitions
End of FTP command definitions.
: FTPcommands \ --
Empty the return stack, store 0 in SOURCE-ID
, and
enter interpretation state. FTPcommands
repeatedly
inputs an command line and FTPinterpret
s it. Note that
any task that uses FTPcommands
must initialise 'TIB
,
BASE
, IPVEC
, and OPVEC
.
: FTPservice \ --
The FTP service task launched for each established
Telnet connection.
: #FTPconns \ -- u
Return the number of FTP connections. This is the number
FTP service tasks running. Each one needs two sockets.
: FTPserverPass \ --
One iteration through the FTP server.
: FTPserver \ -- ; stay here forever
The FTP listening task.
0 value FTPtask \ -- 0|task
Returns 0 or the FTP server task if running.
: startFTPserver \ --
Start the FTP server task.
: stopFTPserver \ --
Stop the FTP server.
This code is only compiled if the EQU
ate DIAGS?
is set non-zero.
: +ftp initfatfs . powernet ;
Start the file system and PowerNet.
: .FTPchain \ --
Display data about the FTP sockets.