ftp(3)

πŸ“¦
version 29.0.6 Β· 2 versions
, in nixos 26.05, nixpkgs unstable, ubuntu stonking

🌸 ftp β€” Erlang Module Definition 🌸

okay so bestie, welcome to ftp(3), the Erlang module page. yes the manual section is 3 and yes this is the Erlang Module Definition. grab a snack, we're doing this πŸ’…


✨ NAME ✨

ftp β€” A File Transfer Protocol client. that's it, that's the girl πŸ¦‹


πŸŽ€ DESCRIPTION πŸŽ€

This module implements a client for file transfer according to a subset of the File Transfer Protocol (FTP), see RFC 959. lowkey a whole classic πŸ“Ό

The FTP client always tries to use passive FTP mode and only resorts to active FTP mode if this fails. This default behavior can be changed by start option mode. so it's passive-first, no cap ✨

For a simple example of an FTP session, see FTP User's Guide. go read it, it's giving tutorial energy πŸ“–

The return values of the following functions depend much on the implementation of the FTP server at the remote host. In particular, the results from ls and nlist varies. Often real errors are not reported as errors by ls, even if, for example, a file or directory does not exist. nlist is usually more strict, but some implementations have the peculiar behaviour of responding with an error if the request is a listing of the contents of a directory that exists but is empty. πŸ’€ servers are gonna server, bestie

😭 Errors 😭

The possible error reasons and the corresponding diagnostic strings returned by formaterror/1 are as follows:

  • echunk β€” Synchronization error during chunk sending according to one of the following:
    • call is made to send_chunk/2 or send_chunk_end/1 before a call to send_chunk_start/2.
    • call has been made to another transfer function during chunk sending, that is, before a call to send_chunk_end/1.

so basically you skipped a step and the vibe check failed πŸ™ˆ

  • eclosed β€” The session is closed. it's over, she's gone πŸ’”
  • econn β€” Connection to the remote server is prematurely closed. rude of them tbh 😀
  • ehost β€” Host is not found, FTP server is not found, or connection is rejected by FTP server. can't find her, she's not answering πŸ«₯
  • elogin β€” User is not logged in. log in first bestie πŸšͺ
  • enotbinary β€” Term is not a binary.
  • epath β€” No such file or directory, or directory already exists, or permission denied.
  • etype β€” No such type.
  • euser β€” Invalid username or password. who even are you 😭
  • etnospc β€” Insufficient storage space in system [452].
  • epnospc β€” Exceeded storage allocation (for current directory or dataset) [552].
  • efnamena β€” Filename not allowed [553].

🧚 DATA TYPES 🧚

client() :: pid().

yep, a client is just a pid. tiny, iconic, powerful πŸ‘‘


πŸ‘‘ FUNCTIONS πŸ‘‘

heads up bestie: a LOT of these have the same deprecation notice, so I'm gonna say it once here and then keep the exact notice on each one because facts are facts. πŸ™ Deprecated: the legacy protocol support will be dropped in OTP-30, use a more modern approach for file transfer as for instance SFTP (SSH File Transfer Protocol).

account/2

account(Client :: client(), Acc :: string()) ->
                 ok | {error, Reason :: term()}.

Deprecated: ftp:account/2 is deprecated; Legacy protocol support will be dropped in OTP-30, use more modern approach for file transfer as for instance SFTP (SSH File Transfer Protocol).

Sets the account for an operation, if needed. only if needed, so don't stress 🫢


append/3

append(Pid :: pid(),
             LocalFileName :: file:filename(),
             RemoteFileName :: file:filename()) ->
                ok | {error, Reason :: term()}.

Deprecated: ftp:append/3 is deprecated; Legacy protocol support will be dropped in OTP-30, use more modern approach for file transfer as for instance SFTP (SSH File Transfer Protocol).

Transfers the file LocalFile to the remote server. If RemoteFile is specified, the name of the remote file that the file is appended to is set to RemoteFile, otherwise to LocalFile. If the file does not exists, it is created. not me appending to a file that doesn't even exist yet... it gets created, slay 🌈


append_bin/3

append_bin(Pid :: pid(), Bin :: binary(), RemoteFile :: string()) ->
                    ok | {error, Reason :: term()}.

Deprecated: ftp:append_bin/3 is deprecated; Legacy protocol support will be dropped in OTP-30, use more modern approach for file transfer as for instance SFTP (SSH File Transfer Protocol).

Transfers the binary Bin to the remote server and appends it to the file RemoteFile. If the file does not exist, it is created. binaries on binaries on binaries, she ate πŸ’…


append_chunk/2

append_chunk(Client :: client(), Bin :: binary()) ->
                          ok | {error, Reason :: term()}.

Deprecated: ftp:append_chunk/2 is deprecated; Legacy protocol support will be dropped in OTP-30, use more modern approach for file transfer as for instance SFTP (SSH File Transfer Protocol).

Transfers the chunk Bin to the remote server, which appends it to the file specified in the call to append_chunk_start/2. one lil chunk at a time, tiny bites πŸ“

fr fr: for some errors, for example, file system full, it is necessary to call append_chunk_end to get the proper reason. like, the error might be hiding until you close it out. messy but true 😭

append_chunk_end/1

append_chunk_end(Client :: client()) ->
                          ok | {error, Reason :: term()}.

Deprecated: ftp:append_chunk_end/1 is deprecated; Legacy protocol support will be dropped in OTP-30, use more modern approach for file transfer as for instance SFTP (SSH File Transfer Protocol).

Stops transfer of chunks for appending to the remote server. The file at the remote server, specified in the call to append_chunk_start/2, is closed by the server. okay that's a wrap, closing the curtain 🎭


append_chunk_start/2

append_chunk_start(Client :: client(), RemoteFile :: string()) ->
                            ok | {error, Reason :: term()}.

Deprecated: ftp:append_chunk_start/2 is deprecated; Legacy protocol support will be dropped in OTP-30, use more modern approach for file transfer as for instance SFTP (SSH File Transfer Protocol).

Starts the transfer of chunks for appending to the file RemoteFile at the remote server. If the file does not exist, it is created. starting era ✨


cd/2

cd(Client :: client(), Dir :: string()) ->
            ok | {error, Reason :: term()}.

Deprecated: ftp:cd/2 is deprecated; Legacy protocol support will be dropped in OTP-30, use more modern approach for file transfer as for instance SFTP (SSH File Transfer Protocol).

Changes the working directory at the remote server to Dir. moving out, moving in, moving on πŸšΆβ™€οΈ


close/1

close(Client :: client()) -> ok.

Deprecated: ftp:close/1 is deprecated; Legacy protocol support will be dropped in OTP-30, use more modern approach for file transfer as for instance SFTP (SSH File Transfer Protocol).

Ends an FTP session, created using function open. and that's the end of that chapter πŸ’œ


delete/2

delete(Client :: client(), File :: string()) ->
                ok | {error, Reason :: term()}.

Deprecated: ftp:delete/2 is deprecated; Legacy protocol support will be dropped in OTP-30, use more modern approach for file transfer as for instance SFTP (SSH File Transfer Protocol).

Deletes the file File at the remote server. gone, reduced to atoms πŸ’€


formaterror/1

formaterror(Tag :: atom() | {error, atom()}) -> string().

Deprecated: ftp:formaterror/1 is deprecated; Legacy protocol support will be dropped in OTP-30, use more modern approach for file transfer as for instance SFTP (SSH File Transfer Protocol).

Given an error return value {error, AtomReason}, this function returns a readable string describing the error. translating the cryptic into the readable, your fave 🫢


lcd/2

lcd(Client :: client(), Dir :: string()) ->
             ok | {error, Reason :: term()}.

Deprecated: ftp:lcd/2 is deprecated; Legacy protocol support will be dropped in OTP-30, use more modern approach for file transfer as for instance SFTP (SSH File Transfer Protocol).

Changes the working directory to Dir for the local client. note the L bestie, local not remote πŸ’…


lpwd/1

lpwd(Client :: client()) -> {ok, Dir :: string()}.

Deprecated: ftp:lpwd/1 is deprecated; Legacy protocol support will be dropped in OTP-30, use more modern approach for file transfer as for instance SFTP (SSH File Transfer Protocol).

Returns the current working directory at the local client. where am I?? local edition 🏠


ls/2

ls(Client :: client(), Dir :: string()) ->
            {ok, Listing :: string()} | {error, Reason :: term()}.

Deprecated: ftp:ls/2 is deprecated; Legacy protocol support will be dropped in OTP-30, use more modern approach for file transfer as for instance SFTP (SSH File Transfer Protocol).

Returns a list of files in long format. the deluxe version ✨

Dir can be a directory or a file. The Dir string can contain wildcards. wildcards?? spicy 🌈

ls/1 implies the current remote directory of the user. (yes, ls/1, not ls/2 β€” that's what it says, i don't make the rules)

The format of Listing depends on the operating system. On UNIX, it is typically produced from the output of the ls -l shell command. so basically it's ls -l wearing an Erlang hat πŸ‘’


mkdir/2

mkdir(Client :: client(), Dir :: string()) ->
               ok | {error, Reason :: term()}.

Deprecated: ftp:mkdir/2 is deprecated; Legacy protocol support will be dropped in OTP-30, use more modern approach for file transfer as for instance SFTP (SSH File Transfer Protocol).

Creates the directory Dir at the remote server. manifesting a directory into existence πŸ¦‹


nlist/2

nlist(Client :: client(), Pathname :: string()) ->
               {ok, Listing :: string()} | {error, Reason :: term()}.

Deprecated: ftp:nlist/2 is deprecated; Legacy protocol support will be dropped in OTP-30, use more modern approach for file transfer as for instance SFTP (SSH File Transfer Protocol).

Returns a list of files in short format. the lowkey version, faster and leaner πŸƒβ™€οΈ

Pathname can be a directory or a file. The Pathname string can contain wildcards.

nlist/1 implies the current remote directory of the user.

The format of Listing is a stream of filenames where each filename is separated by <CRLF> or <NL>. Contrary to function ls, the purpose of nlist is to enable a program to process filename information automatically. so it's the machine-readable one β€” nlist is for the computers, ls is for your eyes πŸ‘€


open/2

open(Host :: string() | inet:ip_address(), Opts) ->
                  {ok, Client :: client()} | {error, Reason :: term()}
                  when
                      Opts :: [Opt],
                      Opt :: StartOption | OpenOption,
                      StartOption :: {verbose, Verbose} | {debug, Debug},
                      Verbose :: boolean(),
                      Debug :: disable | debug | trace,
                      OpenOption ::
                          {ipfamily, IpFamily} |
                          {port, Port :: port()} |
                          {mode, Mode} |
                          {tls, TLSOptions :: [ssl:tls_option()]} |
                          {tls_sec_method, TLSSecMethod :: ftps | ftpes} |
                          {tls_ctrl_session_reuse,
                           TLSSessionReuse :: boolean()} |
                          {timeout, Timeout :: timeout()} |
                          {dtimeout, DTimeout :: timeout()} |
                          {progress, Progress} |
                          {sock_ctrl, SocketCtrls} |
                          {sock_data_act, [SocketControl]} |
                          {sock_data_pass, [SocketControl]},
                      SocketCtrls :: [SocketControl],
                      IpFamily :: inet | inet6 | inet6fb4,
                      Mode :: active | passive,
                      Module :: atom(),
                      Function :: atom(),
                      InitialData :: term(),
                      Progress :: ignore | {Module, Function, InitialData},
                      SocketControl :: gen_tcp:option().

okay so this is the whole big open situation and it's a LOT of type stuff, so let's break it down gently because the type spec is doing the absolute most πŸ’–

  • Host is either a string() or an inet:ip_address() 🏠
  • Opts is a list of Opt, where each Opt is either a StartOption or an OpenOption.
  • StartOption is one of:
    • {verbose, Verbose} where Verbose :: boolean()
    • {debug, Debug} where Debug :: disable | debug | trace
  • OpenOption is one of:
    • {ipfamily, IpFamily}
    • {port, Port :: port()}
    • {mode, Mode}
    • {tls, TLSOptions :: [ssl:tls_option()]}
    • {tls_sec_method, TLSSecMethod :: ftps | ftpes}
    • {tls_ctrl_session_reuse, TLSSessionReuse :: boolean()}
    • {timeout, Timeout :: timeout()}
    • {dtimeout, DTimeout :: timeout()}
    • {progress, Progress}
    • {sock_ctrl, SocketCtrls}
    • {sock_data_act, [SocketControl]}
    • {sock_data_pass, [SocketControl]}
  • and the helper types are:
    • SocketCtrls :: [SocketControl]
    • IpFamily :: inet | inet6 | inet6fb4
    • Mode :: active | passive
    • Module :: atom()
    • Function :: atom()
    • InitialData :: term()
    • Progress :: ignore | {Module, Function, InitialData}
    • SocketControl :: gen_tcp:option()

that's the whole thing, nothing skipped, everything in its place, periodt πŸ‘‘


to be continued bestie… part 2 is on the way πŸ’œ

⚠️ Deprecated: ftp:open/2 is deprecated; Legacy protocol support will be dropped in OTP-30, use more modern approach for file transfer as for instance SFTP (SSH File Transfer Protocol).

okay so this one spins up a whole FTP client process πŸ’« and opens a session with the FTP server sitting at Host. That's the vibe. That's the function.

A session opened this way is closed using function close/1. Close the door behind you bestie, always. πŸšͺ

The available configuration options are as follows (all of them, no crumbs left behind):

  • {host, Host} β€” where Host = string() | ip_address(). The address of the server you're vibing with. πŸ‘‘
  • {port, Port} β€” Default is 0 which aliases to 21 or 990 when used with {tls_sec_method,ftps}).
  • {mode, Mode} β€” Default is passive.
  • {verbose, Verbose} β€” Determines if the FTP communication is to be verbose or not. Default is false.
  • {debug, Debug} β€” Debugging using the dbg toolkit. Default is disable.
  • {ipfamily, IpFamily} β€” With inet6fb4 the client behaves as before, that is, tries to use IPv6, and only if that does not work it uses IPv4). Default is inet (IPv4).
  • {timeout, Timeout} β€” Connection time-out. Default is 60000 (milliseconds). ⏳
  • {dtimeout, DTimeout} β€” Data connect time-out. The time the client waits for the server to connect to the data socket. Default is infinity.
  • {tls, TLSOptions} β€” The FTP session is transported over tls (ftps, see RFC 4217). The list TLSOptions can be empty. The function ssl:connect/3 is used for securing both the control connection and the data sessions. πŸ”
  • {tls_sec_method, TLSSecMethod} β€” When set to ftps will connect immediately with SSL instead of upgrading with STARTTLS. This suboption is ignored unless the suboption tls is also set. Default is ftpes. πŸ™ˆ
  • {tls_ctrl_session_reuse, boolean()} β€” When set to true the client will re-use the TLS session from the control channel on the data channel as enforced by many FTP servers as (proposed and implemented first by vsftpd). Default is false.
  • {sock_ctrl, SocketCtrls :: [SocketControl :: gen_tcp:option()]} β€” Passes options from SocketCtrls down to the underlying transport layer (tcp). gen_tcp:option/0 except for ipv6_v6only, active, packet, mode, packet_size and header. Default value is SocketCtrls = [].
  • {sock_data_act, [SocketControl]} β€” Passes options from [SocketControl] down to the underlying transport layer (tcp). sock_data_act uses the value of sock_ctrl as default value.
  • {sock_data_pass, [SocketControl]} β€” Passes options from [SocketControl] down to the underlying transport layer (tcp). sock_data_pass uses the value of sock_ctrl as default value.
  • {progress, Progress} β€” Progress = ignore | {Module, Function, InitialData}, Module = atom(), Function = atom(), InitialData = term(). Default is ignore. πŸ“Š

Option progress is intended to be used by applications that want to create some type of progress report, such as a progress bar in a GUI. Default for the progress option is ignore, that is, the option is not used. When the progress option is specified, the following happens when ftp:send/[3,4] or ftp:recv/[3,4] are called:

  • Before a file is transferred, the following call is made to indicate the start of the file transfer and how large the file is. The return value of the callback function is to be a new value for the UserProgressTerm that will be used as input the next time the callback function is called.
Module:Function(InitialData, File, {file_size, FileSize})
  • Every time a chunk of bytes is transferred the following call is made:
Module:Function(UserProgressTerm, File, {transfer_size, TransferSize})
  • At the end of the file the following call is made to indicate the end of the transfer:
Module:Function(UserProgressTerm, File, {transfer_size, 0})

The callback function is to be defined as follows:

Module:Function(UserProgressTerm, File, Size) ->
    UserProgressTerm

UserProgressTerm = term()
File = string()
Size = {transfer_size, integer()} | {file_size, integer()} |
       {file_size, unknown}

For remote files, ftp cannot determine the file size in a platform independent way. In this case the size becomes unknown and it is left to the application to determine the size. 😭

NOTE: The callback is made by a middleman process, hence the file transfer is not affected by the code in the progress callback function. If the callback crashes, this is detected by the FTP connection process, which then prints an info-report and goes on as if the progress option was set to ignore.

The file transfer type is set to the default of the FTP server when the session is opened. This is usually ASCII mode.

The current local working directory (compare lpwd/1) is set to the value reported by file:get_cwd/1, the wanted local directory.

The return value Pid is used as a reference to the newly created FTP client in all other functions, and they are to be called by the process that created the connection. The FTP client process monitors the process that created it and terminates if that process terminates. πŸ‘€


🌸 pwd/1

pwd(Client :: client()) ->
    {ok, Dir :: string()} | {error, Reason :: term()}.

⚠️ Deprecated: ftp:pwd/1 is deprecated; Legacy protocol support will be dropped in OTP-30, use more modern approach for file transfer as for instance SFTP (SSH File Transfer Protocol).

Returns the current working directory at the remote server. πŸ“ Where even am I, bestie?

πŸŽ€ quote/2

quote(Client :: client(), Cmd :: string()) ->
    [FTPLine :: string()].

⚠️ Deprecated: ftp:quote/2 is deprecated; Legacy protocol support will be dropped in OTP-30, use more modern approach for file transfer as for instance SFTP (SSH File Transfer Protocol).

NOTE: The telnet end of line characters, from the FTP protocol definition, CRLF, for example, "\\r\\n" has been removed.

Sends an arbitrary FTP command and returns verbatim a list of the lines sent back by the FTP server. This function is intended to give application accesses to FTP commands that are server-specific or that cannot be provided by this FTP client. So it's like "here, say the thing yourself" energy. 🎀

NOTE: FTP commands requiring a data connection cannot be successfully issued with this function.

πŸ’œ recv/3

recv(Pid :: pid(),
     RemoteFileName :: file:filename(),
     LocalFileName :: file:filename()) ->
    ok | {error, Reason :: term()}.

⚠️ Deprecated: ftp:recv/3 is deprecated; Legacy protocol support will be dropped in OTP-30, use more modern approach for file transfer as for instance SFTP (SSH File Transfer Protocol).

Transfers the file RemoteFileName from the remote server to the file system of the local client. If LocalFileName is specified, the local file will be LocalFileName, otherwise RemoteFileName.

If the file write fails, the command is aborted and {error, term()} is returned. However, the file is not removed. πŸ’€ yeah, it just... stays there. Left on read.

πŸ¦‹ recv_bin/2

recv_bin(Pid :: pid(), RemoteFile :: string()) ->
    {ok, Bin :: binary()} | {error, Reason :: term()}.

⚠️ Deprecated: ftp:recv_bin/2 is deprecated; Legacy protocol support will be dropped in OTP-30, use more modern approach for file transfer as for instance SFTP (SSH File Transfer Protocol).

Transfers the file RemoteFile from the remote server and receives it as a binary. Raw bytes straight into your hands. ✨

🌈 recv_chunk/1

recv_chunk(Client :: client()) ->
    ok |
    {ok, Bin :: binary()} |
    {error, Reason :: term()}.

⚠️ Deprecated: ftp:recv_chunk/1 is deprecated; Legacy protocol support will be dropped in OTP-30, use more modern approach for file transfer as for instance SFTP (SSH File Transfer Protocol).

Receives a chunk of the remote file (RemoteFile of recv_chunk_start). The return values have the following meaning:

  • ok = the transfer is complete. πŸŽ‰
  • {ok, Bin} = just another chunk of the file.
  • {error, Reason} = transfer failed. 😭

πŸ“ recv_chunk_start/2

recv_chunk_start(Pid :: pid(), RemoteFile :: string()) ->
    ok | {error, Reason :: term()}.

⚠️ Deprecated: ftp:recv_chunk_start/2 is deprecated; Legacy protocol support will be dropped in OTP-30, use more modern approach for file transfer as for instance SFTP (SSH File Transfer Protocol).

Starts transfer of the file RemoteFile from the remote server. And then the chunks start rolling in. πŸ›Ό

🧚 rename/3

rename(Client :: client(), Old :: string(), New :: string()) ->
    ok | {error, Reason :: term()}.

⚠️ Deprecated: ftp:rename/3 is deprecated; Legacy protocol support will be dropped in OTP-30, use more modern approach for file transfer as for instance SFTP (SSH File Transfer Protocol).

Renames Old to New at the remote server. Glow-up for your files, honestly. πŸ’…

⭐ rmdir/2

rmdir(Client :: client(), Dir :: string()) ->
    ok | {error, Reason :: term()}.

⚠️ Deprecated: ftp:rmdir/2 is deprecated; Legacy protocol support will be dropped in OTP-30, use more modern approach for file transfer as for instance SFTP (SSH File Transfer Protocol).

Removes directory Dir at the remote server. Bye bestie, blocked. πŸ—‘οΈ

🫢 send/3

send(Pid :: pid(),
     LocalFileName :: file:filename(),
     RemoteFileName :: file:filename()) ->
    ok | {error, Reason :: term()}.

⚠️ Deprecated: ftp:send/3 is deprecated; Legacy protocol support will be dropped in OTP-30, use more modern approach for file transfer as for instance SFTP (SSH File Transfer Protocol).

Transfers the file LocalFileName to the remote server. If RemoteFileName is specified, the name of the remote file is set to RemoteFileName, otherwise to LocalFileName.

πŸ’– send_bin/3

send_bin(Client :: client(),
         Bin :: binary(),
         RemoteFile :: string()) ->
    ok | {error, Reason :: term()}.
πŸ’€ Deprecated: ftp:send_bin/3 is deprecated; Legacy protocol support will be dropped in OTP-30, use more modern approach for file transfer as for instance SFTP (SSH File Transfer Protocol).

okay so bestie, this one is simple in the best way: you hand it the binary Bin and it transfers that binary straight into the file RemoteFile living on the remote server. that's the whole vibe. πŸ’–


send_chunk/2 🌸

send_chunk(Client :: client(), Bin :: binary()) ->
                    ok | {error, Reason :: term()}.
😭 Deprecated: ftp:send_chunk/2 is deprecated; Legacy protocol support will be dropped in OTP-30, use more modern approach for file transfer as for instance SFTP (SSH File Transfer Protocol).

She transfers the chunk Bin over to the remote server, and the server writes it into whatever file you named when you called send_chunk_start/2. 🫢

lowkey important: for some errors β€” file system full being the classic one β€” you've gotta call send_chunk_end to actually get the proper reason. no skipping that step, bestie, the error won't spill the tea otherwise. πŸ™ˆ


send_chunk_end/1 πŸŽ€

send_chunk_end(Client :: client()) ->
                            ok | {error, Reason :: term()}.
πŸ’€ Deprecated: ftp:send_chunk_end/1 is deprecated; Legacy protocol support will be dropped in OTP-30, use more modern approach for file transfer as for instance SFTP (SSH File Transfer Protocol).

This stops the chunk transfer to the remote server. And the file on the remote server that you specified back in your send_chunk_start/2 call? the server closes it. done. iconic. ✨


send_chunk_start/2 πŸ¦‹

send_chunk_start(Client :: client(), RemoteFile :: string()) ->
                              ok | {error, Reason :: term()}.
😭 Deprecated: ftp:send_chunk_start/2 is deprecated; Legacy protocol support will be dropped in OTP-30, use more modern approach for file transfer as for instance SFTP (SSH File Transfer Protocol).

She starts the transfer of chunks into the file RemoteFile over at the remote server. that's it, that's the beginning of the chunk saga. πŸ’…


type/2 🌈

type(Client :: client(), Type :: ascii | binary) ->
              ok | {error, Reason :: term()}.
πŸ’€ Deprecated: ftp:type/2 is deprecated; Legacy protocol support will be dropped in OTP-30, use more modern approach for file transfer as for instance SFTP (SSH File Transfer Protocol).

Sets the file transfer type to ascii or binary, your pick. ngl though, when an FTP session first opens, you just get whatever the server's default transfer type is β€” most often ascii, because that's the default according to RFC 959. so the server kinda chose for you already. πŸ€·β€β™€οΈ


user/3 πŸ’œ

user(Pid :: pid(), User :: string(), Pass :: string()) ->
              ok | {error, Reason :: term()}.
😭 Deprecated: ftp:user/3 is deprecated; Legacy protocol support will be dropped in OTP-30, use more modern approach for file transfer as for instance SFTP (SSH File Transfer Protocol).

Performs login of User with Pass. main character login energy, periodt. πŸ‘‘


user/4 ⭐

user(Pid :: pid(),
           User :: string(),
           Pass :: string(),
           Account :: string()) ->
              ok | {error, Reason :: term()}.
πŸ’€ Deprecated: ftp:user/4 is deprecated; Legacy protocol support will be dropped in OTP-30, use more modern approach for file transfer as for instance SFTP (SSH File Transfer Protocol).

Same login deal, but this time User logs in with Pass to the account you name in Account. extra credentials, extra slay. πŸ’–


and that's the whole page, bestie, we made it to the end πŸ₯Ή this one's stamped from Ericsson AB, page ftp 1.2.6 (that's the source + version + date line doing its lil footer thing), nestled in the ftp(3) manual. go forth and transfer files responsibly β€” or better yet, take the page's own advice and use something modern like SFTP. bye bestie, mwah πŸ’…βœ¨

πŸ“¦ where this page comes from