Normalize error names in download action

This commit is contained in:
timvisee 2018-03-27 01:15:20 +02:00
parent 71f0b7c451
commit 160809efa2
No known key found for this signature in database
GPG key ID: 109CBA0BF74036C2

View file

@ -8,6 +8,7 @@ use std::io::{
}; };
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use failure::Error as FailureError;
use openssl::symm::decrypt_aead; use openssl::symm::decrypt_aead;
use reqwest::{Client, Response, StatusCode}; use reqwest::{Client, Response, StatusCode};
use reqwest::header::Authorization; use reqwest::header::Authorization;
@ -21,10 +22,6 @@ use file::file::DownloadFile;
use file::metadata::Metadata; use file::metadata::Metadata;
use reader::{EncryptedFileWriter, ProgressReporter, ProgressWriter}; use reader::{EncryptedFileWriter, ProgressReporter, ProgressWriter};
// TODO: don't use these definitions
pub type Result<T> = ::std::result::Result<T, Error>;
type StdResult<T, E> = ::std::result::Result<T, E>;
/// The name of the header that is used for the authentication nonce. /// The name of the header that is used for the authentication nonce.
const HEADER_AUTH_NONCE: &'static str = "WWW-Authenticate"; const HEADER_AUTH_NONCE: &'static str = "WWW-Authenticate";
@ -47,17 +44,17 @@ impl<'a> Download<'a> {
self, self,
client: &Client, client: &Client,
reporter: Arc<Mutex<ProgressReporter>>, reporter: Arc<Mutex<ProgressReporter>>,
) -> Result<()> { ) -> Result<(), Error> {
// Create a key set for the file // Create a key set for the file
let mut key = KeySet::from(self.file); let mut key = KeySet::from(self.file);
// Fetch the authentication nonce // Fetch the authentication nonce
let auth_nonce = self.fetch_auth_nonce(client) let auth_nonce = self.fetch_auth_nonce(client)
.map_err(|err| Error::Request(RequestErr::Auth(err)))?; .map_err(|err| Error::Request(RequestError::Auth(err)))?;
// Fetch the meta nonce, set the input vector // Fetch the meta nonce, set the input vector
let meta_nonce = self.fetch_meta_nonce(&client, &mut key, auth_nonce) let meta_nonce = self.fetch_meta_nonce(&client, &mut key, auth_nonce)
.map_err(|err| Error::Request(RequestErr::Meta(err)))?; .map_err(|err| Error::Request(RequestError::Meta(err)))?;
// Open the file we will write to // Open the file we will write to
// TODO: this should become a temporary file first // TODO: this should become a temporary file first
@ -89,35 +86,35 @@ impl<'a> Download<'a> {
/// Fetch the authentication nonce for the file from the Send server. /// Fetch the authentication nonce for the file from the Send server.
fn fetch_auth_nonce(&self, client: &Client) fn fetch_auth_nonce(&self, client: &Client)
-> StdResult<Vec<u8>, AuthErr> -> Result<Vec<u8>, AuthError>
{ {
// Get the download url, and parse the nonce // Get the download url, and parse the nonce
let download_url = self.file.download_url(false); let download_url = self.file.download_url(false);
let response = client.get(download_url) let response = client.get(download_url)
.send() .send()
.map_err(|_| AuthErr::NonceReq)?; .map_err(|_| AuthError::NonceReq)?;
// Validate the status code // Validate the status code
let status = response.status(); let status = response.status();
if !status.is_success() { if !status.is_success() {
return Err(AuthErr::NonceReqStatus(status, status.err_text())); return Err(AuthError::NonceReqStatus(status, status.err_text()));
} }
// Get the authentication nonce // Get the authentication nonce
b64::decode( b64::decode(
response.headers() response.headers()
.get_raw(HEADER_AUTH_NONCE) .get_raw(HEADER_AUTH_NONCE)
.ok_or(AuthErr::NoNonceHeader)? .ok_or(AuthError::NoNonceHeader)?
.one() .one()
.ok_or(AuthErr::MalformedNonce) .ok_or(AuthError::MalformedNonce)
.and_then(|line| String::from_utf8(line.to_vec()) .and_then(|line| String::from_utf8(line.to_vec())
.map_err(|_| AuthErr::MalformedNonce) .map_err(|_| AuthError::MalformedNonce)
)? )?
.split_terminator(" ") .split_terminator(" ")
.skip(1) .skip(1)
.next() .next()
.ok_or(AuthErr::MalformedNonce)? .ok_or(AuthError::MalformedNonce)?
).map_err(|_| AuthErr::MalformedNonce) ).map_err(|_| AuthError::MalformedNonce)
} }
/// Fetch the metadata nonce. /// Fetch the metadata nonce.
@ -131,7 +128,7 @@ impl<'a> Download<'a> {
client: &Client, client: &Client,
key: &mut KeySet, key: &mut KeySet,
auth_nonce: Vec<u8>, auth_nonce: Vec<u8>,
) -> StdResult<Vec<u8>, MetaErr> { ) -> Result<Vec<u8>, MetaError> {
// Fetch the metadata and the nonce // Fetch the metadata and the nonce
let (metadata, meta_nonce) = self.fetch_metadata(client, key, auth_nonce)?; let (metadata, meta_nonce) = self.fetch_metadata(client, key, auth_nonce)?;
@ -151,10 +148,10 @@ impl<'a> Download<'a> {
client: &Client, client: &Client,
key: &KeySet, key: &KeySet,
auth_nonce: Vec<u8>, auth_nonce: Vec<u8>,
) -> StdResult<(Metadata, Vec<u8>), MetaErr> { ) -> Result<(Metadata, Vec<u8>), MetaError> {
// Compute the cryptographic signature for authentication // Compute the cryptographic signature for authentication
let sig = signature_encoded(key.auth_key().unwrap(), &auth_nonce) let sig = signature_encoded(key.auth_key().unwrap(), &auth_nonce)
.map_err(|_| MetaErr::ComputeSignature)?; .map_err(|_| MetaError::ComputeSignature)?;
// Build the request, fetch the encrypted metadata // Build the request, fetch the encrypted metadata
let mut response = client.get(self.file.api_meta_url()) let mut response = client.get(self.file.api_meta_url())
@ -162,36 +159,36 @@ impl<'a> Download<'a> {
format!("send-v1 {}", sig) format!("send-v1 {}", sig)
)) ))
.send() .send()
.map_err(|_| MetaErr::NonceReq)?; .map_err(|_| MetaError::NonceReq)?;
// Validate the status code // Validate the status code
let status = response.status(); let status = response.status();
if !status.is_success() { if !status.is_success() {
return Err(MetaErr::NonceReqStatus(status, status.err_text())); return Err(MetaError::NonceReqStatus(status, status.err_text()));
} }
// Get the metadata nonce // Get the metadata nonce
let nonce = b64::decode( let nonce = b64::decode(
response.headers() response.headers()
.get_raw(HEADER_AUTH_NONCE) .get_raw(HEADER_AUTH_NONCE)
.ok_or(MetaErr::NoNonceHeader)? .ok_or(MetaError::NoNonceHeader)?
.one() .one()
.ok_or(MetaErr::MalformedNonce) .ok_or(MetaError::MalformedNonce)
.and_then(|line| String::from_utf8(line.to_vec()) .and_then(|line| String::from_utf8(line.to_vec())
.map_err(|_| MetaErr::MalformedNonce) .map_err(|_| MetaError::MalformedNonce)
)? )?
.split_terminator(" ") .split_terminator(" ")
.skip(1) .skip(1)
.next() .next()
.ok_or(MetaErr::MalformedNonce)? .ok_or(MetaError::MalformedNonce)?
).map_err(|_| MetaErr::MalformedNonce)?; ).map_err(|_| MetaError::MalformedNonce)?;
// Parse the metadata response, and decrypt it // Parse the metadata response, and decrypt it
Ok(( Ok((
response.json::<MetadataResponse>() response.json::<MetadataResponse>()
.map_err(|_| MetaErr::Malformed)? .map_err(|_| MetaError::Malformed)?
.decrypt_metadata(&key) .decrypt_metadata(&key)
.map_err(|_| MetaErr::Decrypt)?, .map_err(|_| MetaError::Decrypt)?,
nonce, nonce,
)) ))
} }
@ -206,10 +203,10 @@ impl<'a> Download<'a> {
key: &KeySet, key: &KeySet,
meta_nonce: Vec<u8>, meta_nonce: Vec<u8>,
client: &Client, client: &Client,
) -> StdResult<(Response, u64), DownloadErr> { ) -> Result<(Response, u64), DownloadError> {
// Compute the cryptographic signature // Compute the cryptographic signature
let sig = signature_encoded(key.auth_key().unwrap(), &meta_nonce) let sig = signature_encoded(key.auth_key().unwrap(), &meta_nonce)
.map_err(|_| DownloadErr::ComputeSignature)?; .map_err(|_| DownloadError::ComputeSignature)?;
// Build and send the download request // Build and send the download request
let response = client.get(self.file.api_download_url()) let response = client.get(self.file.api_download_url())
@ -217,18 +214,18 @@ impl<'a> Download<'a> {
format!("send-v1 {}", sig) format!("send-v1 {}", sig)
)) ))
.send() .send()
.map_err(|_| DownloadErr::Request)?; .map_err(|_| DownloadError::Request)?;
// Validate the status code // Validate the status code
let status = response.status(); let status = response.status();
if !status.is_success() { if !status.is_success() {
return Err(DownloadErr::RequestStatus(status, status.err_text())); return Err(DownloadError::RequestStatus(status, status.err_text()));
} }
// Get the content length // Get the content length
// TODO: make sure there is enough disk space // TODO: make sure there is enough disk space
let len = response.headers().get::<ContentLength>() let len = response.headers().get::<ContentLength>()
.ok_or(DownloadErr::NoLength)?.0; .ok_or(DownloadError::NoLength)?.0;
Ok((response, len)) Ok((response, len))
} }
@ -243,7 +240,7 @@ impl<'a> Download<'a> {
len: u64, len: u64,
key: &KeySet, key: &KeySet,
reporter: Arc<Mutex<ProgressReporter>>, reporter: Arc<Mutex<ProgressReporter>>,
) -> StdResult<ProgressWriter<EncryptedFileWriter>, FileError> { ) -> Result<ProgressWriter<EncryptedFileWriter>, FileError> {
// Build an encrypted writer // Build an encrypted writer
let mut writer = ProgressWriter::new( let mut writer = ProgressWriter::new(
EncryptedFileWriter::new( EncryptedFileWriter::new(
@ -270,18 +267,18 @@ impl<'a> Download<'a> {
mut writer: ProgressWriter<EncryptedFileWriter>, mut writer: ProgressWriter<EncryptedFileWriter>,
len: u64, len: u64,
reporter: Arc<Mutex<ProgressReporter>>, reporter: Arc<Mutex<ProgressReporter>>,
) -> StdResult<(), DownloadErr> { ) -> Result<(), DownloadError> {
// Start the writer // Start the writer
reporter.lock() reporter.lock()
.map_err(|_| DownloadErr::Progress)? .map_err(|_| DownloadError::Progress)?
.start(len); .start(len);
// Write to the output file // Write to the output file
io::copy(&mut reader, &mut writer).map_err(|_| DownloadErr::Download)?; io::copy(&mut reader, &mut writer).map_err(|_| DownloadError::Download)?;
// Finish // Finish
reporter.lock() reporter.lock()
.map_err(|_| DownloadErr::Progress)? .map_err(|_| DownloadError::Progress)?
.finish(); .finish();
// Verify the writer // Verify the writer
@ -289,7 +286,7 @@ impl<'a> Download<'a> {
if writer.unwrap().verified() { if writer.unwrap().verified() {
Ok(()) Ok(())
} else { } else {
Err(DownloadErr::Verify) Err(DownloadError::Verify)
} }
} }
} }
@ -312,10 +309,9 @@ impl MetadataResponse {
/// The decrypted data is verified using an included tag. /// The decrypted data is verified using an included tag.
/// If verification failed, an error is returned. /// If verification failed, an error is returned.
// TODO: do not unwrap, return a proper error // TODO: do not unwrap, return a proper error
pub fn decrypt_metadata(&self, key_set: &KeySet) -> Result<Metadata> { pub fn decrypt_metadata(&self, key_set: &KeySet) -> Result<Metadata, FailureError> {
// Decode the metadata // Decode the metadata
let raw = b64::decode(&self.meta) let raw = b64::decode(&self.meta)?;
.expect("failed to decode metadata from server");
// Get the encrypted metadata, and it's tag // Get the encrypted metadata, and it's tag
let (encrypted, tag) = raw.split_at(raw.len() - 16); let (encrypted, tag) = raw.split_at(raw.len() - 16);
@ -331,13 +327,10 @@ impl MetadataResponse {
&[], &[],
encrypted, encrypted,
&tag, &tag,
).expect("failed to decrypt metadata, invalid tag?"); )?;
// Parse the metadata, and return // Parse the metadata, and return
Ok( Ok(serde_json::from_slice(&meta)?)
serde_json::from_slice(&meta)
.expect("failed to parse decrypted metadata as JSON")
)
} }
} }
@ -347,7 +340,7 @@ pub enum Error {
/// This may be because authentication failed, because decrypting the /// This may be because authentication failed, because decrypting the
/// file metadata didn't succeed, or due to some other reason. /// file metadata didn't succeed, or due to some other reason.
#[fail(display = "Failed to request file data")] #[fail(display = "Failed to request file data")]
Request(#[cause] RequestErr), Request(#[cause] RequestError),
/// The given Send file has expired, or did never exist in the first place. /// The given Send file has expired, or did never exist in the first place.
/// Therefore the file could not be downloaded. /// Therefore the file could not be downloaded.
@ -357,7 +350,7 @@ pub enum Error {
/// An error occurred while downloading the file. /// An error occurred while downloading the file.
#[fail(display = "Failed to download the file")] #[fail(display = "Failed to download the file")]
Download(#[cause] DownloadErr), Download(#[cause] DownloadError),
/// An error occurred while decrypting the downloaded file. /// An error occurred while decrypting the downloaded file.
#[fail(display = "Failed to decrypt the downloaded file")] #[fail(display = "Failed to decrypt the downloaded file")]
@ -370,18 +363,18 @@ pub enum Error {
} }
#[derive(Fail, Debug)] #[derive(Fail, Debug)]
pub enum RequestErr { pub enum RequestError {
/// Failed authenticating, in order to fetch the file data. /// Failed authenticating, in order to fetch the file data.
#[fail(display = "Failed to authenticate")] #[fail(display = "Failed to authenticate")]
Auth(#[cause] AuthErr), Auth(#[cause] AuthError),
/// Failed to retrieve the file metadata. /// Failed to retrieve the file metadata.
#[fail(display = "Failed to retrieve file metadata")] #[fail(display = "Failed to retrieve file metadata")]
Meta(#[cause] MetaErr), Meta(#[cause] MetaError),
} }
#[derive(Fail, Debug)] #[derive(Fail, Debug)]
pub enum AuthErr { pub enum AuthError {
/// Sending the request to gather the authentication encryption nonce /// Sending the request to gather the authentication encryption nonce
/// failed. /// failed.
#[fail(display = "Failed to request authentication nonce")] #[fail(display = "Failed to request authentication nonce")]
@ -406,7 +399,7 @@ pub enum AuthErr {
} }
#[derive(Fail, Debug)] #[derive(Fail, Debug)]
pub enum MetaErr { pub enum MetaError {
/// An error occurred while computing the cryptographic signature used for /// An error occurred while computing the cryptographic signature used for
/// decryption. /// decryption.
#[fail(display = "Failed to compute cryptographic signature")] #[fail(display = "Failed to compute cryptographic signature")]
@ -443,7 +436,7 @@ pub enum MetaErr {
} }
#[derive(Fail, Debug)] #[derive(Fail, Debug)]
pub enum DownloadErr { pub enum DownloadError {
/// An error occurred while computing the cryptographic signature used for /// An error occurred while computing the cryptographic signature used for
/// downloading the file. /// downloading the file.
#[fail(display = "Failed to compute cryptographic signature")] #[fail(display = "Failed to compute cryptographic signature")]
@ -490,7 +483,7 @@ pub enum FileError {
EncryptedWriter, EncryptedWriter,
} }
/// Reqwest status code extention. /// Reqwest status code extention, to easily retrieve an error message.
trait StatusCodeExt { trait StatusCodeExt {
/// Build a basic error message based on the status code. /// Build a basic error message based on the status code.
fn err_text(&self) -> String; fn err_text(&self) -> String;