protocol_ftp_client/
lib.rs

1#[macro_use] extern crate lazy_static;
2extern crate regex;
3
4use regex::Regex;
5use std::str;
6use std::fmt;
7use std::rc::Rc;
8use std::net::Ipv4Addr;
9use std::ptr;
10
11const OPENNING_DATA_CONNECTION:u32 = 150;
12const OPERATION_SUCCESS:u32        = 200;
13const SYSTEM_RECEIVED:u32          = 215;
14const LOGGED_EXPECTED:u32          = 220;
15const CLOSING_DATA_CONNECTION:u32  = 226;
16const PASSIVE_MODE:u32             = 227;
17const LOGGED_IN:u32                = 230;
18const CWD_CONFIRMED:u32            = 250;
19const PATHNAME_AVAILABLE:u32       = 257;
20const PASSWORD_EXPECTED:u32        = 331;
21const AUTHENTICATION_FAILED:u32    = 530;
22
23
24/// Defines data transfer mode: binary (aka image) or text.
25#[derive(Clone)]
26#[derive(PartialEq)]
27#[derive(Debug)]
28pub enum DataMode {
29  Binary,
30  Text
31}
32
33impl fmt::Display for DataMode {
34  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
35    match self {
36      &DataMode::Binary => write!(f, "data-mode:binary"),
37      &DataMode::Text   => write!(f, "data-mode:text"),
38    }
39  }
40}
41
42enum State {
43  NonAuthorized,
44  Authorized,
45  LoginReady,
46  LoginReqSent,
47
48  PasswordExpected,
49  PasswordReqSent,
50
51  PwdReqSent,
52  PathReceived(String),
53
54  CwdReqSent(String),
55  CwdConfirmed,
56
57  DataTypeReqSent(DataMode),
58  DataTypeConfirmed(DataMode),
59
60  SystemReqSent,
61  SystemRecived(String, String),
62
63  PassiveReqSent,
64  PassiveConfirmed(Ipv4Addr, u16),
65
66  ListReqSent,
67  FileReqSent,
68
69  DataTransferStarted,
70  DataTransferCompleted,
71}
72
73impl fmt::Display for State {
74  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
75    match self {
76      &State::PathReceived(ref value)              => write!(f, "[state: path-received({})]", value),
77      &State::CwdReqSent(ref value)                => write!(f, "[state: cwd-req-sent({})]", value),
78      &State::DataTypeReqSent(ref value)           => write!(f, "[state: data-type-req-sent({})]", value),
79      &State::DataTypeConfirmed(ref value)         => write!(f, "[state: data-type-confirmed({})]", value),
80      &State::SystemRecived(ref name, ref subtype) => write!(f, "[state: system-recieved({}/{})]", name, subtype),
81      &State::PassiveConfirmed(ref addr, ref port) => write!(f, "[state: passive-mode ({}:{})]", addr, port),
82      _ => {
83        let state = match self {
84          &State::NonAuthorized         => "non-authorized",
85          &State::Authorized            => "authorized",
86          &State::LoginReady            => "login-ready",
87          &State::LoginReqSent          => "login-req-sent",
88          &State::PasswordExpected      => "password-expected",
89          &State::PasswordReqSent       => "password-req-sent",
90          &State::PwdReqSent            => "pwd-req-sent",
91          &State::SystemReqSent         => "system-req-sent",
92          &State::PassiveReqSent        => "passive-req-sent",
93          &State::ListReqSent           => "list-req-sent",
94          &State::FileReqSent           => "file-req-sent",
95          &State::DataTransferStarted   => "data-transfer-started",
96          &State::DataTransferCompleted => "data-transfer-completed",
97          &State::CwdConfirmed          => "cwd-confirmed",
98          _ => unreachable!(),
99        };
100        write!(f, "[state: {}]", state)
101      }
102    }
103  }
104}
105
106/// Defines files type for parsed `LIST` command.
107#[derive(PartialEq)]
108#[derive(Debug)]
109pub enum RemoteFileKind {
110  File,
111  Directory,
112}
113
114/// Represents single item parsed `LIST` command.
115#[derive(PartialEq)]
116#[derive(Debug)]
117pub struct RemoteFile {
118  pub kind: RemoteFileKind,
119  pub size: usize,
120  pub name: String,
121}
122
123#[derive(PartialEq)]
124/// Error occured in parsing FTP data.
125pub enum FtpError {
126  /// No enough data has been provided.
127  NotEnoughData,
128  /// Protocol error occur, i.e. got `A` while expected `B`.
129  ProtocolError(String),
130  /// Some meaningless data.
131  GarbageData,
132  /// Failed to authenticate.
133  AuthFailed,
134}
135
136impl fmt::Display for FtpError {
137  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
138    match self {
139      &FtpError::NotEnoughData        => { write!(f, "[no enough data]") }
140      &FtpError::AuthFailed           => { write!(f, "[authorization failed]") }
141      &FtpError::GarbageData          => { write!(f, "[garbage]") }
142      &FtpError::ProtocolError(ref s) => { write!(f, "[protocol error: {}]", s) }
143    }
144  }
145}
146
147struct FtpInternals {
148  error: Option<FtpError>,
149  data_mode: Option<DataMode>,
150  working_dir: Option<String>,
151  sent_request: Option<Rc<State>>,
152  system: Option<(String, String)>,
153  endpoint: Option<(Ipv4Addr, u16)>,
154  state: Rc<State>,
155}
156
157/// "Passive" side of FTP protocol, which mean that receiver expects
158/// some data from remote server. As soon as it receives enough data
159/// it can "advance" to transmitter state, i.e. fill buffer with
160/// commands to be further sent to the remote server.
161pub struct FtpReceiver {
162  internals: Rc<FtpInternals>
163}
164
165/// "Active" side of FTP protocol, i.e. fill buffer with desired
166/// FTP commands for further delivery to remote server.
167pub struct FtpTransmitter {
168  internals: Rc<FtpInternals>
169}
170
171
172impl fmt::Debug for FtpError {
173    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
174        match self {
175            &FtpError::GarbageData            => write!(f, "garbage data"),
176            &FtpError::NotEnoughData          => write!(f, "no enough data"),
177            &FtpError::ProtocolError(ref err) => write!(f, "protocol error: {}", err),
178            &FtpError::AuthFailed             => write!(f, "authentication failed"),
179        }
180    }
181}
182
183impl FtpReceiver {
184  pub fn new() -> Self {
185    FtpReceiver {
186      internals: Rc::new(FtpInternals {
187        error: None,
188        data_mode: None,
189        working_dir: None,
190        sent_request: None,
191        system: None,
192        endpoint: None,
193        state: Rc::new(State::NonAuthorized),
194      })
195    }
196  }
197
198
199  fn advance_state(prev_state: &State, prev_req: &Option<Rc<State>>, bytes: &[u8]) -> Result<State, FtpError> {
200
201    lazy_static! {
202      static ref RE_RESPONCE_CODE: Regex = Regex::new("(?m:^(\\d{3}) (.+)\r$)").unwrap();
203      static ref RE_PATHNAME: Regex = Regex::new("\"(.+)\"").unwrap();
204      static ref RE_SYSTEM: Regex = Regex::new("(\\w+) [Tt]ype: (\\w+)").unwrap();
205      static ref RE_PARTRIAL_RESPONCE_CODE: Regex = Regex::new("(?m:^(\\d{3})-.+\r$)").unwrap();
206      static ref RE_PASSIVE_MODE: Regex = Regex::new("Entering Passive Mode \\((\\d+),(\\d+),(\\d+),(\\d+),(\\d+),(\\d+)\\)").unwrap();
207    }
208
209    str::from_utf8(bytes)
210      .map_err(|_| FtpError::GarbageData)
211      .and_then(|response|
212        RE_RESPONCE_CODE.captures(&response)
213          .ok_or_else(||{
214            if RE_PARTRIAL_RESPONCE_CODE.is_match(response) {
215              FtpError::NotEnoughData
216            } else {
217              FtpError::GarbageData
218            }
219          })
220          .and_then(|captures| {
221            let code_str = captures.at(1).unwrap();
222            let code:u32 = code_str.parse().unwrap();
223            match code {
224              LOGGED_EXPECTED          => Ok(State::LoginReady),
225              PASSWORD_EXPECTED        => Ok(State::PasswordExpected),
226              LOGGED_IN                => Ok(State::Authorized),
227              AUTHENTICATION_FAILED    => Err(FtpError::AuthFailed),
228              OPENNING_DATA_CONNECTION => Ok(State::DataTransferStarted),
229              CLOSING_DATA_CONNECTION  => Ok(State::DataTransferCompleted),
230              CWD_CONFIRMED            => Ok(State::CwdConfirmed),
231              OPERATION_SUCCESS  => {
232                match &*prev_req {
233                  &Some(ref prev_sent_req) => {
234                    match &**prev_sent_req {
235                      &State::DataTypeReqSent(ref value) => Ok(State::DataTypeConfirmed(value.clone())),
236                      _ => Err(FtpError::GarbageData),
237                    }
238                  },
239                  _ => Err(FtpError::GarbageData),
240                }
241              },
242              PATHNAME_AVAILABLE => {
243                let pathname_str = captures.at(2).unwrap();
244                RE_PATHNAME.captures(pathname_str)
245                  .ok_or(FtpError::GarbageData)
246                  .and_then(|path_capture|{
247                    let path = path_capture.at(1).unwrap();
248                    Ok(State::PathReceived(path.to_string()))
249                  })
250              },
251              SYSTEM_RECEIVED => {
252                let system_str = captures.at(2).unwrap();
253                RE_SYSTEM.captures(system_str)
254                  .ok_or(FtpError::GarbageData)
255                  .and_then(|path_capture|{
256                    let name = path_capture.at(1).unwrap();
257                    let subtype = path_capture.at(2).unwrap();
258                    Ok(State::SystemRecived(name.to_string(), subtype.to_string()))
259                  })
260              },
261              PASSIVE_MODE => {
262                let addr_str = captures.at(2).unwrap();
263                RE_PASSIVE_MODE.captures(addr_str)
264                  .ok_or(FtpError::GarbageData)
265                  .and_then(|path_capture|{
266                    let mut numbers = path_capture.iter().skip(1).map(|opt_value| {
267                      let value = opt_value.unwrap();
268                      let number:u8 = value.parse().unwrap();
269                      number
270                    });
271                    let a = numbers.next().unwrap();
272                    let b = numbers.next().unwrap();
273                    let c = numbers.next().unwrap();
274                    let d = numbers.next().unwrap();
275                    let p1 = numbers.next().unwrap();
276                    let p2 = numbers.next().unwrap();
277
278                    let p1_16 = p1 as u16;
279                    let p2_16 = p2 as u16;
280
281                    let addr = Ipv4Addr::new(a, b, c, d);
282                    let port = 256 * p1_16 + p2_16;
283                    Ok(State::PassiveConfirmed(addr, port))
284                  })
285              }
286              _ => panic!(format!("unknown responce: {}", response))
287            }
288          })
289      )
290      .and_then(|new_state|{
291        let allowed:bool = match (prev_state, &new_state) {
292          (&State::NonAuthorized, &State::LoginReady)                  => true,
293          (&State::LoginReqSent, &State::PasswordExpected)             => true,
294          (&State::PasswordExpected, &State::PasswordReqSent)          => true,
295          (&State::PasswordReqSent, &State::Authorized)                => true,
296          (&State::PwdReqSent, &State::PathReceived(_))                => true,
297          (&State::DataTypeReqSent(_), &State::DataTypeConfirmed(_))   => true,
298          (&State::SystemReqSent, &State::SystemRecived(_, _))         => true,
299          (&State::PassiveReqSent, &State::PassiveConfirmed(_, _))     => true,
300          (&State::ListReqSent, &State::DataTransferStarted)           => true,
301          (&State::FileReqSent, &State::DataTransferStarted)           => true,
302          (&State::DataTransferStarted, &State::DataTransferCompleted) => true,
303          (&State::CwdReqSent(_), &State::CwdConfirmed)                => true,
304          _ => false,
305        };
306        if allowed {
307          Ok(new_state)
308        } else {
309          println!("transition {} => {} is not allowed", prev_state, new_state);
310          Err(FtpError::ProtocolError(format!("{} => {} is not allowed", prev_state, new_state)))
311        }
312      })
313  }
314
315
316  /// Try to consume `Receiver` by parsing buffer and advance into `Transmitter`.
317  /// In the case of an error, it returns unmodified `Receiver` as the error. The
318  /// actually happened error can be obtained via `take_error`.
319  ///
320  /// In case of success it remembers the last successful state, probably switches
321  /// it and returns `Transmitter` object.
322  pub fn try_advance(self, buffer: &[u8]) -> Result<FtpTransmitter, Self> {
323    let mut internals = self.internals;
324
325    let transition_result = FtpReceiver::advance_state(&internals.state, &internals.sent_request, buffer);
326
327    match transition_result {
328      Err(e) => {
329        println!("error on state: {}", internals.state);
330        if &e == &FtpError::AuthFailed {
331          Rc::get_mut(&mut internals).unwrap().state = Rc::new(State::LoginReady);
332        }
333        Rc::get_mut(&mut internals).unwrap().error = Some(e);
334        Err(FtpReceiver { internals: internals.clone() })
335      }
336      ,
337      Ok(new_state) => {
338        {
339          let mut int_ref = Rc::get_mut(&mut internals).unwrap();
340          let sent_request = int_ref.sent_request.clone();
341
342          let final_state = match new_state {
343            State::PathReceived(path) => {
344              int_ref.working_dir = Some(path);
345              State::Authorized
346            },
347            State::DataTypeConfirmed(data_type) => {
348              int_ref.data_mode = Some(data_type);
349              State::Authorized
350            },
351            State::SystemRecived(name, subtype) => {
352              int_ref.system = Some((name, subtype));
353              State::Authorized
354            }
355            State::PassiveConfirmed(addr, port) => {
356              int_ref.endpoint = Some((addr, port));
357              State::Authorized
358            }
359            State::CwdConfirmed => {
360              int_ref.working_dir = match &sent_request.unwrap().as_ref() {
361                &&State::CwdReqSent(ref path) => Some(path.clone()),
362                _ => { unreachable!() },
363              };
364              State::Authorized
365            }
366            State::DataTransferCompleted => {
367              State::Authorized
368            }
369            _ => new_state,
370          };
371
372          int_ref.state = Rc::new(final_state);
373          int_ref.sent_request = None;
374        }
375        Ok(FtpTransmitter { internals: internals })
376      }
377    }
378  }
379
380  /// Returns tha last occurred error, and internally
381  /// sets up `None`.
382  pub fn take_error(&mut self) -> Option<FtpError> {
383    Rc::get_mut(&mut self.internals).unwrap().error.take()
384  }
385
386  /// Sometimes you need to manually advance to `Transmitter`
387  /// e.g. in case of Authorization Error, you can re-send
388  /// other credentials.
389  pub fn to_transmitter(self) -> FtpTransmitter {
390    FtpTransmitter { internals: self.internals }
391  }
392
393}
394
395
396lazy_static! {
397  static ref DATA_USER: &'static [u8]        = "USER ".as_bytes();
398  static ref DATA_PASS: &'static [u8]        = "PASS ".as_bytes();
399  static ref DATA_PWD: &'static [u8]         = "PWD\r\n".as_bytes();
400  static ref DATA_ENDING: &'static [u8]      = "\r\n".as_bytes();
401  static ref DATA_DATA_BINARY: &'static [u8] = "TYPE I\r\n".as_bytes();
402  static ref DATA_DATA_TEXT: &'static [u8]   = "TYPE T\r\n".as_bytes();
403  static ref DATA_SYST: &'static [u8]        = "SYST\r\n".as_bytes();
404  static ref DATA_PASV: &'static [u8]        = "PASV\r\n".as_bytes();
405  static ref DATA_LIST: &'static [u8]        = "LIST -l\r\n".as_bytes();
406  static ref DATA_CWD:  &'static [u8]        = "CWD ".as_bytes();
407  static ref DATA_RETR: &'static [u8]        = "RETR ".as_bytes();
408}
409
410
411impl FtpTransmitter {
412
413  /// Sometimes you need to manually advance to `Receiver`
414  /// e.g. in case of `LIST` or file get commands, servers sends
415  /// start data transfer and end data transfer responses.
416  pub fn to_receiver(self) -> FtpReceiver {
417    FtpReceiver { internals: self.internals }
418  }
419
420  /// Fills the output buffer with the login command (takes `login` string argument ),
421  /// modifies `count` variable with the count of written bytes and returns `FtpReceiver`.
422  pub fn send_login(self, buffer: &mut [u8], count: &mut usize, login: &str) -> FtpReceiver {
423    let mut internals = self.internals;
424    let current_state = internals.state.clone();
425
426    match &*internals.state {
427
428      &State::LoginReady => {
429        let data_login = login.as_bytes();
430        let mut my_count = 0;
431        unsafe {
432          ptr::copy_nonoverlapping(&DATA_USER[0], &mut buffer[my_count], DATA_USER.len());
433          my_count += DATA_USER.len();
434          ptr::copy_nonoverlapping(&data_login[0], &mut buffer[my_count], data_login.len());
435          my_count += data_login.len();
436          ptr::copy_nonoverlapping(&DATA_ENDING[0], &mut buffer[my_count], DATA_ENDING.len());
437        };
438        my_count += DATA_ENDING.len();
439        *count = my_count;
440        {
441          let mut int_ref = Rc::get_mut(&mut internals).unwrap();
442          int_ref.state = Rc::new(State::LoginReqSent);
443          int_ref.sent_request = Some(int_ref.state.clone());
444        }
445
446        FtpReceiver { internals: internals }
447      },
448      _ => panic!(format!("send_login is not allowed from the {}" , current_state)),
449    }
450  }
451
452  /// Fills the output buffer with the password command (takes `password` string argument ),
453  /// modifies `count` variable with the count of written bytes and returns `FtpReceiver`.
454  pub fn send_password(self, buffer: &mut [u8], count: &mut usize, pass: &str) -> FtpReceiver {
455    let mut internals = self.internals;
456
457    match &*internals.state {
458      &State::PasswordExpected => {
459        let data_password = pass.as_bytes();
460        let mut my_count = 0;
461        unsafe {
462          ptr::copy_nonoverlapping(&DATA_PASS[0], &mut buffer[my_count], DATA_PASS.len());
463          my_count += DATA_PASS.len();
464          ptr::copy_nonoverlapping(&data_password[0], &mut buffer[my_count], data_password.len());
465          my_count += data_password.len();
466          ptr::copy_nonoverlapping(&DATA_ENDING[0], &mut buffer[my_count], DATA_ENDING.len());
467        };
468        my_count += DATA_ENDING.len();
469        *count = my_count;
470        {
471          let mut int_ref = Rc::get_mut(&mut internals).unwrap();
472          int_ref.state = Rc::new(State::PasswordReqSent);
473          int_ref.sent_request = Some(int_ref.state.clone());
474        }
475
476        FtpReceiver { internals: internals }
477      },
478      _ => panic!("send_password is not allowed from the current state"),
479    }
480  }
481
482  /// Fills the output buffer with the PWD command (take current working directory on remote server),
483  /// modifies `count` variable with the count of written bytes and returns `FtpReceiver`.
484  pub fn send_pwd_req(self, buffer: &mut [u8], count: &mut usize) -> FtpReceiver {
485    let mut internals = self.internals;
486
487    match &*internals.state {
488      &State::Authorized => {
489        unsafe { ptr::copy_nonoverlapping(&DATA_PWD[0], &mut buffer[0], DATA_PWD.len()); }
490        *count = DATA_PWD.len();
491        {
492          let mut int_ref = Rc::get_mut(&mut internals).unwrap();
493          int_ref.state = Rc::new(State::PwdReqSent);
494          int_ref.sent_request = Some(int_ref.state.clone());
495        }
496
497        FtpReceiver { internals: internals }
498      },
499      _ => panic!("send_pwd_req is not allowed from the current state"),
500    }
501  }
502
503  /// Returns current working directory. Assumes either that  `send_pwd_req` or `send_cwd_req`
504  /// has been sent and succeeded.
505  pub fn get_wd(&self) -> &str {
506    match &self.internals.working_dir {
507      &Some(ref path) => &path,
508      &None           => panic!("get_wd is not available (did you called send_pwd_req?)"),
509    }
510  }
511
512  /// Fills the output buffer with the data transfer mode request (binary or text),
513  /// modifies `count` variable with the count of written bytes and returns `FtpReceiver`.
514  pub fn send_type_req(self, buffer: &mut [u8], count: &mut usize, data_type: DataMode) -> FtpReceiver {
515    let mut internals = self.internals;
516
517    match &*internals.state {
518      &State::Authorized => {
519        match &data_type {
520          &DataMode::Binary => {
521            unsafe { ptr::copy_nonoverlapping(&DATA_DATA_BINARY[0], &mut buffer[0], DATA_DATA_BINARY.len()); }
522            *count = DATA_DATA_BINARY.len();
523          },
524          &DataMode::Text => {
525            unsafe { ptr::copy_nonoverlapping(&DATA_DATA_TEXT[0], &mut buffer[0], DATA_DATA_TEXT.len()); }
526            *count = DATA_DATA_TEXT.len();
527          }
528        };
529        {
530          let mut int_ref = Rc::get_mut(&mut internals).unwrap();
531          int_ref.state = Rc::new(State::DataTypeReqSent(data_type));
532          int_ref.sent_request = Some(int_ref.state.clone());
533        }
534
535        FtpReceiver { internals: internals }
536      },
537      _ => panic!("send_type_req is not allowed from the {}", internals.state),
538    }
539  }
540
541  /// Returns current data mode. Assumes either that  `send_type_req`
542  /// has been sent and succeeded.
543  pub fn get_type(&self) -> &DataMode {
544    match &self.internals.data_mode {
545      &Some(ref mode) => &mode,
546      &None           => panic!("get_type is not available (did you called send_type_req?)"),
547    }
548  }
549
550  /// Fills the output buffer with the remote system request;
551  /// modifies `count` variable with the count of written bytes and returns `FtpReceiver`.
552  pub fn send_system_req(self, buffer: &mut [u8], count: &mut usize) -> FtpReceiver {
553    let mut internals = self.internals;
554
555    match &*internals.state {
556      &State::Authorized => {
557        unsafe { ptr::copy_nonoverlapping(&DATA_SYST[0], &mut buffer[0], DATA_SYST.len()); }
558        *count = DATA_SYST.len();
559        {
560          let mut int_ref = Rc::get_mut(&mut internals).unwrap();
561          int_ref.state = Rc::new(State::SystemReqSent);
562          int_ref.sent_request = Some(int_ref.state.clone());
563        }
564
565        FtpReceiver { internals: internals }
566      },
567      _ => panic!("send_type_req is not allowed from the {}", internals.state),
568    }
569  }
570
571  /// Returns remote system with subtype. Assumes either that `send_system_req`
572  /// has been sent and succeeded.
573  pub fn get_system(&self) -> (&String, &String) {
574    match &self.internals.system {
575      &Some((ref name, ref subtype)) => (&name, &subtype),
576      &None                          => panic!("get_system is not available (did you called send_system_req?)"),
577    }
578  }
579
580  /// Fills the output buffer with the PASS requests to allow further data transfer (`LIST` or get file)
581  /// modifies `count` variable with the count of written bytes and returns `FtpReceiver`.
582  pub fn send_pasv_req(self, buffer: &mut [u8], count: &mut usize) -> FtpReceiver {
583    let mut internals = self.internals;
584
585    match &*internals.state {
586      &State::Authorized => {
587        unsafe { ptr::copy_nonoverlapping(&DATA_PASV[0], &mut buffer[0], DATA_PASV.len()); }
588        *count = DATA_PASV.len();
589        {
590          let mut int_ref = Rc::get_mut(&mut internals).unwrap();
591          int_ref.state = Rc::new(State::PassiveReqSent);
592          int_ref.sent_request = Some(int_ref.state.clone());
593        }
594
595        FtpReceiver { internals: internals }
596      },
597      _ => panic!("send_pasv_req is not allowed from the {}", internals.state),
598    }
599  }
600
601  /// Fills the output buffer with get remove file command (takes `path` string argument ),
602  /// modifies `count` variable with the count of written bytes and returns `FtpReceiver`.
603  pub fn send_get_req(self, buffer: &mut [u8], count: &mut usize, file_path: &str) -> FtpReceiver {
604    let mut internals = self.internals;
605
606    match &*internals.state {
607      &State::Authorized => {
608        let data_path = file_path.as_bytes();
609        let mut my_count = 0;
610        unsafe {
611          ptr::copy_nonoverlapping(&DATA_RETR[0], &mut buffer[my_count], DATA_RETR.len());
612          my_count += DATA_RETR.len();
613          ptr::copy_nonoverlapping(&data_path[0], &mut buffer[my_count], data_path.len());
614          my_count += data_path.len();
615          ptr::copy_nonoverlapping(&DATA_ENDING[0], &mut buffer[my_count], DATA_ENDING.len());
616        };
617        my_count += DATA_ENDING.len();
618        {
619          let mut int_ref = Rc::get_mut(&mut internals).unwrap();
620          int_ref.state = Rc::new(State::FileReqSent);
621          int_ref.sent_request = Some(int_ref.state.clone());
622        }
623        *count = my_count;
624
625        FtpReceiver { internals: internals }
626      },
627      _ => panic!("send_get_req is not allowed from the {}", internals.state),
628    }
629  }
630
631
632  /// Fills the output buffer with change remote working directory command (takes `path` string argument ),
633  /// modifies `count` variable with the count of written bytes and returns `FtpReceiver`.
634  pub fn send_cwd_req(self, buffer: &mut [u8], count: &mut usize, path: &str) -> FtpReceiver {
635    let mut internals = self.internals;
636
637    match &*internals.state {
638      &State::Authorized => {
639        let data_path = path.as_bytes();
640        let mut my_count = 0;
641        unsafe {
642          ptr::copy_nonoverlapping(&DATA_CWD[0], &mut buffer[my_count], DATA_CWD.len());
643          my_count += DATA_CWD.len();
644          ptr::copy_nonoverlapping(&data_path[0], &mut buffer[my_count], path.len());
645          my_count += path.len();
646          ptr::copy_nonoverlapping(&DATA_ENDING[0], &mut buffer[my_count], DATA_ENDING.len());
647        };
648        my_count += DATA_ENDING.len();
649        {
650          let mut int_ref = Rc::get_mut(&mut internals).unwrap();
651          int_ref.state = Rc::new(State::CwdReqSent(path.to_string()));
652          int_ref.sent_request = Some(int_ref.state.clone());
653        }
654        *count = my_count;
655
656        FtpReceiver { internals: internals }
657      },
658      _ => panic!("send_cwd_req is not allowed from the {}", internals.state),
659    }
660  }
661
662
663  /// Takes pair of IP-address and port, where TCP-connection can be opened to.
664  /// Assumes `send_pasv_req` has been invoked before.
665  pub fn take_endpoint(&mut self) -> (Ipv4Addr, u16) {
666    match Rc::get_mut(&mut self.internals).unwrap().endpoint.take() {
667      Some((addr, port)) => (addr, port),
668      None              => panic!("take_endpoint is not available (did you called send_pass_req?)"),
669    }
670  }
671
672  /// Fills the output buffer with `LIST` command to get directory listing of current remote working directory;
673  /// modifies `count` variable with the count of written bytes and returns `FtpReceiver`.
674  pub fn send_list_req(self, buffer: &mut [u8], count: &mut usize) -> FtpReceiver {
675    let mut internals = self.internals;
676
677    match &*internals.state {
678      &State::Authorized => {
679          unsafe { ptr::copy_nonoverlapping(&DATA_LIST[0], &mut buffer[0], DATA_LIST.len()); }
680          *count = DATA_LIST.len();
681          {
682            let mut int_ref = Rc::get_mut(&mut internals).unwrap();
683            int_ref.state = Rc::new(State::ListReqSent);
684            int_ref.sent_request = Some(int_ref.state.clone());
685          }
686          FtpReceiver { internals: internals }
687        },
688      _ => panic!("send_pass_req is not allowed from the {}", internals.state),
689    }
690  }
691
692  /// Parses remote directory listing, requested by `send_list_req` command.
693  pub fn parse_list(&self, data: &[u8]) -> Result<Vec<RemoteFile>, FtpError> {
694
695    lazy_static! {
696      static ref RE_LINE: Regex = Regex::new("(?m:^(.+)\r$)").unwrap();
697      static ref RE_FILE: Regex = Regex::new("^([d-])(?:[rwx-]{3}){3} +\\d+ +\\w+ +\\w+ +(\\d+) +(.+) +(.+)$").unwrap();
698    }
699    str::from_utf8(data)
700      .map_err(|_| FtpError::GarbageData)
701      .and_then(|list|{
702        let line_captures = RE_LINE.captures_iter(list);
703        let files = line_captures
704          .filter_map(|line_cap| {
705            let line = line_cap.at(1).unwrap();
706            // println!("line = {}", line);
707            match RE_FILE.captures(line) {
708              None => None,
709              Some(captures) => {
710                let kind_str = captures.at(1).unwrap();
711                let size_str = captures.at(2).unwrap();
712                let name = captures.at(4).unwrap();
713                let kind = match kind_str {
714                  "d" => RemoteFileKind::Directory,
715                  "-" => RemoteFileKind::File,
716                  _   => unreachable!(),
717                };
718                let size:usize = size_str.parse().unwrap();
719                // println!("remote file: {} ({})", name, size);
720                let remote_file = RemoteFile {
721                  kind: kind,
722                  size: size,
723                  name: name.to_string(),
724                };
725                Some(remote_file)
726              }
727            }
728          });
729        let mut vec:Vec<RemoteFile> = Vec::new();
730        for file in files {
731          vec.push(file);
732        }
733        Ok(vec)
734      })
735  }
736
737}