1
+ //! FTP module.
2
+
3
+ use std:: borrow:: Cow ;
1
4
use std:: io:: { Read , BufRead , BufReader , BufWriter , Cursor , Write , copy} ;
2
5
#[ cfg( feature = "secure" ) ]
3
6
use std:: error:: Error ;
@@ -86,8 +89,7 @@ impl FtpStream {
86
89
#[ cfg( feature = "secure" ) ]
87
90
pub fn into_secure < T : IntoSsl + Clone > ( mut self , ssl : T ) -> Result < FtpStream > {
88
91
// Ask the server to start securing data.
89
- let auth_command = String :: from ( "AUTH TLS\r \n " ) ;
90
- try!( self . write_str ( & auth_command) ) ;
92
+ try!( self . write_str ( "AUTH TLS\r \n " ) ) ;
91
93
try!( self . read_response ( status:: AUTH_OK ) ) ;
92
94
let ssl_copy = try!( ssl. clone ( ) . into_ssl ( ) . map_err ( |e| FtpError :: SecureError ( e. description ( ) . to_owned ( ) ) ) ) ;
93
95
let stream = try!( SslStream :: connect ( ssl, self . reader . into_inner ( ) . into_tcp_stream ( ) )
@@ -97,12 +99,10 @@ impl FtpStream {
97
99
ssl_cfg : Some ( ssl_copy)
98
100
} ;
99
101
// Set protection buffer size
100
- let pbsz_command = format ! ( "PBSZ 0\r \n " ) ;
101
- try!( secured_ftp_tream. write_str ( & pbsz_command) ) ;
102
+ try!( secured_ftp_tream. write_str ( "PBSZ 0\r \n " ) ) ;
102
103
try!( secured_ftp_tream. read_response ( status:: COMMAND_OK ) ) ;
103
104
// Change the level of data protectio to Private
104
- let prot_command = String :: from ( "PROT P\r \n " ) ;
105
- try!( secured_ftp_tream. write_str ( & prot_command) ) ;
105
+ try!( secured_ftp_tream. write_str ( "PROT P\r \n " ) ) ;
106
106
try!( secured_ftp_tream. read_response ( status:: COMMAND_OK ) ) ;
107
107
Ok ( secured_ftp_tream)
108
108
}
@@ -130,8 +130,7 @@ impl FtpStream {
130
130
#[ cfg( feature = "secure" ) ]
131
131
pub fn into_insecure ( mut self ) -> Result < FtpStream > {
132
132
// Ask the server to stop securing data
133
- let ccc_command = String :: from ( "CCC\r \n " ) ;
134
- try!( self . write_str ( & ccc_command) ) ;
133
+ try!( self . write_str ( "CCC\r \n " ) ) ;
135
134
try!( self . read_response ( status:: COMMAND_OK ) ) ;
136
135
let plain_ftp_stream = FtpStream {
137
136
reader : BufReader :: new ( DataStream :: Tcp ( self . reader . into_inner ( ) . into_tcp_stream ( ) ) ) ,
@@ -185,13 +184,11 @@ impl FtpStream {
185
184
186
185
/// Log in to the FTP server.
187
186
pub fn login ( & mut self , user : & str , password : & str ) -> Result < ( ) > {
188
- let user_command = format ! ( "USER {}\r \n " , user) ;
189
- try!( self . write_str ( & user_command) ) ;
187
+ try!( self . write_str ( format ! ( "USER {}\r \n " , user) ) ) ;
190
188
self . read_response_in ( & [ status:: LOGGED_IN , status:: NEED_PASSWORD ] )
191
189
. and_then ( |Line ( code, _) | {
192
190
if code == status:: NEED_PASSWORD {
193
- let pass_command = format ! ( "PASS {}\r \n " , password) ;
194
- try!( self . write_str ( & pass_command) ) ;
191
+ try!( self . write_str ( format ! ( "PASS {}\r \n " , password) ) ) ;
195
192
try!( self . read_response ( status:: LOGGED_IN ) ) ;
196
193
}
197
194
Ok ( ( ) )
@@ -200,15 +197,13 @@ impl FtpStream {
200
197
201
198
/// Change the current directory to the path specified.
202
199
pub fn cwd ( & mut self , path : & str ) -> Result < ( ) > {
203
- let cwd_command = format ! ( "CWD {}\r \n " , path) ;
204
- try!( self . write_str ( & cwd_command) ) ;
200
+ try!( self . write_str ( format ! ( "CWD {}\r \n " , path) ) ) ;
205
201
self . read_response ( status:: REQUESTED_FILE_ACTION_OK ) . map ( |_| ( ) )
206
202
}
207
203
208
204
/// Move the current directory to the parent directory.
209
205
pub fn cdup ( & mut self ) -> Result < ( ) > {
210
- let cdup_command = format ! ( "CDUP\r \n " ) ;
211
- try!( self . write_str ( & cdup_command) ) ;
206
+ try!( self . write_str ( "CDUP\r \n " ) ) ;
212
207
self . read_response ( status:: REQUESTED_FILE_ACTION_OK ) . map ( |_| ( ) )
213
208
}
214
209
@@ -231,15 +226,13 @@ impl FtpStream {
231
226
232
227
/// This does nothing. This is usually just used to keep the connection open.
233
228
pub fn noop ( & mut self ) -> Result < ( ) > {
234
- let noop_command = format ! ( "NOOP\r \n " ) ;
235
- try!( self . write_str ( & noop_command) ) ;
229
+ try!( self . write_str ( "NOOP\r \n " ) ) ;
236
230
self . read_response ( status:: COMMAND_OK ) . map ( |_| ( ) )
237
231
}
238
232
239
233
/// This creates a new directory on the server.
240
234
pub fn mkdir ( & mut self , pathname : & str ) -> Result < ( ) > {
241
- let mkdir_command = format ! ( "MKD {}\r \n " , pathname) ;
242
- try!( self . write_str ( & mkdir_command) ) ;
235
+ try!( self . write_str ( format ! ( "MKD {}\r \n " , pathname) ) ) ;
243
236
self . read_response ( status:: PATH_CREATED ) . map ( |_| ( ) )
244
237
}
245
238
@@ -279,8 +272,7 @@ impl FtpStream {
279
272
280
273
/// Quits the current FTP session.
281
274
pub fn quit ( & mut self ) -> Result < ( ) > {
282
- let quit_command = format ! ( "QUIT\r \n " ) ;
283
- try!( self . write_str ( & quit_command) ) ;
275
+ try!( self . write_str ( "QUIT\r \n " ) ) ;
284
276
self . read_response ( status:: CLOSING ) . map ( |_| ( ) )
285
277
}
286
278
@@ -296,12 +288,10 @@ impl FtpStream {
296
288
297
289
/// Renames the file from_name to to_name
298
290
pub fn rename ( & mut self , from_name : & str , to_name : & str ) -> Result < ( ) > {
299
- let rnfr_command = format ! ( "RNFR {}\r \n " , from_name) ;
300
- try!( self . write_str ( & rnfr_command) ) ;
291
+ try!( self . write_str ( format ! ( "RNFR {}\r \n " , from_name) ) ) ;
301
292
self . read_response ( status:: REQUEST_FILE_PENDING )
302
293
. and_then ( |_| {
303
- let rnto_command = format ! ( "RNTO {}\r \n " , to_name) ;
304
- try!( self . write_str ( & rnto_command) ) ;
294
+ try!( self . write_str ( format ! ( "RNTO {}\r \n " , to_name) ) ) ;
305
295
self . read_response ( status:: REQUESTED_FILE_ACTION_OK ) . map ( |_| ( ) )
306
296
} )
307
297
}
@@ -326,32 +316,15 @@ impl FtpStream {
326
316
/// }).is_ok());
327
317
/// # assert!(conn.rm("retr.txt").is_ok());
328
318
/// ```
329
- pub fn retr < F > ( & mut self , filename : & str , reader : F ) -> Result < ( ) >
330
- where F : Fn ( & mut Read ) -> Result < ( ) > {
319
+ pub fn retr < F , T > ( & mut self , filename : & str , reader : F ) -> Result < T >
320
+ where F : Fn ( & mut Read ) -> Result < T > {
331
321
let retr_command = format ! ( "RETR {}\r \n " , filename) ;
332
- let mut data_stream = BufReader :: new ( try!( self . data_command ( & retr_command) ) ) ;
333
- self . read_response_in ( & [ status:: ABOUT_TO_SEND , status:: ALREADY_OPEN ] )
334
- . and_then ( |_| {
335
- let result = reader ( & mut data_stream) ;
336
- drop ( data_stream) ;
337
- try!( self . read_response ( status:: CLOSING_DATA_CONNECTION ) ) ;
338
- result
339
- } )
340
- }
341
-
342
- fn simple_retr_ ( & mut self , file_name : & str ) -> Result < Cursor < Vec < u8 > > > {
343
- let mut data_stream = try!( self . get ( file_name) ) ;
344
- let buffer: & mut Vec < u8 > = & mut Vec :: new ( ) ;
345
- loop {
346
- let mut buf = [ 0 ; 256 ] ;
347
- let len = try!( data_stream. read ( & mut buf) . map_err ( |read_err| FtpError :: ConnectionError ( read_err) ) ) ;
348
- if len == 0 {
349
- break ;
350
- }
351
- try!( buffer. write ( & buf[ 0 ..len] ) . map_err ( |write_err| FtpError :: ConnectionError ( write_err) ) ) ;
352
- }
353
- drop ( data_stream) ;
354
- Ok ( Cursor :: new ( buffer. clone ( ) ) )
322
+ {
323
+ let mut data_stream = BufReader :: new ( try!( self . data_command ( & retr_command) ) ) ;
324
+ self . read_response_in ( & [ status:: ABOUT_TO_SEND , status:: ALREADY_OPEN ] )
325
+ . and_then ( |_| reader ( & mut data_stream) )
326
+ } . and_then ( |res|
327
+ self . read_response ( status:: CLOSING_DATA_CONNECTION ) . map ( |_| res) )
355
328
}
356
329
357
330
/// Simple way to retr a file from the server. This stores the file in memory.
@@ -370,21 +343,21 @@ impl FtpStream {
370
343
/// # assert!(conn.rm("simple_retr.txt").is_ok());
371
344
/// ```
372
345
pub fn simple_retr ( & mut self , file_name : & str ) -> Result < Cursor < Vec < u8 > > > {
373
- let r = try!( self . simple_retr_ ( file_name) ) ;
374
- self . read_response ( status:: CLOSING_DATA_CONNECTION ) . map ( |_| r)
346
+ self . retr ( file_name, |reader| {
347
+ let mut buffer = Vec :: new ( ) ;
348
+ reader. read_to_end ( & mut buffer) . map ( |_| buffer) . map_err ( |read_err| FtpError :: ConnectionError ( read_err) )
349
+ } ) . map ( |buffer| Cursor :: new ( buffer) )
375
350
}
376
351
377
352
/// Removes the remote pathname from the server.
378
353
pub fn rmdir ( & mut self , pathname : & str ) -> Result < ( ) > {
379
- let rmd_command = format ! ( "RMD {}\r \n " , pathname) ;
380
- try!( self . write_str ( & rmd_command) ) ;
354
+ try!( self . write_str ( format ! ( "RMD {}\r \n " , pathname) ) ) ;
381
355
self . read_response ( status:: REQUESTED_FILE_ACTION_OK ) . map ( |_| ( ) )
382
356
}
383
357
384
358
/// Remove the remote file from the server.
385
359
pub fn rm ( & mut self , filename : & str ) -> Result < ( ) > {
386
- let rm_command = format ! ( "DELE {}\r \n " , filename) ;
387
- try!( self . write_str ( & rm_command) ) ;
360
+ try!( self . write_str ( format ! ( "DELE {}\r \n " , filename) ) ) ;
388
361
self . read_response ( status:: REQUESTED_FILE_ACTION_OK ) . map ( |_| ( ) )
389
362
}
390
363
@@ -404,7 +377,7 @@ impl FtpStream {
404
377
}
405
378
406
379
/// Execute a command which returns list of strings in a separate stream
407
- fn list_command ( & mut self , cmd : String , open_code : u32 , close_code : u32 ) -> Result < Vec < String > > {
380
+ fn list_command ( & mut self , cmd : Cow < ' static , str > , open_code : u32 , close_code : u32 ) -> Result < Vec < String > > {
408
381
let mut lines: Vec < String > = Vec :: new ( ) ;
409
382
{
410
383
let mut data_stream = BufReader :: new ( try!( self . data_command ( & cmd) ) ) ;
@@ -427,10 +400,7 @@ impl FtpStream {
427
400
/// If `pathname` is omited then the list of files in the current directory will be
428
401
/// returned otherwise it will the list of files on `pathname`.
429
402
pub fn list ( & mut self , pathname : Option < & str > ) -> Result < Vec < String > > {
430
- let command = match pathname {
431
- Some ( path) => format ! ( "LIST {}\r \n " , path) ,
432
- None => String :: from ( "LIST\r \n " ) ,
433
- } ;
403
+ let command = pathname. map_or ( "LIST\r \n " . into ( ) , |path| format ! ( "LIST {}\r \n " , path) . into ( ) ) ;
434
404
435
405
self . list_command ( command, status:: ABOUT_TO_SEND , status:: CLOSING_DATA_CONNECTION )
436
406
}
@@ -439,19 +409,15 @@ impl FtpStream {
439
409
/// If `pathname` is omited then the list of files in the current directory will be
440
410
/// returned otherwise it will the list of files on `pathname`.
441
411
pub fn nlst ( & mut self , pathname : Option < & str > ) -> Result < Vec < String > > {
442
- let command = match pathname {
443
- Some ( path) => format ! ( "NLST {}\r \n " , path) ,
444
- None => String :: from ( "NLST\r \n " ) ,
445
- } ;
412
+ let command = pathname. map_or ( "NLST\r \n " . into ( ) , |path| format ! ( "NLST {}\r \n " , path) . into ( ) ) ;
446
413
447
414
self . list_command ( command, status:: ABOUT_TO_SEND , status:: CLOSING_DATA_CONNECTION )
448
415
}
449
416
450
417
/// Retrieves the modification time of the file at `pathname` if it exists.
451
418
/// In case the file does not exist `None` is returned.
452
419
pub fn mdtm ( & mut self , pathname : & str ) -> Result < Option < DateTime < UTC > > > {
453
- let mdtm_command = format ! ( "MDTM {}\r \n " , pathname) ;
454
- try!( self . write_str ( & mdtm_command) ) ;
420
+ try!( self . write_str ( format ! ( "MDTM {}\r \n " , pathname) ) ) ;
455
421
let Line ( _, content) = try!( self . read_response ( status:: FILE ) ) ;
456
422
457
423
match MDTM_RE . captures ( & content) {
@@ -475,8 +441,7 @@ impl FtpStream {
475
441
/// Retrieves the size of the file in bytes at `pathname` if it exists.
476
442
/// In case the file does not exist `None` is returned.
477
443
pub fn size ( & mut self , pathname : & str ) -> Result < Option < usize > > {
478
- let size_command = format ! ( "SIZE {}\r \n " , pathname) ;
479
- try!( self . write_str ( & size_command) ) ;
444
+ try!( self . write_str ( format ! ( "SIZE {}\r \n " , pathname) ) ) ;
480
445
let Line ( _, content) = try!( self . read_response ( status:: FILE ) ) ;
481
446
482
447
match SIZE_RE . captures ( & content) {
@@ -485,14 +450,13 @@ impl FtpStream {
485
450
}
486
451
}
487
452
488
- fn write_str ( & mut self , s : & str ) -> Result < ( ) > {
489
- let stream = self . reader . get_mut ( ) ;
490
-
453
+ fn write_str < S : AsRef < str > > ( & mut self , command : S ) -> Result < ( ) > {
491
454
if cfg ! ( feature = "debug_print" ) {
492
- print ! ( "CMD {}" , s ) ;
455
+ print ! ( "CMD {}" , command . as_ref ( ) ) ;
493
456
}
494
457
495
- stream. write_fmt ( format_args ! ( "{}" , s) )
458
+ let stream = self . reader . get_mut ( ) ;
459
+ stream. write_all ( command. as_ref ( ) . as_bytes ( ) )
496
460
. map_err ( |send_err| FtpError :: ConnectionError ( send_err) )
497
461
}
498
462
0 commit comments