From 1d87c32b9536fd49b65a11364d882edb115650aa Mon Sep 17 00:00:00 2001 From: Justus Winter Date: Fri, 8 Dec 2017 16:36:40 +0100 Subject: Rework the context. - Create default context with 'Context::new', create builder with 'Context::configure'. - Rename 'Pre' to 'Config'. - Expose builder to ffi. --- src/ffi.rs | 128 ++++++++++++++++++++++++++++++++++++++++++++-------------- src/lib.rs | 60 +++++++++++++++++---------- src/sequoia.h | 79 +++++++++++++++++++++++++++++++++++- 3 files changed, 213 insertions(+), 54 deletions(-) (limited to 'src') diff --git a/src/ffi.rs b/src/ffi.rs index 1a315684..1e1af9b3 100644 --- a/src/ffi.rs +++ b/src/ffi.rs @@ -9,51 +9,33 @@ use std::str; use keys::TPK; use openpgp; use openpgp::types::KeyId; -use super::Context; +use super::{Config, Context}; -/// Create a context object. +/* sequoia::Context. */ + +/// Creates a Context with reasonable defaults. /// -/// If `home` is not `NULL`, it is used as directory containing shared -/// state and rendezvous nodes. If `lib` is not `NULL`, it is used as -/// directory containing backend servers. If either argument is -/// `NULL`, a reasonable default is used. +/// `domain` should uniquely identify your application, it is strongly +/// suggested to use a reversed fully qualified domain name that is +/// associated with your application. `domain` must not be `NULL`. /// /// Returns `NULL` on errors. #[no_mangle] -pub extern "system" fn sq_context_new(domain: *const c_char, - home: *const c_char, - lib: *const c_char) -> *mut Context { +pub extern "system" fn sq_context_new(domain: *const c_char) + -> *mut Context { + assert!(! domain.is_null()); let domain = unsafe { - if domain.is_null() { None } else { Some(CStr::from_ptr(domain)) } - }; - let home = unsafe { - if home.is_null() { None } else { Some(CStr::from_ptr(home)) } + CStr::from_ptr(domain).to_string_lossy() }; - let lib = unsafe { - if lib.is_null() { None } else { Some(CStr::from_ptr(lib)) } - }; - - if domain.is_none() { - return ptr::null_mut(); - } - let mut pre = Context::new(&domain.unwrap().to_string_lossy()); - - if let Some(home) = home { - pre = pre.home(home.to_string_lossy().as_ref()); - } - if let Some(lib) = lib { - pre = pre.lib(lib.to_string_lossy().as_ref()); - } - - if let Ok(context) = pre.finalize() { + if let Ok(context) = Context::new(&domain) { Box::into_raw(Box::new(context)) } else { ptr::null_mut() } } -/// Free a context. +/// Frees a context. #[no_mangle] pub extern "system" fn sq_context_free(context: *mut Context) { unsafe { @@ -61,7 +43,91 @@ pub extern "system" fn sq_context_free(context: *mut Context) { } } +/// Creates a Context that can be configured. +/// +/// `domain` should uniquely identify your application, it is strongly +/// suggested to use a reversed fully qualified domain name that is +/// associated with your application. `domain` must not be `NULL`. +/// +/// The configuration is seeded like in `sq_context_new`, but can be +/// modified. A configuration has to be finalized using +/// `sq_config_build()` in order to turn it into a Context. +#[no_mangle] +pub extern "system" fn sq_context_configure(domain: *const c_char) + -> *mut Config { + assert!(! domain.is_null()); + let domain = unsafe { + CStr::from_ptr(domain).to_string_lossy() + }; + + Box::into_raw(Box::new(Context::configure(&domain))) +} + +/// Returns the domain of the context. +#[no_mangle] +pub extern "system" fn sq_context_domain(ctx: Option<&Context>) -> *const c_char { + assert!(ctx.is_some()); + ctx.unwrap().domain().as_bytes().as_ptr() as *const c_char +} + +/// Returns the directory containing shared state. +#[no_mangle] +pub extern "system" fn sq_context_home(ctx: Option<&Context>) -> *const c_char { + assert!(ctx.is_some()); + ctx.unwrap().home().to_string_lossy().as_ptr() as *const c_char +} + +/// Returns the directory containing backend servers. +#[no_mangle] +pub extern "system" fn sq_context_lib(ctx: Option<&Context>) -> *const c_char { + assert!(ctx.is_some()); + ctx.unwrap().lib().to_string_lossy().as_bytes().as_ptr() as *const c_char +} + +/* sequoia::Config. */ + +/// Finalizes the configuration and return a `Context`. +/// +/// Consumes `cfg`. Returns `NULL` on errors. +#[no_mangle] +pub extern "system" fn sq_config_build(cfg: Option<&mut Config>) + -> *mut Context { + assert!(cfg.is_some()); + let cfg = unsafe { Box::from_raw(cfg.unwrap()) }; + + if let Ok(context) = cfg.build() { + Box::into_raw(Box::new(context)) + } else { + ptr::null_mut() + } +} + +/// Sets the directory containing shared state. +#[no_mangle] +pub extern "system" fn sq_config_home(cfg: Option<&mut Config>, + home: *const c_char) { + assert!(cfg.is_some()); + assert!(! home.is_null()); + let home = unsafe { + CStr::from_ptr(home).to_string_lossy() + }; + cfg.unwrap().set_home(home.as_ref()) +} + +/// Set the directory containing backend servers. +#[no_mangle] +pub extern "system" fn sq_config_lib(cfg: Option<&mut Config>, + lib: *const c_char) { + assert!(cfg.is_some()); + assert!(! lib.is_null()); + let lib = unsafe { + CStr::from_ptr(lib).to_string_lossy() + }; + cfg.unwrap().set_lib(&lib.as_ref()) +} + /* openpgp::types. */ + /// Returns a KeyID with the given `id`. #[no_mangle] pub extern "system" fn sq_keyid_new(id: uint64_t) -> *mut KeyId { diff --git a/src/lib.rs b/src/lib.rs index 97315726..e3890089 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -28,7 +28,7 @@ use std::path::{Path, PathBuf}; /// /// ``` /// # use sequoia::Context; -/// let c = Context::new("org.example.webmail").finalize().unwrap(); +/// let c = Context::new("org.example.webmail").unwrap(); /// ``` pub struct Context { domain: String, @@ -42,16 +42,26 @@ fn prefix() -> PathBuf { } impl Context { - /// Creates a `Pre(Context)` with reasonable defaults. + /// Creates a Context with reasonable defaults. + /// + /// `domain` should uniquely identify your application, it is + /// strongly suggested to use a reversed fully qualified domain + /// name that is associated with your application. + pub fn new(domain: &str) -> io::Result { + Self::configure(domain).build() + } + + /// Creates a Context that can be configured. /// /// `domain` should uniquely identify your application, it is /// strongly suggested to use a reversed fully qualified domain /// name that is associated with your application. /// - /// `Pre(Context)`s can be modified, and have to be finalized in - /// order to turn them into a Context. - pub fn new(domain: &str) -> Pre { - Pre(Context { + /// The configuration is seeded like in `Context::new`, but can be + /// modified. A configuration has to be finalized using + /// `.build()` in order to turn it into a Context. + pub fn configure(domain: &str) -> Config { + Config(Context { domain: String::from(domain), home: env::home_dir().unwrap_or(env::temp_dir()) .join(".sequoia"), @@ -59,44 +69,52 @@ impl Context { }) } - /// Return the directory containing backend servers. + /// Returns the domain of the context. pub fn domain(&self) -> &str { &self.domain } - /// Return the directory containing shared state and rendezvous - /// nodes. + /// Returns the directory containing shared state. pub fn home(&self) -> &Path { &self.home } - /// Return the directory containing backend servers. + /// Returns the directory containing backend servers. pub fn lib(&self) -> &Path { &self.lib } } -/// A `Pre(Context)` is a context object that can be modified. -pub struct Pre(Context); +/// Represents a `Context` configuration. +pub struct Config(Context); -impl Pre { - /// Finalize the configuration and return a `Context`. - pub fn finalize(self) -> io::Result { +impl Config { + /// Finalizes the configuration and return a `Context`. + pub fn build(self) -> io::Result { let c = self.0; fs::create_dir_all(c.home())?; Ok(c) } - /// Set the directory containing shared state and rendezvous - /// nodes. - pub fn home>(mut self, new: P) -> Self { - self.0.home = PathBuf::new().join(new); + /// Sets the directory containing shared state. + pub fn home>(mut self, home: P) -> Self { + self.set_home(home); self } + /// Sets the directory containing shared state. + pub fn set_home>(&mut self, home: P) { + self.0.home = PathBuf::new().join(home); + } + /// Set the directory containing backend servers. - pub fn lib>(mut self, new: P) -> Self { - self.0.lib = PathBuf::new().join(new); + pub fn lib>(mut self, lib: P) -> Self { + self.set_lib(lib); self } + + /// Sets the directory containing shared state. + pub fn set_lib>(&mut self, lib: P) { + self.0.lib = PathBuf::new().join(lib); + } } diff --git a/src/sequoia.h b/src/sequoia.h index 51ac441b..1b7015b5 100644 --- a/src/sequoia.h +++ b/src/sequoia.h @@ -1,13 +1,88 @@ #ifndef SEQUOIA_H #define SEQUOIA_H +#include #include +/*/ +/// A `&Context` is required for many operations. +/// +/// # Example +/// +/// ```c +/// struct sq_context *ctx sq_context_new("org.sequoia-pgp.example"); +/// if (ctx == 0) { ... } +/// ``` +/*/ struct sq_context; -struct sq_context *sq_context_new(const char *domain, const char *home, - const char *lib); + +/*/ +/*/ +struct sq_config; + +/*/ +/// Creates a Context with reasonable defaults. +/// +/// `domain` should uniquely identify your application, it is strongly +/// suggested to use a reversed fully qualified domain name that is +/// associated with your application. `domain` must not be `NULL`. +/// +/// Returns `NULL` on errors. +/*/ +struct sq_context *sq_context_new(const char *domain); + +/*/ +/// Frees a context. +/*/ void sq_context_free(struct sq_context *context); +/*/ +/// Creates a Context that can be configured. +/// +/// `domain` should uniquely identify your application, it is strongly +/// suggested to use a reversed fully qualified domain name that is +/// associated with your application. `domain` must not be `NULL`. +/// +/// The configuration is seeded like in `sq_context_new`, but can be +/// modified. A configuration has to be finalized using +/// `sq_config_build()` in order to turn it into a Context. +/*/ +struct sq_config *sq_context_configure(const char *domain); + +/*/ +/// Returns the domain of the context. +/*/ +const char *sq_context_domain(const struct sq_context *ctx); + +/*/ +/// Returns the directory containing shared state. +/*/ +const char *sq_context_home(const struct sq_context *ctx); + +/*/ +/// Returns the directory containing backend servers. +/*/ +const char *sq_context_lib(const struct sq_context *ctx); + +/* sequoia::Config. */ + +/*/ +/// Finalizes the configuration and return a `Context`. +/// +/// Consumes `cfg`. Returns `NULL` on errors. +/*/ +struct sq_context *sq_config_build(struct sq_config *cfg); + +/*/ +/// Sets the directory containing shared state. +/*/ +void sq_config_home(struct sq_config *cfg, const char *home); + +/*/ +/// Set the directory containing backend servers. +/*/ +void sq_config_lib(struct sq_config *cfg, const char *lib); + struct sq_keyid; struct sq_keyid *sq_keyid_new (uint64_t id); struct sq_keyid *sq_keyid_from_hex (const char *id); -- cgit v1.2.3