summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAndrej Mihajlov <and@mullvad.net>2017-11-17 11:27:16 +0100
committerAndrej Mihajlov <and@mullvad.net>2017-11-17 11:27:16 +0100
commitbece1847f6922893d7a5534cff1300d766e72d0c (patch)
tree02a53c6df32d371789cbf93fe894727413de10c6
parent3c1b54be645ca27542be9ae9b0b56a0eb88a7f30 (diff)
parent36845efa8ff8f075a3ea7841856dccec846d869f (diff)
downloadmullvadvpn-bece1847f6922893d7a5534cff1300d766e72d0c.tar.xz
mullvadvpn-bece1847f6922893d7a5534cff1300d766e72d0c.zip
Merge branch 'account-history'
-rw-r--r--mullvad-daemon/src/account_history.rs106
-rw-r--r--mullvad-daemon/src/main.rs9
-rw-r--r--mullvad-daemon/src/management_interface.rs62
-rw-r--r--mullvad-daemon/src/settings.rs10
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))
}