aboutsummaryrefslogtreecommitdiffstats
path: root/lib/inets
diff options
context:
space:
mode:
Diffstat (limited to 'lib/inets')
-rw-r--r--lib/inets/doc/archive/rfc2428.txt451
-rw-r--r--lib/inets/doc/archive/rfc2577.txt451
-rw-r--r--lib/inets/doc/archive/rfc959.txt3933
-rw-r--r--lib/inets/doc/src/Makefile2
-rw-r--r--lib/inets/doc/src/ftp.xml948
-rw-r--r--lib/inets/doc/src/ftp_client.xml86
-rw-r--r--lib/inets/doc/src/introduction.xml9
-rw-r--r--lib/inets/doc/src/part.xml8
-rw-r--r--lib/inets/doc/src/ref_man.xml9
-rw-r--r--lib/inets/src/ftp/Makefile104
-rw-r--r--lib/inets/src/ftp/ftp.erl2596
-rw-r--r--lib/inets/src/ftp/ftp_progress.erl136
-rw-r--r--lib/inets/src/ftp/ftp_response.erl203
-rw-r--r--lib/inets/src/ftp/ftp_sup.erl60
-rw-r--r--lib/inets/src/inets_app/Makefile3
-rw-r--r--lib/inets/src/inets_app/inets.app.src5
-rw-r--r--lib/inets/src/inets_app/inets.erl4
-rw-r--r--lib/inets/src/inets_app/inets_ftp_wrapper.erl (renamed from lib/inets/src/ftp/ftp_internal.hrl)36
-rw-r--r--lib/inets/src/inets_app/inets_sup.erl12
-rw-r--r--lib/inets/src/subdirs.mk2
-rw-r--r--lib/inets/test/Makefile40
-rw-r--r--lib/inets/test/ftp_SUITE.erl1180
-rw-r--r--lib/inets/test/ftp_SUITE_data/ftpd_hosts.skel18
-rw-r--r--lib/inets/test/ftp_SUITE_data/vsftpd.conf26
-rw-r--r--lib/inets/test/ftp_format_SUITE.erl328
l---------lib/inets/test/ftp_internal.hrl1
-rw-r--r--lib/inets/test/ftp_property_test_SUITE.erl53
-rw-r--r--lib/inets/test/inets_SUITE.erl39
-rw-r--r--lib/inets/test/inets_sup_SUITE.erl32
29 files changed, 48 insertions, 10727 deletions
diff --git a/lib/inets/doc/archive/rfc2428.txt b/lib/inets/doc/archive/rfc2428.txt
deleted file mode 100644
index a6ec3535ed..0000000000
--- a/lib/inets/doc/archive/rfc2428.txt
+++ /dev/null
@@ -1,451 +0,0 @@
-
-
-
-
-
-
-Network Working Group M. Allman
-Request for Comments: 2428 NASA Lewis/Sterling Software
-Category: Standards Track S. Ostermann
- Ohio University
- C. Metz
- The Inner Net
- September 1998
-
-
- FTP Extensions for IPv6 and NATs
-
-Status of this Memo
-
- This document specifies an Internet standards track protocol for the
- Internet community, and requests discussion and suggestions for
- improvements. Please refer to the current edition of the "Internet
- Official Protocol Standards" (STD 1) for the standardization state
- and status of this protocol. Distribution of this memo is unlimited.
-
-Copyright Notice
-
- Copyright (C) The Internet Society (1998). All Rights Reserved.
-
-Abstract
-
- The specification for the File Transfer Protocol assumes that the
- underlying network protocol uses a 32-bit network address
- (specifically IP version 4). With the deployment of version 6 of the
- Internet Protocol, network addresses will no longer be 32-bits. This
- paper specifies extensions to FTP that will allow the protocol to
- work over IPv4 and IPv6. In addition, the framework defined can
- support additional network protocols in the future.
-
-1. Introduction
-
- The keywords, such as MUST and SHOULD, found in this document are
- used as defined in RFC 2119 [Bra97].
-
- The File Transfer Protocol [PR85] only provides the ability to
- communicate information about IPv4 data connections. FTP assumes
- network addresses will be 32 bits in length. However, with the
- deployment of version 6 of the Internet Protocol [DH96] addresses
- will no longer be 32 bits long. RFC 1639 [Pis94] specifies
- extensions to FTP to enable its use over various network protocols.
- Unfortunately, the mechanism can fail in a multi-protocol
- environment. During the transition between IPv4 and IPv6, FTP needs
- the ability to negotiate the network protocol that will be used for
- data transfer.
-
-
-
-Allman, et. al. Standards Track [Page 1]
-
-RFC 2428 FTP Extensions for IPv6 and NATs September 1998
-
-
- This document provides a specification for a way that FTP can
- communicate data connection endpoint information for network
- protocols other than IPv4. In this specification, the FTP commands
- PORT and PASV are replaced with EPRT and EPSV, respectively. This
- document is organized as follows. Section 2 outlines the EPRT
- command and Section 3 outlines the EPSV command. Section 4 defines
- the utilization of these two new FTP commands. Section 5 briefly
- presents security considerations. Finally, Section 6 provides
- conclusions.
-
-2. The EPRT Command
-
- The EPRT command allows for the specification of an extended address
- for the data connection. The extended address MUST consist of the
- network protocol as well as the network and transport addresses. The
- format of EPRT is:
-
- EPRT<space><d><net-prt><d><net-addr><d><tcp-port><d>
-
- The EPRT command keyword MUST be followed by a single space (ASCII
- 32). Following the space, a delimiter character (<d>) MUST be
- specified. The delimiter character MUST be one of the ASCII
- characters in range 33-126 inclusive. The character "|" (ASCII 124)
- is recommended unless it coincides with a character needed to encode
- the network address.
-
- The <net-prt> argument MUST be an address family number defined by
- IANA in the latest Assigned Numbers RFC (RFC 1700 [RP94] as of the
- writing of this document). This number indicates the protocol to be
- used (and, implicitly, the address length). This document will use
- two of address family numbers from [RP94] as examples, according to
- the following table:
-
- AF Number Protocol
- --------- --------
- 1 Internet Protocol, Version 4 [Pos81a]
- 2 Internet Protocol, Version 6 [DH96]
-
- The <net-addr> is a protocol specific string representation of the
- network address. For the two address families specified above (AF
- Number 1 and 2), addresses MUST be in the following format:
-
- AF Number Address Format Example
- --------- -------------- -------
- 1 dotted decimal 132.235.1.2
- 2 IPv6 string 1080::8:800:200C:417A
- representations
- defined in [HD96]
-
-
-
-Allman, et. al. Standards Track [Page 2]
-
-RFC 2428 FTP Extensions for IPv6 and NATs September 1998
-
-
- The <tcp-port> argument must be the string representation of the
- number of the TCP port on which the host is listening for the data
- connection.
-
- The following are sample EPRT commands:
-
- EPRT |1|132.235.1.2|6275|
-
- EPRT |2|1080::8:800:200C:417A|5282|
-
- The first command specifies that the server should use IPv4 to open a
- data connection to the host "132.235.1.2" on TCP port 6275. The
- second command specifies that the server should use the IPv6 network
- protocol and the network address "1080::8:800:200C:417A" to open a
- TCP data connection on port 5282.
-
- Upon receipt of a valid EPRT command, the server MUST return a code
- of 200 (Command OK). The standard negative error code 500 and 501
- [PR85] are sufficient to handle most errors (e.g., syntax errors)
- involving the EPRT command. However, an additional error code is
- needed. The response code 522 indicates that the server does not
- support the requested network protocol. The interpretation of this
- new error code is:
-
- 5yz Negative Completion
- x2z Connections
- xy2 Extended Port Failure - unknown network protocol
-
- The text portion of the response MUST indicate which network
- protocols the server does support. If the network protocol is
- unsupported, the format of the response string MUST be:
-
- <text stating that the network protocol is unsupported> \
- (prot1,prot2,...,protn)
-
- Both the numeric code specified above and the protocol information
- between the characters '(' and ')' are intended for the software
- automata receiving the response; the textual message between the
- numeric code and the '(' is intended for the human user and can be
- any arbitrary text, but MUST NOT include the characters '(' and ')'.
- In the above case, the text SHOULD indicate that the network protocol
- in the EPRT command is not supported by the server. The list of
- protocols inside the parenthesis MUST be a comma separated list of
- address family numbers. Two example response strings follow:
-
- Network protocol not supported, use (1)
-
- Network protocol not supported, use (1,2)
-
-
-
-Allman, et. al. Standards Track [Page 3]
-
-RFC 2428 FTP Extensions for IPv6 and NATs September 1998
-
-
-3. The EPSV Command
-
- The EPSV command requests that a server listen on a data port and
- wait for a connection. The EPSV command takes an optional argument.
- The response to this command includes only the TCP port number of the
- listening connection. The format of the response, however, is
- similar to the argument of the EPRT command. This allows the same
- parsing routines to be used for both commands. In addition, the
- format leaves a place holder for the network protocol and/or network
- address, which may be needed in the EPSV response in the future. The
- response code for entering passive mode using an extended address
- MUST be 229. The interpretation of this code, according to [PR85]
- is:
-
- 2yz Positive Completion
- x2z Connections
- xy9 Extended Passive Mode Entered
-
- The text returned in response to the EPSV command MUST be:
-
- <text indicating server is entering extended passive mode> \
- (<d><d><d><tcp-port><d>)
-
- The portion of the string enclosed in parentheses MUST be the exact
- string needed by the EPRT command to open the data connection, as
- specified above.
-
- The first two fields contained in the parenthesis MUST be blank. The
- third field MUST be the string representation of the TCP port number
- on which the server is listening for a data connection. The network
- protocol used by the data connection will be the same network
- protocol used by the control connection. In addition, the network
- address used to establish the data connection will be the same
- network address used for the control connection. An example response
- string follows:
-
- Entering Extended Passive Mode (|||6446|)
-
- The standard negative error codes 500 and 501 are sufficient to
- handle all errors involving the EPSV command (e.g., syntax errors).
-
- When the EPSV command is issued with no argument, the server will
- choose the network protocol for the data connection based on the
- protocol used for the control connection. However, in the case of
- proxy FTP, this protocol might not be appropriate for communication
- between the two servers. Therefore, the client needs to be able to
- request a specific protocol. If the server returns a protocol that
- is not supported by the host that will be connecting to the port, the
-
-
-
-Allman, et. al. Standards Track [Page 4]
-
-RFC 2428 FTP Extensions for IPv6 and NATs September 1998
-
-
- client MUST issue an ABOR (abort) command to allow the server to
- close down the listening connection. The client can then send an
- EPSV command requesting the use of a specific network protocol, as
- follows:
-
- EPSV<space><net-prt>
-
- If the requested protocol is supported by the server, it SHOULD use
- the protocol. If not, the server MUST return the 522 error messages
- as outlined in section 2.
-
- Finally, the EPSV command can be used with the argument "ALL" to
- inform Network Address Translators that the EPRT command (as well as
- other data commands) will no longer be used. An example of this
- command follows:
-
- EPSV<space>ALL
-
- Upon receipt of an EPSV ALL command, the server MUST reject all data
- connection setup commands other than EPSV (i.e., EPRT, PORT, PASV, et
- al.). This use of the EPSV command is further explained in section
- 4.
-
-4. Command Usage
-
- For all FTP transfers where the control and data connection(s) are
- being established between the same two machines, the EPSV command
- MUST be used. Using the EPSV command benefits performance of
- transfers that traverse firewalls or Network Address Translators
- (NATs). RFC 1579 [Bel94] recommends using the passive command when
- behind firewalls since firewalls do not generally allow incoming
- connections (which are required when using the PORT (EPRT) command).
- In addition, using EPSV as defined in this document does not require
- NATs to change the network address in the traffic as it is forwarded.
- The NAT would have to change the address if the EPRT command was
- used. Finally, if the client issues an "EPSV ALL" command, NATs may
- be able to put the connection on a "fast path" through the
- translator, as the EPRT command will never be used and therefore,
- translation of the data portion of the segments will never be needed.
- When a client only expects to do two-way FTP transfers, it SHOULD
- issue this command as soon as possible. If a client later finds that
- it must do a three-way FTP transfer after issuing an EPSV ALL
- command, a new FTP session MUST be started.
-
-
-
-
-
-
-
-
-Allman, et. al. Standards Track [Page 5]
-
-RFC 2428 FTP Extensions for IPv6 and NATs September 1998
-
-
-5. Security Issues
-
- The authors do not believe that these changes to FTP introduce new
- security problems. A companion Work in Progress [AO98] is a more
- general discussion of FTP security issues and techniques to reduce
- these security problems.
-
-6. Conclusions
-
- The extensions specified in this paper will enable FTP to operate
- over a variety of network protocols.
-
-References
-
- [AO98] Allman, M., and S. Ostermann, "FTP Security
- Considerations", Work in Progress.
-
- [Bel94] Bellovin, S., "Firewall-Friendly FTP", RFC 1579, February
- 1994.
-
- [Bra97] Bradner, S., "Key words for use in RFCs to Indicate
- Requirement Levels", BCP 14, RFC 2119, March 1997.
-
- [DH96] Deering, S., and R. Hinden, "Internet Protocol, Version 6
- (IPv6) Specification", RFC 1883, December 1995.
-
- [HD96] Hinden, R., and S. Deering, "IP Version 6 Addressing
- Architecture", RFC 2373, July 1998.
-
- [Pis94] Piscitello, D., "FTP Operation Over Big Address Records
- (FOOBAR)", RFC 1639, June 1994.
-
- [Pos81a] Postel, J., "Internet Protocol", STD 5, RFC 791, September
- 1981.
-
- [Pos81b] Postel, J., "Transmission Control Protocol", STD 7, RFC 793,
- September 1981.
-
- [PR85] Postel, J., and J. Reynolds, "File Transfer Protocol (FTP)",
- STD 9, RFC 959, October 1985.
-
- [RP94] Reynolds, J., and J. Postel, "Assigned Numbers", STD 2, RFC
- 1700, October 1994. See also:
- http://www.iana.org/numbers.html
-
-
-
-
-
-
-
-Allman, et. al. Standards Track [Page 6]
-
-RFC 2428 FTP Extensions for IPv6 and NATs September 1998
-
-
-Authors' Addresses
-
- Mark Allman
- NASA Lewis Research Center/Sterling Software
- 21000 Brookpark Rd. MS 54-2
- Cleveland, OH 44135
-
- Phone: (216) 433-6586
- http://gigahertz.lerc.nasa.gov/~mallman/
-
-
- Shawn Ostermann
- School of Electrical Engineering and Computer Science
- Ohio University
- 416 Morton Hall
- Athens, OH 45701
-
- Phone: (740) 593-1234
-
-
- Craig Metz
- The Inner Net
- Box 10314-1954
- Blacksburg, VA 24062-0314
-
- Phone: (DSN) 754-8590
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Allman, et. al. Standards Track [Page 7]
-
-RFC 2428 FTP Extensions for IPv6 and NATs September 1998
-
-
-Full Copyright Statement
-
- Copyright (C) The Internet Society (1998). All Rights Reserved.
-
- This document and translations of it may be copied and furnished to
- others, and derivative works that comment on or otherwise explain it
- or assist in its implementation may be prepared, copied, published
- and distributed, in whole or in part, without restriction of any
- kind, provided that the above copyright notice and this paragraph are
- included on all such copies and derivative works. However, this
- document itself may not be modified in any way, such as by removing
- the copyright notice or references to the Internet Society or other
- Internet organizations, except as needed for the purpose of
- developing Internet standards in which case the procedures for
- copyrights defined in the Internet Standards process must be
- followed, or as required to translate it into languages other than
- English.
-
- The limited permissions granted above are perpetual and will not be
- revoked by the Internet Society or its successors or assigns.
-
- This document and the information contained herein is provided on an
- "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
- TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
- BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
- HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
- MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Allman, et. al. Standards Track [Page 8]
-
diff --git a/lib/inets/doc/archive/rfc2577.txt b/lib/inets/doc/archive/rfc2577.txt
deleted file mode 100644
index 83ba203130..0000000000
--- a/lib/inets/doc/archive/rfc2577.txt
+++ /dev/null
@@ -1,451 +0,0 @@
-
-
-
-
-
-
-Network Working Group M. Allman
-Request for Comments: 2577 NASA Glenn/Sterling Software
-Category: Informational S. Ostermann
- Ohio University
- May 1999
-
-
- FTP Security Considerations
-
-Status of this Memo
-
- This memo provides information for the Internet community. It does
- not specify an Internet standard of any kind. Distribution of this
- memo is unlimited.
-
-Copyright Notice
-
- Copyright (C) The Internet Society (1999). All Rights Reserved.
-
-Abstract
-
- The specification for the File Transfer Protocol (FTP) contains a
- number of mechanisms that can be used to compromise network security.
- The FTP specification allows a client to instruct a server to
- transfer files to a third machine. This third-party mechanism, known
- as proxy FTP, causes a well known security problem. The FTP
- specification also allows an unlimited number of attempts at entering
- a user's password. This allows brute force "password guessing"
- attacks. This document provides suggestions for system
- administrators and those implementing FTP servers that will decrease
- the security problems associated with FTP.
-
-1 Introduction
-
- The File Transfer Protocol specification (FTP) [PR85] provides a
- mechanism that allows a client to establish an FTP control connection
- and transfer a file between two FTP servers. This "proxy FTP"
- mechanism can be used to decrease the amount of traffic on the
- network; the client instructs one server to transfer a file to
- another server, rather than transferring the file from the first
- server to the client and then from the client to the second server.
- This is particularly useful when the client connects to the network
- using a slow link (e.g., a modem). While useful, proxy FTP provides
- a security problem known as a "bounce attack" [CERT97:27]. In
- addition to the bounce attack, FTP servers can be used by attackers
- to guess passwords using brute force.
-
-
-
-
-
-Allman & Ostermann Informational [Page 1]
-
-RFC 2577 FTP Security Considerations May 1999
-
-
- This document does not contain a discussion of FTP when used in
- conjunction with strong security protocols, such as IP Security.
- These security concerns should be documented, however they are out of
- the scope of this document.
-
- This paper provides information for FTP server implementers and
- system administrators, as follows. Section 2 describes the FTP
- "bounce attack". Section 3 provides suggestions for minimizing the
- bounce attack. Section 4 provides suggestions for servers which
- limit access based on network address. Section 5 provides
- recommendations for limiting brute force "password guessing" by
- clients. Next, section 6 provides a brief discussion of mechanisms
- to improve privacy. Section 7 provides a mechanism to prevent user
- identity guessing. Section 8 discusses the practice of port
- stealing. Finally, section 9 provides an overview of other FTP
- security issues related to software bugs rather than protocol issues.
-
-2 The Bounce Attack
-
- The version of FTP specified in the standard [PR85] provides a method
- for attacking well known network servers, while making the
- perpetrators difficult to track down. The attack involves sending an
- FTP "PORT" command to an FTP server containing the network address
- and the port number of the machine and service being attacked. At
- this point, the original client can instruct the FTP server to send a
- file to the service being attacked. Such a file would contain
- commands relevant to the service being attacked (SMTP, NNTP, etc.).
- Instructing a third party to connect to the service, rather than
- connecting directly, makes tracking down the perpetrator difficult
- and can circumvent network-address-based access restrictions.
-
- As an example, a client uploads a file containing SMTP commands to an
- FTP server. Then, using an appropriate PORT command, the client
- instructs the server to open a connection to a third machine's SMTP
- port. Finally, the client instructs the server to transfer the
- uploaded file containing SMTP commands to the third machine. This
- may allow the client to forge mail on the third machine without
- making a direct connection. This makes it difficult to track
- attackers.
-
-3 Protecting Against the Bounce Attack
-
- The original FTP specification [PR85] assumes that data connections
- will be made using the Transmission Control Protocol (TCP) [Pos81].
- TCP port numbers in the range 0 - 1023 are reserved for well known
- services such as mail, network news and FTP control connections
- [RP94]. The FTP specification makes no restrictions on the TCP port
- number used for the data connection. Therefore, using proxy FTP,
-
-
-
-Allman & Ostermann Informational [Page 2]
-
-RFC 2577 FTP Security Considerations May 1999
-
-
- clients have the ability to tell the server to attack a well known
- service on any machine.
-
- To avoid such bounce attacks, it is suggested that servers not open
- data connections to TCP ports less than 1024. If a server receives a
- PORT command containing a TCP port number less than 1024, the
- suggested response is 504 (defined as "Command not implemented for
- that parameter" by [PR85]). Note that this still leaves non-well
- known servers (those running on ports greater than 1023) vulnerable
- to bounce attacks.
-
- Several proposals (e.g., [AOM98] and [Pis94]) provide a mechanism
- that would allow data connections to be made using a transport
- protocol other than TCP. Similar precautions should be taken to
- protect well known services when using these protocols.
-
- Also note that the bounce attack generally requires that a
- perpetrator be able to upload a file to an FTP server and later
- download it to the service being attacked. Using proper file
- protections will prevent this behavior. However, attackers can also
- attack services by sending random data from a remote FTP server which
- may cause problems for some services.
-
- Disabling the PORT command is also an option for protecting against
- the bounce attack. Most file transfers can be made using only the
- PASV command [Bel94]. The disadvantage of disabling the PORT command
- is that one loses the ability to use proxy FTP, but proxy FTP may not
- be necessary in a particular environment.
-
-4 Restricted Access
-
- For some FTP servers, it is desirable to restrict access based on
- network address. For example, a server might want to restrict access
- to certain files from certain places (e.g., a certain file should not
- be transferred out of an organization). In such a situation, the
- server should confirm that the network address of the remote hosts on
- both the control connection and the data connection are within the
- organization before sending a restricted file. By checking both
- connections, a server is protected against the case when the control
- connection is established with a trusted host and the data connection
- is not. Likewise, the client should verify the IP address of the
- remote host after accepting a connection on a port opened in listen
- mode to verify that the connection was made by the expected server.
-
- Note that restricting access based on network address leaves the FTP
- server vulnerable to "spoof" attacks. In a spoof attack, for
- example, an attacking machine could assume the host address of
- another machine inside an organization and download files that are
-
-
-
-Allman & Ostermann Informational [Page 3]
-
-RFC 2577 FTP Security Considerations May 1999
-
-
- not accessible from outside the organization. Whenever possible,
- secure authentication mechanisms should be used, such as those
- outlined in [HL97].
-
-5 Protecting Passwords
-
- To minimize the risk of brute force password guessing through the FTP
- server, it is suggested that servers limit the number of attempts
- that can be made at sending a correct password. After a small number
- of attempts (3-5), the server should close the control connection
- with the client. Before closing the control connection the server
- must send a return code of 421 ("Service not available, closing
- control connection." [PR85]) to the client. In addition, it is
- suggested that the server impose a 5 second delay before replying to
- an invalid "PASS" command to diminish the efficiency of a brute force
- attack. If available, mechanisms already provided by the target
- operating system should be used to implement the above suggestions.
-
- An intruder can subvert the above mechanisms by establishing
- multiple, parallel control connections to a server. To combat the
- use of multiple concurrent connections, the server could either limit
- the total number of control connections possible or attempt to detect
- suspicious activity across sessions and refuse further connections
- from the site. However, both of these mechanisms open the door to
- "denial of service" attacks, in which an attacker purposely initiates
- the attack to disable access by a valid user.
-
- Standard FTP [PR85] sends passwords in clear text using the "PASS"
- command. It is suggested that FTP clients and servers use alternate
- authentication mechanisms that are not subject to eavesdropping (such
- as the mechanisms being developed by the IETF Common Authentication
- Technology Working Group [HL97]).
-
-6 Privacy
-
- All data and control information (including passwords) is sent across
- the network in unencrypted form by standard FTP [PR85]. To guarantee
- the privacy of the information FTP transmits, a strong encryption
- scheme should be used whenever possible. One such mechanism is
- defined in [HL97].
-
-7 Protecting Usernames
-
- Standard FTP [PR85] specifies a 530 response to the USER command when
- the username is rejected. If the username is valid and a password is
- required FTP returns a 331 response instead. In order to prevent a
- malicious client from determining valid usernames on a server, it is
- suggested that a server always return 331 to the USER command and
-
-
-
-Allman & Ostermann Informational [Page 4]
-
-RFC 2577 FTP Security Considerations May 1999
-
-
- then reject the combination of username and password for an invalid
- username.
-
-8 Port Stealing
-
- Many operating systems assign dynamic port numbers in increasing
- order. By making a legitimate transfer, an attacker can observe the
- current port number allocated by the server and "guess" the next one
- that will be used. The attacker can make a connection to this port,
- thus denying another legitimate client the ability to make a
- transfer. Alternatively, the attacker can steal a file meant for a
- legitimate user. In addition, an attacker can insert a forged file
- into a data stream thought to come from an authenticated client.
- This problem can be mitigated by making FTP clients and servers use
- random local port numbers for data connections, either by requesting
- random ports from the operating system or using system dependent
- mechanisms.
-
-9 Software-Base Security Problems
-
- The emphasis in this document is on protocol-related security issues.
- There are a number of documented FTP security-related problems that
- are due to poor implementation as well. Although the details of
- these types of problems are beyond the scope of this document, it
- should be pointed out that the following FTP features has been abused
- in the past and should be treated with great care by future
- implementers:
-
- Anonymous FTP
-
- Anonymous FTP refers to the ability of a client to connect to an
- FTP server with minimal authentication and gain access to public
- files. Security problems arise when such a user can read all
- files on the system or can create files. [CERT92:09] [CERT93:06]
-
- Remote Command Execution
-
- An optional FTP extension, "SITE EXEC", allows clients to execute
- arbitrary commands on the server. This feature should obviously
- be implemented with great care. There are several documented
- cases of the FTP "SITE EXEC" command being used to subvert server
- security [CERT94:08] [CERT95:16]
-
- Debug Code
-
- Several previous security compromises related to FTP can be
- attributed to software that was installed with debugging features
- enabled [CERT88:01].
-
-
-
-Allman & Ostermann Informational [Page 5]
-
-RFC 2577 FTP Security Considerations May 1999
-
-
- This document recommends that implementors of FTP servers with these
- capabilities review all of the CERT advisories for attacks on these
- or similar mechanisms before releasing their software.
-
-10 Conclusion
-
- Using the above suggestions can decrease the security problems
- associated with FTP servers without eliminating functionality.
-
-11 Security Considerations
-
- Security issues are discussed throughout this memo.
-
-Acknowledgments
-
- We would like to thank Alex Belits, Jim Bound, William Curtin, Robert
- Elz, Paul Hethmon, Alun Jones and Stephen Tihor for their helpful
- comments on this paper. Also, we thank the FTPEXT WG members who
- gave many useful suggestions at the Memphis IETF meeting.
-
-References
-
- [AOM98] Allman, M., Ostermann, S. and C. Metz, "FTP Extensions
- for IPv6 and NATs", RFC 2428, September 1998.
-
- [Bel94] Bellovin. S., "Firewall-Friendly FTP", RFC 1579, February
- 1994.
-
- [CERT88:01] CERT Advisory CA-88:01. ftpd Vulnerability. December,
- 1988 ftp://info.cert.org/pub/cert_advisories/
-
- [CERT92:09] CERT Advisory CA-92:09. AIX Anonymous FTP Vulnerability.
- April 27, 1992. ftp://info.cert.org/pub/cert_advisories/
-
- [CERT93:06] CERT Advisory CA-93:06. Wuarchive ftpd Vulnerability.
- September 19,1997
- ftp://info.cert.org/pub/cert_advisories/
-
- [CERT94:08] CERT Advisory CA-94:08. ftpd Vulnerabilities. September
- 23, 1997. ftp://info.cert.org/pub/cert_advisories/
-
- [CERT95:16] CERT Advisory CA-95:16. wu-ftpd Misconfiguration
- Vulnerability. September 23, 1997
- ftp://info.cert.org/pub/cert_advisories/
-
- [CERT97:27] CERT Advisory CA-97.27. FTP Bounce. January 8, 1998.
- ftp://info.cert.org/pub/cert_advisories/
-
-
-
-
-Allman & Ostermann Informational [Page 6]
-
-RFC 2577 FTP Security Considerations May 1999
-
-
- [HL97] Horowitz, M. and S. Lunt, "FTP Security Extensions", RFC
- 2228, October 1997.
-
- [Pis94] Piscitello, D., "FTP Operation Over Big Address Records
- (FOOBAR), RFC 1639, June 1994.
-
- [Pos81] Postel, J., "Transmission Control Protocol", STD 7, RFC
- 793, September 1981.
-
- [PR85] Postel, J. and J. Reynolds, "File Transfer Protocol
- (FTP)", STD 9, RFC 959, October 1985.
-
- [RP94] Reynolds, J. and J. Postel, "Assigned Numbers", STD 2,
- RFC 1700, October 1994. See also:
- http://www.iana.org/numbers.html
-
-Authors' Addresses
-
- Mark Allman
- NASA Glenn Research Center/Sterling Software
- 21000 Brookpark Rd. MS 54-2
- Cleveland, OH 44135
-
-
-
- Shawn Ostermann
- School of Electrical Engineering and Computer Science
- Ohio University
- 416 Morton Hall
- Athens, OH 45701
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Allman & Ostermann Informational [Page 7]
-
-RFC 2577 FTP Security Considerations May 1999
-
-
-Full Copyright Statement
-
- Copyright (C) The Internet Society (1999). All Rights Reserved.
-
- This document and translations of it may be copied and furnished to
- others, and derivative works that comment on or otherwise explain it
- or assist in its implementation may be prepared, copied, published
- and distributed, in whole or in part, without restriction of any
- kind, provided that the above copyright notice and this paragraph are
- included on all such copies and derivative works. However, this
- document itself may not be modified in any way, such as by removing
- the copyright notice or references to the Internet Society or other
- Internet organizations, except as needed for the purpose of
- developing Internet standards in which case the procedures for
- copyrights defined in the Internet Standards process must be
- followed, or as required to translate it into languages other than
- English.
-
- The limited permissions granted above are perpetual and will not be
- revoked by the Internet Society or its successors or assigns.
-
- This document and the information contained herein is provided on an
- "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
- TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
- BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
- HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
- MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
-
-Acknowledgement
-
- Funding for the RFC Editor function is currently provided by the
- Internet Society.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Allman & Ostermann Informational [Page 8]
-
diff --git a/lib/inets/doc/archive/rfc959.txt b/lib/inets/doc/archive/rfc959.txt
deleted file mode 100644
index 5c9f11af5d..0000000000
--- a/lib/inets/doc/archive/rfc959.txt
+++ /dev/null
@@ -1,3933 +0,0 @@
-
-
-Network Working Group J. Postel
-Request for Comments: 959 J. Reynolds
- ISI
-Obsoletes RFC: 765 (IEN 149) October 1985
-
- FILE TRANSFER PROTOCOL (FTP)
-
-
-Status of this Memo
-
- This memo is the official specification of the File Transfer
- Protocol (FTP). Distribution of this memo is unlimited.
-
- The following new optional commands are included in this edition of
- the specification:
-
- CDUP (Change to Parent Directory), SMNT (Structure Mount), STOU
- (Store Unique), RMD (Remove Directory), MKD (Make Directory), PWD
- (Print Directory), and SYST (System).
-
- Note that this specification is compatible with the previous edition.
-
-1. INTRODUCTION
-
- The objectives of FTP are 1) to promote sharing of files (computer
- programs and/or data), 2) to encourage indirect or implicit (via
- programs) use of remote computers, 3) to shield a user from
- variations in file storage systems among hosts, and 4) to transfer
- data reliably and efficiently. FTP, though usable directly by a user
- at a terminal, is designed mainly for use by programs.
-
- The attempt in this specification is to satisfy the diverse needs of
- users of maxi-hosts, mini-hosts, personal workstations, and TACs,
- with a simple, and easily implemented protocol design.
-
- This paper assumes knowledge of the Transmission Control Protocol
- (TCP) [2] and the Telnet Protocol [3]. These documents are contained
- in the ARPA-Internet protocol handbook [1].
-
-2. OVERVIEW
-
- In this section, the history, the terminology, and the FTP model are
- discussed. The terms defined in this section are only those that
- have special significance in FTP. Some of the terminology is very
- specific to the FTP model; some readers may wish to turn to the
- section on the FTP model while reviewing the terminology.
-
-
-
-
-
-
-
-Postel & Reynolds [Page 1]
-
-
-
-RFC 959 October 1985
-File Transfer Protocol
-
-
- 2.1. HISTORY
-
- FTP has had a long evolution over the years. Appendix III is a
- chronological compilation of Request for Comments documents
- relating to FTP. These include the first proposed file transfer
- mechanisms in 1971 that were developed for implementation on hosts
- at M.I.T. (RFC 114), plus comments and discussion in RFC 141.
-
- RFC 172 provided a user-level oriented protocol for file transfer
- between host computers (including terminal IMPs). A revision of
- this as RFC 265, restated FTP for additional review, while RFC 281
- suggested further changes. The use of a "Set Data Type"
- transaction was proposed in RFC 294 in January 1982.
-
- RFC 354 obsoleted RFCs 264 and 265. The File Transfer Protocol
- was now defined as a protocol for file transfer between HOSTs on
- the ARPANET, with the primary function of FTP defined as
- transfering files efficiently and reliably among hosts and
- allowing the convenient use of remote file storage capabilities.
- RFC 385 further commented on errors, emphasis points, and
- additions to the protocol, while RFC 414 provided a status report
- on the working server and user FTPs. RFC 430, issued in 1973,
- (among other RFCs too numerous to mention) presented further
- comments on FTP. Finally, an "official" FTP document was
- published as RFC 454.
-
- By July 1973, considerable changes from the last versions of FTP
- were made, but the general structure remained the same. RFC 542
- was published as a new "official" specification to reflect these
- changes. However, many implementations based on the older
- specification were not updated.
-
- In 1974, RFCs 607 and 614 continued comments on FTP. RFC 624
- proposed further design changes and minor modifications. In 1975,
- RFC 686 entitled, "Leaving Well Enough Alone", discussed the
- differences between all of the early and later versions of FTP.
- RFC 691 presented a minor revision of RFC 686, regarding the
- subject of print files.
-
- Motivated by the transition from the NCP to the TCP as the
- underlying protocol, a phoenix was born out of all of the above
- efforts in RFC 765 as the specification of FTP for use on TCP.
-
- This current edition of the FTP specification is intended to
- correct some minor documentation errors, to improve the
- explanation of some protocol features, and to add some new
- optional commands.
-
-
-Postel & Reynolds [Page 2]
-
-
-
-RFC 959 October 1985
-File Transfer Protocol
-
-
- In particular, the following new optional commands are included in
- this edition of the specification:
-
- CDUP - Change to Parent Directory
-
- SMNT - Structure Mount
-
- STOU - Store Unique
-
- RMD - Remove Directory
-
- MKD - Make Directory
-
- PWD - Print Directory
-
- SYST - System
-
- This specification is compatible with the previous edition. A
- program implemented in conformance to the previous specification
- should automatically be in conformance to this specification.
-
- 2.2. TERMINOLOGY
-
- ASCII
-
- The ASCII character set is as defined in the ARPA-Internet
- Protocol Handbook. In FTP, ASCII characters are defined to be
- the lower half of an eight-bit code set (i.e., the most
- significant bit is zero).
-
- access controls
-
- Access controls define users' access privileges to the use of a
- system, and to the files in that system. Access controls are
- necessary to prevent unauthorized or accidental use of files.
- It is the prerogative of a server-FTP process to invoke access
- controls.
-
- byte size
-
- There are two byte sizes of interest in FTP: the logical byte
- size of the file, and the transfer byte size used for the
- transmission of the data. The transfer byte size is always 8
- bits. The transfer byte size is not necessarily the byte size
- in which data is to be stored in a system, nor the logical byte
- size for interpretation of the structure of the data.
-
-
-
-Postel & Reynolds [Page 3]
-
-
-
-RFC 959 October 1985
-File Transfer Protocol
-
-
- control connection
-
- The communication path between the USER-PI and SERVER-PI for
- the exchange of commands and replies. This connection follows
- the Telnet Protocol.
-
- data connection
-
- A full duplex connection over which data is transferred, in a
- specified mode and type. The data transferred may be a part of
- a file, an entire file or a number of files. The path may be
- between a server-DTP and a user-DTP, or between two
- server-DTPs.
-
- data port
-
- The passive data transfer process "listens" on the data port
- for a connection from the active transfer process in order to
- open the data connection.
-
- DTP
-
- The data transfer process establishes and manages the data
- connection. The DTP can be passive or active.
-
- End-of-Line
-
- The end-of-line sequence defines the separation of printing
- lines. The sequence is Carriage Return, followed by Line Feed.
-
- EOF
-
- The end-of-file condition that defines the end of a file being
- transferred.
-
- EOR
-
- The end-of-record condition that defines the end of a record
- being transferred.
-
- error recovery
-
- A procedure that allows a user to recover from certain errors
- such as failure of either host system or transfer process. In
- FTP, error recovery may involve restarting a file transfer at a
- given checkpoint.
-
-
-
-Postel & Reynolds [Page 4]
-
-
-
-RFC 959 October 1985
-File Transfer Protocol
-
-
- FTP commands
-
- A set of commands that comprise the control information flowing
- from the user-FTP to the server-FTP process.
-
- file
-
- An ordered set of computer data (including programs), of
- arbitrary length, uniquely identified by a pathname.
-
- mode
-
- The mode in which data is to be transferred via the data
- connection. The mode defines the data format during transfer
- including EOR and EOF. The transfer modes defined in FTP are
- described in the Section on Transmission Modes.
-
- NVT
-
- The Network Virtual Terminal as defined in the Telnet Protocol.
-
- NVFS
-
- The Network Virtual File System. A concept which defines a
- standard network file system with standard commands and
- pathname conventions.
-
- page
-
- A file may be structured as a set of independent parts called
- pages. FTP supports the transmission of discontinuous files as
- independent indexed pages.
-
- pathname
-
- Pathname is defined to be the character string which must be
- input to a file system by a user in order to identify a file.
- Pathname normally contains device and/or directory names, and
- file name specification. FTP does not yet specify a standard
- pathname convention. Each user must follow the file naming
- conventions of the file systems involved in the transfer.
-
- PI
-
- The protocol interpreter. The user and server sides of the
- protocol have distinct roles implemented in a user-PI and a
- server-PI.
-
-
-Postel & Reynolds [Page 5]
-
-
-
-RFC 959 October 1985
-File Transfer Protocol
-
-
- record
-
- A sequential file may be structured as a number of contiguous
- parts called records. Record structures are supported by FTP
- but a file need not have record structure.
-
- reply
-
- A reply is an acknowledgment (positive or negative) sent from
- server to user via the control connection in response to FTP
- commands. The general form of a reply is a completion code
- (including error codes) followed by a text string. The codes
- are for use by programs and the text is usually intended for
- human users.
-
- server-DTP
-
- The data transfer process, in its normal "active" state,
- establishes the data connection with the "listening" data port.
- It sets up parameters for transfer and storage, and transfers
- data on command from its PI. The DTP can be placed in a
- "passive" state to listen for, rather than initiate a
- connection on the data port.
-
- server-FTP process
-
- A process or set of processes which perform the function of
- file transfer in cooperation with a user-FTP process and,
- possibly, another server. The functions consist of a protocol
- interpreter (PI) and a data transfer process (DTP).
-
- server-PI
-
- The server protocol interpreter "listens" on Port L for a
- connection from a user-PI and establishes a control
- communication connection. It receives standard FTP commands
- from the user-PI, sends replies, and governs the server-DTP.
-
- type
-
- The data representation type used for data transfer and
- storage. Type implies certain transformations between the time
- of data storage and data transfer. The representation types
- defined in FTP are described in the Section on Establishing
- Data Connections.
-
-
-
-
-Postel & Reynolds [Page 6]
-
-
-
-RFC 959 October 1985
-File Transfer Protocol
-
-
- user
-
- A person or a process on behalf of a person wishing to obtain
- file transfer service. The human user may interact directly
- with a server-FTP process, but use of a user-FTP process is
- preferred since the protocol design is weighted towards
- automata.
-
- user-DTP
-
- The data transfer process "listens" on the data port for a
- connection from a server-FTP process. If two servers are
- transferring data between them, the user-DTP is inactive.
-
- user-FTP process
-
- A set of functions including a protocol interpreter, a data
- transfer process and a user interface which together perform
- the function of file transfer in cooperation with one or more
- server-FTP processes. The user interface allows a local
- language to be used in the command-reply dialogue with the
- user.
-
- user-PI
-
- The user protocol interpreter initiates the control connection
- from its port U to the server-FTP process, initiates FTP
- commands, and governs the user-DTP if that process is part of
- the file transfer.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Postel & Reynolds [Page 7]
-
-
-
-RFC 959 October 1985
-File Transfer Protocol
-
-
- 2.3. THE FTP MODEL
-
- With the above definitions in mind, the following model (shown in
- Figure 1) may be diagrammed for an FTP service.
-
- -------------
- |/---------\|
- || User || --------
- ||Interface|<--->| User |
- |\----^----/| --------
- ---------- | | |
- |/------\| FTP Commands |/----V----\|
- ||Server|<---------------->| User ||
- || PI || FTP Replies || PI ||
- |\--^---/| |\----^----/|
- | | | | | |
- -------- |/--V---\| Data |/----V----\| --------
- | File |<--->|Server|<---------------->| User |<--->| File |
- |System| || DTP || Connection || DTP || |System|
- -------- |\------/| |\---------/| --------
- ---------- -------------
-
- Server-FTP USER-FTP
-
- NOTES: 1. The data connection may be used in either direction.
- 2. The data connection need not exist all of the time.
-
- Figure 1 Model for FTP Use
-
- In the model described in Figure 1, the user-protocol interpreter
- initiates the control connection. The control connection follows
- the Telnet protocol. At the initiation of the user, standard FTP
- commands are generated by the user-PI and transmitted to the
- server process via the control connection. (The user may
- establish a direct control connection to the server-FTP, from a
- TAC terminal for example, and generate standard FTP commands
- independently, bypassing the user-FTP process.) Standard replies
- are sent from the server-PI to the user-PI over the control
- connection in response to the commands.
-
- The FTP commands specify the parameters for the data connection
- (data port, transfer mode, representation type, and structure) and
- the nature of file system operation (store, retrieve, append,
- delete, etc.). The user-DTP or its designate should "listen" on
- the specified data port, and the server initiate the data
- connection and data transfer in accordance with the specified
- parameters. It should be noted that the data port need not be in
-
-
-Postel & Reynolds [Page 8]
-
-
-
-RFC 959 October 1985
-File Transfer Protocol
-
-
- the same host that initiates the FTP commands via the control
- connection, but the user or the user-FTP process must ensure a
- "listen" on the specified data port. It ought to also be noted
- that the data connection may be used for simultaneous sending and
- receiving.
-
- In another situation a user might wish to transfer files between
- two hosts, neither of which is a local host. The user sets up
- control connections to the two servers and then arranges for a
- data connection between them. In this manner, control information
- is passed to the user-PI but data is transferred between the
- server data transfer processes. Following is a model of this
- server-server interaction.
-
-
- Control ------------ Control
- ---------->| User-FTP |<-----------
- | | User-PI | |
- | | "C" | |
- V ------------ V
- -------------- --------------
- | Server-FTP | Data Connection | Server-FTP |
- | "A" |<---------------------->| "B" |
- -------------- Port (A) Port (B) --------------
-
-
- Figure 2
-
- The protocol requires that the control connections be open while
- data transfer is in progress. It is the responsibility of the
- user to request the closing of the control connections when
- finished using the FTP service, while it is the server who takes
- the action. The server may abort data transfer if the control
- connections are closed without command.
-
- The Relationship between FTP and Telnet:
-
- The FTP uses the Telnet protocol on the control connection.
- This can be achieved in two ways: first, the user-PI or the
- server-PI may implement the rules of the Telnet Protocol
- directly in their own procedures; or, second, the user-PI or
- the server-PI may make use of the existing Telnet module in the
- system.
-
- Ease of implementaion, sharing code, and modular programming
- argue for the second approach. Efficiency and independence
-
-
-
-Postel & Reynolds [Page 9]
-
-
-
-RFC 959 October 1985
-File Transfer Protocol
-
-
- argue for the first approach. In practice, FTP relies on very
- little of the Telnet Protocol, so the first approach does not
- necessarily involve a large amount of code.
-
-3. DATA TRANSFER FUNCTIONS
-
- Files are transferred only via the data connection. The control
- connection is used for the transfer of commands, which describe the
- functions to be performed, and the replies to these commands (see the
- Section on FTP Replies). Several commands are concerned with the
- transfer of data between hosts. These data transfer commands include
- the MODE command which specify how the bits of the data are to be
- transmitted, and the STRUcture and TYPE commands, which are used to
- define the way in which the data are to be represented. The
- transmission and representation are basically independent but the
- "Stream" transmission mode is dependent on the file structure
- attribute and if "Compressed" transmission mode is used, the nature
- of the filler byte depends on the representation type.
-
- 3.1. DATA REPRESENTATION AND STORAGE
-
- Data is transferred from a storage device in the sending host to a
- storage device in the receiving host. Often it is necessary to
- perform certain transformations on the data because data storage
- representations in the two systems are different. For example,
- NVT-ASCII has different data storage representations in different
- systems. DEC TOPS-20s's generally store NVT-ASCII as five 7-bit
- ASCII characters, left-justified in a 36-bit word. IBM Mainframe's
- store NVT-ASCII as 8-bit EBCDIC codes. Multics stores NVT-ASCII
- as four 9-bit characters in a 36-bit word. It is desirable to
- convert characters into the standard NVT-ASCII representation when
- transmitting text between dissimilar systems. The sending and
- receiving sites would have to perform the necessary
- transformations between the standard representation and their
- internal representations.
-
- A different problem in representation arises when transmitting
- binary data (not character codes) between host systems with
- different word lengths. It is not always clear how the sender
- should send data, and the receiver store it. For example, when
- transmitting 32-bit bytes from a 32-bit word-length system to a
- 36-bit word-length system, it may be desirable (for reasons of
- efficiency and usefulness) to store the 32-bit bytes
- right-justified in a 36-bit word in the latter system. In any
- case, the user should have the option of specifying data
- representation and transformation functions. It should be noted
-
-
-
-Postel & Reynolds [Page 10]
-
-
-
-RFC 959 October 1985
-File Transfer Protocol
-
-
- that FTP provides for very limited data type representations.
- Transformations desired beyond this limited capability should be
- performed by the user directly.
-
- 3.1.1. DATA TYPES
-
- Data representations are handled in FTP by a user specifying a
- representation type. This type may implicitly (as in ASCII or
- EBCDIC) or explicitly (as in Local byte) define a byte size for
- interpretation which is referred to as the "logical byte size."
- Note that this has nothing to do with the byte size used for
- transmission over the data connection, called the "transfer
- byte size", and the two should not be confused. For example,
- NVT-ASCII has a logical byte size of 8 bits. If the type is
- Local byte, then the TYPE command has an obligatory second
- parameter specifying the logical byte size. The transfer byte
- size is always 8 bits.
-
- 3.1.1.1. ASCII TYPE
-
- This is the default type and must be accepted by all FTP
- implementations. It is intended primarily for the transfer
- of text files, except when both hosts would find the EBCDIC
- type more convenient.
-
- The sender converts the data from an internal character
- representation to the standard 8-bit NVT-ASCII
- representation (see the Telnet specification). The receiver
- will convert the data from the standard form to his own
- internal form.
-
- In accordance with the NVT standard, the <CRLF> sequence
- should be used where necessary to denote the end of a line
- of text. (See the discussion of file structure at the end
- of the Section on Data Representation and Storage.)
-
- Using the standard NVT-ASCII representation means that data
- must be interpreted as 8-bit bytes.
-
- The Format parameter for ASCII and EBCDIC types is discussed
- below.
-
-
-
-
-
-
-
-
-Postel & Reynolds [Page 11]
-
-
-
-RFC 959 October 1985
-File Transfer Protocol
-
-
- 3.1.1.2. EBCDIC TYPE
-
- This type is intended for efficient transfer between hosts
- which use EBCDIC for their internal character
- representation.
-
- For transmission, the data are represented as 8-bit EBCDIC
- characters. The character code is the only difference
- between the functional specifications of EBCDIC and ASCII
- types.
-
- End-of-line (as opposed to end-of-record--see the discussion
- of structure) will probably be rarely used with EBCDIC type
- for purposes of denoting structure, but where it is
- necessary the <NL> character should be used.
-
- 3.1.1.3. IMAGE TYPE
-
- The data are sent as contiguous bits which, for transfer,
- are packed into the 8-bit transfer bytes. The receiving
- site must store the data as contiguous bits. The structure
- of the storage system might necessitate the padding of the
- file (or of each record, for a record-structured file) to
- some convenient boundary (byte, word or block). This
- padding, which must be all zeros, may occur only at the end
- of the file (or at the end of each record) and there must be
- a way of identifying the padding bits so that they may be
- stripped off if the file is retrieved. The padding
- transformation should be well publicized to enable a user to
- process a file at the storage site.
-
- Image type is intended for the efficient storage and
- retrieval of files and for the transfer of binary data. It
- is recommended that this type be accepted by all FTP
- implementations.
-
- 3.1.1.4. LOCAL TYPE
-
- The data is transferred in logical bytes of the size
- specified by the obligatory second parameter, Byte size.
- The value of Byte size must be a decimal integer; there is
- no default value. The logical byte size is not necessarily
- the same as the transfer byte size. If there is a
- difference in byte sizes, then the logical bytes should be
- packed contiguously, disregarding transfer byte boundaries
- and with any necessary padding at the end.
-
-
-
-Postel & Reynolds [Page 12]
-
-
-
-RFC 959 October 1985
-File Transfer Protocol
-
-
- When the data reaches the receiving host, it will be
- transformed in a manner dependent on the logical byte size
- and the particular host. This transformation must be
- invertible (i.e., an identical file can be retrieved if the
- same parameters are used) and should be well publicized by
- the FTP implementors.
-
- For example, a user sending 36-bit floating-point numbers to
- a host with a 32-bit word could send that data as Local byte
- with a logical byte size of 36. The receiving host would
- then be expected to store the logical bytes so that they
- could be easily manipulated; in this example putting the
- 36-bit logical bytes into 64-bit double words should
- suffice.
-
- In another example, a pair of hosts with a 36-bit word size
- may send data to one another in words by using TYPE L 36.
- The data would be sent in the 8-bit transmission bytes
- packed so that 9 transmission bytes carried two host words.
-
- 3.1.1.5. FORMAT CONTROL
-
- The types ASCII and EBCDIC also take a second (optional)
- parameter; this is to indicate what kind of vertical format
- control, if any, is associated with a file. The following
- data representation types are defined in FTP:
-
- A character file may be transferred to a host for one of
- three purposes: for printing, for storage and later
- retrieval, or for processing. If a file is sent for
- printing, the receiving host must know how the vertical
- format control is represented. In the second case, it must
- be possible to store a file at a host and then retrieve it
- later in exactly the same form. Finally, it should be
- possible to move a file from one host to another and process
- the file at the second host without undue trouble. A single
- ASCII or EBCDIC format does not satisfy all these
- conditions. Therefore, these types have a second parameter
- specifying one of the following three formats:
-
- 3.1.1.5.1. NON PRINT
-
- This is the default format to be used if the second
- (format) parameter is omitted. Non-print format must be
- accepted by all FTP implementations.
-
-
-
-
-Postel & Reynolds [Page 13]
-
-
-
-RFC 959 October 1985
-File Transfer Protocol
-
-
- The file need contain no vertical format information. If
- it is passed to a printer process, this process may
- assume standard values for spacing and margins.
-
- Normally, this format will be used with files destined
- for processing or just storage.
-
- 3.1.1.5.2. TELNET FORMAT CONTROLS
-
- The file contains ASCII/EBCDIC vertical format controls
- (i.e., <CR>, <LF>, <NL>, <VT>, <FF>) which the printer
- process will interpret appropriately. <CRLF>, in exactly
- this sequence, also denotes end-of-line.
-
- 3.1.1.5.2. CARRIAGE CONTROL (ASA)
-
- The file contains ASA (FORTRAN) vertical format control
- characters. (See RFC 740 Appendix C; and Communications
- of the ACM, Vol. 7, No. 10, p. 606, October 1964.) In a
- line or a record formatted according to the ASA Standard,
- the first character is not to be printed. Instead, it
- should be used to determine the vertical movement of the
- paper which should take place before the rest of the
- record is printed.
-
- The ASA Standard specifies the following control
- characters:
-
- Character Vertical Spacing
-
- blank Move paper up one line
- 0 Move paper up two lines
- 1 Move paper to top of next page
- + No movement, i.e., overprint
-
- Clearly there must be some way for a printer process to
- distinguish the end of the structural entity. If a file
- has record structure (see below) this is no problem;
- records will be explicitly marked during transfer and
- storage. If the file has no record structure, the <CRLF>
- end-of-line sequence is used to separate printing lines,
- but these format effectors are overridden by the ASA
- controls.
-
-
-
-
-
-
-Postel & Reynolds [Page 14]
-
-
-
-RFC 959 October 1985
-File Transfer Protocol
-
-
- 3.1.2. DATA STRUCTURES
-
- In addition to different representation types, FTP allows the
- structure of a file to be specified. Three file structures are
- defined in FTP:
-
- file-structure, where there is no internal structure and
- the file is considered to be a
- continuous sequence of data bytes,
-
- record-structure, where the file is made up of sequential
- records,
-
- and page-structure, where the file is made up of independent
- indexed pages.
-
- File-structure is the default to be assumed if the STRUcture
- command has not been used but both file and record structures
- must be accepted for "text" files (i.e., files with TYPE ASCII
- or EBCDIC) by all FTP implementations. The structure of a file
- will affect both the transfer mode of a file (see the Section
- on Transmission Modes) and the interpretation and storage of
- the file.
-
- The "natural" structure of a file will depend on which host
- stores the file. A source-code file will usually be stored on
- an IBM Mainframe in fixed length records but on a DEC TOPS-20
- as a stream of characters partitioned into lines, for example
- by <CRLF>. If the transfer of files between such disparate
- sites is to be useful, there must be some way for one site to
- recognize the other's assumptions about the file.
-
- With some sites being naturally file-oriented and others
- naturally record-oriented there may be problems if a file with
- one structure is sent to a host oriented to the other. If a
- text file is sent with record-structure to a host which is file
- oriented, then that host should apply an internal
- transformation to the file based on the record structure.
- Obviously, this transformation should be useful, but it must
- also be invertible so that an identical file may be retrieved
- using record structure.
-
- In the case of a file being sent with file-structure to a
- record-oriented host, there exists the question of what
- criteria the host should use to divide the file into records
- which can be processed locally. If this division is necessary,
- the FTP implementation should use the end-of-line sequence,
-
-
-Postel & Reynolds [Page 15]
-
-
-
-RFC 959 October 1985
-File Transfer Protocol
-
-
- <CRLF> for ASCII, or <NL> for EBCDIC text files, as the
- delimiter. If an FTP implementation adopts this technique, it
- must be prepared to reverse the transformation if the file is
- retrieved with file-structure.
-
- 3.1.2.1. FILE STRUCTURE
-
- File structure is the default to be assumed if the STRUcture
- command has not been used.
-
- In file-structure there is no internal structure and the
- file is considered to be a continuous sequence of data
- bytes.
-
- 3.1.2.2. RECORD STRUCTURE
-
- Record structures must be accepted for "text" files (i.e.,
- files with TYPE ASCII or EBCDIC) by all FTP implementations.
-
- In record-structure the file is made up of sequential
- records.
-
- 3.1.2.3. PAGE STRUCTURE
-
- To transmit files that are discontinuous, FTP defines a page
- structure. Files of this type are sometimes known as
- "random access files" or even as "holey files". In these
- files there is sometimes other information associated with
- the file as a whole (e.g., a file descriptor), or with a
- section of the file (e.g., page access controls), or both.
- In FTP, the sections of the file are called pages.
-
- To provide for various page sizes and associated
- information, each page is sent with a page header. The page
- header has the following defined fields:
-
- Header Length
-
- The number of logical bytes in the page header
- including this byte. The minimum header length is 4.
-
- Page Index
-
- The logical page number of this section of the file.
- This is not the transmission sequence number of this
- page, but the index used to identify this page of the
- file.
-
-
-Postel & Reynolds [Page 16]
-
-
-
-RFC 959 October 1985
-File Transfer Protocol
-
-
- Data Length
-
- The number of logical bytes in the page data. The
- minimum data length is 0.
-
- Page Type
-
- The type of page this is. The following page types
- are defined:
-
- 0 = Last Page
-
- This is used to indicate the end of a paged
- structured transmission. The header length must
- be 4, and the data length must be 0.
-
- 1 = Simple Page
-
- This is the normal type for simple paged files
- with no page level associated control
- information. The header length must be 4.
-
- 2 = Descriptor Page
-
- This type is used to transmit the descriptive
- information for the file as a whole.
-
- 3 = Access Controlled Page
-
- This type includes an additional header field
- for paged files with page level access control
- information. The header length must be 5.
-
- Optional Fields
-
- Further header fields may be used to supply per page
- control information, for example, per page access
- control.
-
- All fields are one logical byte in length. The logical byte
- size is specified by the TYPE command. See Appendix I for
- further details and a specific case at the page structure.
-
- A note of caution about parameters: a file must be stored and
- retrieved with the same parameters if the retrieved version is to
-
-
-
-
-Postel & Reynolds [Page 17]
-
-
-
-RFC 959 October 1985
-File Transfer Protocol
-
-
- be identical to the version originally transmitted. Conversely,
- FTP implementations must return a file identical to the original
- if the parameters used to store and retrieve a file are the same.
-
- 3.2. ESTABLISHING DATA CONNECTIONS
-
- The mechanics of transferring data consists of setting up the data
- connection to the appropriate ports and choosing the parameters
- for transfer. Both the user and the server-DTPs have a default
- data port. The user-process default data port is the same as the
- control connection port (i.e., U). The server-process default
- data port is the port adjacent to the control connection port
- (i.e., L-1).
-
- The transfer byte size is 8-bit bytes. This byte size is relevant
- only for the actual transfer of the data; it has no bearing on
- representation of the data within a host's file system.
-
- The passive data transfer process (this may be a user-DTP or a
- second server-DTP) shall "listen" on the data port prior to
- sending a transfer request command. The FTP request command
- determines the direction of the data transfer. The server, upon
- receiving the transfer request, will initiate the data connection
- to the port. When the connection is established, the data
- transfer begins between DTP's, and the server-PI sends a
- confirming reply to the user-PI.
-
- Every FTP implementation must support the use of the default data
- ports, and only the USER-PI can initiate a change to non-default
- ports.
-
- It is possible for the user to specify an alternate data port by
- use of the PORT command. The user may want a file dumped on a TAC
- line printer or retrieved from a third party host. In the latter
- case, the user-PI sets up control connections with both
- server-PI's. One server is then told (by an FTP command) to
- "listen" for a connection which the other will initiate. The
- user-PI sends one server-PI a PORT command indicating the data
- port of the other. Finally, both are sent the appropriate
- transfer commands. The exact sequence of commands and replies
- sent between the user-controller and the servers is defined in the
- Section on FTP Replies.
-
- In general, it is the server's responsibility to maintain the data
- connection--to initiate it and to close it. The exception to this
-
-
-
-
-Postel & Reynolds [Page 18]
-
-
-
-RFC 959 October 1985
-File Transfer Protocol
-
-
- is when the user-DTP is sending the data in a transfer mode that
- requires the connection to be closed to indicate EOF. The server
- MUST close the data connection under the following conditions:
-
- 1. The server has completed sending data in a transfer mode
- that requires a close to indicate EOF.
-
- 2. The server receives an ABORT command from the user.
-
- 3. The port specification is changed by a command from the
- user.
-
- 4. The control connection is closed legally or otherwise.
-
- 5. An irrecoverable error condition occurs.
-
- Otherwise the close is a server option, the exercise of which the
- server must indicate to the user-process by either a 250 or 226
- reply only.
-
- 3.3. DATA CONNECTION MANAGEMENT
-
- Default Data Connection Ports: All FTP implementations must
- support use of the default data connection ports, and only the
- User-PI may initiate the use of non-default ports.
-
- Negotiating Non-Default Data Ports: The User-PI may specify a
- non-default user side data port with the PORT command. The
- User-PI may request the server side to identify a non-default
- server side data port with the PASV command. Since a connection
- is defined by the pair of addresses, either of these actions is
- enough to get a different data connection, still it is permitted
- to do both commands to use new ports on both ends of the data
- connection.
-
- Reuse of the Data Connection: When using the stream mode of data
- transfer the end of the file must be indicated by closing the
- connection. This causes a problem if multiple files are to be
- transfered in the session, due to need for TCP to hold the
- connection record for a time out period to guarantee the reliable
- communication. Thus the connection can not be reopened at once.
-
- There are two solutions to this problem. The first is to
- negotiate a non-default port. The second is to use another
- transfer mode.
-
- A comment on transfer modes. The stream transfer mode is
-
-
-Postel & Reynolds [Page 19]
-
-
-
-RFC 959 October 1985
-File Transfer Protocol
-
-
- inherently unreliable, since one can not determine if the
- connection closed prematurely or not. The other transfer modes
- (Block, Compressed) do not close the connection to indicate the
- end of file. They have enough FTP encoding that the data
- connection can be parsed to determine the end of the file.
- Thus using these modes one can leave the data connection open
- for multiple file transfers.
-
- 3.4. TRANSMISSION MODES
-
- The next consideration in transferring data is choosing the
- appropriate transmission mode. There are three modes: one which
- formats the data and allows for restart procedures; one which also
- compresses the data for efficient transfer; and one which passes
- the data with little or no processing. In this last case the mode
- interacts with the structure attribute to determine the type of
- processing. In the compressed mode, the representation type
- determines the filler byte.
-
- All data transfers must be completed with an end-of-file (EOF)
- which may be explicitly stated or implied by the closing of the
- data connection. For files with record structure, all the
- end-of-record markers (EOR) are explicit, including the final one.
- For files transmitted in page structure a "last-page" page type is
- used.
-
- NOTE: In the rest of this section, byte means "transfer byte"
- except where explicitly stated otherwise.
-
- For the purpose of standardized transfer, the sending host will
- translate its internal end of line or end of record denotation
- into the representation prescribed by the transfer mode and file
- structure, and the receiving host will perform the inverse
- translation to its internal denotation. An IBM Mainframe record
- count field may not be recognized at another host, so the
- end-of-record information may be transferred as a two byte control
- code in Stream mode or as a flagged bit in a Block or Compressed
- mode descriptor. End-of-line in an ASCII or EBCDIC file with no
- record structure should be indicated by <CRLF> or <NL>,
- respectively. Since these transformations imply extra work for
- some systems, identical systems transferring non-record structured
- text files might wish to use a binary representation and stream
- mode for the transfer.
-
-
-
-
-
-
-Postel & Reynolds [Page 20]
-
-
-
-RFC 959 October 1985
-File Transfer Protocol
-
-
- The following transmission modes are defined in FTP:
-
- 3.4.1. STREAM MODE
-
- The data is transmitted as a stream of bytes. There is no
- restriction on the representation type used; record structures
- are allowed.
-
- In a record structured file EOR and EOF will each be indicated
- by a two-byte control code. The first byte of the control code
- will be all ones, the escape character. The second byte will
- have the low order bit on and zeros elsewhere for EOR and the
- second low order bit on for EOF; that is, the byte will have
- value 1 for EOR and value 2 for EOF. EOR and EOF may be
- indicated together on the last byte transmitted by turning both
- low order bits on (i.e., the value 3). If a byte of all ones
- was intended to be sent as data, it should be repeated in the
- second byte of the control code.
-
- If the structure is a file structure, the EOF is indicated by
- the sending host closing the data connection and all bytes are
- data bytes.
-
- 3.4.2. BLOCK MODE
-
- The file is transmitted as a series of data blocks preceded by
- one or more header bytes. The header bytes contain a count
- field, and descriptor code. The count field indicates the
- total length of the data block in bytes, thus marking the
- beginning of the next data block (there are no filler bits).
- The descriptor code defines: last block in the file (EOF) last
- block in the record (EOR), restart marker (see the Section on
- Error Recovery and Restart) or suspect data (i.e., the data
- being transferred is suspected of errors and is not reliable).
- This last code is NOT intended for error control within FTP.
- It is motivated by the desire of sites exchanging certain types
- of data (e.g., seismic or weather data) to send and receive all
- the data despite local errors (such as "magnetic tape read
- errors"), but to indicate in the transmission that certain
- portions are suspect). Record structures are allowed in this
- mode, and any representation type may be used.
-
- The header consists of the three bytes. Of the 24 bits of
- header information, the 16 low order bits shall represent byte
- count, and the 8 high order bits shall represent descriptor
- codes as shown below.
-
-
-
-Postel & Reynolds [Page 21]
-
-
-
-RFC 959 October 1985
-File Transfer Protocol
-
-
- Block Header
-
- +----------------+----------------+----------------+
- | Descriptor | Byte Count |
- | 8 bits | 16 bits |
- +----------------+----------------+----------------+
-
-
- The descriptor codes are indicated by bit flags in the
- descriptor byte. Four codes have been assigned, where each
- code number is the decimal value of the corresponding bit in
- the byte.
-
- Code Meaning
-
- 128 End of data block is EOR
- 64 End of data block is EOF
- 32 Suspected errors in data block
- 16 Data block is a restart marker
-
- With this encoding, more than one descriptor coded condition
- may exist for a particular block. As many bits as necessary
- may be flagged.
-
- The restart marker is embedded in the data stream as an
- integral number of 8-bit bytes representing printable
- characters in the language being used over the control
- connection (e.g., default--NVT-ASCII). <SP> (Space, in the
- appropriate language) must not be used WITHIN a restart marker.
-
- For example, to transmit a six-character marker, the following
- would be sent:
-
- +--------+--------+--------+
- |Descrptr| Byte count |
- |code= 16| = 6 |
- +--------+--------+--------+
-
- +--------+--------+--------+
- | Marker | Marker | Marker |
- | 8 bits | 8 bits | 8 bits |
- +--------+--------+--------+
-
- +--------+--------+--------+
- | Marker | Marker | Marker |
- | 8 bits | 8 bits | 8 bits |
- +--------+--------+--------+
-
-
-Postel & Reynolds [Page 22]
-
-
-
-RFC 959 October 1985
-File Transfer Protocol
-
-
- 3.4.3. COMPRESSED MODE
-
- There are three kinds of information to be sent: regular data,
- sent in a byte string; compressed data, consisting of
- replications or filler; and control information, sent in a
- two-byte escape sequence. If n>0 bytes (up to 127) of regular
- data are sent, these n bytes are preceded by a byte with the
- left-most bit set to 0 and the right-most 7 bits containing the
- number n.
-
- Byte string:
-
- 1 7 8 8
- +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
- |0| n | | d(1) | ... | d(n) |
- +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
- ^ ^
- |---n bytes---|
- of data
-
- String of n data bytes d(1),..., d(n)
- Count n must be positive.
-
- To compress a string of n replications of the data byte d, the
- following 2 bytes are sent:
-
- Replicated Byte:
-
- 2 6 8
- +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
- |1 0| n | | d |
- +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
-
- A string of n filler bytes can be compressed into a single
- byte, where the filler byte varies with the representation
- type. If the type is ASCII or EBCDIC the filler byte is <SP>
- (Space, ASCII code 32, EBCDIC code 64). If the type is Image
- or Local byte the filler is a zero byte.
-
- Filler String:
-
- 2 6
- +-+-+-+-+-+-+-+-+
- |1 1| n |
- +-+-+-+-+-+-+-+-+
-
- The escape sequence is a double byte, the first of which is the
-
-
-Postel & Reynolds [Page 23]
-
-
-
-RFC 959 October 1985
-File Transfer Protocol
-
-
- escape byte (all zeros) and the second of which contains
- descriptor codes as defined in Block mode. The descriptor
- codes have the same meaning as in Block mode and apply to the
- succeeding string of bytes.
-
- Compressed mode is useful for obtaining increased bandwidth on
- very large network transmissions at a little extra CPU cost.
- It can be most effectively used to reduce the size of printer
- files such as those generated by RJE hosts.
-
- 3.5. ERROR RECOVERY AND RESTART
-
- There is no provision for detecting bits lost or scrambled in data
- transfer; this level of error control is handled by the TCP.
- However, a restart procedure is provided to protect users from
- gross system failures (including failures of a host, an
- FTP-process, or the underlying network).
-
- The restart procedure is defined only for the block and compressed
- modes of data transfer. It requires the sender of data to insert
- a special marker code in the data stream with some marker
- information. The marker information has meaning only to the
- sender, but must consist of printable characters in the default or
- negotiated language of the control connection (ASCII or EBCDIC).
- The marker could represent a bit-count, a record-count, or any
- other information by which a system may identify a data
- checkpoint. The receiver of data, if it implements the restart
- procedure, would then mark the corresponding position of this
- marker in the receiving system, and return this information to the
- user.
-
- In the event of a system failure, the user can restart the data
- transfer by identifying the marker point with the FTP restart
- procedure. The following example illustrates the use of the
- restart procedure.
-
- The sender of the data inserts an appropriate marker block in the
- data stream at a convenient point. The receiving host marks the
- corresponding data point in its file system and conveys the last
- known sender and receiver marker information to the user, either
- directly or over the control connection in a 110 reply (depending
- on who is the sender). In the event of a system failure, the user
- or controller process restarts the server at the last server
- marker by sending a restart command with server's marker code as
- its argument. The restart command is transmitted over the control
-
-
-
-
-Postel & Reynolds [Page 24]
-
-
-
-RFC 959 October 1985
-File Transfer Protocol
-
-
- connection and is immediately followed by the command (such as
- RETR, STOR or LIST) which was being executed when the system
- failure occurred.
-
-4. FILE TRANSFER FUNCTIONS
-
- The communication channel from the user-PI to the server-PI is
- established as a TCP connection from the user to the standard server
- port. The user protocol interpreter is responsible for sending FTP
- commands and interpreting the replies received; the server-PI
- interprets commands, sends replies and directs its DTP to set up the
- data connection and transfer the data. If the second party to the
- data transfer (the passive transfer process) is the user-DTP, then it
- is governed through the internal protocol of the user-FTP host; if it
- is a second server-DTP, then it is governed by its PI on command from
- the user-PI. The FTP replies are discussed in the next section. In
- the description of a few of the commands in this section, it is
- helpful to be explicit about the possible replies.
-
- 4.1. FTP COMMANDS
-
- 4.1.1. ACCESS CONTROL COMMANDS
-
- The following commands specify access control identifiers
- (command codes are shown in parentheses).
-
- USER NAME (USER)
-
- The argument field is a Telnet string identifying the user.
- The user identification is that which is required by the
- server for access to its file system. This command will
- normally be the first command transmitted by the user after
- the control connections are made (some servers may require
- this). Additional identification information in the form of
- a password and/or an account command may also be required by
- some servers. Servers may allow a new USER command to be
- entered at any point in order to change the access control
- and/or accounting information. This has the effect of
- flushing any user, password, and account information already
- supplied and beginning the login sequence again. All
- transfer parameters are unchanged and any file transfer in
- progress is completed under the old access control
- parameters.
-
-
-
-
-
-
-Postel & Reynolds [Page 25]
-
-
-
-RFC 959 October 1985
-File Transfer Protocol
-
-
- PASSWORD (PASS)
-
- The argument field is a Telnet string specifying the user's
- password. This command must be immediately preceded by the
- user name command, and, for some sites, completes the user's
- identification for access control. Since password
- information is quite sensitive, it is desirable in general
- to "mask" it or suppress typeout. It appears that the
- server has no foolproof way to achieve this. It is
- therefore the responsibility of the user-FTP process to hide
- the sensitive password information.
-
- ACCOUNT (ACCT)
-
- The argument field is a Telnet string identifying the user's
- account. The command is not necessarily related to the USER
- command, as some sites may require an account for login and
- others only for specific access, such as storing files. In
- the latter case the command may arrive at any time.
-
- There are reply codes to differentiate these cases for the
- automation: when account information is required for login,
- the response to a successful PASSword command is reply code
- 332. On the other hand, if account information is NOT
- required for login, the reply to a successful PASSword
- command is 230; and if the account information is needed for
- a command issued later in the dialogue, the server should
- return a 332 or 532 reply depending on whether it stores
- (pending receipt of the ACCounT command) or discards the
- command, respectively.
-
- CHANGE WORKING DIRECTORY (CWD)
-
- This command allows the user to work with a different
- directory or dataset for file storage or retrieval without
- altering his login or accounting information. Transfer
- parameters are similarly unchanged. The argument is a
- pathname specifying a directory or other system dependent
- file group designator.
-
- CHANGE TO PARENT DIRECTORY (CDUP)
-
- This command is a special case of CWD, and is included to
- simplify the implementation of programs for transferring
- directory trees between operating systems having different
-
-
-
-
-Postel & Reynolds [Page 26]
-
-
-
-RFC 959 October 1985
-File Transfer Protocol
-
-
- syntaxes for naming the parent directory. The reply codes
- shall be identical to the reply codes of CWD. See
- Appendix II for further details.
-
- STRUCTURE MOUNT (SMNT)
-
- This command allows the user to mount a different file
- system data structure without altering his login or
- accounting information. Transfer parameters are similarly
- unchanged. The argument is a pathname specifying a
- directory or other system dependent file group designator.
-
- REINITIALIZE (REIN)
-
- This command terminates a USER, flushing all I/O and account
- information, except to allow any transfer in progress to be
- completed. All parameters are reset to the default settings
- and the control connection is left open. This is identical
- to the state in which a user finds himself immediately after
- the control connection is opened. A USER command may be
- expected to follow.
-
- LOGOUT (QUIT)
-
- This command terminates a USER and if file transfer is not
- in progress, the server closes the control connection. If
- file transfer is in progress, the connection will remain
- open for result response and the server will then close it.
- If the user-process is transferring files for several USERs
- but does not wish to close and then reopen connections for
- each, then the REIN command should be used instead of QUIT.
-
- An unexpected close on the control connection will cause the
- server to take the effective action of an abort (ABOR) and a
- logout (QUIT).
-
- 4.1.2. TRANSFER PARAMETER COMMANDS
-
- All data transfer parameters have default values, and the
- commands specifying data transfer parameters are required only
- if the default parameter values are to be changed. The default
- value is the last specified value, or if no value has been
- specified, the standard default value is as stated here. This
- implies that the server must "remember" the applicable default
- values. The commands may be in any order except that they must
- precede the FTP service request. The following commands
- specify data transfer parameters:
-
-
-Postel & Reynolds [Page 27]
-
-
-
-RFC 959 October 1985
-File Transfer Protocol
-
-
- DATA PORT (PORT)
-
- The argument is a HOST-PORT specification for the data port
- to be used in data connection. There are defaults for both
- the user and server data ports, and under normal
- circumstances this command and its reply are not needed. If
- this command is used, the argument is the concatenation of a
- 32-bit internet host address and a 16-bit TCP port address.
- This address information is broken into 8-bit fields and the
- value of each field is transmitted as a decimal number (in
- character string representation). The fields are separated
- by commas. A port command would be:
-
- PORT h1,h2,h3,h4,p1,p2
-
- where h1 is the high order 8 bits of the internet host
- address.
-
- PASSIVE (PASV)
-
- This command requests the server-DTP to "listen" on a data
- port (which is not its default data port) and to wait for a
- connection rather than initiate one upon receipt of a
- transfer command. The response to this command includes the
- host and port address this server is listening on.
-
- REPRESENTATION TYPE (TYPE)
-
- The argument specifies the representation type as described
- in the Section on Data Representation and Storage. Several
- types take a second parameter. The first parameter is
- denoted by a single Telnet character, as is the second
- Format parameter for ASCII and EBCDIC; the second parameter
- for local byte is a decimal integer to indicate Bytesize.
- The parameters are separated by a <SP> (Space, ASCII code
- 32).
-
- The following codes are assigned for type:
-
- \ /
- A - ASCII | | N - Non-print
- |-><-| T - Telnet format effectors
- E - EBCDIC| | C - Carriage Control (ASA)
- / \
- I - Image
-
- L <byte size> - Local byte Byte size
-
-
-Postel & Reynolds [Page 28]
-
-
-
-RFC 959 October 1985
-File Transfer Protocol
-
-
- The default representation type is ASCII Non-print. If the
- Format parameter is changed, and later just the first
- argument is changed, Format then returns to the Non-print
- default.
-
- FILE STRUCTURE (STRU)
-
- The argument is a single Telnet character code specifying
- file structure described in the Section on Data
- Representation and Storage.
-
- The following codes are assigned for structure:
-
- F - File (no record structure)
- R - Record structure
- P - Page structure
-
- The default structure is File.
-
- TRANSFER MODE (MODE)
-
- The argument is a single Telnet character code specifying
- the data transfer modes described in the Section on
- Transmission Modes.
-
- The following codes are assigned for transfer modes:
-
- S - Stream
- B - Block
- C - Compressed
-
- The default transfer mode is Stream.
-
- 4.1.3. FTP SERVICE COMMANDS
-
- The FTP service commands define the file transfer or the file
- system function requested by the user. The argument of an FTP
- service command will normally be a pathname. The syntax of
- pathnames must conform to server site conventions (with
- standard defaults applicable), and the language conventions of
- the control connection. The suggested default handling is to
- use the last specified device, directory or file name, or the
- standard default defined for local users. The commands may be
- in any order except that a "rename from" command must be
- followed by a "rename to" command and the restart command must
- be followed by the interrupted service command (e.g., STOR or
- RETR). The data, when transferred in response to FTP service
-
-
-Postel & Reynolds [Page 29]
-
-
-
-RFC 959 October 1985
-File Transfer Protocol
-
-
- commands, shall always be sent over the data connection, except
- for certain informative replies. The following commands
- specify FTP service requests:
-
- RETRIEVE (RETR)
-
- This command causes the server-DTP to transfer a copy of the
- file, specified in the pathname, to the server- or user-DTP
- at the other end of the data connection. The status and
- contents of the file at the server site shall be unaffected.
-
- STORE (STOR)
-
- This command causes the server-DTP to accept the data
- transferred via the data connection and to store the data as
- a file at the server site. If the file specified in the
- pathname exists at the server site, then its contents shall
- be replaced by the data being transferred. A new file is
- created at the server site if the file specified in the
- pathname does not already exist.
-
- STORE UNIQUE (STOU)
-
- This command behaves like STOR except that the resultant
- file is to be created in the current directory under a name
- unique to that directory. The 250 Transfer Started response
- must include the name generated.
-
- APPEND (with create) (APPE)
-
- This command causes the server-DTP to accept the data
- transferred via the data connection and to store the data in
- a file at the server site. If the file specified in the
- pathname exists at the server site, then the data shall be
- appended to that file; otherwise the file specified in the
- pathname shall be created at the server site.
-
- ALLOCATE (ALLO)
-
- This command may be required by some servers to reserve
- sufficient storage to accommodate the new file to be
- transferred. The argument shall be a decimal integer
- representing the number of bytes (using the logical byte
- size) of storage to be reserved for the file. For files
- sent with record or page structure a maximum record or page
- size (in logical bytes) might also be necessary; this is
- indicated by a decimal integer in a second argument field of
-
-
-Postel & Reynolds [Page 30]
-
-
-
-RFC 959 October 1985
-File Transfer Protocol
-
-
- the command. This second argument is optional, but when
- present should be separated from the first by the three
- Telnet characters <SP> R <SP>. This command shall be
- followed by a STORe or APPEnd command. The ALLO command
- should be treated as a NOOP (no operation) by those servers
- which do not require that the maximum size of the file be
- declared beforehand, and those servers interested in only
- the maximum record or page size should accept a dummy value
- in the first argument and ignore it.
-
- RESTART (REST)
-
- The argument field represents the server marker at which
- file transfer is to be restarted. This command does not
- cause file transfer but skips over the file to the specified
- data checkpoint. This command shall be immediately followed
- by the appropriate FTP service command which shall cause
- file transfer to resume.
-
- RENAME FROM (RNFR)
-
- This command specifies the old pathname of the file which is
- to be renamed. This command must be immediately followed by
- a "rename to" command specifying the new file pathname.
-
- RENAME TO (RNTO)
-
- This command specifies the new pathname of the file
- specified in the immediately preceding "rename from"
- command. Together the two commands cause a file to be
- renamed.
-
- ABORT (ABOR)
-
- This command tells the server to abort the previous FTP
- service command and any associated transfer of data. The
- abort command may require "special action", as discussed in
- the Section on FTP Commands, to force recognition by the
- server. No action is to be taken if the previous command
- has been completed (including data transfer). The control
- connection is not to be closed by the server, but the data
- connection must be closed.
-
- There are two cases for the server upon receipt of this
- command: (1) the FTP service command was already completed,
- or (2) the FTP service command is still in progress.
-
-
-
-Postel & Reynolds [Page 31]
-
-
-
-RFC 959 October 1985
-File Transfer Protocol
-
-
- In the first case, the server closes the data connection
- (if it is open) and responds with a 226 reply, indicating
- that the abort command was successfully processed.
-
- In the second case, the server aborts the FTP service in
- progress and closes the data connection, returning a 426
- reply to indicate that the service request terminated
- abnormally. The server then sends a 226 reply,
- indicating that the abort command was successfully
- processed.
-
- DELETE (DELE)
-
- This command causes the file specified in the pathname to be
- deleted at the server site. If an extra level of protection
- is desired (such as the query, "Do you really wish to
- delete?"), it should be provided by the user-FTP process.
-
- REMOVE DIRECTORY (RMD)
-
- This command causes the directory specified in the pathname
- to be removed as a directory (if the pathname is absolute)
- or as a subdirectory of the current working directory (if
- the pathname is relative). See Appendix II.
-
- MAKE DIRECTORY (MKD)
-
- This command causes the directory specified in the pathname
- to be created as a directory (if the pathname is absolute)
- or as a subdirectory of the current working directory (if
- the pathname is relative). See Appendix II.
-
- PRINT WORKING DIRECTORY (PWD)
-
- This command causes the name of the current working
- directory to be returned in the reply. See Appendix II.
-
- LIST (LIST)
-
- This command causes a list to be sent from the server to the
- passive DTP. If the pathname specifies a directory or other
- group of files, the server should transfer a list of files
- in the specified directory. If the pathname specifies a
- file then the server should send current information on the
- file. A null argument implies the user's current working or
- default directory. The data transfer is over the data
- connection in type ASCII or type EBCDIC. (The user must
-
-
-Postel & Reynolds [Page 32]
-
-
-
-RFC 959 October 1985
-File Transfer Protocol
-
-
- ensure that the TYPE is appropriately ASCII or EBCDIC).
- Since the information on a file may vary widely from system
- to system, this information may be hard to use automatically
- in a program, but may be quite useful to a human user.
-
- NAME LIST (NLST)
-
- This command causes a directory listing to be sent from
- server to user site. The pathname should specify a
- directory or other system-specific file group descriptor; a
- null argument implies the current directory. The server
- will return a stream of names of files and no other
- information. The data will be transferred in ASCII or
- EBCDIC type over the data connection as valid pathname
- strings separated by <CRLF> or <NL>. (Again the user must
- ensure that the TYPE is correct.) This command is intended
- to return information that can be used by a program to
- further process the files automatically. For example, in
- the implementation of a "multiple get" function.
-
- SITE PARAMETERS (SITE)
-
- This command is used by the server to provide services
- specific to his system that are essential to file transfer
- but not sufficiently universal to be included as commands in
- the protocol. The nature of these services and the
- specification of their syntax can be stated in a reply to
- the HELP SITE command.
-
- SYSTEM (SYST)
-
- This command is used to find out the type of operating
- system at the server. The reply shall have as its first
- word one of the system names listed in the current version
- of the Assigned Numbers document [4].
-
- STATUS (STAT)
-
- This command shall cause a status response to be sent over
- the control connection in the form of a reply. The command
- may be sent during a file transfer (along with the Telnet IP
- and Synch signals--see the Section on FTP Commands) in which
- case the server will respond with the status of the
- operation in progress, or it may be sent between file
- transfers. In the latter case, the command may have an
- argument field. If the argument is a pathname, the command
- is analogous to the "list" command except that data shall be
-
-
-Postel & Reynolds [Page 33]
-
-
-
-RFC 959 October 1985
-File Transfer Protocol
-
-
- transferred over the control connection. If a partial
- pathname is given, the server may respond with a list of
- file names or attributes associated with that specification.
- If no argument is given, the server should return general
- status information about the server FTP process. This
- should include current values of all transfer parameters and
- the status of connections.
-
- HELP (HELP)
-
- This command shall cause the server to send helpful
- information regarding its implementation status over the
- control connection to the user. The command may take an
- argument (e.g., any command name) and return more specific
- information as a response. The reply is type 211 or 214.
- It is suggested that HELP be allowed before entering a USER
- command. The server may use this reply to specify
- site-dependent parameters, e.g., in response to HELP SITE.
-
- NOOP (NOOP)
-
- This command does not affect any parameters or previously
- entered commands. It specifies no action other than that the
- server send an OK reply.
-
- The File Transfer Protocol follows the specifications of the Telnet
- protocol for all communications over the control connection. Since
- the language used for Telnet communication may be a negotiated
- option, all references in the next two sections will be to the
- "Telnet language" and the corresponding "Telnet end-of-line code".
- Currently, one may take these to mean NVT-ASCII and <CRLF>. No other
- specifications of the Telnet protocol will be cited.
-
- FTP commands are "Telnet strings" terminated by the "Telnet end of
- line code". The command codes themselves are alphabetic characters
- terminated by the character <SP> (Space) if parameters follow and
- Telnet-EOL otherwise. The command codes and the semantics of
- commands are described in this section; the detailed syntax of
- commands is specified in the Section on Commands, the reply sequences
- are discussed in the Section on Sequencing of Commands and Replies,
- and scenarios illustrating the use of commands are provided in the
- Section on Typical FTP Scenarios.
-
- FTP commands may be partitioned as those specifying access-control
- identifiers, data transfer parameters, or FTP service requests.
- Certain commands (such as ABOR, STAT, QUIT) may be sent over the
- control connection while a data transfer is in progress. Some
-
-
-Postel & Reynolds [Page 34]
-
-
-
-RFC 959 October 1985
-File Transfer Protocol
-
-
- servers may not be able to monitor the control and data connections
- simultaneously, in which case some special action will be necessary
- to get the server's attention. The following ordered format is
- tentatively recommended:
-
- 1. User system inserts the Telnet "Interrupt Process" (IP) signal
- in the Telnet stream.
-
- 2. User system sends the Telnet "Synch" signal.
-
- 3. User system inserts the command (e.g., ABOR) in the Telnet
- stream.
-
- 4. Server PI, after receiving "IP", scans the Telnet stream for
- EXACTLY ONE FTP command.
-
- (For other servers this may not be necessary but the actions listed
- above should have no unusual effect.)
-
- 4.2. FTP REPLIES
-
- Replies to File Transfer Protocol commands are devised to ensure
- the synchronization of requests and actions in the process of file
- transfer, and to guarantee that the user process always knows the
- state of the Server. Every command must generate at least one
- reply, although there may be more than one; in the latter case,
- the multiple replies must be easily distinguished. In addition,
- some commands occur in sequential groups, such as USER, PASS and
- ACCT, or RNFR and RNTO. The replies show the existence of an
- intermediate state if all preceding commands have been successful.
- A failure at any point in the sequence necessitates the repetition
- of the entire sequence from the beginning.
-
- The details of the command-reply sequence are made explicit in
- a set of state diagrams below.
-
- An FTP reply consists of a three digit number (transmitted as
- three alphanumeric characters) followed by some text. The number
- is intended for use by automata to determine what state to enter
- next; the text is intended for the human user. It is intended
- that the three digits contain enough encoded information that the
- user-process (the User-PI) will not need to examine the text and
- may either discard it or pass it on to the user, as appropriate.
- In particular, the text may be server-dependent, so there are
- likely to be varying texts for each reply code.
-
- A reply is defined to contain the 3-digit code, followed by Space
-
-
-Postel & Reynolds [Page 35]
-
-
-
-RFC 959 October 1985
-File Transfer Protocol
-
-
- <SP>, followed by one line of text (where some maximum line length
- has been specified), and terminated by the Telnet end-of-line
- code. There will be cases however, where the text is longer than
- a single line. In these cases the complete text must be bracketed
- so the User-process knows when it may stop reading the reply (i.e.
- stop processing input on the control connection) and go do other
- things. This requires a special format on the first line to
- indicate that more than one line is coming, and another on the
- last line to designate it as the last. At least one of these must
- contain the appropriate reply code to indicate the state of the
- transaction. To satisfy all factions, it was decided that both
- the first and last line codes should be the same.
-
- Thus the format for multi-line replies is that the first line
- will begin with the exact required reply code, followed
- immediately by a Hyphen, "-" (also known as Minus), followed by
- text. The last line will begin with the same code, followed
- immediately by Space <SP>, optionally some text, and the Telnet
- end-of-line code.
-
- For example:
- 123-First line
- Second line
- 234 A line beginning with numbers
- 123 The last line
-
- The user-process then simply needs to search for the second
- occurrence of the same reply code, followed by <SP> (Space), at
- the beginning of a line, and ignore all intermediary lines. If
- an intermediary line begins with a 3-digit number, the Server
- must pad the front to avoid confusion.
-
- This scheme allows standard system routines to be used for
- reply information (such as for the STAT reply), with
- "artificial" first and last lines tacked on. In rare cases
- where these routines are able to generate three digits and a
- Space at the beginning of any line, the beginning of each
- text line should be offset by some neutral text, like Space.
-
- This scheme assumes that multi-line replies may not be nested.
-
- The three digits of the reply each have a special significance.
- This is intended to allow a range of very simple to very
- sophisticated responses by the user-process. The first digit
- denotes whether the response is good, bad or incomplete.
- (Referring to the state diagram), an unsophisticated user-process
- will be able to determine its next action (proceed as planned,
-
-
-Postel & Reynolds [Page 36]
-
-
-
-RFC 959 October 1985
-File Transfer Protocol
-
-
- redo, retrench, etc.) by simply examining this first digit. A
- user-process that wants to know approximately what kind of error
- occurred (e.g. file system error, command syntax error) may
- examine the second digit, reserving the third digit for the finest
- gradation of information (e.g., RNTO command without a preceding
- RNFR).
-
- There are five values for the first digit of the reply code:
-
- 1yz Positive Preliminary reply
-
- The requested action is being initiated; expect another
- reply before proceeding with a new command. (The
- user-process sending another command before the
- completion reply would be in violation of protocol; but
- server-FTP processes should queue any commands that
- arrive while a preceding command is in progress.) This
- type of reply can be used to indicate that the command
- was accepted and the user-process may now pay attention
- to the data connections, for implementations where
- simultaneous monitoring is difficult. The server-FTP
- process may send at most, one 1yz reply per command.
-
- 2yz Positive Completion reply
-
- The requested action has been successfully completed. A
- new request may be initiated.
-
- 3yz Positive Intermediate reply
-
- The command has been accepted, but the requested action
- is being held in abeyance, pending receipt of further
- information. The user should send another command
- specifying this information. This reply is used in
- command sequence groups.
-
- 4yz Transient Negative Completion reply
-
- The command was not accepted and the requested action did
- not take place, but the error condition is temporary and
- the action may be requested again. The user should
- return to the beginning of the command sequence, if any.
- It is difficult to assign a meaning to "transient",
- particularly when two distinct sites (Server- and
- User-processes) have to agree on the interpretation.
- Each reply in the 4yz category might have a slightly
- different time value, but the intent is that the
-
-
-Postel & Reynolds [Page 37]
-
-
-
-RFC 959 October 1985
-File Transfer Protocol
-
-
- user-process is encouraged to try again. A rule of thumb
- in determining if a reply fits into the 4yz or the 5yz
- (Permanent Negative) category is that replies are 4yz if
- the commands can be repeated without any change in
- command form or in properties of the User or Server
- (e.g., the command is spelled the same with the same
- arguments used; the user does not change his file access
- or user name; the server does not put up a new
- implementation.)
-
- 5yz Permanent Negative Completion reply
-
- The command was not accepted and the requested action did
- not take place. The User-process is discouraged from
- repeating the exact request (in the same sequence). Even
- some "permanent" error conditions can be corrected, so
- the human user may want to direct his User-process to
- reinitiate the command sequence by direct action at some
- point in the future (e.g., after the spelling has been
- changed, or the user has altered his directory status.)
-
- The following function groupings are encoded in the second
- digit:
-
- x0z Syntax - These replies refer to syntax errors,
- syntactically correct commands that don't fit any
- functional category, unimplemented or superfluous
- commands.
-
- x1z Information - These are replies to requests for
- information, such as status or help.
-
- x2z Connections - Replies referring to the control and
- data connections.
-
- x3z Authentication and accounting - Replies for the login
- process and accounting procedures.
-
- x4z Unspecified as yet.
-
- x5z File system - These replies indicate the status of the
- Server file system vis-a-vis the requested transfer or
- other file system action.
-
- The third digit gives a finer gradation of meaning in each of
- the function categories, specified by the second digit. The
- list of replies below will illustrate this. Note that the text
-
-
-Postel & Reynolds [Page 38]
-
-
-
-RFC 959 October 1985
-File Transfer Protocol
-
-
- associated with each reply is recommended, rather than
- mandatory, and may even change according to the command with
- which it is associated. The reply codes, on the other hand,
- must strictly follow the specifications in the last section;
- that is, Server implementations should not invent new codes for
- situations that are only slightly different from the ones
- described here, but rather should adapt codes already defined.
-
- A command such as TYPE or ALLO whose successful execution
- does not offer the user-process any new information will
- cause a 200 reply to be returned. If the command is not
- implemented by a particular Server-FTP process because it
- has no relevance to that computer system, for example ALLO
- at a TOPS20 site, a Positive Completion reply is still
- desired so that the simple User-process knows it can proceed
- with its course of action. A 202 reply is used in this case
- with, for example, the reply text: "No storage allocation
- necessary." If, on the other hand, the command requests a
- non-site-specific action and is unimplemented, the response
- is 502. A refinement of that is the 504 reply for a command
- that is implemented, but that requests an unimplemented
- parameter.
-
- 4.2.1 Reply Codes by Function Groups
-
- 200 Command okay.
- 500 Syntax error, command unrecognized.
- This may include errors such as command line too long.
- 501 Syntax error in parameters or arguments.
- 202 Command not implemented, superfluous at this site.
- 502 Command not implemented.
- 503 Bad sequence of commands.
- 504 Command not implemented for that parameter.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Postel & Reynolds [Page 39]
-
-
-
-RFC 959 October 1985
-File Transfer Protocol
-
-
- 110 Restart marker reply.
- In this case, the text is exact and not left to the
- particular implementation; it must read:
- MARK yyyy = mmmm
- Where yyyy is User-process data stream marker, and mmmm
- server's equivalent marker (note the spaces between markers
- and "=").
- 211 System status, or system help reply.
- 212 Directory status.
- 213 File status.
- 214 Help message.
- On how to use the server or the meaning of a particular
- non-standard command. This reply is useful only to the
- human user.
- 215 NAME system type.
- Where NAME is an official system name from the list in the
- Assigned Numbers document.
-
- 120 Service ready in nnn minutes.
- 220 Service ready for new user.
- 221 Service closing control connection.
- Logged out if appropriate.
- 421 Service not available, closing control connection.
- This may be a reply to any command if the service knows it
- must shut down.
- 125 Data connection already open; transfer starting.
- 225 Data connection open; no transfer in progress.
- 425 Can't open data connection.
- 226 Closing data connection.
- Requested file action successful (for example, file
- transfer or file abort).
- 426 Connection closed; transfer aborted.
- 227 Entering Passive Mode (h1,h2,h3,h4,p1,p2).
-
- 230 User logged in, proceed.
- 530 Not logged in.
- 331 User name okay, need password.
- 332 Need account for login.
- 532 Need account for storing files.
-
-
-
-
-
-
-
-
-
-
-Postel & Reynolds [Page 40]
-
-
-
-RFC 959 October 1985
-File Transfer Protocol
-
-
- 150 File status okay; about to open data connection.
- 250 Requested file action okay, completed.
- 257 "PATHNAME" created.
- 350 Requested file action pending further information.
- 450 Requested file action not taken.
- File unavailable (e.g., file busy).
- 550 Requested action not taken.
- File unavailable (e.g., file not found, no access).
- 451 Requested action aborted. Local error in processing.
- 551 Requested action aborted. Page type unknown.
- 452 Requested action not taken.
- Insufficient storage space in system.
- 552 Requested file action aborted.
- Exceeded storage allocation (for current directory or
- dataset).
- 553 Requested action not taken.
- File name not allowed.
-
-
- 4.2.2 Numeric Order List of Reply Codes
-
- 110 Restart marker reply.
- In this case, the text is exact and not left to the
- particular implementation; it must read:
- MARK yyyy = mmmm
- Where yyyy is User-process data stream marker, and mmmm
- server's equivalent marker (note the spaces between markers
- and "=").
- 120 Service ready in nnn minutes.
- 125 Data connection already open; transfer starting.
- 150 File status okay; about to open data connection.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Postel & Reynolds [Page 41]
-
-
-
-RFC 959 October 1985
-File Transfer Protocol
-
-
- 200 Command okay.
- 202 Command not implemented, superfluous at this site.
- 211 System status, or system help reply.
- 212 Directory status.
- 213 File status.
- 214 Help message.
- On how to use the server or the meaning of a particular
- non-standard command. This reply is useful only to the
- human user.
- 215 NAME system type.
- Where NAME is an official system name from the list in the
- Assigned Numbers document.
- 220 Service ready for new user.
- 221 Service closing control connection.
- Logged out if appropriate.
- 225 Data connection open; no transfer in progress.
- 226 Closing data connection.
- Requested file action successful (for example, file
- transfer or file abort).
- 227 Entering Passive Mode (h1,h2,h3,h4,p1,p2).
- 230 User logged in, proceed.
- 250 Requested file action okay, completed.
- 257 "PATHNAME" created.
-
- 331 User name okay, need password.
- 332 Need account for login.
- 350 Requested file action pending further information.
-
- 421 Service not available, closing control connection.
- This may be a reply to any command if the service knows it
- must shut down.
- 425 Can't open data connection.
- 426 Connection closed; transfer aborted.
- 450 Requested file action not taken.
- File unavailable (e.g., file busy).
- 451 Requested action aborted: local error in processing.
- 452 Requested action not taken.
- Insufficient storage space in system.
-
-
-
-
-
-
-
-
-
-
-
-Postel & Reynolds [Page 42]
-
-
-
-RFC 959 October 1985
-File Transfer Protocol
-
-
- 500 Syntax error, command unrecognized.
- This may include errors such as command line too long.
- 501 Syntax error in parameters or arguments.
- 502 Command not implemented.
- 503 Bad sequence of commands.
- 504 Command not implemented for that parameter.
- 530 Not logged in.
- 532 Need account for storing files.
- 550 Requested action not taken.
- File unavailable (e.g., file not found, no access).
- 551 Requested action aborted: page type unknown.
- 552 Requested file action aborted.
- Exceeded storage allocation (for current directory or
- dataset).
- 553 Requested action not taken.
- File name not allowed.
-
-
-5. DECLARATIVE SPECIFICATIONS
-
- 5.1. MINIMUM IMPLEMENTATION
-
- In order to make FTP workable without needless error messages, the
- following minimum implementation is required for all servers:
-
- TYPE - ASCII Non-print
- MODE - Stream
- STRUCTURE - File, Record
- COMMANDS - USER, QUIT, PORT,
- TYPE, MODE, STRU,
- for the default values
- RETR, STOR,
- NOOP.
-
- The default values for transfer parameters are:
-
- TYPE - ASCII Non-print
- MODE - Stream
- STRU - File
-
- All hosts must accept the above as the standard defaults.
-
-
-
-
-
-
-
-
-Postel & Reynolds [Page 43]
-
-
-
-RFC 959 October 1985
-File Transfer Protocol
-
-
- 5.2. CONNECTIONS
-
- The server protocol interpreter shall "listen" on Port L. The
- user or user protocol interpreter shall initiate the full-duplex
- control connection. Server- and user- processes should follow the
- conventions of the Telnet protocol as specified in the
- ARPA-Internet Protocol Handbook [1]. Servers are under no
- obligation to provide for editing of command lines and may require
- that it be done in the user host. The control connection shall be
- closed by the server at the user's request after all transfers and
- replies are completed.
-
- The user-DTP must "listen" on the specified data port; this may be
- the default user port (U) or a port specified in the PORT command.
- The server shall initiate the data connection from his own default
- data port (L-1) using the specified user data port. The direction
- of the transfer and the port used will be determined by the FTP
- service command.
-
- Note that all FTP implementation must support data transfer using
- the default port, and that only the USER-PI may initiate the use
- of non-default ports.
-
- When data is to be transferred between two servers, A and B (refer
- to Figure 2), the user-PI, C, sets up control connections with
- both server-PI's. One of the servers, say A, is then sent a PASV
- command telling him to "listen" on his data port rather than
- initiate a connection when he receives a transfer service command.
- When the user-PI receives an acknowledgment to the PASV command,
- which includes the identity of the host and port being listened
- on, the user-PI then sends A's port, a, to B in a PORT command; a
- reply is returned. The user-PI may then send the corresponding
- service commands to A and B. Server B initiates the connection
- and the transfer proceeds. The command-reply sequence is listed
- below where the messages are vertically synchronous but
- horizontally asynchronous:
-
-
-
-
-
-
-
-
-
-
-
-
-
-Postel & Reynolds [Page 44]
-
-
-
-RFC 959 October 1985
-File Transfer Protocol
-
-
- User-PI - Server A User-PI - Server B
- ------------------ ------------------
-
- C->A : Connect C->B : Connect
- C->A : PASV
- A->C : 227 Entering Passive Mode. A1,A2,A3,A4,a1,a2
- C->B : PORT A1,A2,A3,A4,a1,a2
- B->C : 200 Okay
- C->A : STOR C->B : RETR
- B->A : Connect to HOST-A, PORT-a
-
- Figure 3
-
- The data connection shall be closed by the server under the
- conditions described in the Section on Establishing Data
- Connections. If the data connection is to be closed following a
- data transfer where closing the connection is not required to
- indicate the end-of-file, the server must do so immediately.
- Waiting until after a new transfer command is not permitted
- because the user-process will have already tested the data
- connection to see if it needs to do a "listen"; (remember that the
- user must "listen" on a closed data port BEFORE sending the
- transfer request). To prevent a race condition here, the server
- sends a reply (226) after closing the data connection (or if the
- connection is left open, a "file transfer completed" reply (250)
- and the user-PI should wait for one of these replies before
- issuing a new transfer command).
-
- Any time either the user or server see that the connection is
- being closed by the other side, it should promptly read any
- remaining data queued on the connection and issue the close on its
- own side.
-
- 5.3. COMMANDS
-
- The commands are Telnet character strings transmitted over the
- control connections as described in the Section on FTP Commands.
- The command functions and semantics are described in the Section
- on Access Control Commands, Transfer Parameter Commands, FTP
- Service Commands, and Miscellaneous Commands. The command syntax
- is specified here.
-
- The commands begin with a command code followed by an argument
- field. The command codes are four or fewer alphabetic characters.
- Upper and lower case alphabetic characters are to be treated
- identically. Thus, any of the following may represent the
- retrieve command:
-
-
-Postel & Reynolds [Page 45]
-
-
-
-RFC 959 October 1985
-File Transfer Protocol
-
-
- RETR Retr retr ReTr rETr
-
- This also applies to any symbols representing parameter values,
- such as A or a for ASCII TYPE. The command codes and the argument
- fields are separated by one or more spaces.
-
- The argument field consists of a variable length character string
- ending with the character sequence <CRLF> (Carriage Return, Line
- Feed) for NVT-ASCII representation; for other negotiated languages
- a different end of line character might be used. It should be
- noted that the server is to take no action until the end of line
- code is received.
-
- The syntax is specified below in NVT-ASCII. All characters in the
- argument field are ASCII characters including any ASCII
- represented decimal integers. Square brackets denote an optional
- argument field. If the option is not taken, the appropriate
- default is implied.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Postel & Reynolds [Page 46]
-
-
-
-RFC 959 October 1985
-File Transfer Protocol
-
-
- 5.3.1. FTP COMMANDS
-
- The following are the FTP commands:
-
- USER <SP> <username> <CRLF>
- PASS <SP> <password> <CRLF>
- ACCT <SP> <account-information> <CRLF>
- CWD <SP> <pathname> <CRLF>
- CDUP <CRLF>
- SMNT <SP> <pathname> <CRLF>
- QUIT <CRLF>
- REIN <CRLF>
- PORT <SP> <host-port> <CRLF>
- PASV <CRLF>
- TYPE <SP> <type-code> <CRLF>
- STRU <SP> <structure-code> <CRLF>
- MODE <SP> <mode-code> <CRLF>
- RETR <SP> <pathname> <CRLF>
- STOR <SP> <pathname> <CRLF>
- STOU <CRLF>
- APPE <SP> <pathname> <CRLF>
- ALLO <SP> <decimal-integer>
- [<SP> R <SP> <decimal-integer>] <CRLF>
- REST <SP> <marker> <CRLF>
- RNFR <SP> <pathname> <CRLF>
- RNTO <SP> <pathname> <CRLF>
- ABOR <CRLF>
- DELE <SP> <pathname> <CRLF>
- RMD <SP> <pathname> <CRLF>
- MKD <SP> <pathname> <CRLF>
- PWD <CRLF>
- LIST [<SP> <pathname>] <CRLF>
- NLST [<SP> <pathname>] <CRLF>
- SITE <SP> <string> <CRLF>
- SYST <CRLF>
- STAT [<SP> <pathname>] <CRLF>
- HELP [<SP> <string>] <CRLF>
- NOOP <CRLF>
-
-
-
-
-
-
-
-
-
-
-
-Postel & Reynolds [Page 47]
-
-
-
-RFC 959 October 1985
-File Transfer Protocol
-
-
- 5.3.2. FTP COMMAND ARGUMENTS
-
- The syntax of the above argument fields (using BNF notation
- where applicable) is:
-
- <username> ::= <string>
- <password> ::= <string>
- <account-information> ::= <string>
- <string> ::= <char> | <char><string>
- <char> ::= any of the 128 ASCII characters except <CR> and
- <LF>
- <marker> ::= <pr-string>
- <pr-string> ::= <pr-char> | <pr-char><pr-string>
- <pr-char> ::= printable characters, any
- ASCII code 33 through 126
- <byte-size> ::= <number>
- <host-port> ::= <host-number>,<port-number>
- <host-number> ::= <number>,<number>,<number>,<number>
- <port-number> ::= <number>,<number>
- <number> ::= any decimal integer 1 through 255
- <form-code> ::= N | T | C
- <type-code> ::= A [<sp> <form-code>]
- | E [<sp> <form-code>]
- | I
- | L <sp> <byte-size>
- <structure-code> ::= F | R | P
- <mode-code> ::= S | B | C
- <pathname> ::= <string>
- <decimal-integer> ::= any decimal integer
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Postel & Reynolds [Page 48]
-
-
-
-RFC 959 October 1985
-File Transfer Protocol
-
-
- 5.4. SEQUENCING OF COMMANDS AND REPLIES
-
- The communication between the user and server is intended to be an
- alternating dialogue. As such, the user issues an FTP command and
- the server responds with a prompt primary reply. The user should
- wait for this initial primary success or failure response before
- sending further commands.
-
- Certain commands require a second reply for which the user should
- also wait. These replies may, for example, report on the progress
- or completion of file transfer or the closing of the data
- connection. They are secondary replies to file transfer commands.
-
- One important group of informational replies is the connection
- greetings. Under normal circumstances, a server will send a 220
- reply, "awaiting input", when the connection is completed. The
- user should wait for this greeting message before sending any
- commands. If the server is unable to accept input right away, a
- 120 "expected delay" reply should be sent immediately and a 220
- reply when ready. The user will then know not to hang up if there
- is a delay.
-
- Spontaneous Replies
-
- Sometimes "the system" spontaneously has a message to be sent
- to a user (usually all users). For example, "System going down
- in 15 minutes". There is no provision in FTP for such
- spontaneous information to be sent from the server to the user.
- It is recommended that such information be queued in the
- server-PI and delivered to the user-PI in the next reply
- (possibly making it a multi-line reply).
-
- The table below lists alternative success and failure replies for
- each command. These must be strictly adhered to; a server may
- substitute text in the replies, but the meaning and action implied
- by the code numbers and by the specific command reply sequence
- cannot be altered.
-
- Command-Reply Sequences
-
- In this section, the command-reply sequence is presented. Each
- command is listed with its possible replies; command groups are
- listed together. Preliminary replies are listed first (with
- their succeeding replies indented and under them), then
- positive and negative completion, and finally intermediary
-
-
-
-
-Postel & Reynolds [Page 49]
-
-
-
-RFC 959 October 1985
-File Transfer Protocol
-
-
- replies with the remaining commands from the sequence
- following. This listing forms the basis for the state
- diagrams, which will be presented separately.
-
- Connection Establishment
- 120
- 220
- 220
- 421
- Login
- USER
- 230
- 530
- 500, 501, 421
- 331, 332
- PASS
- 230
- 202
- 530
- 500, 501, 503, 421
- 332
- ACCT
- 230
- 202
- 530
- 500, 501, 503, 421
- CWD
- 250
- 500, 501, 502, 421, 530, 550
- CDUP
- 200
- 500, 501, 502, 421, 530, 550
- SMNT
- 202, 250
- 500, 501, 502, 421, 530, 550
- Logout
- REIN
- 120
- 220
- 220
- 421
- 500, 502
- QUIT
- 221
- 500
-
-
-
-
-Postel & Reynolds [Page 50]
-
-
-
-RFC 959 October 1985
-File Transfer Protocol
-
-
- Transfer parameters
- PORT
- 200
- 500, 501, 421, 530
- PASV
- 227
- 500, 501, 502, 421, 530
- MODE
- 200
- 500, 501, 504, 421, 530
- TYPE
- 200
- 500, 501, 504, 421, 530
- STRU
- 200
- 500, 501, 504, 421, 530
- File action commands
- ALLO
- 200
- 202
- 500, 501, 504, 421, 530
- REST
- 500, 501, 502, 421, 530
- 350
- STOR
- 125, 150
- (110)
- 226, 250
- 425, 426, 451, 551, 552
- 532, 450, 452, 553
- 500, 501, 421, 530
- STOU
- 125, 150
- (110)
- 226, 250
- 425, 426, 451, 551, 552
- 532, 450, 452, 553
- 500, 501, 421, 530
- RETR
- 125, 150
- (110)
- 226, 250
- 425, 426, 451
- 450, 550
- 500, 501, 421, 530
-
-
-
-
-Postel & Reynolds [Page 51]
-
-
-
-RFC 959 October 1985
-File Transfer Protocol
-
-
- LIST
- 125, 150
- 226, 250
- 425, 426, 451
- 450
- 500, 501, 502, 421, 530
- NLST
- 125, 150
- 226, 250
- 425, 426, 451
- 450
- 500, 501, 502, 421, 530
- APPE
- 125, 150
- (110)
- 226, 250
- 425, 426, 451, 551, 552
- 532, 450, 550, 452, 553
- 500, 501, 502, 421, 530
- RNFR
- 450, 550
- 500, 501, 502, 421, 530
- 350
- RNTO
- 250
- 532, 553
- 500, 501, 502, 503, 421, 530
- DELE
- 250
- 450, 550
- 500, 501, 502, 421, 530
- RMD
- 250
- 500, 501, 502, 421, 530, 550
- MKD
- 257
- 500, 501, 502, 421, 530, 550
- PWD
- 257
- 500, 501, 502, 421, 550
- ABOR
- 225, 226
- 500, 501, 502, 421
-
-
-
-
-
-
-Postel & Reynolds [Page 52]
-
-
-
-RFC 959 October 1985
-File Transfer Protocol
-
-
- Informational commands
- SYST
- 215
- 500, 501, 502, 421
- STAT
- 211, 212, 213
- 450
- 500, 501, 502, 421, 530
- HELP
- 211, 214
- 500, 501, 502, 421
- Miscellaneous commands
- SITE
- 200
- 202
- 500, 501, 530
- NOOP
- 200
- 500 421
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Postel & Reynolds [Page 53]
-
-
-
-RFC 959 October 1985
-File Transfer Protocol
-
-
-6. STATE DIAGRAMS
-
- Here we present state diagrams for a very simple minded FTP
- implementation. Only the first digit of the reply codes is used.
- There is one state diagram for each group of FTP commands or command
- sequences.
-
- The command groupings were determined by constructing a model for
- each command then collecting together the commands with structurally
- identical models.
-
- For each command or command sequence there are three possible
- outcomes: success (S), failure (F), and error (E). In the state
- diagrams below we use the symbol B for "begin", and the symbol W for
- "wait for reply".
-
- We first present the diagram that represents the largest group of FTP
- commands:
-
-
- 1,3 +---+
- ----------->| E |
- | +---+
- |
- +---+ cmd +---+ 2 +---+
- | B |---------->| W |---------->| S |
- +---+ +---+ +---+
- |
- | 4,5 +---+
- ----------->| F |
- +---+
-
-
- This diagram models the commands:
-
- ABOR, ALLO, DELE, CWD, CDUP, SMNT, HELP, MODE, NOOP, PASV,
- QUIT, SITE, PORT, SYST, STAT, RMD, MKD, PWD, STRU, and TYPE.
-
-
-
-
-
-
-
-
-
-
-
-
-Postel & Reynolds [Page 54]
-
-
-
-RFC 959 October 1985
-File Transfer Protocol
-
-
- The other large group of commands is represented by a very similar
- diagram:
-
-
- 3 +---+
- ----------->| E |
- | +---+
- |
- +---+ cmd +---+ 2 +---+
- | B |---------->| W |---------->| S |
- +---+ --->+---+ +---+
- | | |
- | | | 4,5 +---+
- | 1 | ----------->| F |
- ----- +---+
-
-
- This diagram models the commands:
-
- APPE, LIST, NLST, REIN, RETR, STOR, and STOU.
-
- Note that this second model could also be used to represent the first
- group of commands, the only difference being that in the first group
- the 100 series replies are unexpected and therefore treated as error,
- while the second group expects (some may require) 100 series replies.
- Remember that at most, one 100 series reply is allowed per command.
-
- The remaining diagrams model command sequences, perhaps the simplest
- of these is the rename sequence:
-
-
- +---+ RNFR +---+ 1,2 +---+
- | B |---------->| W |---------->| E |
- +---+ +---+ -->+---+
- | | |
- 3 | | 4,5 |
- -------------- ------ |
- | | | +---+
- | ------------->| S |
- | | 1,3 | | +---+
- | 2| --------
- | | | |
- V | | |
- +---+ RNTO +---+ 4,5 ----->+---+
- | |---------->| W |---------->| F |
- +---+ +---+ +---+
-
-
-
-Postel & Reynolds [Page 55]
-
-
-
-RFC 959 October 1985
-File Transfer Protocol
-
-
- The next diagram is a simple model of the Restart command:
-
-
- +---+ REST +---+ 1,2 +---+
- | B |---------->| W |---------->| E |
- +---+ +---+ -->+---+
- | | |
- 3 | | 4,5 |
- -------------- ------ |
- | | | +---+
- | ------------->| S |
- | | 3 | | +---+
- | 2| --------
- | | | |
- V | | |
- +---+ cmd +---+ 4,5 ----->+---+
- | |---------->| W |---------->| F |
- +---+ -->+---+ +---+
- | |
- | 1 |
- ------
-
-
- Where "cmd" is APPE, STOR, or RETR.
-
- We note that the above three models are similar. The Restart differs
- from the Rename two only in the treatment of 100 series replies at
- the second stage, while the second group expects (some may require)
- 100 series replies. Remember that at most, one 100 series reply is
- allowed per command.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Postel & Reynolds [Page 56]
-
-
-
-RFC 959 October 1985
-File Transfer Protocol
-
-
- The most complicated diagram is for the Login sequence:
-
-
- 1
- +---+ USER +---+------------->+---+
- | B |---------->| W | 2 ---->| E |
- +---+ +---+------ | -->+---+
- | | | | |
- 3 | | 4,5 | | |
- -------------- ----- | | |
- | | | | |
- | | | | |
- | --------- |
- | 1| | | |
- V | | | |
- +---+ PASS +---+ 2 | ------>+---+
- | |---------->| W |------------->| S |
- +---+ +---+ ---------->+---+
- | | | | |
- 3 | |4,5| | |
- -------------- -------- |
- | | | | |
- | | | | |
- | -----------
- | 1,3| | | |
- V | 2| | |
- +---+ ACCT +---+-- | ----->+---+
- | |---------->| W | 4,5 -------->| F |
- +---+ +---+------------->+---+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Postel & Reynolds [Page 57]
-
-
-
-RFC 959 October 1985
-File Transfer Protocol
-
-
- Finally, we present a generalized diagram that could be used to model
- the command and reply interchange:
-
-
- ------------------------------------
- | |
- Begin | |
- | V |
- | +---+ cmd +---+ 2 +---+ |
- -->| |------->| |---------->| | |
- | | | W | | S |-----|
- -->| | -->| |----- | | |
- | +---+ | +---+ 4,5 | +---+ |
- | | | | | | |
- | | | 1| |3 | +---+ |
- | | | | | | | | |
- | | ---- | ---->| F |-----
- | | | | |
- | | | +---+
- -------------------
- |
- |
- V
- End
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Postel & Reynolds [Page 58]
-
-
-
-RFC 959 October 1985
-File Transfer Protocol
-
-
-7. TYPICAL FTP SCENARIO
-
- User at host U wanting to transfer files to/from host S:
-
- In general, the user will communicate to the server via a mediating
- user-FTP process. The following may be a typical scenario. The
- user-FTP prompts are shown in parentheses, '---->' represents
- commands from host U to host S, and '<----' represents replies from
- host S to host U.
-
- LOCAL COMMANDS BY USER ACTION INVOLVED
-
- ftp (host) multics<CR> Connect to host S, port L,
- establishing control connections.
- <---- 220 Service ready <CRLF>.
- username Doe <CR> USER Doe<CRLF>---->
- <---- 331 User name ok,
- need password<CRLF>.
- password mumble <CR> PASS mumble<CRLF>---->
- <---- 230 User logged in<CRLF>.
- retrieve (local type) ASCII<CR>
- (local pathname) test 1 <CR> User-FTP opens local file in ASCII.
- (for. pathname) test.pl1<CR> RETR test.pl1<CRLF> ---->
- <---- 150 File status okay;
- about to open data
- connection<CRLF>.
- Server makes data connection
- to port U.
-
- <---- 226 Closing data connection,
- file transfer successful<CRLF>.
- type Image<CR> TYPE I<CRLF> ---->
- <---- 200 Command OK<CRLF>
- store (local type) image<CR>
- (local pathname) file dump<CR> User-FTP opens local file in Image.
- (for.pathname) >udd>cn>fd<CR> STOR >udd>cn>fd<CRLF> ---->
- <---- 550 Access denied<CRLF>
- terminate QUIT <CRLF> ---->
- Server closes all
- connections.
-
-8. CONNECTION ESTABLISHMENT
-
- The FTP control connection is established via TCP between the user
- process port U and the server process port L. This protocol is
- assigned the service port 21 (25 octal), that is L=21.
-
-
-
-Postel & Reynolds [Page 59]
-
-
-
-RFC 959 October 1985
-File Transfer Protocol
-
-
-APPENDIX I - PAGE STRUCTURE
-
- The need for FTP to support page structure derives principally from
- the need to support efficient transmission of files between TOPS-20
- systems, particularly the files used by NLS.
-
- The file system of TOPS-20 is based on the concept of pages. The
- operating system is most efficient at manipulating files as pages.
- The operating system provides an interface to the file system so that
- many applications view files as sequential streams of characters.
- However, a few applications use the underlying page structures
- directly, and some of these create holey files.
-
- A TOPS-20 disk file consists of four things: a pathname, a page
- table, a (possibly empty) set of pages, and a set of attributes.
-
- The pathname is specified in the RETR or STOR command. It includes
- the directory name, file name, file name extension, and generation
- number.
-
- The page table contains up to 2**18 entries. Each entry may be
- EMPTY, or may point to a page. If it is not empty, there are also
- some page-specific access bits; not all pages of a file need have the
- same access protection.
-
- A page is a contiguous set of 512 words of 36 bits each.
-
- The attributes of the file, in the File Descriptor Block (FDB),
- contain such things as creation time, write time, read time, writer's
- byte-size, end-of-file pointer, count of reads and writes, backup
- system tape numbers, etc.
-
- Note that there is NO requirement that entries in the page table be
- contiguous. There may be empty page table slots between occupied
- ones. Also, the end of file pointer is simply a number. There is no
- requirement that it in fact point at the "last" datum in the file.
- Ordinary sequential I/O calls in TOPS-20 will cause the end of file
- pointer to be left after the last datum written, but other operations
- may cause it not to be so, if a particular programming system so
- requires.
-
- In fact, in both of these special cases, "holey" files and
- end-of-file pointers NOT at the end of the file, occur with NLS data
- files.
-
-
-
-
-
-Postel & Reynolds [Page 60]
-
-
-
-RFC 959 October 1985
-File Transfer Protocol
-
-
- The TOPS-20 paged files can be sent with the FTP transfer parameters:
- TYPE L 36, STRU P, and MODE S (in fact, any mode could be used).
-
- Each page of information has a header. Each header field, which is a
- logical byte, is a TOPS-20 word, since the TYPE is L 36.
-
- The header fields are:
-
- Word 0: Header Length.
-
- The header length is 5.
-
- Word 1: Page Index.
-
- If the data is a disk file page, this is the number of that
- page in the file's page map. Empty pages (holes) in the file
- are simply not sent. Note that a hole is NOT the same as a
- page of zeros.
-
- Word 2: Data Length.
-
- The number of data words in this page, following the header.
- Thus, the total length of the transmission unit is the Header
- Length plus the Data Length.
-
- Word 3: Page Type.
-
- A code for what type of chunk this is. A data page is type 3,
- the FDB page is type 2.
-
- Word 4: Page Access Control.
-
- The access bits associated with the page in the file's page
- map. (This full word quantity is put into AC2 of an SPACS by
- the program reading from net to disk.)
-
- After the header are Data Length data words. Data Length is
- currently either 512 for a data page or 31 for an FDB. Trailing
- zeros in a disk file page may be discarded, making Data Length less
- than 512 in that case.
-
-
-
-
-
-
-
-
-
-Postel & Reynolds [Page 61]
-
-
-
-RFC 959 October 1985
-File Transfer Protocol
-
-
-APPENDIX II - DIRECTORY COMMANDS
-
- Since UNIX has a tree-like directory structure in which directories
- are as easy to manipulate as ordinary files, it is useful to expand
- the FTP servers on these machines to include commands which deal with
- the creation of directories. Since there are other hosts on the
- ARPA-Internet which have tree-like directories (including TOPS-20 and
- Multics), these commands are as general as possible.
-
- Four directory commands have been added to FTP:
-
- MKD pathname
-
- Make a directory with the name "pathname".
-
- RMD pathname
-
- Remove the directory with the name "pathname".
-
- PWD
-
- Print the current working directory name.
-
- CDUP
-
- Change to the parent of the current working directory.
-
- The "pathname" argument should be created (removed) as a
- subdirectory of the current working directory, unless the "pathname"
- string contains sufficient information to specify otherwise to the
- server, e.g., "pathname" is an absolute pathname (in UNIX and
- Multics), or pathname is something like "<abso.lute.path>" to
- TOPS-20.
-
- REPLY CODES
-
- The CDUP command is a special case of CWD, and is included to
- simplify the implementation of programs for transferring directory
- trees between operating systems having different syntaxes for
- naming the parent directory. The reply codes for CDUP be
- identical to the reply codes of CWD.
-
- The reply codes for RMD be identical to the reply codes for its
- file analogue, DELE.
-
- The reply codes for MKD, however, are a bit more complicated. A
- freshly created directory will probably be the object of a future
-
-
-Postel & Reynolds [Page 62]
-
-
-
-RFC 959 October 1985
-File Transfer Protocol
-
-
- CWD command. Unfortunately, the argument to MKD may not always be
- a suitable argument for CWD. This is the case, for example, when
- a TOPS-20 subdirectory is created by giving just the subdirectory
- name. That is, with a TOPS-20 server FTP, the command sequence
-
- MKD MYDIR
- CWD MYDIR
-
- will fail. The new directory may only be referred to by its
- "absolute" name; e.g., if the MKD command above were issued while
- connected to the directory <DFRANKLIN>, the new subdirectory
- could only be referred to by the name <DFRANKLIN.MYDIR>.
-
- Even on UNIX and Multics, however, the argument given to MKD may
- not be suitable. If it is a "relative" pathname (i.e., a pathname
- which is interpreted relative to the current directory), the user
- would need to be in the same current directory in order to reach
- the subdirectory. Depending on the application, this may be
- inconvenient. It is not very robust in any case.
-
- To solve these problems, upon successful completion of an MKD
- command, the server should return a line of the form:
-
- 257<space>"<directory-name>"<space><commentary>
-
- That is, the server will tell the user what string to use when
- referring to the created directory. The directory name can
- contain any character; embedded double-quotes should be escaped by
- double-quotes (the "quote-doubling" convention).
-
- For example, a user connects to the directory /usr/dm, and creates
- a subdirectory, named pathname:
-
- CWD /usr/dm
- 200 directory changed to /usr/dm
- MKD pathname
- 257 "/usr/dm/pathname" directory created
-
- An example with an embedded double quote:
-
- MKD foo"bar
- 257 "/usr/dm/foo""bar" directory created
- CWD /usr/dm/foo"bar
- 200 directory changed to /usr/dm/foo"bar
-
-
-
-
-
-Postel & Reynolds [Page 63]
-
-
-
-RFC 959 October 1985
-File Transfer Protocol
-
-
- The prior existence of a subdirectory with the same name is an
- error, and the server must return an "access denied" error reply
- in that case.
-
- CWD /usr/dm
- 200 directory changed to /usr/dm
- MKD pathname
- 521-"/usr/dm/pathname" directory already exists;
- 521 taking no action.
-
- The failure replies for MKD are analogous to its file creating
- cousin, STOR. Also, an "access denied" return is given if a file
- name with the same name as the subdirectory will conflict with the
- creation of the subdirectory (this is a problem on UNIX, but
- shouldn't be one on TOPS-20).
-
- Essentially because the PWD command returns the same type of
- information as the successful MKD command, the successful PWD
- command uses the 257 reply code as well.
-
- SUBTLETIES
-
- Because these commands will be most useful in transferring
- subtrees from one machine to another, carefully observe that the
- argument to MKD is to be interpreted as a sub-directory of the
- current working directory, unless it contains enough information
- for the destination host to tell otherwise. A hypothetical
- example of its use in the TOPS-20 world:
-
- CWD <some.where>
- 200 Working directory changed
- MKD overrainbow
- 257 "<some.where.overrainbow>" directory created
- CWD overrainbow
- 431 No such directory
- CWD <some.where.overrainbow>
- 200 Working directory changed
-
- CWD <some.where>
- 200 Working directory changed to <some.where>
- MKD <unambiguous>
- 257 "<unambiguous>" directory created
- CWD <unambiguous>
-
- Note that the first example results in a subdirectory of the
- connected directory. In contrast, the argument in the second
- example contains enough information for TOPS-20 to tell that the
-
-
-Postel & Reynolds [Page 64]
-
-
-
-RFC 959 October 1985
-File Transfer Protocol
-
-
- <unambiguous> directory is a top-level directory. Note also that
- in the first example the user "violated" the protocol by
- attempting to access the freshly created directory with a name
- other than the one returned by TOPS-20. Problems could have
- resulted in this case had there been an <overrainbow> directory;
- this is an ambiguity inherent in some TOPS-20 implementations.
- Similar considerations apply to the RMD command. The point is
- this: except where to do so would violate a host's conventions for
- denoting relative versus absolute pathnames, the host should treat
- the operands of the MKD and RMD commands as subdirectories. The
- 257 reply to the MKD command must always contain the absolute
- pathname of the created directory.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Postel & Reynolds [Page 65]
-
-
-
-RFC 959 October 1985
-File Transfer Protocol
-
-
-APPENDIX III - RFCs on FTP
-
- Bhushan, Abhay, "A File Transfer Protocol", RFC 114 (NIC 5823),
- MIT-Project MAC, 16 April 1971.
-
- Harslem, Eric, and John Heafner, "Comments on RFC 114 (A File
- Transfer Protocol)", RFC 141 (NIC 6726), RAND, 29 April 1971.
-
- Bhushan, Abhay, et al, "The File Transfer Protocol", RFC 172
- (NIC 6794), MIT-Project MAC, 23 June 1971.
-
- Braden, Bob, "Comments on DTP and FTP Proposals", RFC 238 (NIC 7663),
- UCLA/CCN, 29 September 1971.
-
- Bhushan, Abhay, et al, "The File Transfer Protocol", RFC 265
- (NIC 7813), MIT-Project MAC, 17 November 1971.
-
- McKenzie, Alex, "A Suggested Addition to File Transfer Protocol",
- RFC 281 (NIC 8163), BBN, 8 December 1971.
-
- Bhushan, Abhay, "The Use of "Set Data Type" Transaction in File
- Transfer Protocol", RFC 294 (NIC 8304), MIT-Project MAC,
- 25 January 1972.
-
- Bhushan, Abhay, "The File Transfer Protocol", RFC 354 (NIC 10596),
- MIT-Project MAC, 8 July 1972.
-
- Bhushan, Abhay, "Comments on the File Transfer Protocol (RFC 354)",
- RFC 385 (NIC 11357), MIT-Project MAC, 18 August 1972.
-
- Hicks, Greg, "User FTP Documentation", RFC 412 (NIC 12404), Utah,
- 27 November 1972.
-
- Bhushan, Abhay, "File Transfer Protocol (FTP) Status and Further
- Comments", RFC 414 (NIC 12406), MIT-Project MAC, 20 November 1972.
-
- Braden, Bob, "Comments on File Transfer Protocol", RFC 430
- (NIC 13299), UCLA/CCN, 7 February 1973.
-
- Thomas, Bob, and Bob Clements, "FTP Server-Server Interaction",
- RFC 438 (NIC 13770), BBN, 15 January 1973.
-
- Braden, Bob, "Print Files in FTP", RFC 448 (NIC 13299), UCLA/CCN,
- 27 February 1973.
-
- McKenzie, Alex, "File Transfer Protocol", RFC 454 (NIC 14333), BBN,
- 16 February 1973.
-
-
-Postel & Reynolds [Page 66]
-
-
-
-RFC 959 October 1985
-File Transfer Protocol
-
-
- Bressler, Bob, and Bob Thomas, "Mail Retrieval via FTP", RFC 458
- (NIC 14378), BBN-NET and BBN-TENEX, 20 February 1973.
-
- Neigus, Nancy, "File Transfer Protocol", RFC 542 (NIC 17759), BBN,
- 12 July 1973.
-
- Krilanovich, Mark, and George Gregg, "Comments on the File Transfer
- Protocol", RFC 607 (NIC 21255), UCSB, 7 January 1974.
-
- Pogran, Ken, and Nancy Neigus, "Response to RFC 607 - Comments on the
- File Transfer Protocol", RFC 614 (NIC 21530), BBN, 28 January 1974.
-
- Krilanovich, Mark, George Gregg, Wayne Hathaway, and Jim White,
- "Comments on the File Transfer Protocol", RFC 624 (NIC 22054), UCSB,
- Ames Research Center, SRI-ARC, 28 February 1974.
-
- Bhushan, Abhay, "FTP Comments and Response to RFC 430", RFC 463
- (NIC 14573), MIT-DMCG, 21 February 1973.
-
- Braden, Bob, "FTP Data Compression", RFC 468 (NIC 14742), UCLA/CCN,
- 8 March 1973.
-
- Bhushan, Abhay, "FTP and Network Mail System", RFC 475 (NIC 14919),
- MIT-DMCG, 6 March 1973.
-
- Bressler, Bob, and Bob Thomas "FTP Server-Server Interaction - II",
- RFC 478 (NIC 14947), BBN-NET and BBN-TENEX, 26 March 1973.
-
- White, Jim, "Use of FTP by the NIC Journal", RFC 479 (NIC 14948),
- SRI-ARC, 8 March 1973.
-
- White, Jim, "Host-Dependent FTP Parameters", RFC 480 (NIC 14949),
- SRI-ARC, 8 March 1973.
-
- Padlipsky, Mike, "An FTP Command-Naming Problem", RFC 506
- (NIC 16157), MIT-Multics, 26 June 1973.
-
- Day, John, "Memo to FTP Group (Proposal for File Access Protocol)",
- RFC 520 (NIC 16819), Illinois, 25 June 1973.
-
- Merryman, Robert, "The UCSD-CC Server-FTP Facility", RFC 532
- (NIC 17451), UCSD-CC, 22 June 1973.
-
- Braden, Bob, "TENEX FTP Problem", RFC 571 (NIC 18974), UCLA/CCN,
- 15 November 1973.
-
-
-
-
-Postel & Reynolds [Page 67]
-
-
-
-RFC 959 October 1985
-File Transfer Protocol
-
-
- McKenzie, Alex, and Jon Postel, "Telnet and FTP Implementation -
- Schedule Change", RFC 593 (NIC 20615), BBN and MITRE,
- 29 November 1973.
-
- Sussman, Julie, "FTP Error Code Usage for More Reliable Mail
- Service", RFC 630 (NIC 30237), BBN, 10 April 1974.
-
- Postel, Jon, "Revised FTP Reply Codes", RFC 640 (NIC 30843),
- UCLA/NMC, 5 June 1974.
-
- Harvey, Brian, "Leaving Well Enough Alone", RFC 686 (NIC 32481),
- SU-AI, 10 May 1975.
-
- Harvey, Brian, "One More Try on the FTP", RFC 691 (NIC 32700), SU-AI,
- 28 May 1975.
-
- Lieb, J., "CWD Command of FTP", RFC 697 (NIC 32963), 14 July 1975.
-
- Harrenstien, Ken, "FTP Extension: XSEN", RFC 737 (NIC 42217), SRI-KL,
- 31 October 1977.
-
- Harrenstien, Ken, "FTP Extension: XRSQ/XRCP", RFC 743 (NIC 42758),
- SRI-KL, 30 December 1977.
-
- Lebling, P. David, "Survey of FTP Mail and MLFL", RFC 751, MIT,
- 10 December 1978.
-
- Postel, Jon, "File Transfer Protocol Specification", RFC 765, ISI,
- June 1980.
-
- Mankins, David, Dan Franklin, and Buzz Owen, "Directory Oriented FTP
- Commands", RFC 776, BBN, December 1980.
-
- Padlipsky, Michael, "FTP Unique-Named Store Command", RFC 949, MITRE,
- July 1985.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Postel & Reynolds [Page 68]
-
-
-
-RFC 959 October 1985
-File Transfer Protocol
-
-
-REFERENCES
-
- [1] Feinler, Elizabeth, "Internet Protocol Transition Workbook",
- Network Information Center, SRI International, March 1982.
-
- [2] Postel, Jon, "Transmission Control Protocol - DARPA Internet
- Program Protocol Specification", RFC 793, DARPA, September 1981.
-
- [3] Postel, Jon, and Joyce Reynolds, "Telnet Protocol
- Specification", RFC 854, ISI, May 1983.
-
- [4] Reynolds, Joyce, and Jon Postel, "Assigned Numbers", RFC 943,
- ISI, April 1985.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Postel & Reynolds [Page 69]
-
diff --git a/lib/inets/doc/src/Makefile b/lib/inets/doc/src/Makefile
index cbfa5c9e30..d66ef31ba6 100644
--- a/lib/inets/doc/src/Makefile
+++ b/lib/inets/doc/src/Makefile
@@ -43,12 +43,10 @@ XML_CHAPTER_FILES = \
inets_services.xml \
http_client.xml \
http_server.xml \
- ftp_client.xml \
notes.xml
XML_REF3_FILES = \
inets.xml \
- ftp.xml \
tftp.xml \
http_uri.xml\
httpc.xml\
diff --git a/lib/inets/doc/src/ftp.xml b/lib/inets/doc/src/ftp.xml
deleted file mode 100644
index 42bece4d38..0000000000
--- a/lib/inets/doc/src/ftp.xml
+++ /dev/null
@@ -1,948 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE erlref SYSTEM "erlref.dtd">
-
-<erlref>
- <header>
- <copyright>
- <year>1997</year><year>2016</year>
- <holder>Ericsson AB. All Rights Reserved.</holder>
- </copyright>
- <legalnotice>
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
- </legalnotice>
-
- <title>ftp</title>
- <prepared>Peter H&ouml;gfeldt</prepared>
- <docno></docno>
- <date>1997-11-05</date>
- <rev>B</rev>
- <file>ftp.xml</file>
- </header>
- <module>ftp</module>
- <modulesummary>A File Transfer Protocol client.</modulesummary>
-
- <description>
-
- <p>This module implements a client for file transfer
- according to a subset of the File Transfer Protocol (FTP), see
- <url href="http://www.ietf.org/rfc/rfc959.txt">RFC 959</url>.</p>
-
- <p>As from <c>Inets</c> 4.4.1, the FTP
- client always tries to use passive FTP mode and only resort
- to active FTP mode if this fails. This default behavior can be
- changed by start option <seealso marker="#mode">mode</seealso>.</p>
-
- <marker id="two_start"></marker>
-
- <p>An FTP client can be started in two ways. One is using the
- <seealso marker="#service_start">Inets service framework</seealso>,
- the other is to start it directly as a standalone process
- using function <seealso marker="#open">open</seealso>.</p>
-
- <p>For a simple example of an FTP session, see
- <seealso marker="ftp_client">Inets User's Guide</seealso>.</p>
-
- <p>In addition to the ordinary functions for receiving and sending
- files (see <c>recv/2</c>, <c>recv/3</c>, <c>send/2</c>, and
- <c>send/3</c>) there are functions for receiving remote files as
- binaries (see <c>recv_bin/2</c>) and for sending binaries to be
- stored as remote files (see <c>send_bin/3</c>).</p>
-
- <p>A set of functions is provvided for sending and receiving
- contiguous parts of a file to be stored in a remote file. For send,
- see <c>send_chunk_start/2</c>, <c>send_chunk/2</c>, and
- <c>send_chunk_end/1</c>. For receive, see
- <c>recv_chunk_start/2</c> and <c>recv_chunk/</c>).</p>
-
- <p>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 <c>ls</c> and <c>nlist</c>
- varies. Often real errors are not reported as errors by <c>ls</c>,
- even if, for example, a file or directory does not
- exist. <c>nlist</c> 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.</p>
-
- <marker id="service_start"></marker>
- </description>
-
- <section>
- <title>FTP CLIENT SERVICE START/STOP</title>
-
- <p>The FTP client can be started and stopped dynamically in runtime by
- calling the <c>Inets</c> application API
- <c>inets:start(ftpc, ServiceConfig)</c>,
- or <c>inets:start(ftpc, ServiceConfig, How)</c>, and
- <c>inets:stop(ftpc, Pid)</c>.
- For details, see <seealso marker="inets">inets(3)</seealso>.</p>
-
- <p>The available configuration options are as follows:</p>
-
- <taglist>
- <tag>{host, Host}</tag>
- <item>
- <marker id="host"></marker>
- <p>Host = <c>string() | ip_address()</c></p>
- </item>
-
- <tag>{port, Port}</tag>
- <item>
- <marker id="port"></marker>
- <p>Port = <c>integer() > 0</c></p>
- <p>Default is <c>21</c>.</p>
- </item>
-
- <tag>{mode, Mode}</tag>
- <item>
- <marker id="mode"></marker>
- <p>Mode = <c>active | passive</c></p>
- <p>Default is <c>passive</c>.</p>
- </item>
-
- <tag>{verbose, Verbose}</tag>
- <item>
- <marker id="verbose"></marker>
- <p>Verbose = <c>boolean()</c> </p>
- <p>Determines if the FTP communication is to be
- verbose or not.</p>
- <p>Default is <c>false</c>.</p>
- </item>
-
- <tag>{debug, Debug}</tag>
- <item>
- <marker id="debug"></marker>
- <p>Debug = <c>trace | debug | disable</c> </p>
- <p>Debugging using the dbg toolkit. </p>
- <p>Default is <c>disable</c>.</p>
- </item>
-
- <tag>{ipfamily, IpFamily}</tag>
- <item>
- <marker id="ipfamily"></marker>
- <p>IpFamily = <c>inet | inet6 | inet6fb4</c> </p>
- <p>With <c>inet6fb4</c> the client behaves as before, that is,
- tries to use IPv6, and only if that does not work it
- uses IPv4).</p>
- <p>Default is <c>inet</c> (IPv4).</p>
- </item>
-
- <tag>{timeout, Timeout}</tag>
- <item>
- <marker id="timeout"></marker>
- <p>Timeout = <c>non_neg_integer()</c></p>
- <p>Connection time-out.</p>
- <p>Default is <c>60000</c> (milliseconds).</p>
- </item>
-
- <tag>{dtimeout, DTimeout}</tag>
- <item>
- <marker id="dtimeout"></marker>
- <p>DTimeout = <c>non_neg_integer() | infinity</c> </p>
- <p>Data connect time-out.
- The time the client waits for the server to connect to the
- data socket.</p>
- <p>Default is <c>infinity</c>. </p>
- </item>
-
- <tag>{progress, Progress}</tag>
- <item>
- <marker id="progress"></marker>
- <p>Progress = <c>ignore | {CBModule, CBFunction, InitProgress}</c></p>
- <p><c>CBModule = atom()</c>, <c>CBFunction = atom()</c></p>
- <p><c>InitProgress = term()</c></p>
- <p>Default is <c>ignore</c>.</p>
- </item>
-
- </taglist>
-
- <p>Option <c>progress</c> 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 <c>ignore</c>,
- that is, the option is not used. When the progress option is
- specified, the following happens when <c>ftp:send/[3,4]</c> or
- <c>ftp:recv/[3,4]</c> are called:</p>
-
- <list type="bulleted">
- <item>
- <p>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 <c>UserProgressTerm</c> that will
- be used as input the next time the callback function is
- called.</p>
- <p><c>
- CBModule:CBFunction(InitProgress, File, {file_size, FileSize})
- </c></p>
- </item>
-
- <item>
- <p>Every time a chunk of bytes is transferred the
- following call is made:</p>
- <p><c>
- CBModule:CBFunction(UserProgressTerm, File, {transfer_size, TransferSize})
- </c></p>
- </item>
-
- <item>
- <p>At the end of the file the following call is
- made to indicate the end of the transfer:</p>
- <p><c>
- CBModule:CBFunction(UserProgressTerm, File, {transfer_size, 0})
- </c></p>
- </item>
- </list>
-
- <p>The callback function is to be defined as follows:</p>
-
- <p><c>
- CBModule:CBFunction(UserProgressTerm, File, Size) -> UserProgressTerm
- </c></p>
-
- <p><c>
- CBModule = CBFunction = atom()
- </c></p>
-
- <p><c>
- UserProgressTerm = term()
- </c></p>
-
- <p><c>
- File = string()
- </c></p>
-
- <p><c>
- Size = {transfer_size, integer()} | {file_size, integer()} | {file_size, unknown}
- </c></p>
-
- <p>For remote files, <c>ftp</c> cannot determine the
- file size in a platform independent way. In this case the size
- becomes <c>unknown</c> and it is left to the application to
- determine the size.</p>
-
- <note>
- <p>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 <c>ignore</c>.</p>
- </note>
-
- <p>The file transfer type is set to the default of the FTP server
- when the session is opened. This is usually ASCCI mode.
- </p>
-
- <p>The current local working directory (compare <c>lpwd/1</c>) is set
- to the value reported by <c>file:get_cwd/1</c>, the wanted
- local directory.
- </p>
-
- <p>The return value <c>Pid</c> 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.</p>
- </section>
-
- <section>
- <title>DATA TYPES</title>
- <p>The following type definitions are used by more than one
- function in the FTP client API:</p>
- <p><c>pid()</c> = identifier of an FTP connection</p>
- <p><c>string()</c> = list of ASCII characters</p>
- <p><c>shortage_reason()</c> = <c>etnospc | epnospc</c></p>
- <p><c>restriction_reason()</c> = <c>epath | efnamena | elogin | enotbinary</c>
- - all restrictions are not always relevant to all functions
- </p>
- <p><c>common_reason()</c> = <c>econn | eclosed | term()</c>
- - some explanation of what went wrong</p>
-
- <marker id="account"></marker>
- </section>
-
- <funcs>
- <func>
- <name>account(Pid, Account) -> ok | {error, Reason}</name>
- <fsummary>Specifies which account to use.</fsummary>
- <type>
- <v>Pid = pid()</v>
- <v>Account = string()</v>
- <v>Reason = eacct | common_reason()</v>
- </type>
- <desc>
- <p>Sets the account for an operation, if needed.</p>
-
- <marker id="append"></marker>
- <marker id="append2"></marker>
- <marker id="append3"></marker>
- </desc>
- </func>
-
- <func>
- <name>append(Pid, LocalFile) -> </name>
- <name>append(Pid, LocalFile, RemoteFile) -> ok | {error, Reason}</name>
- <fsummary>Transfers a file to remote server, and appends it to
- <c>Remotefile</c>.</fsummary>
- <type>
- <v>Pid = pid()</v>
- <v>LocalFile = RemoteFile = string()</v>
- <v>Reason = epath | elogin | etnospc | epnospc | efnamena | common_reason</v>
- </type>
- <desc>
- <p>Transfers the file <c>LocalFile</c> to the remote server. If
- <c>RemoteFile</c> is specified, the name of the remote file that the
- file is appended to is set to <c>RemoteFile</c>, otherwise
- to <c>LocalFile</c>. If the file does not exists,
- it is created.</p>
-
- <marker id="append_bin"></marker>
- </desc>
- </func>
-
- <func>
- <name>append_bin(Pid, Bin, RemoteFile) -> ok | {error, Reason}</name>
- <fsummary>Transfers a binary into a remote file.</fsummary>
- <type>
- <v>Pid = pid()</v>
- <v>Bin = binary()()</v>
- <v>RemoteFile = string()</v>
- <v>Reason = restriction_reason()| shortage_reason() | common_reason()</v>
- </type>
- <desc>
- <p>Transfers the binary <c>Bin</c> to the remote server and appends
- it to the file <c>RemoteFile</c>. If the file does not exist, it
- is created.</p>
-
- <marker id="append_chunk"></marker>
- </desc>
- </func>
-
- <func>
- <name>append_chunk(Pid, Bin) -> ok | {error, Reason}</name>
- <fsummary>Appends a chunk to the remote file.</fsummary>
- <type>
- <v>Pid = pid()</v>
- <v>Bin = binary()</v>
- <v>Reason = echunk | restriction_reason() | common_reason()</v>
- </type>
- <desc>
- <p>Transfers the chunk <c>Bin</c> to the remote server, which
- appends it to the file specified in the call to
- <c>append_chunk_start/2</c>.</p>
- <p>For some errors, for example, file system full, it is
- necessary to call <c>append_chunk_end</c> to get the
- proper reason.</p>
-
- <marker id="append_chunk_start"></marker>
- </desc>
- </func>
-
- <func>
- <name>append_chunk_start(Pid, File) -> ok | {error, Reason}</name>
- <fsummary>Starts transfer of file chunks for appending to <c>File</c>.</fsummary>
- <type>
- <v>Pid = pid()</v>
- <v>File = string()</v>
- <v>Reason = restriction_reason() | common_reason()</v>
- </type>
- <desc>
- <p>Starts the transfer of chunks for appending to the file
- <c>File</c> at the remote server. If the file does not exist,
- it is created.</p>
-
- <marker id="append_chunk_end"></marker>
- </desc>
- </func>
-
- <func>
- <name>append_chunk_end(Pid) -> ok | {error, Reason}</name>
- <fsummary>Stops transfer of chunks for appending.</fsummary>
- <type>
- <v>Pid = pid()</v>
- <v>Reason = echunk | restriction_reason() | shortage_reason() </v>
- </type>
- <desc>
- <p>Stops transfer of chunks for appending to the remote server.
- The file at the remote server, specified in the call to
- <c>append_chunk_start/2</c>, is closed by the server.</p>
-
- <marker id="cd"></marker>
- </desc>
- </func>
-
- <func>
- <name>cd(Pid, Dir) -> ok | {error, Reason}</name>
- <fsummary>Changes remote working directory.</fsummary>
- <type>
- <v>Pid = pid()</v>
- <v>Dir = string()</v>
- <v>Reason = restriction_reason() | common_reason() </v>
- </type>
- <desc>
- <p>Changes the working directory at the remote server to
- <c>Dir</c>.</p>
-
- <marker id="close"></marker>
- </desc>
- </func>
-
- <func>
- <name>close(Pid) -> ok</name>
- <fsummary>Ends the FTP session.</fsummary>
- <type>
- <v>Pid = pid()</v>
- </type>
- <desc>
- <p>Ends an FTP session, created using function
- <seealso marker="#open">open</seealso>.</p>
-
- <marker id="delete"></marker>
- </desc>
- </func>
-
- <func>
- <name>delete(Pid, File) -> ok | {error, Reason}</name>
- <fsummary>Deletes a file at the remote server.</fsummary>
- <type>
- <v>Pid = pid()</v>
- <v>File = string()</v>
- <v>Reason = restriction_reason() | common_reason()</v>
- </type>
- <desc>
- <p>Deletes the file <c>File</c> at the remote server.</p>
-
- <marker id="append"></marker>
- </desc>
- </func>
-
- <func>
- <name>formaterror(Tag) -> string()</name>
- <fsummary>Returns error diagnostics.</fsummary>
- <type>
- <v>Tag = {error, atom()} | atom()</v>
- </type>
- <desc>
- <p>Given an error return value <c>{error, AtomReason}</c>,
- this function returns a readable string describing the error.</p>
-
- <marker id="lcd"></marker>
- </desc>
- </func>
-
- <func>
- <name>lcd(Pid, Dir) -> ok | {error, Reason}</name>
- <fsummary>Changes local working directory.</fsummary>
- <type>
- <v>Pid = pid()</v>
- <v>Dir = string()</v>
- <v>Reason = restriction_reason()</v>
- </type>
- <desc>
- <p>Changes the working directory to <c>Dir</c> for the local client.</p>
-
- <marker id="lpwd"></marker>
- </desc>
- </func>
-
- <func>
- <name>lpwd(Pid) -> {ok, Dir}</name>
- <fsummary>Gets local current working directory.</fsummary>
- <type>
- <v>Pid = pid()</v>
- </type>
- <desc>
- <p>Returns the current working directory at the local client.</p>
-
- <marker id="ls"></marker>
- <marker id="ls1"></marker>
- <marker id="ls2"></marker>
- </desc>
- </func>
-
- <func>
- <name>ls(Pid) -> </name>
- <name>ls(Pid, Pathname) -> {ok, Listing} | {error, Reason}</name>
- <fsummary>List of files.</fsummary>
- <type>
- <v>Pid = pid()</v>
- <v>Pathname = string()</v>
- <v>Listing = string()</v>
- <v>Reason = restriction_reason() | common_reason()</v>
- </type>
- <desc>
- <p>Returns a list of files in long format.</p>
- <p><c>Pathname</c> can be a directory, a group of files, or
- a file. The <c>Pathname</c> string can contain wildcards.</p>
- <p><c>ls/1</c> implies the current remote directory of the user.</p>
- <p>The format of <c>Listing</c> depends on the operating system.
- On UNIX, it is typically produced from the output of the
- <c>ls -l</c> shell command.</p>
-
- <marker id="mkdir"></marker>
- </desc>
- </func>
-
- <func>
- <name>mkdir(Pid, Dir) -> ok | {error, Reason}</name>
- <fsummary>Creates a remote directory.</fsummary>
- <type>
- <v>Pid = pid()</v>
- <v>Dir = string()</v>
- <v>Reason = restriction_reason() | common_reason()</v>
- </type>
- <desc>
- <p>Creates the directory <c>Dir</c> at the remote server.</p>
-
- <marker id="nlist"></marker>
- <marker id="nlist1"></marker>
- <marker id="nlist2"></marker>
- </desc>
- </func>
-
- <func>
- <name>nlist(Pid) -> </name>
- <name>nlist(Pid, Pathname) -> {ok, Listing} | {error, Reason}</name>
- <fsummary>List of files.</fsummary>
- <type>
- <v>Pid = pid()</v>
- <v>Pathname = string()</v>
- <v>Listing = string()</v>
- <v>Reason = restriction_reason() | common_reason()</v>
- </type>
- <desc>
- <p>Returns a list of files in short format.</p>
- <p><c>Pathname</c> can be a directory, a group of files, or
- a file. The <c>Pathname</c> string can contain wildcards.</p>
- <p><c>nlist/1</c> implies the current remote directory of the user.</p>
- <p>The format of <c>Listing</c> is a stream of
- filenames where each filename is separated by &lt;CRLF&gt; or
- &lt;NL&gt;. Contrary to function <c>ls</c>, the purpose of
- <c>nlist</c> is to enable a program to
- process filename information automatically.</p>
-
- <marker id="open"></marker>
- </desc>
- </func>
-
- <func>
- <name>open(Host) -> {ok, Pid} | {error, Reason}</name>
- <name>open(Host, Opts) -> {ok, Pid} | {error, Reason}</name>
- <fsummary>Starts a standalone FTP client.</fsummary>
- <type>
- <v>Host = string() | ip_address()</v>
- <v>Opts = options()</v>
- <v>options() = [option()]</v>
- <v>option() = start_option() | open_option()</v>
- <v>start_option() = {verbose, verbose()} | {debug, debug()}</v>
- <v>verbose() = boolean() (default is false)</v>
- <v>debug() = disable | debug | trace (default is disable)</v>
- <v>open_option() = {ipfamily, ipfamily()} | {port, port()} | {mode, mode()} | {tls, tls_options()} | {timeout, timeout()} | {dtimeout, dtimeout()} | {progress, progress()}</v>
- <v>ipfamily() = inet | inet6 | inet6fb4 (default is inet)</v>
- <v>port() = integer() > 0 (default is 21)</v>
- <v>mode() = active | passive (default is passive)</v>
- <v>tls_options() = [<seealso marker="ssl:ssl#type-ssloption">ssl:ssloption()</seealso>]</v>
- <v>timeout() = integer() > 0 (default is 60000 milliseconds)</v>
- <v>dtimeout() = integer() > 0 | infinity (default is infinity)</v>
- <v>pogress() = ignore | {module(), function(), initial_data()} (default is ignore)</v>
- <v>module() = atom()</v>
- <v>function() = atom()</v>
- <v>initial_data() = term()</v>
- <v>Reason = ehost | term()</v>
- </type>
-
- <desc>
- <p>Starts a standalone FTP client process
- (without the <c>Inets</c> service framework) and
- opens a session with the FTP server at <c>Host</c>. </p>
-
- <p>If option <c>{tls, tls_options()}</c> is present, the FTP session
- is transported over <c>tls</c> (<c>ftps</c>, see
- <url href="http://www.ietf.org/rfc/rfc4217.txt">RFC 4217</url>).
- The list <c>tls_options()</c> can be empty. The function
- <seealso marker="ssl:ssl#connect/3"><c>ssl:connect/3</c></seealso>
- is used for securing both the control connection and the data sessions.
- </p>
-
- <p>A session opened in this way is closed using function
- <seealso marker="#close">close</seealso>.</p>
-
- <marker id="pwd"></marker>
- </desc>
- </func>
-
- <func>
- <name>pwd(Pid) -> {ok, Dir} | {error, Reason}</name>
- <fsummary>Gets the remote current working directory.</fsummary>
- <type>
- <v>Pid = pid()</v>
- <v>Reason = restriction_reason() | common_reason()</v>
- </type>
- <desc>
- <p>Returns the current working directory at the remote server.</p>
-
- <marker id="recv"></marker>
- <marker id="recv2"></marker>
- <marker id="recv3"></marker>
- </desc>
- </func>
-
- <func>
- <name>recv(Pid, RemoteFile) -> </name>
- <name>recv(Pid, RemoteFile, LocalFile) -> ok | {error, Reason}</name>
- <fsummary>Transfers a file from remote server.</fsummary>
- <type>
- <v>Pid = pid()</v>
- <v>RemoteFile = LocalFile = string()</v>
- <v>Reason = restriction_reason() | common_reason() | file_write_error_reason() </v>
- <v>file_write_error_reason() = see file:write/2</v>
- </type>
- <desc>
- <p>Transfers the file <c>RemoteFile</c> from the remote server
- to the file system of the local client. If
- <c>LocalFile</c> is specified, the local file will be
- <c>LocalFile</c>, otherwise
- <c>RemoteFile</c>.</p>
- <p>If the file write fails (for example, <c>enospc</c>), the command is
- aborted and <c>{error, file_write_error_reason()}</c> is returned.
- However, the file is <em>not</em> removed.</p>
-
- <marker id="recv_bin"></marker>
- </desc>
- </func>
-
- <func>
- <name>recv_bin(Pid, RemoteFile) -> {ok, Bin} | {error, Reason}</name>
- <fsummary>Transfers a file from remote server as a binary.</fsummary>
- <type>
- <v>Pid = pid()</v>
- <v>Bin = binary()</v>
- <v>RemoteFile = string()</v>
- <v>Reason = restriction_reason() | common_reason()</v>
- </type>
- <desc>
- <p>Transfers the file <c>RemoteFile</c> from the remote server and
- receives it as a binary.</p>
-
- <marker id="recv_chunk_start"></marker>
- </desc>
- </func>
-
- <func>
- <name>recv_chunk_start(Pid, RemoteFile) -> ok | {error, Reason}</name>
- <fsummary>Starts chunk-reading of the remote file.</fsummary>
- <type>
- <v>Pid = pid()</v>
- <v>RemoteFile = string()</v>
- <v>Reason = restriction_reason() | common_reason()</v>
- </type>
- <desc>
- <p>Starts transfer of the file <c>RemoteFile</c> from the
- remote server.</p>
-
- <marker id="recv_chunk"></marker>
- </desc>
- </func>
-
- <func>
- <name>recv_chunk(Pid) -> ok | {ok, Bin} | {error, Reason}</name>
- <fsummary>Receives a chunk of the remote file.</fsummary>
- <type>
- <v>Pid = pid()</v>
- <v>Bin = binary()</v>
- <v>Reason = restriction_reason() | common_reason()</v>
- </type>
- <desc>
- <p>Receives a chunk of the remote file (<c>RemoteFile</c> of
- <c>recv_chunk_start</c>). The return values have the following
- meaning:</p>
- <list type="bulleted">
- <item><c>ok</c> = the transfer is complete.</item>
- <item><c>{ok, Bin}</c> = just another chunk of the file.</item>
- <item><c>{error, Reason}</c> = transfer failed.</item>
- </list>
-
- <marker id="rename"></marker>
- </desc>
- </func>
-
- <func>
- <name>rename(Pid, Old, New) -> ok | {error, Reason}</name>
- <fsummary>Renames a file at the remote server.</fsummary>
- <type>
- <v>Pid = pid()</v>
- <v>CurrFile = NewFile = string()</v>
- <v>Reason = restriction_reason() | common_reason()</v>
- </type>
- <desc>
- <p>Renames <c>Old</c> to <c>New</c> at the remote server.</p>
-
- <marker id="rmdir"></marker>
- </desc>
- </func>
-
- <func>
- <name>rmdir(Pid, Dir) -> ok | {error, Reason}</name>
- <fsummary>Removes a remote directory.</fsummary>
- <type>
- <v>Pid = pid()</v>
- <v>Dir = string()</v>
- <v>Reason = restriction_reason() | common_reason()</v>
- </type>
- <desc>
- <p>Removes directory <c>Dir</c> at the remote server.</p>
-
- <marker id="send"></marker>
- <marker id="send2"></marker>
- <marker id="send3"></marker>
- </desc>
- </func>
-
- <func>
- <name>send(Pid, LocalFile) -></name>
- <name>send(Pid, LocalFile, RemoteFile) -> ok | {error, Reason}</name>
- <fsummary>Transfers a file to the remote server.</fsummary>
- <type>
- <v>Pid = pid()</v>
- <v>LocalFile = RemoteFile = string()</v>
- <v>Reason = restriction_reason() | common_reason() | shortage_reason()</v>
- </type>
- <desc>
- <p>Transfers the file <c>LocalFile</c> to the remote server. If
- <c>RemoteFile</c> is specified, the name of the remote file is set
- to <c>RemoteFile</c>, otherwise to <c>LocalFile</c>.</p>
-
- <marker id="send_bin"></marker>
- </desc>
- </func>
-
- <func>
- <name>send_bin(Pid, Bin, RemoteFile) -> ok | {error, Reason}</name>
- <fsummary>Transfers a binary into a remote file.</fsummary>
- <type>
- <v>Pid = pid()</v>
- <v>Bin = binary()()</v>
- <v>RemoteFile = string()</v>
- <v>Reason = restriction_reason() | common_reason() | shortage_reason()</v>
- </type>
- <desc>
- <p>Transfers the binary <c>Bin</c> into the file <c>RemoteFile</c>
- at the remote server.</p>
-
- <marker id="send_chunk"></marker>
- </desc>
- </func>
-
- <func>
- <name>send_chunk(Pid, Bin) -> ok | {error, Reason}</name>
- <fsummary>Writes a chunk to the remote file.</fsummary>
- <type>
- <v>Pid = pid()</v>
- <v>Bin = binary()</v>
- <v>Reason = echunk | restriction_reason() | common_reason()</v>
- </type>
- <desc>
- <p>Transfers the chunk <c>Bin</c> to the remote server, which
- writes it into the file specified in the call to
- <c>send_chunk_start/2</c>.</p>
- <p>For some errors, for example, file system full, it is
- necessary to to call <c>send_chunk_end</c> to get the
- proper reason.</p>
-
- <marker id="send_chunk_start"></marker>
- </desc>
- </func>
-
- <func>
- <name>send_chunk_start(Pid, File) -> ok | {error, Reason}</name>
- <fsummary>Starts transfer of file chunks.</fsummary>
- <type>
- <v>Pid = pid()</v>
- <v>File = string()</v>
- <v>Reason = restriction_reason() | common_reason()</v>
- </type>
- <desc>
- <p>Starts transfer of chunks into the file <c>File</c> at the
- remote server.</p>
-
- <marker id="send_chunk_end"></marker>
- </desc>
- </func>
-
- <func>
- <name>send_chunk_end(Pid) -> ok | {error, Reason}</name>
- <fsummary>Stops transfer of chunks.</fsummary>
- <type>
- <v>Pid = pid()</v>
- <v>Reason = restriction_reason() | common_reason() | shortage_reason()</v>
- </type>
- <desc>
- <p>Stops transfer of chunks to the remote server. The file at the
- remote server, specified in the call to <c>send_chunk_start/2</c>
- is closed by the server.</p>
-
- <marker id="type"></marker>
- </desc>
- </func>
-
- <func>
- <name>type(Pid, Type) -> ok | {error, Reason}</name>
- <fsummary>Sets transfer type to <c>ascii</c>or <c>binary</c>.</fsummary>
- <type>
- <v>Pid = pid()</v>
- <v>Type = ascii | binary</v>
- <v>Reason = etype | restriction_reason() | common_reason()</v>
- </type>
- <desc>
- <p>Sets the file transfer type to <c>ascii</c> or <c>binary</c>. When
- an FTP session is opened, the default transfer type of the
- server is used, most often <c>ascii</c>, which is default
- according to <url href="http://www.ietf.org/rfc/rfc959.txt">RFC 959</url>.</p>
- <marker id="user3"></marker>
- </desc>
- </func>
-
- <func>
- <name>user(Pid, User, Password) -> ok | {error, Reason}</name>
- <fsummary>User login.</fsummary>
- <type>
- <v>Pid = pid()</v>
- <v>User = Password = string()</v>
- <v>Reason = euser | common_reason()</v>
- </type>
- <desc>
- <p>Performs login of <c>User</c> with <c>Password</c>.</p>
-
- <marker id="user4"></marker>
- </desc>
- </func>
-
- <func>
- <name>user(Pid, User, Password, Account) -> ok | {error, Reason}</name>
- <fsummary>User login.</fsummary>
- <type>
- <v>Pid = pid()</v>
- <v>User = Password = string()</v>
- <v>Reason = euser | common_reason() </v>
- </type>
- <desc>
- <p>Performs login of <c>User</c> with <c>Password</c> to the account
- specified by <c>Account</c>.</p>
-
- <marker id="quote"></marker>
- </desc>
- </func>
-
- <func>
- <name>quote(Pid, Command) -> [FTPLine]</name>
- <fsummary>Sends an arbitrary FTP command.</fsummary>
- <type>
- <v>Pid = pid()</v>
- <v>Command = string()</v>
- <v>FTPLine = string(</v>
- </type>
- <desc><note><p>The telnet end of line characters, from the FTP
- protocol definition, CRLF, for example, "\\r\\n" has been removed.</p></note>
- <p>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.</p>
- <note>
- <p>FTP commands requiring a data connection cannot be
- successfully issued with this function.</p>
- </note>
- </desc>
- </func>
- </funcs>
-
- <section>
- <title>ERRORS</title>
- <p>The possible error reasons and the corresponding diagnostic strings
- returned by <c>formaterror/1</c> are as follows:
- </p>
- <taglist>
- <tag><c>echunk</c></tag>
- <item>
- <p>Synchronization error during chunk sending according to one
- of the following:
- </p><list type="bulleted">
- <item>A call is made to <c>send_chunk/2</c> or <c>send_chunk_end/1</c>
- before a call to <c>send_chunk_start/2</c>.</item>
- <item>A call has been made to another transfer function during chunk
- sending, that is, before a call to <c>send_chunk_end/1</c>.</item>
- </list>
- </item>
- <tag><c>eclosed</c></tag>
- <item>
- <p>The session is closed.</p>
- </item>
- <tag><c>econn</c></tag>
- <item>
- <p>Connection to the remote server is prematurely closed.</p>
- </item>
- <tag><c>ehost</c></tag>
- <item>
- <p>Host is not found, FTP server is not found, or connection is rejected
- by FTP server.</p>
- </item>
- <tag><c>elogin</c></tag>
- <item>
- <p>User is not logged in.</p>
- </item>
- <tag><c>enotbinary</c></tag>
- <item>
- <p>Term is not a binary.</p>
- </item>
- <tag><c>epath</c></tag>
- <item>
- <p>No such file or directory, or directory already exists, or
- permission denied.</p>
- </item>
- <tag><c>etype</c></tag>
- <item>
- <p>No such type.</p>
- </item>
- <tag><c>euser</c></tag>
- <item>
- <p>Invalid username or password.</p>
- </item>
- <tag><c>etnospc</c></tag>
- <item>
- <p>Insufficient storage space in system [452].</p>
- </item>
- <tag><c>epnospc</c></tag>
- <item>
- <p>Exceeded storage allocation (for current directory or
- dataset) [552].</p>
- </item>
- <tag><c>efnamena</c></tag>
- <item>
- <p>Filename not allowed [553].</p>
- </item>
- </taglist>
- </section>
-
- <section>
- <title>SEE ALSO</title>
- <p><seealso marker="kernel:file">file(3)</seealso>
- <seealso marker="stdlib:filename">filename(3)</seealso>
- and J. Postel and J. Reynolds: File Transfer Protocol
- (<url href="http://www.ietf.org/rfc/rfc959.txt">RFC 959</url>).
- </p>
- </section>
-
-</erlref>
-
-
diff --git a/lib/inets/doc/src/ftp_client.xml b/lib/inets/doc/src/ftp_client.xml
deleted file mode 100644
index 990dd68604..0000000000
--- a/lib/inets/doc/src/ftp_client.xml
+++ /dev/null
@@ -1,86 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE chapter SYSTEM "chapter.dtd">
-
-<chapter>
- <header>
- <copyright>
- <year>2004</year><year>2016</year>
- <holder>Ericsson AB. All Rights Reserved.</holder>
- </copyright>
- <legalnotice>
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
- </legalnotice>
-
- <title>FTP Client</title>
- <prepared>Ingela Anderton Andin</prepared>
- <responsible></responsible>
- <docno></docno>
- <approved></approved>
- <checked></checked>
- <date></date>
- <rev></rev>
- <file>ftp_client.xml</file>
- </header>
-
- <section>
- <title>Getting Started</title>
-
- <p>FTP clients are considered to be rather temporary. Thus,
- they are only started and stopped during runtime and cannot
- be started at application startup.
- The FTP client API is designed to allow some functions to
- return intermediate results. This implies that only the process
- that started the FTP client can access it with
- preserved sane semantics.
- If the process that started the FTP session
- dies, the FTP client process terminates.</p>
-
- <p>The client supports IPv6 as long as the underlying mechanisms
- also do so.</p>
-
- <p>The following is a simple example of an FTP session, where
- the user <c>guest</c> with password <c>password</c> logs on to
- the remote host <c>erlang.org</c>:</p>
- <code type="erl"><![CDATA[
- 1> inets:start().
- ok
- 2> {ok, Pid} = inets:start(ftpc, [{host, "erlang.org"}]).
- {ok,<0.22.0>}
- 3> ftp:user(Pid, "guest", "password").
- ok
- 4> ftp:pwd(Pid).
- {ok, "/home/guest"}
- 5> ftp:cd(Pid, "appl/examples").
- ok
- 6> ftp:lpwd(Pid).
- {ok, "/home/fred"}.
- 7> ftp:lcd(Pid, "/home/eproj/examples").
- ok
- 8> ftp:recv(Pid, "appl.erl").
- ok
- 9> inets:stop(ftpc, Pid).
- ok
- ]]></code>
- <p> The file
- <c>appl.erl</c> is transferred from the remote to the local
- host. When the session is opened, the current directory at
- the remote host is <c>/home/guest</c>, and <c>/home/fred</c>
- at the local host. Before transferring the file, the current
- local directory is changed to <c>/home/eproj/examples</c>, and
- the remote directory is set to
- <c>/home/guest/appl/examples</c>.</p>
- </section>
-</chapter>
-
-
diff --git a/lib/inets/doc/src/introduction.xml b/lib/inets/doc/src/introduction.xml
index 1af2ef5dae..c006599a21 100644
--- a/lib/inets/doc/src/introduction.xml
+++ b/lib/inets/doc/src/introduction.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>1997</year><year>2016</year>
+ <year>1997</year><year>2018</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -22,12 +22,12 @@
</legalnotice>
<title>Introduction</title>
- <prepared>Ingela Anderton Andin</prepared>
+ <prepared>Péter Dimitrov</prepared>
<responsible></responsible>
<docno></docno>
<approved></approved>
<checked></checked>
- <date>2004-09-28</date>
+ <date>2018-02-28</date>
<rev>A</rev>
<file>introduction.xml</file>
</header>
@@ -37,7 +37,6 @@
<p><c>Inets</c> is a container for Internet clients and servers
including the following:</p>
<list type="bulleted">
- <item>An FTP client</item>
<item>A TFTP client and server</item>
<item>An <term id="HTTP"></term> client and server</item>
</list>
@@ -50,7 +49,7 @@
<title>Prerequisites</title>
<p>It is assumed that the reader is familiar with the Erlang
programming language, concepts of OTP, and has a basic
- understanding of the FTP, TFTP, and HTTP protocols.</p>
+ understanding of the TFTP and HTTP protocols.</p>
</section>
</chapter>
diff --git a/lib/inets/doc/src/part.xml b/lib/inets/doc/src/part.xml
index f777481b5c..e7a786d47b 100644
--- a/lib/inets/doc/src/part.xml
+++ b/lib/inets/doc/src/part.xml
@@ -4,7 +4,7 @@
<part xmlns:xi="http://www.w3.org/2001/XInclude">
<header>
<copyright>
- <year>2004</year><year>2016</year>
+ <year>2004</year><year>2018</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -23,9 +23,9 @@
</legalnotice>
<title>Inets User's Guide</title>
- <prepared>Ingela Anderton Andin</prepared>
+ <prepared>Péter Dimitrov</prepared>
<docno></docno>
- <date>2002-09-17</date>
+ <date>2018-02-28</date>
<rev>A</rev>
<file>part.sgml</file>
</header>
@@ -33,7 +33,6 @@
<p>The <c>Inets</c> application provides a set of
Internet-related services as follows:</p>
<list type="bulleted">
- <item>An FTP client</item>
<item>A TFTP client and server</item>
<item>An <term id="HTTP"></term> client and server</item>
</list>
@@ -43,7 +42,6 @@
</description>
<xi:include href="introduction.xml"/>
<xi:include href="inets_services.xml"/>
- <xi:include href="ftp_client.xml"/>
<xi:include href="http_client.xml"/>
<xi:include href="http_server.xml"/>
</part>
diff --git a/lib/inets/doc/src/ref_man.xml b/lib/inets/doc/src/ref_man.xml
index 27021ea09a..33886d1a41 100644
--- a/lib/inets/doc/src/ref_man.xml
+++ b/lib/inets/doc/src/ref_man.xml
@@ -4,7 +4,7 @@
<application xmlns:xi="http://www.w3.org/2001/XInclude">
<header>
<copyright>
- <year>1997</year><year>2015</year>
+ <year>1997</year><year>2018</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -23,19 +23,18 @@
</legalnotice>
<title>Inets Reference Manual</title>
- <prepared>Joakim Greben&ouml;</prepared>
+ <prepared>Péter Dimitrov</prepared>
<docno></docno>
- <date>1997-07-16</date>
+ <date>2018-02-28</date>
<rev>2.1</rev>
<file>ref_man.xml</file>
</header>
<description>
<p><c>Inets</c> is a container for Internet clients and
- servers. An FTP client, an HTTP client and server, and
+ servers. An HTTP client and server, and
a TFTP client and server are incorporated in <c>Inets</c>.</p>
</description>
<xi:include href="inets.xml"/>
- <xi:include href="ftp.xml"/>
<xi:include href="tftp.xml"/>
<xi:include href="httpc.xml"/>
<xi:include href="httpd.xml"/>
diff --git a/lib/inets/src/ftp/Makefile b/lib/inets/src/ftp/Makefile
deleted file mode 100644
index 6b99694ea7..0000000000
--- a/lib/inets/src/ftp/Makefile
+++ /dev/null
@@ -1,104 +0,0 @@
-#
-# %CopyrightBegin%
-#
-# Copyright Ericsson AB 2005-2016. All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-# %CopyrightEnd%
-#
-#
-
-include $(ERL_TOP)/make/target.mk
-EBIN = ../../ebin
-include $(ERL_TOP)/make/$(TARGET)/otp.mk
-
-
-# ----------------------------------------------------
-# Application version
-# ----------------------------------------------------
-include ../../vsn.mk
-
-VSN = $(INETS_VSN)
-
-
-# ----------------------------------------------------
-# Release directory specification
-# ----------------------------------------------------
-RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
-
-
-# ----------------------------------------------------
-# Target Specs
-# ----------------------------------------------------
-MODULES = \
- ftp \
- ftp_progress \
- ftp_response \
- ftp_sup
-
-HRL_FILES = ftp_internal.hrl
-
-ERL_FILES = $(MODULES:%=%.erl)
-
-TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR))
-
-
-# ----------------------------------------------------
-# FLAGS
-# ----------------------------------------------------
-
-include ../inets_app/inets.mk
-
-ifeq ($(FTP_DEBUG),true)
- INETS_FLAGS += -Dftp_debug
-endif
-
-ERL_COMPILE_FLAGS += \
- $(INETS_FLAGS) \
- $(INETS_ERL_COMPILE_FLAGS) \
- -I../../include \
- -I../inets_app
-
-
-# ----------------------------------------------------
-# Targets
-# ----------------------------------------------------
-
-debug opt: $(TARGET_FILES)
-
-clean:
- rm -f $(TARGET_FILES)
- rm -f core
-
-docs:
-
-# ----------------------------------------------------
-# Release Target
-# ----------------------------------------------------
-include $(ERL_TOP)/make/otp_release_targets.mk
-
-release_spec: opt
- $(INSTALL_DIR) "$(RELSYSDIR)/src"
- $(INSTALL_DIR) "$(RELSYSDIR)/src/ftp"
- $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) "$(RELSYSDIR)/src/ftp"
- $(INSTALL_DIR) "$(RELSYSDIR)/ebin"
- $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin"
-
-release_docs_spec:
-
-info:
- @echo "APPLICATION = $(APPLICATION)"
- @echo "INETS_DEBUG = $(INETS_DEBUG)"
- @echo "INETS_FLAGS = $(INETS_FLAGS)"
- @echo "ERL_COMPILE_FLAGS = $(ERL_COMPILE_FLAGS)"
diff --git a/lib/inets/src/ftp/ftp.erl b/lib/inets/src/ftp/ftp.erl
deleted file mode 100644
index e0430654eb..0000000000
--- a/lib/inets/src/ftp/ftp.erl
+++ /dev/null
@@ -1,2596 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1997-2017. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%% http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-%%
-%% %CopyrightEnd%
-%%
-%%
-%% Description: This module implements an ftp client, RFC 959.
-%% It also supports ipv6 RFC 2428 and starttls RFC 4217.
-
--module(ftp).
-
--behaviour(gen_server).
--behaviour(inets_service).
-
-
-%% API - Client interface
--export([cd/2, close/1, delete/2, formaterror/1,
- lcd/2, lpwd/1, ls/1, ls/2,
- mkdir/2, nlist/1, nlist/2,
- open/1, open/2,
- pwd/1, quote/2,
- recv/2, recv/3, recv_bin/2,
- recv_chunk_start/2, recv_chunk/1,
- rename/3, rmdir/2,
- send/2, send/3, send_bin/3,
- send_chunk_start/2, send_chunk/2, send_chunk_end/1,
- type/2, user/3, user/4, account/2,
- append/3, append/2, append_bin/3,
- append_chunk/2, append_chunk_end/1, append_chunk_start/2,
- info/1, latest_ctrl_response/1]).
-
-%% gen_server callbacks
--export([init/1, handle_call/3, handle_cast/2,
- handle_info/2, terminate/2, code_change/3]).
-
-%% supervisor callbacks
--export([start_link/1, start_link/2]).
-
-%% Behavior callbacks
--export([start_standalone/1, start_service/1,
- stop_service/1, services/0, service_info/1]).
-
--include("ftp_internal.hrl").
-
-%% Constants used in internal state definition
--define(CONNECTION_TIMEOUT, 60*1000).
--define(DATA_ACCEPT_TIMEOUT, infinity).
--define(DEFAULT_MODE, passive).
--define(PROGRESS_DEFAULT, ignore).
--define(FTP_EXT_DEFAULT, false).
-
-%% Internal Constants
--define(FTP_PORT, 21).
--define(FILE_BUFSIZE, 4096).
-
-%% Internal state
--record(state, {
- csock = undefined, % socket() - Control connection socket
- dsock = undefined, % socket() - Data connection socket
- tls_options = undefined, % list()
- verbose = false, % boolean()
- ldir = undefined, % string() - Current local directory
- type = ftp_server_default, % atom() - binary | ascii
- chunk = false, % boolean() - Receiving data chunks
- mode = ?DEFAULT_MODE, % passive | active
- timeout = ?CONNECTION_TIMEOUT, % integer()
- %% Data received so far on the data connection
- data = <<>>, % binary()
- %% Data received so far on the control connection
- %% {BinStream, AccLines}. If a binary sequence
- %% ends with ?CR then keep it in the binary to
- %% be able to detect if the next received byte is ?LF
- %% and hence the end of the response is reached!
- ctrl_data = {<<>>, [], start}, % {binary(), [bytes()], LineStatus}
- %% pid() - Client pid (note not the same as "From")
- latest_ctrl_response = "",
- owner = undefined,
- client = undefined, % "From" to be used in gen_server:reply/2
- %% Function that activated a connection and maybe some
- %% data needed further on.
- caller = undefined, % term()
- ipfamily, % inet | inet6 | inet6fb4
- progress = ignore, % ignore | pid()
- dtimeout = ?DATA_ACCEPT_TIMEOUT, % non_neg_integer() | infinity
- tls_upgrading_data_connection = false,
- ftp_extension = ?FTP_EXT_DEFAULT
- }).
-
--record(recv_chunk_closing, {
- dconn_closed = false,
- pos_compl_received = false,
- client_called_us = false
- }).
-
-
--type shortage_reason() :: 'etnospc' | 'epnospc'.
--type restriction_reason() :: 'epath' | 'efnamena' | 'elogin' | 'enotbinary'.
--type common_reason() :: 'econn' | 'eclosed' | term().
--type file_write_error_reason() :: term(). % See file:write for more info
-
--define(DBG(F,A), 'n/a').
-%%-define(DBG(F,A), io:format(F,A)).
-%%-define(DBG(F,A), ct:pal("~p:~p " ++ if is_list(F) -> F; is_atom(F) -> atom_to_list(F) end, [?MODULE,?LINE|A])).
-
-%%%=========================================================================
-%%% API - CLIENT FUNCTIONS
-%%%=========================================================================
-
-%%--------------------------------------------------------------------------
-%% open(HostOrOtpList, <Port>, <Flags>) -> {ok, Pid} | {error, ehost}
-%% HostOrOtpList = string() | [{option_list, Options}]
-%% Port = integer(),
-%% Flags = [Flag],
-%% Flag = verbose | debug | trace
-%%
-%% Description: Start an ftp client and connect to a host.
-%%--------------------------------------------------------------------------
-
--spec open(Host :: string() | inet:ip_address()) ->
- {'ok', Pid :: pid()} | {'error', Reason :: 'ehost' | term()}.
-
-%% <BACKWARD-COMPATIBILLITY>
-open({option_list, Options}) when is_list(Options) ->
- try
- {ok, StartOptions} = start_options(Options),
- {ok, OpenOptions} = open_options(Options),
- case ftp_sup:start_child([[[{client, self()} | StartOptions], []]]) of
- {ok, Pid} ->
- call(Pid, {open, ip_comm, OpenOptions}, plain);
- Error1 ->
- Error1
- end
- catch
- throw:Error2 ->
- Error2
- end;
-%% </BACKWARD-COMPATIBILLITY>
-
-open(Host) ->
- open(Host, []).
-
--spec open(Host :: string() | inet:ip_address(), Opts :: list()) ->
- {'ok', Pid :: pid()} | {'error', Reason :: 'ehost' | term()}.
-
-%% <BACKWARD-COMPATIBILLITY>
-open(Host, Port) when is_integer(Port) ->
- open(Host, [{port, Port}]);
-%% </BACKWARD-COMPATIBILLITY>
-
-open(Host, Opts) when is_list(Opts) ->
- ?fcrt("open", [{host, Host}, {opts, Opts}]),
- try
- {ok, StartOptions} = start_options(Opts),
- ?fcrt("open", [{start_options, StartOptions}]),
- {ok, OpenOptions} = open_options([{host, Host}|Opts]),
- ?fcrt("open", [{open_options, OpenOptions}]),
- case start_link(StartOptions, []) of
- {ok, Pid} ->
- do_open(Pid, OpenOptions, tls_options(Opts));
- Error1 ->
- ?fcrt("open - error", [{error1, Error1}]),
- Error1
- end
- catch
- throw:Error2 ->
- ?fcrt("open - error", [{error2, Error2}]),
- Error2
- end.
-
-do_open(Pid, OpenOptions, TLSOpts) ->
- case call(Pid, {open, ip_comm, OpenOptions}, plain) of
- {ok, Pid} ->
- maybe_tls_upgrade(Pid, TLSOpts);
- Error ->
- Error
- end.
-%%--------------------------------------------------------------------------
-%% user(Pid, User, Pass, <Acc>) -> ok | {error, euser} | {error, econn}
-%% | {error, eacct}
-%% Pid = pid(),
-%% User = Pass = Acc = string()
-%%
-%% Description: Login with or without a supplied account name.
-%%--------------------------------------------------------------------------
--spec user(Pid :: pid(),
- User :: string(),
- Pass :: string()) ->
- 'ok' | {'error', Reason :: 'euser' | common_reason()}.
-
-user(Pid, User, Pass) ->
- case {is_name_sane(User), is_name_sane(Pass)} of
- {true, true} ->
- call(Pid, {user, User, Pass}, atom);
- _ ->
- {error, euser}
- end.
-
--spec user(Pid :: pid(),
- User :: string(),
- Pass :: string(),
- Acc :: string()) ->
- 'ok' | {'error', Reason :: 'euser' | common_reason()}.
-
-user(Pid, User, Pass, Acc) ->
- case {is_name_sane(User), is_name_sane(Pass), is_name_sane(Acc)} of
- {true, true, true} ->
- call(Pid, {user, User, Pass, Acc}, atom);
- _ ->
- {error, euser}
- end.
-
-
-%%--------------------------------------------------------------------------
-%% account(Pid, Acc) -> ok | {error, eacct}
-%% Pid = pid()
-%% Acc= string()
-%%
-%% Description: Set a user Account.
-%%--------------------------------------------------------------------------
-
--spec account(Pid :: pid(), Acc :: string()) ->
- 'ok' | {'error', Reason :: 'eacct' | common_reason()}.
-
-account(Pid, Acc) ->
- case is_name_sane(Acc) of
- true ->
- call(Pid, {account, Acc}, atom);
- _ ->
- {error, eacct}
- end.
-
-
-%%--------------------------------------------------------------------------
-%% pwd(Pid) -> {ok, Dir} | {error, elogin} | {error, econn}
-%% Pid = pid()
-%% Dir = string()
-%%
-%% Description: Get the current working directory at remote server.
-%%--------------------------------------------------------------------------
-
--spec pwd(Pid :: pid()) ->
- {'ok', Dir :: string()} |
- {'error', Reason :: restriction_reason() | common_reason()}.
-
-pwd(Pid) ->
- call(Pid, pwd, ctrl).
-
-
-%%--------------------------------------------------------------------------
-%% lpwd(Pid) -> {ok, Dir}
-%% Pid = pid()
-%% Dir = string()
-%%
-%% Description: Get the current working directory at local server.
-%%--------------------------------------------------------------------------
-
--spec lpwd(Pid :: pid()) ->
- {'ok', Dir :: string()}.
-
-lpwd(Pid) ->
- call(Pid, lpwd, string).
-
-
-%%--------------------------------------------------------------------------
-%% cd(Pid, Dir) -> ok | {error, epath} | {error, elogin} | {error, econn}
-%% Pid = pid()
-%% Dir = string()
-%%
-%% Description: Change current working directory at remote server.
-%%--------------------------------------------------------------------------
-
--spec cd(Pid :: pid(), Dir :: string()) ->
- 'ok' | {'error', Reason :: restriction_reason() | common_reason()}.
-
-cd(Pid, Dir) ->
- case is_name_sane(Dir) of
- true ->
- call(Pid, {cd, Dir}, atom);
- _ ->
- {error, efnamena}
- end.
-
-
-%%--------------------------------------------------------------------------
-%% lcd(Pid, Dir) -> ok | {error, epath}
-%% Pid = pid()
-%% Dir = string()
-%%
-%% Description: Change current working directory for the local client.
-%%--------------------------------------------------------------------------
-
--spec lcd(Pid :: pid(), Dir :: string()) ->
- 'ok' | {'error', Reason :: restriction_reason()}.
-
-lcd(Pid, Dir) ->
- call(Pid, {lcd, Dir}, string).
-
-
-%%--------------------------------------------------------------------------
-%% ls(Pid) -> Result
-%% ls(Pid, <Dir>) -> Result
-%%
-%% Pid = pid()
-%% Dir = string()
-%% Result = {ok, Listing} | {error, Reason}
-%% Listing = string()
-%% Reason = epath | elogin | econn
-%%
-%% Description: Returns a list of files in long format.
-%%--------------------------------------------------------------------------
-
--spec ls(Pid :: pid()) ->
- {'ok', Listing :: string()} |
- {'error', Reason :: restriction_reason() | common_reason()}.
-
-ls(Pid) ->
- ls(Pid, "").
-
--spec ls(Pid :: pid(), Dir :: string()) ->
- {'ok', Listing :: string()} |
- {'error', Reason :: restriction_reason() | common_reason()}.
-
-ls(Pid, Dir) ->
- case is_name_sane(Dir) of
- true ->
- call(Pid, {dir, long, Dir}, string);
- _ ->
- {error, efnamena}
- end.
-
-
-%%--------------------------------------------------------------------------
-%% nlist(Pid) -> Result
-%% nlist(Pid, Pathname) -> Result
-%%
-%% Pid = pid()
-%% Pathname = string()
-%% Result = {ok, Listing} | {error, Reason}
-%% Listing = string()
-%% Reason = epath | elogin | econn
-%%
-%% Description: Returns a list of files in short format
-%%--------------------------------------------------------------------------
-
--spec nlist(Pid :: pid()) ->
- {'ok', Listing :: string()} |
- {'error', Reason :: restriction_reason() | common_reason()}.
-
-nlist(Pid) ->
- nlist(Pid, "").
-
--spec nlist(Pid :: pid(), Pathname :: string()) ->
- {'ok', Listing :: string()} |
- {'error', Reason :: restriction_reason() | common_reason()}.
-
-nlist(Pid, Dir) ->
- case is_name_sane(Dir) of
- true ->
- call(Pid, {dir, short, Dir}, string);
- _ ->
- {error, efnamena}
- end.
-
-
-%%--------------------------------------------------------------------------
-%% rename(Pid, Old, New) -> ok | {error, epath} | {error, elogin}
-%% | {error, econn}
-%% Pid = pid()
-%% CurrFile = NewFile = string()
-%%
-%% Description: Rename a file at remote server.
-%%--------------------------------------------------------------------------
-
--spec rename(Pid :: pid(), Old :: string(), New :: string()) ->
- 'ok' | {'error', Reason :: restriction_reason() | common_reason()}.
-
-rename(Pid, Old, New) ->
- case {is_name_sane(Old), is_name_sane(New)} of
- {true, true} ->
- call(Pid, {rename, Old, New}, string);
- _ ->
- {error, efnamena}
- end.
-
-
-%%--------------------------------------------------------------------------
-%% delete(Pid, File) -> ok | {error, epath} | {error, elogin} |
-%% {error, econn}
-%% Pid = pid()
-%% File = string()
-%%
-%% Description: Remove file at remote server.
-%%--------------------------------------------------------------------------
-
--spec delete(Pid :: pid(), File :: string()) ->
- 'ok' | {'error', Reason :: restriction_reason() | common_reason()}.
-
-delete(Pid, File) ->
- case is_name_sane(File) of
- true ->
- call(Pid, {delete, File}, string);
- _ ->
- {error, efnamena}
- end.
-
-
-%%--------------------------------------------------------------------------
-%% mkdir(Pid, Dir) -> ok | {error, epath} | {error, elogin} | {error, econn}
-%% Pid = pid(),
-%% Dir = string()
-%%
-%% Description: Make directory at remote server.
-%%--------------------------------------------------------------------------
-
--spec mkdir(Pid :: pid(), Dir :: string()) ->
- 'ok' | {'error', Reason :: restriction_reason() | common_reason()}.
-
-mkdir(Pid, Dir) ->
- case is_name_sane(Dir) of
- true ->
- call(Pid, {mkdir, Dir}, atom);
- _ ->
- {error, efnamena}
- end.
-
-
-%%--------------------------------------------------------------------------
-%% rmdir(Pid, Dir) -> ok | {error, epath} | {error, elogin} | {error, econn}
-%% Pid = pid(),
-%% Dir = string()
-%%
-%% Description: Remove directory at remote server.
-%%--------------------------------------------------------------------------
-
--spec rmdir(Pid :: pid(), Dir :: string()) ->
- 'ok' | {'error', Reason :: restriction_reason() | common_reason()}.
-
-rmdir(Pid, Dir) ->
- case is_name_sane(Dir) of
- true ->
- call(Pid, {rmdir, Dir}, atom);
- _ ->
- {error, efnamena}
- end.
-
-
-%%--------------------------------------------------------------------------
-%% type(Pid, Type) -> ok | {error, etype} | {error, elogin} | {error, econn}
-%% Pid = pid()
-%% Type = ascii | binary
-%%
-%% Description: Set transfer type.
-%%--------------------------------------------------------------------------
-
--spec type(Pid :: pid(), Type :: ascii | binary) ->
- 'ok' |
- {'error', Reason :: 'etype' | restriction_reason() | common_reason()}.
-
-type(Pid, Type) ->
- call(Pid, {type, Type}, atom).
-
-
-%%--------------------------------------------------------------------------
-%% recv(Pid, RemoteFileName [, LocalFileName]) -> ok | {error, epath} |
-%% {error, elogin} | {error, econn}
-%% Pid = pid()
-%% RemoteFileName = LocalFileName = string()
-%%
-%% Description: Transfer file from remote server.
-%%--------------------------------------------------------------------------
-
--spec recv(Pid :: pid(), RemoteFileName :: string()) ->
- 'ok' | {'error', Reason :: restriction_reason() |
- common_reason() |
- file_write_error_reason()}.
-
-recv(Pid, RemotFileName) ->
- recv(Pid, RemotFileName, RemotFileName).
-
--spec recv(Pid :: pid(),
- RemoteFileName :: string(),
- LocalFileName :: string()) ->
- 'ok' | {'error', Reason :: term()}.
-
-recv(Pid, RemotFileName, LocalFileName) ->
- case is_name_sane(RemotFileName) of
- true ->
- call(Pid, {recv, RemotFileName, LocalFileName}, atom);
- _ ->
- {error, efnamena}
- end.
-
-
-%%--------------------------------------------------------------------------
-%% recv_bin(Pid, RemoteFile) -> {ok, Bin} | {error, epath} | {error, elogin}
-%% | {error, econn}
-%% Pid = pid()
-%% RemoteFile = string()
-%% Bin = binary()
-%%
-%% Description: Transfer file from remote server into binary.
-%%--------------------------------------------------------------------------
-
--spec recv_bin(Pid :: pid(),
- RemoteFile :: string()) ->
- {'ok', Bin :: binary()} |
- {'error', Reason :: restriction_reason() | common_reason()}.
-
-recv_bin(Pid, RemoteFile) ->
- case is_name_sane(RemoteFile) of
- true ->
- call(Pid, {recv_bin, RemoteFile}, bin);
- _ ->
- {error, efnamena}
- end.
-
-
-%%--------------------------------------------------------------------------
-%% recv_chunk_start(Pid, RemoteFile) -> ok | {error, elogin} | {error, epath}
-%% | {error, econn}
-%% Pid = pid()
-%% RemoteFile = string()
-%%
-%% Description: Start receive of chunks of remote file.
-%%--------------------------------------------------------------------------
-
--spec recv_chunk_start(Pid :: pid(),
- RemoteFile :: string()) ->
- 'ok' | {'error', Reason :: restriction_reason() | common_reason()}.
-
-recv_chunk_start(Pid, RemoteFile) ->
- case is_name_sane(RemoteFile) of
- true ->
- call(Pid, {recv_chunk_start, RemoteFile}, atom);
- _ ->
- {error, efnamena}
- end.
-
-
-%%--------------------------------------------------------------------------
-%% recv_chunk(Pid, RemoteFile) -> ok | {ok, Bin} | {error, Reason}
-%% Pid = pid()
-%% RemoteFile = string()
-%%
-%% Description: Transfer file from remote server into binary in chunks
-%%--------------------------------------------------------------------------
-
--spec recv_chunk(Pid :: pid()) ->
- 'ok' |
- {'ok', Bin :: binary()} |
- {'error', Reason :: restriction_reason() | common_reason()}.
-
-recv_chunk(Pid) ->
- call(Pid, recv_chunk, atom).
-
-
-%%--------------------------------------------------------------------------
-%% send(Pid, LocalFileName [, RemotFileName]) -> ok | {error, epath}
-%% | {error, elogin}
-%% | {error, econn}
-%% Pid = pid()
-%% LocalFileName = RemotFileName = string()
-%%
-%% Description: Transfer file to remote server.
-%%--------------------------------------------------------------------------
-
--spec send(Pid :: pid(), LocalFileName :: string()) ->
- 'ok' |
- {'error', Reason :: restriction_reason() |
- common_reason() |
- shortage_reason()}.
-
-send(Pid, LocalFileName) ->
- send(Pid, LocalFileName, LocalFileName).
-
--spec send(Pid :: pid(),
- LocalFileName :: string(),
- RemoteFileName :: string()) ->
- 'ok' |
- {'error', Reason :: restriction_reason() |
- common_reason() |
- shortage_reason()}.
-
-send(Pid, LocalFileName, RemotFileName) ->
- case is_name_sane(RemotFileName) of
- true ->
- call(Pid, {send, LocalFileName, RemotFileName}, atom);
- _ ->
- {error, efnamena}
- end.
-
-
-%%--------------------------------------------------------------------------
-%% send_bin(Pid, Bin, RemoteFile) -> ok | {error, epath} | {error, elogin}
-%% | {error, enotbinary} | {error, econn}
-%% Pid = pid()
-%% Bin = binary()
-%% RemoteFile = string()
-%%
-%% Description: Transfer a binary to a remote file.
-%%--------------------------------------------------------------------------
-
--spec send_bin(Pid :: pid(), Bin :: binary(), RemoteFile :: string()) ->
- 'ok' |
- {'error', Reason :: restriction_reason() |
- common_reason() |
- shortage_reason()}.
-
-send_bin(Pid, Bin, RemoteFile) when is_binary(Bin) ->
- case is_name_sane(RemoteFile) of
- true ->
- call(Pid, {send_bin, Bin, RemoteFile}, atom);
- _ ->
- {error, efnamena}
- end;
-send_bin(_Pid, _Bin, _RemoteFile) ->
- {error, enotbinary}.
-
-
-%%--------------------------------------------------------------------------
-%% send_chunk_start(Pid, RemoteFile) -> ok | {error, elogin} | {error, epath}
-%% | {error, econn}
-%% Pid = pid()
-%% RemoteFile = string()
-%%
-%% Description: Start transfer of chunks to remote file.
-%%--------------------------------------------------------------------------
-
--spec send_chunk_start(Pid :: pid(), RemoteFile :: string()) ->
- 'ok' | {'error', Reason :: restriction_reason() | common_reason()}.
-
-send_chunk_start(Pid, RemoteFile) ->
- case is_name_sane(RemoteFile) of
- true ->
- call(Pid, {send_chunk_start, RemoteFile}, atom);
- _ ->
- {error, efnamena}
- end.
-
-
-%%--------------------------------------------------------------------------
-%% append_chunk_start(Pid, RemoteFile) -> ok | {error, elogin} |
-%% {error, epath} | {error, econn}
-%% Pid = pid()
-%% RemoteFile = string()
-%%
-%% Description: Start append chunks of data to remote file.
-%%--------------------------------------------------------------------------
-
--spec append_chunk_start(Pid :: pid(), RemoteFile :: string()) ->
- 'ok' | {'error', Reason :: term()}.
-
-append_chunk_start(Pid, RemoteFile) ->
- case is_name_sane(RemoteFile) of
- true ->
- call(Pid, {append_chunk_start, RemoteFile}, atom);
- _ ->
- {error, efnamena}
- end.
-
-
-%%--------------------------------------------------------------------------
-%% send_chunk(Pid, Bin) -> ok | {error, elogin} | {error, enotbinary}
-%% | {error, echunk} | {error, econn}
-%% Pid = pid()
-%% Bin = binary().
-%%
-%% Purpose: Send chunk to remote file.
-%%--------------------------------------------------------------------------
-
--spec send_chunk(Pid :: pid(), Bin :: binary()) ->
- 'ok' |
- {'error', Reason :: 'echunk' |
- restriction_reason() |
- common_reason()}.
-
-send_chunk(Pid, Bin) when is_binary(Bin) ->
- call(Pid, {transfer_chunk, Bin}, atom);
-send_chunk(_Pid, _Bin) ->
- {error, enotbinary}.
-
-
-%%--------------------------------------------------------------------------
-%% append_chunk(Pid, Bin) -> ok | {error, elogin} | {error, enotbinary}
-%% | {error, echunk} | {error, econn}
-%% Pid = pid()
-%% Bin = binary()
-%%
-%% Description: Append chunk to remote file.
-%%--------------------------------------------------------------------------
-
--spec append_chunk(Pid :: pid(), Bin :: binary()) ->
- 'ok' |
- {'error', Reason :: 'echunk' |
- restriction_reason() |
- common_reason()}.
-
-append_chunk(Pid, Bin) when is_binary(Bin) ->
- call(Pid, {transfer_chunk, Bin}, atom);
-append_chunk(_Pid, _Bin) ->
- {error, enotbinary}.
-
-
-%%--------------------------------------------------------------------------
-%% send_chunk_end(Pid) -> ok | {error, elogin} | {error, echunk}
-%% | {error, econn}
-%% Pid = pid()
-%%
-%% Description: End sending of chunks to remote file.
-%%--------------------------------------------------------------------------
-
--spec send_chunk_end(Pid :: pid()) ->
- 'ok' |
- {'error', Reason :: restriction_reason() |
- common_reason() |
- shortage_reason()}.
-
-send_chunk_end(Pid) ->
- call(Pid, chunk_end, atom).
-
-
-%%--------------------------------------------------------------------------
-%% append_chunk_end(Pid) -> ok | {error, elogin} | {error, echunk}
-%% | {error, econn}
-%% Pid = pid()
-%%
-%% Description: End appending of chunks to remote file.
-%%--------------------------------------------------------------------------
-
--spec append_chunk_end(Pid :: pid()) ->
- 'ok' |
- {'error', Reason :: restriction_reason() |
- common_reason() |
- shortage_reason()}.
-
-append_chunk_end(Pid) ->
- call(Pid, chunk_end, atom).
-
-
-%%--------------------------------------------------------------------------
-%% append(Pid, LocalFileName [, RemotFileName]) -> ok | {error, epath}
-%% | {error, elogin}
-%% | {error, econn}
-%% Pid = pid()
-%% LocalFileName = RemotFileName = string()
-%%
-%% Description: Append the local file to the remote file
-%%--------------------------------------------------------------------------
-
--spec append(Pid :: pid(), LocalFileName :: string()) ->
- 'ok' |
- {'error', Reason :: 'epath' |
- 'elogin' |
- 'etnospc' |
- 'epnospc' |
- 'efnamena' | common_reason()}.
-
-append(Pid, LocalFileName) ->
- append(Pid, LocalFileName, LocalFileName).
-
--spec append(Pid :: pid(),
- LocalFileName :: string(),
- RemoteFileName :: string()) ->
- 'ok' | {'error', Reason :: term()}.
-
-append(Pid, LocalFileName, RemotFileName) ->
- case is_name_sane(RemotFileName) of
- true ->
- call(Pid, {append, LocalFileName, RemotFileName}, atom);
- _ ->
- {error, efnamena}
- end.
-
-
-%%--------------------------------------------------------------------------
-%% append_bin(Pid, Bin, RemoteFile) -> ok | {error, epath} | {error, elogin}
-%% | {error, enotbinary} | {error, econn}
-%% Pid = pid()
-%% Bin = binary()
-%% RemoteFile = string()
-%%
-%% Purpose: Append a binary to a remote file.
-%%--------------------------------------------------------------------------
-
--spec append_bin(Pid :: pid(),
- Bin :: binary(),
- RemoteFile :: string()) ->
- 'ok' |
- {'error', Reason :: restriction_reason() |
- common_reason() |
- shortage_reason()}.
-
-append_bin(Pid, Bin, RemoteFile) when is_binary(Bin) ->
- case is_name_sane(RemoteFile) of
- true ->
- call(Pid, {append_bin, Bin, RemoteFile}, atom);
- _ ->
- {error, efnamena}
- end;
-append_bin(_Pid, _Bin, _RemoteFile) ->
- {error, enotbinary}.
-
-
-%%--------------------------------------------------------------------------
-%% quote(Pid, Cmd) -> list()
-%% Pid = pid()
-%% Cmd = string()
-%%
-%% Description: Send arbitrary ftp command.
-%%--------------------------------------------------------------------------
-
--spec quote(Pid :: pid(), Cmd :: string()) -> list().
-
-quote(Pid, Cmd) when is_list(Cmd) ->
- call(Pid, {quote, Cmd}, atom).
-
-
-%%--------------------------------------------------------------------------
-%% close(Pid) -> ok
-%% Pid = pid()
-%%
-%% Description: End the ftp session.
-%%--------------------------------------------------------------------------
-
--spec close(Pid :: pid()) -> 'ok'.
-
-close(Pid) ->
- cast(Pid, close),
- ok.
-
-
-%%--------------------------------------------------------------------------
-%% formaterror(Tag) -> string()
-%% Tag = atom() | {error, atom()}
-%%
-%% Description: Return diagnostics.
-%%--------------------------------------------------------------------------
-
--spec formaterror(Tag :: term()) -> string().
-
-formaterror(Tag) ->
- ftp_response:error_string(Tag).
-
-
-info(Pid) ->
- call(Pid, info, list).
-
-
-%%--------------------------------------------------------------------------
-%% latest_ctrl_response(Pid) -> string()
-%% Pid = pid()
-%%
-%% Description: The latest received response from the server
-%%--------------------------------------------------------------------------
-
--spec latest_ctrl_response(Pid :: pid()) -> string().
-
-latest_ctrl_response(Pid) ->
- call(Pid, latest_ctrl_response, string).
-
-%%%========================================================================
-%%% Behavior callbacks
-%%%========================================================================
-start_standalone(Options) ->
- try
- {ok, StartOptions} = start_options(Options),
- {ok, OpenOptions} = open_options(Options),
- case start_link(StartOptions, []) of
- {ok, Pid} ->
- call(Pid, {open, ip_comm, OpenOptions}, plain);
- Error1 ->
- Error1
- end
- catch
- throw:Error2 ->
- Error2
- end.
-
-start_service(Options) ->
- try
- {ok, StartOptions} = start_options(Options),
- {ok, OpenOptions} = open_options(Options),
- case ftp_sup:start_child([[[{client, self()} | StartOptions], []]]) of
- {ok, Pid} ->
- call(Pid, {open, ip_comm, OpenOptions}, plain);
- Error1 ->
- Error1
- end
- catch
- throw:Error2 ->
- Error2
- end.
-
-stop_service(Pid) ->
- close(Pid).
-
-services() ->
- [{ftpc, Pid} || {_, Pid, _, _} <-
- supervisor:which_children(ftp_sup)].
-service_info(Pid) ->
- {ok, Info} = call(Pid, info, list),
- {ok, [proplists:lookup(mode, Info),
- proplists:lookup(local_port, Info),
- proplists:lookup(peer, Info),
- proplists:lookup(peer_port, Info)]}.
-
-
-%% This function extracts the start options from the
-%% Valid options:
-%% debug,
-%% verbose
-%% ipfamily
-%% priority
-%% flags (for backward compatibillity)
-start_options(Options) ->
- ?fcrt("start_options", [{options, Options}]),
- case lists:keysearch(flags, 1, Options) of
- {value, {flags, Flags}} ->
- Verbose = lists:member(verbose, Flags),
- IsTrace = lists:member(trace, Flags),
- IsDebug = lists:member(debug, Flags),
- DebugLevel =
- if
- (IsTrace =:= true) ->
- trace;
- IsDebug =:= true ->
- debug;
- true ->
- disable
- end,
- {ok, [{verbose, Verbose},
- {debug, DebugLevel},
- {priority, low}]};
- false ->
- ValidateVerbose =
- fun(true) -> true;
- (false) -> true;
- (_) -> false
- end,
- ValidateDebug =
- fun(trace) -> true;
- (debug) -> true;
- (disable) -> true;
- (_) -> false
- end,
- ValidatePriority =
- fun(low) -> true;
- (normal) -> true;
- (high) -> true;
- (_) -> false
- end,
- ValidOptions =
- [{verbose, ValidateVerbose, false, false},
- {debug, ValidateDebug, false, disable},
- {priority, ValidatePriority, false, low}],
- validate_options(Options, ValidOptions, [])
- end.
-
-
-%% This function extracts and validates the open options from the
-%% Valid options:
-%% mode
-%% host
-%% port
-%% timeout
-%% dtimeout
-%% progress
-%% ftp_extension
-
-open_options(Options) ->
- ?fcrt("open_options", [{options, Options}]),
- ValidateMode =
- fun(active) -> true;
- (passive) -> true;
- (_) -> false
- end,
- ValidateHost =
- fun(Host) when is_list(Host) ->
- true;
- (Host) when is_tuple(Host) andalso
- ((size(Host) =:= 4) orelse (size(Host) =:= 8)) ->
- true;
- (_) ->
- false
- end,
- ValidatePort =
- fun(Port) when is_integer(Port) andalso (Port > 0) -> true;
- (_) -> false
- end,
- ValidateIpFamily =
- fun(inet) -> true;
- (inet6) -> true;
- (inet6fb4) -> true;
- (_) -> false
- end,
- ValidateTimeout =
- fun(Timeout) when is_integer(Timeout) andalso (Timeout >= 0) -> true;
- (_) -> false
- end,
- ValidateDTimeout =
- fun(DTimeout) when is_integer(DTimeout) andalso (DTimeout >= 0) -> true;
- (infinity) -> true;
- (_) -> false
- end,
- ValidateProgress =
- fun(ignore) ->
- true;
- ({Mod, Func, _InitProgress}) when is_atom(Mod) andalso
- is_atom(Func) ->
- true;
- (_) ->
- false
- end,
- ValidateFtpExtension =
- fun(true) -> true;
- (false) -> true;
- (_) -> false
- end,
- ValidOptions =
- [{mode, ValidateMode, false, ?DEFAULT_MODE},
- {host, ValidateHost, true, ehost},
- {port, ValidatePort, false, ?FTP_PORT},
- {ipfamily, ValidateIpFamily, false, inet},
- {timeout, ValidateTimeout, false, ?CONNECTION_TIMEOUT},
- {dtimeout, ValidateDTimeout, false, ?DATA_ACCEPT_TIMEOUT},
- {progress, ValidateProgress, false, ?PROGRESS_DEFAULT},
- {ftp_extension, ValidateFtpExtension, false, ?FTP_EXT_DEFAULT}],
- validate_options(Options, ValidOptions, []).
-
-tls_options(Options) ->
- %% Options will be validated by ssl application
- proplists:get_value(tls, Options, undefined).
-
-validate_options([], [], Acc) ->
- ?fcrt("validate_options -> done", [{acc, Acc}]),
- {ok, lists:reverse(Acc)};
-validate_options([], ValidOptions, Acc) ->
- ?fcrt("validate_options -> done",
- [{valid_options, ValidOptions}, {acc, Acc}]),
- %% Check if any mandatory options are missing!
- case [{Key, Reason} || {Key, _, true, Reason} <- ValidOptions] of
- [] ->
- Defaults =
- [{Key, Default} || {Key, _, _, Default} <- ValidOptions],
- {ok, lists:reverse(Defaults ++ Acc)};
- [{_, Reason}|_Missing] ->
- throw({error, Reason})
- end;
-validate_options([{Key, Value}|Options], ValidOptions, Acc) ->
- ?fcrt("validate_options -> check",
- [{key, Key}, {value, Value}, {acc, Acc}]),
- case lists:keysearch(Key, 1, ValidOptions) of
- {value, {Key, Validate, _, Default}} ->
- case (catch Validate(Value)) of
- true ->
- ?fcrt("validate_options -> check - accept", []),
- NewValidOptions = lists:keydelete(Key, 1, ValidOptions),
- validate_options(Options, NewValidOptions,
- [{Key, Value} | Acc]);
- _ ->
- ?fcrt("validate_options -> check - reject",
- [{default, Default}]),
- NewValidOptions = lists:keydelete(Key, 1, ValidOptions),
- validate_options(Options, NewValidOptions,
- [{Key, Default} | Acc])
- end;
- false ->
- validate_options(Options, ValidOptions, Acc)
- end;
-validate_options([_|Options], ValidOptions, Acc) ->
- validate_options(Options, ValidOptions, Acc).
-
-
-
-%%%========================================================================
-%%% gen_server callback functions
-%%%========================================================================
-
-%%-------------------------------------------------------------------------
-%% init(Args) -> {ok, State} | {ok, State, Timeout} | {stop, Reason}
-%% Description: Initiates the erlang process that manages a ftp connection.
-%%-------------------------------------------------------------------------
-init(Options) ->
- process_flag(trap_exit, true),
-
- %% Keep track of the client
- {value, {client, Client}} = lists:keysearch(client, 1, Options),
- erlang:monitor(process, Client),
-
- %% Make sure inet is started
- _ = inet_db:start(),
-
- %% Where are we
- {ok, Dir} = file:get_cwd(),
-
- %% Maybe activate dbg
- case key_search(debug, Options, disable) of
- trace ->
- dbg:tracer(),
- dbg:p(all, [call]),
- {ok, _} = dbg:tpl(ftp, [{'_', [], [{return_trace}]}]),
- {ok, _} = dbg:tpl(ftp_response, [{'_', [], [{return_trace}]}]),
- {ok, _} = dbg:tpl(ftp_progress, [{'_', [], [{return_trace}]}]),
- ok;
- debug ->
- dbg:tracer(),
- dbg:p(all, [call]),
- {ok, _} = dbg:tp(ftp, [{'_', [], [{return_trace}]}]),
- {ok, _} = dbg:tp(ftp_response, [{'_', [], [{return_trace}]}]),
- {ok, _} = dbg:tp(ftp_progress, [{'_', [], [{return_trace}]}]),
- ok;
- _ ->
- %% Keep silent
- ok
- end,
-
- %% Verbose?
- Verbose = key_search(verbose, Options, false),
-
- %% IpFamily?
- IpFamily = key_search(ipfamily, Options, inet),
-
- State = #state{owner = Client,
- verbose = Verbose,
- ipfamily = IpFamily,
- ldir = Dir},
-
- %% Set process prio
- Priority = key_search(priority, Options, low),
- process_flag(priority, Priority),
-
- %% And we are done
- {ok, State}.
-
-
-%%--------------------------------------------------------------------------
-%% handle_call(Request, From, State) -> {reply, Reply, State} |
-%% {reply, Reply, State, Timeout} |
-%% {noreply, State} |
-%% {noreply, State, Timeout} |
-%% {stop, Reason, Reply, State} |
-%% Description: Handle incoming requests.
-%%-------------------------------------------------------------------------
-
-%% Anyone can ask this question
-handle_call({_, info}, _, #state{verbose = Verbose,
- mode = Mode,
- timeout = Timeout,
- ipfamily = IpFamily,
- csock = Socket,
- progress = Progress} = State) ->
- {ok, {_, LocalPort}} = sockname(Socket),
- {ok, {Address, Port}} = peername(Socket),
- Options = [{verbose, Verbose},
- {ipfamily, IpFamily},
- {mode, Mode},
- {peer, Address},
- {peer_port, Port},
- {local_port, LocalPort},
- {timeout, Timeout},
- {progress, Progress}],
- {reply, {ok, Options}, State};
-
-handle_call({_,latest_ctrl_response}, _, #state{latest_ctrl_response=Resp} = State) ->
- {reply, {ok,Resp}, State};
-
-%% But everything else must come from the owner
-handle_call({Pid, _}, _, #state{owner = Owner} = State) when Owner =/= Pid ->
- {reply, {error, not_connection_owner}, State};
-
-handle_call({_, {open, ip_comm, Opts}}, From, State) ->
- ?fcrd("handle_call(open)", [{opts, Opts}]),
- case key_search(host, Opts, undefined) of
- undefined ->
- {stop, normal, {error, ehost}, State};
- Host ->
- Mode = key_search(mode, Opts, ?DEFAULT_MODE),
- Port = key_search(port, Opts, ?FTP_PORT),
- Timeout = key_search(timeout, Opts, ?CONNECTION_TIMEOUT),
- DTimeout = key_search(dtimeout, Opts, ?DATA_ACCEPT_TIMEOUT),
- Progress = key_search(progress, Opts, ignore),
- IpFamily = key_search(ipfamily, Opts, inet),
- FtpExt = key_search(ftp_extension, Opts, ?FTP_EXT_DEFAULT),
-
- State2 = State#state{client = From,
- mode = Mode,
- progress = progress(Progress),
- ipfamily = IpFamily,
- dtimeout = DTimeout,
- ftp_extension = FtpExt},
-
- ?fcrd("handle_call(open) -> setup ctrl connection with",
- [{host, Host}, {port, Port}, {timeout, Timeout}]),
- case setup_ctrl_connection(Host, Port, Timeout, State2) of
- {ok, State3, WaitTimeout} ->
- ?fcrd("handle_call(open) -> ctrl connection setup done",
- [{waittimeout, WaitTimeout}]),
- {noreply, State3, WaitTimeout};
- {error, Reason} ->
- ?fcrd("handle_call(open) -> ctrl connection setup failed",
- [{reason, Reason}]),
- gen_server:reply(From, {error, ehost}),
- {stop, normal, State2#state{client = undefined}}
- end
- end;
-
-handle_call({_, {open, ip_comm, Host, Opts}}, From, State) ->
- Mode = key_search(mode, Opts, ?DEFAULT_MODE),
- Port = key_search(port, Opts, ?FTP_PORT),
- Timeout = key_search(timeout, Opts, ?CONNECTION_TIMEOUT),
- DTimeout = key_search(dtimeout, Opts, ?DATA_ACCEPT_TIMEOUT),
- Progress = key_search(progress, Opts, ignore),
- FtpExt = key_search(ftp_extension, Opts, ?FTP_EXT_DEFAULT),
-
- State2 = State#state{client = From,
- mode = Mode,
- progress = progress(Progress),
- dtimeout = DTimeout,
- ftp_extension = FtpExt},
-
- case setup_ctrl_connection(Host, Port, Timeout, State2) of
- {ok, State3, WaitTimeout} ->
- {noreply, State3, WaitTimeout};
- {error, _Reason} ->
- gen_server:reply(From, {error, ehost}),
- {stop, normal, State2#state{client = undefined}}
- end;
-
-handle_call({_, {open, tls_upgrade, TLSOptions}}, From, State) ->
- send_ctrl_message(State, mk_cmd("AUTH TLS", [])),
- activate_ctrl_connection(State),
- {noreply, State#state{client = From, caller = open, tls_options = TLSOptions}};
-
-handle_call({_, {user, User, Password}}, From,
- #state{csock = CSock} = State) when (CSock =/= undefined) ->
- handle_user(User, Password, "", State#state{client = From});
-
-handle_call({_, {user, User, Password, Acc}}, From,
- #state{csock = CSock} = State) when (CSock =/= undefined) ->
- handle_user(User, Password, Acc, State#state{client = From});
-
-handle_call({_, {account, Acc}}, From, State)->
- handle_user_account(Acc, State#state{client = From});
-
-handle_call({_, pwd}, From, #state{chunk = false} = State) ->
- send_ctrl_message(State, mk_cmd("PWD", [])),
- activate_ctrl_connection(State),
- {noreply, State#state{client = From, caller = pwd}};
-
-handle_call({_, lpwd}, From, #state{ldir = LDir} = State) ->
- {reply, {ok, LDir}, State#state{client = From}};
-
-handle_call({_, {cd, Dir}}, From, #state{chunk = false} = State) ->
- send_ctrl_message(State, mk_cmd("CWD ~s", [Dir])),
- activate_ctrl_connection(State),
- {noreply, State#state{client = From, caller = cd}};
-
-handle_call({_,{lcd, Dir}}, _From, #state{ldir = LDir0} = State) ->
- LDir = filename:absname(Dir, LDir0),
- case file:read_file_info(LDir) of %% FIX better check that LDir is a dir.
- {ok, _ } ->
- {reply, ok, State#state{ldir = LDir}};
- _ ->
- {reply, {error, epath}, State}
- end;
-
-handle_call({_, {dir, Len, Dir}}, {_Pid, _} = From,
- #state{chunk = false} = State) ->
- setup_data_connection(State#state{caller = {dir, Dir, Len},
- client = From});
-handle_call({_, {rename, CurrFile, NewFile}}, From,
- #state{chunk = false} = State) ->
- send_ctrl_message(State, mk_cmd("RNFR ~s", [CurrFile])),
- activate_ctrl_connection(State),
- {noreply, State#state{caller = {rename, NewFile}, client = From}};
-
-handle_call({_, {delete, File}}, {_Pid, _} = From,
- #state{chunk = false} = State) ->
- send_ctrl_message(State, mk_cmd("DELE ~s", [File])),
- activate_ctrl_connection(State),
- {noreply, State#state{client = From}};
-
-handle_call({_, {mkdir, Dir}}, From, #state{chunk = false} = State) ->
- send_ctrl_message(State, mk_cmd("MKD ~s", [Dir])),
- activate_ctrl_connection(State),
- {noreply, State#state{client = From}};
-
-handle_call({_,{rmdir, Dir}}, From, #state{chunk = false} = State) ->
- send_ctrl_message(State, mk_cmd("RMD ~s", [Dir])),
- activate_ctrl_connection(State),
- {noreply, State#state{client = From}};
-
-handle_call({_,{type, Type}}, From, #state{chunk = false} = State) ->
- case Type of
- ascii ->
- send_ctrl_message(State, mk_cmd("TYPE A", [])),
- activate_ctrl_connection(State),
- {noreply, State#state{caller = type, type = ascii,
- client = From}};
- binary ->
- send_ctrl_message(State, mk_cmd("TYPE I", [])),
- activate_ctrl_connection(State),
- {noreply, State#state{caller = type, type = binary,
- client = From}};
- _ ->
- {reply, {error, etype}, State}
- end;
-
-handle_call({_,{recv, RemoteFile, LocalFile}}, From,
- #state{chunk = false, ldir = LocalDir} = State) ->
- progress_report({remote_file, RemoteFile}, State),
- NewLocalFile = filename:absname(LocalFile, LocalDir),
-
- case file_open(NewLocalFile, write) of
- {ok, Fd} ->
- setup_data_connection(State#state{client = From,
- caller =
- {recv_file,
- RemoteFile, Fd}});
- {error, _What} ->
- {reply, {error, epath}, State}
- end;
-
-handle_call({_, {recv_bin, RemoteFile}}, From, #state{chunk = false} =
- State) ->
- setup_data_connection(State#state{caller = {recv_bin, RemoteFile},
- client = From});
-
-handle_call({_,{recv_chunk_start, RemoteFile}}, From, #state{chunk = false}
- = State) ->
- setup_data_connection(State#state{caller = {start_chunk_transfer,
- "RETR", RemoteFile},
- client = From});
-
-handle_call({_, recv_chunk}, _, #state{chunk = false} = State) ->
- {reply, {error, "ftp:recv_chunk_start/2 not called"}, State};
-
-handle_call({_, recv_chunk}, _From, #state{chunk = true,
- caller = #recv_chunk_closing{dconn_closed = true,
- pos_compl_received = true
- }
- } = State0) ->
- %% The ftp:recv_chunk call was the last event we waited for, finnish and clean up
- ?DBG("recv_chunk_closing ftp:recv_chunk, last event",[]),
- activate_ctrl_connection(State0),
- {reply, ok, State0#state{caller = undefined,
- chunk = false,
- client = undefined}};
-
-handle_call({_, recv_chunk}, From, #state{chunk = true,
- caller = #recv_chunk_closing{} = R
- } = State) ->
- %% Waiting for more, don't care what
- ?DBG("recv_chunk_closing ftp:recv_chunk, get more",[]),
- {noreply, State#state{client = From, caller = R#recv_chunk_closing{client_called_us=true}}};
-
-handle_call({_, recv_chunk}, From, #state{chunk = true} = State0) ->
- State = activate_data_connection(State0),
- {noreply, State#state{client = From, caller = recv_chunk}};
-
-handle_call({_, {send, LocalFile, RemoteFile}}, From,
- #state{chunk = false, ldir = LocalDir} = State) ->
- progress_report({local_file, filename:absname(LocalFile, LocalDir)},
- State),
- setup_data_connection(State#state{caller = {transfer_file,
- {"STOR",
- LocalFile, RemoteFile}},
- client = From});
-handle_call({_, {append, LocalFile, RemoteFile}}, From,
- #state{chunk = false} = State) ->
- setup_data_connection(State#state{caller = {transfer_file,
- {"APPE",
- LocalFile, RemoteFile}},
- client = From});
-handle_call({_, {send_bin, Bin, RemoteFile}}, From,
- #state{chunk = false} = State) ->
- setup_data_connection(State#state{caller = {transfer_data,
- {"STOR", Bin, RemoteFile}},
- client = From});
-handle_call({_,{append_bin, Bin, RemoteFile}}, From,
- #state{chunk = false} = State) ->
- setup_data_connection(State#state{caller = {transfer_data,
- {"APPE", Bin, RemoteFile}},
- client = From});
-handle_call({_, {send_chunk_start, RemoteFile}}, From, #state{chunk = false}
- = State) ->
- setup_data_connection(State#state{caller = {start_chunk_transfer,
- "STOR", RemoteFile},
- client = From});
-handle_call({_, {append_chunk_start, RemoteFile}}, From, #state{chunk = false}
- = State) ->
- setup_data_connection(State#state{caller = {start_chunk_transfer,
- "APPE", RemoteFile},
- client = From});
-handle_call({_, {transfer_chunk, Bin}}, _, #state{chunk = true} = State) ->
- send_data_message(State, Bin),
- {reply, ok, State};
-
-handle_call({_, {transfer_chunk, _}}, _, #state{chunk = false} = State) ->
- {reply, {error, echunk}, State};
-
-handle_call({_, chunk_end}, From, #state{chunk = true} = State) ->
- close_data_connection(State),
- activate_ctrl_connection(State),
- {noreply, State#state{client = From, dsock = undefined,
- caller = end_chunk_transfer, chunk = false}};
-
-handle_call({_, chunk_end}, _, #state{chunk = false} = State) ->
- {reply, {error, echunk}, State};
-
-handle_call({_, {quote, Cmd}}, From, #state{chunk = false} = State) ->
- send_ctrl_message(State, mk_cmd(Cmd, [])),
- activate_ctrl_connection(State),
- {noreply, State#state{client = From, caller = quote}};
-
-handle_call({_, _Req}, _From, #state{csock = CSock} = State)
- when (CSock =:= undefined) ->
- {reply, {error, not_connected}, State};
-
-handle_call(_, _, #state{chunk = true} = State) ->
- {reply, {error, echunk}, State};
-
-%% Catch all - This can only happen if the application programmer writes
-%% really bad code that violates the API.
-handle_call(Request, _Timeout, State) ->
- {stop, {'API_violation_connection_closed', Request},
- {error, {connection_terminated, 'API_violation'}}, State}.
-
-%%--------------------------------------------------------------------------
-%% handle_cast(Request, State) -> {noreply, State} |
-%% {noreply, State, Timeout} |
-%% {stop, Reason, State}
-%% Description: Handles cast messages.
-%%-------------------------------------------------------------------------
-handle_cast({Pid, close}, #state{owner = Pid} = State) ->
- send_ctrl_message(State, mk_cmd("QUIT", [])),
- close_ctrl_connection(State),
- close_data_connection(State),
- {stop, normal, State#state{csock = undefined, dsock = undefined}};
-
-handle_cast({Pid, close}, State) ->
- Report = io_lib:format("A none owner process ~p tried to close an "
- "ftp connection: ~n", [Pid]),
- error_logger:info_report(Report),
- {noreply, State};
-
-%% Catch all - This can oly happen if the application programmer writes
-%% really bad code that violates the API.
-handle_cast(Msg, State) ->
- {stop, {'API_violation_connection_closed', Msg}, State}.
-
-%%--------------------------------------------------------------------------
-%% handle_info(Msg, State) -> {noreply, State} | {noreply, State, Timeout} |
-%% {stop, Reason, State}
-%% Description: Handles tcp messages from the ftp-server.
-%% Note: The order of the function clauses is significant.
-%%--------------------------------------------------------------------------
-
-handle_info(timeout, #state{caller = open} = State) ->
- {stop, timeout, State};
-
-handle_info(timeout, State) ->
- {noreply, State};
-
-%%% Data socket messages %%%
-handle_info({Trpt, Socket, Data},
- #state{dsock = {Trpt,Socket},
- caller = {recv_file, Fd}} = State0) when Trpt==tcp;Trpt==ssl ->
- ?DBG('L~p --data ~p ----> ~s~p~n',[?LINE,Socket,Data,State0]),
- ok = file_write(binary_to_list(Data), Fd),
- progress_report({binary, Data}, State0),
- State = activate_data_connection(State0),
- {noreply, State};
-
-handle_info({Trpt, Socket, Data}, #state{dsock = {Trpt,Socket}, client = From,
- caller = recv_chunk}
- = State) when Trpt==tcp;Trpt==ssl ->
- ?DBG('L~p --data ~p ----> ~s~p~n',[?LINE,Socket,Data,State]),
- gen_server:reply(From, {ok, Data}),
- {noreply, State#state{client = undefined, data = <<>>}};
-
-handle_info({Trpt, Socket, Data}, #state{dsock = {Trpt,Socket}} = State0) when Trpt==tcp;Trpt==ssl ->
- ?DBG('L~p --data ~p ----> ~s~p~n',[?LINE,Socket,Data,State0]),
- State = activate_data_connection(State0),
- {noreply, State#state{data = <<(State#state.data)/binary,
- Data/binary>>}};
-
-handle_info({Cls, Socket}, #state{dsock = {Trpt,Socket},
- caller = {recv_file, Fd}} = State)
- when {Cls,Trpt}=={tcp_closed,tcp} ; {Cls,Trpt}=={ssl_closed,ssl} ->
- file_close(Fd),
- progress_report({transfer_size, 0}, State),
- activate_ctrl_connection(State),
- ?DBG("Data channel close",[]),
- {noreply, State#state{dsock = undefined, data = <<>>}};
-
-handle_info({Cls, Socket}, #state{dsock = {Trpt,Socket},
- client = Client,
- caller = recv_chunk} = State)
- when {Cls,Trpt}=={tcp_closed,tcp} ; {Cls,Trpt}=={ssl_closed,ssl} ->
- ?DBG("Data channel close recv_chunk",[]),
- activate_ctrl_connection(State),
- {noreply, State#state{dsock = undefined,
- caller = #recv_chunk_closing{dconn_closed = true,
- client_called_us = Client =/= undefined}
- }};
-
-handle_info({Cls, Socket}, #state{dsock = {Trpt,Socket}, caller = recv_bin,
- data = Data} = State)
- when {Cls,Trpt}=={tcp_closed,tcp} ; {Cls,Trpt}=={ssl_closed,ssl} ->
- ?DBG("Data channel close",[]),
- activate_ctrl_connection(State),
- {noreply, State#state{dsock = undefined, data = <<>>,
- caller = {recv_bin, Data}}};
-
-handle_info({Cls, Socket}, #state{dsock = {Trpt,Socket}, data = Data,
- caller = {handle_dir_result, Dir}}
- = State) when {Cls,Trpt}=={tcp_closed,tcp} ; {Cls,Trpt}=={ssl_closed,ssl} ->
- ?DBG("Data channel close",[]),
- activate_ctrl_connection(State),
- {noreply, State#state{dsock = undefined,
- caller = {handle_dir_result, Dir, Data},
-% data = <<?CR,?LF>>}};
- data = <<>>}};
-
-handle_info({Err, Socket, Reason}, #state{dsock = {Trpt,Socket},
- client = From} = State)
- when {Err,Trpt}=={tcp_error,tcp} ; {Err,Trpt}=={ssl_error,ssl} ->
- gen_server:reply(From, {error, Reason}),
- close_data_connection(State),
- {noreply, State#state{dsock = undefined, client = undefined,
- data = <<>>, caller = undefined, chunk = false}};
-
-%%% Ctrl socket messages %%%
-handle_info({Transport, Socket, Data}, #state{csock = {Transport, Socket},
- verbose = Verbose,
- caller = Caller,
- client = From,
- ctrl_data = {CtrlData, AccLines,
- LineStatus}}
- = State) ->
- ?DBG('--ctrl ~p ----> ~s~p~n',[Socket,<<CtrlData/binary, Data/binary>>,State]),
- case ftp_response:parse_lines(<<CtrlData/binary, Data/binary>>,
- AccLines, LineStatus) of
- {ok, Lines, NextMsgData} ->
- verbose(Lines, Verbose, 'receive'),
- CtrlResult = ftp_response:interpret(Lines),
- case Caller of
- quote ->
- gen_server:reply(From, string:tokens(Lines, [?CR, ?LF])),
- {noreply, State#state{client = undefined,
- caller = undefined,
- latest_ctrl_response = Lines,
- ctrl_data = {NextMsgData, [],
- start}}};
- _ ->
- ?DBG(' ...handle_ctrl_result(~p,...) ctrl_data=~p~n',[CtrlResult,{NextMsgData, [], start}]),
- handle_ctrl_result(CtrlResult,
- State#state{latest_ctrl_response = Lines,
- ctrl_data =
- {NextMsgData, [], start}})
- end;
- {continue, NewCtrlData} ->
- ?DBG(' ...Continue... ctrl_data=~p~n',[NewCtrlData]),
- activate_ctrl_connection(State),
- {noreply, State#state{ctrl_data = NewCtrlData}}
- end;
-
-%% If the server closes the control channel it is
-%% the expected behavior that connection process terminates.
-handle_info({Cls, Socket}, #state{csock = {Trpt, Socket}})
- when {Cls,Trpt}=={tcp_closed,tcp} ; {Cls,Trpt}=={ssl_closed,ssl} ->
- exit(normal); %% User will get error message from terminate/2
-
-handle_info({Err, Socket, Reason}, _) when Err==tcp_error ; Err==ssl_error ->
- Report =
- io_lib:format("~p on socket: ~p for reason: ~p~n",
- [Err, Socket, Reason]),
- error_logger:error_report(Report),
- %% If tcp does not work the only option is to terminate,
- %% this is the expected behavior under these circumstances.
- exit(normal); %% User will get error message from terminate/2
-
-%% Monitor messages - if the process owning the ftp connection goes
-%% down there is no point in continuing.
-handle_info({'DOWN', _Ref, _Type, _Process, normal}, State) ->
- {stop, normal, State#state{client = undefined}};
-
-handle_info({'DOWN', _Ref, _Type, _Process, shutdown}, State) ->
- {stop, normal, State#state{client = undefined}};
-
-handle_info({'DOWN', _Ref, _Type, _Process, timeout}, State) ->
- {stop, normal, State#state{client = undefined}};
-
-handle_info({'DOWN', _Ref, _Type, Process, Reason}, State) ->
- {stop, {stopped, {'EXIT', Process, Reason}},
- State#state{client = undefined}};
-
-handle_info({'EXIT', Pid, Reason}, #state{progress = Pid} = State) ->
- Report = io_lib:format("Progress reporting stopped for reason ~p~n",
- [Reason]),
- error_logger:info_report(Report),
- {noreply, State#state{progress = ignore}};
-
-%% Catch all - throws away unknown messages (This could happen by "accident"
-%% so we do not want to crash, but we make a log entry as it is an
-%% unwanted behaviour.)
-handle_info(Info, State) ->
- Report = io_lib:format("ftp : ~p : Unexpected message: ~p~nState: ~p~n",
- [self(), Info, State]),
- error_logger:info_report(Report),
- {noreply, State}.
-
-%%--------------------------------------------------------------------------
-%% terminate/2 and code_change/3
-%%--------------------------------------------------------------------------
-terminate(normal, State) ->
- %% If terminate reason =/= normal the progress reporting process will
- %% be killed by the exit signal.
- progress_report(stop, State),
- do_terminate({error, econn}, State);
-terminate(Reason, State) ->
- Report = io_lib:format("Ftp connection closed due to: ~p~n", [Reason]),
- error_logger:error_report(Report),
- do_terminate({error, eclosed}, State).
-
-do_terminate(ErrorMsg, State) ->
- close_data_connection(State),
- close_ctrl_connection(State),
- case State#state.client of
- undefined ->
- ok;
- From ->
- gen_server:reply(From, ErrorMsg)
- end,
- ok.
-
-code_change(_Vsn, State1, upgrade_from_pre_5_12) ->
- {state, CSock, DSock, Verbose, LDir, Type, Chunk, Mode, Timeout,
- Data, CtrlData, Owner, Client, Caller, IPv6Disable, Progress} = State1,
- IpFamily =
- if
- (IPv6Disable =:= true) ->
- inet;
- true ->
- inet6fb4
- end,
- State2 = #state{csock = CSock,
- dsock = DSock,
- verbose = Verbose,
- ldir = LDir,
- type = Type,
- chunk = Chunk,
- mode = Mode,
- timeout = Timeout,
- data = Data,
- ctrl_data = CtrlData,
- owner = Owner,
- client = Client,
- caller = Caller,
- ipfamily = IpFamily,
- progress = Progress},
- {ok, State2};
-
-code_change(_Vsn, State1, downgrade_to_pre_5_12) ->
- #state{csock = CSock,
- dsock = DSock,
- verbose = Verbose,
- ldir = LDir,
- type = Type,
- chunk = Chunk,
- mode = Mode,
- timeout = Timeout,
- data = Data,
- ctrl_data = CtrlData,
- owner = Owner,
- client = Client,
- caller = Caller,
- ipfamily = IpFamily,
- progress = Progress} = State1,
- IPv6Disable =
- if
- (IpFamily =:= inet) ->
- true;
- true ->
- false
- end,
- State2 =
- {state, CSock, DSock, Verbose, LDir, Type, Chunk, Mode, Timeout,
- Data, CtrlData, Owner, Client, Caller, IPv6Disable, Progress},
- {ok, State2};
-
-code_change(_Vsn, State, _Extra) ->
- {ok, State}.
-
-
-%%%=========================================================================
-%% Start/stop
-%%%=========================================================================
-%%--------------------------------------------------------------------------
-%% start_link([Opts, GenServerOptions]) -> {ok, Pid} | {error, Reason}
-%%
-%% Description: Callback function for the ftp supervisor. It is called
-%% : when start_service/1 calls ftp_sup:start_child/1 to start an
-%% : instance of the ftp process. Also called by start_standalone/1
-%%--------------------------------------------------------------------------
-start_link([Opts, GenServerOptions]) ->
- start_link(Opts, GenServerOptions).
-
-start_link(Opts, GenServerOptions) ->
- case lists:keysearch(client, 1, Opts) of
- {value, _} ->
- %% Via the supervisor
- gen_server:start_link(?MODULE, Opts, GenServerOptions);
- false ->
- Opts2 = [{client, self()} | Opts],
- gen_server:start_link(?MODULE, Opts2, GenServerOptions)
- end.
-
-
-%%% Stop functionality is handled by close/1
-
-%%%========================================================================
-%%% Internal functions
-%%%========================================================================
-
-%%--------------------------------------------------------------------------
-%%% Help functions to handle_call and/or handle_ctrl_result
-%%--------------------------------------------------------------------------
-%% User handling
-handle_user(User, Password, Acc, State) ->
- send_ctrl_message(State, mk_cmd("USER ~s", [User])),
- activate_ctrl_connection(State),
- {noreply, State#state{caller = {handle_user, Password, Acc}}}.
-
-handle_user_passwd(Password, Acc, State) ->
- send_ctrl_message(State, mk_cmd("PASS ~s", [Password])),
- activate_ctrl_connection(State),
- {noreply, State#state{caller = {handle_user_passwd, Acc}}}.
-
-handle_user_account(Acc, State) ->
- send_ctrl_message(State, mk_cmd("ACCT ~s", [Acc])),
- activate_ctrl_connection(State),
- {noreply, State#state{caller = handle_user_account}}.
-
-
-%%--------------------------------------------------------------------------
-%% handle_ctrl_result
-%%--------------------------------------------------------------------------
-handle_ctrl_result({tls_upgrade, _}, #state{csock = {tcp, Socket},
- tls_options = TLSOptions,
- timeout = Timeout,
- caller = open, client = From}
- = State0) ->
- ?DBG('<--ctrl ssl:connect(~p, ~p)~n~p~n',[Socket,TLSOptions,State0]),
- case ssl:connect(Socket, TLSOptions, Timeout) of
- {ok, TLSSocket} ->
- State = State0#state{csock = {ssl,TLSSocket}},
- send_ctrl_message(State, mk_cmd("PBSZ 0", [])),
- activate_ctrl_connection(State),
- {noreply, State#state{tls_upgrading_data_connection = {true, pbsz}} };
- {error, _} = Error ->
- gen_server:reply(From, {Error, self()}),
- {stop, normal, State0#state{client = undefined,
- caller = undefined,
- tls_upgrading_data_connection = false}}
- end;
-
-handle_ctrl_result({pos_compl, _}, #state{tls_upgrading_data_connection = {true, pbsz}} = State) ->
- send_ctrl_message(State, mk_cmd("PROT P", [])),
- activate_ctrl_connection(State),
- {noreply, State#state{tls_upgrading_data_connection = {true, prot}}};
-
-handle_ctrl_result({pos_compl, _}, #state{tls_upgrading_data_connection = {true, prot},
- client = From} = State) ->
- gen_server:reply(From, {ok, self()}),
- {noreply, State#state{client = undefined,
- caller = undefined,
- tls_upgrading_data_connection = false}};
-
-handle_ctrl_result({pos_compl, _}, #state{caller = open, client = From}
- = State) ->
- gen_server:reply(From, {ok, self()}),
- {noreply, State#state{client = undefined,
- caller = undefined }};
-handle_ctrl_result({_, Lines}, #state{caller = open} = State) ->
- ctrl_result_response(econn, State, {error, Lines});
-
-%%--------------------------------------------------------------------------
-%% Data connection setup active mode
-handle_ctrl_result({pos_compl, _Lines},
- #state{mode = active,
- caller = {setup_data_connection,
- {LSock, Caller}}} = State) ->
- handle_caller(State#state{caller = Caller, dsock = {lsock, LSock}});
-
-handle_ctrl_result({Status, _Lines},
- #state{mode = active,
- caller = {setup_data_connection, {LSock, _}}}
- = State) ->
- close_connection({tcp,LSock}),
- ctrl_result_response(Status, State, {error, Status});
-
-%% Data connection setup passive mode
-handle_ctrl_result({pos_compl, Lines},
- #state{mode = passive,
- ipfamily = inet6,
- client = From,
- caller = {setup_data_connection, Caller},
- csock = CSock,
- timeout = Timeout}
- = State) ->
- [_, PortStr | _] = lists:reverse(string:tokens(Lines, "|")),
- {ok, {IP, _}} = peername(CSock),
- case connect(IP, list_to_integer(PortStr), Timeout, State) of
- {ok, _, Socket} ->
- handle_caller(State#state{caller = Caller, dsock = {tcp, Socket}});
- {error, _Reason} = Error ->
- gen_server:reply(From, Error),
- {noreply, State#state{client = undefined, caller = undefined}}
- end;
-
-handle_ctrl_result({pos_compl, Lines},
- #state{mode = passive,
- ipfamily = inet,
- client = From,
- caller = {setup_data_connection, Caller},
- timeout = Timeout,
- ftp_extension = false} = State) ->
-
- {_, [?LEFT_PAREN | Rest]} =
- lists:splitwith(fun(?LEFT_PAREN) -> false; (_) -> true end, Lines),
- {NewPortAddr, _} =
- lists:splitwith(fun(?RIGHT_PAREN) -> false; (_) -> true end, Rest),
- [A1, A2, A3, A4, P1, P2] =
- lists:map(fun(X) -> list_to_integer(X) end,
- string:tokens(NewPortAddr, [$,])),
- IP = {A1, A2, A3, A4},
- Port = (P1 * 256) + P2,
-
- ?DBG('<--data tcp connect to ~p:~p, Caller=~p~n',[IP,Port,Caller]),
- case connect(IP, Port, Timeout, State) of
- {ok, _, Socket} ->
- handle_caller(State#state{caller = Caller, dsock = {tcp,Socket}});
- {error, _Reason} = Error ->
- gen_server:reply(From, Error),
- {noreply,State#state{client = undefined, caller = undefined}}
- end;
-
-handle_ctrl_result({pos_compl, Lines},
- #state{mode = passive,
- ipfamily = inet,
- client = From,
- caller = {setup_data_connection, Caller},
- csock = CSock,
- timeout = Timeout,
- ftp_extension = true} = State) ->
-
- [_, PortStr | _] = lists:reverse(string:tokens(Lines, "|")),
- {ok, {IP, _}} = peername(CSock),
-
- ?DBG('<--data tcp connect to ~p:~p, Caller=~p~n',[IP,PortStr,Caller]),
- case connect(IP, list_to_integer(PortStr), Timeout, State) of
- {ok, _, Socket} ->
- handle_caller(State#state{caller = Caller, dsock = {tcp, Socket}});
- {error, _Reason} = Error ->
- gen_server:reply(From, Error),
- {noreply, State#state{client = undefined, caller = undefined}}
- end;
-
-
-%% FTP server does not support passive mode: try to fallback on active mode
-handle_ctrl_result(_,
- #state{mode = passive,
- caller = {setup_data_connection, Caller}} = State) ->
- setup_data_connection(State#state{mode = active, caller = Caller});
-
-
-%%--------------------------------------------------------------------------
-%% User handling
-handle_ctrl_result({pos_interm, _},
- #state{caller = {handle_user, PassWord, Acc}} = State) ->
- handle_user_passwd(PassWord, Acc, State);
-handle_ctrl_result({Status, _},
- #state{caller = {handle_user, _, _}} = State) ->
- ctrl_result_response(Status, State, {error, euser});
-
-%% Accounts
-handle_ctrl_result({pos_interm_acct, _},
- #state{caller = {handle_user_passwd, Acc}} = State)
- when Acc =/= "" ->
- handle_user_account(Acc, State);
-handle_ctrl_result({Status, _},
- #state{caller = {handle_user_passwd, _}} = State) ->
- ctrl_result_response(Status, State, {error, euser});
-
-%%--------------------------------------------------------------------------
-%% Print current working directory
-handle_ctrl_result({pos_compl, Lines},
- #state{caller = pwd, client = From} = State) ->
- Dir = pwd_result(Lines),
- gen_server:reply(From, {ok, Dir}),
- {noreply, State#state{client = undefined, caller = undefined}};
-
-%%--------------------------------------------------------------------------
-%% Directory listing
-handle_ctrl_result({pos_prel, _}, #state{caller = {dir, Dir}} = State0) ->
- case accept_data_connection(State0) of
- {ok, State1} ->
- State = activate_data_connection(State1),
- {noreply, State#state{caller = {handle_dir_result, Dir}}};
- {error, _Reason} = ERROR ->
- case State0#state.client of
- undefined ->
- {stop, ERROR, State0};
- From ->
- gen_server:reply(From, ERROR),
- {stop, normal, State0#state{client = undefined}}
- end
- end;
-
-handle_ctrl_result({pos_compl, _}, #state{caller = {handle_dir_result, Dir,
- Data}, client = From}
- = State) ->
- case Dir of
- "" -> % Current directory
- gen_server:reply(From, {ok, Data}),
- {noreply, State#state{client = undefined,
- caller = undefined}};
- _ ->
- %% <WTF>
- %% Dir cannot be assumed to be a dir. It is a string that
- %% could be a dir, but could also be a file or even a string
- %% containing wildcards (*).
- %%
- %% %% If there is only one line it might be a directory with one
- %% %% file but it might be an error message that the directory
- %% %% was not found. So in this case we have to endure a little
- %% %% overhead to be able to give a good return value. Alas not
- %% %% all ftp implementations behave the same and returning
- %% %% an error string is allowed by the FTP RFC.
- %% case lists:dropwhile(fun(?CR) -> false;(_) -> true end,
- %% binary_to_list(Data)) of
- %% L when (L =:= [?CR, ?LF]) orelse (L =:= []) ->
- %% send_ctrl_message(State, mk_cmd("PWD", [])),
- %% activate_ctrl_connection(State),
- %% {noreply,
- %% State#state{caller = {handle_dir_data, Dir, Data}}};
- %% _ ->
- %% gen_server:reply(From, {ok, Data}),
- %% {noreply, State#state{client = undefined,
- %% caller = undefined}}
- %% end
- %% </WTF>
- gen_server:reply(From, {ok, Data}),
- {noreply, State#state{client = undefined,
- caller = undefined}}
- end;
-
-handle_ctrl_result({pos_compl, Lines},
- #state{caller = {handle_dir_data, Dir, DirData}} =
- State) ->
- OldDir = pwd_result(Lines),
- send_ctrl_message(State, mk_cmd("CWD ~s", [Dir])),
- activate_ctrl_connection(State),
- {noreply, State#state{caller = {handle_dir_data_second_phase, OldDir,
- DirData}}};
-handle_ctrl_result({Status, _},
- #state{caller = {handle_dir_data, _, _}} = State) ->
- ctrl_result_response(Status, State, {error, epath});
-
-handle_ctrl_result(S={_Status, _},
- #state{caller = {handle_dir_result, _, _}} = State) ->
- %% OTP-5731, macosx
- ctrl_result_response(S, State, {error, epath});
-
-handle_ctrl_result({pos_compl, _},
- #state{caller = {handle_dir_data_second_phase, OldDir,
- DirData}} = State) ->
- send_ctrl_message(State, mk_cmd("CWD ~s", [OldDir])),
- activate_ctrl_connection(State),
- {noreply, State#state{caller = {handle_dir_data_third_phase, DirData}}};
-handle_ctrl_result({Status, _},
- #state{caller = {handle_dir_data_second_phase, _, _}}
- = State) ->
- ctrl_result_response(Status, State, {error, epath});
-handle_ctrl_result(_, #state{caller = {handle_dir_data_third_phase, DirData},
- client = From} = State) ->
- gen_server:reply(From, {ok, DirData}),
- {noreply, State#state{client = undefined, caller = undefined}};
-
-handle_ctrl_result({Status, _}, #state{caller = cd} = State) ->
- ctrl_result_response(Status, State, {error, Status});
-
-handle_ctrl_result(Status={epath, _}, #state{caller = {dir,_}} = State) ->
- ctrl_result_response(Status, State, {error, epath});
-
-%%--------------------------------------------------------------------------
-%% File renaming
-handle_ctrl_result({pos_interm, _}, #state{caller = {rename, NewFile}}
- = State) ->
- send_ctrl_message(State, mk_cmd("RNTO ~s", [NewFile])),
- activate_ctrl_connection(State),
- {noreply, State#state{caller = rename_second_phase}};
-
-handle_ctrl_result({Status, _},
- #state{caller = {rename, _}} = State) ->
- ctrl_result_response(Status, State, {error, Status});
-
-handle_ctrl_result({Status, _},
- #state{caller = rename_second_phase} = State) ->
- ctrl_result_response(Status, State, {error, Status});
-
-%%--------------------------------------------------------------------------
-%% File handling - recv_bin
-handle_ctrl_result({pos_prel, _}, #state{caller = recv_bin} = State0) ->
- case accept_data_connection(State0) of
- {ok, State1} ->
- State = activate_data_connection(State1),
- {noreply, State};
- {error, _Reason} = ERROR ->
- case State0#state.client of
- undefined ->
- {stop, ERROR, State0};
- From ->
- gen_server:reply(From, ERROR),
- {stop, normal, State0#state{client = undefined}}
- end
- end;
-
-handle_ctrl_result({pos_compl, _}, #state{caller = {recv_bin, Data},
- client = From} = State) ->
- gen_server:reply(From, {ok, Data}),
- close_data_connection(State),
- {noreply, State#state{client = undefined, caller = undefined}};
-
-handle_ctrl_result({Status, _}, #state{caller = recv_bin} = State) ->
- close_data_connection(State),
- ctrl_result_response(Status, State#state{dsock = undefined},
- {error, epath});
-
-handle_ctrl_result({Status, _}, #state{caller = {recv_bin, _}} = State) ->
- close_data_connection(State),
- ctrl_result_response(Status, State#state{dsock = undefined},
- {error, epath});
-%%--------------------------------------------------------------------------
-%% File handling - start_chunk_transfer
-handle_ctrl_result({pos_prel, _}, #state{client = From,
- caller = start_chunk_transfer}
- = State0) ->
- case accept_data_connection(State0) of
- {ok, State1} ->
- State = start_chunk(State1),
- {noreply, State};
- {error, _Reason} = ERROR ->
- case State0#state.client of
- undefined ->
- {stop, ERROR, State0};
- From ->
- gen_server:reply(From, ERROR),
- {stop, normal, State0#state{client = undefined}}
- end
- end;
-
-%%--------------------------------------------------------------------------
-%% File handling - chunk_transfer complete
-
-handle_ctrl_result({pos_compl, _}, #state{client = From,
- caller = #recv_chunk_closing{dconn_closed = true,
- client_called_us = true,
- pos_compl_received = false
- }}
- = State0) when From =/= undefined ->
- %% The pos_compl was the last event we waited for, finnish and clean up
- ?DBG("recv_chunk_closing pos_compl, last event",[]),
- gen_server:reply(From, ok),
- activate_ctrl_connection(State0),
- {noreply, State0#state{caller = undefined,
- chunk = false,
- client = undefined}};
-
-handle_ctrl_result({pos_compl, _}, #state{caller = #recv_chunk_closing{}=R}
- = State0) ->
- %% Waiting for more, don't care what
- ?DBG("recv_chunk_closing pos_compl, wait more",[]),
- {noreply, State0#state{caller = R#recv_chunk_closing{pos_compl_received=true}}};
-
-
-%%--------------------------------------------------------------------------
-%% File handling - recv_file
-handle_ctrl_result({pos_prel, _}, #state{caller = {recv_file, _}} = State0) ->
- case accept_data_connection(State0) of
- {ok, State1} ->
- State = activate_data_connection(State1),
- {noreply, State};
- {error, _Reason} = ERROR ->
- case State0#state.client of
- undefined ->
- {stop, ERROR, State0};
- From ->
- gen_server:reply(From, ERROR),
- {stop, normal, State0#state{client = undefined}}
- end
- end;
-
-handle_ctrl_result({Status, _}, #state{caller = {recv_file, Fd}} = State) ->
- file_close(Fd),
- close_data_connection(State),
- ctrl_result_response(Status, State#state{dsock = undefined},
- {error, epath});
-%%--------------------------------------------------------------------------
-%% File handling - transfer_*
-handle_ctrl_result({pos_prel, _}, #state{caller = {transfer_file, Fd}}
- = State0) ->
- case accept_data_connection(State0) of
- {ok, State1} ->
- send_file(State1, Fd);
- {error, _Reason} = ERROR ->
- case State0#state.client of
- undefined ->
- {stop, ERROR, State0};
- From ->
- gen_server:reply(From, ERROR),
- {stop, normal, State0#state{client = undefined}}
- end
- end;
-
-handle_ctrl_result({pos_prel, _}, #state{caller = {transfer_data, Bin}}
- = State0) ->
- case accept_data_connection(State0) of
- {ok, State} ->
- send_bin(State, Bin);
- {error, _Reason} = ERROR ->
- case State0#state.client of
- undefined ->
- {stop, ERROR, State0};
- From ->
- gen_server:reply(From, ERROR),
- {stop, normal, State0#state{client = undefined}}
- end
- end;
-
-%%--------------------------------------------------------------------------
-%% Default
-handle_ctrl_result({Status, _Lines}, #state{client = From} = State)
- when From =/= undefined ->
- ctrl_result_response(Status, State, {error, Status}).
-
-%%--------------------------------------------------------------------------
-%% Help functions to handle_ctrl_result
-%%--------------------------------------------------------------------------
-ctrl_result_response(pos_compl, #state{client = From} = State, _) ->
- gen_server:reply(From, ok),
- {noreply, State#state{client = undefined, caller = undefined}};
-
-ctrl_result_response(enofile, #state{client = From} = State, _) ->
- gen_server:reply(From, {error, enofile}),
- {noreply, State#state{client = undefined, caller = undefined}};
-
-ctrl_result_response(Status, #state{client = From} = State, _)
- when (Status =:= etnospc) orelse
- (Status =:= epnospc) orelse
- (Status =:= efnamena) orelse
- (Status =:= econn) ->
- gen_server:reply(From, {error, Status}),
-%% {stop, normal, {error, Status}, State#state{client = undefined}};
- {stop, normal, State#state{client = undefined}};
-
-ctrl_result_response(_, #state{client = From} = State, ErrorMsg) ->
- gen_server:reply(From, ErrorMsg),
- {noreply, State#state{client = undefined, caller = undefined}}.
-
-%%--------------------------------------------------------------------------
-handle_caller(#state{caller = {dir, Dir, Len}} = State) ->
- Cmd = case Len of
- short -> "NLST";
- long -> "LIST"
- end,
- case Dir of
- "" ->
- send_ctrl_message(State, mk_cmd(Cmd, ""));
- _ ->
- send_ctrl_message(State, mk_cmd(Cmd ++ " ~s", [Dir]))
- end,
- activate_ctrl_connection(State),
- {noreply, State#state{caller = {dir, Dir}}};
-
-handle_caller(#state{caller = {recv_bin, RemoteFile}} = State) ->
- send_ctrl_message(State, mk_cmd("RETR ~s", [RemoteFile])),
- activate_ctrl_connection(State),
- {noreply, State#state{caller = recv_bin}};
-
-handle_caller(#state{caller = {start_chunk_transfer, Cmd, RemoteFile}} =
- State) ->
- send_ctrl_message(State, mk_cmd("~s ~s", [Cmd, RemoteFile])),
- activate_ctrl_connection(State),
- {noreply, State#state{caller = start_chunk_transfer}};
-
-handle_caller(#state{caller = {recv_file, RemoteFile, Fd}} = State) ->
- send_ctrl_message(State, mk_cmd("RETR ~s", [RemoteFile])),
- activate_ctrl_connection(State),
- {noreply, State#state{caller = {recv_file, Fd}}};
-
-handle_caller(#state{caller = {transfer_file, {Cmd, LocalFile, RemoteFile}},
- ldir = LocalDir, client = From} = State) ->
- case file_open(filename:absname(LocalFile, LocalDir), read) of
- {ok, Fd} ->
- send_ctrl_message(State, mk_cmd("~s ~s", [Cmd, RemoteFile])),
- activate_ctrl_connection(State),
- {noreply, State#state{caller = {transfer_file, Fd}}};
- {error, _} ->
- gen_server:reply(From, {error, epath}),
- {noreply, State#state{client = undefined, caller = undefined,
- dsock = undefined}}
- end;
-
-handle_caller(#state{caller = {transfer_data, {Cmd, Bin, RemoteFile}}} =
- State) ->
- send_ctrl_message(State, mk_cmd("~s ~s", [Cmd, RemoteFile])),
- activate_ctrl_connection(State),
- {noreply, State#state{caller = {transfer_data, Bin}}}.
-
-%% ----------- FTP SERVER COMMUNICATION -------------------------
-
-%% Connect to FTP server at Host (default is TCP port 21)
-%% in order to establish a control connection.
-setup_ctrl_connection(Host, Port, Timeout, State) ->
- MsTime = erlang:monotonic_time(),
- case connect(Host, Port, Timeout, State) of
- {ok, IpFam, CSock} ->
- NewState = State#state{csock = {tcp, CSock}, ipfamily = IpFam},
- activate_ctrl_connection(NewState),
- case Timeout - inets_lib:millisec_passed(MsTime) of
- Timeout2 when (Timeout2 >= 0) ->
- {ok, NewState#state{caller = open}, Timeout2};
- _ ->
- %% Oups: Simulate timeout
- {ok, NewState#state{caller = open}, 0}
- end;
- Error ->
- Error
- end.
-
-setup_data_connection(#state{mode = active,
- caller = Caller,
- csock = CSock,
- ftp_extension = FtpExt} = State) ->
- case (catch sockname(CSock)) of
- {ok, {{_, _, _, _, _, _, _, _} = IP, _}} ->
- {ok, LSock} =
- gen_tcp:listen(0, [{ip, IP}, {active, false},
- inet6, binary, {packet, 0}]),
- {ok, {_, Port}} = sockname({tcp,LSock}),
- IpAddress = inet_parse:ntoa(IP),
- Cmd = mk_cmd("EPRT |2|~s|~p|", [IpAddress, Port]),
- send_ctrl_message(State, Cmd),
- activate_ctrl_connection(State),
- {noreply, State#state{caller = {setup_data_connection,
- {LSock, Caller}}}};
- {ok, {{_,_,_,_} = IP, _}} ->
- {ok, LSock} = gen_tcp:listen(0, [{ip, IP}, {active, false},
- binary, {packet, 0}]),
- {ok, Port} = inet:port(LSock),
- case FtpExt of
- false ->
- {IP1, IP2, IP3, IP4} = IP,
- {Port1, Port2} = {Port div 256, Port rem 256},
- send_ctrl_message(State,
- mk_cmd("PORT ~w,~w,~w,~w,~w,~w",
- [IP1, IP2, IP3, IP4, Port1, Port2]));
- true ->
- IpAddress = inet_parse:ntoa(IP),
- Cmd = mk_cmd("EPRT |1|~s|~p|", [IpAddress, Port]),
- send_ctrl_message(State, Cmd)
- end,
- activate_ctrl_connection(State),
- {noreply, State#state{caller = {setup_data_connection,
- {LSock, Caller}}}}
- end;
-
-setup_data_connection(#state{mode = passive, ipfamily = inet6,
- caller = Caller} = State) ->
- send_ctrl_message(State, mk_cmd("EPSV", [])),
- activate_ctrl_connection(State),
- {noreply, State#state{caller = {setup_data_connection, Caller}}};
-
-setup_data_connection(#state{mode = passive, ipfamily = inet,
- caller = Caller,
- ftp_extension = false} = State) ->
- send_ctrl_message(State, mk_cmd("PASV", [])),
- activate_ctrl_connection(State),
- {noreply, State#state{caller = {setup_data_connection, Caller}}};
-
-setup_data_connection(#state{mode = passive, ipfamily = inet,
- caller = Caller,
- ftp_extension = true} = State) ->
- send_ctrl_message(State, mk_cmd("EPSV", [])),
- activate_ctrl_connection(State),
- {noreply, State#state{caller = {setup_data_connection, Caller}}}.
-
-connect(Host, Port, Timeout, #state{ipfamily = inet = IpFam}) ->
- connect2(Host, Port, IpFam, Timeout);
-
-connect(Host, Port, Timeout, #state{ipfamily = inet6 = IpFam}) ->
- connect2(Host, Port, IpFam, Timeout);
-
-connect(Host, Port, Timeout, #state{ipfamily = inet6fb4}) ->
- case inet:getaddr(Host, inet6) of
- {ok, {0, 0, 0, 0, 0, 16#ffff, _, _} = IPv6} ->
- case inet:getaddr(Host, inet) of
- {ok, IPv4} ->
- IpFam = inet,
- connect2(IPv4, Port, IpFam, Timeout);
-
- _ ->
- IpFam = inet6,
- connect2(IPv6, Port, IpFam, Timeout)
- end;
-
- {ok, IPv6} ->
- IpFam = inet6,
- connect2(IPv6, Port, IpFam, Timeout);
-
- _ ->
- case inet:getaddr(Host, inet) of
- {ok, IPv4} ->
- IpFam = inet,
- connect2(IPv4, Port, IpFam, Timeout);
- Error ->
- Error
- end
- end.
-
-connect2(Host, Port, IpFam, Timeout) ->
- Opts = [IpFam, binary, {packet, 0}, {active, false}],
- case gen_tcp:connect(Host, Port, Opts, Timeout) of
- {ok, Sock} ->
- {ok, IpFam, Sock};
- Error ->
- Error
- end.
-
-
-accept_data_connection(#state{mode = active,
- dtimeout = DTimeout,
- tls_options = TLSOptions,
- dsock = {lsock, LSock}} = State0) ->
- case gen_tcp:accept(LSock, DTimeout) of
- {ok, Socket} when is_list(TLSOptions) ->
- gen_tcp:close(LSock),
- ?DBG('<--data ssl:connect(~p, ~p)~n~p~n',[Socket,TLSOptions,State0]),
- case ssl:connect(Socket, TLSOptions, DTimeout) of
- {ok, TLSSocket} ->
- {ok, State0#state{dsock={ssl,TLSSocket}}};
- {error, Reason} ->
- {error, {ssl_connect_failed, Reason}}
- end;
- {ok, Socket} ->
- gen_tcp:close(LSock),
- {ok, State0#state{dsock={tcp,Socket}}};
- {error, Reason} ->
- {error, {data_connect_failed, Reason}}
- end;
-
-accept_data_connection(#state{mode = passive,
- dtimeout = DTimeout,
- dsock = {tcp,Socket},
- tls_options = TLSOptions} = State) when is_list(TLSOptions) ->
- ?DBG('<--data ssl:connect(~p, ~p)~n~p~n',[Socket,TLSOptions,State]),
- case ssl:connect(Socket, TLSOptions, DTimeout) of
- {ok, TLSSocket} ->
- {ok, State#state{dsock={ssl,TLSSocket}}};
- {error, Reason} ->
- {error, {ssl_connect_failed, Reason}}
- end;
-accept_data_connection(#state{mode = passive} = State) ->
- {ok,State}.
-
-
-send_ctrl_message(_S=#state{csock = Socket, verbose = Verbose}, Message) ->
- verbose(lists:flatten(Message),Verbose,send),
- ?DBG('<--ctrl ~p ---- ~s~p~n',[Socket,Message,_S]),
- _ = send_message(Socket, Message).
-
-send_data_message(_S=#state{dsock = Socket}, Message) ->
- ?DBG('<==data ~p ==== ~s~n~p~n',[Socket,Message,_S]),
- case send_message(Socket, Message) of
- ok ->
- ok;
- {error, Reason} ->
- Report = io_lib:format("send/2 for socket ~p failed with "
- "reason ~p~n", [Socket, Reason]),
- error_logger:error_report(Report),
- %% If tcp/ssl does not work the only option is to terminate,
- %% this is the expected behavior under these circumstances.
- exit(normal) %% User will get error message from terminate/2
- end.
-
-send_message({tcp, Socket}, Message) ->
- gen_tcp:send(Socket, Message);
-send_message({ssl, Socket}, Message) ->
- ssl:send(Socket, Message).
-
-activate_ctrl_connection(#state{csock = CSock, ctrl_data = {<<>>, _, _}}) ->
- activate_connection(CSock);
-activate_ctrl_connection(#state{csock = CSock}) ->
- activate_connection(CSock),
- %% We have already received at least part of the next control message,
- %% that has been saved in ctrl_data, process this first.
- self() ! {socket_type(CSock), unwrap_socket(CSock), <<>>},
- ok.
-
-activate_data_connection(#state{dsock = DSock} = State) ->
- activate_connection(DSock),
- State.
-
-activate_connection(Socket) ->
- ignore_return_value(
- case socket_type(Socket) of
- tcp -> inet:setopts(unwrap_socket(Socket), [{active, once}]);
- ssl -> ssl:setopts(unwrap_socket(Socket), [{active, once}])
- end).
-
-
-ignore_return_value(_) -> ok.
-
-unwrap_socket({tcp,Socket}) -> Socket;
-unwrap_socket({ssl,Socket}) -> Socket.
-
-socket_type({tcp,_Socket}) -> tcp;
-socket_type({ssl,_Socket}) -> ssl.
-
-close_ctrl_connection(#state{csock = undefined}) -> ok;
-close_ctrl_connection(#state{csock = Socket}) -> close_connection(Socket).
-
-close_data_connection(#state{dsock = undefined}) -> ok;
-close_data_connection(#state{dsock = Socket}) -> close_connection(Socket).
-
-close_connection({lsock,Socket}) -> ignore_return_value( gen_tcp:close(Socket) );
-close_connection({tcp, Socket}) -> ignore_return_value( gen_tcp:close(Socket) );
-close_connection({ssl, Socket}) -> ignore_return_value( ssl:close(Socket) ).
-
-%% ------------ FILE HANDLING ----------------------------------------
-send_file(#state{tls_upgrading_data_connection = {true, CTRL, _}} = State, Fd) ->
- {noreply, State#state{tls_upgrading_data_connection = {true, CTRL, ?MODULE, send_file, Fd}}};
-send_file(State, Fd) ->
- case file_read(Fd) of
- {ok, N, Bin} when N > 0 ->
- send_data_message(State, Bin),
- progress_report({binary, Bin}, State),
- send_file(State, Fd);
- {ok, _, _} ->
- file_close(Fd),
- close_data_connection(State),
- progress_report({transfer_size, 0}, State),
- activate_ctrl_connection(State),
- {noreply, State#state{caller = transfer_file_second_phase,
- dsock = undefined}};
- {error, Reason} ->
- gen_server:reply(State#state.client, {error, Reason}),
- {stop, normal, State#state{client = undefined}}
- end.
-
-file_open(File, Option) ->
- file:open(File, [raw, binary, Option]).
-
-file_close(Fd) ->
- ignore_return_value( file:close(Fd) ).
-
-file_read(Fd) ->
- case file:read(Fd, ?FILE_BUFSIZE) of
- {ok, Bytes} ->
- {ok, size(Bytes), Bytes};
- eof ->
- {ok, 0, []};
- Other ->
- Other
- end.
-
-file_write(Bytes, Fd) ->
- file:write(Fd, Bytes).
-
-%% -------------- MISC ----------------------------------------------
-
-call(GenServer, Msg, Format) ->
- call(GenServer, Msg, Format, infinity).
-call(GenServer, Msg, Format, Timeout) ->
- Req = {self(), Msg},
- case (catch gen_server:call(GenServer, Req, Timeout)) of
- {ok, Bin} when is_binary(Bin) andalso (Format =:= string) ->
- {ok, binary_to_list(Bin)};
- {'EXIT', _} ->
- {error, eclosed};
- Result ->
- Result
- end.
-
-cast(GenServer, Msg) ->
- gen_server:cast(GenServer, {self(), Msg}).
-
-send_bin(#state{tls_upgrading_data_connection = {true, CTRL, _}} = State, Bin) ->
- State#state{tls_upgrading_data_connection = {true, CTRL, ?MODULE, send_bin, Bin}};
-send_bin(State, Bin) ->
- send_data_message(State, Bin),
- close_data_connection(State),
- activate_ctrl_connection(State),
- {noreply, State#state{caller = transfer_data_second_phase,
- dsock = undefined}}.
-
-mk_cmd(Fmt, Args) ->
- [io_lib:format(Fmt, Args)| [?CR, ?LF]]. % Deep list ok.
-
-is_name_sane([]) ->
- true;
-is_name_sane([?CR| _]) ->
- false;
-is_name_sane([?LF| _]) ->
- false;
-is_name_sane([_| Rest]) ->
- is_name_sane(Rest).
-
-pwd_result(Lines) ->
- {_, [?DOUBLE_QUOTE | Rest]} =
- lists:splitwith(fun(?DOUBLE_QUOTE) -> false; (_) -> true end, Lines),
- {Dir, _} =
- lists:splitwith(fun(?DOUBLE_QUOTE) -> false; (_) -> true end, Rest),
- Dir.
-
-
-key_search(Key, List, Default) ->
- case lists:keysearch(Key, 1, List) of
- {value, {_,Val}} ->
- Val;
- false ->
- Default
- end.
-
-verbose(Lines, true, Direction) ->
- DirStr =
- case Direction of
- send ->
- "Sending: ";
- _ ->
- "Receiving: "
- end,
- Str = string:strip(string:strip(Lines, right, ?LF), right, ?CR),
- erlang:display(DirStr++Str);
-verbose(_, false,_) ->
- ok.
-
-progress(Options) ->
- ftp_progress:start_link(Options).
-
-progress_report(_, #state{progress = ignore}) ->
- ok;
-progress_report(stop, #state{progress = ProgressPid}) ->
- ftp_progress:stop(ProgressPid);
-progress_report({binary, Data}, #state{progress = ProgressPid}) ->
- ftp_progress:report(ProgressPid, {transfer_size, size(Data)});
-progress_report(Report, #state{progress = ProgressPid}) ->
- ftp_progress:report(ProgressPid, Report).
-
-
-peername({tcp, Socket}) -> inet:peername(Socket);
-peername({ssl, Socket}) -> ssl:peername(Socket).
-
-sockname({tcp, Socket}) -> inet:sockname(Socket);
-sockname({ssl, Socket}) -> ssl:sockname(Socket).
-
-maybe_tls_upgrade(Pid, undefined) ->
- {ok, Pid};
-maybe_tls_upgrade(Pid, TLSOptions) ->
- catch ssl:start(),
- call(Pid, {open, tls_upgrade, TLSOptions}, plain).
-
-start_chunk(#state{tls_upgrading_data_connection = {true, CTRL, _}} = State) ->
- State#state{tls_upgrading_data_connection = {true, CTRL, ?MODULE, start_chunk, undefined}};
-start_chunk(#state{client = From} = State) ->
- gen_server:reply(From, ok),
- State#state{chunk = true,
- client = undefined,
- caller = undefined}.
diff --git a/lib/inets/src/ftp/ftp_progress.erl b/lib/inets/src/ftp/ftp_progress.erl
deleted file mode 100644
index a6263e5cd7..0000000000
--- a/lib/inets/src/ftp/ftp_progress.erl
+++ /dev/null
@@ -1,136 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2005-2016. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%% http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-%%
-%% %CopyrightEnd%
-%%
-%%
-%% Description: This module impements a temporary process that
-%% performes progress reporting during file transfer calling a user
-%% defined callback function. Its life span is as long as the ftp connection
-%% processes that spawned it lives. The purpose of this process is to
-%% shild the ftp connection process from errors and time consuming operations
-%% in the user defined callback function.
-
--module(ftp_progress).
-
-%% Internal API
--export([start_link/1, report/2, stop/1]).
-
-%% Spawn export
--export([init/1]).
-
--include_lib("kernel/include/file.hrl").
-
--record(progress, {
- file :: string() | 'undefined',
- cb_module :: module(),
- cb_function :: atom(),
- init_progress_term :: term(),
- current_progress_term :: term()
- }).
-
-%%%=========================================================================
-%%% Internal application API
-%%%=========================================================================
-%%--------------------------------------------------------------------------
-%% start_link(Options) -> ignore | pid()
-%% Options = ignore | {CBModule, CBFunction, InitProgressTerm}
-%%
-%% Description: Starts the progress report process unless progress reporting
-%% should not be performed.
-%%--------------------------------------------------------------------------
--type options() :: 'ignore' | {module(), atom(), term()}.
--spec start_link(options()) -> 'ignore' | pid().
-start_link(ignore) ->
- ignore;
-start_link(Options) ->
- spawn_link(?MODULE, init, [Options]).
-
-%%--------------------------------------------------------------------------
-%% report_progress(Pid, Report) -> ok
-%% Pid = pid()
-%% Report = {local_file, File} | {remote_file, File} |
-%% {transfer_size, Size}
-%% Size = integer()
-%%
-%% Description: Reports progress to the reporting process that calls the
-%% user defined callback function.
-%%--------------------------------------------------------------------------
--type report() :: {'local_file', string()} | {'remote_file', string()}
- | {'transfer_size', non_neg_integer()}.
--spec report(pid(), report()) -> 'ok'.
-report(Pid, Report) ->
- Pid ! {progress_report, Report},
- ok.
-
-%%--------------------------------------------------------------------------
-%% stop(Pid) -> ok
-%% Pid = pid()
-%%
-%% Description:
-%%--------------------------------------------------------------------------
--spec stop(pid()) -> 'ok'.
-stop(Pid) ->
- Pid ! stop,
- ok.
-
-%%%=========================================================================
-%%% Internal functions
-%%%=========================================================================
-init(Options) ->
- loop(progress(Options)).
-
-loop(Progress) ->
- receive
- {progress_report, Report} ->
- NewProgress = report_progress(Report, Progress),
- loop(NewProgress);
- stop ->
- ok
- end.
-
-progress({CBModule, CBFunction, InitProgressTerm}) when is_atom(CBModule),
- is_atom(CBFunction) ->
- #progress{cb_module = CBModule,
- cb_function = CBFunction,
- init_progress_term = InitProgressTerm,
- current_progress_term = InitProgressTerm}.
-
-report_progress({local_file, File}, Progress) ->
- {ok, FileInfo} = file:read_file_info(File),
- report_progress({file_size, FileInfo#file_info.size},
- Progress#progress{file = File});
-
-report_progress({remote_file, File}, Progress) ->
- report_progress({file_size, unknown}, Progress#progress{file = File});
-
-report_progress(Size, #progress{file = File,
- cb_module = CBModule,
- cb_function = CBFunction,
- current_progress_term = Term,
- init_progress_term = InitTerm} = Progress) ->
-
- NewProgressTerm = CBModule:CBFunction(Term, File, Size),
-
- case Size of
- {transfer_size, 0} ->
- %% Transfer is compleat reset initial values
- Progress#progress{current_progress_term = InitTerm,
- file = undefined};
- _ ->
- Progress#progress{current_progress_term = NewProgressTerm}
- end.
diff --git a/lib/inets/src/ftp/ftp_response.erl b/lib/inets/src/ftp/ftp_response.erl
deleted file mode 100644
index d54d97dc91..0000000000
--- a/lib/inets/src/ftp/ftp_response.erl
+++ /dev/null
@@ -1,203 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2005-2016. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%% http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-%%
-%% %CopyrightEnd%
-%%
-%%
-%% Description: This module impements handling of ftp server responses.
-
--module(ftp_response).
-
-%% Internal API
--export([parse_lines/3, interpret/1, error_string/1]).
-
--include("ftp_internal.hrl").
-
-%% First group of reply code digits
--define(POS_PREL, 1).
--define(POS_COMPL, 2).
--define(POS_INTERM, 3).
--define(TRANS_NEG_COMPL, 4).
--define(PERM_NEG_COMPL, 5).
-%% Second group of reply code digits
--define(SYNTAX,0).
--define(INFORMATION,1).
--define(CONNECTION,2).
--define(AUTH_ACC,3).
--define(UNSPEC,4).
--define(FILE_SYSTEM,5).
-
-%%%=========================================================================
-%%% INTERNAL API
-%%%=========================================================================
-
-%%--------------------------------------------------------------------------
-%% parse_lines(Data, AccLines, StatusCode) -> {ok, Lines} |
-%% {continue, {Data,
-%% AccLines, StatusCode}}
-%%
-%% Data = binary() - data recived on the control connection from the
-%% ftp-server.
-%% AccLines = [string()]
-%% StatusCode = start | {byte(), byte(), byte()} | finish -
-%% Indicates where in the parsing process we are.
-%% start - (looking for the status code of the message)
-%% {byte(), byte(), byte()} - status code found, now
-%% looking for the last line indication.
-%% finish - now on the last line.
-%% Description: Parses a ftp control response message.
-%% "A reply is defined to contain the 3-digit code, followed by Space
-%% <SP>, followed by one line of text (where some maximum line length
-%% has been specified), and terminated by the Telnet end-of-line
-%% code (CRLF), or a so called multilined reply for example:
-%%
-%% 123-First line
-%% Second line
-%% 234 A line beginning with numbers
-%% 123 The last line
-%%
-%% The user-process then simply needs to search for the second
-%% occurrence of the same reply code, followed by <SP> (Space), at
-%% the beginning of a line, and ignore all intermediary lines. If
-%% an intermediary line begins with a 3-digit number, the Server
-%% will pad the front to avoid confusion.
-%%--------------------------------------------------------------------------
-
-%% Make sure we received the first 4 bytes so we know how to parse
-%% the FTP server response e.i. is the response composed of one
-%% or multiple lines.
-parse_lines(Bin, Lines, start) when size(Bin) < 4 ->
- {continue, {Bin, Lines, start}};
-%% Multiple lines exist
-parse_lines(<<C1, C2, C3, $-, Rest/binary>>, Lines, start) ->
- parse_lines(Rest, [$-, C3, C2, C1 | Lines], {C1, C2, C3});
-%% Only one line exists
-parse_lines(<<C1, C2, C3, ?WHITE_SPACE, Bin/binary>>, Lines, start) ->
- parse_lines(Bin, [?WHITE_SPACE, C3, C2, C1 | Lines], finish);
-
-%% Last line found
-parse_lines(<<?CR, ?LF, C1, C2, C3, ?WHITE_SPACE, Rest/binary>>, Lines, {C1, C2, C3}) ->
- parse_lines(Rest, [?WHITE_SPACE, C3, C2, C1, ?LF, ?CR | Lines], finish);
-%% Potential end found wait for more data
-parse_lines(<<?CR, ?LF, C1, C2, C3>> = Bin, Lines, {C1, C2, C3}) ->
- {continue, {Bin, Lines, {C1, C2, C3}}};
-%% Intermidate line begining with status code
-parse_lines(<<?CR, ?LF, C1, C2, C3, Rest/binary>>, Lines, {C1, C2, C3}) ->
- parse_lines(Rest, [C3, C2, C1, ?LF, ?CR | Lines], {C1, C2, C3});
-
-%% Potential last line wait for more data
-parse_lines(<<?CR, ?LF, C1, C2>> = Data, Lines, {C1, C2, _} = StatusCode) ->
- {continue, {Data, Lines, StatusCode}};
-parse_lines(<<?CR, ?LF, C1>> = Data, Lines, {C1, _, _} = StatusCode) ->
- {continue, {Data, Lines, StatusCode}};
-parse_lines(<<?CR, ?LF>> = Data, Lines, {_,_,_} = StatusCode) ->
- {continue, {Data, Lines, StatusCode}};
-parse_lines(<<?LF>> = Data, Lines, {_,_,_} = StatusCode) ->
- {continue, {Data, Lines, StatusCode}};
-parse_lines(<<>> = Data, Lines, {_,_,_} = StatusCode) ->
- {continue, {Data, Lines, StatusCode}};
-%% Part of the multiple lines
-parse_lines(<<Octet, Rest/binary>>, Lines, {_,_, _} = StatusCode) ->
- parse_lines(Rest, [Octet | Lines], StatusCode);
-
-%% End of FTP server response found
-parse_lines(<<?CR, ?LF>>, Lines, finish) ->
- {ok, lists:reverse([?LF, ?CR | Lines]), <<>>};
-parse_lines(<<?CR, ?LF, Rest/binary>>, Lines, finish) ->
- {ok, lists:reverse([?LF, ?CR | Lines]), Rest};
-
-%% Potential end found wait for more data
-parse_lines(<<?CR>> = Data, Lines, finish) ->
- {continue, {Data, Lines, finish}};
-parse_lines(<<>> = Data, Lines, finish) ->
- {continue, {Data, Lines, finish}};
-%% Part of last line
-parse_lines(<<Octet, Rest/binary>>, Lines, finish) ->
- parse_lines(Rest, [Octet | Lines], finish).
-
-%%--------------------------------------------------------------------------
-%% interpret(Lines) -> {Status, Text}
-%% Lines = [byte(), byte(), byte() | Text] - ftp server response as
-%% returned by parse_lines/3
-%% Stauts = atom() (see interpret_status/3)
-%% Text = [string()]
-%%
-%% Description: Create nicer data to match on.
-%%--------------------------------------------------------------------------
-interpret([Didgit1, Didgit2, Didgit3 | Data]) ->
- Code1 = Didgit1 - $0,
- Code2 = Didgit2 - $0,
- Code3 = Didgit3 - $0,
- {interpret_status(Code1, Code2, Code3), Data}.
-
-%%--------------------------------------------------------------------------
-%% error_string(Error) -> string()
-%% Error = {error, term()} | term()
-%%
-%% Description: Translates error codes into strings intended for
-%% human interpretation.
-%%--------------------------------------------------------------------------
-error_string({error, Reason}) ->
- error_string(Reason);
-
-error_string(echunk) -> "Synchronisation error during chunk sending.";
-error_string(eclosed) -> "Session has been closed.";
-error_string(econn) -> "Connection to remote server prematurely closed.";
-error_string(eexists) ->"File or directory already exists.";
-error_string(ehost) -> "Host not found, FTP server not found, "
- "or connection rejected.";
-error_string(elogin) -> "User not logged in.";
-error_string(enotbinary) -> "Term is not a binary.";
-error_string(epath) -> "No such file or directory, already exists, "
- "or permission denied.";
-error_string(etype) -> "No such type.";
-error_string(euser) -> "User name or password not valid.";
-error_string(etnospc) -> "Insufficient storage space in system.";
-error_string(enofile) -> "No files found or file unavailable";
-error_string(epnospc) -> "Exceeded storage allocation "
- "(for current directory or dataset).";
-error_string(efnamena) -> "File name not allowed.";
-error_string(Reason) ->
- lists:flatten(io_lib:format("Unknown error: ~w", [Reason])).
-
-%%%========================================================================
-%%% Internal functions
-%%%========================================================================
-
-%% Positive Preleminary Reply
-interpret_status(?POS_PREL,_,_) -> pos_prel;
-%%FIXME ??? 3??? interpret_status(?POS_COMPL, ?AUTH_ACC, 3) -> tls_upgrade;
-interpret_status(?POS_COMPL, ?AUTH_ACC, 4) -> tls_upgrade;
-%% Positive Completion Reply
-interpret_status(?POS_COMPL,_,_) -> pos_compl;
-%% Positive Intermediate Reply nedd account
-interpret_status(?POS_INTERM,?AUTH_ACC,2) -> pos_interm_acct;
-%% Positive Intermediate Reply
-interpret_status(?POS_INTERM,_,_) -> pos_interm;
-%% No files found or file not available
-interpret_status(?TRANS_NEG_COMPL,?FILE_SYSTEM,0) -> enofile;
-%% No storage area no action taken
-interpret_status(?TRANS_NEG_COMPL,?FILE_SYSTEM,2) -> etnospc;
-%% Temporary Error, no action taken
-interpret_status(?TRANS_NEG_COMPL,_,_) -> trans_neg_compl;
-%% Permanent disk space error, the user shall not try again
-interpret_status(?PERM_NEG_COMPL,?FILE_SYSTEM,0) -> epath;
-interpret_status(?PERM_NEG_COMPL,?FILE_SYSTEM,2) -> epnospc;
-interpret_status(?PERM_NEG_COMPL,?FILE_SYSTEM,3) -> efnamena;
-interpret_status(?PERM_NEG_COMPL,?AUTH_ACC,0) -> elogin;
-interpret_status(?PERM_NEG_COMPL,_,_) -> perm_neg_compl.
-
diff --git a/lib/inets/src/ftp/ftp_sup.erl b/lib/inets/src/ftp/ftp_sup.erl
deleted file mode 100644
index 21dcfb6ab2..0000000000
--- a/lib/inets/src/ftp/ftp_sup.erl
+++ /dev/null
@@ -1,60 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2004-2016. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%% http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-%%
-%% %CopyrightEnd%
-%%
-%%
-%%----------------------------------------------------------------------
-%% Purpose: The top supervisor for the ftp hangs under inets_sup.
-%%----------------------------------------------------------------------
--module(ftp_sup).
-
--behaviour(supervisor).
-
-%% API
--export([start_link/0]).
--export([start_child/1]).
-
-%% Supervisor callback
--export([init/1]).
-
-%%%=========================================================================
-%%% API
-%%%=========================================================================
-start_link() ->
- supervisor:start_link({local, ?MODULE}, ?MODULE, []).
-
-start_child(Args) ->
- supervisor:start_child(?MODULE, Args).
-
-%%%=========================================================================
-%%% Supervisor callback
-%%%=========================================================================
-init(_) ->
- RestartStrategy = simple_one_for_one,
- MaxR = 0,
- MaxT = 3600,
-
- Name = undefined, % As simple_one_for_one is used.
- StartFunc = {ftp, start_link, []},
- Restart = temporary, % E.g. should not be restarted
- Shutdown = 4000,
- Modules = [ftp],
- Type = worker,
-
- ChildSpec = {Name, StartFunc, Restart, Shutdown, Type, Modules},
- {ok, {{RestartStrategy, MaxR, MaxT}, [ChildSpec]}}.
diff --git a/lib/inets/src/inets_app/Makefile b/lib/inets/src/inets_app/Makefile
index eb0098dbee..450e49dcc3 100644
--- a/lib/inets/src/inets_app/Makefile
+++ b/lib/inets/src/inets_app/Makefile
@@ -48,7 +48,8 @@ MODULES = \
inets_app \
inets_sup \
inets_trace \
- inets_lib
+ inets_lib \
+ inets_ftp_wrapper
INTERNAL_HRL_FILES = inets_internal.hrl
EXTERNAL_HRL_FILES = ../../include/httpd.hrl \
diff --git a/lib/inets/src/inets_app/inets.app.src b/lib/inets/src/inets_app/inets.app.src
index eb4be932ac..0af44e39ab 100644
--- a/lib/inets/src/inets_app/inets.app.src
+++ b/lib/inets/src/inets_app/inets.app.src
@@ -30,10 +30,7 @@
inets_lib,
%% FTP
- ftp,
- ftp_progress,
- ftp_response,
- ftp_sup,
+ inets_ftp_wrapper,
%% HTTP client:
httpc,
diff --git a/lib/inets/src/inets_app/inets.erl b/lib/inets/src/inets_app/inets.erl
index 2d380012d7..615a4cf635 100644
--- a/lib/inets/src/inets_app/inets.erl
+++ b/lib/inets/src/inets_app/inets.erl
@@ -470,8 +470,10 @@ service_module(tftpd) ->
tftp;
service_module(tftpc) ->
tftp;
+%% Obsolete! Kept for backward compatiblity!
+%% FTP application has been moved out from inets
service_module(ftpc) ->
- ftp;
+ inets_ftp_wrapper;
service_module(Service) ->
Service.
diff --git a/lib/inets/src/ftp/ftp_internal.hrl b/lib/inets/src/inets_app/inets_ftp_wrapper.erl
index f29bb4a099..b3fc7a8436 100644
--- a/lib/inets/src/ftp/ftp_internal.hrl
+++ b/lib/inets/src/inets_app/inets_ftp_wrapper.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -17,17 +17,31 @@
%%
%% %CopyrightEnd%
%%
-%%
+-module(inets_ftp_wrapper).
+
+
+-export([start_standalone/1,
+ start_service/1,
+ stop_service/1,
+ services/0,
+ service_info/1]).
+
+
+start_standalone(_) ->
+ ok.
+
+start_service(Options) ->
+ application:ensure_started(ftp),
+ ftp:start_service(Options).
+
+
+stop_service(Pid) ->
+ ftp:stop_service(Pid).
--ifndef(ftp_internal_hrl).
--define(ftp_internal_hrl, true).
--include_lib("inets/src/inets_app/inets_internal.hrl").
+services() ->
+ [].
--define(SERVICE, ftpc).
--define(fcri(Label, Content), ?report_important(Label, ?SERVICE, Content)).
--define(fcrv(Label, Content), ?report_verbose(Label, ?SERVICE, Content)).
--define(fcrd(Label, Content), ?report_debug(Label, ?SERVICE, Content)).
--define(fcrt(Label, Content), ?report_trace(Label, ?SERVICE, Content)).
--endif. % -ifdef(ftp_internal_hrl).
+service_info(_) ->
+ [].
diff --git a/lib/inets/src/inets_app/inets_sup.erl b/lib/inets/src/inets_app/inets_sup.erl
index d8ae7eff26..8f4d806529 100644
--- a/lib/inets/src/inets_app/inets_sup.erl
+++ b/lib/inets/src/inets_app/inets_sup.erl
@@ -62,19 +62,9 @@ children() ->
HttpdServices = [Service || Service <- Services, is_httpd(Service)],
HttpcServices = [Service || Service <- Services, is_httpc(Service)],
TftpdServices = [Service || Service <- Services, is_tftpd(Service)],
- [ftp_child_spec(), httpc_child_spec(HttpcServices),
+ [httpc_child_spec(HttpcServices),
httpd_child_spec(HttpdServices), tftpd_child_spec(TftpdServices)].
-ftp_child_spec() ->
- Name = ftp_sup,
- StartFunc = {ftp_sup, start_link, []},
- Restart = permanent,
- Shutdown = infinity,
- Modules = [ftp_sup],
- Type = supervisor,
- {Name, StartFunc, Restart, Shutdown, Type, Modules}.
-
-
httpc_child_spec(HttpcServices0) ->
HttpcServices = default_profile(HttpcServices0, []),
Name = httpc_sup,
diff --git a/lib/inets/src/subdirs.mk b/lib/inets/src/subdirs.mk
index 9f2a0079f2..8c18691aef 100644
--- a/lib/inets/src/subdirs.mk
+++ b/lib/inets/src/subdirs.mk
@@ -1,3 +1,3 @@
#-*-makefile-*- ; force emacs to enter makefile-mode
-SUB_DIRECTORIES = inets_app http_lib http_client http_server ftp tftp
+SUB_DIRECTORIES = inets_app http_lib http_client http_server tftp
diff --git a/lib/inets/test/Makefile b/lib/inets/test/Makefile
index 99a7e6a9db..4dab4e14c6 100644
--- a/lib/inets/test/Makefile
+++ b/lib/inets/test/Makefile
@@ -44,8 +44,7 @@ RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
INCLUDES = -I. \
-I$(ERL_TOP)/lib/inets/src/inets_app \
-I$(ERL_TOP)/lib/inets/src/http_lib \
- -I$(ERL_TOP)/lib/inets/src/http_client \
- -I$(ERL_TOP)/lib/inets/src/ftp
+ -I$(ERL_TOP)/lib/inets/src/http_client
CP = cp
@@ -68,34 +67,6 @@ INETS_FLAGS = -Dinets_data_dir='"$(INETS_DATA_DIR)"' \
###
### test suite debug flags
###
-ifeq ($(FTP_DEBUG_CLIENT),)
- FTP_DEBUG_CLIENT = y
-endif
-
-ifeq ($(FTP_DEBUG_CLIENT),)
- FTP_FLAGS += -Dftp_debug_client
-endif
-
-ifeq ($(FTP_TRACE_CLIENT),)
- FTP_DEBUG_CLIENT = y
-endif
-
-ifeq ($(FTP_TRACE_CLIENT),y)
- FTP_FLAGS += -Dftp_trace_client
-endif
-
-ifneq ($(FTP_DEBUG),)
- FTP_DEBUG = s
-endif
-
-ifeq ($(FTP_DEBUG),l)
- FTP_FLAGS += -Dftp_log
-endif
-
-ifeq ($(FTP_DEBUG),d)
- FTP_FLAGS += -Dftp_debug -Dftp_log
-endif
-
ifeq ($(INETS_DEBUG),)
INETS_DEBUG = d
endif
@@ -151,8 +122,6 @@ MODULES = \
inets_test_lib \
erl_make_certs \
make_certs \
- ftp_SUITE \
- ftp_format_SUITE \
http_format_SUITE \
httpc_SUITE \
httpc_cookie_SUITE \
@@ -179,7 +148,6 @@ EBIN = .
HRL_FILES = inets_test_lib.hrl \
inets_internal.hrl \
- ftp_internal.hrl \
httpc_internal.hrl \
http_internal.hrl \
tftp_test_lib.hrl
@@ -198,7 +166,6 @@ INETS_FILES = inets.config $(INETS_SPECS)
# inets_sup_suite \
# inets_httpd_suite \
# inets_httpc_suite \
-# inets_ftp_suite \
# inets_tftp_suite
@@ -206,9 +173,8 @@ INETS_DATADIRS = inets_SUITE_data inets_socketwrap_SUITE_data
HTTPD_DATADIRS = httpd_test_data httpd_SUITE_data httpd_basic_SUITE_data old_httpd_SUITE_data httpd_bench_SUITE_data
HTTPC_DATADIRS = httpc_SUITE_data httpc_proxy_SUITE_data
-FTP_DATADIRS = ftp_SUITE_data
-DATADIRS = $(INETS_DATADIRS) $(HTTPD_DATADIRS) $(HTTPC_DATADIRS) $(FTP_DATADIRS)
+DATADIRS = $(INETS_DATADIRS) $(HTTPD_DATADIRS) $(HTTPC_DATADIRS)
EMAKEFILE = Emakefile
MAKE_EMAKE = $(wildcard $(ERL_TOP)/make/make_emakefile)
@@ -238,7 +204,6 @@ RELTESTSYSBINDIR = $(RELTESTSYSALLDATADIR)/bin
# ----------------------------------------------------
ERL_COMPILE_FLAGS += \
$(INCLUDES) \
- $(FTP_FLAGS) \
$(INETS_FLAGS)
# ----------------------------------------------------
@@ -334,7 +299,6 @@ info:
@echo "INETS_PRIV_DIR = $(INETS_PRIV_DIR)"
@echo "INETS_ROOT = $(INETS_ROOT)"
@echo "INETS_FLAGS = $(INETS_FLAGS)"
- @echo "FTP_FLAGS = $(FTP_FLAGS)"
tftp:
erlc $(ERL_COMPILE_FLAGS) tftp_test_lib.erl tftp_SUITE.erl && erl -pa ../../inets/ebin -s tftp_SUITE t -s erlang halt
diff --git a/lib/inets/test/ftp_SUITE.erl b/lib/inets/test/ftp_SUITE.erl
deleted file mode 100644
index 3dfec01ba2..0000000000
--- a/lib/inets/test/ftp_SUITE.erl
+++ /dev/null
@@ -1,1180 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2004-2017. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%% http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-%%
-%% %CopyrightEnd%
-%%
-%%
-
-%%
-%% ct:run("../inets_test", ftp_SUITE).
-%%
-
--module(ftp_SUITE).
-
--include_lib("kernel/include/file.hrl").
--include_lib("common_test/include/ct.hrl").
--include("inets_test_lib.hrl").
-
-%% Note: This directive should only be used in test suites.
--compile(export_all).
-
--define(FTP_USER, "anonymous").
--define(FTP_PASS(Cmnt), (fun({ok,__H}) -> "ftp_SUITE_"++Cmnt++"@" ++ __H;
- (_) -> "ftp_SUITE_"++Cmnt++"@localhost"
- end)(inet:gethostname())
- ).
-
--define(BAD_HOST, "badhostname").
--define(BAD_USER, "baduser").
--define(BAD_DIR, "baddirectory").
-
--record(progress, {
- current = 0,
- total
- }).
-
-%%--------------------------------------------------------------------
-%% Common Test interface functions -----------------------------------
-%%--------------------------------------------------------------------
-suite() ->
- [{timetrap,{seconds,20}}].
-
-all() ->
- [
- {group, ftp_passive},
- {group, ftp_active},
- {group, ftps_passive},
- {group, ftps_active},
- error_ehost,
- clean_shutdown
- ].
-
-groups() ->
- [
- {ftp_passive, [], ftp_tests()},
- {ftp_active, [], ftp_tests()},
- {ftps_passive, [], ftp_tests()},
- {ftps_active, [], ftp_tests()}
- ].
-
-ftp_tests()->
- [
- user,
- bad_user,
- pwd,
- cd,
- lcd,
- ls,
- nlist,
- rename,
- delete,
- mkdir,
- rmdir,
- send,
- send_3,
- send_bin,
- send_chunk,
- append,
- append_bin,
- append_chunk,
- recv,
- recv_3,
- recv_bin,
- recv_bin_twice,
- recv_chunk,
- recv_chunk_twice,
- recv_chunk_three_times,
- type,
- quote,
- error_elogin,
- progress_report_send,
- progress_report_recv,
- not_owner,
- unexpected_call,
- unexpected_cast,
- unexpected_bang
- ].
-
-%%--------------------------------------------------------------------
-
-%%% Config
-%%% key meaning
-%%% ................................................................
-%%% ftpservers list of servers to check if they are available
-%%% The element is:
-%%% {Name, % string(). The os command name
-%%% Path, % string(). The os PATH syntax, e.g "/bin:/usr/bin"
-%%% StartCommand, % fun()->{ok,start_result()} | {error,string()}.
-%%% % The command to start the daemon with.
-%%% ChkUp, % fun(start_result()) -> string(). Os command to check
-%%% % if the server is running. [] if not running.
-%%% % The string in string() is suitable for logging.
-%%% StopCommand, % fun(start_result()) -> void(). The command to stop the daemon with.
-%%% AugmentFun, % fun(config()) -> config() Adds two funs for transforming names of files
-%%% % and directories to the form they are returned from this server
-%%% ServerHost, % string(). Mostly "localhost"
-%%% ServerPort % pos_integer()
-%%% }
-%%%
-
--define(default_ftp_servers,
- [{"vsftpd",
- "/sbin:/usr/sbin:/usr/local/sbin",
- fun(__CONF__, AbsName) ->
- DataDir = proplists:get_value(data_dir,__CONF__),
- ConfFile = filename:join(DataDir, "vsftpd.conf"),
- PrivDir = proplists:get_value(priv_dir,__CONF__),
- AnonRoot = PrivDir,
- Cmd = [AbsName ++" "++filename:join(DataDir,"vsftpd.conf"),
- " -oftpd_banner=erlang_otp_testing",
- " -oanon_root=\"",AnonRoot,"\"",
- " -orsa_cert_file=\"",filename:join(DataDir,"server-cert.pem"),"\"",
- " -orsa_private_key_file=\"",filename:join(DataDir,"server-key.pem"),"\""
- ],
- Result = os:cmd(Cmd),
- ct:log("Config file:~n~s~n~nServer start command:~n ~s~nResult:~n ~p",
- [case file:read_file(ConfFile) of
- {ok,X} -> X;
- _ -> ""
- end,
- Cmd, Result
- ]),
- case Result of
- [] -> {ok,'dont care'};
- [Msg] -> {error,Msg}
- end
- end,
- fun(_StartResult) -> os:cmd("ps ax | grep erlang_otp_testing | grep -v grep")
- end,
- fun(_StartResult) -> os:cmd("kill `ps ax | grep erlang_otp_testing | awk '/vsftpd/{print $1}'`")
- end,
- fun(__CONF__) ->
- AnonRoot = proplists:get_value(priv_dir,__CONF__),
- [{id2ftp, fun(Id) -> filename:join(AnonRoot,Id) end},
- {id2ftp_result,fun(Id) -> filename:join(AnonRoot,Id) end} | __CONF__]
- end,
- "localhost",
- 9999
- }
- ]
- ).
-
-
-init_per_suite(Config) ->
- case find_executable(Config) of
- false ->
- {skip, "No ftp server found"};
- {ok,Data} ->
- TstDir = filename:join(proplists:get_value(priv_dir,Config), "test"),
- file:make_dir(TstDir),
- make_cert_files(dsa, rsa, "server-", proplists:get_value(data_dir,Config)),
- start_ftpd([{test_dir,TstDir},
- {ftpd_data,Data}
- | Config])
- end.
-
-end_per_suite(Config) ->
- ps_ftpd(Config),
- stop_ftpd(Config),
- ps_ftpd(Config),
- ok.
-
-%%--------------------------------------------------------------------
-init_per_group(Group, Config) when Group == ftps_active,
- Group == ftps_passive ->
- catch crypto:stop(),
- try crypto:start() of
- ok ->
- Config
- catch
- _:_ ->
- {skip, "Crypto did not start"}
- end;
-
-init_per_group(_Group, Config) ->
- Config.
-
-end_per_group(_Group, Config) ->
- Config.
-
-%%--------------------------------------------------------------------
-init_per_testcase(Case, Config0) ->
- Group = proplists:get_value(name, proplists:get_value(tc_group_properties,Config0)),
- TLS = [{tls,[{reuse_sessions,true}]}],
- ACTIVE = [{mode,active}],
- PASSIVE = [{mode,passive}],
- CaseOpts = case Case of
- progress_report_send -> [{progress, {?MODULE,progress,#progress{}}}];
- progress_report_recv -> [{progress, {?MODULE,progress,#progress{}}}];
- _ -> []
- end,
- ExtraOpts = [verbose | CaseOpts],
- Config =
- case Group of
- ftp_active -> ftp__open(Config0, ACTIVE ++ ExtraOpts);
- ftps_active -> ftp__open(Config0, TLS++ ACTIVE ++ ExtraOpts);
- ftp_passive -> ftp__open(Config0, PASSIVE ++ ExtraOpts);
- ftps_passive -> ftp__open(Config0, TLS++PASSIVE ++ ExtraOpts);
- undefined -> Config0
- end,
- case Case of
- user -> Config;
- bad_user -> Config;
- error_elogin -> Config;
- error_ehost -> Config;
- clean_shutdown -> Config;
- _ ->
- Pid = proplists:get_value(ftp,Config),
- ok = ftp:user(Pid, ?FTP_USER, ?FTP_PASS(atom_to_list(Group)++"-"++atom_to_list(Case)) ),
- ok = ftp:cd(Pid, proplists:get_value(priv_dir,Config)),
- Config
- end.
-
-
-end_per_testcase(user, _Config) -> ok;
-end_per_testcase(bad_user, _Config) -> ok;
-end_per_testcase(error_elogin, _Config) -> ok;
-end_per_testcase(error_ehost, _Config) -> ok;
-end_per_testcase(clean_shutdown, _Config) -> ok;
-end_per_testcase(_Case, Config) ->
- case proplists:get_value(tc_status,Config) of
- ok -> ok;
- _ ->
- try ftp:latest_ctrl_response(proplists:get_value(ftp,Config))
- of
- {ok,S} -> ct:log("***~n*** Latest ctrl channel response:~n*** ~p~n***",[S])
- catch
- _:_ -> ok
- end
- end,
- ftp__close(Config).
-
-%%--------------------------------------------------------------------
-%% Test Cases --------------------------------------------------------
-%%--------------------------------------------------------------------
-user() -> [
- {doc, "Open an ftp connection to a host, and logon as anonymous ftp,"
- " then logoff"}].
-user(Config) ->
- Pid = proplists:get_value(ftp, Config),
- ok = ftp:user(Pid, ?FTP_USER, ?FTP_PASS("")),% logon
- ok = ftp:close(Pid), % logoff
- {error,eclosed} = ftp:pwd(Pid), % check logoff result
- ok.
-
-%%-------------------------------------------------------------------------
-bad_user() ->
- [{doc, "Open an ftp connection to a host, and logon with bad user."}].
-bad_user(Config) ->
- Pid = proplists:get_value(ftp, Config),
- {error, euser} = ftp:user(Pid, ?BAD_USER, ?FTP_PASS("")),
- ok.
-
-%%-------------------------------------------------------------------------
-pwd() ->
- [{doc, "Test ftp:pwd/1 & ftp:lpwd/1"}].
-pwd(Config0) ->
- Config = set_state([reset], Config0),
- Pid = proplists:get_value(ftp, Config),
- {ok, PWD} = ftp:pwd(Pid),
- {ok, PathLpwd} = ftp:lpwd(Pid),
- PWD = id2ftp_result("", Config),
- PathLpwd = id2ftp_result("", Config).
-
-%%-------------------------------------------------------------------------
-cd() ->
- ["Open an ftp connection, log on as anonymous ftp, and cd to a"
- "directory and to a non-existent directory."].
-cd(Config0) ->
- Dir = "test",
- Config = set_state([reset,{mkdir,Dir}], Config0),
- Pid = proplists:get_value(ftp, Config),
- ok = ftp:cd(Pid, id2ftp(Dir,Config)),
- {ok, PWD} = ftp:pwd(Pid),
- ExpectedPWD = id2ftp_result(Dir, Config),
- PWD = ExpectedPWD,
- {error, epath} = ftp:cd(Pid, ?BAD_DIR),
- ok.
-
-%%-------------------------------------------------------------------------
-lcd() ->
- [{doc, "Test api function ftp:lcd/2"}].
-lcd(Config0) ->
- Dir = "test",
- Config = set_state([reset,{mkdir,Dir}], Config0),
- Pid = proplists:get_value(ftp, Config),
- ok = ftp:lcd(Pid, id2ftp(Dir,Config)),
- {ok, PWD} = ftp:lpwd(Pid),
- ExpectedPWD = id2ftp_result(Dir, Config),
- PWD = ExpectedPWD,
- {error, epath} = ftp:lcd(Pid, ?BAD_DIR).
-
-%%-------------------------------------------------------------------------
-ls() ->
- [{doc, "Open an ftp connection; ls the current directory, and the "
- "\"test\" directory. We assume that ls never fails, since "
- "it's output is meant to be read by humans. "}].
-ls(Config0) ->
- Config = set_state([reset,{mkdir,"test"}], Config0),
- Pid = proplists:get_value(ftp, Config),
- {ok, _R1} = ftp:ls(Pid),
- {ok, _R2} = ftp:ls(Pid, id2ftp("test",Config)),
- %% neither nlist nor ls operates on a directory
- %% they operate on a pathname, which *can* be a
- %% directory, but can also be a filename or a group
- %% of files (including wildcards).
- case proplists:get_value(wildcard_support, Config) of
- true ->
- {ok, _R3} = ftp:ls(Pid, id2ftp("te*",Config));
- _ ->
- ok
- end.
-
-%%-------------------------------------------------------------------------
-nlist() ->
- [{doc,"Open an ftp connection; nlist the current directory, and the "
- "\"test\" directory. Nlist does not behave consistenly over "
- "operating systems. On some it is an error to have an empty "
- "directory."}].
-nlist(Config0) ->
- Config = set_state([reset,{mkdir,"test"}], Config0),
- Pid = proplists:get_value(ftp, Config),
- {ok, _R1} = ftp:nlist(Pid),
- {ok, _R2} = ftp:nlist(Pid, id2ftp("test",Config)),
- %% neither nlist nor ls operates on a directory
- %% they operate on a pathname, which *can* be a
- %% directory, but can also be a filename or a group
- %% of files (including wildcards).
- case proplists:get_value(wildcard_support, Config) of
- true ->
- {ok, _R3} = ftp:nlist(Pid, id2ftp("te*",Config));
- _ ->
- ok
- end.
-
-%%-------------------------------------------------------------------------
-rename() ->
- [{doc, "Rename a file."}].
-rename(Config0) ->
- Contents = <<"ftp_SUITE test ...">>,
- OldFile = "old.txt",
- NewFile = "new.txt",
- Config = set_state([reset,{mkfile,OldFile,Contents}], Config0),
- Pid = proplists:get_value(ftp, Config),
-
- ok = ftp:rename(Pid,
- id2ftp(OldFile,Config),
- id2ftp(NewFile,Config)),
-
- true = (chk_file(NewFile,Contents,Config)
- and chk_no_file([OldFile],Config)),
- {error,epath} = ftp:rename(Pid,
- id2ftp("non_existing_file",Config),
- id2ftp(NewFile,Config)),
- ok.
-
-%%-------------------------------------------------------------------------
-send() ->
- [{doc, "Transfer a file with ftp using send/2."}].
-send(Config0) ->
- Contents = <<"ftp_SUITE test ...">>,
- SrcDir = "data",
- File = "file.txt",
- Config = set_state([reset,{mkfile,[SrcDir,File],Contents}], Config0),
- Pid = proplists:get_value(ftp, Config),
-
- chk_no_file([File],Config),
- chk_file([SrcDir,File],Contents,Config),
-
- ok = ftp:lcd(Pid, id2ftp(SrcDir,Config)),
- ok = ftp:cd(Pid, id2ftp("",Config)),
- ok = ftp:send(Pid, File),
- chk_file(File, Contents, Config),
-
- {error,epath} = ftp:send(Pid, "non_existing_file"),
- ok.
-
-%%-------------------------------------------------------------------------
-send_3() ->
- [{doc, "Transfer a file with ftp using send/3."}].
-send_3(Config0) ->
- Contents = <<"ftp_SUITE test ...">>,
- Dir = "incoming",
- File = "file.txt",
- RemoteFile = "remfile.txt",
- Config = set_state([reset,{mkfile,File,Contents},{mkdir,Dir}], Config0),
- Pid = proplists:get_value(ftp, Config),
-
- ok = ftp:cd(Pid, id2ftp(Dir,Config)),
- ok = ftp:lcd(Pid, id2ftp("",Config)),
- ok = ftp:send(Pid, File, RemoteFile),
- chk_file([Dir,RemoteFile], Contents, Config),
-
- {error,epath} = ftp:send(Pid, "non_existing_file", RemoteFile),
- ok.
-
-%%-------------------------------------------------------------------------
-send_bin() ->
- [{doc, "Send a binary."}].
-send_bin(Config0) ->
- BinContents = <<"ftp_SUITE test ...">>,
- File = "file.txt",
- Config = set_state([reset], Config0),
- Pid = proplists:get_value(ftp, Config),
- {error, enotbinary} = ftp:send_bin(Pid, "some string", id2ftp(File,Config)),
- ok = ftp:send_bin(Pid, BinContents, id2ftp(File,Config)),
- chk_file(File, BinContents, Config),
- {error, efnamena} = ftp:send_bin(Pid, BinContents, "/nothere"),
- ok.
-
-%%-------------------------------------------------------------------------
-send_chunk() ->
- [{doc, "Send a binary using chunks."}].
-send_chunk(Config0) ->
- Contents1 = <<"1: ftp_SUITE test ...">>,
- Contents2 = <<"2: ftp_SUITE test ...">>,
- File = "file.txt",
- Config = set_state([reset,{mkdir,"incoming"}], Config0),
- Pid = proplists:get_value(ftp, Config),
-
- ok = ftp:send_chunk_start(Pid, id2ftp(File,Config)),
- {error, echunk} = ftp:send_chunk_start(Pid, id2ftp(File,Config)),
- {error, echunk} = ftp:cd(Pid, "incoming"),
- {error, enotbinary} = ftp:send_chunk(Pid, "some string"),
- ok = ftp:send_chunk(Pid, Contents1),
- ok = ftp:send_chunk(Pid, Contents2),
- ok = ftp:send_chunk_end(Pid),
- chk_file(File, <<Contents1/binary,Contents2/binary>>, Config),
-
- {error, echunk} = ftp:send_chunk(Pid, Contents1),
- {error, echunk} = ftp:send_chunk_end(Pid),
- {error, efnamena} = ftp:send_chunk_start(Pid, "/"),
- ok.
-
-%%-------------------------------------------------------------------------
-delete() ->
- [{doc, "Delete a file."}].
-delete(Config0) ->
- Contents = <<"ftp_SUITE test ...">>,
- File = "file.txt",
- Config = set_state([reset,{mkfile,File,Contents}], Config0),
- Pid = proplists:get_value(ftp, Config),
- ok = ftp:delete(Pid, id2ftp(File,Config)),
- chk_no_file([File], Config),
- {error,epath} = ftp:delete(Pid, id2ftp(File,Config)),
- ok.
-
-%%-------------------------------------------------------------------------
-mkdir() ->
- [{doc, "Make a remote directory."}].
-mkdir(Config0) ->
- NewDir = "new_dir",
- Config = set_state([reset], Config0),
- Pid = proplists:get_value(ftp, Config),
- ok = ftp:mkdir(Pid, id2ftp(NewDir,Config)),
- chk_dir([NewDir], Config),
- {error,epath} = ftp:mkdir(Pid, id2ftp(NewDir,Config)),
- ok.
-
-%%-------------------------------------------------------------------------
-rmdir() ->
- [{doc, "Remove a directory."}].
-rmdir(Config0) ->
- Dir = "dir",
- Config = set_state([reset,{mkdir,Dir}], Config0),
- Pid = proplists:get_value(ftp, Config),
- ok = ftp:rmdir(Pid, id2ftp(Dir,Config)),
- chk_no_dir([Dir], Config),
- {error,epath} = ftp:rmdir(Pid, id2ftp(Dir,Config)),
- ok.
-
-%%-------------------------------------------------------------------------
-append() ->
- [{doc, "Append a local file twice to a remote file"}].
-append(Config0) ->
- SrcFile = "f_src.txt",
- DstFile = "f_dst.txt",
- Contents = <<"ftp_SUITE test ...">>,
- Config = set_state([reset,{mkfile,SrcFile,Contents}], Config0),
- Pid = proplists:get_value(ftp, Config),
- ok = ftp:append(Pid, id2ftp(SrcFile,Config), id2ftp(DstFile,Config)),
- ok = ftp:append(Pid, id2ftp(SrcFile,Config), id2ftp(DstFile,Config)),
- chk_file(DstFile, <<Contents/binary,Contents/binary>>, Config),
- {error,epath} = ftp:append(Pid, id2ftp("non_existing_file",Config), id2ftp(DstFile,Config)),
- ok.
-
-%%-------------------------------------------------------------------------
-append_bin() ->
- [{doc, "Append a local file twice to a remote file using append_bin"}].
-append_bin(Config0) ->
- DstFile = "f_dst.txt",
- Contents = <<"ftp_SUITE test ...">>,
- Config = set_state([reset], Config0),
- Pid = proplists:get_value(ftp, Config),
- ok = ftp:append_bin(Pid, Contents, id2ftp(DstFile,Config)),
- ok = ftp:append_bin(Pid, Contents, id2ftp(DstFile,Config)),
- chk_file(DstFile, <<Contents/binary,Contents/binary>>, Config).
-
-%%-------------------------------------------------------------------------
-append_chunk() ->
- [{doc, "Append chunks."}].
-append_chunk(Config0) ->
- File = "f_dst.txt",
- Contents = [<<"ER">>,<<"LE">>,<<"RL">>],
- Config = set_state([reset], Config0),
- Pid = proplists:get_value(ftp, Config),
- ok = ftp:append_chunk_start(Pid, id2ftp(File,Config)),
- {error, enotbinary} = ftp:append_chunk(Pid, binary_to_list(lists:nth(1,Contents))),
- ok = ftp:append_chunk(Pid,lists:nth(1,Contents)),
- ok = ftp:append_chunk(Pid,lists:nth(2,Contents)),
- ok = ftp:append_chunk(Pid,lists:nth(3,Contents)),
- ok = ftp:append_chunk_end(Pid),
- chk_file(File, <<"ERLERL">>, Config).
-
-%%-------------------------------------------------------------------------
-recv() ->
- [{doc, "Receive a file using recv/2"}].
-recv(Config0) ->
- File1 = "f_dst1.txt",
- File2 = "f_dst2.txt",
- SrcDir = "a_dir",
- Contents1 = <<"1 ftp_SUITE test ...">>,
- Contents2 = <<"2 ftp_SUITE test ...">>,
- Config = set_state([reset, {mkfile,[SrcDir,File1],Contents1}, {mkfile,[SrcDir,File2],Contents2}], Config0),
- Pid = proplists:get_value(ftp, Config),
- ok = ftp:cd(Pid, id2ftp(SrcDir,Config)),
- ok = ftp:lcd(Pid, id2ftp("",Config)),
- ok = ftp:recv(Pid, File1),
- chk_file(File1, Contents1, Config),
- ok = ftp:recv(Pid, File2),
- chk_file(File2, Contents2, Config),
- {error,epath} = ftp:recv(Pid, "non_existing_file"),
- ok.
-
-%%-------------------------------------------------------------------------
-recv_3() ->
- [{doc,"Receive a file using recv/3"}].
-recv_3(Config0) ->
- DstFile = "f_src.txt",
- SrcFile = "f_dst.txt",
- Contents = <<"ftp_SUITE test ...">>,
- Config = set_state([reset, {mkfile,SrcFile,Contents}], Config0),
- Pid = proplists:get_value(ftp, Config),
- ok = ftp:cd(Pid, id2ftp("",Config)),
- ok = ftp:recv(Pid, SrcFile, id2abs(DstFile,Config)),
- chk_file(DstFile, Contents, Config).
-
-%%-------------------------------------------------------------------------
-recv_bin() ->
- [{doc, "Receive a file as a binary."}].
-recv_bin(Config0) ->
- File = "f_dst.txt",
- Contents = <<"ftp_SUITE test ...">>,
- Config = set_state([reset, {mkfile,File,Contents}], Config0),
- Pid = proplists:get_value(ftp, Config),
- {ok,Received} = ftp:recv_bin(Pid, id2ftp(File,Config)),
- find_diff(Received, Contents),
- {error,epath} = ftp:recv_bin(Pid, id2ftp("non_existing_file",Config)),
- ok.
-
-%%-------------------------------------------------------------------------
-recv_bin_twice() ->
- [{doc, "Receive two files as a binaries."}].
-recv_bin_twice(Config0) ->
- File1 = "f_dst1.txt",
- File2 = "f_dst2.txt",
- Contents1 = <<"1 ftp_SUITE test ...">>,
- Contents2 = <<"2 ftp_SUITE test ...">>,
- Config = set_state([reset, {mkfile,File1,Contents1}, {mkfile,File2,Contents2}], Config0),
- ct:log("First transfer",[]),
- Pid = proplists:get_value(ftp, Config),
- {ok,Received1} = ftp:recv_bin(Pid, id2ftp(File1,Config)),
- find_diff(Received1, Contents1),
- ct:log("Second transfer",[]),
- {ok,Received2} = ftp:recv_bin(Pid, id2ftp(File2,Config)),
- find_diff(Received2, Contents2),
- ct:log("Transfers ready!",[]),
- {error,epath} = ftp:recv_bin(Pid, id2ftp("non_existing_file",Config)),
- ok.
-%%-------------------------------------------------------------------------
-recv_chunk() ->
- [{doc, "Receive a file using chunk-wise."}].
-recv_chunk(Config0) ->
- File = "big_file.txt",
- Contents = list_to_binary( lists:duplicate(1000, lists:seq(0,255)) ),
- Config = set_state([reset, {mkfile,File,Contents}], Config0),
- Pid = proplists:get_value(ftp, Config),
- {{error, "ftp:recv_chunk_start/2 not called"},_} = recv_chunk(Pid, <<>>),
- ok = ftp:recv_chunk_start(Pid, id2ftp(File,Config)),
- {ok, ReceivedContents, _Ncunks} = recv_chunk(Pid, <<>>),
- find_diff(ReceivedContents, Contents).
-
-recv_chunk_twice() ->
- [{doc, "Receive two files using chunk-wise."}].
-recv_chunk_twice(Config0) ->
- File1 = "big_file1.txt",
- File2 = "big_file2.txt",
- Contents1 = list_to_binary( lists:duplicate(1000, lists:seq(0,255)) ),
- Contents2 = crypto:strong_rand_bytes(1200),
- Config = set_state([reset, {mkfile,File1,Contents1}, {mkfile,File2,Contents2}], Config0),
- Pid = proplists:get_value(ftp, Config),
- {{error, "ftp:recv_chunk_start/2 not called"},_} = recv_chunk(Pid, <<>>),
- ok = ftp:recv_chunk_start(Pid, id2ftp(File1,Config)),
- {ok, ReceivedContents1, _Ncunks1} = recv_chunk(Pid, <<>>),
- ok = ftp:recv_chunk_start(Pid, id2ftp(File2,Config)),
- {ok, ReceivedContents2, _Ncunks2} = recv_chunk(Pid, <<>>),
- find_diff(ReceivedContents1, Contents1),
- find_diff(ReceivedContents2, Contents2).
-
-recv_chunk_three_times() ->
- [{doc, "Receive two files using chunk-wise."},
- {timetrap,{seconds,120}}].
-recv_chunk_three_times(Config0) ->
- File1 = "big_file1.txt",
- File2 = "big_file2.txt",
- File3 = "big_file3.txt",
- Contents1 = list_to_binary( lists:duplicate(1000, lists:seq(0,255)) ),
- Contents2 = crypto:strong_rand_bytes(1200),
- Contents3 = list_to_binary( lists:duplicate(1000, lists:seq(255,0,-1)) ),
-
- Config = set_state([reset, {mkfile,File1,Contents1}, {mkfile,File2,Contents2}, {mkfile,File3,Contents3}], Config0),
- Pid = proplists:get_value(ftp, Config),
- {{error, "ftp:recv_chunk_start/2 not called"},_} = recv_chunk(Pid, <<>>),
-
- ok = ftp:recv_chunk_start(Pid, id2ftp(File1,Config)),
- {ok, ReceivedContents1, Nchunks1} = recv_chunk(Pid, <<>>),
-
- ok = ftp:recv_chunk_start(Pid, id2ftp(File2,Config)),
- {ok, ReceivedContents2, _Nchunks2} = recv_chunk(Pid, <<>>),
-
- ok = ftp:recv_chunk_start(Pid, id2ftp(File3,Config)),
- {ok, ReceivedContents3, _Nchunks3} = recv_chunk(Pid, <<>>, 10000, 0, Nchunks1),
-
- find_diff(ReceivedContents1, Contents1),
- find_diff(ReceivedContents2, Contents2),
- find_diff(ReceivedContents3, Contents3).
-
-
-
-recv_chunk(Pid, Acc) ->
- recv_chunk(Pid, Acc, 0, 0, undefined).
-
-
-
-%% ExpectNchunks :: integer() | undefined
-recv_chunk(Pid, Acc, DelayMilliSec, N, ExpectNchunks) when N+1 < ExpectNchunks ->
- %% for all I in integer(), I < undefined
- recv_chunk1(Pid, Acc, DelayMilliSec, N, ExpectNchunks);
-
-recv_chunk(Pid, Acc, DelayMilliSec, N, ExpectNchunks) ->
- %% N >= ExpectNchunks-1
- timer:sleep(DelayMilliSec),
- recv_chunk1(Pid, Acc, DelayMilliSec, N, ExpectNchunks).
-
-
-recv_chunk1(Pid, Acc, DelayMilliSec, N, ExpectNchunks) ->
- ct:log("Call ftp:recv_chunk",[]),
- case ftp:recv_chunk(Pid) of
- ok -> {ok, Acc, N};
- {ok, Bin} -> recv_chunk(Pid, <<Acc/binary, Bin/binary>>, DelayMilliSec, N+1, ExpectNchunks);
- Error -> {Error, N}
- end.
-
-%%-------------------------------------------------------------------------
-type() ->
- [{doc,"Test that we can change btween ASCCI and binary transfer mode"}].
-type(Config) ->
- Pid = proplists:get_value(ftp, Config),
- ok = ftp:type(Pid, ascii),
- ok = ftp:type(Pid, binary),
- ok = ftp:type(Pid, ascii),
- {error, etype} = ftp:type(Pid, foobar).
-
-%%-------------------------------------------------------------------------
-quote(Config) ->
- Pid = proplists:get_value(ftp, Config),
- ["257 \""++_Rest] = ftp:quote(Pid, "pwd"), %% 257
- [_| _] = ftp:quote(Pid, "help"),
- %% This negativ test causes some ftp servers to hang. This test
- %% is not important for the client, so we skip it for now.
- %%["425 Can't build data connection: Connection refused."]
- %% = ftp:quote(Pid, "list"),
- ok.
-
-%%-------------------------------------------------------------------------
-progress_report_send() ->
- [{doc, "Test the option progress for ftp:send/[2,3]"}].
-progress_report_send(Config) when is_list(Config) ->
- ReportPid =
- spawn_link(?MODULE, progress_report_receiver_init, [self(), 1]),
- send(Config),
- receive
- {ReportPid, ok} ->
- ok
- end.
-
-%%-------------------------------------------------------------------------
-progress_report_recv() ->
- [{doc, "Test the option progress for ftp:recv/[2,3]"}].
-progress_report_recv(Config) when is_list(Config) ->
- ReportPid =
- spawn_link(?MODULE, progress_report_receiver_init, [self(), 3]),
- recv(Config),
- receive
- {ReportPid, ok} ->
- ok
- end.
-
-%%-------------------------------------------------------------------------
-
-not_owner() ->
- [{doc, "Test what happens if a process that not owns the connection tries "
- "to use it"}].
-not_owner(Config) when is_list(Config) ->
- Pid = proplists:get_value(ftp, Config),
-
- Parent = self(),
- OtherPid = spawn_link(
- fun() ->
- {error, not_connection_owner} = ftp:pwd(Pid),
- ftp:close(Pid),
- Parent ! {self(), ok}
- end),
- receive
- {OtherPid, ok} ->
- {ok, _} = ftp:pwd(Pid)
- end.
-
-
-%%-------------------------------------------------------------------------
-
-
-unexpected_call()->
- [{doc, "Test that behaviour of the ftp process if the api is abused"}].
-unexpected_call(Config) when is_list(Config) ->
- Flag = process_flag(trap_exit, true),
- Pid = proplists:get_value(ftp, Config),
-
- %% Serious programming fault, connetion will be shut down
- case (catch gen_server:call(Pid, {self(), foobar, 10}, infinity)) of
- {error, {connection_terminated, 'API_violation'}} ->
- ok;
- Unexpected1 ->
- exit({unexpected_result, Unexpected1})
- end,
- ct:sleep(500),
- undefined = process_info(Pid, status),
- process_flag(trap_exit, Flag).
-%%-------------------------------------------------------------------------
-
-unexpected_cast()->
- [{doc, "Test that behaviour of the ftp process if the api is abused"}].
-unexpected_cast(Config) when is_list(Config) ->
- Flag = process_flag(trap_exit, true),
- Pid = proplists:get_value(ftp, Config),
- %% Serious programming fault, connetion will be shut down
- gen_server:cast(Pid, {self(), foobar, 10}),
- ct:sleep(500),
- undefined = process_info(Pid, status),
- process_flag(trap_exit, Flag).
-%%-------------------------------------------------------------------------
-
-unexpected_bang()->
- [{doc, "Test that connection ignores unexpected bang"}].
-unexpected_bang(Config) when is_list(Config) ->
- Flag = process_flag(trap_exit, true),
- Pid = proplists:get_value(ftp, Config),
- %% Could be an innocent misstake the connection lives.
- Pid ! foobar,
- ct:sleep(500),
- {status, _} = process_info(Pid, status),
- process_flag(trap_exit, Flag).
-
-%%-------------------------------------------------------------------------
-
-clean_shutdown() ->
- [{doc, "Test that owning process that exits with reason "
- "'shutdown' does not cause an error message. OTP 6035"}].
-
-clean_shutdown(Config) ->
- Parent = self(),
- HelperPid = spawn(
- fun() ->
- ftp__open(Config, [verbose]),
- Parent ! ok,
- receive
- nothing -> ok
- end
- end),
- receive
- ok ->
- PrivDir = proplists:get_value(priv_dir, Config),
- LogFile = filename:join([PrivDir,"ticket_6035.log"]),
- error_logger:logfile({open, LogFile}),
- exit(HelperPid, shutdown),
- timer:sleep(2000),
- error_logger:logfile(close),
- case is_error_report_6035(LogFile) of
- true -> ok;
- false -> {fail, "Bad logfile"}
- end
- end.
-
-%%%----------------------------------------------------------------
-%%% Error codes not tested elsewhere
-
-error_elogin(Config0) ->
- Dir = "test",
- OldFile = "old.txt",
- NewFile = "new.txt",
- SrcDir = "data",
- File = "file.txt",
- Config = set_state([reset,
- {mkdir,Dir},
- {mkfile,OldFile,<<"Contents..">>},
- {mkfile,[SrcDir,File],<<"Contents..">>}], Config0),
-
- Pid = proplists:get_value(ftp, Config),
- ok = ftp:lcd(Pid, id2ftp(SrcDir,Config)),
- {error,elogin} = ftp:send(Pid, File),
- ok = ftp:lcd(Pid, id2ftp("",Config)),
- {error,elogin} = ftp:pwd(Pid),
- {error,elogin} = ftp:cd(Pid, id2ftp(Dir,Config)),
- {error,elogin} = ftp:rename(Pid,
- id2ftp(OldFile,Config),
- id2ftp(NewFile,Config)),
- ok.
-
-error_ehost(_Config) ->
- {error, ehost} = ftp:open("nohost.nodomain"),
- ok.
-
-%%--------------------------------------------------------------------
-%% Internal functions -----------------------------------------------
-%%--------------------------------------------------------------------
-
-make_cert_files(Alg1, Alg2, Prefix, Dir) ->
- CaInfo = {CaCert,_} = erl_make_certs:make_cert([{key,Alg1}]),
- {Cert,CertKey} = erl_make_certs:make_cert([{key,Alg2},{issuer,CaInfo}]),
- CaCertFile = filename:join(Dir, Prefix++"cacerts.pem"),
- CertFile = filename:join(Dir, Prefix++"cert.pem"),
- KeyFile = filename:join(Dir, Prefix++"key.pem"),
- der_to_pem(CaCertFile, [{'Certificate', CaCert, not_encrypted}]),
- der_to_pem(CertFile, [{'Certificate', Cert, not_encrypted}]),
- der_to_pem(KeyFile, [CertKey]),
- ok.
-
-der_to_pem(File, Entries) ->
- PemBin = public_key:pem_encode(Entries),
- file:write_file(File, PemBin).
-
-%%--------------------------------------------------------------------
-chk_file(Path=[C|_], ExpectedContents, Config) when 0<C,C=<255 ->
- chk_file([Path], ExpectedContents, Config);
-
-chk_file(PathList, ExpectedContents, Config) ->
- Path = filename:join(PathList),
- AbsPath = id2abs(Path,Config),
- case file:read_file(AbsPath) of
- {ok,ExpectedContents} ->
- true;
- {ok,ReadContents} ->
- {error,{diff,Pos,RC,LC}} = find_diff(ReadContents, ExpectedContents, 1),
- ct:log("Bad contents of ~p.~nGot:~n~p~nExpected:~n~p~nDiff at pos ~p ~nRead: ~p~nExp : ~p",
- [AbsPath,ReadContents,ExpectedContents,Pos,RC,LC]),
- ct:fail("Bad contents of ~p", [Path]);
- {error,Error} ->
- try begin
- {ok,CWD} = file:get_cwd(),
- ct:log("file:get_cwd()=~p~nfiles:~n~p",[CWD,file:list_dir(CWD)])
- end
- of _ -> ok
- catch _:_ ->ok
- end,
- ct:fail("Error reading ~p: ~p",[Path,Error])
- end.
-
-
-chk_no_file(Path=[C|_], Config) when 0<C,C=<255 ->
- chk_no_file([Path], Config);
-
-chk_no_file(PathList, Config) ->
- Path = filename:join(PathList),
- AbsPath = id2abs(Path,Config),
- case file:read_file(AbsPath) of
- {error,enoent} ->
- true;
- {ok,Contents} ->
- ct:log("File ~p exists although it shouldn't. Contents:~n~p",
- [AbsPath,Contents]),
- ct:fail("File exists: ~p", [Path]);
- {error,Error} ->
- ct:fail("Unexpected error reading ~p: ~p",[Path,Error])
- end.
-
-
-chk_dir(Path=[C|_], Config) when 0<C,C=<255 ->
- chk_dir([Path], Config);
-
-chk_dir(PathList, Config) ->
- Path = filename:join(PathList),
- AbsPath = id2abs(Path,Config),
- case file:read_file_info(AbsPath) of
- {ok, #file_info{type=directory}} ->
- true;
- {ok, #file_info{type=Type}} ->
- ct:fail("Expected dir ~p is a ~p",[Path,Type]);
- {error,Error} ->
- ct:fail("Expected dir ~p: ~p",[Path,Error])
- end.
-
-chk_no_dir(PathList, Config) ->
- Path = filename:join(PathList),
- AbsPath = id2abs(Path,Config),
- case file:read_file_info(AbsPath) of
- {error,enoent} ->
- true;
- {ok, #file_info{type=directory}} ->
- ct:fail("Dir ~p erroneously exists",[Path]);
- {ok, #file_info{type=Type}} ->
- ct:fail("~p ~p erroneously exists",[Type,Path]);
- {error,Error} ->
- ct:fail("Unexpected error for ~p: ~p",[Path,Error])
- end.
-
-%%--------------------------------------------------------------------
-find_executable(Config) ->
- search_executable(proplists:get_value(ftpservers, Config, ?default_ftp_servers)).
-
-
-search_executable([{Name,Paths,_StartCmd,_ChkUp,_StopCommand,_ConfigUpd,_Host,_Port}|Srvrs]) ->
- case os_find(Name,Paths) of
- false ->
- ct:log("~p not found",[Name]),
- search_executable(Srvrs);
- AbsName ->
- ct:comment("Found ~p",[AbsName]),
- {ok, {AbsName,_StartCmd,_ChkUp,_StopCommand,_ConfigUpd,_Host,_Port}}
- end;
-search_executable([]) ->
- false.
-
-
-os_find(Name, Paths) ->
- case os:find_executable(Name, Paths) of
- false -> os:find_executable(Name);
- AbsName -> AbsName
- end.
-
-%%%----------------------------------------------------------------
-start_ftpd(Config0) ->
- {AbsName,StartCmd,_ChkUp,_StopCommand,ConfigRewrite,Host,Port} =
- proplists:get_value(ftpd_data, Config0),
- case StartCmd(Config0, AbsName) of
- {ok,StartResult} ->
- Config = [{ftpd_host,Host},
- {ftpd_port,Port},
- {ftpd_start_result,StartResult} | ConfigRewrite(Config0)],
- try
- ftp__close(ftp__open(Config,[verbose]))
- of
- Config1 when is_list(Config1) ->
- ct:log("Usuable ftp server ~p started on ~p:~p",[AbsName,Host,Port]),
- Config
- catch
- Class:Exception ->
- ct:log("Ftp server ~p started on ~p:~p but is unusable:~n~p:~p",
- [AbsName,Host,Port,Class,Exception]),
- {skip, [AbsName," started but unusuable"]}
- end;
- {error,Msg} ->
- {skip, [AbsName," not started: ",Msg]}
- end.
-
-stop_ftpd(Config) ->
- {_Name,_StartCmd,_ChkUp,StopCommand,_ConfigUpd,_Host,_Port} = proplists:get_value(ftpd_data, Config),
- StopCommand(proplists:get_value(ftpd_start_result,Config)).
-
-ps_ftpd(Config) ->
- {_Name,_StartCmd,ChkUp,_StopCommand,_ConfigUpd,_Host,_Port} = proplists:get_value(ftpd_data, Config),
- ct:log( ChkUp(proplists:get_value(ftpd_start_result,Config)) ).
-
-
-ftpd_running(Config) ->
- {_Name,_StartCmd,ChkUp,_StopCommand,_ConfigUpd,_Host,_Port} = proplists:get_value(ftpd_data, Config),
- ChkUp(proplists:get_value(ftpd_start_result,Config)).
-
-ftp__open(Config, Options) ->
- Host = proplists:get_value(ftpd_host,Config),
- Port = proplists:get_value(ftpd_port,Config),
- ct:log("Host=~p, Port=~p",[Host,Port]),
- {ok,Pid} = ftp:open(Host, [{port,Port} | Options]),
- [{ftp,Pid}|Config].
-
-ftp__close(Config) ->
- ok = ftp:close(proplists:get_value(ftp,Config)),
- Config.
-
-split(Cs) -> string:tokens(Cs, "\r\n").
-
-find_diff(Bin1, Bin2) ->
- case find_diff(Bin1, Bin2, 1) of
- {error, {diff,Pos,RC,LC}} ->
- ct:log("Contents differ at position ~p.~nOp1: ~p~nOp2: ~p",[Pos,RC,LC]),
- ct:fail("Contents differ at pos ~p",[Pos]);
- Other ->
- Other
- end.
-
-find_diff(A, A, _) -> true;
-find_diff(<<H,T1/binary>>, <<H,T2/binary>>, Pos) -> find_diff(T1, T2, Pos+1);
-find_diff(RC, LC, Pos) -> {error, {diff, Pos, RC, LC}}.
-
-set_state(Ops, Config) when is_list(Ops) -> lists:foldl(fun set_state/2, Config, Ops);
-
-set_state(reset, Config) ->
- rm('*', id2abs("",Config)),
- PrivDir = proplists:get_value(priv_dir,Config),
- file:set_cwd(PrivDir),
- ftp:lcd(proplists:get_value(ftp,Config),PrivDir),
- set_state({mkdir,""},Config);
-set_state({mkdir,Id}, Config) ->
- Abs = id2abs(Id, Config),
- mk_path(Abs),
- file:make_dir(Abs),
- Config;
-set_state({mkfile,Id,Contents}, Config) ->
- Abs = id2abs(Id, Config),
- mk_path(Abs),
- ok = file:write_file(Abs, Contents),
- Config.
-
-mk_path(Abs) -> lists:foldl(fun mk_path/2, [], filename:split(filename:dirname(Abs))).
-
-mk_path(F, Pfx) ->
- case file:read_file_info(AbsName=filename:join(Pfx,F)) of
- {ok,#file_info{type=directory}} ->
- AbsName;
- {error,eexist} ->
- AbsName;
- {error,enoent} ->
- ok = file:make_dir(AbsName),
- AbsName
- end.
-
-rm('*', Pfx) ->
- {ok,Fs} = file:list_dir(Pfx),
- lists:foreach(fun(F) -> rm(F, Pfx) end, Fs);
-rm(F, Pfx) ->
- case file:read_file_info(AbsName=filename:join(Pfx,F)) of
- {ok,#file_info{type=directory}} ->
- {ok,Fs} = file:list_dir(AbsName),
- lists:foreach(fun(F1) -> rm(F1,AbsName) end, Fs),
- ok = file:del_dir(AbsName);
-
- {ok,#file_info{type=regular}} ->
- ok = file:delete(AbsName);
-
- {error,enoent} ->
- ok
- end.
-
-id2abs(Id, Conf) -> filename:join(proplists:get_value(priv_dir,Conf),ids(Id)).
-id2ftp(Id, Conf) -> (proplists:get_value(id2ftp,Conf))(ids(Id)).
-id2ftp_result(Id, Conf) -> (proplists:get_value(id2ftp_result,Conf))(ids(Id)).
-
-ids([[_|_]|_]=Ids) -> filename:join(Ids);
-ids(Id) -> Id.
-
-
-is_expected_absName(Id, File, Conf) -> File = (proplists:get_value(id2abs,Conf))(Id).
-is_expected_ftpInName(Id, File, Conf) -> File = (proplists:get_value(id2ftp,Conf))(Id).
-is_expected_ftpOutName(Id, File, Conf) -> File = (proplists:get_value(id2ftp_result,Conf))(Id).
-
-
-%%%----------------------------------------------------------------
-%%% Help functions for the option '{progress,Progress}'
-%%%
-
-%%%----------------
-%%% Callback:
-
-progress(#progress{} = P, _File, {file_size, Total} = M) ->
- ct:pal("Progress: ~p",[M]),
- progress_report_receiver ! start,
- P#progress{total = Total};
-
-progress(#progress{current = Current} = P, _File, {transfer_size, 0} = M) ->
- ct:pal("Progress: ~p",[M]),
- progress_report_receiver ! finish,
- case P#progress.total of
- unknown -> P;
- Current -> P;
- Total -> ct:fail({error, {progress, {total,Total}, {current,Current}}}),
- P
- end;
-
-progress(#progress{current = Current} = P, _File, {transfer_size, Size} = M) ->
- ct:pal("Progress: ~p",[M]),
- progress_report_receiver ! update,
- P#progress{current = Current + Size};
-
-progress(P, _File, M) ->
- ct:pal("Progress **** Strange: ~p",[M]),
- P.
-
-
-%%%----------------
-%%% Help process that counts the files transferred:
-
-progress_report_receiver_init(Parent, N) ->
- register(progress_report_receiver, self()),
- progress_report_receiver_expect_N_files(Parent, N).
-
-progress_report_receiver_expect_N_files(_Parent, 0) ->
- ct:pal("progress_report got all files!", []);
-progress_report_receiver_expect_N_files(Parent, N) ->
- ct:pal("progress_report expects ~p more files",[N]),
- receive
- start -> ok
- end,
- progress_report_receiver_loop(Parent, N-1).
-
-
-progress_report_receiver_loop(Parent, N) ->
- ct:pal("progress_report expect update | finish. N = ~p",[N]),
- receive
- update ->
- ct:pal("progress_report got update",[]),
- progress_report_receiver_loop(Parent, N);
- finish ->
- ct:pal("progress_report got finish, send ~p to ~p",[{self(),ok}, Parent]),
- Parent ! {self(), ok},
- progress_report_receiver_expect_N_files(Parent, N)
- end.
-
-%%%----------------------------------------------------------------
-%%% Help functions for bug OTP-6035
-
-is_error_report_6035(LogFile) ->
- case file:read_file(LogFile) of
- {ok, Bin} ->
- nomatch =/= binary:match(Bin, <<"=ERROR REPORT====">>);
- _ ->
- false
- end.
-
diff --git a/lib/inets/test/ftp_SUITE_data/ftpd_hosts.skel b/lib/inets/test/ftp_SUITE_data/ftpd_hosts.skel
deleted file mode 100644
index 75096ce687..0000000000
--- a/lib/inets/test/ftp_SUITE_data/ftpd_hosts.skel
+++ /dev/null
@@ -1,18 +0,0 @@
-%% Add a host name in the appropriate list
-%% Each "platform" contains a list of hostnames (a string) that can
-%% be used for testing the ftp client.
-%% The definition below are an example!!
-[{solaris8_sparc, ["solaris8_sparc_dummy1", "solaris8_sparc_dummy2"]},
- {solaris9_sparc, ["solaris9_sparc_dummy1"]},
- {solaris10_sparc, ["solaris10_sparc_dummy1"]},
- {solaris10_x86, ["solaris10_x86_dummy1", "solaris10_x86_dummy2"]},
- {linux_x86, ["linux_x86_dummy1", "linux_x86_dummy2"]},
- {linux_ppc, ["linux_ppc_dummy1"]},
- {macosx_ppc, ["macosx_ppc_dummy1"]},
- {macosx_x86, ["macosx_x86_dummy1", "macosx_x86_dummy2"]},
- {openbsd_x86, []},
- {freebsd_x86, ["freebsd_x86_dummy1"]},
- {netbsd_x86, []},
- {windows_xp, []},
- {windows_2003_server, ["win2003_dummy1"]},
- {ticket_test, ["solaris8_x86_dummy1", "linux_x86_dummy1"]}].
diff --git a/lib/inets/test/ftp_SUITE_data/vsftpd.conf b/lib/inets/test/ftp_SUITE_data/vsftpd.conf
deleted file mode 100644
index a5584f5916..0000000000
--- a/lib/inets/test/ftp_SUITE_data/vsftpd.conf
+++ /dev/null
@@ -1,26 +0,0 @@
-
-###
-### Some parameters are given in the vsftpd start command.
-###
-### Typical command-line paramters are such that has a file path
-### component like cert files.
-###
-
-
-listen=YES
-listen_port=9999
-run_as_launching_user=YES
-ssl_enable=YES
-allow_anon_ssl=YES
-
-background=YES
-
-write_enable=YES
-anonymous_enable=YES
-anon_upload_enable=YES
-anon_mkdir_write_enable=YES
-anon_other_write_enable=YES
-anon_world_readable_only=NO
-
-### Shouldn't be necessary....
-require_ssl_reuse=NO
diff --git a/lib/inets/test/ftp_format_SUITE.erl b/lib/inets/test/ftp_format_SUITE.erl
deleted file mode 100644
index 95d594a44b..0000000000
--- a/lib/inets/test/ftp_format_SUITE.erl
+++ /dev/null
@@ -1,328 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2005-2016. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%% http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-%%
-%% %CopyrightEnd%
-%%
-%%
--module(ftp_format_SUITE).
--author('[email protected]').
-
--include_lib("common_test/include/ct.hrl").
--include("ftp_internal.hrl").
-
-%% Note: This directive should only be used in test suites.
--compile(export_all).
-
-suite() ->
- [{ct_hooks,[ts_install_cth]},
- {timetrap,{seconds,5}}
- ].
-
-all() ->
- [{group, ftp_response}, format_error].
-
-groups() ->
- [{ftp_response, [],
- [ftp_150, ftp_200, ftp_220, ftp_226, ftp_257, ftp_331,
- ftp_425, ftp_other_status_codes, ftp_multiple_lines_status_in_msg,
- ftp_multiple_lines, ftp_multipel_ctrl_messages]}].
-
-init_per_suite(Config) ->
- Config.
-
-end_per_suite(_Config) ->
- ok.
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
-init_per_testcase(_, Config) ->
- Config.
-end_per_testcase(_, _) ->
- ok.
-
-%%-------------------------------------------------------------------------
-%% Test cases starts here.
-%%-------------------------------------------------------------------------
-
-ftp_150() ->
- [{doc, "Especially check that respons can be devided in a random place."}].
-ftp_150(Config) when is_list(Config) ->
- FtpResponse = ["150 ASCII data conn", "ection for /bin/ls ",
- "(134.138.177", ".89,50434) (0 bytes).\r\n"],
-
- "150 ASCII data connection for /bin/ls "
- "(134.138.177.89,50434) (0 bytes).\r\n" = Msg =
- parse(ftp_response, parse_lines, [[], start], FtpResponse),
- {pos_prel, _} = ftp_response:interpret(Msg).
-
-ftp_200() ->
- [{doc, "Especially check that respons can be devided after the first status "
- "code character and in the end delimiter."}].
-ftp_200(Config) when is_list(Config) ->
- FtpResponse = ["2", "00 PORT command successful.", [?CR], [?LF]],
-
- "200 PORT command successful.\r\n" = Msg =
- parse(ftp_response, parse_lines, [[], start], FtpResponse),
- {pos_compl, _} = ftp_response:interpret(Msg),
- ok.
-
-ftp_220() ->
- [{doc, "Especially check that respons can be devided after the "
- "first with space "}].
-ftp_220(Config) when is_list(Config) ->
- FtpResponse = ["220 ","fingon FTP server (SunOS 5.8) ready.\r\n"],
-
- "220 fingon FTP server (SunOS 5.8) ready.\r\n" = Msg =
- parse(ftp_response, parse_lines, [[], start], FtpResponse),
- {pos_compl, _} = ftp_response:interpret(Msg),
- ok.
-
-ftp_226() ->
- [{doc, "Especially check that respons can be devided after second status code"
- " character and in the end delimiter."}].
-ftp_226(Config) when is_list(Config) ->
- FtpResponse = ["22" "6 Transfer complete.\r", [?LF]],
-
- "226 Transfer complete.\r\n" = Msg =
- parse(ftp_response, parse_lines, [[], start], FtpResponse),
- {pos_compl, _} = ftp_response:interpret(Msg),
- ok.
-
-ftp_257() ->
- [{doc, "Especially check that quoted chars do not cause a problem."}].
-ftp_257(Config) when is_list(Config) ->
- FtpResponse = ["257 \"/\" is current directory.\r\n"],
-
- "257 \"/\" is current directory.\r\n" = Msg =
- parse(ftp_response, parse_lines, [[], start], FtpResponse),
- {pos_compl, _} = ftp_response:interpret(Msg),
- ok.
-
-ftp_331() ->
- [{doc, "Especially check that respons can be devided after the third status "
- " status code character."}].
-ftp_331(Config) when is_list(Config) ->
- %% Brake before white space after code
- FtpResponse =
- ["331"," Guest login ok, send ient as password.\r\n"],
-
- "331 Guest login ok, send ient as password.\r\n" = Msg =
- parse(ftp_response, parse_lines, [[], start], FtpResponse),
- {pos_interm, _} = ftp_response:interpret(Msg),
- ok.
-
-ftp_425() ->
- [{doc, "Especially check a message that was received in only one part."}].
-ftp_425(Config) when is_list(Config) ->
- FtpResponse =
- ["425 Can't build data connection: Connection refused.\r\n"],
-
- "425 Can't build data connection: Connection refused.\r\n"
- = Msg = parse(ftp_response, parse_lines, [[], start], FtpResponse),
- {trans_neg_compl, _} = ftp_response:interpret(Msg),
- ok.
-
-ftp_multiple_lines_status_in_msg() ->
- [{doc, "check that multiple lines gets parsed correct, even if we have "
- " the status code within the msg being sent"}].
-ftp_multiple_lines_status_in_msg(Config) when is_list(Config) ->
- ML = "230-User usr-230 is logged in\r\n" ++
- "230 OK. Current directory is /\r\n",
- {ok, ML, <<>>} = ftp_response:parse_lines(list_to_binary(ML), [], start),
- ok.
-
-ftp_multiple_lines() ->
- [{doc, "Especially check multiple lines devided in significant places"}].
-ftp_multiple_lines(Config) when is_list(Config) ->
- FtpResponse = ["21", "4","-The",
- " following commands are recognized:\r\n"
- " USER EPRT STRU MAIL* ALLO CWD",
- " STAT* XRMD \r\n"
- " PASS LPRT MODE MSND* "
- " REST* XCWD HELP PWD ", [?CRLF],
- " ACCT* EPSV RETR MSOM* RNFR LIST "
- " NOOP XPWD \r\n",
- " REIN* LPSV STOR MSAM* RNTO NLST "
- " MKD CDUP \r\n"
- " QUIT PASV APPE MRSQ* ABOR SITE* "
- " XMKD XCUP \r\n"
- " PORT TYPE MLFL* MRCP* DELE SYST "
- " RMD STOU \r\n"
- "214 (*'s => unimplemented)", [?CR], [?LF]],
-
-
- FtpResponse1 = ["214-", "The",
- " following commands are recognized:\r\n"
- " USER EPRT STRU MAIL* ALLO CWD",
- " STAT* XRMD \r\n"
- " PASS LPRT MODE MSND* "
- " REST* XCWD HELP PWD ", [?CRLF],
- " ACCT* EPSV RETR MSOM* RNFR LIST "
- " NOOP XPWD \r\n",
- " REIN* LPSV STOR MSAM* RNTO NLST "
- " MKD CDUP \r\n"
- " QUIT PASV APPE MRSQ* ABOR SITE* "
- " XMKD XCUP \r\n"
- " PORT TYPE MLFL* MRCP* DELE SYST "
- " RMD STOU \r\n"
- "2", "14 (*'s => unimplemented)", [?CR], [?LF]],
-
- FtpResponse2 = ["214-", "The",
- " following commands are recognized:\r\n"
- " USER EPRT STRU MAIL* ALLO CWD",
- " STAT* XRMD \r\n"
- " PASS LPRT MODE MSND* "
- " REST* XCWD HELP PWD ", [?CRLF],
- " ACCT* EPSV RETR MSOM* RNFR LIST "
- " NOOP XPWD \r\n",
- " REIN* LPSV STOR MSAM* RNTO NLST "
- " MKD CDUP \r\n"
- " QUIT PASV APPE MRSQ* ABOR SITE* "
- " XMKD XCUP \r\n"
- " PORT TYPE MLFL* MRCP* DELE SYST "
- " RMD STOU \r\n"
- "21", "4"," (*'s => unimplemented)", [?CR], [?LF]],
-
- MultiLineResultStr =
- "214-The following commands are recognized:\r\n"
- " USER EPRT STRU MAIL* ALLO CWD STAT* "
- "XRMD \r\n"
- " PASS LPRT MODE MSND* REST* XCWD HELP "
- "PWD \r\n"
- " ACCT* EPSV RETR MSOM* RNFR LIST NOOP "
- "XPWD \r\n"
- " REIN* LPSV STOR MSAM* RNTO NLST MKD "
- "CDUP \r\n"
- " QUIT PASV APPE MRSQ* ABOR SITE* XMKD "
- "XCUP \r\n"
- " PORT TYPE MLFL* MRCP* DELE SYST RMD "
- "STOU \r\n"
- "214 (*'s => unimplemented)\r\n",
-
- MultiLineResultStr =
- parse(ftp_response, parse_lines, [[], start], FtpResponse),
- {pos_compl, _} = ftp_response:interpret(MultiLineResultStr),
-
- MultiLineResultStr = parse(ftp_response, parse_lines, [[], start],
- FtpResponse1),
-
- MultiLineResultStr = parse(ftp_response, parse_lines, [[], start],
- FtpResponse2),
- ok.
-
-ftp_other_status_codes() ->
- [{doc, "Check that other valid status codes, than the ones above, are handled"
- "by ftp_response:interpret/1. Note there are som ftp status codes"
- "that will not be received with the current ftp instruction support,"
- "they are not included here."}].
-ftp_other_status_codes(Config) when is_list(Config) ->
-
- %% 1XX
- {pos_prel, _ } = ftp_response:interpret("120 Foobar\r\n"),
-
- %% 2XX
- {pos_compl, _ } = ftp_response:interpret("202 Foobar\r\n"),
- {pos_compl, _ } = ftp_response:interpret("221 Foobar\r\n"),
- {pos_compl, _ } = ftp_response:interpret("227 Foobar\r\n"),
- {pos_compl, _ } = ftp_response:interpret("230 Foobar\r\n"),
- {pos_compl, _ } = ftp_response:interpret("250 Foobar\r\n"),
-
- %% 3XX
- {pos_interm_acct, _ } = ftp_response:interpret("332 Foobar\r\n"),
- {pos_interm, _ } = ftp_response:interpret("350 Foobar\r\n"),
-
- %% 4XX
- {trans_neg_compl, _ } = ftp_response:interpret("421 Foobar\r\n"),
- {trans_neg_compl, _ } = ftp_response:interpret("426 Foobar\r\n"),
- {enofile, _ } = ftp_response:interpret("450 Foobar\r\n"),
- {trans_neg_compl, _ } = ftp_response:interpret("451 Foobar\r\n"),
- {etnospc, _ } = ftp_response:interpret("452 Foobar\r\n"),
-
- %% 5XX
- {perm_neg_compl, _ } = ftp_response:interpret("500 Foobar\r\n"),
- {perm_neg_compl, _ } = ftp_response:interpret("501 Foobar\r\n"),
- {perm_neg_compl, _ } = ftp_response:interpret("503 Foobar\r\n"),
- {perm_neg_compl, _ } = ftp_response:interpret("504 Foobar\r\n"),
- {elogin, _ } = ftp_response:interpret("530 Foobar\r\n"),
- {perm_neg_compl, _ } = ftp_response:interpret("532 Foobar\r\n"),
- {epath, _ } = ftp_response:interpret("550 Foobar\r\n"),
- {epnospc, _ } = ftp_response:interpret("552 Foobar\r\n"),
- {efnamena, _ } = ftp_response:interpret("553 Foobar\r\n"),
- ok.
-
-ftp_multipel_ctrl_messages() ->
- [{doc, "The ftp server may send more than one control message as a reply,"
- "check that they are handled one at the time."}].
-ftp_multipel_ctrl_messages(Config) when is_list(Config) ->
- FtpResponse = ["200 PORT command successful.\r\n200 Foobar\r\n"],
-
- {"200 PORT command successful.\r\n" = Msg, NextMsg} =
- parse(ftp_response, parse_lines, [[], start], FtpResponse),
- {pos_compl, _} = ftp_response:interpret(Msg),
- NewMsg = parse(ftp_response, parse_lines, [[], start], NextMsg),
- {pos_compl, _} = ftp_response:interpret(NewMsg),
- ok.
-
-
-%%-------------------------------------------------------------------------
-format_error(Config) when is_list(Config) ->
- "Synchronisation error during chunk sending." =
- ftp:formaterror(echunk),
- "Session has been closed." = ftp:formaterror(eclosed),
- "Connection to remote server prematurely closed." =
- ftp:formaterror(econn),
- "File or directory already exists." = ftp:formaterror(eexists),
- "Host not found, FTP server not found, or connection rejected." =
- ftp:formaterror(ehost),
- "User not logged in." = ftp:formaterror(elogin),
- "Term is not a binary." = ftp:formaterror(enotbinary),
- "No such file or directory, already exists, or permission denied."
- = ftp:formaterror(epath),
- "No such type." = ftp:formaterror(etype),
- "User name or password not valid." = ftp:formaterror(euser),
- "Insufficient storage space in system." = ftp:formaterror(etnospc),
- "Exceeded storage allocation (for current directory or dataset)."
- = ftp:formaterror(epnospc),
- "File name not allowed." = ftp:formaterror(efnamena),
- "Unknown error: foobar" = ftp:formaterror({error, foobar}).
-
-%%--------------------------------------------------------------------
-%%% Internal functions
-%%--------------------------------------------------------------------
-parse(Module, Function, Args, Bin) when is_binary(Bin) ->
- parse(Module, Function, Args, [binary_to_list(Bin)]);
-
-parse(Module, Function, [AccLines, StatusCode], [Data | Rest]) ->
- case Module:Function(list_to_binary(Data), AccLines, StatusCode) of
- {ok, Result, <<>>} ->
- Result;
- {ok, Result, Next} ->
- {Result, Next};
- {continue, {NewData, NewAccLines, NewStatusCode}} ->
- case Rest of
- [] ->
- ct:fail({wrong_input, Data, Rest});
- [_ | _] ->
- parse(Module, Function, [NewAccLines, NewStatusCode],
- [binary_to_list(NewData) ++ hd(Rest) | tl(Rest)])
- end
- end.
diff --git a/lib/inets/test/ftp_internal.hrl b/lib/inets/test/ftp_internal.hrl
deleted file mode 120000
index af57081f14..0000000000
--- a/lib/inets/test/ftp_internal.hrl
+++ /dev/null
@@ -1 +0,0 @@
-../src/ftp/ftp_internal.hrl \ No newline at end of file
diff --git a/lib/inets/test/ftp_property_test_SUITE.erl b/lib/inets/test/ftp_property_test_SUITE.erl
deleted file mode 100644
index b314882296..0000000000
--- a/lib/inets/test/ftp_property_test_SUITE.erl
+++ /dev/null
@@ -1,53 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2004-2016. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%% http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-%%
-%% %CopyrightEnd%
-%%
-%%
-
-%%% Run like this:
-%%% ct:run_test([{suite,"ftp_property_test_SUITE"}, {logdir,"/ldisk/OTP/LOG"}]).
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%%% %%%
-%%% WARNING %%%
-%%% %%%
-%%% This is experimental code which may be changed or removed %%%
-%%% anytime without any warning. %%%
-%%% %%%
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
--module(ftp_property_test_SUITE).
-
--compile(export_all).
-
--include_lib("common_test/include/ct.hrl").
-
-all() -> [prop_ftp_case].
-
-
-init_per_suite(Config) ->
- inets:start(),
- ct_property_test:init_per_suite(Config).
-
-
-%%%---- test case
-prop_ftp_case(Config) ->
- ct_property_test:quickcheck(
- ftp_simple_client_server:prop_ftp(Config),
- Config
- ).
diff --git a/lib/inets/test/inets_SUITE.erl b/lib/inets/test/inets_SUITE.erl
index 1abd96a228..12db2479d2 100644
--- a/lib/inets/test/inets_SUITE.erl
+++ b/lib/inets/test/inets_SUITE.erl
@@ -42,7 +42,6 @@ groups() ->
[start_inets,
start_httpc,
start_httpd,
- start_ftpc,
start_tftpd
]},
{app_test, [], [app, appup]}].
@@ -298,44 +297,6 @@ start_httpd(Config) when is_list(Config) ->
%%-------------------------------------------------------------------------
-start_ftpc(doc) ->
- [{doc, "Start/stop of ftpc service"}];
-start_ftpc(Config0) when is_list(Config0) ->
- process_flag(trap_exit, true),
- ok = inets:start(),
- case ftp_SUITE:init_per_suite(Config0) of
- {skip, _} = Skip ->
- Skip;
- Config ->
- FtpdHost = proplists:get_value(ftpd_host,Config),
- {ok, Pid0} = inets:start(ftpc, [{host, FtpdHost}]),
- Pids0 = [ServicePid || {_, ServicePid} <-
- inets:services()],
- true = lists:member(Pid0, Pids0),
- [_|_] = inets:services_info(),
- inets:stop(ftpc, Pid0),
- ct:sleep(100),
- Pids1 = [ServicePid || {_, ServicePid} <-
- inets:services()],
- false = lists:member(Pid0, Pids1),
- {ok, Pid1} =
- inets:start(ftpc, [{host, FtpdHost}], stand_alone),
- Pids2 = [ServicePid || {_, ServicePid} <-
- inets:services()],
- false = lists:member(Pid1, Pids2),
- ok = inets:stop(stand_alone, Pid1),
- receive
- {'EXIT', Pid1, shutdown} ->
- ok
- after 100 ->
- ct:fail(stand_alone_not_shutdown)
- end,
- ok = inets:stop(),
- catch ftp_SUITE:end_per_SUITE(Config)
- end.
-
-%%-------------------------------------------------------------------------
-
start_tftpd() ->
[{doc, "Start/stop of tfpd service"}].
start_tftpd(Config) when is_list(Config) ->
diff --git a/lib/inets/test/inets_sup_SUITE.erl b/lib/inets/test/inets_sup_SUITE.erl
index 1e664337e6..d36d4dc53e 100644
--- a/lib/inets/test/inets_sup_SUITE.erl
+++ b/lib/inets/test/inets_sup_SUITE.erl
@@ -32,7 +32,7 @@ suite() ->
].
all() ->
- [default_tree, ftpc_worker, tftpd_worker,
+ [default_tree, tftpd_worker,
httpd_config, httpd_subtree, httpd_subtree_profile,
httpc_subtree].
@@ -147,13 +147,11 @@ default_tree() ->
"in the default case."}].
default_tree(Config) when is_list(Config) ->
TopSupChildren = supervisor:which_children(inets_sup),
- 4 = length(TopSupChildren),
+ 3 = length(TopSupChildren),
{value, {httpd_sup, _, supervisor,[httpd_sup]}} =
lists:keysearch(httpd_sup, 1, TopSupChildren),
{value, {httpc_sup, _,supervisor,[httpc_sup]}} =
lists:keysearch(httpc_sup, 1, TopSupChildren),
- {value, {ftp_sup,_,supervisor,[ftp_sup]}} =
- lists:keysearch(ftp_sup, 1, TopSupChildren),
{value, {tftp_sup,_,supervisor,[tftp_sup]}} =
lists:keysearch(tftp_sup, 1, TopSupChildren),
@@ -163,8 +161,6 @@ default_tree(Config) when is_list(Config) ->
{value, {httpc_handler_sup,_, supervisor, [httpc_handler_sup]}} =
lists:keysearch(httpc_handler_sup, 1, HttpcSupChildren),
- [] = supervisor:which_children(ftp_sup),
-
[] = supervisor:which_children(httpd_sup),
%% Default profile
@@ -177,30 +173,6 @@ default_tree(Config) when is_list(Config) ->
ok.
-ftpc_worker() ->
- [{doc, "Makes sure the ftp worker processes are added and removed "
- "appropriatly to/from the supervison tree."}].
-ftpc_worker(Config0) when is_list(Config0) ->
- [] = supervisor:which_children(ftp_sup),
- case ftp_SUITE:init_per_suite(Config0) of
- {skip, _} = Skip ->
- Skip;
- Config ->
- FtpdHost = proplists:get_value(ftpd_host,Config),
- {ok, Pid} = inets:start(ftpc, [{host, FtpdHost}]),
- case supervisor:which_children(ftp_sup) of
- [{_,_, worker, [ftp]}] ->
- inets:stop(ftpc, Pid),
- ct:sleep(5000),
- [] = supervisor:which_children(ftp_sup),
- catch ftp_SUITE:end_per_SUITE(Config),
- ok;
- Children ->
- catch ftp_SUITE:end_per_SUITE(Config),
- exit({unexpected_children, Children})
- end
- end.
-
tftpd_worker() ->
[{doc, "Makes sure the tftp sub tree is correct."}].
tftpd_worker(Config) when is_list(Config) ->