mirror of
https://github.com/timvisee/ffsend.git
synced 2025-10-03 17:49:15 +02:00
Start implementing upload logic in an update module
This commit is contained in:
parent
a4ccf395b9
commit
fdb5a5a8ac
11 changed files with 180 additions and 161 deletions
8
Cargo.lock
generated
8
Cargo.lock
generated
|
@ -185,7 +185,6 @@ dependencies = [
|
||||||
"clap 2.31.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"clap 2.31.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"hkdf 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"hkdf 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"hyper 0.11.22 (registry+https://github.com/rust-lang/crates.io-index)",
|
"hyper 0.11.22 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"mime_guess 2.0.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"mime_guess 2.0.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"open 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"open 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"openssl 0.10.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"openssl 0.10.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -195,7 +194,6 @@ dependencies = [
|
||||||
"serde_json 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde_json 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"sha2 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"sha2 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"version-compare 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -988,11 +986,6 @@ name = "vec_map"
|
||||||
version = "0.8.0"
|
version = "0.8.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "version-compare"
|
|
||||||
version = "0.0.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "version_check"
|
name = "version_check"
|
||||||
version = "0.1.3"
|
version = "0.1.3"
|
||||||
|
@ -1155,7 +1148,6 @@ dependencies = [
|
||||||
"checksum uuid 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bcc7e3b898aa6f6c08e5295b6c89258d1331e9ac578cc992fb818759951bdc22"
|
"checksum uuid 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bcc7e3b898aa6f6c08e5295b6c89258d1331e9ac578cc992fb818759951bdc22"
|
||||||
"checksum vcpkg 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9e0a7d8bed3178a8fb112199d466eeca9ed09a14ba8ad67718179b4fd5487d0b"
|
"checksum vcpkg 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9e0a7d8bed3178a8fb112199d466eeca9ed09a14ba8ad67718179b4fd5487d0b"
|
||||||
"checksum vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "887b5b631c2ad01628bbbaa7dd4c869f80d3186688f8d0b6f58774fbe324988c"
|
"checksum vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "887b5b631c2ad01628bbbaa7dd4c869f80d3186688f8d0b6f58774fbe324988c"
|
||||||
"checksum version-compare 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "78068add8bf1e4d37d13fa5867182fe4c03f8e525c831053733f83aaba942d37"
|
|
||||||
"checksum version_check 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6b772017e347561807c1aa192438c5fd74242a670a6cffacc40f2defd1dc069d"
|
"checksum version_check 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6b772017e347561807c1aa192438c5fd74242a670a6cffacc40f2defd1dc069d"
|
||||||
"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
|
"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
|
||||||
"checksum winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "04e3bd221fcbe8a271359c04f21a76db7d0c6028862d1bb5512d85e1e2eb5bb3"
|
"checksum winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "04e3bd221fcbe8a271359c04f21a76db7d0c6028862d1bb5512d85e1e2eb5bb3"
|
||||||
|
|
|
@ -9,7 +9,6 @@ chrono = "0.4"
|
||||||
clap = "2.31"
|
clap = "2.31"
|
||||||
hkdf = "0.3"
|
hkdf = "0.3"
|
||||||
hyper = "0.11.9" # same as reqwest
|
hyper = "0.11.9" # same as reqwest
|
||||||
lazy_static = "1.0"
|
|
||||||
mime_guess = "2.0.0-alpha.2"
|
mime_guess = "2.0.0-alpha.2"
|
||||||
open = "1"
|
open = "1"
|
||||||
openssl = "0.10"
|
openssl = "0.10"
|
||||||
|
@ -19,4 +18,3 @@ serde_derive = "1.0"
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
sha2 = "0.7"
|
sha2 = "0.7"
|
||||||
url = "1.7"
|
url = "1.7"
|
||||||
version-compare = "0.0"
|
|
||||||
|
|
|
@ -1,39 +0,0 @@
|
||||||
use super::super::url::Url;
|
|
||||||
|
|
||||||
use super::super::send::file::File;
|
|
||||||
|
|
||||||
/// The response from the server after a file has been uploaded.
|
|
||||||
/// This response contains the file ID and owner key, to manage the file.
|
|
||||||
///
|
|
||||||
/// It also contains the download URL, although an additional secret is
|
|
||||||
/// required.
|
|
||||||
///
|
|
||||||
/// The download URL can be generated using `download_url()` which will
|
|
||||||
/// include the required secret in the URL.
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
pub struct UploadResponse {
|
|
||||||
/// The file ID.
|
|
||||||
id: String,
|
|
||||||
|
|
||||||
/// The URL the file is reachable at.
|
|
||||||
/// This includes the file ID, but does not include the secret.
|
|
||||||
url: String,
|
|
||||||
|
|
||||||
/// The owner key, used to do further file modifications.
|
|
||||||
owner: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UploadResponse {
|
|
||||||
/// Convert this response into a file object.
|
|
||||||
///
|
|
||||||
/// The `host` and `secret` must be given.
|
|
||||||
pub fn into_file(self, host: Url, secret: Vec<u8>) -> File {
|
|
||||||
File::new_now(
|
|
||||||
self.id,
|
|
||||||
host,
|
|
||||||
self.url,
|
|
||||||
secret,
|
|
||||||
self.owner,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
10
src/b64.rs
10
src/b64.rs
|
@ -6,14 +6,14 @@
|
||||||
|
|
||||||
extern crate base64;
|
extern crate base64;
|
||||||
|
|
||||||
use self::base64::DecodeError;
|
// use self::base64::DecodeError;
|
||||||
|
|
||||||
/// Encode the given byte slice using base64, in an URL-safe manner.
|
/// Encode the given byte slice using base64, in an URL-safe manner.
|
||||||
pub fn encode(input: &[u8]) -> String {
|
pub fn encode(input: &[u8]) -> String {
|
||||||
base64::encode_config(input, base64::URL_SAFE_NO_PAD)
|
base64::encode_config(input, base64::URL_SAFE_NO_PAD)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Decode the given string as base64, in an URL-safe manner.
|
// /// Decode the given string as base64, in an URL-safe manner.
|
||||||
pub fn decode(input: &str) -> Result<Vec<u8>, DecodeError> {
|
// pub fn decode(input: &str) -> Result<Vec<u8>, DecodeError> {
|
||||||
base64::decode_config(input, base64::URL_SAFE_NO_PAD)
|
// base64::decode_config(input, base64::URL_SAFE_NO_PAD)
|
||||||
}
|
// }
|
||||||
|
|
108
src/main.rs
108
src/main.rs
|
@ -1,5 +1,4 @@
|
||||||
extern crate hyper;
|
extern crate hyper;
|
||||||
extern crate lazy_static;
|
|
||||||
extern crate mime_guess;
|
extern crate mime_guess;
|
||||||
extern crate open;
|
extern crate open;
|
||||||
extern crate openssl;
|
extern crate openssl;
|
||||||
|
@ -13,26 +12,11 @@ mod app;
|
||||||
mod b64;
|
mod b64;
|
||||||
mod cmd;
|
mod cmd;
|
||||||
mod crypto;
|
mod crypto;
|
||||||
mod metadata;
|
|
||||||
mod reader;
|
|
||||||
mod send;
|
mod send;
|
||||||
mod util;
|
mod util;
|
||||||
|
|
||||||
use std::fs::File;
|
|
||||||
use std::io::BufReader;
|
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
use openssl::symm::{Cipher, encrypt_aead};
|
|
||||||
use reqwest::header::Authorization;
|
|
||||||
use reqwest::mime::APPLICATION_OCTET_STREAM;
|
|
||||||
use reqwest::multipart::Part;
|
|
||||||
|
|
||||||
use action::upload::UploadResponse;
|
|
||||||
use cmd::Handler;
|
use cmd::Handler;
|
||||||
use cmd::cmd_upload::CmdUpload;
|
use cmd::cmd_upload::CmdUpload;
|
||||||
use metadata::{Metadata, XFileMetadata};
|
|
||||||
use reader::EncryptedFileReaderTagged;
|
|
||||||
use send::key_set::KeySet;
|
|
||||||
|
|
||||||
/// Application entrypoint.
|
/// Application entrypoint.
|
||||||
fn main() {
|
fn main() {
|
||||||
|
@ -61,92 +45,10 @@ fn invoke_action(handler: &Handler) {
|
||||||
|
|
||||||
/// The upload action.
|
/// The upload action.
|
||||||
fn action_upload(cmd_upload: &CmdUpload) {
|
fn action_upload(cmd_upload: &CmdUpload) {
|
||||||
// Get the path and host
|
// // Get the path and host
|
||||||
let path = Path::new(cmd_upload.file());
|
// let path = Path::new(cmd_upload.file());
|
||||||
let host = cmd_upload.host();
|
// let host = cmd_upload.host();
|
||||||
|
|
||||||
// Make sure the path is a file
|
// // Open the URL in the browser
|
||||||
if !path.is_file() {
|
// open::that(url).expect("failed to open URL");
|
||||||
panic!("The selected path is not a file");
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: a fixed path for now, as upload test
|
|
||||||
let file_ext = path.extension().unwrap().to_str().unwrap();
|
|
||||||
let file_name = path.file_name().unwrap().to_str().unwrap().to_owned();
|
|
||||||
|
|
||||||
// Create a new reqwest client
|
|
||||||
let client = reqwest::Client::new();
|
|
||||||
|
|
||||||
// Generate a key
|
|
||||||
let key = KeySet::generate(true);
|
|
||||||
|
|
||||||
// Guess the mimetype of the file
|
|
||||||
let file_mime = mime_guess::get_mime_type(file_ext);
|
|
||||||
|
|
||||||
// Construct the metadata
|
|
||||||
let metadata = Metadata::from(key.iv(), file_name.clone(), file_mime);
|
|
||||||
|
|
||||||
// Convert the metadata to JSON bytes
|
|
||||||
let metadata = metadata.to_json().into_bytes();
|
|
||||||
|
|
||||||
// Choose a file and meta cipher type
|
|
||||||
let cipher = Cipher::aes_128_gcm();
|
|
||||||
|
|
||||||
// Encrypt the metadata, and append the tag to it
|
|
||||||
let mut metadata_tag = vec![0u8; 16];
|
|
||||||
let mut metadata = encrypt_aead(
|
|
||||||
cipher,
|
|
||||||
key.meta_key().unwrap(),
|
|
||||||
Some(&[0u8; 12]),
|
|
||||||
&[],
|
|
||||||
&metadata,
|
|
||||||
&mut metadata_tag,
|
|
||||||
).unwrap();
|
|
||||||
metadata.append(&mut metadata_tag);
|
|
||||||
|
|
||||||
// Open the file and create an encrypted file reader
|
|
||||||
let file = File::open(path).unwrap();
|
|
||||||
let reader = EncryptedFileReaderTagged::new(
|
|
||||||
file,
|
|
||||||
cipher,
|
|
||||||
key.file_key().unwrap(),
|
|
||||||
key.iv(),
|
|
||||||
).unwrap();
|
|
||||||
|
|
||||||
// Buffer the encrypted reader, and determine the length
|
|
||||||
let reader_len = reader.len().unwrap();
|
|
||||||
let reader = BufReader::new(reader);
|
|
||||||
|
|
||||||
// Build the file part, configure the form to send
|
|
||||||
let part = Part::reader_with_length(reader, reader_len)
|
|
||||||
.file_name(file_name)
|
|
||||||
.mime(APPLICATION_OCTET_STREAM);
|
|
||||||
let form = reqwest::multipart::Form::new()
|
|
||||||
.part("data", part);
|
|
||||||
|
|
||||||
// Make the request
|
|
||||||
// TODO: properly format an URL here
|
|
||||||
let url = host.join("api/upload").expect("invalid host");
|
|
||||||
let mut res = client.post(url.as_str())
|
|
||||||
.header(Authorization(format!("send-v1 {}", key.auth_key_encoded().unwrap())))
|
|
||||||
.header(XFileMetadata::from(&metadata))
|
|
||||||
.multipart(form)
|
|
||||||
.send()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// Parse the response
|
|
||||||
let upload_res: UploadResponse = res.json().unwrap();
|
|
||||||
|
|
||||||
// Print the response
|
|
||||||
let file = upload_res.into_file(host, key.secret().to_vec());
|
|
||||||
let url = file.download_url();
|
|
||||||
println!("File: {:#?}", file);
|
|
||||||
println!("Secret key: {}", key.secret_encoded());
|
|
||||||
println!("Download URL: {}", url);
|
|
||||||
|
|
||||||
// Open the URL in the browser
|
|
||||||
open::that(url).expect("failed to open URL");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: implement this some other way
|
|
||||||
unsafe impl Send for EncryptedFileReaderTagged {}
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
use super::super::openssl::symm::Cipher;
|
||||||
|
|
||||||
use b64;
|
use b64;
|
||||||
use crypto::{derive_auth_key, derive_file_key, derive_meta_key, rand_bytes};
|
use crypto::{derive_auth_key, derive_file_key, derive_meta_key, rand_bytes};
|
||||||
|
|
||||||
|
@ -98,4 +100,9 @@ impl KeySet {
|
||||||
pub fn meta_key(&self) -> Option<&Vec<u8>> {
|
pub fn meta_key(&self) -> Option<&Vec<u8>> {
|
||||||
self.meta_key.as_ref()
|
self.meta_key.as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the cipher type to use in combination with these keys.
|
||||||
|
pub fn cipher() -> Cipher {
|
||||||
|
Cipher::aes_128_gcm()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,5 +3,8 @@
|
||||||
//! This API client may be used to upload, download, modify or delete files
|
//! This API client may be used to upload, download, modify or delete files
|
||||||
//! to and from a secure Firefox Send server.
|
//! to and from a secure Firefox Send server.
|
||||||
|
|
||||||
pub mod file;
|
|
||||||
pub mod key_set;
|
pub mod key_set;
|
||||||
|
pub mod metadata;
|
||||||
|
pub mod reader;
|
||||||
|
pub mod send_file;
|
||||||
|
pub mod upload;
|
||||||
|
|
|
@ -215,3 +215,6 @@ impl Read for EncryptedFileReaderTagged {
|
||||||
Ok(self.read(buf)? + total)
|
Ok(self.read(buf)? + total)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: implement this some other way
|
||||||
|
unsafe impl Send for EncryptedFileReaderTagged {}
|
|
@ -10,7 +10,7 @@ use super::super::b64;
|
||||||
/// The struct contains the file ID, the file URL, the key that is required
|
/// The struct contains the file ID, the file URL, the key that is required
|
||||||
/// in combination with the file, and the owner key.
|
/// in combination with the file, and the owner key.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct File {
|
pub struct SendFile {
|
||||||
/// The ID of the file on that server.
|
/// The ID of the file on that server.
|
||||||
id: String,
|
id: String,
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ pub struct File {
|
||||||
owner_key: String,
|
owner_key: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl File {
|
impl SendFile {
|
||||||
/// Construct a new file.
|
/// Construct a new file.
|
||||||
pub fn new(
|
pub fn new(
|
||||||
id: String,
|
id: String,
|
||||||
|
@ -40,7 +40,7 @@ impl File {
|
||||||
secret: Vec<u8>,
|
secret: Vec<u8>,
|
||||||
owner_key: String,
|
owner_key: String,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
File {
|
Self {
|
||||||
id,
|
id,
|
||||||
time,
|
time,
|
||||||
host,
|
host,
|
153
src/send/upload.rs
Normal file
153
src/send/upload.rs
Normal file
|
@ -0,0 +1,153 @@
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::BufReader;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use super::super::mime_guess::get_mime_type;
|
||||||
|
use super::super::openssl::symm::encrypt_aead;
|
||||||
|
use super::super::reqwest;
|
||||||
|
use super::super::reqwest::header::Authorization;
|
||||||
|
use super::super::reqwest::mime::APPLICATION_OCTET_STREAM;
|
||||||
|
use super::super::reqwest::multipart::Part;
|
||||||
|
use super::super::url::Url;
|
||||||
|
|
||||||
|
use super::key_set::KeySet;
|
||||||
|
use super::metadata::{Metadata, XFileMetadata};
|
||||||
|
use super::reader::EncryptedFileReaderTagged;
|
||||||
|
use super::send_file::SendFile;
|
||||||
|
|
||||||
|
pub type Result<T> = ::std::result::Result<T, UploadError>;
|
||||||
|
|
||||||
|
/// A file upload action to a Send server.
|
||||||
|
pub struct Upload {
|
||||||
|
/// The Send host to upload the file to.
|
||||||
|
host: Url,
|
||||||
|
|
||||||
|
/// The file to upload.
|
||||||
|
path: Box<Path>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Upload {
|
||||||
|
/// Construct a new upload action.
|
||||||
|
pub fn new(host: Url, path: Box<Path>) -> Self {
|
||||||
|
Self {
|
||||||
|
host,
|
||||||
|
path,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Invoke the upload action.
|
||||||
|
pub fn invoke(self) -> Result<SendFile> {
|
||||||
|
// Make sure the given path is a file
|
||||||
|
if !self.path.is_file() {
|
||||||
|
return Err(UploadError::NotAFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Grab some file details
|
||||||
|
let file_ext = self.path.extension().unwrap().to_str().unwrap();
|
||||||
|
let file_name = self.path.file_name().unwrap().to_str().unwrap().to_owned();
|
||||||
|
let file_mime = get_mime_type(file_ext);
|
||||||
|
|
||||||
|
// Generate a key set
|
||||||
|
let key = KeySet::generate(true);
|
||||||
|
|
||||||
|
// Construct the metadata
|
||||||
|
let metadata = Metadata::from(key.iv(), file_name.clone(), file_mime)
|
||||||
|
.to_json()
|
||||||
|
.into_bytes();
|
||||||
|
|
||||||
|
// Encrypt the metadata, and append the tag to it
|
||||||
|
let mut metadata_tag = vec![0u8; 16];
|
||||||
|
let mut metadata = encrypt_aead(
|
||||||
|
KeySet::cipher(),
|
||||||
|
key.meta_key().unwrap(),
|
||||||
|
Some(&[0u8; 12]),
|
||||||
|
&[],
|
||||||
|
&metadata,
|
||||||
|
&mut metadata_tag,
|
||||||
|
).unwrap();
|
||||||
|
metadata.append(&mut metadata_tag);
|
||||||
|
|
||||||
|
// Open the file and create an encrypted file reader
|
||||||
|
let file = File::open(&self.path).unwrap();
|
||||||
|
let reader = EncryptedFileReaderTagged::new(
|
||||||
|
file,
|
||||||
|
KeySet::cipher(),
|
||||||
|
key.file_key().unwrap(),
|
||||||
|
key.iv(),
|
||||||
|
).unwrap();
|
||||||
|
|
||||||
|
// Buffer the encrypted reader, and determine the length
|
||||||
|
let reader_len = reader.len().unwrap();
|
||||||
|
let reader = BufReader::new(reader);
|
||||||
|
|
||||||
|
// Build the file part, configure the form to send
|
||||||
|
let part = Part::reader_with_length(reader, reader_len)
|
||||||
|
.file_name(file_name)
|
||||||
|
.mime(APPLICATION_OCTET_STREAM);
|
||||||
|
let form = reqwest::multipart::Form::new()
|
||||||
|
.part("data", part);
|
||||||
|
|
||||||
|
// Create a new reqwest client
|
||||||
|
let client = reqwest::Client::new();
|
||||||
|
|
||||||
|
// Make the request
|
||||||
|
// TODO: properly format an URL here
|
||||||
|
let url = self.host.join("api/upload").expect("invalid host");
|
||||||
|
let mut res = client.post(url.as_str())
|
||||||
|
.header(Authorization(format!("send-v1 {}", key.auth_key_encoded().unwrap())))
|
||||||
|
.header(XFileMetadata::from(&metadata))
|
||||||
|
.multipart(form)
|
||||||
|
.send()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// Parse the response
|
||||||
|
let upload_res: UploadResponse = res.json().unwrap();
|
||||||
|
|
||||||
|
// Print the response
|
||||||
|
Ok(
|
||||||
|
upload_res.into_file(self.host, key.secret().to_vec())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum UploadError {
|
||||||
|
/// The given file is not not an existing file.
|
||||||
|
/// Maybe it is a directory, or maybe it doesn't exist.
|
||||||
|
NotAFile,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The response from the server after a file has been uploaded.
|
||||||
|
/// This response contains the file ID and owner key, to manage the file.
|
||||||
|
///
|
||||||
|
/// It also contains the download URL, although an additional secret is
|
||||||
|
/// required.
|
||||||
|
///
|
||||||
|
/// The download URL can be generated using `download_url()` which will
|
||||||
|
/// include the required secret in the URL.
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct UploadResponse {
|
||||||
|
/// The file ID.
|
||||||
|
id: String,
|
||||||
|
|
||||||
|
/// The URL the file is reachable at.
|
||||||
|
/// This includes the file ID, but does not include the secret.
|
||||||
|
url: String,
|
||||||
|
|
||||||
|
/// The owner key, used to do further file modifications.
|
||||||
|
owner: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UploadResponse {
|
||||||
|
/// Convert this response into a file object.
|
||||||
|
///
|
||||||
|
/// The `host` and `secret` must be given.
|
||||||
|
pub fn into_file(self, host: Url, secret: Vec<u8>) -> SendFile {
|
||||||
|
SendFile::new_now(
|
||||||
|
self.id,
|
||||||
|
host,
|
||||||
|
self.url,
|
||||||
|
secret,
|
||||||
|
self.owner,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue