diff options
| author | Andrej Mihajlov <and@mullvad.net> | 2017-11-17 11:27:16 +0100 |
|---|---|---|
| committer | Andrej Mihajlov <and@mullvad.net> | 2017-11-17 11:27:16 +0100 |
| commit | bece1847f6922893d7a5534cff1300d766e72d0c (patch) | |
| tree | 02a53c6df32d371789cbf93fe894727413de10c6 | |
| parent | 3c1b54be645ca27542be9ae9b0b56a0eb88a7f30 (diff) | |
| parent | 36845efa8ff8f075a3ea7841856dccec846d869f (diff) | |
| download | mullvadvpn-bece1847f6922893d7a5534cff1300d766e72d0c.tar.xz mullvadvpn-bece1847f6922893d7a5534cff1300d766e72d0c.zip | |
Merge branch 'account-history'
| -rw-r--r-- | mullvad-daemon/src/account_history.rs | 106 | ||||
| -rw-r--r-- | mullvad-daemon/src/main.rs | 9 | ||||
| -rw-r--r-- | mullvad-daemon/src/management_interface.rs | 62 | ||||
| -rw-r--r-- | mullvad-daemon/src/settings.rs | 10 |
4 files changed, 176 insertions, 11 deletions
diff --git a/mullvad-daemon/src/account_history.rs b/mullvad-daemon/src/account_history.rs new file mode 100644 index 0000000000..16834854cd --- /dev/null +++ b/mullvad-daemon/src/account_history.rs @@ -0,0 +1,106 @@ +extern crate serde_json; + +use app_dirs::{self, AppDataType}; +use std::fs::File; +use std::io; +use std::path::PathBuf; + +use mullvad_types::account::AccountToken; + +error_chain! { + errors { + DirectoryError { + description("Unable to create account history directory for program") + } + ReadError(path: PathBuf) { + description("Unable to read account history file") + display("Unable to read account history from {}", path.to_string_lossy()) + } + WriteError(path: PathBuf) { + description("Unable to write account history file") + display("Unable to write account history to {}", path.to_string_lossy()) + } + ParseError { + description("Malformed account history") + } + } +} + +static ACCOUNT_HISTORY_FILE: &str = "account-history.json"; +static ACCOUNT_HISTORY_LIMIT: usize = 3; + +#[derive(Debug, Clone, Deserialize, Serialize, Default)] +#[serde(default)] +pub struct AccountHistory { + accounts: Vec<AccountToken>, +} + +impl AccountHistory { + /// Loads account history from file. If no file is present it returns the defaults. + pub fn load() -> Result<AccountHistory> { + let history_path = Self::get_path()?; + match File::open(&history_path) { + Ok(mut file) => { + info!( + "Loading account history from {}", + history_path.to_string_lossy() + ); + Self::parse(&mut file) + } + Err(ref e) if e.kind() == io::ErrorKind::NotFound => { + info!( + "No account history file at {}, using defaults", + history_path.to_string_lossy() + ); + Ok(AccountHistory::default()) + } + Err(e) => Err(e).chain_err(|| ErrorKind::ReadError(history_path)), + } + } + + pub fn get_accounts(&self) -> Vec<AccountToken> { + self.accounts.clone() + } + + /// Add account token to the account history removing duplicate entries + pub fn add_account_token(&mut self, account_token: AccountToken) -> Result<()> { + self.accounts + .retain(|existing_token| existing_token != &account_token); + self.accounts.push(account_token); + + let num_accounts = self.accounts.len(); + if num_accounts > ACCOUNT_HISTORY_LIMIT { + self.accounts = self.accounts + .split_off(num_accounts - ACCOUNT_HISTORY_LIMIT); + } + + self.save() + } + + /// Remove account token from the account history + pub fn remove_account_token(&mut self, account_token: AccountToken) -> Result<()> { + self.accounts + .retain(|existing_token| existing_token != &account_token); + self.save() + } + + /// Serializes the account history and saves it to the file it was loaded from. + fn save(&self) -> Result<()> { + let path = Self::get_path()?; + + debug!("Writing account history to {}", path.to_string_lossy()); + let file = File::create(&path).chain_err(|| ErrorKind::WriteError(path.clone()))?; + + serde_json::to_writer_pretty(file, self).chain_err(|| ErrorKind::WriteError(path)) + } + + fn parse(file: &mut File) -> Result<AccountHistory> { + serde_json::from_reader(file).chain_err(|| ErrorKind::ParseError) + } + + fn get_path() -> Result<PathBuf> { + let dir = app_dirs::app_root(AppDataType::UserCache, &::APP_INFO) + .chain_err(|| ErrorKind::DirectoryError)?; + Ok(dir.join(ACCOUNT_HISTORY_FILE)) + } +} diff --git a/mullvad-daemon/src/main.rs b/mullvad-daemon/src/main.rs index 1619205669..b5ec71b2a3 100644 --- a/mullvad-daemon/src/main.rs +++ b/mullvad-daemon/src/main.rs @@ -6,6 +6,7 @@ //! GNU General Public License as published by the Free Software Foundation, either version 3 of //! the License, or (at your option) any later version. +extern crate app_dirs; extern crate chrono; #[macro_use] extern crate clap; @@ -41,8 +42,9 @@ mod management_interface; mod rpc_info; mod settings; mod shutdown; +mod account_history; - +use app_dirs::AppInfo; use error_chain::ChainedError; use futures::Future; use jsonrpc_core::futures::sync::oneshot::Sender as OneshotSender; @@ -69,6 +71,11 @@ use talpid_core::tunnel::{self, TunnelEvent, TunnelMetadata, TunnelMonitor}; use talpid_types::net::{Endpoint, OpenVpnParameters, TransportProtocol, TunnelEndpoint, TunnelParameters}; +pub static APP_INFO: AppInfo = AppInfo { + name: ::CRATE_NAME, + author: "Mullvad", +}; + error_chain!{ errors { /// The client is in the wrong state for the requested operation. Optimally the code should diff --git a/mullvad-daemon/src/management_interface.rs b/mullvad-daemon/src/management_interface.rs index a71dddfb35..95e5c99833 100644 --- a/mullvad-daemon/src/management_interface.rs +++ b/mullvad-daemon/src/management_interface.rs @@ -25,6 +25,8 @@ use talpid_core::mpsc::IntoSender; use talpid_ipc; use uuid; +use account_history::AccountHistory; + /// FIXME(linus): This is here just because the futures crate has deprecated it and jsonrpc_core /// did not introduce their own yet (https://github.com/paritytech/jsonrpc/pull/196). /// Remove this and use the one in jsonrpc_core when that is released. @@ -101,6 +103,14 @@ build_rpc_trait! { #[rpc(meta, name = "shutdown")] fn shutdown(&self, Self::Metadata) -> BoxFuture<(), Error>; + /// Get previously used account tokens from the account history + #[rpc(meta, name = "get_account_history")] + fn get_account_history(&self, Self::Metadata) -> BoxFuture<Vec<AccountToken>, Error>; + + /// Remove given account token from the account history + #[rpc(meta, name = "remove_account_from_history")] + fn remove_account_from_history(&self, Self::Metadata, AccountToken) -> BoxFuture<(), Error>; + #[pubsub(name = "new_state")] { /// Subscribes to the `new_state` event notifications. #[rpc(name = "new_state_subscribe")] @@ -376,8 +386,21 @@ impl<T: From<TunnelCommand> + 'static + Send> ManagementInterfaceApi for Managem trace!("set_account"); try_future!(self.check_auth(&meta)); let (tx, rx) = sync::oneshot::channel(); - let future = self.send_command_to_daemon(TunnelCommand::SetAccount(tx, account_token)) - .and_then(|_| rx.map_err(|_| Error::internal_error())); + let future = self.send_command_to_daemon( + TunnelCommand::SetAccount(tx, account_token.clone()), + ).and_then(|_| rx.map_err(|_| Error::internal_error())); + + if let Some(new_account_token) = account_token { + if let Err(e) = AccountHistory::load().and_then(|mut account_history| { + account_history.add_account_token(new_account_token) + }) { + error!( + "Unable to add an account into the account history: {}", + e.display_chain() + ); + } + } + Box::new(future) } @@ -463,6 +486,41 @@ impl<T: From<TunnelCommand> + 'static + Send> ManagementInterfaceApi for Managem self.send_command_to_daemon(TunnelCommand::Shutdown) } + fn get_account_history(&self, meta: Self::Metadata) -> BoxFuture<Vec<AccountToken>, Error> { + trace!("get_account_history"); + try_future!(self.check_auth(&meta)); + Box::new(future::result( + AccountHistory::load() + .map(|account_history| account_history.get_accounts()) + .map_err(|error| { + error!("Unable to get account history: {}", error.display_chain()); + Error::internal_error() + }), + )) + } + + fn remove_account_from_history( + &self, + meta: Self::Metadata, + account_token: AccountToken, + ) -> BoxFuture<(), Error> { + trace!("remove_account_from_history"); + try_future!(self.check_auth(&meta)); + Box::new(future::result( + AccountHistory::load() + .and_then(|mut account_history| { + account_history.remove_account_token(account_token) + }) + .map_err(|error| { + error!( + "Unable to remove account from history: {}", + error.display_chain() + ); + Error::internal_error() + }), + )) + } + fn new_state_subscribe( &self, meta: Self::Metadata, diff --git a/mullvad-daemon/src/settings.rs b/mullvad-daemon/src/settings.rs index cb9c0daabb..4053c36c19 100644 --- a/mullvad-daemon/src/settings.rs +++ b/mullvad-daemon/src/settings.rs @@ -1,7 +1,6 @@ -extern crate app_dirs; extern crate serde_json; -use self::app_dirs::{AppDataType, AppInfo}; +use app_dirs::{self, AppDataType}; use mullvad_types::relay_constraints::{Constraint, OpenVpnConstraints, RelayConstraints, RelayConstraintsUpdate, TunnelConstraints}; @@ -28,11 +27,6 @@ error_chain! { } } -static APP_INFO: AppInfo = AppInfo { - name: ::CRATE_NAME, - author: "Mullvad", -}; - static SETTINGS_FILE: &str = "settings.json"; #[derive(Debug, Clone, Deserialize, Serialize)] @@ -90,7 +84,7 @@ impl Settings { } fn get_settings_path() -> Result<PathBuf> { - let dir = app_dirs::app_root(AppDataType::UserConfig, &APP_INFO) + let dir = app_dirs::app_root(AppDataType::UserConfig, &::APP_INFO) .chain_err(|| ErrorKind::DirectoryError)?; Ok(dir.join(SETTINGS_FILE)) } |
