diff options
| -rw-r--r-- | Cargo.lock | 1 | ||||
| -rw-r--r-- | mullvad-daemon/Cargo.toml | 4 | ||||
| -rw-r--r-- | mullvad-daemon/src/account_history.rs | 4 | ||||
| -rw-r--r-- | mullvad-daemon/src/event_loop.rs | 2 | ||||
| -rw-r--r-- | mullvad-daemon/src/geoip.rs | 2 | ||||
| -rw-r--r-- | mullvad-daemon/src/lib.rs | 27 | ||||
| -rw-r--r-- | mullvad-daemon/src/relays.rs | 2 | ||||
| -rw-r--r-- | mullvad-daemon/src/version_check.rs | 444 | ||||
| -rw-r--r-- | mullvad-daemon/src/wireguard.rs | 2 | ||||
| -rw-r--r-- | mullvad-rpc/Cargo.toml | 1 | ||||
| -rw-r--r-- | mullvad-rpc/src/lib.rs | 32 | ||||
| -rw-r--r-- | mullvad-types/src/version.rs | 6 |
12 files changed, 391 insertions, 136 deletions
diff --git a/Cargo.lock b/Cargo.lock index bb0f1c8344..ed008e0bec 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1520,6 +1520,7 @@ dependencies = [ "ipnetwork 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "mullvad-types 0.1.0", + "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", "talpid-types 0.1.0", diff --git a/mullvad-daemon/Cargo.toml b/mullvad-daemon/Cargo.toml index c69cf1cbd4..0ebffd12ea 100644 --- a/mullvad-daemon/Cargo.toml +++ b/mullvad-daemon/Cargo.toml @@ -13,7 +13,8 @@ chrono = { version = "0.4", features = ["serde"] } clap = "2.25" err-derive = "0.2.1" fern = { version = "0.5", features = ["colored"] } -futures = "0.1" +futures01 = { package = "futures", version = "0.1" } +futures = { package = "futures", version = "0.3", features = [ "compat" ]} ipnetwork = "0.16" jsonrpc-client-core = "0.5" jsonrpc-core = { git = "https://github.com/mullvad/jsonrpc", branch = "mullvad-fork" } @@ -28,6 +29,7 @@ rand = "0.7" regex = "1.0" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" +tokio02 = { package = "tokio", version = "0.2", features = [ "io-util", "process", "rt-core", "rt-threaded", "stream", "fs"] } tokio-core = "0.1" tokio-retry = "0.2" tokio-timer = "0.1" diff --git a/mullvad-daemon/src/account_history.rs b/mullvad-daemon/src/account_history.rs index 23dd85a487..4a96428d94 100644 --- a/mullvad-daemon/src/account_history.rs +++ b/mullvad-daemon/src/account_history.rs @@ -1,7 +1,7 @@ #[cfg(target_os = "android")] -use futures::future::{Executor, Future}; +use futures01::future::{Executor, Future}; #[cfg(not(target_os = "android"))] -use futures::{ +use futures01::{ future::{self, Executor, Future}, sync::oneshot, }; diff --git a/mullvad-daemon/src/event_loop.rs b/mullvad-daemon/src/event_loop.rs index 238b9f4eef..58f638a2ac 100644 --- a/mullvad-daemon/src/event_loop.rs +++ b/mullvad-daemon/src/event_loop.rs @@ -1,4 +1,4 @@ -use futures::{sync::oneshot, Future}; +use futures01::{sync::oneshot, Future}; use std::thread; use tokio_core::reactor::{Core, Remote}; diff --git a/mullvad-daemon/src/geoip.rs b/mullvad-daemon/src/geoip.rs index 5cc3085bfe..991ced7e5f 100644 --- a/mullvad-daemon/src/geoip.rs +++ b/mullvad-daemon/src/geoip.rs @@ -1,4 +1,4 @@ -use futures::{self, Future}; +use futures01::{self, Future}; use mullvad_rpc::{self, rest::RequestServiceHandle}; use mullvad_types::location::{AmIMullvad, GeoIpLocation}; diff --git a/mullvad-daemon/src/lib.rs b/mullvad-daemon/src/lib.rs index 63dca09237..a3a856f792 100644 --- a/mullvad-daemon/src/lib.rs +++ b/mullvad-daemon/src/lib.rs @@ -1,4 +1,5 @@ #![deny(rust_2018_idioms)] +#![recursion_limit = "256"] #[macro_use] extern crate serde; @@ -17,7 +18,7 @@ mod settings; pub mod version; mod version_check; -use futures::{ +use futures01::{ future::{self, Executor}, stream::Wait, sync::{ @@ -337,7 +338,7 @@ pub struct DaemonCommandChannel { impl DaemonCommandChannel { pub fn new() -> Self { - let (untracked_sender, receiver) = futures::sync::mpsc::unbounded(); + let (untracked_sender, receiver) = futures01::sync::mpsc::unbounded(); let sender = DaemonCommandSender(Arc::new(untracked_sender)); Self { sender, receiver } @@ -464,6 +465,7 @@ pub struct Daemon<L: EventListener> { rpc_runtime: mullvad_rpc::MullvadRpcRuntime, rpc_handle: mullvad_rpc::rest::MullvadRestHandle, wireguard_key_manager: wireguard::KeyManager, + version_updater_handle: version_check::VersionUpdaterHandle, core_handle: event_loop::CoreHandle, relay_selector: relays::RelaySelector, last_generated_relay: Option<Relay>, @@ -510,14 +512,6 @@ where let (internal_event_tx, internal_event_rx) = command_channel.destructure(); - let app_version_info = version_check::load_cache(&cache_dir); - let version_check_future = version_check::VersionUpdater::new( - rpc_handle.clone(), - cache_dir.clone(), - internal_event_tx.to_specialized_sender(), - app_version_info.clone(), - ); - core_handle.remote.spawn(|_| version_check_future); let mut settings = SettingsPersister::load(&settings_dir); @@ -525,6 +519,15 @@ where let _ = settings.set_show_beta_releases(true); } + let app_version_info = version_check::load_cache(&cache_dir); + let (version_updater, version_updater_handle) = version_check::VersionUpdater::new( + rpc_handle.clone(), + cache_dir.clone(), + internal_event_tx.to_specialized_sender(), + app_version_info.clone(), + settings.show_beta_releases, + ); + rpc_runtime.runtime().spawn(version_updater.run()); let account_history = account_history::AccountHistory::new( &cache_dir, &settings_dir, @@ -609,6 +612,7 @@ where accounts_proxy: AccountsProxy::new(rpc_handle.clone()), rpc_handle, wireguard_key_manager, + version_updater_handle, core_handle, relay_selector, last_generated_relay: None, @@ -1458,6 +1462,9 @@ where if settings_changed { self.event_listener .notify_settings(self.settings.to_settings()); + let runtime = self.rpc_runtime.runtime(); + let mut handle = self.version_updater_handle.clone(); + runtime.block_on(async { handle.set_show_beta_releases(enabled).await }); } } Err(e) => error!("{}", e.display_chain_with_msg("Unable to save settings")), diff --git a/mullvad-daemon/src/relays.rs b/mullvad-daemon/src/relays.rs index 44c1d7ef90..2f8e7a7c61 100644 --- a/mullvad-daemon/src/relays.rs +++ b/mullvad-daemon/src/relays.rs @@ -2,7 +2,7 @@ //! updated as well. use chrono::{DateTime, Local}; -use futures::Future; +use futures01::Future; use mullvad_rpc::{rest::MullvadRestHandle, RelayListProxy}; use mullvad_types::{ endpoint::MullvadEndpoint, diff --git a/mullvad-daemon/src/version_check.rs b/mullvad-daemon/src/version_check.rs index 7a4446708d..c08238d23f 100644 --- a/mullvad-daemon/src/version_check.rs +++ b/mullvad-daemon/src/version_check.rs @@ -1,20 +1,33 @@ -use crate::{version::PRODUCT_VERSION, DaemonEventSender}; -use futures::{Async, Future, Poll}; +use crate::{ + version::{is_beta_version, PRODUCT_VERSION}, + DaemonEventSender, +}; +use futures::{channel::mpsc, stream::FusedStream, FutureExt, SinkExt, StreamExt, TryFutureExt}; use mullvad_rpc::{rest::MullvadRestHandle, AppVersionProxy}; use mullvad_types::version::AppVersionInfo; +use regex::Regex; use serde::{Deserialize, Serialize}; use std::{ - fs::File, + cmp::{Ord, Ordering, PartialOrd}, + fs, + future::Future, io, path::{Path, PathBuf}, time::{Duration, Instant}, }; use talpid_core::mpsc::Sender; use talpid_types::ErrorExt; -use tokio_timer::{TimeoutError, Timer}; +use tokio02::fs::File; const VERSION_INFO_FILENAME: &str = "version-info.json"; +lazy_static::lazy_static! { + static ref STABLE_REGEX: Regex = Regex::new(r"^(\d{4})\.(\d+)$").unwrap(); + static ref BETA_REGEX: Regex = Regex::new(r"^(\d{4})\.(\d+)-beta(\d+)$").unwrap(); + static ref APP_VERSION: Option<AppVersion> = AppVersion::from_str(PRODUCT_VERSION); + static ref IS_DEV_BUILD: bool = APP_VERSION.is_some(); +} + const DOWNLOAD_TIMEOUT: Duration = Duration::from_secs(15); /// How often the updater should wake up to check the in-memory cache. /// This exist to prevent problems around sleeping. If you set it to sleep @@ -56,17 +69,14 @@ impl From<AppVersionInfo> for CachedAppVersionInfo { #[error(no_from)] pub enum Error { #[error(display = "Failed to open app version cache file for reading")] - ReadCachedRelays(#[error(source)] io::Error), + ReadVersionCache(#[error(source)] io::Error), #[error(display = "Failed to open app version cache file for writing")] - WriteRelayCache(#[error(source)] io::Error), + WriteVersionCache(#[error(source)] io::Error), #[error(display = "Failure in serialization of the version info")] Serialize(#[error(source)] serde_json::Error), - #[error(display = "Timed out when trying to check the latest app version")] - DownloadTimeout, - #[error(display = "Failed to check the latest app version")] Download(#[error(source)] mullvad_rpc::rest::Error), @@ -74,12 +84,6 @@ pub enum Error { CacheVersionMismatch, } -impl<T> From<TimeoutError<T>> for Error { - fn from(_: TimeoutError<T>) -> Error { - Error::DownloadTimeout - } -} - pub(crate) struct VersionUpdater { version_proxy: AppVersionProxy, @@ -87,117 +91,199 @@ pub(crate) struct VersionUpdater { update_sender: DaemonEventSender<AppVersionInfo>, last_app_version_info: AppVersionInfo, next_update_time: Instant, - state: VersionUpdaterState, + show_beta_releases: bool, + rx: Option<mpsc::Receiver<bool>>, +} + +#[derive(Clone)] +pub(crate) struct VersionUpdaterHandle { + tx: mpsc::Sender<bool>, } -enum VersionUpdaterState { - Sleeping(tokio_timer::Sleep), - Updating(Box<dyn Future<Item = AppVersionInfo, Error = Error> + Send + 'static>), +impl VersionUpdaterHandle { + pub async fn set_show_beta_releases(&mut self, show_beta_releases: bool) { + if self.tx.send(show_beta_releases).await.is_err() { + log::error!("Version updater already down, can't send new `show_beta_releases` state"); + } + } } impl VersionUpdater { pub fn new( - rpc_handle: MullvadRestHandle, + mut rpc_handle: MullvadRestHandle, cache_dir: PathBuf, update_sender: DaemonEventSender<AppVersionInfo>, last_app_version_info: AppVersionInfo, - ) -> Self { + show_beta_releases: bool, + ) -> (Self, VersionUpdaterHandle) { + rpc_handle.factory.timeout = DOWNLOAD_TIMEOUT; let version_proxy = AppVersionProxy::new(rpc_handle); let cache_path = cache_dir.join(VERSION_INFO_FILENAME); - Self { - version_proxy, - cache_path, - update_sender, - last_app_version_info, - next_update_time: Instant::now(), - state: VersionUpdaterState::Sleeping(Self::create_sleep_future()), - } - } + let (tx, rx) = mpsc::channel(1); - fn create_sleep_future() -> tokio_timer::Sleep { - Timer::default().sleep(UPDATE_CHECK_INTERVAL) + ( + Self { + version_proxy, + cache_path, + update_sender, + last_app_version_info, + next_update_time: Instant::now(), + show_beta_releases, + rx: Some(rx), + }, + VersionUpdaterHandle { tx }, + ) } fn create_update_future( - &mut self, - ) -> Box<dyn Future<Item = AppVersionInfo, Error = Error> + Send + 'static> { - let download_future = self - .version_proxy - .version_check(PRODUCT_VERSION.to_owned(), PLATFORM) - .map_err(Error::Download); - let future = Timer::default().timeout(download_future, DOWNLOAD_TIMEOUT); - Box::new(future) + &self, + ) -> impl Future<Output = Result<mullvad_rpc::AppVersionResponse, Error>> + Send + 'static { + let version_proxy = self.version_proxy.clone(); + let download_future_factory = move || { + let response = version_proxy.version_check(PRODUCT_VERSION.to_owned(), PLATFORM); + response.map_err(Error::Download) + }; + + let should_retry = |result: &Result<_, _>| -> bool { result.is_err() }; + + Box::pin(talpid_core::future_retry::retry_future_with_backoff( + download_future_factory, + should_retry, + std::iter::repeat(UPDATE_INTERVAL_ERROR), + )) } - fn write_cache(&self) -> Result<(), Error> { + async fn write_cache(&self) -> Result<(), Error> { log::debug!( "Writing version check cache to {}", self.cache_path.display() ); - let file = File::create(&self.cache_path).map_err(Error::WriteRelayCache)?; + let mut file = File::create(&self.cache_path) + .await + .map_err(Error::WriteVersionCache)?; let cached_app_version = CachedAppVersionInfo::from(self.last_app_version_info.clone()); - serde_json::to_writer_pretty(io::BufWriter::new(file), &cached_app_version) - .map_err(Error::Serialize) + let mut buf = serde_json::to_vec_pretty(&cached_app_version).map_err(Error::Serialize)?; + let mut read_buf: &[u8] = buf.as_mut(); + + let _ = tokio02::io::copy(&mut read_buf, &mut file) + .await + .map_err(Error::WriteVersionCache)?; + Ok(()) } -} -impl Future for VersionUpdater { - type Item = (); - type Error = (); + fn response_to_version_info( + &mut self, + response: mullvad_rpc::AppVersionResponse, + ) -> AppVersionInfo { + let suggested_upgrade = APP_VERSION.and_then(|current_version| { + Self::suggested_upgrade( + ¤t_version, + &response, + self.show_beta_releases || is_beta_version(), + ) + }); + + AppVersionInfo { + supported: response.supported, + latest_stable: response.latest_stable.unwrap_or_else(|| "".to_owned()), + latest_beta: response.latest_beta, + suggested_upgrade, + } + } + + fn suggested_upgrade( + current_version: &AppVersion, + response: &mullvad_rpc::AppVersionResponse, + show_beta: bool, + ) -> Option<String> { + let stable_version = response + .latest_stable + .as_ref() + .and_then(|stable| AppVersion::from_str(stable)); + + let beta_version = if show_beta { + AppVersion::from_str(&response.latest_beta) + } else { + None + }; + + let latest_version = stable_version.iter().chain(beta_version.iter()).max()?; + + if current_version < latest_version { + Some(latest_version.to_string()) + } else { + None + } + } + + pub async fn run(mut self) { + let mut rx = self.rx.take().unwrap().fuse(); + let next_delay = || tokio02::time::delay_for(UPDATE_CHECK_INTERVAL).fuse(); + let mut check_delay = next_delay(); + let mut version_check = futures::future::Fuse::terminated(); + + // If this is a dev build ,there's no need to pester the API for version checks. + if *IS_DEV_BUILD { + while let Some(_) = rx.next().await {} + return; + } - fn poll(&mut self) -> Poll<Self::Item, Self::Error> { loop { - if self.update_sender.is_closed() { - log::warn!("Version update receiver is closed, stopping version updater"); - return Ok(Async::Ready(())); - } - let next_state = match &mut self.state { - VersionUpdaterState::Sleeping(timer) => match timer.poll() { - Ok(Async::NotReady) => return Ok(Async::NotReady), - Err(e) => { - log::error!("Version check sleep error: {}", e); - return Err(()); + futures::select! { + show_beta_releases = rx.next() => { + match show_beta_releases { + Some(show_beta_releases ) => { + self.show_beta_releases = show_beta_releases; + }, + // time to shut down + None => { + return; + }, + } + }, + + _sleep = check_delay => { + if rx.is_terminated() || self.update_sender.is_closed() { + return; } - Ok(Async::Ready(())) => { - if Instant::now() > self.next_update_time { - VersionUpdaterState::Updating(self.create_update_future()) - } else { - VersionUpdaterState::Sleeping(Self::create_sleep_future()) - } + + if Instant::now() > self.next_update_time { + let download_future = self.create_update_future().fuse(); + version_check = download_future; + } else { + check_delay = next_delay(); } + }, - VersionUpdaterState::Updating(future) => match future.poll() { - Ok(Async::NotReady) => return Ok(Async::NotReady), - Err(error) => { - log::error!("{}", error.display_chain_with_msg("Version check failed")); - self.next_update_time = Instant::now() + UPDATE_INTERVAL_ERROR; - VersionUpdaterState::Sleeping(Self::create_sleep_future()) + + response = version_check => { + if rx.is_terminated() || self.update_sender.is_closed() { + return; } - Ok(Async::Ready(app_version_info)) => { - log::debug!("Got new version check: {:?}", app_version_info); - self.next_update_time = Instant::now() + UPDATE_INTERVAL; - if app_version_info != self.last_app_version_info { - if self.update_sender.send(app_version_info.clone()).is_err() { - log::warn!( - "Version update receiver is closed, stopping version updater" - ); - return Ok(Async::Ready(())); + self.next_update_time = Instant::now() + UPDATE_INTERVAL; + + match response { + Ok(version_info_response) => { + let new_version_info = self.response_to_version_info(version_info_response); + // if daemon can't be reached, return immediately + if self.update_sender.send(new_version_info.clone()).is_err() { + return; } - self.last_app_version_info = app_version_info; - if let Err(e) = self.write_cache() { - log::error!( - "{}", - e.display_chain_with_msg( - "Unable to cache version check response" - ) - ); + + self.last_app_version_info = new_version_info; + if let Err(err) = self.write_cache().await { + log::error!("Failed to save version cache to disk: {}", err); + } - } - VersionUpdaterState::Sleeping(Self::create_sleep_future()) + }, + Err(err) => { + log::error!("Failed to get fetch version info - {}", err); + }, } + + check_delay = next_delay(); }, - }; - self.state = next_state; + } } } } @@ -205,7 +291,7 @@ impl Future for VersionUpdater { fn try_load_cache(cache_dir: &Path) -> Result<AppVersionInfo, Error> { let path = cache_dir.join(VERSION_INFO_FILENAME); log::debug!("Loading version check cache from {}", path.display()); - let file = File::open(&path).map_err(Error::ReadCachedRelays)?; + let file = fs::File::open(&path).map_err(Error::ReadVersionCache)?; let version_info: CachedAppVersionInfo = serde_json::from_reader(io::BufReader::new(file)).map_err(Error::Serialize)?; @@ -226,11 +312,179 @@ pub fn load_cache(cache_dir: &Path) -> AppVersionInfo { ); // If we don't have a cache, start out with sane defaults. AppVersionInfo { - supported: true, + supported: *IS_DEV_BUILD, latest_stable: PRODUCT_VERSION.to_owned(), latest_beta: PRODUCT_VERSION.to_owned(), - latest: PRODUCT_VERSION.to_owned(), + suggested_upgrade: None, + } + } + } +} + +#[derive(Eq, PartialEq, Debug, Copy, Clone)] +enum AppVersion { + Stable(u32, u32), + Beta(u32, u32, u32), +} + +impl AppVersion { + fn from_str(version: &str) -> Option<Self> { + let get_int = |cap: ®ex::Captures<'_>, idx| cap.get(idx)?.as_str().parse().ok(); + + if let Some(caps) = STABLE_REGEX.captures(version) { + let year = get_int(&caps, 1)?; + let version = get_int(&caps, 2)?; + Some(Self::Stable(year, version)) + } else if let Some(caps) = BETA_REGEX.captures(version) { + let year = get_int(&caps, 1)?; + let version = get_int(&caps, 2)?; + let beta_version = get_int(&caps, 3)?; + Some(Self::Beta(year, version, beta_version)) + } else { + None + } + } +} + +impl Ord for AppVersion { + fn cmp(&self, other: &Self) -> Ordering { + use AppVersion::*; + match (self, other) { + (Stable(year, version), Stable(other_year, other_version)) => { + year.cmp(other_year).then(version.cmp(other_version)) } + // A stable version of the same year and version is always greater than a beta + (Stable(year, version), Beta(other_year, other_version, _)) => year + .cmp(other_year) + .then(version.cmp(other_version)) + .then(Ordering::Greater), + ( + Beta(year, version, beta_version), + Beta(other_year, other_version, other_beta_version), + ) => year + .cmp(other_year) + .then(version.cmp(other_version)) + .then(beta_version.cmp(other_beta_version)), + (Beta(year, version, _beta_version), Stable(other_year, other_version)) => year + .cmp(other_year) + .then(version.cmp(other_version)) + .then(Ordering::Less), + } + } +} + +impl PartialOrd for AppVersion { + fn partial_cmp(&self, other: &AppVersion) -> Option<Ordering> { + Some(self.cmp(other)) + } +} + +impl ToString for AppVersion { + fn to_string(&self) -> String { + match self { + Self::Stable(year, version) => format!("{}.{}", year, version), + Self::Beta(year, version, beta_version) => { + format!("{}.{}-beta{}", year, version, beta_version) + } + } + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_version_regex() { + assert!(STABLE_REGEX.is_match("2020.4")); + assert!(!STABLE_REGEX.is_match("2020.4-beta3")); + assert!(BETA_REGEX.is_match("2020.4-beta3")); + assert!(!STABLE_REGEX.is_match("2020.5-beta1-dev-f16be4")); + assert!(!STABLE_REGEX.is_match("2020.5-dev-f16be4")); + assert!(!BETA_REGEX.is_match("2020.5-beta1-dev-f16be4")); + assert!(!BETA_REGEX.is_match("2020.5-dev-f16be4")); + assert!(!BETA_REGEX.is_match("2020.4")); + } + + #[test] + fn test_version_parsing() { + let tests = vec![ + ("2020.4", Some(AppVersion::Stable(2020, 4))), + ("2020.4-beta3", Some(AppVersion::Beta(2020, 4, 3))), + ("2020.15-beta1-dev-f16be4", None), + ("2020.15-dev-f16be4", None), + ("", None), + ]; + + for (input, expected_output) in tests { + assert_eq!(AppVersion::from_str(&input), expected_output,); } } + + #[test] + fn test_version_upgrade_suggestions() { + let app_version_info = mullvad_rpc::AppVersionResponse { + supported: true, + latest: "2020.5-beta3".to_owned(), + latest_stable: Some("2020.4".to_string()), + latest_beta: "2020.5-beta3".to_string(), + }; + + let older_stable = AppVersion::from_str("2020.3").unwrap(); + let current_stable = AppVersion::from_str("2020.4").unwrap(); + let newer_stable = AppVersion::from_str("2021.5").unwrap(); + + let older_beta = AppVersion::from_str("2020.3-beta3").unwrap(); + let current_beta = AppVersion::from_str("2020.5-beta3").unwrap(); + let newer_beta = AppVersion::from_str("2021.5-beta3").unwrap(); + + assert_eq!( + VersionUpdater::suggested_upgrade(&older_stable, &app_version_info, false), + Some("2020.4".to_owned()) + ); + assert_eq!( + VersionUpdater::suggested_upgrade(&older_stable, &app_version_info, true), + Some("2020.5-beta3".to_owned()) + ); + assert_eq!( + VersionUpdater::suggested_upgrade(¤t_stable, &app_version_info, false), + None + ); + assert_eq!( + VersionUpdater::suggested_upgrade(¤t_stable, &app_version_info, true), + Some("2020.5-beta3".to_owned()) + ); + assert_eq!( + VersionUpdater::suggested_upgrade(&newer_stable, &app_version_info, false), + None + ); + assert_eq!( + VersionUpdater::suggested_upgrade(&newer_stable, &app_version_info, true), + None + ); + assert_eq!( + VersionUpdater::suggested_upgrade(&older_beta, &app_version_info, false), + Some("2020.4".to_owned()) + ); + assert_eq!( + VersionUpdater::suggested_upgrade(&older_beta, &app_version_info, true), + Some("2020.5-beta3".to_owned()) + ); + assert_eq!( + VersionUpdater::suggested_upgrade(¤t_beta, &app_version_info, false), + None + ); + assert_eq!( + VersionUpdater::suggested_upgrade(¤t_beta, &app_version_info, true), + None + ); + assert_eq!( + VersionUpdater::suggested_upgrade(&newer_beta, &app_version_info, false), + None + ); + assert_eq!( + VersionUpdater::suggested_upgrade(&newer_beta, &app_version_info, true), + None + ); + } } diff --git a/mullvad-daemon/src/wireguard.rs b/mullvad-daemon/src/wireguard.rs index 1b5913699f..7cf1209429 100644 --- a/mullvad-daemon/src/wireguard.rs +++ b/mullvad-daemon/src/wireguard.rs @@ -1,6 +1,6 @@ use crate::{account_history::AccountHistory, DaemonEventSender, InternalDaemonEvent}; use chrono::offset::Utc; -use futures::{future::Executor, stream::Stream, sync::oneshot, Async, Future, Poll}; +use futures01::{future::Executor, stream::Stream, sync::oneshot, Async, Future, Poll}; use mullvad_rpc::rest::{Error as RestError, MullvadRestHandle}; use mullvad_types::account::AccountToken; pub use mullvad_types::wireguard::*; diff --git a/mullvad-rpc/Cargo.toml b/mullvad-rpc/Cargo.toml index 3bd6a78fe4..3c5a29a617 100644 --- a/mullvad-rpc/Cargo.toml +++ b/mullvad-rpc/Cargo.toml @@ -16,6 +16,7 @@ http = "0.2" hyper = "0.13" ipnetwork = "0.16" log = "0.4" +regex = "1" serde = "1" serde_json = "1.0" hyper-rustls = "0.20" diff --git a/mullvad-rpc/src/lib.rs b/mullvad-rpc/src/lib.rs index 1394cfabbb..bc6a71a5ba 100644 --- a/mullvad-rpc/src/lib.rs +++ b/mullvad-rpc/src/lib.rs @@ -5,10 +5,11 @@ use futures01::future::Future as Future01; use hyper::Method; use mullvad_types::{ account::{AccountToken, VoucherSubmission}, - version::{AppVersion, AppVersionInfo}, + version::AppVersion, }; use std::{ collections::BTreeMap, + future::Future, net::{IpAddr, Ipv4Addr}, path::Path, }; @@ -278,12 +279,12 @@ pub struct AppVersionProxy { handle: rest::MullvadRestHandle, } -#[derive(serde::Deserialize)] -struct AppVersionResponse { - supported: bool, - latest: AppVersion, - latest_stable: Option<AppVersion>, - latest_beta: AppVersion, +#[derive(serde::Deserialize, Debug)] +pub struct AppVersionResponse { + pub supported: bool, + pub latest: AppVersion, + pub latest_stable: Option<AppVersion>, + pub latest_beta: AppVersion, } impl AppVersionProxy { @@ -295,7 +296,7 @@ impl AppVersionProxy { &self, version: AppVersion, platform: &str, - ) -> impl Future01<Item = AppVersionInfo, Error = rest::Error> { + ) -> impl Future<Output = Result<AppVersionResponse, rest::Error>> { let service = self.handle.service.clone(); let request = rest::send_request( @@ -307,20 +308,7 @@ impl AppVersionProxy { StatusCode::OK, ); - let future = async move { - let response: AppVersionResponse = rest::deserialize_body(request.await?).await?; - - let version_info = AppVersionInfo { - supported: response.supported, - latest: response.latest, - latest_stable: response.latest_stable.unwrap_or_else(|| "".to_owned()), - latest_beta: response.latest_beta, - }; - - Ok(version_info) - }; - - self.handle.service.compat_spawn(future) + async move { rest::deserialize_body(request.await?).await } } } diff --git a/mullvad-types/src/version.rs b/mullvad-types/src/version.rs index 1603c7752b..47f26f2de0 100644 --- a/mullvad-types/src/version.rs +++ b/mullvad-types/src/version.rs @@ -15,13 +15,15 @@ pub struct AppVersionInfo { /// issues, so using it is no longer recommended. /// The user should really upgrade when this is false. pub supported: bool, - /// Latest version - pub latest: AppVersion, /// Latest stable version + #[cfg_attr(target_os = "android", jnix(skip))] pub latest_stable: AppVersion, /// Equal to `latest_stable` when the newest release is a stable release. But will contain /// beta versions when those are out for testing. + #[cfg_attr(target_os = "android", jnix(skip))] pub latest_beta: AppVersion, + /// Whether should update to newer version + pub suggested_upgrade: Option<AppVersion>, } pub type AppVersion = String; |
