|
@@ -22,6 +22,7 @@ use url::{
|
|
Url,
|
|
Url,
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+use crypto::b64;
|
|
use crypto::key_set::KeySet;
|
|
use crypto::key_set::KeySet;
|
|
use ext::status_code::StatusCodeExt;
|
|
use ext::status_code::StatusCodeExt;
|
|
use file::file::File as SendFile;
|
|
use file::file::File as SendFile;
|
|
@@ -32,9 +33,16 @@ use reader::{
|
|
ProgressReader,
|
|
ProgressReader,
|
|
ProgressReporter,
|
|
ProgressReporter,
|
|
};
|
|
};
|
|
|
|
+use super::password::{
|
|
|
|
+ Error as PasswordError,
|
|
|
|
+ Password,
|
|
|
|
+};
|
|
|
|
|
|
type EncryptedReader = ProgressReader<BufReader<EncryptedFileReader>>;
|
|
type EncryptedReader = ProgressReader<BufReader<EncryptedFileReader>>;
|
|
|
|
|
|
|
|
+/// The name of the header that is used for the authentication nonce.
|
|
|
|
+const HEADER_AUTH_NONCE: &'static str = "WWW-Authenticate";
|
|
|
|
+
|
|
/// A file upload action to a Send server.
|
|
/// A file upload action to a Send server.
|
|
pub struct Upload {
|
|
pub struct Upload {
|
|
/// The Send host to upload the file to.
|
|
/// The Send host to upload the file to.
|
|
@@ -42,14 +50,18 @@ pub struct Upload {
|
|
|
|
|
|
/// The file to upload.
|
|
/// The file to upload.
|
|
path: PathBuf,
|
|
path: PathBuf,
|
|
|
|
+
|
|
|
|
+ /// An optional password to protect the file with.
|
|
|
|
+ password: Option<String>,
|
|
}
|
|
}
|
|
|
|
|
|
impl Upload {
|
|
impl Upload {
|
|
/// Construct a new upload action.
|
|
/// Construct a new upload action.
|
|
- pub fn new(host: Url, path: PathBuf) -> Self {
|
|
|
|
|
|
+ pub fn new(host: Url, path: PathBuf, password: Option<String>) -> Self {
|
|
Self {
|
|
Self {
|
|
host,
|
|
host,
|
|
path,
|
|
path,
|
|
|
|
+ password,
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -82,15 +94,24 @@ impl Upload {
|
|
.start(reader_len);
|
|
.start(reader_len);
|
|
|
|
|
|
// Execute the request
|
|
// Execute the request
|
|
- let result = self.execute_request(req, client, &key)
|
|
|
|
- .map_err(|err| err.into());
|
|
|
|
|
|
+ // TODO: don't fail on nonce error, just don't use it
|
|
|
|
+ let (result, nonce) = self.execute_request(req, client, &key)?;
|
|
|
|
|
|
// Mark the reporter as finished
|
|
// Mark the reporter as finished
|
|
reporter.lock()
|
|
reporter.lock()
|
|
.map_err(|_| UploadError::Progress)?
|
|
.map_err(|_| UploadError::Progress)?
|
|
.finish();
|
|
.finish();
|
|
|
|
|
|
- result
|
|
|
|
|
|
+ // Change the password if set
|
|
|
|
+ if let Some(password) = self.password {
|
|
|
|
+ Password::new(
|
|
|
|
+ &result.to_download_file(),
|
|
|
|
+ &password,
|
|
|
|
+ nonce,
|
|
|
|
+ ).invoke(client)?;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ Ok(result)
|
|
}
|
|
}
|
|
|
|
|
|
/// Create a blob of encrypted metadata.
|
|
/// Create a blob of encrypted metadata.
|
|
@@ -197,7 +218,7 @@ impl Upload {
|
|
/// Execute the given request, and create a file object that represents the
|
|
/// Execute the given request, and create a file object that represents the
|
|
/// uploaded file.
|
|
/// uploaded file.
|
|
fn execute_request(&self, req: Request, client: &Client, key: &KeySet)
|
|
fn execute_request(&self, req: Request, client: &Client, key: &KeySet)
|
|
- -> Result<SendFile, UploadError>
|
|
|
|
|
|
+ -> Result<(SendFile, Option<Vec<u8>>), UploadError>
|
|
{
|
|
{
|
|
// Execute the request
|
|
// Execute the request
|
|
let mut response = match client.execute(req) {
|
|
let mut response = match client.execute(req) {
|
|
@@ -214,6 +235,16 @@ impl Upload {
|
|
);
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ // Try to get the nonce, don't error on failure
|
|
|
|
+ let nonce = response.headers()
|
|
|
|
+ .get_raw(HEADER_AUTH_NONCE)
|
|
|
|
+ .and_then(|h| h.one())
|
|
|
|
+ .and_then(|line| String::from_utf8(line.to_vec()).ok())
|
|
|
|
+ .and_then(|line| line.split_terminator(" ").skip(1).next()
|
|
|
|
+ .map(|line| line.to_owned())
|
|
|
|
+ )
|
|
|
|
+ .and_then(|nonce| b64::decode(&nonce).ok());
|
|
|
|
+
|
|
// Decode the response
|
|
// Decode the response
|
|
let response: UploadResponse = match response.json() {
|
|
let response: UploadResponse = match response.json() {
|
|
Ok(response) => response,
|
|
Ok(response) => response,
|
|
@@ -221,7 +252,10 @@ impl Upload {
|
|
};
|
|
};
|
|
|
|
|
|
// Transform the responce into a file object
|
|
// Transform the responce into a file object
|
|
- Ok(response.into_file(self.host.clone(), &key)?)
|
|
|
|
|
|
+ Ok((
|
|
|
|
+ response.into_file(self.host.clone(), &key)?,
|
|
|
|
+ nonce,
|
|
|
|
+ ))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -323,6 +357,10 @@ pub enum Error {
|
|
/// An error occurred while uploading the file.
|
|
/// An error occurred while uploading the file.
|
|
#[fail(display = "Failed to upload the file")]
|
|
#[fail(display = "Failed to upload the file")]
|
|
Upload(#[cause] UploadError),
|
|
Upload(#[cause] UploadError),
|
|
|
|
+
|
|
|
|
+ /// An error occurred while setting the password.
|
|
|
|
+ #[fail(display = "Failed to set the password")]
|
|
|
|
+ Password(#[cause] PasswordError),
|
|
}
|
|
}
|
|
|
|
|
|
impl From<MetaError> for Error {
|
|
impl From<MetaError> for Error {
|
|
@@ -349,6 +387,12 @@ impl From<UploadError> for Error {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+impl From<PasswordError> for Error {
|
|
|
|
+ fn from(err: PasswordError) -> Error {
|
|
|
|
+ Error::Password(err)
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
#[derive(Fail, Debug)]
|
|
#[derive(Fail, Debug)]
|
|
pub enum PrepareError {
|
|
pub enum PrepareError {
|
|
/// Failed to prepare the file metadata for uploading.
|
|
/// Failed to prepare the file metadata for uploading.
|