77use peer \SSLSocket ;
88use peer \SocketException ;
99use util \log \Traceable ;
10-
10+ use lang \ IllegalArgumentException ;
1111
1212/**
1313 * FTP client
1414 *
1515 * Usage example:
16- * <code>
17- * $c= create(new FtpConnection('ftp://user:pass@example.com/'))->connect();
18- *
19- * // Retrieve root directory's listing
20- * Console::writeLine($c->rootDir()->entries());
16+ * ```
17+ * $c= create(new FtpConnection('ftp://user:pass@example.com/'))->connect();
18+ *
19+ * // Retrieve root directory's listing
20+ * Console::writeLine($c->rootDir()->entries());
2121 *
22- * $c->close();
23- * </code>
22+ * $c->close();
23+ * ```
2424 *
25- * @test xp://net.xp_framework.unittest.peer.ftp.IntegrationTest
26- * @see rfc ://959
27- * @purpose FTP protocol implementation
25+ * @test xp://net.xp_framework.unittest.peer.ftp.FtpConnectionTest
26+ * @test xp ://net.xp_framework.unittest.peer.ftp.IntegrationTest
27+ * @see rfc://959
2828 */
2929class FtpConnection extends \lang \Object implements Traceable {
3030 protected
@@ -39,31 +39,64 @@ class FtpConnection extends \lang\Object implements Traceable {
3939
4040 /**
4141 * Constructor. Accepts a DSN of the following form:
42- * <pre>
43- * {scheme}://[{user}:{password}@]{host}[:{port}]/[?{options}]
44- * </pre>
42+ * `{scheme}://[{user}:{password}@]{host}[:{port}]/[?{options}]`
4543 *
4644 * Scheme is one of the following:
47- * <ul>
48- * <li>ftp (default)</li>
49- * <li>ftps (with SSL)</li>
50- * </ul>
45+ * - ftp (default)
46+ * - ftps (with SSL)
5147 *
5248 * Note: SSL connect is only available if OpenSSL support is enabled
5349 * into your version of PHP.
5450 *
5551 * Options include:
56- * <ul>
57- * <li>timeout - integer value indicating connection timeout in seconds, default: 4</li>
58- * <li>passive - boolean value controlling whether to use passive mode or not</li>
59- * </ul>
52+ * - timeout - integer value indicating connection timeout in seconds, default: 4
53+ * - passive - boolean value controlling whether to use passive mode or not, default: true
6054 *
61- * @param string dsn
55+ * @param string|peer.URL $dsn
56+ * @throws lang.IllegalArgumentException if scheme is unsupported
6257 */
6358 public function __construct ($ dsn ) {
64- $ this ->url = new URL ($ dsn );
59+ $ this ->url = $ dsn instanceof URL ? $ dsn : new URL ($ dsn );
60+
61+ switch ($ this ->url ->getScheme ()) {
62+ case 'ftp ' :
63+ $ this ->socket = new Socket ($ this ->url ->getHost (), $ this ->url ->getPort (21 ));
64+ break ;
65+
66+ case 'ftps ' :
67+ $ this ->socket = new SSLSocket ($ this ->url ->getHost (), $ this ->url ->getPort (21 ));
68+ break ;
69+
70+ default :
71+ throw new IllegalArgumentException ('Unsupported scheme " ' .$ this ->url ->getScheme ().'" ' );
72+ }
73+
74+ switch (strtolower ($ this ->url ->getParam ('passive ' , 'false ' ))) {
75+ case 'true ' : case 'yes ' : case 'on ' : case '1 ' :
76+ $ this ->setPassive (true );
77+ break ;
78+
79+ case 'false ' : case 'no ' : case 'off ' : case '0 ' :
80+ $ this ->setPassive (false );
81+ break ;
82+
83+ default :
84+ throw new IllegalArgumentException ('Unexpected value " ' .$ this ->url ->getParam ('passive ' ).'" for passive ' );
85+ }
6586 }
66-
87+
88+ /** @return string */
89+ public function user () { return $ this ->url ->getUser (); }
90+
91+ /** @return peer.SocketEndpoint */
92+ public function remoteEndpoint () { return $ this ->socket ->remoteEndpoint (); }
93+
94+ /** @return bool */
95+ public function passive () { return $ this ->passive ; }
96+
97+ /** @return double */
98+ public function timeout () { return (double )$ this ->url ->getParam ('timeout ' , 4 ); }
99+
67100 /**
68101 * Connect (and log in, if necessary)
69102 *
@@ -73,47 +106,29 @@ public function __construct($dsn) {
73106 * @throws peer.SocketException for general I/O failures
74107 */
75108 public function connect () {
76- $ host = $ this ->url ->getHost ();
77- $ port = $ this ->url ->getPort (21 );
78- $ timeout = $ this ->url ->getParam ('timeout ' , 4 );
79-
80- switch ($ this ->url ->getScheme ()) {
81- case 'ftp ' :
82- $ this ->socket = new Socket ($ host , $ port );
83- break ;
84-
85- case 'ftps ' :
86- $ this ->socket = new SSLSocket ($ host , $ port );
87- break ;
88- }
89-
90- $ this ->socket ->connect ($ timeout );
109+ $ this ->socket ->connect ($ this ->timeout ());
91110
92111 // Read banner message
93112 $ this ->expect ($ this ->getResponse (), [220 ]);
94113
95114 // User & password
96- if ($ this ->url ->getUser ()) {
115+ if (null !== ( $ user = $ this ->url ->getUser () )) {
97116 try {
98- $ this ->expect ($ this ->sendCommand ('USER %s ' , $ this -> url -> getUser () ), [331 ]);
117+ $ this ->expect ($ this ->sendCommand ('USER %s ' , $ user ), [331 ]);
99118 $ this ->expect ($ this ->sendCommand ('PASS %s ' , $ this ->url ->getPassword ()), [230 ]);
100119 } catch (\peer \ProtocolException $ e ) {
101120 $ this ->socket ->close ();
102121 throw new AuthenticationException (sprintf (
103- 'Authentication failed for %s@%s (using password: %s): %s ' ,
104- $ this ->url ->getUser (),
105- $ host ,
122+ 'Authentication failed for %s@%s:%d (using password: %s): %s ' ,
123+ $ this ->url ->getUser (),
124+ $ this ->url ->getPort (21 ),
125+ $ this ->url ->getHost (),
106126 $ this ->url ->getPassword () ? 'yes ' : 'no ' ,
107127 $ e ->getMessage ()
108128 ), $ this ->url ->getUser (), $ this ->url ->getPassword ());
109129 }
110130 }
111131
112- // Set passive mode
113- if (null !== ($ pasv = $ this ->url ->getParam ('passive ' ))) {
114- $ this ->setPassive ((bool )$ pasv );
115- }
116-
117132 // Setup list parser
118133 $ this ->setupListParser ();
119134
@@ -154,7 +169,16 @@ public function close() {
154169 }
155170 return true ;
156171 }
157-
172+
173+ /**
174+ * Returns true if connection is established
175+ *
176+ * @return bool
177+ */
178+ public function isConnected () {
179+ return $ this ->socket !== null && $ this ->socket ->isConnected ();
180+ }
181+
158182 /**
159183 * Retrieve transfer socket
160184 *
@@ -176,7 +200,6 @@ public function transferSocket() {
176200 *
177201 * @param bool enable enable or disable passive mode
178202 * @return bool success
179- * @throws peer.SocketException
180203 */
181204 public function setPassive ($ enable ) {
182205 $ this ->passive = $ enable ;
@@ -187,9 +210,7 @@ public function setPassive($enable) {
187210 *
188211 * @return peer.ftp.FtpDir
189212 */
190- public function rootDir () {
191- return $ this ->root ;
192- }
213+ public function rootDir () { return $ this ->root ; }
193214
194215 /**
195216 * Read response
0 commit comments