2018-08-15 17:00:55 +02:00
use lettre ::smtp ::authentication ::Credentials ;
2018-12-30 23:34:31 +01:00
use lettre ::smtp ::ConnectionReuseParameters ;
use lettre ::{ ClientSecurity , ClientTlsParameters , SmtpClient , SmtpTransport , Transport } ;
2018-08-15 08:32:19 +02:00
use lettre_email ::EmailBuilder ;
2018-12-30 23:34:31 +01:00
use native_tls ::{ Protocol , TlsConnector } ;
2018-08-15 08:32:19 +02:00
2018-12-07 02:05:45 +01:00
use crate ::MailConfig ;
2019-01-04 16:32:51 +01:00
use crate ::CONFIG ;
2019-01-06 05:03:49 +01:00
use crate ::auth ::{ generate_invite_claims , encode_jwt } ;
2018-12-19 21:52:53 +01:00
use crate ::api ::EmptyResult ;
use crate ::error ::Error ;
2018-08-15 08:32:19 +02:00
fn mailer ( config : & MailConfig ) -> SmtpTransport {
let client_security = if config . smtp_ssl {
2018-10-04 00:01:04 +02:00
let tls = TlsConnector ::builder ( )
. min_protocol_version ( Some ( Protocol ::Tlsv11 ) )
. build ( )
. unwrap ( ) ;
ClientSecurity ::Required ( ClientTlsParameters ::new ( config . smtp_host . clone ( ) , tls ) )
2018-08-15 08:32:19 +02:00
} else {
ClientSecurity ::None
} ;
2018-12-30 23:34:31 +01:00
let smtp_client = SmtpClient ::new ( ( config . smtp_host . as_str ( ) , config . smtp_port ) , client_security ) . unwrap ( ) ;
2018-08-15 17:00:55 +02:00
2018-09-19 21:45:50 +02:00
let smtp_client = match ( & config . smtp_username , & config . smtp_password ) {
2018-10-04 00:01:04 +02:00
( Some ( user ) , Some ( pass ) ) = > smtp_client . credentials ( Credentials ::new ( user . clone ( ) , pass . clone ( ) ) ) ,
_ = > smtp_client ,
2018-08-15 17:00:55 +02:00
} ;
2018-09-19 21:45:50 +02:00
smtp_client
2018-08-15 08:32:19 +02:00
. smtp_utf8 ( true )
2018-08-15 17:00:55 +02:00
. connection_reuse ( ConnectionReuseParameters ::NoReuse )
2018-09-19 21:45:50 +02:00
. transport ( )
2018-08-15 08:32:19 +02:00
}
2018-12-19 21:52:53 +01:00
pub fn send_password_hint ( address : & str , hint : Option < String > , config : & MailConfig ) -> EmptyResult {
2018-09-11 13:04:34 +02:00
let ( subject , body ) = if let Some ( hint ) = hint {
2018-12-30 23:34:31 +01:00
(
" 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
) ,
)
2018-09-11 13:04:34 +02:00
} else {
2018-12-30 23:34:31 +01:00
(
" Sorry, you have no password hint... " ,
" Sorry, you have not specified any password hint... \n " . into ( ) ,
)
2018-09-11 13:04:34 +02:00
} ;
2018-08-15 10:17:05 +02:00
2019-01-04 16:32:51 +01:00
send_email ( & address , & subject , & body , & config )
}
pub fn send_invite (
address : & str ,
2019-01-06 05:03:49 +01:00
uuid : & str ,
org_id : Option < String > ,
org_user_id : Option < String > ,
2019-01-04 16:32:51 +01:00
org_name : & str ,
2019-01-06 05:03:49 +01:00
invited_by_email : Option < String > ,
2019-01-04 16:32:51 +01:00
config : & MailConfig ,
) -> EmptyResult {
2019-01-06 05:03:49 +01:00
let claims = generate_invite_claims (
uuid . to_string ( ) ,
String ::from ( address ) ,
org_id . clone ( ) ,
org_user_id . clone ( ) ,
invited_by_email . clone ( ) ,
) ;
let invite_token = encode_jwt ( & claims ) ;
2019-01-04 16:32:51 +01:00
let ( subject , body ) = {
( format! ( " Join {} " , & org_name ) ,
format! (
" <html>
< p > You have been invited to join the < b > { } < / b > organization . < br > < br >
< a href = \ " {}/#/accept-organization/?organizationId={}&organizationUserId={}&email={}&organizationName={}&token={} \" >Click here to join</a></p>
< p > If you do not wish to join this organization , you can safely ignore this email . < / p >
< / html > " ,
2019-01-06 05:03:49 +01:00
org_name , CONFIG . domain , org_id . unwrap_or ( " _ " . to_string ( ) ) , org_user_id . unwrap_or ( " _ " . to_string ( ) ) , address , org_name , invite_token
2019-01-04 16:32:51 +01:00
) )
} ;
send_email ( & address , & subject , & body , & config )
}
pub fn send_invite_accepted (
new_user_email : & str ,
address : & str ,
org_name : & str ,
config : & MailConfig ,
) -> EmptyResult {
let ( subject , body ) = {
( " Invitation accepted " ,
format! (
" <html>
2019-01-05 19:36:08 +01:00
< p > Your invitation for < b > { } < / b > to join < b > { } < / b > was accepted . Please < a href = \ " {} \" >log in</a> to the bitwarden_rs server and confirm them from the organization management page.</p>
< / html > " , new_user_email, org_name, CONFIG.domain))
2019-01-04 16:32:51 +01:00
} ;
send_email ( & address , & subject , & body , & config )
}
pub fn send_invite_confirmed (
address : & str ,
org_name : & str ,
config : & MailConfig ,
) -> EmptyResult {
let ( subject , body ) = {
( format! ( " Invitation to {} confirmed " , org_name ) ,
format! (
" <html>
2019-01-05 19:36:08 +01:00
< p > Your invitation to join < b > { } < / b > was confirmed . It will now appear under the Organizations the next time you < a href = \ " {} \" >log in</a> to the web vault.</p>
< / html > " , org_name, CONFIG.domain))
2019-01-04 16:32:51 +01:00
} ;
send_email ( & address , & subject , & body , & config )
2018-08-15 08:32:19 +02:00
}
2018-12-15 03:54:03 +01:00
2019-01-04 16:32:51 +01:00
fn send_email ( address : & str , subject : & str , body : & str , config : & MailConfig ) -> EmptyResult {
2018-12-15 03:54:03 +01:00
let email = EmailBuilder ::new ( )
2019-01-03 04:19:44 +01:00
. to ( address )
. from ( ( config . smtp_from . clone ( ) , " Bitwarden-rs " ) )
. subject ( subject )
. header ( ( " Content-Type " , " text/html " ) )
. body ( body )
. build ( )
. map_err ( | e | Error ::new ( " Error building email " , e . to_string ( ) ) ) ? ;
2018-12-15 03:54:03 +01:00
2019-01-05 19:36:08 +01:00
mailer ( config )
. send ( email . into ( ) )
. map_err ( | e | Error ::new ( " Error sending email " , e . to_string ( ) ) )
. and ( Ok ( ( ) ) )
2019-01-03 04:19:44 +01:00
}