From f7ffb81d9ec0c5deb159bae9064b7f5fb4231731 Mon Sep 17 00:00:00 2001
From: Jean-Christophe BEGUE <jean-Christophe.begue@fraudbuster.mobi>
Date: Mon, 13 Aug 2018 13:46:32 +0200
Subject: [PATCH 1/9] SMTP configuration parsing and checking

---
 src/main.rs | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 55 insertions(+)

diff --git a/src/main.rs b/src/main.rs
index 4a42452d..e506e05e 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -153,6 +153,56 @@ lazy_static! {
     static ref CONFIG: Config = Config::load();
 }
 
+#[derive(Debug)]
+pub struct MailConfig {
+    reply_to_email: Option<String>,
+    smtp_host: String,
+    smtp_port: u16,
+    smtp_ssl: bool,
+    smtp_username: String,
+    smtp_password: String,
+}
+
+impl MailConfig {
+    fn load() -> Option<Self> {
+        let smtp_host = util::parse_option_string(env::var("SMTP_HOST").ok());
+        
+        // When SMTP_HOST is absent, we assume the user does not want to enable it.
+        if smtp_host.is_none() {
+            return None
+        }
+
+        let smtp_ssl = util::parse_option_string(env::var("SMTP_SSL").ok()).unwrap_or(false);
+        let smtp_port = util::parse_option_string(env::var("SMTP_PORT").ok())
+            .unwrap_or_else(|| {
+                if smtp_ssl {
+                    465u16
+                } else {
+                    25u16
+                }
+            });
+
+        Some(MailConfig {
+            reply_to_email: util::parse_option_string(env::var("REPLY_TO_EMAIL").ok()),
+            smtp_host: smtp_host.unwrap(),
+            smtp_port: smtp_port,
+            smtp_ssl: smtp_ssl,
+            // If username or password is not specified, and SMTP support seems to be wanted,
+            // don't let the app start: the configuration is clearly incomplete.
+            smtp_username: util::parse_option_string(env::var("SMTP_USERNAME").ok())
+                .unwrap_or_else(|| {
+                    println!("Please specify SMTP_USERNAME to enable SMTP support.");
+                    exit(1);
+                }),
+            smtp_password: util::parse_option_string(env::var("SMTP_PASSWORD").ok())
+                .unwrap_or_else(|| {
+                    println!("Please specify SMTP_PASSWORD to enable SMTP support.");
+                    exit(1);
+                }),
+        })
+    }
+}
+
 #[derive(Debug)]
 pub struct Config {
     database_url: String,
@@ -170,8 +220,11 @@ pub struct Config {
     signups_allowed: bool,
     password_iterations: i32,
     show_password_hint: bool,
+
     domain: String,
     domain_set: bool,
+
+    mail: Option<MailConfig>,
 }
 
 impl Config {
@@ -202,6 +255,8 @@ impl Config {
 
             domain_set: domain.is_ok(),
             domain: domain.unwrap_or("http://localhost".into()),
+
+            mail: MailConfig::load(),
         }
     }
 }

From 812387e5860daefba2e0f57237cc65ae330e6196 Mon Sep 17 00:00:00 2001
From: Jean-Christophe BEGUE <jc.begue@devisubox.com>
Date: Wed, 15 Aug 2018 08:32:19 +0200
Subject: [PATCH 2/9] SMTP integration, send password hint by email.

---
 .env                     |  7 ++++++
 Cargo.toml               |  4 ++++
 src/api/core/accounts.rs | 25 ++++++++++++++-------
 src/mail.rs              | 47 ++++++++++++++++++++++++++++++++++++++++
 src/main.rs              | 15 ++++++++-----
 5 files changed, 85 insertions(+), 13 deletions(-)
 create mode 100644 src/mail.rs

diff --git a/.env b/.env
index 735724ee..475df852 100644
--- a/.env
+++ b/.env
@@ -41,3 +41,10 @@
 # ROCKET_ADDRESS=0.0.0.0 # Enable this to test mobile app
 # ROCKET_PORT=8000
 # ROCKET_TLS={certs="/path/to/certs.pem",key="/path/to/key.pem"}
+
+## Mail specific settings, if SMTP_HOST is specified, SMTP_USERNAME and SMTP_PASSWORD are mandatory
+# SMTP_HOST=smtp.domain.tld
+# SMTP_PORT=587
+# SMTP_SSL=true
+# SMTP_USERNAME=username
+# SMTP_PASSWORD=password
\ No newline at end of file
diff --git a/Cargo.toml b/Cargo.toml
index fb5adafb..537b4e1e 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -58,6 +58,10 @@ lazy_static = "1.0.1"
 num-traits = "0.2.5"
 num-derive = "0.2.2"
 
+lettre = "0.8.2"
+lettre_email = "0.8.2"
+native-tls = "0.1.5"
+
 [patch.crates-io]
  # Make jwt use ring 0.11, to match rocket
 jsonwebtoken = { path = "libs/jsonwebtoken" }
diff --git a/src/api/core/accounts.rs b/src/api/core/accounts.rs
index e45c08dc..bc018f1e 100644
--- a/src/api/core/accounts.rs
+++ b/src/api/core/accounts.rs
@@ -5,6 +5,7 @@ use db::models::*;
 
 use api::{PasswordData, JsonResult, EmptyResult, JsonUpcase, NumberOrString};
 use auth::Headers;
+use mail;
 
 use CONFIG;
 
@@ -258,15 +259,23 @@ struct PasswordHintData {
 fn password_hint(data: JsonUpcase<PasswordHintData>, conn: DbConn) -> EmptyResult {
     let data: PasswordHintData = data.into_inner().data;
 
-    if !CONFIG.show_password_hint {
-        return Ok(())
+    let user = User::find_by_mail(&data.Email, &conn);
+    if user.is_none() {
+        return Ok(());
+    }
+
+    let user = user.unwrap();
+    let hint = user.password_hint.to_owned().unwrap_or("You don't have any...".to_string());
+
+    if let Some(ref mail_config) = CONFIG.mail {
+        if let Err(e) = mail::send_password_hint(&user.email, &hint, mail_config) {
+            err!(format!("There have been a problem sending the email: {}", e));
+        }
     }
 
-    match User::find_by_mail(&data.Email, &conn) {
-        Some(user) => {
-            let hint = user.password_hint.to_owned().unwrap_or_default();
-            err!(format!("Your password hint is: {}", hint))
-        },
-        None => Ok(()),
+    if !CONFIG.show_password_hint {
+        err!(format!("Your password hint is: {}", &hint));
     }
+
+    Ok(())
 }
diff --git a/src/mail.rs b/src/mail.rs
new file mode 100644
index 00000000..7faa791f
--- /dev/null
+++ b/src/mail.rs
@@ -0,0 +1,47 @@
+use std::error::Error;
+use native_tls::TlsConnector;
+use native_tls::{Protocol};
+use lettre::{EmailTransport, SmtpTransport, ClientTlsParameters, ClientSecurity};
+use lettre::smtp::{ConnectionReuseParameters, SmtpTransportBuilder};
+use lettre::smtp::authentication::{Credentials, Mechanism};
+use lettre_email::EmailBuilder;
+
+use MailConfig;
+
+fn mailer(config: &MailConfig) -> SmtpTransport {
+    let client_security = if config.smtp_ssl {
+        let mut tls_builder = TlsConnector::builder().unwrap();
+        tls_builder.supported_protocols(&[
+            Protocol::Tlsv10, Protocol::Tlsv11, Protocol::Tlsv12
+        ]).unwrap();
+
+        ClientSecurity::Required(
+            ClientTlsParameters::new(config.smtp_host.to_owned(), tls_builder.build().unwrap())
+        )
+    } else {
+        ClientSecurity::None
+    };
+
+    SmtpTransportBuilder::new((config.smtp_host.to_owned().as_str(), config.smtp_port), client_security)
+        .unwrap()
+        .credentials(Credentials::new(config.smtp_username.to_owned(), config.smtp_password.to_owned()))
+        .authentication_mechanism(Mechanism::Login)
+        .smtp_utf8(true)
+        .connection_reuse(ConnectionReuseParameters::ReuseUnlimited)
+        .build()
+}
+
+pub fn send_password_hint(address: &str, hint: &str, config: &MailConfig) -> Result<(), String> {
+    let email = EmailBuilder::new()
+        .to(address)
+        .from((config.smtp_from.to_owned(), "Bitwarden-rs"))
+        .subject("Your Master Password Hint")
+        .body(hint)
+        .build().unwrap();
+
+    match mailer(config).send(&email) {
+        Ok(_) => Ok(()),
+        Err(e) => Err(e.description().to_string()),
+    }
+}
+        
diff --git a/src/main.rs b/src/main.rs
index e506e05e..8f39057f 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -26,6 +26,9 @@ extern crate lazy_static;
 #[macro_use]
 extern crate num_derive;
 extern crate num_traits;
+extern crate lettre;
+extern crate lettre_email;
+extern crate native_tls;
 
 use std::{env, path::Path, process::{exit, Command}};
 use rocket::Rocket;
@@ -37,6 +40,7 @@ mod api;
 mod db;
 mod crypto;
 mod auth;
+mod mail;
 
 fn init_rocket() -> Rocket {
     rocket::ignite()
@@ -155,10 +159,10 @@ lazy_static! {
 
 #[derive(Debug)]
 pub struct MailConfig {
-    reply_to_email: Option<String>,
     smtp_host: String,
     smtp_port: u16,
     smtp_ssl: bool,
+    smtp_from: String,
     smtp_username: String,
     smtp_password: String,
 }
@@ -172,22 +176,23 @@ impl MailConfig {
             return None
         }
 
-        let smtp_ssl = util::parse_option_string(env::var("SMTP_SSL").ok()).unwrap_or(false);
+        let smtp_ssl = util::parse_option_string(env::var("SMTP_SSL").ok()).unwrap_or(true);
         let smtp_port = util::parse_option_string(env::var("SMTP_PORT").ok())
             .unwrap_or_else(|| {
                 if smtp_ssl {
-                    465u16
+                    587u16
                 } else {
                     25u16
                 }
             });
 
         Some(MailConfig {
-            reply_to_email: util::parse_option_string(env::var("REPLY_TO_EMAIL").ok()),
             smtp_host: smtp_host.unwrap(),
             smtp_port: smtp_port,
             smtp_ssl: smtp_ssl,
-            // If username or password is not specified, and SMTP support seems to be wanted,
+            smtp_from: util::parse_option_string(env::var("SMTP_FROM").ok())
+                .unwrap_or("bitwarden@localhost".to_string()),
+            // If username or password is not specified and SMTP support seems to be wanted,
             // don't let the app start: the configuration is clearly incomplete.
             smtp_username: util::parse_option_string(env::var("SMTP_USERNAME").ok())
                 .unwrap_or_else(|| {

From 19e0605d30384e04b5bba4a3aa34d40619f61add Mon Sep 17 00:00:00 2001
From: Jean-Christophe BEGUE <jc.begue@devisubox.com>
Date: Wed, 15 Aug 2018 10:17:05 +0200
Subject: [PATCH 3/9] Better message into the password hint email

---
 src/mail.rs | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/src/mail.rs b/src/mail.rs
index 7faa791f..0af83ce7 100644
--- a/src/mail.rs
+++ b/src/mail.rs
@@ -32,11 +32,17 @@ fn mailer(config: &MailConfig) -> SmtpTransport {
 }
 
 pub fn send_password_hint(address: &str, hint: &str, config: &MailConfig) -> Result<(), String> {
+    let body = format!(
+        "You (or someone) recently requested your master password hint.\n\n\
+         Your hint is:  \"{}\"\n\n\
+         If you did not request your master password hint you can safely ignore this email.\n",
+        hint); 
+
     let email = EmailBuilder::new()
         .to(address)
         .from((config.smtp_from.to_owned(), "Bitwarden-rs"))
         .subject("Your Master Password Hint")
-        .body(hint)
+        .body(body)
         .build().unwrap();
 
     match mailer(config).send(&email) {

From d68f57cbba3e1d1c929fbb72b135ffbd88c3732a Mon Sep 17 00:00:00 2001
From: Jean-Christophe BEGUE <jc.begue@devisubox.com>
Date: Wed, 15 Aug 2018 14:08:00 +0200
Subject: [PATCH 4/9] Fix password hint showing logic

---
 src/api/core/accounts.rs | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/src/api/core/accounts.rs b/src/api/core/accounts.rs
index bc018f1e..1d6469c2 100644
--- a/src/api/core/accounts.rs
+++ b/src/api/core/accounts.rs
@@ -265,15 +265,16 @@ fn password_hint(data: JsonUpcase<PasswordHintData>, conn: DbConn) -> EmptyResul
     }
 
     let user = user.unwrap();
-    let hint = user.password_hint.to_owned().unwrap_or("You don't have any...".to_string());
+    let hint = match user.password_hint {
+        Some(hint) => hint,
+        None => return Ok(()),
+    };
 
     if let Some(ref mail_config) = CONFIG.mail {
         if let Err(e) = mail::send_password_hint(&user.email, &hint, mail_config) {
             err!(format!("There have been a problem sending the email: {}", e));
         }
-    }
-
-    if !CONFIG.show_password_hint {
+    } else if CONFIG.show_password_hint {
         err!(format!("Your password hint is: {}", &hint));
     }
 

From 401aa7c699d87a7c6777893e0dc010a722bfba24 Mon Sep 17 00:00:00 2001
From: Jean-Christophe BEGUE <jc.begue@devisubox.com>
Date: Wed, 15 Aug 2018 17:00:55 +0200
Subject: [PATCH 5/9] make SMTP authentication optionnal, let lettre pick the
 better auth mechanism

---
 src/mail.rs | 26 ++++++++++++++++----------
 src/main.rs | 32 ++++++++++++++++----------------
 2 files changed, 32 insertions(+), 26 deletions(-)

diff --git a/src/mail.rs b/src/mail.rs
index 0af83ce7..fef03c25 100644
--- a/src/mail.rs
+++ b/src/mail.rs
@@ -3,7 +3,7 @@ use native_tls::TlsConnector;
 use native_tls::{Protocol};
 use lettre::{EmailTransport, SmtpTransport, ClientTlsParameters, ClientSecurity};
 use lettre::smtp::{ConnectionReuseParameters, SmtpTransportBuilder};
-use lettre::smtp::authentication::{Credentials, Mechanism};
+use lettre::smtp::authentication::Credentials;
 use lettre_email::EmailBuilder;
 
 use MailConfig;
@@ -11,10 +11,7 @@ use MailConfig;
 fn mailer(config: &MailConfig) -> SmtpTransport {
     let client_security = if config.smtp_ssl {
         let mut tls_builder = TlsConnector::builder().unwrap();
-        tls_builder.supported_protocols(&[
-            Protocol::Tlsv10, Protocol::Tlsv11, Protocol::Tlsv12
-        ]).unwrap();
-
+        tls_builder.supported_protocols(&[Protocol::Tlsv11, Protocol::Tlsv12]).unwrap();
         ClientSecurity::Required(
             ClientTlsParameters::new(config.smtp_host.to_owned(), tls_builder.build().unwrap())
         )
@@ -22,12 +19,21 @@ fn mailer(config: &MailConfig) -> SmtpTransport {
         ClientSecurity::None
     };
 
-    SmtpTransportBuilder::new((config.smtp_host.to_owned().as_str(), config.smtp_port), client_security)
-        .unwrap()
-        .credentials(Credentials::new(config.smtp_username.to_owned(), config.smtp_password.to_owned()))
-        .authentication_mechanism(Mechanism::Login)
+    let smtp_transport = SmtpTransportBuilder::new(
+        (config.smtp_host.to_owned().as_str(), config.smtp_port),
+        client_security
+    ).unwrap();
+
+    let smtp_transport = match (&config.smtp_username, &config.smtp_password) {
+        (Some(username), Some(password)) => {
+            smtp_transport.credentials(Credentials::new(username.to_owned(), password.to_owned()))
+        },
+        (_, _) => smtp_transport,
+    };
+
+    smtp_transport
         .smtp_utf8(true)
-        .connection_reuse(ConnectionReuseParameters::ReuseUnlimited)
+        .connection_reuse(ConnectionReuseParameters::NoReuse)
         .build()
 }
 
diff --git a/src/main.rs b/src/main.rs
index 8f39057f..9c0675e7 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -163,13 +163,13 @@ pub struct MailConfig {
     smtp_port: u16,
     smtp_ssl: bool,
     smtp_from: String,
-    smtp_username: String,
-    smtp_password: String,
+    smtp_username: Option<String>,
+    smtp_password: Option<String>,
 }
 
 impl MailConfig {
     fn load() -> Option<Self> {
-        let smtp_host = util::parse_option_string(env::var("SMTP_HOST").ok());
+        let smtp_host = env::var("SMTP_HOST").ok();
         
         // When SMTP_HOST is absent, we assume the user does not want to enable it.
         if smtp_host.is_none() {
@@ -186,24 +186,24 @@ impl MailConfig {
                 }
             });
 
+        let smtp_username = env::var("SMTP_USERNAME").ok();
+        let smtp_password = env::var("SMTP_PASSWORD").ok().or_else(|| {
+            if smtp_username.as_ref().is_some() {
+                println!("Please specify SMTP_PASSWORD to enable SMTP support.");
+                exit(1);
+            } else {
+                None
+            }
+        });
+
         Some(MailConfig {
             smtp_host: smtp_host.unwrap(),
             smtp_port: smtp_port,
             smtp_ssl: smtp_ssl,
             smtp_from: util::parse_option_string(env::var("SMTP_FROM").ok())
-                .unwrap_or("bitwarden@localhost".to_string()),
-            // If username or password is not specified and SMTP support seems to be wanted,
-            // don't let the app start: the configuration is clearly incomplete.
-            smtp_username: util::parse_option_string(env::var("SMTP_USERNAME").ok())
-                .unwrap_or_else(|| {
-                    println!("Please specify SMTP_USERNAME to enable SMTP support.");
-                    exit(1);
-                }),
-            smtp_password: util::parse_option_string(env::var("SMTP_PASSWORD").ok())
-                .unwrap_or_else(|| {
-                    println!("Please specify SMTP_PASSWORD to enable SMTP support.");
-                    exit(1);
-                }),
+                .unwrap_or("bitwarden-rs@localhost".to_string()),
+            smtp_username: smtp_username,
+            smtp_password: smtp_password,
         })
     }
 }

From 9e63985b284e4529825b8ac9a41a27eb42153396 Mon Sep 17 00:00:00 2001
From: Jean-Christophe BEGUE <jc.begue@devisubox.com>
Date: Wed, 15 Aug 2018 17:25:59 +0200
Subject: [PATCH 6/9] Check email validity before using it for password hint
 sending

---
 Cargo.toml               | 1 +
 src/api/core/accounts.rs | 5 +++++
 src/mail.rs              | 3 +--
 src/main.rs              | 1 +
 4 files changed, 8 insertions(+), 2 deletions(-)

diff --git a/Cargo.toml b/Cargo.toml
index 537b4e1e..3a514738 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -61,6 +61,7 @@ num-derive = "0.2.2"
 lettre = "0.8.2"
 lettre_email = "0.8.2"
 native-tls = "0.1.5"
+fast_chemail = "0.9.5"
 
 [patch.crates-io]
  # Make jwt use ring 0.11, to match rocket
diff --git a/src/api/core/accounts.rs b/src/api/core/accounts.rs
index 1d6469c2..ffc76f3a 100644
--- a/src/api/core/accounts.rs
+++ b/src/api/core/accounts.rs
@@ -5,6 +5,7 @@ use db::models::*;
 
 use api::{PasswordData, JsonResult, EmptyResult, JsonUpcase, NumberOrString};
 use auth::Headers;
+use fast_chemail::is_valid_email;
 use mail;
 
 use CONFIG;
@@ -259,6 +260,10 @@ struct PasswordHintData {
 fn password_hint(data: JsonUpcase<PasswordHintData>, conn: DbConn) -> EmptyResult {
     let data: PasswordHintData = data.into_inner().data;
 
+    if !is_valid_email(&data.Email) {
+        return Ok(());
+    }
+
     let user = User::find_by_mail(&data.Email, &conn);
     if user.is_none() {
         return Ok(());
diff --git a/src/mail.rs b/src/mail.rs
index fef03c25..09409e94 100644
--- a/src/mail.rs
+++ b/src/mail.rs
@@ -1,6 +1,5 @@
 use std::error::Error;
-use native_tls::TlsConnector;
-use native_tls::{Protocol};
+use native_tls::{Protocol, TlsConnector};
 use lettre::{EmailTransport, SmtpTransport, ClientTlsParameters, ClientSecurity};
 use lettre::smtp::{ConnectionReuseParameters, SmtpTransportBuilder};
 use lettre::smtp::authentication::Credentials;
diff --git a/src/main.rs b/src/main.rs
index 9c0675e7..5fbefb0b 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -29,6 +29,7 @@ extern crate num_traits;
 extern crate lettre;
 extern crate lettre_email;
 extern crate native_tls;
+extern crate fast_chemail;
 
 use std::{env, path::Path, process::{exit, Command}};
 use rocket::Rocket;

From e2ab2f7306a7adf31daf9f560f840be9174e583f Mon Sep 17 00:00:00 2001
From: Jean-Christophe BEGUE <jean-Christophe.begue@fraudbuster.mobi>
Date: Tue, 11 Sep 2018 13:00:59 +0200
Subject: [PATCH 7/9] Save None instead of empty password hint

---
 src/api/core/accounts.rs | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/src/api/core/accounts.rs b/src/api/core/accounts.rs
index ffc76f3a..cfec3e73 100644
--- a/src/api/core/accounts.rs
+++ b/src/api/core/accounts.rs
@@ -82,7 +82,10 @@ fn post_profile(data: JsonUpcase<ProfileData>, headers: Headers, conn: DbConn) -
     let mut user = headers.user;
 
     user.name = data.Name;
-    user.password_hint = data.MasterPasswordHint;
+    user.password_hint = match data.MasterPasswordHint {
+        Some(ref h) if h.is_empty() => None,
+        _ => data.MasterPasswordHint,
+    };
     user.save(&conn);
 
     Ok(Json(user.to_json(&conn)))

From 1c641d7635d12c9fcb1efce6262c6f33606b61aa Mon Sep 17 00:00:00 2001
From: Jean-Christophe BEGUE <jean-Christophe.begue@fraudbuster.mobi>
Date: Tue, 11 Sep 2018 13:04:34 +0200
Subject: [PATCH 8/9] Special messages when user has no password hint

---
 src/api/core/accounts.rs | 13 ++++++-------
 src/mail.rs              | 21 +++++++++++++--------
 2 files changed, 19 insertions(+), 15 deletions(-)

diff --git a/src/api/core/accounts.rs b/src/api/core/accounts.rs
index cfec3e73..11b7700b 100644
--- a/src/api/core/accounts.rs
+++ b/src/api/core/accounts.rs
@@ -273,17 +273,16 @@ fn password_hint(data: JsonUpcase<PasswordHintData>, conn: DbConn) -> EmptyResul
     }
 
     let user = user.unwrap();
-    let hint = match user.password_hint {
-        Some(hint) => hint,
-        None => return Ok(()),
-    };
-
     if let Some(ref mail_config) = CONFIG.mail {
-        if let Err(e) = mail::send_password_hint(&user.email, &hint, mail_config) {
+        if let Err(e) = mail::send_password_hint(&user.email, user.password_hint, mail_config) {
             err!(format!("There have been a problem sending the email: {}", e));
         }
     } else if CONFIG.show_password_hint {
-        err!(format!("Your password hint is: {}", &hint));
+        if let Some(hint) = user.password_hint {
+            err!(format!("Your password hint is: {}", &hint));
+        } else {
+            err!(format!("Sorry, you have no password hint..."));
+        }
     }
 
     Ok(())
diff --git a/src/mail.rs b/src/mail.rs
index 09409e94..ccf83cca 100644
--- a/src/mail.rs
+++ b/src/mail.rs
@@ -36,17 +36,23 @@ fn mailer(config: &MailConfig) -> SmtpTransport {
         .build()
 }
 
-pub fn send_password_hint(address: &str, hint: &str, config: &MailConfig) -> Result<(), String> {
-    let body = format!(
-        "You (or someone) recently requested your master password hint.\n\n\
-         Your hint is:  \"{}\"\n\n\
-         If you did not request your master password hint you can safely ignore this email.\n",
-        hint); 
+pub fn send_password_hint(address: &str, hint: Option<String>, config: &MailConfig) -> Result<(), String> {
+    let (subject, body) = if let Some(hint) = hint {
+        ("Your master password hint",
+         format!(
+            "You (or someone) recently requested your master password hint.\n\n\
+             Your hint is:  \"{}\"\n\n\
+             If you did not request your master password hint you can safely ignore this email.\n",
+            hint))
+    } else {
+        ("Sorry, you have no password hint...",
+         "Sorry, you have not specified any password hint...\n".to_string())
+    };
 
     let email = EmailBuilder::new()
         .to(address)
         .from((config.smtp_from.to_owned(), "Bitwarden-rs"))
-        .subject("Your Master Password Hint")
+        .subject(subject)
         .body(body)
         .build().unwrap();
 
@@ -55,4 +61,3 @@ pub fn send_password_hint(address: &str, hint: &str, config: &MailConfig) -> Res
         Err(e) => Err(e.description().to_string()),
     }
 }
-        

From 37d88be2be484ab044d0954600049909876348ef Mon Sep 17 00:00:00 2001
From: Jean-Christophe BEGUE <jean-Christophe.begue@fraudbuster.mobi>
Date: Tue, 11 Sep 2018 13:12:24 +0200
Subject: [PATCH 9/9] return an error when email adress for password hint is
 not valid

---
 src/api/core/accounts.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/api/core/accounts.rs b/src/api/core/accounts.rs
index 11b7700b..2f66b200 100644
--- a/src/api/core/accounts.rs
+++ b/src/api/core/accounts.rs
@@ -264,7 +264,7 @@ fn password_hint(data: JsonUpcase<PasswordHintData>, conn: DbConn) -> EmptyResul
     let data: PasswordHintData = data.into_inner().data;
 
     if !is_valid_email(&data.Email) {
-        return Ok(());
+        return err!("This email address is not valid...");
     }
 
     let user = User::find_by_mail(&data.Email, &conn);