ce380a6b0d
* initial crate * foomp * Make it work for x86_64-linux-android * remove unused stuff * Add header * another layer of hacks * additional target os locking * cleanup * bootstrap android app * android jni function * instructions + xcode project * update jni name * add native socks5 class * typo * gitkeep android native lib path * add native socks5 class * add socks5 native lib in java * add build script * fix jni dependency declaration * wip * Update build.sh * Move build.sh to new subdir * rename to build-android.sh * fix typo in FFI function name * use a good SP * wip not crashing state * add android network permissions * android_logging * starting client on button in swift + safer ffi * set tag for libnyms5 logs * testing callbacks * android: start socks5 process in a separated thread * non-blocking client with callbacks * Remove the old non-working logger * Restore commented out functionality in socks5 client * basic file write/load + possible android fix * Fully working state (minus task manager) * Remove unused function * data persistence + cb with address * Remove stray old MyClass file from the merge * Make storage_dir and Option * Fix char_p for android * Android now works with the new branch * Tidy up a little in the jni code * Move android mod to seperate file * jni wrap start/stop * Add android build to Makefile * android: add basic UI and start/stop actions * typo * add nym word * dirty persistence restored * dirty android fixes * even dirtier workaround * Move rust crate to sdk/lib * Update cargo.toml * Strip release binary * Update lib name in android project * Move ios project to nym-connect directory * remove old gitignore file * Move ios client one step deeper * fixed xcode lib paths * removed old tracked file * move android app under new path * a bit of cleanup * hopefully fixing the CI issues (🤞) * Update Makefile * android: add better support for persistent state * updating ios UI on ffi callbacks * missing dead code * Added toggle button (wip) * swapped connect and disconnect methods around * icon * fixed android build * reset button + reuse service provider * disabling reset button * android: run proxy in a worker as foreground service * todo user cancel action * android build script: add aarch64 * add stop action from notification * add simple callbacks to the socks5 bridge * pick a sp randomly * pass stop cb to lib call * add loading state support * refactor(android): base connection state on callback calls * android: add optimistic ui * android: unique instance of libnym * removing deadcode --------- Co-authored-by: Jędrzej Stuczyński <jedrzej.stuczynski@gmail.com> Co-authored-by: pierre <dommerc.pierre@gmail.com> Co-authored-by: Mark Sinclair <mmsinclair@users.noreply.github.com>
220 lines
6.7 KiB
Rust
220 lines
6.7 KiB
Rust
// Copyright 2020 - Nym Technologies SA <contact@nymtech.net>
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
use handlebars::Handlebars;
|
|
use nym_network_defaults::mainnet::read_var_if_not_default;
|
|
use nym_network_defaults::var_names::CONFIGURED;
|
|
use serde::de::DeserializeOwned;
|
|
use serde::Serialize;
|
|
use std::any::type_name;
|
|
use std::fmt::Debug;
|
|
#[cfg(unix)]
|
|
use std::os::unix::fs::PermissionsExt;
|
|
use std::path::{Path, PathBuf};
|
|
use std::str::FromStr;
|
|
use std::{fs, io};
|
|
|
|
pub mod defaults;
|
|
|
|
pub const CONFIG_DIR: &str = "config";
|
|
pub const DATA_DIR: &str = "data";
|
|
pub const CRED_DB_FILE_NAME: &str = "credentials_database.db";
|
|
|
|
pub trait NymConfig: Default + Serialize + DeserializeOwned {
|
|
fn template() -> &'static str;
|
|
|
|
fn config_file_name() -> String {
|
|
"config.toml".to_string()
|
|
}
|
|
|
|
fn default_root_directory() -> PathBuf;
|
|
|
|
// default, most probable, implementations; can be easily overridden where required
|
|
fn default_config_directory(id: &str) -> PathBuf {
|
|
Self::default_config_directory_with_root(Self::default_root_directory(), id)
|
|
}
|
|
|
|
fn default_config_directory_with_root<P: AsRef<Path>>(root: P, id: &str) -> PathBuf {
|
|
root.as_ref().join(id).join(CONFIG_DIR)
|
|
}
|
|
|
|
fn default_data_directory(id: &str) -> PathBuf {
|
|
Self::default_data_directory_with_root(Self::default_root_directory(), id)
|
|
}
|
|
|
|
fn default_data_directory_with_root<P: AsRef<Path>>(root: P, id: &str) -> PathBuf {
|
|
root.as_ref().join(id).join(DATA_DIR)
|
|
}
|
|
|
|
fn default_config_file_path(id: &str) -> PathBuf {
|
|
Self::default_config_directory(id).join(Self::config_file_name())
|
|
}
|
|
|
|
fn default_config_file_path_with_root<P: AsRef<Path>>(root: P, id: &str) -> PathBuf {
|
|
Self::default_config_directory_with_root(root, id).join(Self::config_file_name())
|
|
}
|
|
|
|
// We provide a second set of functions that tries to not panic.
|
|
|
|
fn try_default_root_directory() -> Option<PathBuf>;
|
|
|
|
fn try_default_config_directory(id: &str) -> Option<PathBuf> {
|
|
Self::try_default_root_directory().map(|d| d.join(id).join(CONFIG_DIR))
|
|
}
|
|
|
|
fn try_default_data_directory(id: &str) -> Option<PathBuf> {
|
|
Self::try_default_root_directory().map(|d| d.join(id).join(DATA_DIR))
|
|
}
|
|
|
|
fn try_default_config_file_path(id: &str) -> Option<PathBuf> {
|
|
Self::try_default_config_directory(id).map(|d| d.join(Self::config_file_name()))
|
|
}
|
|
|
|
fn root_directory(&self) -> PathBuf;
|
|
fn config_directory(&self) -> PathBuf;
|
|
fn data_directory(&self) -> PathBuf;
|
|
|
|
fn save_to_file(&self, custom_location: Option<PathBuf>) -> io::Result<()> {
|
|
let reg = Handlebars::new();
|
|
// it's whoever is implementing the trait responsibility to make sure you can execute your own template on your data
|
|
let templated_config = reg.render_template(Self::template(), self).unwrap();
|
|
|
|
// make sure the whole directory structure actually exists
|
|
match custom_location.clone() {
|
|
Some(loc) => {
|
|
if let Some(parent_dir) = loc.parent() {
|
|
fs::create_dir_all(parent_dir)
|
|
} else {
|
|
Ok(())
|
|
}
|
|
}
|
|
None => fs::create_dir_all(self.config_directory()),
|
|
}?;
|
|
|
|
let location = custom_location
|
|
.unwrap_or_else(|| self.config_directory().join(Self::config_file_name()));
|
|
log::info!("Configuration file will be saved to {:?}", location);
|
|
|
|
cfg_if::cfg_if! {
|
|
if #[cfg(unix)] {
|
|
fs::write(location.clone(), templated_config)?;
|
|
let mut perms = fs::metadata(location.clone())?.permissions();
|
|
perms.set_mode(0o600);
|
|
fs::set_permissions(location, perms)?;
|
|
} else {
|
|
fs::write(location, templated_config)?;
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn load_from_file(id: &str) -> io::Result<Self> {
|
|
let file = Self::default_config_file_path(id);
|
|
Self::load_from_filepath(file)
|
|
}
|
|
|
|
fn load_from_filepath<P: AsRef<Path>>(filepath: P) -> io::Result<Self> {
|
|
log::trace!("Loading from file: {:#?}", filepath.as_ref().to_owned());
|
|
let config_contents = fs::read_to_string(filepath)?;
|
|
|
|
toml::from_str(&config_contents)
|
|
.map_err(|toml_err| io::Error::new(io::ErrorKind::Other, toml_err))
|
|
}
|
|
}
|
|
|
|
// this function is only used for parsing values from the network defaults and thus the "expect" there are fine
|
|
pub fn parse_urls(raw: &str) -> Vec<url::Url> {
|
|
raw.split(',')
|
|
.map(|raw_url| {
|
|
raw_url
|
|
.trim()
|
|
.parse()
|
|
.expect("one of the provided nym api urls is invalid")
|
|
})
|
|
.collect()
|
|
}
|
|
|
|
pub trait OptionalSet {
|
|
fn with_optional<F, T>(self, f: F, val: Option<T>) -> Self
|
|
where
|
|
F: Fn(Self, T) -> Self,
|
|
Self: Sized,
|
|
{
|
|
if let Some(val) = val {
|
|
f(self, val)
|
|
} else {
|
|
self
|
|
}
|
|
}
|
|
|
|
fn with_validated_optional<F, T, V, E>(
|
|
self,
|
|
f: F,
|
|
value: Option<T>,
|
|
validate: V,
|
|
) -> Result<Self, E>
|
|
where
|
|
F: Fn(Self, T) -> Self,
|
|
V: Fn(&T) -> Result<(), E>,
|
|
Self: Sized,
|
|
{
|
|
if let Some(val) = value {
|
|
validate(&val)?;
|
|
Ok(f(self, val))
|
|
} else {
|
|
Ok(self)
|
|
}
|
|
}
|
|
|
|
fn with_optional_env<F, T>(self, f: F, val: Option<T>, env_var: &str) -> Self
|
|
where
|
|
F: Fn(Self, T) -> Self,
|
|
T: FromStr,
|
|
<T as FromStr>::Err: Debug,
|
|
Self: Sized,
|
|
{
|
|
if let Some(val) = val {
|
|
return f(self, val);
|
|
} else if std::env::var(CONFIGURED).is_ok() {
|
|
if let Some(raw) = read_var_if_not_default(env_var) {
|
|
return f(
|
|
self,
|
|
raw.parse().unwrap_or_else(|err| {
|
|
panic!(
|
|
"failed to parse value of {raw} into type {}. the error was {:?}",
|
|
type_name::<T>(),
|
|
err
|
|
)
|
|
}),
|
|
);
|
|
}
|
|
}
|
|
self
|
|
}
|
|
|
|
fn with_optional_custom_env<F, T, G>(
|
|
self,
|
|
f: F,
|
|
val: Option<T>,
|
|
env_var: &str,
|
|
parser: G,
|
|
) -> Self
|
|
where
|
|
F: Fn(Self, T) -> Self,
|
|
G: Fn(&str) -> T,
|
|
Self: Sized,
|
|
{
|
|
if let Some(val) = val {
|
|
return f(self, val);
|
|
} else if std::env::var(CONFIGURED).is_ok() {
|
|
if let Some(raw) = read_var_if_not_default(env_var) {
|
|
return f(self, parser(&raw));
|
|
}
|
|
}
|
|
self
|
|
}
|
|
}
|
|
|
|
impl<T> OptionalSet for T where T: NymConfig {}
|