summaryrefslogtreecommitdiffstats
path: root/tests/auto/qftp
diff options
context:
space:
mode:
Diffstat (limited to 'tests/auto/qftp')
-rw-r--r--tests/auto/qftp/.gitattributes1
-rw-r--r--tests/auto/qftp/.gitignore2
-rw-r--r--tests/auto/qftp/qftp.pro20
-rw-r--r--tests/auto/qftp/rfc3252.txt899
-rw-r--r--tests/auto/qftp/tst_qftp.cpp2168
5 files changed, 3090 insertions, 0 deletions
diff --git a/tests/auto/qftp/.gitattributes b/tests/auto/qftp/.gitattributes
new file mode 100644
index 0000000000..e04709aa2e
--- /dev/null
+++ b/tests/auto/qftp/.gitattributes
@@ -0,0 +1 @@
+rfc3252.txt -crlf
diff --git a/tests/auto/qftp/.gitignore b/tests/auto/qftp/.gitignore
new file mode 100644
index 0000000000..7a4845df05
--- /dev/null
+++ b/tests/auto/qftp/.gitignore
@@ -0,0 +1,2 @@
+tst_qftp
+tst_QFtp_activeMode_inittab
diff --git a/tests/auto/qftp/qftp.pro b/tests/auto/qftp/qftp.pro
new file mode 100644
index 0000000000..ac1702e715
--- /dev/null
+++ b/tests/auto/qftp/qftp.pro
@@ -0,0 +1,20 @@
+load(qttest_p4)
+SOURCES += tst_qftp.cpp
+
+
+QT = core network
+
+wince*: {
+ addFiles.files = rfc3252.txt
+ addFiles.path = .
+ DEPLOYMENT += addFiles
+ DEFINES += SRCDIR=\\\"\\\"
+} else:symbian {
+ addFiles.files = rfc3252.txt
+ addFiles.path = .
+ DEPLOYMENT += addFiles
+ TARGET.EPOCHEAPSIZE="0x100 0x1000000"
+ TARGET.CAPABILITY = NetworkServices
+} else {
+ DEFINES += SRCDIR=\\\"$$PWD/\\\"
+}
diff --git a/tests/auto/qftp/rfc3252.txt b/tests/auto/qftp/rfc3252.txt
new file mode 100644
index 0000000000..b80c61bf0a
--- /dev/null
+++ b/tests/auto/qftp/rfc3252.txt
@@ -0,0 +1,899 @@
+
+
+
+
+
+
+Network Working Group H. Kennedy
+Request for Comments: 3252 Mimezine
+Category: Informational 1 April 2002
+
+
+ Binary Lexical Octet Ad-hoc Transport
+
+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 (2002). All Rights Reserved.
+
+Abstract
+
+ This document defines a reformulation of IP and two transport layer
+ protocols (TCP and UDP) as XML applications.
+
+1. Introduction
+
+1.1. Overview
+
+ This document describes the Binary Lexical Octet Ad-hoc Transport
+ (BLOAT): a reformulation of a widely-deployed network-layer protocol
+ (IP [RFC791]), and two associated transport layer protocols (TCP
+ [RFC793] and UDP [RFC768]) as XML [XML] applications. It also
+ describes methods for transporting BLOAT over Ethernet and IEEE 802
+ networks as well as encapsulating BLOAT in IP for gatewaying BLOAT
+ across the public Internet.
+
+1.2. Motivation
+
+ The wild popularity of XML as a basis for application-level protocols
+ such as the Blocks Extensible Exchange Protocol [RFC3080], the Simple
+ Object Access Protocol [SOAP], and Jabber [JABBER] prompted
+ investigation into the possibility of extending the use of XML in the
+ protocol stack. Using XML at both the transport and network layer in
+ addition to the application layer would provide for an amazing amount
+ of power and flexibility while removing dependencies on proprietary
+ and hard-to-understand binary protocols. This protocol unification
+ would also allow applications to use a single XML parser for all
+ aspects of their operation, eliminating developer time spent figuring
+ out the intricacies of each new protocol, and moving the hard work of
+
+
+
+
+Kennedy Informational [Page 1]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ parsing to the XML toolset. The use of XML also mitigates concerns
+ over "network vs. host" byte ordering which is at the root of many
+ network application bugs.
+
+1.3. Relation to Existing Protocols
+
+ The reformulations specified in this RFC follow as closely as
+ possible the spirit of the RFCs on which they are based, and so MAY
+ contain elements or attributes that would not be needed in a pure
+ reworking (e.g. length attributes, which are implicit in XML.)
+
+ The layering of network and transport protocols are maintained in
+ this RFC despite the optimizations that could be made if the line
+ were somewhat blurred (i.e. merging TCP and IP into a single, larger
+ element in the DTD) in order to foster future use of this protocol as
+ a basis for reformulating other protocols (such as ICMP.)
+
+ Other than the encoding, the behavioral aspects of each of the
+ existing protocols remain unchanged. Routing, address spaces, TCP
+ congestion control, etc. behave as specified in the extant standards.
+ Adapting to new standards and experimental algorithm heuristics for
+ improving performance will become much easier once the move to BLOAT
+ has been completed.
+
+1.4. Requirement Levels
+
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+ "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
+ document are to be interpreted as described in BCP 14, RFC 2119
+ [RFC2119].
+
+2. IPoXML
+
+ This protocol MUST be implemented to be compliant with this RFC.
+ IPoXML is the root protocol REQUIRED for effective use of TCPoXML
+ (section 3.) and higher-level application protocols.
+
+ The DTD for this document type can be found in section 7.1.
+
+ The routing of IPoXML can be easily implemented on hosts with an XML
+ parser, as the regular structure lends itself handily to parsing and
+ validation of the document/datagram and then processing the
+ destination address, TTL, and checksum before sending it on to its
+ next-hop.
+
+ The reformulation of IPv4 was chosen over IPv6 [RFC2460] due to the
+ wider deployment of IPv4 and the fact that implementing IPv6 as XML
+ would have exceeded the 1500 byte Ethernet MTU.
+
+
+
+Kennedy Informational [Page 2]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ All BLOAT implementations MUST use - and specify - the UTF-8 encoding
+ of RFC 2279 [RFC2279]. All BLOAT document/datagrams MUST be well-
+ formed and include the XMLDecl.
+
+2.1. IP Description
+
+ A number of items have changed (for the better) from the original IP
+ specification. Bit-masks, where present have been converted into
+ human-readable values. IP addresses are listed in their dotted-
+ decimal notation [RFC1123]. Length and checksum values are present
+ as decimal integers.
+
+ To calculate the length and checksum fields of the IP element, a
+ canonicalized form of the element MUST be used. The canonical form
+ SHALL have no whitespace (including newline characters) between
+ elements and only one space character between attributes. There
+ SHALL NOT be a space following the last attribute in an element.
+
+ An iterative method SHOULD be used to calculate checksums, as the
+ length field will vary based on the size of the checksum.
+
+ The payload element bears special attention. Due to the character
+ set restrictions of XML, the payload of IP datagrams (which MAY
+ contain arbitrary data) MUST be encoded for transport. This RFC
+ REQUIRES the contents of the payload to be encoded in the base-64
+ encoding of RFC 2045 [RFC2045], but removes the requirement that the
+ encoded output MUST be wrapped on 76-character lines.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Kennedy Informational [Page 3]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+2.2. Example Datagram
+
+ The following is an example IPoXML datagram with an empty payload:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE ip PUBLIC "-//IETF//DTD BLOAT 1.0 IP//EN" "bloat.dtd">
+ <ip>
+ <header length="474">
+ <version value="4"/>
+ <tos precedence="Routine" delay="Normal" throughput="Normal"
+ relibility="Normal" reserved="0"/>
+ <total.length value="461"/>
+ <id value="1"/>
+ <flags reserved="0" df="dont" mf="last"/>
+ <offset value="0"/>
+ <ttl value="255"/>
+ <protocol value="6"/>
+ <checksum value="8707"/>
+ <source address="10.0.0.22"/>
+ <destination address="10.0.0.1"/>
+ <options>
+ <end copied="0" class="0" number="0"/>
+ </options>
+ <padding pad="0"/>
+ </header>
+ <payload>
+ </payload>
+ </ip>
+
+3. TCPoXML
+
+ This protocol MUST be implemented to be compliant with this RFC. The
+ DTD for this document type can be found in section 7.2.
+
+3.1. TCP Description
+
+ A number of items have changed from the original TCP specification.
+ Bit-masks, where present have been converted into human-readable
+ values. Length and checksum and port values are present as decimal
+ integers.
+
+ To calculate the length and checksum fields of the TCP element, a
+ canonicalized form of the element MUST be used as in section 2.1.
+
+ An iterative method SHOULD be used to calculate checksums as in
+ section 2.1.
+
+ The payload element MUST be encoded as in section 2.1.
+
+
+
+Kennedy Informational [Page 4]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ The TCP offset element was expanded to a maximum of 255 from 16 to
+ allow for the increased size of the header in XML.
+
+ TCPoXML datagrams encapsulated by IPoXML MAY omit the <?xml?> header
+ as well as the <!DOCTYPE> declaration.
+
+3.2. Example Datagram
+
+ The following is an example TCPoXML datagram with an empty payload:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE tcp PUBLIC "-//IETF//DTD BLOAT 1.0 TCP//EN" "bloat.dtd">
+ <tcp>
+ <tcp.header>
+ <src port="31415"/>
+ <dest port="42424"/>
+ <sequence number="322622954"/>
+ <acknowledgement number="689715995"/>
+ <offset number=""/>
+ <reserved value="0"/>
+ <control syn="1" ack="1"/>
+ <window size="1"/>
+ <urgent pointer="0"/>
+ <checksum value="2988"/>
+ <tcp.options>
+ <tcp.end kind="0"/>
+ </tcp.options>
+ <padding pad="0"/>
+ </tcp.header>
+ <payload>
+ </payload>
+ </tcp>
+
+4. UDPoXML
+
+ This protocol MUST be implemented to be compliant with this RFC. The
+ DTD for this document type can be found in section 7.3.
+
+4.1. UDP Description
+
+ A number of items have changed from the original UDP specification.
+ Bit-masks, where present have been converted into human-readable
+ values. Length and checksum and port values are present as decimal
+ integers.
+
+
+
+
+
+
+
+Kennedy Informational [Page 5]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ To calculate the length and checksum fields of the UDP element, a
+ canonicalized form of the element MUST be used as in section 2.1. An
+ iterative method SHOULD be used to calculate checksums as in section
+ 2.1.
+
+ The payload element MUST be encoded as in section 2.1.
+
+ UDPoXML datagrams encapsulated by IPoXML MAY omit the <?xml?> header
+ as well as the <!DOCTYPE> declaration.
+
+4.2. Example Datagram
+
+ The following is an example UDPoXML datagram with an empty payload:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE udp PUBLIC "-//IETF//DTD BLOAT 1.0 UDP//EN" "bloat.dtd">
+ <udp>
+ <udp.header>
+ <src port="31415"/>
+ <dest port="42424"/>
+ <udp.length value="143"/>
+ <checksum value="2988"/>
+ </udp.header>
+ <payload>
+ </payload>
+ </udp>
+
+5. Network Transport
+
+ This document provides for the transmission of BLOAT datagrams over
+ two common families of physical layer transport. Future RFCs will
+ address additional transports as routing vendors catch up to the
+ specification, and we begin to see BLOAT routed across the Internet
+ backbone.
+
+5.1. Ethernet
+
+ BLOAT is encapsulated in Ethernet datagrams as in [RFC894] with the
+ exception that the type field of the Ethernet frame MUST contain the
+ value 0xBEEF. The first 5 octets of the Ethernet frame payload will
+ be 0x3c 3f 78 6d 6c ("<?xml".)
+
+5.2. IEEE 802
+
+ BLOAT is encapsulated in IEEE 802 Networks as in [RFC1042] except
+ that the protocol type code for IPoXML is 0xBEEF.
+
+
+
+
+
+Kennedy Informational [Page 6]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+6. Gatewaying over IP
+
+ In order to facilitate the gradual introduction of BLOAT into the
+ public Internet, BLOAT MAY be encapsulated in IP as in [RFC2003] to
+ gateway between networks that run BLOAT natively on their LANs.
+
+7. DTDs
+
+ The Transport DTDs (7.2. and 7.3.) build on the definitions in the
+ Network DTD (7.1.)
+
+ The DTDs are referenced by their PubidLiteral and SystemLiteral (from
+ [XML]) although it is understood that most IPoXML implementations
+ will not need to pull down the DTD, as it will normally be embedded
+ in the implementation, and presents something of a catch-22 if you
+ need to load part of your network protocol over the network.
+
+7.1. IPoXML DTD
+
+ <!--
+ DTD for IP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE ip PUBLIC "-//IETF//DTD BLOAT 1.0 IP//EN" "bloat.dtd">
+ -->
+ <!--
+ DTD data types:
+
+ Digits [0..9]+
+
+ Precedence "NetworkControl | InternetworkControl |
+ CRITIC | FlashOverride | Flash | Immediate |
+ Priority | Routine"
+
+ IP4Addr "dotted-decimal" notation of [RFC1123]
+
+ Class [0..3]
+
+ Sec "Unclassified | Confidential | EFTO | MMMM | PROG |
+ Restricted | Secret | Top Secret | Reserved"
+
+ Compartments [0..65535]
+
+ Handling [0..65535]
+
+ TCC [0..16777216]
+
+ -->
+
+
+
+Kennedy Informational [Page 7]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ENTITY % Digits "CDATA">
+ <!ENTITY % Precedence "CDATA">
+ <!ENTITY % IP4Addr "CDATA">
+ <!ENTITY % Class "CDATA">
+ <!ENTITY % Sec "CDATA">
+ <!ENTITY % Compartments "CDATA">
+ <!ENTITY % Handling "CDATA">
+ <!ENTITY % TCC "CDATA">
+
+ <!ELEMENT ip (header, payload)>
+
+ <!ELEMENT header (version, tos, total.length, id, flags, offset, ttl,
+ protocol, checksum, source, destination, options,
+ padding)>
+ <!-- length of header in 32-bit words -->
+ <!ATTLIST header
+ length %Digits; #REQUIRED>
+
+ <!ELEMENT version EMPTY>
+ <!-- ip version. SHOULD be "4" -->
+ <!ATTLIST version
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT tos EMPTY>
+ <!ATTLIST tos
+ precedence %Precedence; #REQUIRED
+ delay (normal | low) #REQUIRED
+ throughput (normal | high) #REQUIRED
+ relibility (normal | high) #REQUIRED
+ reserved CDATA #FIXED "0">
+
+ <!ELEMENT total.length EMPTY>
+ <!--
+ total length of datagram (header and payload) in octets, MUST be
+ less than 65,535 (and SHOULD be less than 1024 for IPoXML on local
+ ethernets).
+ -->
+ <!ATTLIST total.length
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT id EMPTY>
+ <!-- 0 <= id <= 65,535 -->
+ <!ATTLIST id
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT flags EMPTY>
+ <!-- df = don't fragment, mf = more fragments -->
+ <!ATTLIST flags
+
+
+
+Kennedy Informational [Page 8]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ reserved CDATA #FIXED "0"
+ df (may|dont) #REQUIRED
+ mf (last|more) #REQUIRED>
+
+ <!ELEMENT offset EMPTY>
+ <!-- 0 <= offset <= 8192 measured in 8 octet (64-bit) chunks -->
+ <!ATTLIST offset
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT ttl EMPTY>
+ <!-- 0 <= ttl <= 255 -->
+ <!ATTLIST ttl
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT protocol EMPTY>
+ <!-- 0 <= protocol <= 255 (per IANA) -->
+ <!ATTLIST protocol
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT checksum EMPTY>
+ <!-- 0 <= checksum <= 65535 (over header only) -->
+ <!ATTLIST checksum
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT source EMPTY>
+ <!ATTLIST source
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT destination EMPTY>
+ <!ATTLIST destination
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT options ( end | noop | security | loose | strict | record
+ | stream | timestamp )*>
+
+ <!ELEMENT end EMPTY>
+ <!ATTLIST end
+ copied (0|1) #REQUIRED
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "0">
+
+ <!ELEMENT noop EMPTY>
+ <!ATTLIST noop
+ copied (0|1) #REQUIRED
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "1">
+
+ <!ELEMENT security EMPTY>
+
+
+
+Kennedy Informational [Page 9]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST security
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "2"
+ length CDATA #FIXED "11"
+ security %Sec; #REQUIRED
+ compartments %Compartments; #REQUIRED
+ handling %Handling; #REQUIRED
+ tcc %TCC; #REQUIRED>
+ <!ELEMENT loose (hop)+>
+ <!ATTLIST loose
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "3"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT hop EMPTY>
+ <!ATTLIST hop
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT strict (hop)+>
+ <!ATTLIST strict
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "9"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT record (hop)+>
+ <!ATTLIST record
+ copied CDATA #FIXED "0"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "7"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT stream EMPTY>
+ <!-- 0 <= id <= 65,535 -->
+ <!ATTLIST stream
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "8"
+ length CDATA #FIXED "4"
+ id %Digits; #REQUIRED>
+
+ <!ELEMENT timestamp (tstamp)+>
+ <!-- 0 <= oflw <=15 -->
+
+
+
+Kennedy Informational [Page 10]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST timestamp
+ copied CDATA #FIXED "0"
+ class CDATA #FIXED "2"
+ number CDATA #FIXED "4"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED
+ oflw %Digits; #REQUIRED
+ flag (0 | 1 | 3) #REQUIRED>
+
+ <!ELEMENT tstamp EMPTY>
+ <!ATTLIST tstamp
+ time %Digits; #REQUIRED
+ address %IP4Addr; #IMPLIED>
+ <!--
+ padding to bring header to 32-bit boundary.
+ pad MUST be "0"*
+ -->
+ <!ELEMENT padding EMPTY>
+ <!ATTLIST padding
+ pad CDATA #REQUIRED>
+
+ <!-- payload MUST be encoded as base-64 [RFC2045], as modified
+ by section 2.1 of this RFC -->
+ <!ELEMENT payload (CDATA)>
+
+7.2. TCPoXML DTD
+
+ <!--
+ DTD for TCP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE tcp PUBLIC "-//IETF//DTD BLOAT 1.0 TCP//EN" "bloat.dtd">
+ -->
+
+ <!-- the pseudoheader is only included for checksum calculations -->
+ <!ELEMENT tcp (tcp.pseudoheader?, tcp.header, payload)>
+
+ <!ELEMENT tcp.header (src, dest, sequence, acknowledgement, offset,
+ reserved, control, window, checksum, urgent,
+ tcp.options, padding)>
+
+ <!ELEMENT src EMPTY>
+ <!-- 0 <= port <= 65,535 -->
+ <!ATTLIST src
+ port %Digits; #REQUIRED>
+
+ <!ELEMENT dest EMPTY>
+ <!-- 0 <= port <= 65,535 -->
+
+
+
+Kennedy Informational [Page 11]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST dest
+ port %Digits; #REQUIRED>
+
+ <!ELEMENT sequence EMPTY>
+ <!-- 0 <= number <= 4294967295 -->
+ <!ATTLIST sequence
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT acknowledgement EMPTY>
+ <!-- 0 <= number <= 4294967295 -->
+ <!ATTLIST acknowledgement
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT offset EMPTY>
+ <!-- 0 <= number <= 255 -->
+ <!ATTLIST offset
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT reserved EMPTY>
+ <!ATTLIST reserved
+ value CDATA #FIXED "0">
+
+ <!ELEMENT control EMPTY>
+ <!ATTLIST control
+ urg (0|1) #IMPLIED
+ ack (0|1) #IMPLIED
+ psh (0|1) #IMPLIED
+ rst (0|1) #IMPLIED
+ syn (0|1) #IMPLIED
+ fin (0|1) #IMPLIED>
+
+ <!ELEMENT window EMPTY>
+ <!-- 0 <= size <= 65,535 -->
+ <!ATTLIST window
+ size %Digits; #REQUIRED>
+
+ <!--
+ checksum as in ip, but with
+ the following pseudo-header added into the tcp element:
+ -->
+ <!ELEMENT tcp.pseudoheader (source, destination, protocol,
+ tcp.length)>
+
+ <!--
+ tcp header + data length in octets. does not include the size of
+
+ the pseudoheader.
+ -->
+
+
+
+Kennedy Informational [Page 12]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ELEMENT tcp.length EMPTY>
+ <!ATTLIST tcp.length
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT urgent EMPTY>
+ <!-- 0 <= pointer <= 65,535 -->
+ <!ATTLIST urgent
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT tcp.options (tcp.end | tcp.noop | tcp.mss)+>
+
+ <!ELEMENT tcp.end EMPTY>
+ <!ATTLIST tcp.end
+ kind CDATA #FIXED "0">
+
+ <!ELEMENT tcp.noop EMPTY>
+ <!ATTLIST tcp.noop
+ kind CDATA #FIXED "1">
+
+ <!ELEMENT tcp.mss EMPTY>
+ <!ATTLIST tcp.mss
+ kind CDATA #FIXED "2"
+ length CDATA #FIXED "4"
+ size %Digits; #REQUIRED>
+
+7.3. UDPoXML DTD
+
+ <!--
+ DTD for UDP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE udp PUBLIC "-//IETF//DTD BLOAT 1.0 UDP//EN" "bloat.dtd">
+ -->
+
+ <!ELEMENT udp (udp.pseudoheader?, udp.header, payload)>
+
+ <!ELEMENT udp.header (src, dest, udp.length, checksum)>
+
+ <!ELEMENT udp.pseudoheader (source, destination, protocol,
+ udp.length)>
+
+ <!--
+ udp header + data length in octets. does not include the size of
+ the pseudoheader.
+ -->
+ <!ELEMENT udp.length EMPTY>
+ <!ATTLIST udp.length
+ value %Digits; #REQUIRED>
+
+
+
+Kennedy Informational [Page 13]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+8. Security Considerations
+
+ XML, as a subset of SGML, has the same security considerations as
+ specified in SGML Media Types [RFC1874]. Security considerations
+ that apply to IP, TCP and UDP also likely apply to BLOAT as it does
+ not attempt to correct for issues not related to message format.
+
+9. References
+
+ [JABBER] Miller, J., "Jabber", draft-miller-jabber-00.txt,
+ February 2002. (Work in Progress)
+
+ [RFC768] Postel, J., "User Datagram Protocol", STD 6, RFC 768,
+ August 1980.
+
+ [RFC791] Postel, J., "Internet Protocol", STD 5, RFC 791,
+ September 1981.
+
+ [RFC793] Postel, J., "Transmission Control Protocol", STD 7, RFC
+ 793, September 1981.
+
+ [RFC894] Hornig, C., "Standard for the Transmission of IP
+ Datagrams over Ethernet Networks.", RFC 894, April 1984.
+
+ [RFC1042] Postel, J. and J. Reynolds, "Standard for the
+ Transmission of IP Datagrams Over IEEE 802 Networks", STD
+ 43, RFC 1042, February 1988.
+
+ [RFC1123] Braden, R., "Requirements for Internet Hosts -
+ Application and Support", RFC 1123, October 1989.
+
+ [RFC1874] Levinson, E., "SGML Media Types", RFC 1874, December
+ 1995.
+
+ [RFC2003] Perkins, C., "IP Encapsulation within IP", RFC 2003,
+ October 1996.
+
+ [RFC2045] Freed, N. and N. Borenstein, "Multipurpose Internet Mail
+ Extensions (MIME) Part One: Format of Internet Message
+ Bodies", RFC 2045, November 1996.
+
+ [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+ [RFC2279] Yergeau, F., "UTF-8, a transformation format of ISO
+ 10646", RFC 2279, January 1998.
+
+
+
+
+
+Kennedy Informational [Page 14]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ [RFC2460] Deering, S. and R. Hinden, "Internet Protocol, Version 6
+ (IPv6) Specification", RFC 2460, December 1998.
+
+ [RFC3080] Rose, M., "The Blocks Extensible Exchange Protocol Core",
+ RFC 3080, March 2001.
+
+ [SOAP] Box, D., Ehnebuske, D., Kakivaya, G., Layman, A.,
+ Mendelsohn, N., Nielsen, H. F., Thatte, S. Winer, D.,
+ "Simple Object Access Protocol (SOAP) 1.1" World Wide Web
+ Consortium Note, May 2000 http://www.w3.org/TR/SOAP/
+
+ [XML] Bray, T., Paoli, J., Sperberg-McQueen, C. M., "Extensible
+ Markup Language (XML)" World Wide Web Consortium
+ Recommendation REC- xml-19980210.
+ http://www.w3.org/TR/1998/REC-xml-19980210
+
+10. Author's Address
+
+ Hugh Kennedy
+ Mimezine
+ 1060 West Addison
+ Chicago, IL 60613
+ USA
+
+ EMail: kennedyh@engin.umich.edu
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Kennedy Informational [Page 15]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+11. Full Copyright Statement
+
+ Copyright (C) The Internet Society (2002). 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.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Kennedy Informational [Page 16]
+
diff --git a/tests/auto/qftp/tst_qftp.cpp b/tests/auto/qftp/tst_qftp.cpp
new file mode 100644
index 0000000000..78157d84ff
--- /dev/null
+++ b/tests/auto/qftp/tst_qftp.cpp
@@ -0,0 +1,2168 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include <QtTest/QtTest>
+
+#include <qcoreapplication.h>
+#include <qfile.h>
+#include <qbuffer.h>
+#include "qftp.h"
+#include <qmap.h>
+#include <time.h>
+#include <stdlib.h>
+#include <QNetworkProxy>
+#include <QNetworkConfiguration>
+#include <qnetworkconfigmanager.h>
+#include <QNetworkSession>
+#include <QtNetwork/private/qnetworksession_p.h>
+
+#include "../network-settings.h"
+
+//TESTED_CLASS=
+//TESTED_FILES=
+
+#ifdef Q_OS_SYMBIAN
+// In Symbian OS test data is located in applications private dir
+// Application private dir is default serach path for files, so SRCDIR can be set to empty
+#define SRCDIR ""
+#endif
+
+#ifndef QT_NO_BEARERMANAGEMENT
+Q_DECLARE_METATYPE(QNetworkConfiguration)
+#endif
+
+class tst_QFtp : public QObject
+{
+ Q_OBJECT
+
+public:
+ tst_QFtp();
+ virtual ~tst_QFtp();
+
+
+public slots:
+ void initTestCase_data();
+ void initTestCase();
+ void cleanupTestCase();
+ void init();
+ void cleanup();
+private slots:
+ void connectToHost_data();
+ void connectToHost();
+ void connectToUnresponsiveHost();
+ void login_data();
+ void login();
+ void close_data();
+ void close();
+
+ void list_data();
+ void list();
+ void cd_data();
+ void cd();
+ void get_data();
+ void get();
+ void put_data();
+ void put();
+ void remove();
+ void mkdir_data();
+ void mkdir();
+ void mkdir2();
+ void rmdir();
+ void rename_data();
+ void rename();
+
+ void commandSequence_data();
+ void commandSequence();
+
+ void abort_data();
+ void abort();
+
+ void bytesAvailable_data();
+ void bytesAvailable();
+
+ void activeMode();
+
+ void proxy_data();
+ void proxy();
+
+ void binaryAscii();
+
+ void doneSignal();
+ void queueMoreCommandsInDoneSlot();
+
+ void qtbug7359Crash();
+
+protected slots:
+ void stateChanged( int );
+ void listInfo( const QUrlInfo & );
+ void readyRead();
+ void dataTransferProgress(qint64, qint64);
+
+ void commandStarted( int );
+ void commandFinished( int, bool );
+ void done( bool );
+ void activeModeDone( bool );
+ void mkdir2Slot(int id, bool error);
+ void cdUpSlot(bool);
+
+private:
+ QFtp *newFtp();
+ void addCommand( QFtp::Command, int );
+ bool fileExists( const QString &host, quint16 port, const QString &user, const QString &password, const QString &file, const QString &cdDir = QString::null );
+ bool dirExists( const QString &host, quint16 port, const QString &user, const QString &password, const QString &cdDir, const QString &dirToCreate );
+
+ void renameInit( const QString &host, const QString &user, const QString &password, const QString &createFile );
+ void renameCleanup( const QString &host, const QString &user, const QString &password, const QString &fileToDelete );
+
+ QFtp *ftp;
+#ifndef QT_NO_BEARERMANAGEMENT
+ QSharedPointer<QNetworkSession> networkSessionExplicit;
+ QSharedPointer<QNetworkSession> networkSessionImplicit;
+#endif
+
+ QList<int> ids; // helper to make sure that all expected signals are emitted
+ int current_id;
+
+ int connectToHost_state;
+ int close_state;
+ int login_state;
+ int cur_state;
+ struct CommandResult
+ {
+ int id;
+ int success;
+ };
+ QMap< QFtp::Command, CommandResult > resultMap;
+ typedef QMap<QFtp::Command,CommandResult>::Iterator ResMapIt;
+
+ int done_success;
+ int commandSequence_success;
+
+ qlonglong bytesAvailable_finishedGet;
+ qlonglong bytesAvailable_finished;
+ qlonglong bytesAvailable_done;
+
+ QList<QUrlInfo> listInfo_i;
+ QByteArray newData_ba;
+ qlonglong bytesTotal;
+ qlonglong bytesDone;
+
+ bool inFileDirExistsFunction;
+
+ QString uniqueExtension;
+};
+
+//#define DUMP_SIGNALS
+
+const int bytesTotal_init = -10;
+const int bytesDone_init = -10;
+
+tst_QFtp::tst_QFtp() :
+ ftp(0)
+{
+}
+
+tst_QFtp::~tst_QFtp()
+{
+}
+
+void tst_QFtp::initTestCase_data()
+{
+ QTest::addColumn<bool>("setProxy");
+ QTest::addColumn<int>("proxyType");
+ QTest::addColumn<bool>("setSession");
+
+ QTest::newRow("WithoutProxy") << false << 0 << false;
+ QTest::newRow("WithSocks5Proxy") << true << int(QNetworkProxy::Socks5Proxy) << false;
+ //### doesn't work well yet.
+ //QTest::newRow("WithHttpProxy") << true << int(QNetworkProxy::HttpProxy);
+
+#ifndef QT_NO_BEARERMANAGEMENT
+ QTest::newRow("WithoutProxyWithSession") << false << 0 << true;
+ QTest::newRow("WithSocks5ProxyAndSession") << true << int(QNetworkProxy::Socks5Proxy) << true;
+#endif
+}
+
+void tst_QFtp::initTestCase()
+{
+#ifndef QT_NO_BEARERMANAGEMENT
+ QNetworkConfigurationManager manager;
+ networkSessionImplicit = QSharedPointer<QNetworkSession>(new QNetworkSession(manager.defaultConfiguration()));
+ networkSessionImplicit->open();
+ QVERIFY(networkSessionImplicit->waitForOpened(60000)); //there may be user prompt on 1st connect
+#endif
+}
+
+void tst_QFtp::cleanupTestCase()
+{
+#ifndef QT_NO_BEARERMANAGEMENT
+ networkSessionExplicit.clear();
+ networkSessionImplicit.clear();
+#endif
+}
+
+void tst_QFtp::init()
+{
+ QFETCH_GLOBAL(bool, setProxy);
+ QFETCH_GLOBAL(int, proxyType);
+ QFETCH_GLOBAL(bool, setSession);
+ if (setProxy) {
+ if (proxyType == QNetworkProxy::Socks5Proxy) {
+ QNetworkProxy::setApplicationProxy(QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::serverName(), 1080));
+ } else if (proxyType == QNetworkProxy::HttpProxy) {
+ QNetworkProxy::setApplicationProxy(QNetworkProxy(QNetworkProxy::HttpProxy, QtNetworkSettings::serverName(), 3128));
+ }
+ }
+#ifndef QT_NO_BEARERMANAGEMENT
+ if (setSession) {
+ networkSessionExplicit = networkSessionImplicit;
+ if (!networkSessionExplicit->isOpen()) {
+ networkSessionExplicit->open();
+ QVERIFY(networkSessionExplicit->waitForOpened(30000));
+ }
+ } else {
+ networkSessionExplicit.clear();
+ }
+#endif
+
+ delete ftp;
+ ftp = 0;
+
+ ids.clear();
+ current_id = 0;
+
+ resultMap.clear();
+ connectToHost_state = -1;
+ close_state = -1;
+ login_state = -1;
+ cur_state = QFtp::Unconnected;
+
+ listInfo_i.clear();
+ newData_ba = QByteArray();
+ bytesTotal = bytesTotal_init;
+ bytesDone = bytesDone_init;
+
+ done_success = -1;
+ commandSequence_success = -1;
+
+ bytesAvailable_finishedGet = 1234567890;
+ bytesAvailable_finished = 1234567890;
+ bytesAvailable_done = 1234567890;
+
+ inFileDirExistsFunction = FALSE;
+
+#if !defined(Q_OS_WINCE)
+ srand(time(0));
+ uniqueExtension = QString("%1%2%3").arg((qulonglong)this).arg(rand()).arg((qulonglong)time(0));
+#else
+ srand(0);
+ uniqueExtension = QString("%1%2%3").arg((qulonglong)this).arg(rand()).arg((qulonglong)(0));
+#endif
+}
+
+void tst_QFtp::cleanup()
+{
+ if (ftp) {
+ delete ftp;
+ ftp = 0;
+ }
+ QFETCH_GLOBAL(bool, setProxy);
+ if (setProxy) {
+ QNetworkProxy::setApplicationProxy(QNetworkProxy::DefaultProxy);
+ }
+
+ delete ftp;
+ ftp = 0;
+#ifndef QT_NO_BEARERMANAGEMENT
+ networkSessionExplicit.clear();
+#endif
+}
+
+void tst_QFtp::connectToHost_data()
+{
+ QTest::addColumn<QString>("host");
+ QTest::addColumn<uint>("port");
+ QTest::addColumn<int>("state");
+
+ QTest::newRow( "ok01" ) << QtNetworkSettings::serverName() << (uint)21 << (int)QFtp::Connected;
+ QTest::newRow( "error01" ) << QtNetworkSettings::serverName() << (uint)2222 << (int)QFtp::Unconnected;
+ QTest::newRow( "error02" ) << QString("foo.bar") << (uint)21 << (int)QFtp::Unconnected;
+}
+
+void tst_QFtp::connectToHost()
+{
+ QFETCH( QString, host );
+ QFETCH( uint, port );
+
+ ftp = newFtp();
+ addCommand( QFtp::ConnectToHost, ftp->connectToHost( host, port ) );
+
+ QTestEventLoop::instance().enterLoop( 61 );
+ delete ftp;
+ ftp = 0;
+ if ( QTestEventLoop::instance().timeout() )
+ QFAIL( "Network operation timed out" );
+
+ QTEST( connectToHost_state, "state" );
+
+ ResMapIt it = resultMap.find( QFtp::ConnectToHost );
+ QVERIFY( it != resultMap.end() );
+ QFETCH( int, state );
+ if ( state == QFtp::Connected ) {
+ QVERIFY( it.value().success == 1 );
+ } else {
+ QVERIFY( it.value().success == 0 );
+ }
+}
+
+void tst_QFtp::connectToUnresponsiveHost()
+{
+ QFETCH_GLOBAL(bool, setProxy);
+ if (setProxy)
+ QSKIP( "This test takes too long if we test with proxies too", SkipSingle );
+
+ QString host = "192.0.2.42"; // IP out of TEST-NET, should be unreachable
+ uint port = 21;
+
+ ftp = newFtp();
+ addCommand( QFtp::ConnectToHost, ftp->connectToHost( host, port ) );
+
+ qDebug( "About to connect to host that won't reply (this test takes 60 seconds)" );
+ QTestEventLoop::instance().enterLoop( 61 );
+#ifdef Q_OS_WIN
+ /* On Windows, we do not get a timeout, because Winsock is behaving in a strange way:
+ We issue two "WSAConnect()" calls, after the first, as a result we get WSAEWOULDBLOCK,
+ after the second, we get WSAEISCONN, which means that the socket is connected, which cannot be.
+ However, after some seconds we get a socket error saying that the remote host closed the connection,
+ which can neither be. For this test, that would actually enable us to finish before timout, but handling that case
+ (in void QFtpPI::error(QAbstractSocket::SocketError e)) breaks
+ a lot of other stuff in QFtp, so we just expect this test to fail on Windows.
+ */
+ QEXPECT_FAIL("", "timeout not working due to strange Windows socket behaviour (see source file of this test for explanation)", Abort);
+#endif
+ QVERIFY2(! QTestEventLoop::instance().timeout(), "Network timeout longer than expected (should have been 60 seconds)");
+
+ QVERIFY( ftp->state() == QFtp::Unconnected);
+ ResMapIt it = resultMap.find( QFtp::ConnectToHost );
+ QVERIFY( it != resultMap.end() );
+ QVERIFY( it.value().success == 0 );
+
+ delete ftp;
+ ftp = 0;
+}
+
+void tst_QFtp::login_data()
+{
+ QTest::addColumn<QString>("host");
+ QTest::addColumn<uint>("port");
+ QTest::addColumn<QString>("user");
+ QTest::addColumn<QString>("password");
+ QTest::addColumn<int>("success");
+
+ QTest::newRow( "ok01" ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString() << 1;
+ QTest::newRow( "ok02" ) << QtNetworkSettings::serverName() << (uint)21 << QString("ftp") << QString() << 1;
+ QTest::newRow( "ok03" ) << QtNetworkSettings::serverName() << (uint)21 << QString("ftp") << QString("foo") << 1;
+ QTest::newRow( "ok04" ) << QtNetworkSettings::serverName() << (uint)21 << QString("ftptest") << QString("password") << 1;
+
+ QTest::newRow( "error01" ) << QtNetworkSettings::serverName() << (uint)21 << QString("foo") << QString() << 0;
+ QTest::newRow( "error02" ) << QtNetworkSettings::serverName() << (uint)21 << QString("foo") << QString("bar") << 0;
+}
+
+void tst_QFtp::login()
+{
+ QFETCH( QString, host );
+ QFETCH( uint, port );
+ QFETCH( QString, user );
+ QFETCH( QString, password );
+
+ ftp = newFtp();
+ addCommand( QFtp::ConnectToHost, ftp->connectToHost( host, port ) );
+ addCommand( QFtp::Login, ftp->login( user, password ) );
+
+ QTestEventLoop::instance().enterLoop( 30 );
+ delete ftp;
+ ftp = 0;
+ if ( QTestEventLoop::instance().timeout() )
+ QFAIL( "Network operation timed out" );
+
+ ResMapIt it = resultMap.find( QFtp::Login );
+ QVERIFY( it != resultMap.end() );
+ QTEST( it.value().success, "success" );
+
+ if ( it.value().success ) {
+ QVERIFY( login_state == QFtp::LoggedIn );
+ } else {
+ QVERIFY( login_state != QFtp::LoggedIn );
+ }
+}
+
+void tst_QFtp::close_data()
+{
+ QTest::addColumn<QString>("host");
+ QTest::addColumn<uint>("port");
+ QTest::addColumn<QString>("user");
+ QTest::addColumn<QString>("password");
+ QTest::addColumn<bool>("login");
+
+ QTest::newRow( "login01" ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString() << (bool)TRUE;
+ QTest::newRow( "login02" ) << QtNetworkSettings::serverName() << (uint)21 << QString("ftp") << QString() << (bool)TRUE;
+ QTest::newRow( "login03" ) << QtNetworkSettings::serverName() << (uint)21 << QString("ftp") << QString("foo") << (bool)TRUE;
+ QTest::newRow( "login04" ) << QtNetworkSettings::serverName() << (uint)21 << QString("ftptest") << QString("password") << (bool)TRUE;
+
+ QTest::newRow( "no-login01" ) << QtNetworkSettings::serverName() << (uint)21 << QString("") << QString("") << (bool)FALSE;
+}
+
+void tst_QFtp::close()
+{
+ QFETCH( QString, host );
+ QFETCH( uint, port );
+ QFETCH( QString, user );
+ QFETCH( QString, password );
+ QFETCH( bool, login );
+
+ ftp = newFtp();
+ addCommand( QFtp::ConnectToHost, ftp->connectToHost( host, port ) );
+ if ( login )
+ addCommand( QFtp::Login, ftp->login( user, password ) );
+ addCommand( QFtp::Close, ftp->close() );
+
+ QTestEventLoop::instance().enterLoop( 30 );
+ delete ftp;
+ ftp = 0;
+ if ( QTestEventLoop::instance().timeout() )
+ QFAIL( "Network operation timed out" );
+
+ QCOMPARE( close_state, (int)QFtp::Unconnected );
+
+ ResMapIt it = resultMap.find( QFtp::Close );
+ QVERIFY( it != resultMap.end() );
+ QVERIFY( it.value().success == 1 );
+}
+
+void tst_QFtp::list_data()
+{
+ QTest::addColumn<QString>("host");
+ QTest::addColumn<uint>("port");
+ QTest::addColumn<QString>("user");
+ QTest::addColumn<QString>("password");
+ QTest::addColumn<QString>("dir");
+ QTest::addColumn<int>("success");
+ QTest::addColumn<QStringList>("entryNames"); // ### we should rather use a QList<QUrlInfo> here
+
+ QStringList flukeRoot;
+ flukeRoot << "pub";
+ flukeRoot << "qtest";
+ QStringList flukeQtest;
+ flukeQtest << "bigfile";
+ flukeQtest << "nonASCII";
+ flukeQtest << "rfc3252";
+ flukeQtest << "rfc3252.txt";
+ flukeQtest << "upload";
+
+ QTest::newRow( "workDir01" ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString() << QString() << 1 << flukeRoot;
+ QTest::newRow( "workDir02" ) << QtNetworkSettings::serverName() << (uint)21 << QString("ftptest") << QString("password") << QString() << 1 << flukeRoot;
+
+ QTest::newRow( "relPath01" ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString() << QString("qtest") << 1 << flukeQtest;
+ QTest::newRow( "relPath02" ) << QtNetworkSettings::serverName() << (uint)21 << QString("ftptest") << QString("password") << QString("qtest") << 1 << flukeQtest;
+
+ QTest::newRow( "absPath01" ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString() << QString("/qtest") << 1 << flukeQtest;
+ QTest::newRow( "absPath02" ) << QtNetworkSettings::serverName() << (uint)21 << QString("ftptest") << QString("password") << QString("/var/ftp/qtest") << 1 << flukeQtest;
+
+ QTest::newRow( "nonExist01" ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString() << QString("foo") << 1 << QStringList();
+ QTest::newRow( "nonExist02" ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString() << QString("/foo") << 1 << QStringList();
+ // ### The microsoft server does not seem to work properly at the moment --
+ // I am also not able to open a data connection with other, non-Qt FTP
+ // clients to it.
+ // QTest::newRow( "nonExist03" ) << "ftp.microsoft.com" << (uint)21 << QString() << QString() << QString("/foo") << 0 << QStringList();
+
+ QStringList susePub;
+ susePub << "README.mirror-policy" << "axp" << "i386" << "ia64" << "install" << "noarch" << "pubring.gpg-build.suse.de" << "update" << "x86_64";
+ QTest::newRow( "epsvNotSupported" ) << QString("ftp.funet.fi") << (uint)21 << QString::fromLatin1("ftp") << QString::fromLatin1("root@") << QString("/pub/Linux/suse/suse") << 1 << susePub;
+}
+
+void tst_QFtp::list()
+{
+ QFETCH( QString, host );
+ QFETCH( uint, port );
+ QFETCH( QString, user );
+ QFETCH( QString, password );
+ QFETCH( QString, dir );
+
+ ftp = newFtp();
+ addCommand( QFtp::ConnectToHost, ftp->connectToHost( host, port ) );
+ addCommand( QFtp::Login, ftp->login( user, password ) );
+ addCommand( QFtp::List, ftp->list( dir ) );
+ addCommand( QFtp::Close, ftp->close() );
+
+ QTestEventLoop::instance().enterLoop( 30 );
+ delete ftp;
+ ftp = 0;
+ if ( QTestEventLoop::instance().timeout() )
+ QFAIL( "Network operation timed out" );
+
+ ResMapIt it = resultMap.find( QFtp::List );
+ QVERIFY( it != resultMap.end() );
+ QTEST( it.value().success, "success" );
+ QFETCH( QStringList, entryNames );
+ QCOMPARE( listInfo_i.count(), entryNames.count() );
+ for ( uint i=0; i < (uint) entryNames.count(); i++ ) {
+ QCOMPARE( listInfo_i[i].name(), entryNames[i] );
+ }
+}
+
+void tst_QFtp::cd_data()
+{
+ QTest::addColumn<QString>("host");
+ QTest::addColumn<uint>("port");
+ QTest::addColumn<QString>("user");
+ QTest::addColumn<QString>("password");
+ QTest::addColumn<QString>("dir");
+ QTest::addColumn<int>("success");
+ QTest::addColumn<QStringList>("entryNames"); // ### we should rather use a QList<QUrlInfo> here
+
+ QStringList flukeRoot;
+ flukeRoot << "qtest";
+ QStringList flukeQtest;
+ flukeQtest << "bigfile";
+ flukeQtest << "nonASCII";
+ flukeQtest << "rfc3252";
+ flukeQtest << "rfc3252.txt";
+ flukeQtest << "upload";
+
+ QTest::newRow( "relPath01" ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString() << QString("qtest") << 1 << flukeQtest;
+ QTest::newRow( "relPath02" ) << QtNetworkSettings::serverName() << (uint)21 << QString("ftptest") << QString("password") << QString("qtest") << 1 << flukeQtest;
+
+ QTest::newRow( "absPath01" ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString() << QString("/qtest") << 1 << flukeQtest;
+ QTest::newRow( "absPath02" ) << QtNetworkSettings::serverName() << (uint)21 << QString("ftptest") << QString("password") << QString("/var/ftp/qtest") << 1 << flukeQtest;
+
+ QTest::newRow( "nonExist01" ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString() << QString("foo") << 0 << QStringList();
+ QTest::newRow( "nonExist03" ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString() << QString("/foo") << 0 << QStringList();
+}
+
+void tst_QFtp::cd()
+{
+ QFETCH( QString, host );
+ QFETCH( uint, port );
+ QFETCH( QString, user );
+ QFETCH( QString, password );
+ QFETCH( QString, dir );
+
+ ftp = newFtp();
+ addCommand( QFtp::ConnectToHost, ftp->connectToHost( host, port ) );
+ addCommand( QFtp::Login, ftp->login( user, password ) );
+ addCommand( QFtp::Cd, ftp->cd( dir ) );
+ addCommand( QFtp::List, ftp->list() );
+ addCommand( QFtp::Close, ftp->close() );
+
+ QTestEventLoop::instance().enterLoop( 30 );
+
+ delete ftp;
+ ftp = 0;
+ if ( QTestEventLoop::instance().timeout() ) {
+ QFAIL( "Network operation timed out" );
+ }
+
+ ResMapIt it = resultMap.find( QFtp::Cd );
+ QVERIFY( it != resultMap.end() );
+ QTEST( it.value().success, "success" );
+ QFETCH( QStringList, entryNames );
+ QCOMPARE( listInfo_i.count(), entryNames.count() );
+ for ( uint i=0; i < (uint) entryNames.count(); i++ ) {
+ QCOMPARE( listInfo_i[i].name(), entryNames[i] );
+ }
+}
+
+void tst_QFtp::get_data()
+{
+ QTest::addColumn<QString>("host");
+ QTest::addColumn<uint>("port");
+ QTest::addColumn<QString>("user");
+ QTest::addColumn<QString>("password");
+ QTest::addColumn<QString>("file");
+ QTest::addColumn<int>("success");
+ QTest::addColumn<QByteArray>("res");
+ QTest::addColumn<bool>("useIODevice");
+
+ // ### move this into external testdata
+ QFile file( SRCDIR "rfc3252.txt" );
+ QVERIFY( file.open( QIODevice::ReadOnly ) );
+ QByteArray rfc3252 = file.readAll();
+
+ // test the two get() overloads in one routine
+ for ( int i=0; i<2; i++ ) {
+ QTest::newRow( QString("relPath01_%1").arg(i).toLatin1().constData() ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString()
+ << "qtest/rfc3252" << 1 << rfc3252 << (bool)(i==1);
+ QTest::newRow( QString("relPath02_%1").arg(i).toLatin1().constData() ) << QtNetworkSettings::serverName() << (uint)21 << QString("ftptest") << QString("password")
+ << "qtest/rfc3252" << 1 << rfc3252 << (bool)(i==1);
+
+ QTest::newRow( QString("absPath01_%1").arg(i).toLatin1().constData() ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString()
+ << "/qtest/rfc3252" << 1 << rfc3252 << (bool)(i==1);
+ QTest::newRow( QString("absPath02_%1").arg(i).toLatin1().constData() ) << QtNetworkSettings::serverName() << (uint)21 << QString("ftptest") << QString("password")
+ << "/var/ftp/qtest/rfc3252" << 1 << rfc3252 << (bool)(i==1);
+
+ QTest::newRow( QString("nonExist01_%1").arg(i).toLatin1().constData() ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString()
+ << QString("foo") << 0 << QByteArray() << (bool)(i==1);
+ QTest::newRow( QString("nonExist02_%1").arg(i).toLatin1().constData() ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString()
+ << QString("/foo") << 0 << QByteArray() << (bool)(i==1);
+ }
+}
+
+void tst_QFtp::get()
+{
+ // for the overload that takes a QIODevice
+ QByteArray buf_ba;
+ QBuffer buf( &buf_ba );
+
+ QFETCH( QString, host );
+ QFETCH( uint, port );
+ QFETCH( QString, user );
+ QFETCH( QString, password );
+ QFETCH( QString, file );
+ QFETCH( bool, useIODevice );
+
+ ftp = newFtp();
+ addCommand( QFtp::ConnectToHost, ftp->connectToHost( host, port ) );
+ addCommand( QFtp::Login, ftp->login( user, password ) );
+ if ( useIODevice ) {
+ buf.open( QIODevice::WriteOnly );
+ addCommand( QFtp::Get, ftp->get( file, &buf ) );
+ } else {
+ addCommand( QFtp::Get, ftp->get( file ) );
+ }
+ addCommand( QFtp::Close, ftp->close() );
+
+ QTestEventLoop::instance().enterLoop( 50 );
+ delete ftp;
+ ftp = 0;
+ if ( QTestEventLoop::instance().timeout() )
+ QFAIL( "Network operation timed out" );
+
+ ResMapIt it = resultMap.find( QFtp::Get );
+ QVERIFY( it != resultMap.end() );
+ QTEST( it.value().success, "success" );
+ if ( useIODevice ) {
+ QTEST( buf_ba, "res" );
+ } else {
+ QTEST( newData_ba, "res" );
+ }
+ QVERIFY( bytesTotal != bytesTotal_init );
+ if ( bytesTotal != -1 ) {
+ QVERIFY( bytesDone == bytesTotal );
+ }
+ if ( useIODevice ) {
+ if ( bytesDone != bytesDone_init ) {
+ QVERIFY( (int)buf_ba.size() == bytesDone );
+ }
+ } else {
+ if ( bytesDone != bytesDone_init ) {
+ QVERIFY( (int)newData_ba.size() == bytesDone );
+ }
+ }
+}
+
+void tst_QFtp::put_data()
+{
+ QTest::addColumn<QString>("host");
+ QTest::addColumn<uint>("port");
+ QTest::addColumn<QString>("user");
+ QTest::addColumn<QString>("password");
+ QTest::addColumn<QString>("file");
+ QTest::addColumn<QByteArray>("fileData");
+ QTest::addColumn<bool>("useIODevice");
+ QTest::addColumn<int>("success");
+
+ // ### move this into external testdata
+ QFile file( SRCDIR "rfc3252.txt" );
+ QVERIFY( file.open( QIODevice::ReadOnly ) );
+ QByteArray rfc3252 = file.readAll();
+
+ QByteArray bigData( 10*1024*1024, 0 );
+ bigData.fill( 'A' );
+
+ // test the two put() overloads in one routine
+ for ( int i=0; i<2; i++ ) {
+ QTest::newRow( QString("relPath01_%1").arg(i).toLatin1().constData() ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString()
+ << QString("qtest/upload/rel01_%1") << rfc3252
+ << (bool)(i==1) << 1;
+ /*
+ QTest::newRow( QString("relPath02_%1").arg(i).toLatin1().constData() ) << QtNetworkSettings::serverName() << (uint)21 << QString("ftptest") << QString("password")
+ << QString("qtest/upload/rel02_%1") << rfc3252
+ << (bool)(i==1) << 1;
+ QTest::newRow( QString("relPath03_%1").arg(i).toLatin1().constData() ) << QtNetworkSettings::serverName() << (uint)21 << QString("ftptest") << QString("password")
+ << QString("qtest/upload/rel03_%1") << QByteArray()
+ << (bool)(i==1) << 1;
+ QTest::newRow( QString("relPath04_%1").arg(i).toLatin1().constData() ) << QtNetworkSettings::serverName() << (uint)21 << QString("ftptest") << QString("password")
+ << QString("qtest/upload/rel04_%1") << bigData
+ << (bool)(i==1) << 1;
+
+ QTest::newRow( QString("absPath01_%1").arg(i).toLatin1().constData() ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString()
+ << QString("/qtest/upload/abs01_%1") << rfc3252
+ << (bool)(i==1) << 1;
+ QTest::newRow( QString("absPath02_%1").arg(i).toLatin1().constData() ) << QtNetworkSettings::serverName() << (uint)21 << QString("ftptest") << QString("password")
+ << QString("/srv/ftp/qtest/upload/abs02_%1") << rfc3252
+ << (bool)(i==1) << 1;
+
+ QTest::newRow( QString("nonExist01_%1").arg(i).toLatin1().constData() ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString()
+ << QString("foo") << QByteArray()
+ << (bool)(i==1) << 0;
+ QTest::newRow( QString("nonExist02_%1").arg(i).toLatin1().constData() ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString()
+ << QString("/foo") << QByteArray()
+ << (bool)(i==1) << 0;
+*/
+ }
+}
+
+void tst_QFtp::put()
+{
+ QFETCH( QString, host );
+ QFETCH( uint, port );
+ QFETCH( QString, user );
+ QFETCH( QString, password );
+ QFETCH( QString, file );
+ QFETCH( QByteArray, fileData );
+ QFETCH( bool, useIODevice );
+
+#ifdef Q_OS_WIN
+ QFETCH_GLOBAL(bool, setProxy);
+ if (setProxy) {
+ QFETCH_GLOBAL(int, proxyType);
+ if (proxyType == QNetworkProxy::Socks5Proxy) {
+ QSKIP("With socks5 the put() test takes too long time on Windows.", SkipAll);
+ }
+ }
+#endif
+
+ const int timestep = 50;
+
+ if(file.contains('%'))
+ file = file.arg(uniqueExtension);
+
+ // for the overload that takes a QIODevice
+ QBuffer buf_fileData( &fileData );
+ buf_fileData.open( QIODevice::ReadOnly );
+
+ ResMapIt it;
+ //////////////////////////////////////////////////////////////////
+ // upload the file
+ init();
+ ftp = newFtp();
+ addCommand( QFtp::ConnectToHost, ftp->connectToHost( host, port ) );
+ addCommand( QFtp::Login, ftp->login( user, password ) );
+ if ( useIODevice )
+ addCommand( QFtp::Put, ftp->put( &buf_fileData, file ) );
+ else
+ addCommand( QFtp::Put, ftp->put( fileData, file ) );
+ addCommand( QFtp::Close, ftp->close() );
+
+ for(int time = 0; time <= fileData.length() / 20000; time += timestep) {
+ QTestEventLoop::instance().enterLoop( timestep );
+ if(ftp->currentCommand() == QFtp::None)
+ break;
+ }
+ delete ftp;
+ ftp = 0;
+ if ( QTestEventLoop::instance().timeout() )
+ QFAIL( "Network operation timed out" );
+
+ it = resultMap.find( QFtp::Put );
+ QVERIFY( it != resultMap.end() );
+ QTEST( it.value().success, "success" );
+ if ( !it.value().success ) {
+ QVERIFY( !fileExists( host, port, user, password, file ) );
+ return; // the following tests are only meaningful if the file could be put
+ }
+ QVERIFY( bytesTotal == (int)fileData.size() );
+ QVERIFY( bytesDone == bytesTotal );
+
+ QVERIFY( fileExists( host, port, user, password, file ) );
+
+ //////////////////////////////////////////////////////////////////
+ // fetch file to make sure that it is equal to the uploaded file
+ init();
+ ftp = newFtp();
+ QBuffer buf;
+ buf.open( QIODevice::WriteOnly );
+ addCommand( QFtp::ConnectToHost, ftp->connectToHost( host, port ) );
+ addCommand( QFtp::Login, ftp->login( user, password ) );
+ addCommand( QFtp::Get, ftp->get( file, &buf ) );
+ addCommand( QFtp::Close, ftp->close() );
+
+ for(int time = 0; time <= fileData.length() / 20000; time += timestep) {
+ QTestEventLoop::instance().enterLoop( timestep );
+ if(ftp->currentCommand() == QFtp::None)
+ break;
+ }
+ delete ftp;
+ ftp = 0;
+ if ( QTestEventLoop::instance().timeout() )
+ QFAIL( "Network operation timed out" );
+
+ QVERIFY( done_success == 1 );
+ QTEST( buf.buffer(), "fileData" );
+
+ //////////////////////////////////////////////////////////////////
+ // cleanup (i.e. remove the file) -- this also tests the remove command
+ init();
+ ftp = newFtp();
+ addCommand( QFtp::ConnectToHost, ftp->connectToHost( host, port ) );
+ addCommand( QFtp::Login, ftp->login( user, password ) );
+ addCommand( QFtp::Remove, ftp->remove( file ) );
+ addCommand( QFtp::Close, ftp->close() );
+
+ QTestEventLoop::instance().enterLoop( timestep );
+ delete ftp;
+ ftp = 0;
+ if ( QTestEventLoop::instance().timeout() )
+ QFAIL( "Network operation timed out" );
+
+ it = resultMap.find( QFtp::Remove );
+ QVERIFY( it != resultMap.end() );
+ QCOMPARE( it.value().success, 1 );
+
+ QVERIFY( !fileExists( host, port, user, password, file ) );
+}
+
+void tst_QFtp::remove()
+{
+ DEPENDS_ON( "put" );
+}
+
+void tst_QFtp::mkdir_data()
+{
+ QTest::addColumn<QString>("host");
+ QTest::addColumn<uint>("port");
+ QTest::addColumn<QString>("user");
+ QTest::addColumn<QString>("password");
+ QTest::addColumn<QString>("cdDir");
+ QTest::addColumn<QString>("dirToCreate");
+ QTest::addColumn<int>("success");
+
+ QTest::newRow( "relPath01" ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString()
+ << "qtest/upload" << QString("rel01_%1") << 1;
+ QTest::newRow( "relPath02" ) << QtNetworkSettings::serverName() << (uint)21 << QString("ftptest") << QString("password")
+ << "qtest/upload" << QString("rel02_%1") << 1;
+ QTest::newRow( "relPath03" ) << QtNetworkSettings::serverName() << (uint)21 << QString("ftptest") << QString("password")
+ << "qtest/upload" << QString("rel03_%1") << 1;
+
+ QTest::newRow( "absPath01" ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString()
+ << "." << QString("/qtest/upload/abs01_%1") << 1;
+ QTest::newRow( "absPath02" ) << QtNetworkSettings::serverName() << (uint)21 << QString("ftptest") << QString("password")
+ << "." << QString("/var/ftp/qtest/upload/abs02_%1") << 1;
+
+ // QTest::newRow( "nonExist01" ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString() << QString("foo") << 0;
+ QTest::newRow( "nonExist01" ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString()
+ << "." << QString("foo") << 0;
+ QTest::newRow( "nonExist02" ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString()
+ << "." << QString("/foo") << 0;
+}
+
+void tst_QFtp::mkdir()
+{
+ QFETCH( QString, host );
+ QFETCH( uint, port );
+ QFETCH( QString, user );
+ QFETCH( QString, password );
+ QFETCH( QString, cdDir );
+ QFETCH( QString, dirToCreate );
+
+ if(dirToCreate.contains('%'))
+ dirToCreate = dirToCreate.arg(uniqueExtension);
+
+ //////////////////////////////////////////////////////////////////
+ // create the directory
+ init();
+ ftp = newFtp();
+ addCommand( QFtp::ConnectToHost, ftp->connectToHost( host, port ) );
+ addCommand( QFtp::Login, ftp->login( user, password ) );
+ addCommand( QFtp::Cd, ftp->cd( cdDir ) );
+ addCommand( QFtp::Mkdir, ftp->mkdir( dirToCreate ) );
+ addCommand( QFtp::Close, ftp->close() );
+
+ QTestEventLoop::instance().enterLoop( 30 );
+ delete ftp;
+ ftp = 0;
+ if ( QTestEventLoop::instance().timeout() )
+ QFAIL( "Network operation timed out" );
+
+ ResMapIt it = resultMap.find( QFtp::Mkdir );
+ QVERIFY( it != resultMap.end() );
+ QTEST( it.value().success, "success" );
+ if ( !it.value().success ) {
+ QVERIFY( !dirExists( host, port, user, password, cdDir, dirToCreate ) );
+ return; // the following tests are only meaningful if the dir could be created
+ }
+ QVERIFY( dirExists( host, port, user, password, cdDir, dirToCreate ) );
+
+ //////////////////////////////////////////////////////////////////
+ // create the directory again (should always fail!)
+ init();
+ ftp = newFtp();
+ addCommand( QFtp::ConnectToHost, ftp->connectToHost( host, port ) );
+ addCommand( QFtp::Login, ftp->login( user, password ) );
+ addCommand( QFtp::Cd, ftp->cd( cdDir ) );
+ addCommand( QFtp::Mkdir, ftp->mkdir( dirToCreate ) );
+ addCommand( QFtp::Close, ftp->close() );
+
+ QTestEventLoop::instance().enterLoop( 30 );
+ delete ftp;
+ ftp = 0;
+ if ( QTestEventLoop::instance().timeout() )
+ QFAIL( "Network operation timed out" );
+
+ it = resultMap.find( QFtp::Mkdir );
+ QVERIFY( it != resultMap.end() );
+ QCOMPARE( it.value().success, 0 );
+
+ //////////////////////////////////////////////////////////////////
+ // remove the directory
+ init();
+ ftp = newFtp();
+ addCommand( QFtp::ConnectToHost, ftp->connectToHost( host, port ) );
+ addCommand( QFtp::Login, ftp->login( user, password ) );
+ addCommand( QFtp::Cd, ftp->cd( cdDir ) );
+ addCommand( QFtp::Rmdir, ftp->rmdir( dirToCreate ) );
+ addCommand( QFtp::Close, ftp->close() );
+
+ QTestEventLoop::instance().enterLoop( 30 );
+ delete ftp;
+ ftp = 0;
+ if ( QTestEventLoop::instance().timeout() )
+ QFAIL( "Network operation timed out" );
+
+ it = resultMap.find( QFtp::Rmdir );
+ QVERIFY( it != resultMap.end() );
+ QCOMPARE( it.value().success, 1 );
+
+ QVERIFY( !dirExists( host, port, user, password, cdDir, dirToCreate ) );
+}
+
+void tst_QFtp::mkdir2()
+{
+ ftp = new QFtp;
+ ftp->connectToHost(QtNetworkSettings::serverName());
+ ftp->login();
+ current_id = ftp->cd("kake/test");
+
+ QEventLoop loop;
+ connect(ftp, SIGNAL(done(bool)), &loop, SLOT(quit()));
+ connect(ftp, SIGNAL(commandFinished(int, bool)), this, SLOT(mkdir2Slot(int, bool)));
+ QTimer::singleShot(5000, &loop, SLOT(quit()));
+
+ QSignalSpy commandStartedSpy(ftp, SIGNAL(commandStarted(int)));
+ QSignalSpy commandFinishedSpy(ftp, SIGNAL(commandFinished(int, bool)));
+
+ loop.exec();
+
+ QCOMPARE(commandStartedSpy.count(), 4); // connect, login, cd, mkdir
+ QCOMPARE(commandFinishedSpy.count(), 4);
+
+ for (int i = 0; i < 4; ++i)
+ QCOMPARE(commandFinishedSpy.at(i).at(0), commandStartedSpy.at(i).at(0));
+
+ QVERIFY(!commandFinishedSpy.at(0).at(1).toBool());
+ QVERIFY(!commandFinishedSpy.at(1).at(1).toBool());
+ QVERIFY(commandFinishedSpy.at(2).at(1).toBool());
+ QVERIFY(commandFinishedSpy.at(3).at(1).toBool());
+
+ delete ftp;
+ ftp = 0;
+}
+
+void tst_QFtp::mkdir2Slot(int id, bool)
+{
+ if (id == current_id)
+ ftp->mkdir("kake/test");
+}
+
+void tst_QFtp::rmdir()
+{
+ DEPENDS_ON( "mkdir" );
+}
+
+void tst_QFtp::rename_data()
+{
+ QTest::addColumn<QString>("host");
+ QTest::addColumn<QString>("user");
+ QTest::addColumn<QString>("password");
+ QTest::addColumn<QString>("cdDir");
+ QTest::addColumn<QString>("oldfile");
+ QTest::addColumn<QString>("newfile");
+ QTest::addColumn<QString>("createFile");
+ QTest::addColumn<QString>("renamedFile");
+ QTest::addColumn<int>("success");
+
+ QTest::newRow("relPath01") << QtNetworkSettings::serverName() << QString() << QString()
+ << "qtest/upload"
+ << QString("rel_old01_%1") << QString("rel_new01_%1")
+ << QString("qtest/upload/rel_old01_%1") << QString("qtest/upload/rel_new01_%1")
+ << 1;
+ QTest::newRow("relPath02") << QtNetworkSettings::serverName() << QString("ftptest") << "password"
+ << "qtest/upload"
+ << QString("rel_old02_%1") << QString("rel_new02_%1")
+ << QString("qtest/upload/rel_old02_%1") << QString("qtest/upload/rel_new02_%1")
+ << 1;
+ QTest::newRow("relPath03") << QtNetworkSettings::serverName() << QString("ftptest") << "password"
+ << "qtest/upload"
+ << QString("rel_old03_%1")<< QString("rel_new03_%1")
+ << QString("qtest/upload/rel_old03_%1") << QString("qtest/upload/rel_new03_%1")
+ << 1;
+
+ QTest::newRow("absPath01") << QtNetworkSettings::serverName() << QString() << QString()
+ << QString()
+ << QString("/qtest/upload/abs_old01_%1") << QString("/qtest/upload/abs_new01_%1")
+ << QString("/qtest/upload/abs_old01_%1") << QString("/qtest/upload/abs_new01_%1")
+ << 1;
+ QTest::newRow("absPath02") << QtNetworkSettings::serverName() << QString("ftptest") << "password"
+ << QString()
+ << QString("/var/ftp/qtest/upload/abs_old02_%1") << QString("/var/ftp/qtest/upload/abs_new02_%1")
+ << QString("/var/ftp/qtest/upload/abs_old02_%1") << QString("/var/ftp/qtest/upload/abs_new02_%1")
+ << 1;
+
+ QTest::newRow("nonExist01") << QtNetworkSettings::serverName() << QString() << QString()
+ << QString()
+ << QString("foo") << "new_foo"
+ << QString() << QString()
+ << 0;
+ QTest::newRow("nonExist02") << QtNetworkSettings::serverName() << QString() << QString()
+ << QString()
+ << QString("/foo") << QString("/new_foo")
+ << QString() << QString()
+ << 0;
+}
+
+void tst_QFtp::renameInit( const QString &host, const QString &user, const QString &password, const QString &createFile )
+{
+ if ( !createFile.isNull() ) {
+ // upload the file
+ init();
+ ftp = newFtp();
+ addCommand( QFtp::ConnectToHost, ftp->connectToHost( host ) );
+ addCommand( QFtp::Login, ftp->login( user, password ) );
+ addCommand( QFtp::Put, ftp->put( QByteArray(), createFile ) );
+ addCommand( QFtp::Close, ftp->close() );
+
+ QTestEventLoop::instance().enterLoop( 50 );
+ delete ftp;
+ ftp = 0;
+ if ( QTestEventLoop::instance().timeout() )
+ QFAIL( "Network operation timed out" );
+
+ ResMapIt it = resultMap.find( QFtp::Put );
+ QVERIFY( it != resultMap.end() );
+ QVERIFY( it.value().success == 1 );
+
+ QVERIFY( fileExists( host, 21, user, password, createFile ) );
+ }
+}
+
+void tst_QFtp::renameCleanup( const QString &host, const QString &user, const QString &password, const QString &fileToDelete )
+{
+ if ( !fileToDelete.isNull() ) {
+ // cleanup (i.e. remove the file)
+ init();
+ ftp = newFtp();
+ addCommand( QFtp::ConnectToHost, ftp->connectToHost( host ) );
+ addCommand( QFtp::Login, ftp->login( user, password ) );
+ addCommand( QFtp::Remove, ftp->remove( fileToDelete ) );
+ addCommand( QFtp::Close, ftp->close() );
+
+ QTestEventLoop::instance().enterLoop( 30 );
+ delete ftp;
+ ftp = 0;
+ if ( QTestEventLoop::instance().timeout() )
+ QFAIL( "Network operation timed out" );
+
+ ResMapIt it = resultMap.find( QFtp::Remove );
+ QVERIFY( it != resultMap.end() );
+ QVERIFY( it.value().success == 1 );
+
+ QVERIFY( !fileExists( host, 21, user, password, fileToDelete ) );
+ }
+}
+
+void tst_QFtp::rename()
+{
+ QFETCH( QString, host );
+ QFETCH( QString, user );
+ QFETCH( QString, password );
+ QFETCH( QString, cdDir );
+ QFETCH( QString, oldfile );
+ QFETCH( QString, newfile );
+ QFETCH( QString, createFile );
+ QFETCH( QString, renamedFile );
+
+ if(oldfile.contains('%'))
+ oldfile = oldfile.arg(uniqueExtension);
+ if(newfile.contains('%'))
+ newfile = newfile.arg(uniqueExtension);
+ if(createFile.contains('%'))
+ createFile = createFile.arg(uniqueExtension);
+ if(renamedFile.contains('%'))
+ renamedFile = renamedFile.arg(uniqueExtension);
+
+ renameInit( host, user, password, createFile );
+
+ init();
+ ftp = newFtp();
+ addCommand( QFtp::ConnectToHost, ftp->connectToHost( host ) );
+ addCommand( QFtp::Login, ftp->login( user, password ) );
+ if ( !cdDir.isNull() )
+ addCommand( QFtp::Cd, ftp->cd( cdDir ) );
+ addCommand( QFtp::Rename, ftp->rename( oldfile, newfile ) );
+ addCommand( QFtp::Close, ftp->close() );
+
+ QTestEventLoop::instance().enterLoop( 30 );
+ delete ftp;
+ ftp = 0;
+ if ( QTestEventLoop::instance().timeout() )
+ QFAIL( "Network operation timed out" );
+
+ ResMapIt it = resultMap.find( QFtp::Rename );
+ QVERIFY( it != resultMap.end() );
+ QTEST( it.value().success, "success" );
+
+ if ( it.value().success ) {
+ QVERIFY( !fileExists( host, 21, user, password, oldfile, cdDir ) );
+ QVERIFY( fileExists( host, 21, user, password, newfile, cdDir ) );
+ QVERIFY( fileExists( host, 21, user, password, renamedFile ) );
+ } else {
+ QVERIFY( !fileExists( host, 21, user, password, newfile, cdDir ) );
+ QVERIFY( !fileExists( host, 21, user, password, renamedFile ) );
+ }
+
+ renameCleanup( host, user, password, renamedFile );
+}
+
+/*
+ The commandSequence() test does not test any particular function. It rather
+ tests a sequence of arbitrary commands specified in the test data.
+*/
+class FtpCommand
+{
+public:
+ FtpCommand() :
+ cmd(QFtp::None)
+ { }
+
+ FtpCommand( QFtp::Command command ) :
+ cmd(command)
+ { }
+
+ FtpCommand( QFtp::Command command, const QStringList &arguments ) :
+ cmd(command), args(arguments)
+ { }
+
+ FtpCommand( const FtpCommand &c )
+ { *this = c; }
+
+ FtpCommand &operator=( const FtpCommand &c )
+ {
+ this->cmd = c.cmd;
+ this->args = c.args;
+ return *this;
+ }
+
+ QFtp::Command cmd;
+ QStringList args;
+};
+QDataStream &operator<<( QDataStream &s, const FtpCommand &command )
+{
+ s << (int)command.cmd;
+ s << command.args;
+ return s;
+}
+QDataStream &operator>>( QDataStream &s, FtpCommand &command )
+{
+ int tmp;
+ s >> tmp;
+ command.cmd = (QFtp::Command)tmp;
+ s >> command.args;
+ return s;
+}
+Q_DECLARE_METATYPE(QList<FtpCommand>)
+
+void tst_QFtp::commandSequence_data()
+{
+ // some "constants"
+ QStringList argConnectToHost01;
+ argConnectToHost01 << QtNetworkSettings::serverName() << "21";
+
+ QStringList argLogin01, argLogin02, argLogin03, argLogin04;
+ argLogin01 << QString() << QString();
+ argLogin02 << "ftp" << QString();
+ argLogin03 << "ftp" << "foo";
+ argLogin04 << QString("ftptest") << "password";
+
+ FtpCommand connectToHost01( QFtp::ConnectToHost, argConnectToHost01 );
+ FtpCommand login01( QFtp::Login, argLogin01 );
+ FtpCommand login02( QFtp::Login, argLogin01 );
+ FtpCommand login03( QFtp::Login, argLogin01 );
+ FtpCommand login04( QFtp::Login, argLogin01 );
+ FtpCommand close01( QFtp::Close );
+
+ QTest::addColumn<QList<FtpCommand> >("cmds");
+ QTest::addColumn<int>("success");
+
+ // success data
+ {
+ QList<FtpCommand> cmds;
+ cmds << connectToHost01;
+ QTest::newRow( "simple_ok01" ) << cmds << 1;
+ }
+ {
+ QList<FtpCommand> cmds;
+ cmds << connectToHost01;
+ cmds << login01;
+ QTest::newRow( "simple_ok02" ) << cmds << 1;
+ }
+ {
+ QList<FtpCommand> cmds;
+ cmds << connectToHost01;
+ cmds << login01;
+ cmds << close01;
+ QTest::newRow( "simple_ok03" ) << cmds << 1;
+ }
+ {
+ QList<FtpCommand> cmds;
+ cmds << connectToHost01;
+ cmds << close01;
+ QTest::newRow( "simple_ok04" ) << cmds << 1;
+ }
+ {
+ QList<FtpCommand> cmds;
+ cmds << connectToHost01;
+ cmds << login01;
+ cmds << close01;
+ cmds << connectToHost01;
+ cmds << login02;
+ cmds << close01;
+ QTest::newRow( "connect_twice" ) << cmds << 1;
+ }
+
+ // error data
+ {
+ QList<FtpCommand> cmds;
+ cmds << close01;
+ QTest::newRow( "error01" ) << cmds << 0;
+ }
+ {
+ QList<FtpCommand> cmds;
+ cmds << login01;
+ QTest::newRow( "error02" ) << cmds << 0;
+ }
+ {
+ QList<FtpCommand> cmds;
+ cmds << login01;
+ cmds << close01;
+ QTest::newRow( "error03" ) << cmds << 0;
+ }
+ {
+ QList<FtpCommand> cmds;
+ cmds << connectToHost01;
+ cmds << login01;
+ cmds << close01;
+ cmds << login01;
+ QTest::newRow( "error04" ) << cmds << 0;
+ }
+}
+
+void tst_QFtp::commandSequence()
+{
+ QFETCH( QList<FtpCommand>, cmds );
+
+ ftp = newFtp();
+ QList<FtpCommand>::iterator it;
+ for ( it = cmds.begin(); it != cmds.end(); ++it ) {
+ switch ( (*it).cmd ) {
+ case QFtp::ConnectToHost:
+ {
+ QVERIFY( (*it).args.count() == 2 );
+ uint port;
+ bool portOk;
+ port = (*it).args[1].toUInt( &portOk );
+ QVERIFY( portOk );
+ ids << ftp->connectToHost( (*it).args[0], port );
+ }
+ break;
+ case QFtp::Login:
+ QVERIFY( (*it).args.count() == 2 );
+ ids << ftp->login( (*it).args[0], (*it).args[1] );
+ break;
+ case QFtp::Close:
+ QVERIFY( (*it).args.count() == 0 );
+ ids << ftp->close();
+ break;
+ default:
+ QFAIL( "Error in test: unexpected enum value" );
+ break;
+ }
+ }
+
+ QTestEventLoop::instance().enterLoop( 30 );
+ delete ftp;
+ ftp = 0;
+ if ( QTestEventLoop::instance().timeout() )
+ QFAIL( "Network operation timed out" );
+
+ QTEST( commandSequence_success, "success" );
+}
+
+void tst_QFtp::abort_data()
+{
+ QTest::addColumn<QString>("host");
+ QTest::addColumn<uint>("port");
+ QTest::addColumn<QString>("file");
+ QTest::addColumn<QByteArray>("uploadData");
+
+ QTest::newRow( "get_fluke01" ) << QtNetworkSettings::serverName() << (uint)21 << QString("qtest/bigfile") << QByteArray();
+ QTest::newRow( "get_fluke02" ) << QtNetworkSettings::serverName() << (uint)21 << QString("qtest/rfc3252") << QByteArray();
+
+ // Qt/CE and Symbian test environment has to less memory for this test
+#if !defined(Q_OS_WINCE) && !defined(Q_OS_SYMBIAN)
+ QByteArray bigData( 10*1024*1024, 0 );
+#else
+ QByteArray bigData( 1*1024*1024, 0 );
+#endif
+ bigData.fill( 'B' );
+ QTest::newRow( "put_fluke01" ) << QtNetworkSettings::serverName() << (uint)21 << QString("qtest/upload/abort_put") << bigData;
+}
+
+void tst_QFtp::abort()
+{
+ // In case you wonder where the abort() actually happens, look into
+ // tst_QFtp::dataTransferProgress
+ //
+ QFETCH( QString, host );
+ QFETCH( uint, port );
+ QFETCH( QString, file );
+ QFETCH( QByteArray, uploadData );
+
+ QFtp::Command cmd;
+ if ( uploadData.size() == 0 )
+ cmd = QFtp::Get;
+ else
+ cmd = QFtp::Put;
+
+ ftp = newFtp();
+ addCommand( QFtp::ConnectToHost, ftp->connectToHost( host, port ) );
+ addCommand( QFtp::Login, ftp->login() );
+ if ( cmd == QFtp::Get )
+ addCommand( cmd, ftp->get( file ) );
+ else
+ addCommand( cmd, ftp->put( uploadData, file ) );
+ addCommand( QFtp::Close, ftp->close() );
+
+ for(int time = 0; time <= uploadData.length() / 30000; time += 30) {
+ QTestEventLoop::instance().enterLoop( 50 );
+ if(ftp->currentCommand() == QFtp::None)
+ break;
+ }
+ delete ftp;
+ ftp = 0;
+ if ( QTestEventLoop::instance().timeout() )
+ QFAIL( "Network operation timed out" );
+
+ ResMapIt it = resultMap.find( cmd );
+ QVERIFY( it != resultMap.end() );
+ // ### how to test the abort?
+ if ( it.value().success ) {
+ // The FTP server on fluke is sadly returning a success, even when
+ // the operation was aborted. So we have to use some heuristics.
+ if ( host == QtNetworkSettings::serverName() ) {
+ if ( cmd == QFtp::Get ) {
+ QVERIFY(bytesDone <= bytesTotal);
+ } else {
+ // put commands should always be aborted, since we use really
+ // big data
+ QVERIFY( bytesDone != bytesTotal );
+ }
+ } else {
+ // this could be tested by verifying that no more progress signals are emited
+ QVERIFY(bytesDone <= bytesTotal);
+ }
+ } else {
+ QVERIFY( bytesDone != bytesTotal );
+ }
+
+ if ( cmd == QFtp::Put ) {
+ //////////////////////////////////////
+ // cleanup (i.e. remove the file)
+ init();
+ ftp = newFtp();
+ addCommand( QFtp::ConnectToHost, ftp->connectToHost( host, port ) );
+ addCommand( QFtp::Login, ftp->login() );
+ addCommand( QFtp::Remove, ftp->remove( file ) );
+ addCommand( QFtp::Close, ftp->close() );
+
+ QTestEventLoop::instance().enterLoop( 30 );
+ delete ftp;
+ ftp = 0;
+ if ( QTestEventLoop::instance().timeout() )
+ QFAIL( "Network operation timed out" );
+
+ it = resultMap.find( QFtp::Remove );
+ QVERIFY( it != resultMap.end() );
+ QVERIFY( it.value().success == 1 );
+ }
+}
+
+void tst_QFtp::bytesAvailable_data()
+{
+ QTest::addColumn<QString>("host");
+ QTest::addColumn<QString>("file");
+ QTest::addColumn<int>("type");
+ QTest::addColumn<qlonglong>("bytesAvailFinishedGet");
+ QTest::addColumn<qlonglong>("bytesAvailFinished");
+ QTest::addColumn<qlonglong>("bytesAvailDone");
+
+ QTest::newRow( "fluke01" ) << QtNetworkSettings::serverName() << QString("qtest/bigfile") << 0 << (qlonglong)519240 << (qlonglong)519240 << (qlonglong)519240;
+ QTest::newRow( "fluke02" ) << QtNetworkSettings::serverName() << QString("qtest/rfc3252") << 0 << (qlonglong)25962 << (qlonglong)25962 << (qlonglong)25962;
+
+ QTest::newRow( "fluke03" ) << QtNetworkSettings::serverName() << QString("qtest/bigfile") << 1 << (qlonglong)519240 << (qlonglong)0 << (qlonglong)0;
+ QTest::newRow( "fluke04" ) << QtNetworkSettings::serverName() << QString("qtest/rfc3252") << 1 << (qlonglong)25962 << (qlonglong)0 << (qlonglong)0;
+}
+
+void tst_QFtp::bytesAvailable()
+{
+ QFETCH( QString, host );
+ QFETCH( QString, file );
+ QFETCH( int, type );
+
+ ftp = newFtp();
+ addCommand( QFtp::ConnectToHost, ftp->connectToHost( host ) );
+ addCommand( QFtp::Login, ftp->login() );
+ addCommand( QFtp::Get, ftp->get( file ) );
+ if ( type != 0 )
+ addCommand( QFtp::Close, ftp->close() );
+
+ QTestEventLoop::instance().enterLoop( 40 );
+ if ( QTestEventLoop::instance().timeout() )
+ QFAIL( "Network operation timed out" );
+
+ ResMapIt it = resultMap.find( QFtp::Get );
+ QVERIFY( it != resultMap.end() );
+ QVERIFY( it.value().success );
+
+ QFETCH(qlonglong, bytesAvailFinishedGet);
+ QCOMPARE(bytesAvailable_finishedGet, bytesAvailFinishedGet);
+
+ QFETCH(qlonglong, bytesAvailFinished);
+ QCOMPARE(bytesAvailable_finished, bytesAvailFinished);
+
+ QFETCH(qlonglong, bytesAvailDone);
+ QCOMPARE(bytesAvailable_done, bytesAvailDone);
+
+ ftp->readAll();
+ QVERIFY( ftp->bytesAvailable() == 0 );
+ delete ftp;
+ ftp = 0;
+}
+
+void tst_QFtp::activeMode()
+{
+ QFile file("tst_QFtp_activeMode_inittab");
+ file.open(QIODevice::ReadWrite);
+ QFtp ftp;
+ ftp.setTransferMode(QFtp::Active);
+ ftp.connectToHost(QtNetworkSettings::serverName(), 21);
+ ftp.login();
+ ftp.list();
+ ftp.get("/qtest/rfc3252.txt", &file);
+ connect(&ftp, SIGNAL(done(bool)), SLOT(activeModeDone(bool)));
+ QTestEventLoop::instance().enterLoop(900);
+ QFile::remove("tst_QFtp_activeMode_inittab");
+ QVERIFY(done_success == 1);
+
+}
+
+void tst_QFtp::activeModeDone(bool error)
+{
+ done_success = error ? -1 : 1;
+ QTestEventLoop::instance().exitLoop();
+}
+
+void tst_QFtp::proxy_data()
+{
+ QTest::addColumn<QString>("host");
+ QTest::addColumn<uint>("port");
+ QTest::addColumn<QString>("user");
+ QTest::addColumn<QString>("password");
+ QTest::addColumn<QString>("dir");
+ QTest::addColumn<int>("success");
+ QTest::addColumn<QStringList>("entryNames"); // ### we should rather use a QList<QUrlInfo> here
+
+ QStringList flukeRoot;
+ flukeRoot << "qtest";
+ QStringList flukeQtest;
+ flukeQtest << "bigfile";
+ flukeQtest << "nonASCII";
+ flukeQtest << "rfc3252";
+ flukeQtest << "rfc3252.txt";
+ flukeQtest << "upload";
+
+ QTest::newRow( "proxy_relPath01" ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString() << QString("qtest") << 1 << flukeQtest;
+ QTest::newRow( "proxy_relPath02" ) << QtNetworkSettings::serverName() << (uint)21 << QString("ftptest") << QString("password") << QString("qtest") << 1 << flukeQtest;
+
+ QTest::newRow( "proxy_absPath01" ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString() << QString("/qtest") << 1 << flukeQtest;
+ QTest::newRow( "proxy_absPath02" ) << QtNetworkSettings::serverName() << (uint)21 << QString("ftptest") << QString("password") << QString("/var/ftp/qtest") << 1 << flukeQtest;
+
+ QTest::newRow( "proxy_nonExist01" ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString() << QString("foo") << 0 << QStringList();
+ QTest::newRow( "proxy_nonExist03" ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString() << QString("/foo") << 0 << QStringList();
+}
+
+void tst_QFtp::proxy()
+{
+ QFETCH( QString, host );
+ QFETCH( uint, port );
+ QFETCH( QString, user );
+ QFETCH( QString, password );
+ QFETCH( QString, dir );
+
+ ftp = newFtp();
+ addCommand( QFtp::SetProxy, ftp->setProxy( QtNetworkSettings::serverName(), 2121 ) );
+ addCommand( QFtp::ConnectToHost, ftp->connectToHost( host, port ) );
+ addCommand( QFtp::Login, ftp->login( user, password ) );
+ addCommand( QFtp::Cd, ftp->cd( dir ) );
+ addCommand( QFtp::List, ftp->list() );
+
+ QTestEventLoop::instance().enterLoop( 50 );
+
+ delete ftp;
+ ftp = 0;
+ if ( QTestEventLoop::instance().timeout() ) {
+ QFAIL( "Network operation timed out" );
+ }
+
+ ResMapIt it = resultMap.find( QFtp::Cd );
+ QVERIFY( it != resultMap.end() );
+ QFETCH( int, success );
+ QCOMPARE( it.value().success, success );
+ QFETCH( QStringList, entryNames );
+ QCOMPARE( listInfo_i.count(), entryNames.count() );
+ for ( uint i=0; i < (uint) entryNames.count(); i++ ) {
+ QCOMPARE( listInfo_i[i].name(), entryNames[i] );
+ }
+}
+
+void tst_QFtp::binaryAscii()
+{
+ QString file = "asciifile%1.txt";
+
+ if(file.contains('%'))
+ file = file.arg(uniqueExtension);
+
+ QByteArray putData = "a line of text\r\n";
+
+ init();
+ ftp = newFtp();
+ addCommand(QFtp::ConnectToHost, ftp->connectToHost(QtNetworkSettings::serverName(), 21));
+ addCommand(QFtp::Login, ftp->login("ftptest", "password"));
+ addCommand(QFtp::Cd, ftp->cd("qtest/upload"));
+ addCommand(QFtp::Put, ftp->put(putData, file, QFtp::Ascii));
+ addCommand(QFtp::Close, ftp->close());
+
+ QTestEventLoop::instance().enterLoop( 30 );
+ delete ftp;
+ ftp = 0;
+ if ( QTestEventLoop::instance().timeout() )
+ QFAIL( "Network operation timed out" );
+
+ ResMapIt it = resultMap.find(QFtp::Put);
+ QVERIFY(it != resultMap.end());
+ QVERIFY(it.value().success);
+
+ QByteArray getData;
+ QBuffer getBuf(&getData);
+ getBuf.open(QBuffer::WriteOnly);
+
+ init();
+ ftp = newFtp();
+ addCommand(QFtp::ConnectToHost, ftp->connectToHost(QtNetworkSettings::serverName(), 21));
+ addCommand(QFtp::Login, ftp->login("ftptest", "password"));
+ addCommand(QFtp::Cd, ftp->cd("qtest/upload"));
+ addCommand(QFtp::Get, ftp->get(file, &getBuf, QFtp::Binary));
+ addCommand(QFtp::Close, ftp->close());
+
+ QTestEventLoop::instance().enterLoop( 30 );
+ delete ftp;
+ ftp = 0;
+ if ( QTestEventLoop::instance().timeout() )
+ QFAIL( "Network operation timed out" );
+
+ ResMapIt it2 = resultMap.find(QFtp::Get);
+ QVERIFY(it2 != resultMap.end());
+ QVERIFY(it2.value().success);
+ // most modern ftp servers leave the file as it is by default
+ // (and do not remove the windows line ending), the -1 below could be
+ // deleted in the future
+ QVERIFY(getData.size() == putData.size()-1);
+ //////////////////////////////////////////////////////////////////
+ // cleanup (i.e. remove the file) -- this also tests the remove command
+ init();
+ ftp = newFtp();
+ addCommand(QFtp::ConnectToHost, ftp->connectToHost(QtNetworkSettings::serverName(), 21));
+ addCommand(QFtp::Login, ftp->login("ftptest", "password"));
+ addCommand(QFtp::Cd, ftp->cd("qtest/upload"));
+ addCommand(QFtp::Remove, ftp->remove(file));
+ addCommand(QFtp::Close, ftp->close());
+
+ QTestEventLoop::instance().enterLoop( 30 );
+ delete ftp;
+ ftp = 0;
+ if ( QTestEventLoop::instance().timeout() )
+ QFAIL( "Network operation timed out" );
+
+ it = resultMap.find( QFtp::Remove );
+ QVERIFY( it != resultMap.end() );
+ QCOMPARE( it.value().success, 1 );
+
+ QVERIFY(!fileExists(QtNetworkSettings::serverName(), 21, "ftptest", "password", file));
+}
+
+
+// test QFtp::currentId() and QFtp::currentCommand()
+#define CURRENTCOMMAND_TEST \
+{ \
+ ResMapIt it; \
+ for ( it = resultMap.begin(); it != resultMap.end(); ++it ) { \
+ if ( it.value().id == ftp->currentId() ) { \
+ QVERIFY( it.key() == ftp->currentCommand() ); \
+ } \
+} \
+}
+
+void tst_QFtp::commandStarted( int id )
+{
+#if defined( DUMP_SIGNALS )
+ qDebug( "%d:commandStarted( %d )", ftp->currentId(), id );
+#endif
+ // make sure that the commandStarted and commandFinished are nested correctly
+ QVERIFY( current_id == 0 );
+ current_id = id;
+
+ QVERIFY( !ids.isEmpty() );
+ QVERIFY( ids.first() == id );
+ if ( ids.count() > 1 ) {
+ QVERIFY( ftp->hasPendingCommands() );
+ } else {
+ QVERIFY( !ftp->hasPendingCommands() );
+ }
+
+ QVERIFY( ftp->currentId() == id );
+ QVERIFY( cur_state == ftp->state() );
+ CURRENTCOMMAND_TEST;
+
+ QVERIFY( ftp->error() == QFtp::NoError );
+}
+
+void tst_QFtp::commandFinished( int id, bool error )
+{
+#if defined( DUMP_SIGNALS )
+ qDebug( "%d:commandFinished( %d, %d ) -- errorString: '%s'",
+ ftp->currentId(), id, (int)error, ftp->errorString().toLatin1().constData() );
+#endif
+ if ( ftp->currentCommand() == QFtp::Get ) {
+ bytesAvailable_finishedGet = ftp->bytesAvailable();
+ }
+ bytesAvailable_finished = ftp->bytesAvailable();
+
+ // make sure that the commandStarted and commandFinished are nested correctly
+ QVERIFY( current_id == id );
+ current_id = 0;
+
+ QVERIFY( !ids.isEmpty() );
+ QVERIFY( ids.first() == id );
+ if ( !error && ids.count() > 1) {
+ QVERIFY( ftp->hasPendingCommands() );
+ } else {
+ QVERIFY( !ftp->hasPendingCommands() );
+ }
+ if ( error ) {
+ QVERIFY( ftp->error() != QFtp::NoError );
+ ids.clear();
+ } else {
+ QVERIFY( ftp->error() == QFtp::NoError );
+ ids.pop_front();
+ }
+
+ QVERIFY( ftp->currentId() == id );
+ QVERIFY( cur_state == ftp->state() );
+ CURRENTCOMMAND_TEST;
+
+ if ( QTest::currentTestFunction() != QLatin1String("commandSequence") ) {
+ ResMapIt it = resultMap.find( ftp->currentCommand() );
+ QVERIFY( it != resultMap.end() );
+ QVERIFY( it.value().success == -1 );
+ if ( error )
+ it.value().success = 0;
+ else
+ it.value().success = 1;
+ }
+}
+
+void tst_QFtp::done( bool error )
+{
+#if defined( DUMP_SIGNALS )
+ qDebug( "%d:done( %d )", ftp->currentId(), (int)error );
+#endif
+ bytesAvailable_done = ftp->bytesAvailable();
+
+ QVERIFY( ftp->currentId() == 0 );
+ QVERIFY( current_id == 0 );
+ QVERIFY( ids.isEmpty() );
+ QVERIFY( cur_state == ftp->state() );
+ QVERIFY( !ftp->hasPendingCommands() );
+
+ if ( QTest::currentTestFunction() == QLatin1String("commandSequence") ) {
+ QVERIFY( commandSequence_success == -1 );
+ if ( error )
+ commandSequence_success = 0;
+ else
+ commandSequence_success = 1;
+ }
+ QVERIFY( done_success == -1 );
+ if ( error ) {
+ QVERIFY( ftp->error() != QFtp::NoError );
+ done_success = 0;
+ } else {
+ QVERIFY( ftp->error() == QFtp::NoError );
+ done_success = 1;
+ }
+ QTestEventLoop::instance().exitLoop();
+}
+
+void tst_QFtp::stateChanged( int state )
+{
+#if defined( DUMP_SIGNALS )
+ qDebug( "%d: stateChanged( %d )", ftp->currentId(), state );
+#endif
+ QCOMPARE( ftp->currentId(), current_id );
+ CURRENTCOMMAND_TEST;
+
+ QVERIFY( state != cur_state );
+ QCOMPARE( state, (int)ftp->state() );
+ if ( state != QFtp::Unconnected ) {
+ // make sure that the states are always emitted in the right order (for
+ // this, we assume an ordering on the enum values, which they have at
+ // the moment)
+ QVERIFY( cur_state < state );
+
+ // make sure that state changes are only emitted in response to certain
+ // commands
+ switch ( state ) {
+ case QFtp::HostLookup:
+ case QFtp::Connecting:
+ case QFtp::Connected:
+ QCOMPARE( (int)ftp->currentCommand(), (int)QFtp::ConnectToHost );
+ break;
+ case QFtp::LoggedIn:
+ QCOMPARE( (int)ftp->currentCommand(), (int)QFtp::Login );
+ break;
+ case QFtp::Closing:
+ QCOMPARE( (int)ftp->currentCommand(), (int)QFtp::Close );
+ break;
+ default:
+ QWARN( QString("Unexpected state '%1'").arg(state).toLatin1().constData() );
+ break;
+ }
+ }
+ cur_state = state;
+
+ if ( QTest::currentTestFunction() == QLatin1String("connectToHost") ) {
+ switch ( state ) {
+ case QFtp::HostLookup:
+ case QFtp::Connecting:
+ case QFtp::LoggedIn:
+ case QFtp::Closing:
+ // ignore
+ break;
+ case QFtp::Connected:
+ case QFtp::Unconnected:
+ QVERIFY( connectToHost_state == -1 );
+ connectToHost_state = state;
+ break;
+ default:
+ QWARN( QString("Unknown state '%1'").arg(state).toLatin1().constData() );
+ break;
+ }
+ } else if ( QTest::currentTestFunction() == QLatin1String("close") ) {
+ ResMapIt it = resultMap.find( QFtp::Close );
+ if ( it!=resultMap.end() && ftp->currentId()==it.value().id ) {
+ if ( state == QFtp::Closing ) {
+ QVERIFY( close_state == -1 );
+ close_state = state;
+ } else if ( state == QFtp::Unconnected ) {
+ QVERIFY( close_state == QFtp::Closing );
+ close_state = state;
+ }
+ }
+ } else if ( QTest::currentTestFunction() == QLatin1String("login") ) {
+ ResMapIt it = resultMap.find( QFtp::Login );
+ if ( it!=resultMap.end() && ftp->currentId()==it.value().id ) {
+ if ( state == QFtp::LoggedIn ) {
+ QVERIFY( login_state == -1 );
+ login_state = state;
+ }
+ }
+ }
+}
+
+void tst_QFtp::listInfo( const QUrlInfo &i )
+{
+#if defined( DUMP_SIGNALS )
+ qDebug( "%d: listInfo( %s )", ftp->currentId(), i.name().toLatin1().constData() );
+#endif
+ QCOMPARE( ftp->currentId(), current_id );
+ if ( ids.count() > 1 ) {
+ QVERIFY( ftp->hasPendingCommands() );
+ } else {
+ QVERIFY( !ftp->hasPendingCommands() );
+ }
+ QVERIFY( cur_state == ftp->state() );
+ CURRENTCOMMAND_TEST;
+
+ if ( QTest::currentTestFunction()==QLatin1String("list") || QTest::currentTestFunction()==QLatin1String("cd") || QTest::currentTestFunction()==QLatin1String("proxy") || inFileDirExistsFunction ) {
+ ResMapIt it = resultMap.find( QFtp::List );
+ QVERIFY( it != resultMap.end() );
+ QVERIFY( ftp->currentId() == it.value().id );
+ listInfo_i << i;
+ }
+}
+
+void tst_QFtp::readyRead()
+{
+#if defined( DUMP_SIGNALS )
+ qDebug( "%d: readyRead(), bytesAvailable == %lu", ftp->currentId(), ftp->bytesAvailable() );
+#endif
+ QCOMPARE( ftp->currentId(), current_id );
+ if ( ids.count() > 1 ) {
+ QVERIFY( ftp->hasPendingCommands() );
+ } else {
+ QVERIFY( !ftp->hasPendingCommands() );
+ }
+ QVERIFY( cur_state == ftp->state() );
+ CURRENTCOMMAND_TEST;
+
+ if ( QTest::currentTestFunction() != QLatin1String("bytesAvailable") ) {
+ int oldSize = newData_ba.size();
+ qlonglong bytesAvail = ftp->bytesAvailable();
+ QByteArray ba = ftp->readAll();
+ QVERIFY( ba.size() == (int) bytesAvail );
+ newData_ba.resize( oldSize + ba.size() );
+ memcpy( newData_ba.data()+oldSize, ba.data(), ba.size() );
+
+ if ( bytesTotal != -1 ) {
+ QVERIFY( (int)newData_ba.size() <= bytesTotal );
+ }
+ QVERIFY( (int)newData_ba.size() == bytesDone );
+ }
+}
+
+void tst_QFtp::dataTransferProgress( qint64 done, qint64 total )
+{
+#if defined( DUMP_SIGNALS )
+ qDebug( "%d: dataTransferProgress( %lli, %lli )", ftp->currentId(), done, total );
+#endif
+ QCOMPARE( ftp->currentId(), current_id );
+ if ( ids.count() > 1 ) {
+ QVERIFY( ftp->hasPendingCommands() );
+ } else {
+ QVERIFY( !ftp->hasPendingCommands() );
+ }
+ QVERIFY( cur_state == ftp->state() );
+ CURRENTCOMMAND_TEST;
+
+ if ( bytesTotal == bytesTotal_init ) {
+ bytesTotal = total;
+ } else {
+ QVERIFY( bytesTotal == total );
+ }
+
+ QVERIFY( bytesTotal != bytesTotal_init );
+ QVERIFY( bytesDone <= done );
+ bytesDone = done;
+ if ( bytesTotal != -1 ) {
+ QVERIFY( bytesDone <= bytesTotal );
+ }
+
+ if ( QTest::currentTestFunction() == QLatin1String("abort") ) {
+ // ### it would be nice if we could specify in our testdata when to do
+ // the abort
+ if ( done >= total/100000 ) {
+ if ( ids.count() != 1 ) {
+ // do abort only once
+ int tmpId = ids.first();
+ ids.clear();
+ ids << tmpId;
+ ftp->abort();
+ }
+ }
+ }
+}
+
+
+QFtp *tst_QFtp::newFtp()
+{
+ QFtp *nFtp = new QFtp( this );
+#ifndef QT_NO_BEARERMANAGEMENT
+ if (networkSessionExplicit) {
+ nFtp->setProperty("_q_networksession", QVariant::fromValue(networkSessionExplicit));
+ }
+#endif
+ connect( nFtp, SIGNAL(commandStarted(int)),
+ SLOT(commandStarted(int)) );
+ connect( nFtp, SIGNAL(commandFinished(int,bool)),
+ SLOT(commandFinished(int,bool)) );
+ connect( nFtp, SIGNAL(done(bool)),
+ SLOT(done(bool)) );
+ connect( nFtp, SIGNAL(stateChanged(int)),
+ SLOT(stateChanged(int)) );
+ connect( nFtp, SIGNAL(listInfo(const QUrlInfo&)),
+ SLOT(listInfo(const QUrlInfo&)) );
+ connect( nFtp, SIGNAL(readyRead()),
+ SLOT(readyRead()) );
+ connect( nFtp, SIGNAL(dataTransferProgress(qint64, qint64)),
+ SLOT(dataTransferProgress(qint64, qint64)) );
+
+ return nFtp;
+}
+
+void tst_QFtp::addCommand( QFtp::Command cmd, int id )
+{
+ ids << id;
+ CommandResult res;
+ res.id = id;
+ res.success = -1;
+ resultMap[ cmd ] = res;
+}
+
+bool tst_QFtp::fileExists( const QString &host, quint16 port, const QString &user, const QString &password, const QString &file, const QString &cdDir )
+{
+ init();
+ ftp = newFtp();
+ // ### make these tests work
+ if (ftp->currentId() != 0) {
+ qWarning("ftp->currentId() != 0");
+ return FALSE;
+ }
+
+ if (ftp->state() != QFtp::Unconnected) {
+ qWarning("ftp->state() != QFtp::Unconnected");
+ return FALSE;
+ }
+
+ addCommand( QFtp::ConnectToHost, ftp->connectToHost( host, port ) );
+ addCommand( QFtp::Login, ftp->login( user, password ) );
+ if ( !cdDir.isNull() )
+ addCommand( QFtp::Cd, ftp->cd( cdDir ) );
+ addCommand( QFtp::List, ftp->list( file ) );
+ addCommand( QFtp::Close, ftp->close() );
+
+ inFileDirExistsFunction = TRUE;
+ QTestEventLoop::instance().enterLoop( 30 );
+ delete ftp;
+ ftp = 0;
+ if ( QTestEventLoop::instance().timeout() ) {
+ // ### make this test work
+ qWarning("tst_QFtp::fileExists: Network operation timed out");
+ return FALSE;
+ }
+ inFileDirExistsFunction = FALSE;
+
+ ResMapIt it = resultMap.find( QFtp::ConnectToHost );
+ // ### make these tests work
+ if (it == resultMap.end()) {
+ qWarning("it != resultMap.end()");
+ return FALSE;
+ }
+
+ if (it.value().success == -1) {
+ qWarning("it.value().success != -1");
+ return FALSE;
+ }
+
+ if ( it.value().success == 1 ) {
+ for ( uint i=0; i < (uint) listInfo_i.count(); i++ ) {
+ if ( QFileInfo(listInfo_i[i].name()).fileName() == QFileInfo(file).fileName() )
+ return TRUE;
+ }
+ }
+
+ //this is not a good warning considering sometime this function is used to test that a file does not exist
+ //qWarning("file doesn't exist");
+ return FALSE;
+}
+
+bool tst_QFtp::dirExists( const QString &host, quint16 port, const QString &user, const QString &password, const QString &cdDir, const QString &dirToCreate )
+{
+ init();
+ ftp = newFtp();
+ // ### make these tests work
+ // QCOMPARE( ftp->currentId(), 0 );
+ // QCOMPARE( (int)ftp->state(), (int)QFtp::Unconnected );
+
+ addCommand( QFtp::ConnectToHost, ftp->connectToHost( host, port ) );
+ addCommand( QFtp::Login, ftp->login( user, password ) );
+ if ( dirToCreate.startsWith( "/" ) )
+ addCommand( QFtp::Cd, ftp->cd( dirToCreate ) );
+ else
+ addCommand( QFtp::Cd, ftp->cd( cdDir + "/" + dirToCreate ) );
+ addCommand( QFtp::Close, ftp->close() );
+
+ inFileDirExistsFunction = TRUE;
+ QTestEventLoop::instance().enterLoop( 30 );
+ delete ftp;
+ ftp = 0;
+ if ( QTestEventLoop::instance().timeout() ) {
+ // ### make this test work
+ // QFAIL( "Network operation timed out" );
+ qWarning("tst_QFtp::dirExists: Network operation timed out");
+ return FALSE;
+ }
+ inFileDirExistsFunction = FALSE;
+
+ ResMapIt it = resultMap.find( QFtp::Cd );
+ // ### make these tests work
+ // QVERIFY( it != resultMap.end() );
+ // QVERIFY( it.value().success != -1 );
+ return it.value().success == 1;
+}
+
+void tst_QFtp::doneSignal()
+{
+ QFtp ftp;
+ QSignalSpy spy(&ftp, SIGNAL(done(bool)));
+
+ ftp.connectToHost(QtNetworkSettings::serverName());
+ ftp.login("anonymous");
+ ftp.list();
+ ftp.close();
+
+ done_success = 0;
+ connect(&ftp, SIGNAL(done(bool)), &(QTestEventLoop::instance()), SLOT(exitLoop()));
+ QTestEventLoop::instance().enterLoop(61);
+ if (QTestEventLoop::instance().timeout())
+ QFAIL("Network operation timed out");
+
+ QTest::qWait(200);
+
+ QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.first().first().toBool(), false);
+}
+
+void tst_QFtp::queueMoreCommandsInDoneSlot()
+{
+ QSKIP("Task 127050 && 113966", SkipSingle);
+
+ QFtp ftp;
+ QSignalSpy doneSpy(&ftp, SIGNAL(done(bool)));
+ QSignalSpy commandFinishedSpy(&ftp, SIGNAL(commandFinished(int, bool)));
+
+ this->ftp = &ftp;
+ connect(&ftp, SIGNAL(done(bool)), this, SLOT(cdUpSlot(bool)));
+
+ ftp.connectToHost("ftp.qt.nokia.com");
+ ftp.login();
+ ftp.cd("qt");
+ ftp.rmdir("qtest-removedir-noexist");
+
+ while ( ftp.hasPendingCommands() || ftp.currentCommand() != QFtp::None ) {
+ QCoreApplication::instance()->processEvents(QEventLoop::AllEvents
+ | QEventLoop::WaitForMoreEvents);
+ }
+
+ QCOMPARE(doneSpy.count(), 2);
+ QCOMPARE(doneSpy.first().first().toBool(), true);
+ QCOMPARE(doneSpy.last().first().toBool(), false);
+
+ QCOMPARE(commandFinishedSpy.count(), 6);
+ int firstId = commandFinishedSpy.at(0).at(0).toInt();
+ QCOMPARE(commandFinishedSpy.at(0).at(1).toBool(), false);
+ QCOMPARE(commandFinishedSpy.at(1).at(0).toInt(), firstId + 1);
+ QCOMPARE(commandFinishedSpy.at(1).at(1).toBool(), false);
+ QCOMPARE(commandFinishedSpy.at(2).at(0).toInt(), firstId + 2);
+ QCOMPARE(commandFinishedSpy.at(2).at(1).toBool(), false);
+ QCOMPARE(commandFinishedSpy.at(3).at(0).toInt(), firstId + 3);
+ QCOMPARE(commandFinishedSpy.at(3).at(1).toBool(), true);
+ QCOMPARE(commandFinishedSpy.at(4).at(0).toInt(), firstId + 4);
+ QCOMPARE(commandFinishedSpy.at(4).at(1).toBool(), false);
+ QCOMPARE(commandFinishedSpy.at(5).at(0).toInt(), firstId + 5);
+ QCOMPARE(commandFinishedSpy.at(5).at(1).toBool(), false);
+
+ this->ftp = 0;
+}
+
+void tst_QFtp::cdUpSlot(bool error)
+{
+ if (error) {
+ ftp->cd("..");
+ ftp->cd("qt");
+ }
+}
+
+void tst_QFtp::qtbug7359Crash()
+{
+ QFtp ftp;
+ ftp.connectToHost("127.0.0.1");
+
+ QTime t;
+ int elapsed;
+
+ t.start();
+ while ((elapsed = t.elapsed()) < 200)
+ QCoreApplication::processEvents(QEventLoop::AllEvents, 200 - elapsed);
+
+ ftp.close();
+ t.restart();
+ while ((elapsed = t.elapsed()) < 1000)
+ QCoreApplication::processEvents(QEventLoop::AllEvents, 1000 - elapsed);
+
+ ftp.connectToHost("127.0.0.1");
+
+ t.restart();
+ while ((elapsed = t.elapsed()) < 2000)
+ QCoreApplication::processEvents(QEventLoop::AllEvents, 2000 - elapsed);
+}
+
+QTEST_MAIN(tst_QFtp)
+
+#include "tst_qftp.moc"