Merge branch 'configurable-client' into 'master'

Add basic HTTP authentication support, update ffsend API dependency

See merge request timvisee/ffsend!23
This commit is contained in:
Tim Visée 2019-03-19 22:18:25 +00:00
commit 12e6606164
20 changed files with 110 additions and 57 deletions

6
Cargo.lock generated
View file

@ -536,7 +536,7 @@ dependencies = [
"derive_builder 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
"directories 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"ffsend-api 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"ffsend-api 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"fs2 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"open 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
@ -556,7 +556,7 @@ dependencies = [
[[package]]
name = "ffsend-api"
version = "0.2.3"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
@ -2171,7 +2171,7 @@ dependencies = [
"checksum failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "795bd83d3abeb9220f257e597aa0080a508b27533824adf336529648f6abf7e2"
"checksum failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ea1063915fd7ef4309e222a5a07cf9c319fb9c7836b1f89b85458672dbb127e1"
"checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
"checksum ffsend-api 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0bb56b37f75daa958389b9257ff1b904d98007e67d40a3c56cb4dc620cafba0b"
"checksum ffsend-api 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca01e082c12a10fc477f7bb7ea4e264508c0c5d2a8f7bad85862dd403477ab57"
"checksum filetime 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a2df5c1a8c4be27e7707789dc42ae65976e60b394afd293d1419ab915833e646"
"checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3"
"checksum foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"

View file

@ -84,7 +84,7 @@ colored = "1.7"
derive_builder = "0.7"
directories = "1.0"
failure = "0.1"
ffsend-api = { version = "0.2.3", default-features = false }
ffsend-api = { version = "0.3.0", default-features = false }
fs2 = "0.4"
lazy_static = "1.0"
open = "1"

View file

@ -435,13 +435,14 @@ The following environment variables may be used to configure the following
defaults. The CLI flag is shown along with it, to better describe the relation
to command line arguments:
| Variable | CLI flag | Description |
| :------------------------ | :----------------------------: | :-------------------------------- |
| `FFSEND_HISTORY` | `--history <FILE>` | History file path |
| `FFSEND_HOST` | `--host <URL>` | Upload host |
| `FFSEND_TIMEOUT` | `--timeout <SECONDS>` | Request timeout (0 to disable) |
| `FFSEND_TRANSFER_TIMEOUT` | `--transfer-timeout <SECONDS>` | Transfer timeout (0 to disable) |
| `FFSEND_API` | `--api <VERSION>` | Server API version, `-` to lookup |
| Variable | CLI flag | Description |
| :------------------------ | :----------------------------: | :-------------------------------------------- |
| `FFSEND_HISTORY` | `--history <FILE>` | History file path |
| `FFSEND_HOST` | `--host <URL>` | Upload host |
| `FFSEND_TIMEOUT` | `--timeout <SECONDS>` | Request timeout (0 to disable) |
| `FFSEND_TRANSFER_TIMEOUT` | `--transfer-timeout <SECONDS>` | Transfer timeout (0 to disable) |
| `FFSEND_API` | `--api <VERSION>` | Server API version, `-` to lookup |
| `FFSEND_BASIC_AUTH` | `--basic-auth <USER:PASSWORD>` | Basic HTTP authentication credentials to use. |
These environment variables may be used to toggle a flag, simply by making them
available. The actual value of these variables is ignored, and variables may be
@ -567,6 +568,7 @@ FLAGS:
OPTIONS:
-A, --api <VERSION> Server API version to use, '-' to lookup [env: FFSEND_API]
--basic-auth <USER:PASSWORD> HTTP basic authentication credentials [env: FFSEND_BASIC_AUTH]
-H, --history <FILE> Use the specified history file [env: FFSEND_HISTORY]
-t, --timeout <SECONDS> Request timeout (0 to disable) [env: FFSEND_TIMEOUT]
-T, --transfer-timeout <SECONDS> Transfer timeout (0 to disable) [env: FFSEND_TRANSFER_TIMEOUT]

View file

@ -2,7 +2,7 @@ use clap::ArgMatches;
use ffsend_api::action::delete::{Delete as ApiDelete, Error as DeleteError};
use ffsend_api::file::remote_file::{FileParseError, RemoteFile};
use crate::client::create_client;
use crate::client::create_config;
use crate::cmd::matcher::{delete::DeleteMatcher, main::MainMatcher, Matcher};
use crate::error::ActionError;
#[cfg(feature = "history")]
@ -30,9 +30,9 @@ impl<'a> Delete<'a> {
// Get the share link
let url = matcher_delete.url();
// Create a reqwest client
// TODO: create transfer client when draining downloads
let client = create_client(&matcher_main);
// Create client
let client_config = create_config(&matcher_main);
let client = client_config.client(false);
// Parse the remote file based on the share link, derive the owner token from history
let mut file = RemoteFile::parse_url(url, matcher_delete.owner())?;

View file

@ -19,7 +19,7 @@ use tempfile::{Builder as TempBuilder, NamedTempFile};
use super::select_api_version;
#[cfg(feature = "archive")]
use crate::archive::archive::Archive;
use crate::client::{create_client, create_transfer_client};
use crate::client::create_config;
use crate::cmd::matcher::{download::DownloadMatcher, main::MainMatcher, Matcher};
#[cfg(feature = "history")]
use crate::history_tool;
@ -48,7 +48,8 @@ impl<'a> Download<'a> {
let matcher_download = DownloadMatcher::with(self.cmd_matches).unwrap();
// Create a regular client
let client = create_client(&matcher_main);
let client_config = create_config(&matcher_main);
let client = client_config.clone().client(false);
// Get the share URL, attempt to follow it
let url = matcher_download.url();
@ -165,7 +166,7 @@ impl<'a> Download<'a> {
let progress_reader: Arc<Mutex<ProgressReporter>> = progress_bar;
// Create a transfer client
let transfer_client = create_transfer_client(&matcher_main);
let transfer_client = client_config.client(true);
// Execute an download action
let progress = if !matcher_main.quiet() {

View file

@ -2,7 +2,7 @@ use clap::ArgMatches;
use ffsend_api::action::exists::{Error as ExistsError, Exists as ApiExists};
use ffsend_api::file::remote_file::{FileParseError, RemoteFile};
use crate::client::create_client;
use crate::client::create_config;
use crate::cmd::matcher::main::MainMatcher;
use crate::cmd::matcher::{exists::ExistsMatcher, Matcher};
use crate::error::ActionError;
@ -31,7 +31,8 @@ impl<'a> Exists<'a> {
let url = matcher_exists.url();
// Create a reqwest client
let client = create_client(&matcher_main);
let client_config = create_config(&matcher_main);
let client = client_config.client(false);
// Parse the remote file based on the share URL
let file = RemoteFile::parse_url(url, None)?;

View file

@ -7,7 +7,7 @@ use ffsend_api::action::metadata::Metadata as ApiMetadata;
use ffsend_api::file::remote_file::{FileParseError, RemoteFile};
use prettytable::{format::FormatBuilder, Cell, Row, Table};
use crate::client::create_client;
use crate::client::create_config;
use crate::cmd::matcher::{info::InfoMatcher, main::MainMatcher, Matcher};
#[cfg(feature = "history")]
use crate::history_tool;
@ -37,7 +37,8 @@ impl<'a> Info<'a> {
let url = matcher_info.url();
// Create a reqwest client
let client = create_client(&matcher_main);
let client_config = create_config(&matcher_main);
let client = client_config.client(false);
// Parse the remote file based on the share URL, derive the owner token from history
let mut file = RemoteFile::parse_url(url, matcher_info.owner())?;

View file

@ -13,9 +13,9 @@ pub mod version;
use ffsend_api::action::version::{Error as VersionError, Version as ApiVersion};
use ffsend_api::api::DesiredVersion;
use ffsend_api::client::Client;
use ffsend_api::url::Url;
use crate::client::Client;
use crate::config::API_VERSION_ASSUME;
use crate::util::print_warning;

View file

@ -2,7 +2,7 @@ use clap::ArgMatches;
use ffsend_api::action::params::{Error as ParamsError, Params as ApiParams, ParamsDataBuilder};
use ffsend_api::file::remote_file::RemoteFile;
use crate::client::create_client;
use crate::client::create_config;
use crate::cmd::matcher::{main::MainMatcher, params::ParamsMatcher, Matcher};
use crate::error::ActionError;
#[cfg(feature = "history")]
@ -31,7 +31,8 @@ impl<'a> Params<'a> {
let url = matcher_params.url();
// Create a reqwest client
let client = create_client(&matcher_main);
let client_config = create_config(&matcher_main);
let client = client_config.client(false);
// Parse the remote file based on the share URL, derive the owner token from history
let mut file = RemoteFile::parse_url(url, matcher_params.owner())?;

View file

@ -3,7 +3,7 @@ use ffsend_api::action::password::{Error as PasswordError, Password as ApiPasswo
use ffsend_api::file::remote_file::RemoteFile;
use prettytable::{format::FormatBuilder, Cell, Row, Table};
use crate::client::create_client;
use crate::client::create_config;
use crate::cmd::matcher::{main::MainMatcher, password::PasswordMatcher, Matcher};
use crate::error::ActionError;
#[cfg(feature = "history")]
@ -32,7 +32,8 @@ impl<'a> Password<'a> {
let url = matcher_password.url();
// Create a reqwest client
let client = create_client(&matcher_main);
let client_config = create_config(&matcher_main);
let client = client_config.client(false);
// Parse the remote file based on the share URL, derive the owner token from history
let mut file = RemoteFile::parse_url(url, matcher_password.owner())?;

View file

@ -20,7 +20,7 @@ use tempfile::{Builder as TempBuilder, NamedTempFile};
use super::select_api_version;
#[cfg(feature = "archive")]
use crate::archive::archiver::Archiver;
use crate::client::{create_client, create_transfer_client};
use crate::client::create_config;
use crate::cmd::matcher::{MainMatcher, Matcher, UploadMatcher};
#[cfg(feature = "history")]
use crate::history_tool;
@ -58,7 +58,8 @@ impl<'a> Upload<'a> {
let host = matcher_upload.host();
// Create a reqwest client capable for uploading files
let client = create_client(&matcher_main);
let client_config = create_config(&matcher_main);
let client = client_config.clone().client(false);
// Determine the API version to use
let mut desired_version = matcher_main.api();
@ -109,7 +110,7 @@ impl<'a> Upload<'a> {
}
// Create a reqwest client capable for uploading files
let transfer_client = create_transfer_client(&matcher_main);
let transfer_client = client_config.client(true);
// Create a progress bar reporter
let progress_bar = Arc::new(Mutex::new(ProgressBar::new_upload()));

View file

@ -1,7 +1,7 @@
use clap::ArgMatches;
use ffsend_api::action::version::{Error as VersionError, Version as ApiVersion};
use crate::client::create_client;
use crate::client::create_config;
use crate::cmd::matcher::main::MainMatcher;
use crate::cmd::matcher::{version::VersionMatcher, Matcher};
use crate::error::ActionError;
@ -28,7 +28,8 @@ impl<'a> Version<'a> {
let host = matcher_version.host();
// Create a reqwest client
let client = create_client(&matcher_main);
let client_config = create_config(&matcher_main);
let client = client_config.client(false);
// Make sure the file version
let response = ApiVersion::new(host).invoke(&client);

View file

@ -1,30 +1,22 @@
use std::time::Duration;
pub use ffsend_api::reqwest::Client;
use ffsend_api::reqwest::ClientBuilder;
use ffsend_api::client::{ClientConfig, ClientConfigBuilder};
use crate::cmd::matcher::MainMatcher;
/// Create the default client, which is used for generic Send requests.
/// Create a client configuration for ffsend actions.
///
/// Note: use `create_transfer_client()` instead for clients that upload/download.
pub fn create_client(matcher_main: &MainMatcher) -> Client {
create_custom_client(to_duration(matcher_main.timeout()))
}
/// Create the default client, which is used for generic Send requests.
///
/// Note: use `create_transfer_client()` instead for clients that upload/download.
pub fn create_transfer_client(matcher_main: &MainMatcher) -> Client {
create_custom_client(to_duration(matcher_main.transfer_timeout()))
}
/// Create the Send client with a custom timeout.
fn create_custom_client(timeout: Option<Duration>) -> Client {
ClientBuilder::new()
.timeout(timeout)
/// A client configuration allows you to build a client, which must be passed to ffsend API
/// actions.
// TODO: properly handle errors, do not unwrap
pub fn create_config(matcher_main: &MainMatcher) -> ClientConfig {
// TODO: configure HTTP authentication properties
ClientConfigBuilder::default()
.timeout(to_duration(matcher_main.timeout()))
.transfer_timeout(to_duration(matcher_main.transfer_timeout()))
.basic_auth(matcher_main.basic_auth())
.build()
.expect("failed to build custom reqwest client")
.expect("failed to create network client configuration")
}
/// Convert the given number of seconds into an optional duration, used for clients.

41
src/cmd/arg/basic_auth.rs Normal file
View file

@ -0,0 +1,41 @@
use clap::{Arg, ArgMatches};
use super::{CmdArg, CmdArgOption};
/// The basicauth argument.
pub struct ArgBasicAuth {}
impl CmdArg for ArgBasicAuth {
fn name() -> &'static str {
"basic-auth"
}
fn build<'b, 'c>() -> Arg<'b, 'c> {
Arg::with_name("basic-auth")
.long("basic-auth")
.value_name("USER:PASSWORD")
.env("FFSEND_BASIC_AUTH")
.hide_env_values(true)
.global(true)
.help("HTTP basic authentication credentials")
}
}
impl<'a> CmdArgOption<'a> for ArgBasicAuth {
type Value = Option<(String, Option<String>)>;
fn value<'b: 'a>(matches: &'a ArgMatches<'b>) -> Self::Value {
// Get the authentication credentials
let raw = match Self::value_raw(matches) {
Some(raw) => raw,
None => return None,
};
// Split the properties
let mut iter = raw.splitn(2, ':');
Some((
iter.next().unwrap_or("").to_owned(),
iter.next().map(|pass| pass.to_owned()),
))
}
}

View file

@ -1,4 +1,5 @@
pub mod api;
pub mod basic_auth;
pub mod download_limit;
pub mod gen_passphrase;
pub mod host;
@ -8,6 +9,7 @@ pub mod url;
// Re-eexport to arg module
pub use self::api::ArgApi;
pub use self::basic_auth::ArgBasicAuth;
pub use self::download_limit::ArgDownloadLimit;
pub use self::gen_passphrase::ArgGenPassphrase;
pub use self::host::ArgHost;

View file

@ -5,7 +5,7 @@ use std::ffi::OsString;
use clap::{App, AppSettings, Arg, ArgMatches};
use super::arg::{ArgApi, CmdArg};
use super::arg::{ArgApi, ArgBasicAuth, CmdArg};
#[cfg(feature = "history")]
use super::matcher::HistoryMatcher;
use super::matcher::{
@ -149,6 +149,7 @@ impl<'a: 'b, 'b> Handler<'a> {
.help("Enable verbose information and logging"),
)
.arg(ArgApi::build())
.arg(ArgBasicAuth::build())
.subcommand(CmdDebug::build())
.subcommand(CmdDelete::build())
.subcommand(CmdDownload::build().display_order(2))

View file

@ -5,7 +5,7 @@ use clap::ArgMatches;
use ffsend_api::api::DesiredVersion;
use super::Matcher;
use crate::cmd::arg::{ArgApi, CmdArgOption};
use crate::cmd::arg::{ArgApi, ArgBasicAuth, CmdArgOption};
use crate::util::env_var_present;
#[cfg(feature = "history")]
use crate::util::{quit_error_msg, ErrorHintsBuilder};
@ -36,6 +36,11 @@ impl<'a: 'b, 'b> MainMatcher<'a> {
ArgApi::value(self.matches)
}
/// Get basic HTTP authentication credentials to use.
pub fn basic_auth(&'a self) -> Option<(String, Option<String>)> {
ArgBasicAuth::value(self.matches)
}
/// Get the history file to use.
#[cfg(feature = "history")]
pub fn history(&self) -> PathBuf {

View file

@ -110,7 +110,8 @@ impl History {
}
// Set file permissions on unix based systems
#[cfg(unix)] {
#[cfg(unix)]
{
use std::fs::Permissions;
use std::os::unix::fs::PermissionsExt;

View file

@ -2,7 +2,8 @@
use ffsend_api::{
api::request::{ensure_success, ResponseError},
reqwest::{self, Client},
client::Client,
reqwest,
url::{self, Url},
};
use urlshortener::{

View file

@ -30,7 +30,8 @@ use failure::{err_msg, Fail};
use failure::{Compat, Error};
use ffsend_api::{
api::request::{ensure_success, ResponseError},
reqwest::{self, Client},
client::Client,
reqwest,
url::Url,
};
use rpassword::prompt_password_stderr;