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:
commit
12e6606164
20 changed files with 110 additions and 57 deletions
6
Cargo.lock
generated
6
Cargo.lock
generated
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
16
README.md
16
README.md
|
@ -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]
|
||||
|
|
|
@ -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())?;
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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)?;
|
||||
|
|
|
@ -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())?;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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())?;
|
||||
|
|
|
@ -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())?;
|
||||
|
|
|
@ -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()));
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
41
src/cmd/arg/basic_auth.rs
Normal 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()),
|
||||
))
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
|
||||
use ffsend_api::{
|
||||
api::request::{ensure_success, ResponseError},
|
||||
reqwest::{self, Client},
|
||||
client::Client,
|
||||
reqwest,
|
||||
url::{self, Url},
|
||||
};
|
||||
use urlshortener::{
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Add table
Reference in a new issue