1pub mod catalogue;
6pub mod filter;
7pub mod davclient;
8
9use std::env;
10use std::fs::File;
11use std::io::{BufReader, BufWriter, Error as IoError, ErrorKind, Write};
12use std::path::Path;
13use netrc::Netrc;
14pub use catalogue::CatalogueInfo;
15use filter::FilterCriteria;
16pub use davclient::{ClientExt, DavCtrlError};
17use url::Url;
18use http::StatusCode;
19
20pub struct DavController {
30 client_ext: ClientExt
31}
32
33impl DavController {
34 pub fn new (rc: Netrc) -> Self {
36 Self{client_ext: ClientExt::new(rc)}
37 }
38
39 fn _put_one (&self, file_path: &Path, target_url: &Url) -> Result<StatusCode, DavCtrlError> {
46 if file_path.is_file() {
47 let file = File::open(file_path)?;
48 self.client_ext.write(file, target_url)
49 } else {
50 Err(DavCtrlError::InvalidSource(format!("Not an existing file: {}", file_path.display())))
51 }
52 }
53
54 pub fn put (&self, file_paths: &Vec<&Path>, target_base: &Url) -> Vec<Result<StatusCode, DavCtrlError>> {
58 let mut retvec = Vec::new();
59 for file_path in file_paths {
60 if !target_base.path().ends_with('/') {
61 if file_paths.len() == 1 {
63 retvec.push(self._put_one(file_path, target_base));
65 } else {
66 retvec.push(Err(DavCtrlError::InvalidDestination(
67 format!("Given target URL {target_base} is not a directory and cannot receive multiple files")
68 )));
69 }
70 } else if let Some(filename) = file_path.file_name() {
71 match target_base.join(&filename.to_string_lossy()) {
72 Ok(target_url) => {
73 retvec.push(self._put_one( file_path, &target_url));
74 },
75 Err(error) => {
76 retvec.push(Err(DavCtrlError::from(error)));
77 }
78 }
79 } else {
80 retvec.push(
81 Err(DavCtrlError::InvalidSource(
82 format!("Source path '{}' does not end with a file name", file_path.display())))
83 );
84 }
85 }
86 retvec
87 }
88
89
90 fn _get_one(&self, source: &Url, target_dir: &Path) -> Result<StatusCode, DavCtrlError> {
97 if target_dir.is_dir() {
98 let filename = source.path_segments().
99 and_then(|paths| paths.last()).
100 ok_or_else(|| DavCtrlError::InvalidSource(format!("Source URL '{}' contains no filename", source)))?;
101 let file = std::fs::File::create(target_dir.join(filename))?;
102 let mut buffer = BufWriter::new(file);
103 let status_code = self.client_ext.read(source, &mut buffer)?;
104 buffer.flush()?;
105 Ok(status_code)
106 } else {
107 Err(DavCtrlError::InvalidDestination(format!("Destination '{}' is not a directory", target_dir.display())))
108 }
109 }
110
111 pub fn get (&self, sources: &Vec<&Url>, target_dir: &Path) -> Vec<Result<StatusCode, DavCtrlError>> {
115 let mut retvec = Vec::new();
116 for source in sources {
117 retvec.push(self._get_one(source, target_dir));
118 }
119 retvec
120 }
121
122 pub fn get_slice(&self, source: &Url, offset: u64, size: u32, target_file: &Path) -> Result<StatusCode,DavCtrlError> {
124 if target_file.is_file() {
125 let file = std::fs::File::create(target_file)?;
126 let mut buffer = BufWriter::new(file);
127 let status_code = self.client_ext.read_slice(source, offset, size, &mut buffer)?;
128 buffer.flush()?;
129 Ok(status_code)
130 } else {
131 Err(DavCtrlError::InvalidDestination(format!("Destination '{}' is not a file", target_file.display())))
132 }
133
134 }
135
136 pub fn ls (&self, url_to_list: &Url, filter: &FilterCriteria) -> Result<Vec<CatalogueInfo>, DavCtrlError> {
138 self.client_ext.list(url_to_list, filter)
139 }
140
141 pub fn delete (&self, url_to_delete: &Url) -> Result<StatusCode, DavCtrlError> {
143 self.client_ext.delete(url_to_delete)
144 }
145
146 pub fn mv (&self, from: &Url, to: &Url) -> Result<StatusCode, DavCtrlError> {
148 self.client_ext.mv(from, to)
149 }
150
151 pub fn mkdir (&self, collection_url: &Url) -> Result<StatusCode, DavCtrlError> {
153 self.client_ext.mkdir (collection_url)
154 }
155
156 pub fn set_default_credentials(&mut self, username: String, password: String) {
160 self.client_ext.netrc.default = Some(netrc::Machine {
161 login: username,
162 password: Some(password),
163 account: None,
164 port: None
165 });
166 }
167}
168
169pub fn read_netrc() -> Result<Netrc, IoError> {
175 #[allow(deprecated)]
176 let home = env::home_dir().ok_or(IoError::from(ErrorKind::NotFound))?;
178 let path = home.join(".netrc");
179 if path.is_file() {
180 let netrc = File::open(path)?;
181 Netrc::parse(BufReader::new(netrc)).map_err(|_e| IoError::from(ErrorKind::InvalidInput))
182 } else {
183 Err(IoError::from(ErrorKind::NotFound))
184 }
185}
186
187
188#[cfg(test)]
189mod tests;