1
1
use std:: io:: { Read , BufRead , BufReader , BufWriter , Cursor , Write , copy} ;
2
+ #[ cfg( feature = "secure" ) ]
3
+ use std:: error:: Error ;
2
4
use std:: net:: { TcpStream , SocketAddr } ;
3
5
use std:: string:: String ;
4
6
use std:: str:: FromStr ;
@@ -7,7 +9,7 @@ use regex::Regex;
7
9
use chrono:: { DateTime , UTC } ;
8
10
use chrono:: offset:: TimeZone ;
9
11
#[ cfg( feature = "secure" ) ]
10
- use openssl:: ssl:: { Ssl , SslContext , SslMethod , SslStream , IntoSsl } ;
12
+ use openssl:: ssl:: { Ssl , SslStream , IntoSsl } ;
11
13
use super :: data_stream:: DataStream ;
12
14
use super :: status;
13
15
use super :: types:: { FileType , FtpError , Line , Result } ;
@@ -24,61 +26,44 @@ lazy_static! {
24
26
static ref SIZE_RE : Regex = Regex :: new( r"\s+(\d+)\s*$" ) . unwrap( ) ;
25
27
}
26
28
27
- #[ cfg( feature = "secure" ) ]
28
- lazy_static ! {
29
- // Shared SSL context
30
- static ref SSL_CONTEXT : SslContext = match SslContext :: new( SslMethod :: Sslv23 ) {
31
- Ok ( ctx) => ctx,
32
- Err ( e) => panic!( "{}" , e)
33
- } ;
34
- }
35
-
36
29
/// Stream to interface with the FTP server. This interface is only for the command stream.
37
30
#[ derive( Debug ) ]
38
31
pub struct FtpStream {
39
32
reader : BufReader < DataStream > ,
33
+ #[ cfg( feature = "secure" ) ]
34
+ ssl_cfg : Option < Ssl > ,
40
35
}
41
36
42
37
impl FtpStream {
43
38
/// Creates an FTP Stream.
39
+ #[ cfg( not( feature = "secure" ) ) ]
44
40
pub fn connect < A : ToSocketAddrs > ( addr : A ) -> Result < FtpStream > {
45
41
TcpStream :: connect ( addr)
46
42
. map_err ( |e| FtpError :: ConnectionError ( e) )
47
43
. and_then ( |stream| {
48
44
let mut ftp_stream = FtpStream {
49
- reader : BufReader :: new ( DataStream :: Tcp ( stream) )
45
+ reader : BufReader :: new ( DataStream :: Tcp ( stream) ) ,
50
46
} ;
51
47
ftp_stream. read_response ( status:: READY )
52
48
. map ( |_| ftp_stream)
53
49
} )
54
50
}
55
-
56
- /// Switch to secure mode if possible. If the connection is already
57
- /// secure does nothing.
58
- ///
59
- /// ## Panics
60
- ///
61
- /// Panics if the plain TCP connection cannot be switched to TLS mode.
62
- ///
63
- /// ## Example
64
- ///
65
- /// ```
66
- /// use ftp::FtpStream;
67
- /// let mut ftp_stream = FtpStream::connect("127.0.0.1:21").unwrap();
68
- /// // Switch to the secure mode
69
- /// let mut ftp_stream = ftp_stream.secure().unwrap();
70
- /// // Do all secret things
71
- /// let _ = ftp_stream.quit();
72
- /// ```
73
- ///
51
+
52
+ /// Creates an FTP Stream.
74
53
#[ cfg( feature = "secure" ) ]
75
- pub fn secure ( mut self ) -> Result < FtpStream > {
76
- // Initialize SSL with a default context and make secure the stream.
77
- Ssl :: new ( & SSL_CONTEXT )
78
- . map_err ( |e| FtpError :: SecureError ( e. description ( ) . to_owned ( ) ) )
79
- . and_then ( |ssl| self . secure_with_ssl ( ssl) )
54
+ pub fn connect < A : ToSocketAddrs > ( addr : A ) -> Result < FtpStream > {
55
+ TcpStream :: connect ( addr)
56
+ . map_err ( |e| FtpError :: ConnectionError ( e) )
57
+ . and_then ( |stream| {
58
+ let mut ftp_stream = FtpStream {
59
+ reader : BufReader :: new ( DataStream :: Tcp ( stream) ) ,
60
+ ssl_cfg : None ,
61
+ } ;
62
+ ftp_stream. read_response ( status:: READY )
63
+ . map ( |_| ftp_stream)
64
+ } )
80
65
}
81
-
66
+
82
67
/// Switch to a secure mode if possible, using a provided SSL configuration.
83
68
/// This method does nothing if the connect is already secured.
84
69
///
@@ -96,22 +81,20 @@ impl FtpStream {
96
81
/// let mut ctx = SslContext::new(SslMethod::Sslv23).unwrap();
97
82
/// let _ = ctx.set_CA_file("/path/to/a/cert.pem").unwrap();
98
83
/// let mut ftp_stream = FtpStream::connect("127.0.0.1:21").unwrap();
99
- /// let mut ftp_stream = ftp_stream.secure_with_ssl (ctx).unwrap();
84
+ /// let mut ftp_stream = ftp_stream.into_secure (ctx).unwrap();
100
85
/// ```
101
86
#[ cfg( feature = "secure" ) ]
102
- pub fn secure_with_ssl < S : IntoSsl > ( mut self , ssl : S ) -> Result < FtpStream > {
103
- // Do nothing if the connection is already secured.
104
- if self . reader . get_ref ( ) . is_ssl ( ) {
105
- return Ok ( self ) ;
106
- }
87
+ pub fn into_secure < T : IntoSsl + Clone > ( mut self , ssl : T ) -> Result < FtpStream > {
107
88
// Ask the server to start securing data.
108
89
let auth_command = String :: from ( "AUTH TLS\r \n " ) ;
109
90
try!( self . write_str ( & auth_command) ) ;
110
91
try!( self . read_response ( status:: AUTH_OK ) ) ;
92
+ let ssl_copy = try!( ssl. clone ( ) . into_ssl ( ) . map_err ( |e| FtpError :: SecureError ( e. description ( ) . to_owned ( ) ) ) ) ;
111
93
let stream = try!( SslStream :: connect ( ssl, self . reader . into_inner ( ) . into_tcp_stream ( ) )
112
94
. map_err ( |e| FtpError :: SecureError ( e. description ( ) . to_owned ( ) ) ) ) ;
113
95
let mut secured_ftp_tream = FtpStream {
114
96
reader : BufReader :: new ( DataStream :: Ssl ( stream) ) ,
97
+ ssl_cfg : Some ( ssl_copy)
115
98
} ;
116
99
// Set protection buffer size
117
100
let pbsz_command = format ! ( "PBSZ 0\r \n " ) ;
@@ -123,7 +106,7 @@ impl FtpStream {
123
106
try!( secured_ftp_tream. read_response ( status:: COMMAND_OK ) ) ;
124
107
Ok ( secured_ftp_tream)
125
108
}
126
-
109
+
127
110
/// Switch to insecure mode. If the connection is already
128
111
/// insecure does nothing.
129
112
///
@@ -140,21 +123,46 @@ impl FtpStream {
140
123
/// // Do all public things
141
124
/// let _ = ftp_stream.quit();
142
125
/// ```
143
- ///
144
126
#[ cfg( feature = "secure" ) ]
145
- pub fn insecure ( mut self ) -> Result < FtpStream > {
146
- if !self . reader . get_ref ( ) . is_ssl ( ) {
147
- return Ok ( self ) ;
148
- }
127
+ pub fn into_insecure ( mut self ) -> Result < FtpStream > {
149
128
// Ask the server to stop securing data
150
129
let ccc_command = String :: from ( "CCC\r \n " ) ;
151
130
try!( self . write_str ( & ccc_command) ) ;
152
131
try!( self . read_response ( status:: COMMAND_OK ) ) ;
153
132
let plain_ftp_stream = FtpStream {
154
133
reader : BufReader :: new ( DataStream :: Tcp ( self . reader . into_inner ( ) . into_tcp_stream ( ) ) ) ,
134
+ ssl_cfg : None ,
155
135
} ;
156
136
Ok ( plain_ftp_stream)
157
137
}
138
+
139
+ /// Execute command which send data back in a separate stream
140
+ #[ cfg( not( feature = "secure" ) ) ]
141
+ fn data_command ( & mut self , cmd : & str ) -> Result < DataStream > {
142
+ self . pasv ( )
143
+ . and_then ( |addr| self . write_str ( cmd) . map ( |_| addr) )
144
+ . and_then ( |addr| TcpStream :: connect ( addr)
145
+ . map_err ( |e| FtpError :: ConnectionError ( e) ) )
146
+ . map ( |stream| DataStream :: Tcp ( stream) )
147
+ }
148
+
149
+ /// Execute command which send data back in a separate stream
150
+ #[ cfg( feature = "secure" ) ]
151
+ fn data_command ( & mut self , cmd : & str ) -> Result < DataStream > {
152
+ self . pasv ( )
153
+ . and_then ( |addr| self . write_str ( cmd) . map ( |_| addr) )
154
+ . and_then ( |addr| TcpStream :: connect ( addr) . map_err ( |e| FtpError :: ConnectionError ( e) ) )
155
+ . and_then ( |stream| {
156
+ match self . ssl_cfg {
157
+ Some ( ref ssl) => {
158
+ SslStream :: connect ( ssl. clone ( ) , stream)
159
+ . map ( |stream| DataStream :: Ssl ( stream) )
160
+ . map_err ( |e| FtpError :: SecureError ( e. description ( ) . to_owned ( ) ) )
161
+ } ,
162
+ None => Ok ( DataStream :: Tcp ( stream) )
163
+ }
164
+ } )
165
+ }
158
166
159
167
/// Log in to the FTP server.
160
168
pub fn login ( & mut self , user : & str , password : & str ) -> Result < ( ) > {
@@ -242,33 +250,6 @@ impl FtpStream {
242
250
} )
243
251
}
244
252
245
- // Execute command which send data back in a separate stream
246
- #[ cfg( not( feature = "secure" ) ) ]
247
- fn data_command ( & mut self , cmd : & str ) -> Result < DataStream > {
248
- self . pasv ( )
249
- . and_then ( |addr| self . write_str ( cmd) . map ( |_| addr) )
250
- . and_then ( |addr| TcpStream :: connect ( addr)
251
- . map_err ( |e| FtpError :: ConnectionError ( e) ) )
252
- . map ( |stream| DataStream :: Tcp ( stream) )
253
- }
254
-
255
- #[ cfg( feature = "secure" ) ]
256
- fn data_command ( & mut self , cmd : & str ) -> Result < DataStream > {
257
- self . pasv ( )
258
- . and_then ( |addr| self . write_str ( cmd) . map ( |_| addr) )
259
- . and_then ( |addr| TcpStream :: connect ( addr) )
260
- . and_then ( |stream| {
261
- if self . reader . get_ref ( ) . is_ssl ( ) {
262
- Ssl :: new ( & SSL_CONTEXT )
263
- . and_then ( |ssl| SslStream :: connect ( ssl, stream) )
264
- . map ( |stream| DataStream :: Ssl ( stream) )
265
- . map_err ( |e| FtpError :: SecureError ( e. description ( ) . to_owned ( ) ) )
266
- } else {
267
- Ok ( DataStream :: Tcp ( stream) )
268
- }
269
- } )
270
- }
271
-
272
253
/// Sets the type of file to be transferred. That is the implementation
273
254
/// of `TYPE` command.
274
255
pub fn transfer_type ( & mut self , file_type : FileType ) -> Result < ( ) > {
0 commit comments