ftp-client
Transfer files with FTP and FTPS
https://github.com/flipstone/ftp-client
| LTS Haskell 24.58: | 0.5.1.6@rev:2 |
| Stackage Nightly 2026-09-13: | 0.6.0.0 |
| Latest on Hackage: | 0.6.0.0 |
ftp-client-0.6.0.0@sha256:72c52a387c2103e26e93007a600c08a559fe5373dce1c855db745207fd149508,3345Module documentation for 0.6.0.0
- Network
- Network.FTP
FTP Client
ftp-client is a client library for the FTP protocol in Haskell.
Examples
Insecure
withFTP "ftp.server.com" 21 $ \h welcome -> do
print welcome
login h "username" "password"
print =<< nlst h []
Secured with TLS
withFTPS verifies the server’s certificate chain and host name, so a server
that cannot be validated is refused.
withFTPS "ftps.server.com" 21 $ \h welcome -> do
print welcome
login h "username" "password"
print =<< nlst h []
To talk to a server whose certificate cannot be validated, pass your own settings. Disabling validation leaves the connection encrypted but not authenticated, so anyone on the network path can read the credentials and alter transferred data:
let insecure = def { settingDisableCertificateValidation = True }
withFTPSSettings insecure "ftps.server.com" 21 $ \h welcome ->
print welcome
Changes
Changelog for ftp-client
0.6.0.0
Breaking change. withFTPS now verifies the server’s certificate chain and
host name. Validation was previously disabled, and a caller had no way to enable
it. Reported by @ysangkok in
https://github.com/flipstone/ftp-client/pull/1, which proposed the same fix and
was approved but closed unmerged; this completes that change.
If you connect to a server whose certificate cannot be validated, that
connection will now fail. Use the new withFTPSSettings with
settingDisableCertificateValidation set to keep the previous behaviour
deliberately.
-
withFTPSSettingstakesConnection.TLSSettings, for callers who need to choose their own. -
Data connections now authenticate against the host the control connection was opened to. They previously used the local end of the data socket, which no server certificate can match. This is why enabling validation on the control connection alone was not sufficient: PR #1 changed only that, and on its own would have left every FTPS data transfer unable to validate.
-
Securitynow carries aTLSContext(settings, host, port) so a data connection can reproduce the control connection’s protection.connectTLS,createTLSConnection,withTLSHandleandtlsHandleImpltake the settings or context they need. -
Reply lines are now length limited on both control connections.
connectionGetLinewas called withmaxBound, so a server that never sent a newline could exhaust memory before authentication. The clear-channel handle used unboundedhGetLine, which leftwithFTPreplies – and the plaintext greeting andAUTH TLSreply thatcreateTLSConnectionreads before authenticating – without any limit at all. Both now applymaxReplyLineLength, which is exported, and raiseNetwork.Connection.LineTooLongpast it. -
IO failures during a transfer are no longer reported as a completed one.
recvAll,getAllLineRespandgetMlsxResponseturned anyIOErrorinto a clean end of data, so a reset or timed-out connection produced a truncated result that a caller could not distinguish from a whole one. End of input is now distinguished from failure, and only end of input terminates a read. -
Fixed three descriptor leaks:
createTLSConnectionon a refused greeting, rejectedAUTH TLSor failed handshake; the data handshake, wheresocketToHandlehad already invalidated the socket the release closed; and the active-mode listening socket, which was never closed on success. -
A data transfer that does not complete normally now still consumes the server’s completion reply. Left unread it became the answer to the next command, and every reply after that belonged to the previous command.
That drain is now conditional on a completion reply actually being pending. A transfer the server rejects outright –
PBSZ,PROT,PASVor the transfer command itself – fails with its error reply already consumed, and nothing further is coming, so draining blocked until the server gave up on the connection.PendingCompletion,newPendingCompletionanddrainPendingCompletionare exported, andcreateSendDataCommandandcreateTLSSendDataCommandtake aPendingCompletion. -
TYPE Atransfers now send CRLF as RFC 959 requires.sendType TAdoubled a CR that was already there and appended a record the input did not have, andsendLinesent a bare LF. -
cccandauthare removed. CCC cannot work here – there is no way to downgrade our side of the connection, so the control connection would desynchronise – andauthon its own tells the server to expect a handshake that never happens. Both remain reachable asFTPCommandconstructors. -
getLineRespMaybe,getAllLineResp,toNetworkAsciiandmaxReplyLineLengthare now exported.
0.5.3.1
- Enable the
henforcerplugin andfourmoluunder theciflag. Imports are now qualified per the house style and the source is fourmolu formatted; neither changes the API.
0.5.3.0
-
Export
acct,pbsz,prot,cccandauth. These command wrappers were defined but never exported, unlike every other command wrapper in the module. -
Drop the
transformersdependency. The only module it supplied,Control.Monad.IO.Class, has been inbasesince 4.9. -
Stop deriving
TypeableforFTPException. It has been a no-op since GHC 7.10 and GHC 9.12 warns about it.
0.5.2.0
-
Expose
createSIOHandle,createTLSConnectionandconnectTLSso callers can manage the handle lifecycle themselves rather than going throughwithFTPandwithFTPS. Thanks to @pucsdian. -
Fix multiline response parsing. A response was terminated at the first continuation line whose first three bytes matched the response code, so a reply such as
220-First/220-Second/220 Thirdwas truncated to two lines. Per RFC 959 only the code followed by a space ends a multiline reply; the code followed by a hyphen continues it. A final line consisting of the bare code is also accepted, for servers that omit the trailing space. Thanks to @pucsdian.
0.5.1.8
-
Fix a crash on short response lines.
getResponsecalledheadon the bytes following the response code, so a line shorter than four bytes failed withPrelude.head: empty listinstead of anFTPException. Response lines that do not begin with a three digit code now raiseBadProtocolResponseException. -
Fix a hang when the server closes the connection partway through a multiline response. The read loop had no terminating condition other than the closing code, so it never returned. An exhausted stream now raises
BadProtocolResponseException. The loop terminates, and a reply the server never finished is reported as bad rather than handed back as though it were complete – which would have let a truncated220-greeting read as a successful 220 and letwithFTPproceed against a dead control connection.
0.5.1.7
- Correct the
basebound. The package claimed>= 4.8, i.e. support back to GHC 7.10, but no compiler that old can build it becausecrypton-connectiondoes not exist there. The bound is now>= 4.16, the oldest GHC that is actually tested, andtested-withrecords the full set.
Earlier releases
Prior to 0.5.1.7 this package had no changelog. See the git history at https://github.com/flipstone/ftp-client for changes in earlier versions.