|  |  | @ -3,77 +3,149 @@ use std::sync::RwLock; | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  | use handlebars::Handlebars; | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  | use crate::error::Error; | 
			
		
	
		
			
				
					|  |  |  | use crate::util::IntoResult; | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  | lazy_static! { | 
			
		
	
		
			
				
					|  |  |  |     pub static ref CONFIG: Config = Config::load(); | 
			
		
	
		
			
				
					|  |  |  |     pub static ref CONFIG: Config = Config::load().unwrap_or_else(|e| { | 
			
		
	
		
			
				
					|  |  |  |         println!("Error loading config:\n\t{:?}\n", e); | 
			
		
	
		
			
				
					|  |  |  |         exit(12) | 
			
		
	
		
			
				
					|  |  |  |     }); | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  | macro_rules! make_config { | 
			
		
	
		
			
				
					|  |  |  |     ( $( $name:ident: $ty:ty ),+ $(,)* ) => { | 
			
		
	
		
			
				
					|  |  |  |     ( $( $name:ident : $ty:ty $(, $default_fn:expr)? );+ $(;)* ) => { | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |         pub struct Config { inner: RwLock<_Config> } | 
			
		
	
		
			
				
					|  |  |  |         pub struct Config { inner: RwLock<Inner> } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |         #[derive(Default)] | 
			
		
	
		
			
				
					|  |  |  |         struct _Config { | 
			
		
	
		
			
				
					|  |  |  |             _templates: Handlebars, | 
			
		
	
		
			
				
					|  |  |  |            $(pub $name: $ty),+ | 
			
		
	
		
			
				
					|  |  |  |         struct Inner { | 
			
		
	
		
			
				
					|  |  |  |             templates: Handlebars, | 
			
		
	
		
			
				
					|  |  |  |             config: _Config, | 
			
		
	
		
			
				
					|  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |         #[derive(Debug, Default, Serialize, Deserialize)] | 
			
		
	
		
			
				
					|  |  |  |         struct _Config { $(pub $name: $ty),+ } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |         paste::item! { | 
			
		
	
		
			
				
					|  |  |  |         #[allow(unused)] | 
			
		
	
		
			
				
					|  |  |  |         impl Config { | 
			
		
	
		
			
				
					|  |  |  |             $( | 
			
		
	
		
			
				
					|  |  |  |                 pub fn $name(&self) -> $ty { | 
			
		
	
		
			
				
					|  |  |  |                         self.inner.read().unwrap().$name.clone() | 
			
		
	
		
			
				
					|  |  |  |                     self.inner.read().unwrap().config.$name.clone() | 
			
		
	
		
			
				
					|  |  |  |                 } | 
			
		
	
		
			
				
					|  |  |  |                 pub fn [<set_ $name>](&self, value: $ty) { | 
			
		
	
		
			
				
					|  |  |  |                         self.inner.write().unwrap().$name = value; | 
			
		
	
		
			
				
					|  |  |  |                     self.inner.write().unwrap().config.$name = value; | 
			
		
	
		
			
				
					|  |  |  |                 } | 
			
		
	
		
			
				
					|  |  |  |             )+ | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |             pub fn load() -> Result<Self, Error> { | 
			
		
	
		
			
				
					|  |  |  |                 use crate::util::get_env; | 
			
		
	
		
			
				
					|  |  |  |                 dotenv::dotenv().ok(); | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |                 let mut config = _Config::default(); | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |                 $( | 
			
		
	
		
			
				
					|  |  |  |                     config.$name = make_config!{ @expr &stringify!($name).to_uppercase(), $ty, &config, $($default_fn)? }; | 
			
		
	
		
			
				
					|  |  |  |                 )+ | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |                 Ok(Config { | 
			
		
	
		
			
				
					|  |  |  |                     inner: RwLock::new(Inner { | 
			
		
	
		
			
				
					|  |  |  |                         templates: load_templates(&config.templates_folder), | 
			
		
	
		
			
				
					|  |  |  |                         config, | 
			
		
	
		
			
				
					|  |  |  |                     }), | 
			
		
	
		
			
				
					|  |  |  |                 }) | 
			
		
	
		
			
				
					|  |  |  |             } | 
			
		
	
		
			
				
					|  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     }; | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     ( @expr $name:expr, $ty:ty, $config:expr, $default_fn:expr ) => {{ | 
			
		
	
		
			
				
					|  |  |  |         match get_env($name) { | 
			
		
	
		
			
				
					|  |  |  |             Some(v) => v, | 
			
		
	
		
			
				
					|  |  |  |             None => { | 
			
		
	
		
			
				
					|  |  |  |                 let f: &Fn(&_Config) -> _ = &$default_fn; | 
			
		
	
		
			
				
					|  |  |  |                 f($config).into_result()? | 
			
		
	
		
			
				
					|  |  |  |             } | 
			
		
	
		
			
				
					|  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |     }}; | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     ( @expr $name:expr, $ty:ty, $config:expr, ) => { | 
			
		
	
		
			
				
					|  |  |  |         get_env($name) | 
			
		
	
		
			
				
					|  |  |  |     }; | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  | make_config! { | 
			
		
	
		
			
				
					|  |  |  |     database_url: String, | 
			
		
	
		
			
				
					|  |  |  |     icon_cache_folder: String, | 
			
		
	
		
			
				
					|  |  |  |     attachments_folder: String, | 
			
		
	
		
			
				
					|  |  |  |     data_folder:            String, |_| "data".to_string(); | 
			
		
	
		
			
				
					|  |  |  |     database_url:           String, |c| format!("{}/{}", c.data_folder, "db.sqlite3"); | 
			
		
	
		
			
				
					|  |  |  |     icon_cache_folder:      String, |c| format!("{}/{}", c.data_folder, "icon_cache"); | 
			
		
	
		
			
				
					|  |  |  |     attachments_folder:     String, |c| format!("{}/{}", c.data_folder, "attachments"); | 
			
		
	
		
			
				
					|  |  |  |     templates_folder:       String, |c| format!("{}/{}", c.data_folder, "templates"); | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     icon_cache_ttl: u64, | 
			
		
	
		
			
				
					|  |  |  |     icon_cache_negttl: u64, | 
			
		
	
		
			
				
					|  |  |  |     rsa_key_filename:       String, |c| format!("{}/{}", c.data_folder, "rsa_key"); | 
			
		
	
		
			
				
					|  |  |  |     private_rsa_key:        String, |c| format!("{}.der", c.rsa_key_filename); | 
			
		
	
		
			
				
					|  |  |  |     private_rsa_key_pem:    String, |c| format!("{}.pem", c.rsa_key_filename); | 
			
		
	
		
			
				
					|  |  |  |     public_rsa_key:         String, |c| format!("{}.pub.der", c.rsa_key_filename); | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     private_rsa_key: String, | 
			
		
	
		
			
				
					|  |  |  |     private_rsa_key_pem: String, | 
			
		
	
		
			
				
					|  |  |  |     public_rsa_key: String, | 
			
		
	
		
			
				
					|  |  |  |     websocket_enabled:      bool,   |_| false; | 
			
		
	
		
			
				
					|  |  |  |     websocket_address:      String, |_| "0.0.0.0".to_string(); | 
			
		
	
		
			
				
					|  |  |  |     websocket_port:         u16,    |_| 3012; | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     web_vault_folder: String, | 
			
		
	
		
			
				
					|  |  |  |     web_vault_enabled: bool, | 
			
		
	
		
			
				
					|  |  |  |     web_vault_folder:       String, |_| "web-vault/".to_string(); | 
			
		
	
		
			
				
					|  |  |  |     web_vault_enabled:      bool,   |_| true; | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     websocket_enabled: bool, | 
			
		
	
		
			
				
					|  |  |  |     websocket_url: String, | 
			
		
	
		
			
				
					|  |  |  |     icon_cache_ttl:         u64,    |_| 2_592_000; | 
			
		
	
		
			
				
					|  |  |  |     icon_cache_negttl:      u64,    |_| 259_200; | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     extended_logging: bool, | 
			
		
	
		
			
				
					|  |  |  |     log_file: Option<String>, | 
			
		
	
		
			
				
					|  |  |  |     disable_icon_download:  bool,   |_| false; | 
			
		
	
		
			
				
					|  |  |  |     signups_allowed:        bool,   |_| true; | 
			
		
	
		
			
				
					|  |  |  |     invitations_allowed:    bool,   |_| true; | 
			
		
	
		
			
				
					|  |  |  |     password_iterations:    i32,    |_| 100_000; | 
			
		
	
		
			
				
					|  |  |  |     show_password_hint:     bool,   |_| true; | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     disable_icon_download: bool, | 
			
		
	
		
			
				
					|  |  |  |     signups_allowed: bool, | 
			
		
	
		
			
				
					|  |  |  |     invitations_allowed: bool, | 
			
		
	
		
			
				
					|  |  |  |     admin_token: Option<String>, | 
			
		
	
		
			
				
					|  |  |  |     password_iterations: i32, | 
			
		
	
		
			
				
					|  |  |  |     show_password_hint: bool, | 
			
		
	
		
			
				
					|  |  |  |     domain:                 String, |_| "http://localhost".to_string(); | 
			
		
	
		
			
				
					|  |  |  |     domain_set:             bool,   |_| false; | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     domain: String, | 
			
		
	
		
			
				
					|  |  |  |     domain_set: bool, | 
			
		
	
		
			
				
					|  |  |  |     reload_templates:       bool,   |_| false; | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     extended_logging:       bool,   |_| true; | 
			
		
	
		
			
				
					|  |  |  |     log_file:               Option<String>; | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     admin_token:            Option<String>; | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     yubico_client_id:       Option<String>; | 
			
		
	
		
			
				
					|  |  |  |     yubico_secret_key:      Option<String>; | 
			
		
	
		
			
				
					|  |  |  |     yubico_server:          Option<String>; | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     // Mail settings
 | 
			
		
	
		
			
				
					|  |  |  |     smtp_host:              Option<String>; | 
			
		
	
		
			
				
					|  |  |  |     smtp_ssl:               bool,   |_| true; | 
			
		
	
		
			
				
					|  |  |  |     smtp_port:              u16,    |c| if c.smtp_ssl {587} else {25}; | 
			
		
	
		
			
				
					|  |  |  |     smtp_from:              String, |c| if c.smtp_host.is_some() { err!("Please specify SMTP_FROM to enable SMTP support") } else { Ok(String::new() )}; | 
			
		
	
		
			
				
					|  |  |  |     smtp_from_name:         String, |_| "Bitwarden_RS".to_string(); | 
			
		
	
		
			
				
					|  |  |  |     smtp_username:          Option<String>; | 
			
		
	
		
			
				
					|  |  |  |     smtp_password:          Option<String>; | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     yubico_cred_set: bool, | 
			
		
	
		
			
				
					|  |  |  |     yubico_client_id: String, | 
			
		
	
		
			
				
					|  |  |  |     yubico_secret_key: String, | 
			
		
	
		
			
				
					|  |  |  |     yubico_server: Option<String>, | 
			
		
	
		
			
				
					|  |  |  | impl Config { | 
			
		
	
		
			
				
					|  |  |  |     pub fn mail_enabled(&self) -> bool { | 
			
		
	
		
			
				
					|  |  |  |         self.inner.read().unwrap().config.smtp_host.is_some() | 
			
		
	
		
			
				
					|  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     mail: Option<MailConfig>, | 
			
		
	
		
			
				
					|  |  |  |     templates_folder: String, | 
			
		
	
		
			
				
					|  |  |  |     reload_templates: bool, | 
			
		
	
		
			
				
					|  |  |  |     pub fn render_template<T: serde::ser::Serialize>( | 
			
		
	
		
			
				
					|  |  |  |         &self, | 
			
		
	
		
			
				
					|  |  |  |         name: &str, | 
			
		
	
		
			
				
					|  |  |  |         data: &T, | 
			
		
	
		
			
				
					|  |  |  |     ) -> Result<String, crate::error::Error> { | 
			
		
	
		
			
				
					|  |  |  |         if CONFIG.reload_templates() { | 
			
		
	
		
			
				
					|  |  |  |             warn!("RELOADING TEMPLATES"); | 
			
		
	
		
			
				
					|  |  |  |             let hb = load_templates(CONFIG.templates_folder().as_ref()); | 
			
		
	
		
			
				
					|  |  |  |             hb.render(name, data).map_err(Into::into) | 
			
		
	
		
			
				
					|  |  |  |         } else { | 
			
		
	
		
			
				
					|  |  |  |             let hb = &CONFIG.inner.read().unwrap().templates; | 
			
		
	
		
			
				
					|  |  |  |             hb.render(name, data).map_err(Into::into) | 
			
		
	
		
			
				
					|  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  | fn load_templates(path: &str) -> Handlebars { | 
			
		
	
	
		
			
				
					|  |  | @ -106,140 +178,3 @@ fn load_templates(path: &str) -> Handlebars { | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     hb | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  | impl Config { | 
			
		
	
		
			
				
					|  |  |  |     pub fn render_template<T: serde::ser::Serialize>( | 
			
		
	
		
			
				
					|  |  |  |         &self, | 
			
		
	
		
			
				
					|  |  |  |         name: &str, | 
			
		
	
		
			
				
					|  |  |  |         data: &T, | 
			
		
	
		
			
				
					|  |  |  |     ) -> Result<String, crate::error::Error> { | 
			
		
	
		
			
				
					|  |  |  |         if CONFIG.reload_templates() { | 
			
		
	
		
			
				
					|  |  |  |             warn!("RELOADING TEMPLATES"); | 
			
		
	
		
			
				
					|  |  |  |             let hb = load_templates(CONFIG.templates_folder().as_ref()); | 
			
		
	
		
			
				
					|  |  |  |             hb.render(name, data).map_err(Into::into) | 
			
		
	
		
			
				
					|  |  |  |         } else { | 
			
		
	
		
			
				
					|  |  |  |             let hb = &CONFIG.inner.read().unwrap()._templates; | 
			
		
	
		
			
				
					|  |  |  |             hb.render(name, data).map_err(Into::into) | 
			
		
	
		
			
				
					|  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     fn load() -> Self { | 
			
		
	
		
			
				
					|  |  |  |         use crate::util::{get_env, get_env_or}; | 
			
		
	
		
			
				
					|  |  |  |         dotenv::dotenv().ok(); | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |         let df = get_env_or("DATA_FOLDER", "data".to_string()); | 
			
		
	
		
			
				
					|  |  |  |         let key = get_env_or("RSA_KEY_FILENAME", format!("{}/{}", &df, "rsa_key")); | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |         let domain = get_env("DOMAIN"); | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |         let yubico_client_id = get_env("YUBICO_CLIENT_ID"); | 
			
		
	
		
			
				
					|  |  |  |         let yubico_secret_key = get_env("YUBICO_SECRET_KEY"); | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |         let templates_folder = get_env_or("TEMPLATES_FOLDER", format!("{}/{}", &df, "templates")); | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |         let cfg = _Config { | 
			
		
	
		
			
				
					|  |  |  |             database_url: get_env_or("DATABASE_URL", format!("{}/{}", &df, "db.sqlite3")), | 
			
		
	
		
			
				
					|  |  |  |             icon_cache_folder: get_env_or("ICON_CACHE_FOLDER", format!("{}/{}", &df, "icon_cache")), | 
			
		
	
		
			
				
					|  |  |  |             attachments_folder: get_env_or("ATTACHMENTS_FOLDER", format!("{}/{}", &df, "attachments")), | 
			
		
	
		
			
				
					|  |  |  |             _templates: load_templates(&templates_folder), | 
			
		
	
		
			
				
					|  |  |  |             templates_folder, | 
			
		
	
		
			
				
					|  |  |  |             reload_templates: get_env_or("RELOAD_TEMPLATES", false), | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |             // icon_cache_ttl defaults to 30 days (30 * 24 * 60 * 60 seconds)
 | 
			
		
	
		
			
				
					|  |  |  |             icon_cache_ttl: get_env_or("ICON_CACHE_TTL", 2_592_000), | 
			
		
	
		
			
				
					|  |  |  |             // icon_cache_negttl defaults to 3 days (3 * 24 * 60 * 60 seconds)
 | 
			
		
	
		
			
				
					|  |  |  |             icon_cache_negttl: get_env_or("ICON_CACHE_NEGTTL", 259_200), | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |             private_rsa_key: format!("{}.der", &key), | 
			
		
	
		
			
				
					|  |  |  |             private_rsa_key_pem: format!("{}.pem", &key), | 
			
		
	
		
			
				
					|  |  |  |             public_rsa_key: format!("{}.pub.der", &key), | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |             web_vault_folder: get_env_or("WEB_VAULT_FOLDER", "web-vault/".into()), | 
			
		
	
		
			
				
					|  |  |  |             web_vault_enabled: get_env_or("WEB_VAULT_ENABLED", true), | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |             websocket_enabled: get_env_or("WEBSOCKET_ENABLED", false), | 
			
		
	
		
			
				
					|  |  |  |             websocket_url: format!( | 
			
		
	
		
			
				
					|  |  |  |                 "{}:{}", | 
			
		
	
		
			
				
					|  |  |  |                 get_env_or("WEBSOCKET_ADDRESS", "0.0.0.0".to_string()), | 
			
		
	
		
			
				
					|  |  |  |                 get_env_or("WEBSOCKET_PORT", 3012) | 
			
		
	
		
			
				
					|  |  |  |             ), | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |             extended_logging: get_env_or("EXTENDED_LOGGING", true), | 
			
		
	
		
			
				
					|  |  |  |             log_file: get_env("LOG_FILE"), | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |             disable_icon_download: get_env_or("DISABLE_ICON_DOWNLOAD", false), | 
			
		
	
		
			
				
					|  |  |  |             signups_allowed: get_env_or("SIGNUPS_ALLOWED", true), | 
			
		
	
		
			
				
					|  |  |  |             admin_token: get_env("ADMIN_TOKEN"), | 
			
		
	
		
			
				
					|  |  |  |             invitations_allowed: get_env_or("INVITATIONS_ALLOWED", true), | 
			
		
	
		
			
				
					|  |  |  |             password_iterations: get_env_or("PASSWORD_ITERATIONS", 100_000), | 
			
		
	
		
			
				
					|  |  |  |             show_password_hint: get_env_or("SHOW_PASSWORD_HINT", true), | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |             domain_set: domain.is_some(), | 
			
		
	
		
			
				
					|  |  |  |             domain: domain.unwrap_or("http://localhost".into()), | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |             yubico_cred_set: yubico_client_id.is_some() && yubico_secret_key.is_some(), | 
			
		
	
		
			
				
					|  |  |  |             yubico_client_id: yubico_client_id.unwrap_or("00000".into()), | 
			
		
	
		
			
				
					|  |  |  |             yubico_secret_key: yubico_secret_key.unwrap_or("AAAAAAA".into()), | 
			
		
	
		
			
				
					|  |  |  |             yubico_server: get_env("YUBICO_SERVER"), | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |             mail: MailConfig::load(), | 
			
		
	
		
			
				
					|  |  |  |         }; | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |         Config { | 
			
		
	
		
			
				
					|  |  |  |             inner: RwLock::new(cfg), | 
			
		
	
		
			
				
					|  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  | #[derive(Debug, Clone)] | 
			
		
	
		
			
				
					|  |  |  | pub struct MailConfig { | 
			
		
	
		
			
				
					|  |  |  |     pub smtp_host: String, | 
			
		
	
		
			
				
					|  |  |  |     pub smtp_port: u16, | 
			
		
	
		
			
				
					|  |  |  |     pub smtp_ssl: bool, | 
			
		
	
		
			
				
					|  |  |  |     pub smtp_from: String, | 
			
		
	
		
			
				
					|  |  |  |     pub smtp_from_name: String, | 
			
		
	
		
			
				
					|  |  |  |     pub smtp_username: Option<String>, | 
			
		
	
		
			
				
					|  |  |  |     pub smtp_password: Option<String>, | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  | impl MailConfig { | 
			
		
	
		
			
				
					|  |  |  |     fn load() -> Option<Self> { | 
			
		
	
		
			
				
					|  |  |  |         use crate::util::{get_env, get_env_or}; | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |         // When SMTP_HOST is absent, we assume the user does not want to enable it.
 | 
			
		
	
		
			
				
					|  |  |  |         let smtp_host = match get_env("SMTP_HOST") { | 
			
		
	
		
			
				
					|  |  |  |             Some(host) => host, | 
			
		
	
		
			
				
					|  |  |  |             None => return None, | 
			
		
	
		
			
				
					|  |  |  |         }; | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |         let smtp_from = get_env("SMTP_FROM").unwrap_or_else(|| { | 
			
		
	
		
			
				
					|  |  |  |             error!("Please specify SMTP_FROM to enable SMTP support."); | 
			
		
	
		
			
				
					|  |  |  |             exit(1); | 
			
		
	
		
			
				
					|  |  |  |         }); | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |         let smtp_from_name = get_env_or("SMTP_FROM_NAME", "Bitwarden_RS".into()); | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |         let smtp_ssl = get_env_or("SMTP_SSL", true); | 
			
		
	
		
			
				
					|  |  |  |         let smtp_port = get_env("SMTP_PORT").unwrap_or_else(|| if smtp_ssl { 587u16 } else { 25u16 }); | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |         let smtp_username = get_env("SMTP_USERNAME"); | 
			
		
	
		
			
				
					|  |  |  |         let smtp_password = get_env("SMTP_PASSWORD").or_else(|| { | 
			
		
	
		
			
				
					|  |  |  |             if smtp_username.as_ref().is_some() { | 
			
		
	
		
			
				
					|  |  |  |                 error!("SMTP_PASSWORD is mandatory when specifying SMTP_USERNAME."); | 
			
		
	
		
			
				
					|  |  |  |                 exit(1); | 
			
		
	
		
			
				
					|  |  |  |             } else { | 
			
		
	
		
			
				
					|  |  |  |                 None | 
			
		
	
		
			
				
					|  |  |  |             } | 
			
		
	
		
			
				
					|  |  |  |         }); | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |         Some(MailConfig { | 
			
		
	
		
			
				
					|  |  |  |             smtp_host, | 
			
		
	
		
			
				
					|  |  |  |             smtp_port, | 
			
		
	
		
			
				
					|  |  |  |             smtp_ssl, | 
			
		
	
		
			
				
					|  |  |  |             smtp_from, | 
			
		
	
		
			
				
					|  |  |  |             smtp_from_name, | 
			
		
	
		
			
				
					|  |  |  |             smtp_username, | 
			
		
	
		
			
				
					|  |  |  |             smtp_password, | 
			
		
	
		
			
				
					|  |  |  |         }) | 
			
		
	
		
			
				
					|  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
	
		
			
				
					|  |  | 
 |