@ -4,7 +4,7 @@ use chrono::NaiveDateTime;
use percent_encoding ::{ percent_encode , NON_ALPHANUMERIC } ;
use percent_encoding ::{ percent_encode , NON_ALPHANUMERIC } ;
use lettre ::{
use lettre ::{
message ::{ Mailbox, Message , Multi Part} ,
message ::{ Attachment, Body , Mailbox, Message , Multi Part, Single Part} ,
transport ::smtp ::authentication ::{ Credentials , Mechanism as SmtpAuthMechanism } ,
transport ::smtp ::authentication ::{ Credentials , Mechanism as SmtpAuthMechanism } ,
transport ::smtp ::client ::{ Tls , TlsParameters } ,
transport ::smtp ::client ::{ Tls , TlsParameters } ,
transport ::smtp ::extension ::ClientId ,
transport ::smtp ::extension ::ClientId ,
@ -117,7 +117,14 @@ pub async fn send_password_hint(address: &str, hint: Option<String>) -> EmptyRes
"email/pw_hint_none"
"email/pw_hint_none"
} ;
} ;
let ( subject , body_html , body_text ) = get_text ( template_name , json ! ( { "hint" : hint , "url" : CONFIG . domain ( ) } ) ) ? ;
let ( subject , body_html , body_text ) = get_text (
template_name ,
json ! ( {
"url" : CONFIG . domain ( ) ,
"img_src" : CONFIG . _smtp_img_src ( ) ,
"hint" : hint ,
} ) ,
) ? ;
send_email ( address , & subject , body_html , body_text ) . await
send_email ( address , & subject , body_html , body_text ) . await
}
}
@ -130,6 +137,7 @@ pub async fn send_delete_account(address: &str, uuid: &str) -> EmptyResult {
"email/delete_account" ,
"email/delete_account" ,
json ! ( {
json ! ( {
"url" : CONFIG . domain ( ) ,
"url" : CONFIG . domain ( ) ,
"img_src" : CONFIG . _smtp_img_src ( ) ,
"user_id" : uuid ,
"user_id" : uuid ,
"email" : percent_encode ( address . as_bytes ( ) , NON_ALPHANUMERIC ) . to_string ( ) ,
"email" : percent_encode ( address . as_bytes ( ) , NON_ALPHANUMERIC ) . to_string ( ) ,
"token" : delete_token ,
"token" : delete_token ,
@ -147,6 +155,7 @@ pub async fn send_verify_email(address: &str, uuid: &str) -> EmptyResult {
"email/verify_email" ,
"email/verify_email" ,
json ! ( {
json ! ( {
"url" : CONFIG . domain ( ) ,
"url" : CONFIG . domain ( ) ,
"img_src" : CONFIG . _smtp_img_src ( ) ,
"user_id" : uuid ,
"user_id" : uuid ,
"email" : percent_encode ( address . as_bytes ( ) , NON_ALPHANUMERIC ) . to_string ( ) ,
"email" : percent_encode ( address . as_bytes ( ) , NON_ALPHANUMERIC ) . to_string ( ) ,
"token" : verify_email_token ,
"token" : verify_email_token ,
@ -161,6 +170,7 @@ pub async fn send_welcome(address: &str) -> EmptyResult {
"email/welcome" ,
"email/welcome" ,
json ! ( {
json ! ( {
"url" : CONFIG . domain ( ) ,
"url" : CONFIG . domain ( ) ,
"img_src" : CONFIG . _smtp_img_src ( ) ,
} ) ,
} ) ,
) ? ;
) ? ;
@ -175,6 +185,7 @@ pub async fn send_welcome_must_verify(address: &str, uuid: &str) -> EmptyResult
"email/welcome_must_verify" ,
"email/welcome_must_verify" ,
json ! ( {
json ! ( {
"url" : CONFIG . domain ( ) ,
"url" : CONFIG . domain ( ) ,
"img_src" : CONFIG . _smtp_img_src ( ) ,
"user_id" : uuid ,
"user_id" : uuid ,
"token" : verify_email_token ,
"token" : verify_email_token ,
} ) ,
} ) ,
@ -188,6 +199,7 @@ pub async fn send_2fa_removed_from_org(address: &str, org_name: &str) -> EmptyRe
"email/send_2fa_removed_from_org" ,
"email/send_2fa_removed_from_org" ,
json ! ( {
json ! ( {
"url" : CONFIG . domain ( ) ,
"url" : CONFIG . domain ( ) ,
"img_src" : CONFIG . _smtp_img_src ( ) ,
"org_name" : org_name ,
"org_name" : org_name ,
} ) ,
} ) ,
) ? ;
) ? ;
@ -200,6 +212,7 @@ pub async fn send_single_org_removed_from_org(address: &str, org_name: &str) ->
"email/send_single_org_removed_from_org" ,
"email/send_single_org_removed_from_org" ,
json ! ( {
json ! ( {
"url" : CONFIG . domain ( ) ,
"url" : CONFIG . domain ( ) ,
"img_src" : CONFIG . _smtp_img_src ( ) ,
"org_name" : org_name ,
"org_name" : org_name ,
} ) ,
} ) ,
) ? ;
) ? ;
@ -228,6 +241,7 @@ pub async fn send_invite(
"email/send_org_invite" ,
"email/send_org_invite" ,
json ! ( {
json ! ( {
"url" : CONFIG . domain ( ) ,
"url" : CONFIG . domain ( ) ,
"img_src" : CONFIG . _smtp_img_src ( ) ,
"org_id" : org_id . as_deref ( ) . unwrap_or ( "_" ) ,
"org_id" : org_id . as_deref ( ) . unwrap_or ( "_" ) ,
"org_user_id" : org_user_id . as_deref ( ) . unwrap_or ( "_" ) ,
"org_user_id" : org_user_id . as_deref ( ) . unwrap_or ( "_" ) ,
"email" : percent_encode ( address . as_bytes ( ) , NON_ALPHANUMERIC ) . to_string ( ) ,
"email" : percent_encode ( address . as_bytes ( ) , NON_ALPHANUMERIC ) . to_string ( ) ,
@ -260,6 +274,7 @@ pub async fn send_emergency_access_invite(
"email/send_emergency_access_invite" ,
"email/send_emergency_access_invite" ,
json ! ( {
json ! ( {
"url" : CONFIG . domain ( ) ,
"url" : CONFIG . domain ( ) ,
"img_src" : CONFIG . _smtp_img_src ( ) ,
"emer_id" : emer_id . unwrap_or_else ( | | "_" . to_string ( ) ) ,
"emer_id" : emer_id . unwrap_or_else ( | | "_" . to_string ( ) ) ,
"email" : percent_encode ( address . as_bytes ( ) , NON_ALPHANUMERIC ) . to_string ( ) ,
"email" : percent_encode ( address . as_bytes ( ) , NON_ALPHANUMERIC ) . to_string ( ) ,
"grantor_name" : grantor_name ,
"grantor_name" : grantor_name ,
@ -275,6 +290,7 @@ pub async fn send_emergency_access_invite_accepted(address: &str, grantee_email:
"email/emergency_access_invite_accepted" ,
"email/emergency_access_invite_accepted" ,
json ! ( {
json ! ( {
"url" : CONFIG . domain ( ) ,
"url" : CONFIG . domain ( ) ,
"img_src" : CONFIG . _smtp_img_src ( ) ,
"grantee_email" : grantee_email ,
"grantee_email" : grantee_email ,
} ) ,
} ) ,
) ? ;
) ? ;
@ -287,6 +303,7 @@ pub async fn send_emergency_access_invite_confirmed(address: &str, grantor_name:
"email/emergency_access_invite_confirmed" ,
"email/emergency_access_invite_confirmed" ,
json ! ( {
json ! ( {
"url" : CONFIG . domain ( ) ,
"url" : CONFIG . domain ( ) ,
"img_src" : CONFIG . _smtp_img_src ( ) ,
"grantor_name" : grantor_name ,
"grantor_name" : grantor_name ,
} ) ,
} ) ,
) ? ;
) ? ;
@ -299,6 +316,7 @@ pub async fn send_emergency_access_recovery_approved(address: &str, grantor_name
"email/emergency_access_recovery_approved" ,
"email/emergency_access_recovery_approved" ,
json ! ( {
json ! ( {
"url" : CONFIG . domain ( ) ,
"url" : CONFIG . domain ( ) ,
"img_src" : CONFIG . _smtp_img_src ( ) ,
"grantor_name" : grantor_name ,
"grantor_name" : grantor_name ,
} ) ,
} ) ,
) ? ;
) ? ;
@ -316,6 +334,7 @@ pub async fn send_emergency_access_recovery_initiated(
"email/emergency_access_recovery_initiated" ,
"email/emergency_access_recovery_initiated" ,
json ! ( {
json ! ( {
"url" : CONFIG . domain ( ) ,
"url" : CONFIG . domain ( ) ,
"img_src" : CONFIG . _smtp_img_src ( ) ,
"grantee_name" : grantee_name ,
"grantee_name" : grantee_name ,
"atype" : atype ,
"atype" : atype ,
"wait_time_days" : wait_time_days ,
"wait_time_days" : wait_time_days ,
@ -335,6 +354,7 @@ pub async fn send_emergency_access_recovery_reminder(
"email/emergency_access_recovery_reminder" ,
"email/emergency_access_recovery_reminder" ,
json ! ( {
json ! ( {
"url" : CONFIG . domain ( ) ,
"url" : CONFIG . domain ( ) ,
"img_src" : CONFIG . _smtp_img_src ( ) ,
"grantee_name" : grantee_name ,
"grantee_name" : grantee_name ,
"atype" : atype ,
"atype" : atype ,
"days_left" : days_left ,
"days_left" : days_left ,
@ -349,6 +369,7 @@ pub async fn send_emergency_access_recovery_rejected(address: &str, grantor_name
"email/emergency_access_recovery_rejected" ,
"email/emergency_access_recovery_rejected" ,
json ! ( {
json ! ( {
"url" : CONFIG . domain ( ) ,
"url" : CONFIG . domain ( ) ,
"img_src" : CONFIG . _smtp_img_src ( ) ,
"grantor_name" : grantor_name ,
"grantor_name" : grantor_name ,
} ) ,
} ) ,
) ? ;
) ? ;
@ -361,6 +382,7 @@ pub async fn send_emergency_access_recovery_timed_out(address: &str, grantee_nam
"email/emergency_access_recovery_timed_out" ,
"email/emergency_access_recovery_timed_out" ,
json ! ( {
json ! ( {
"url" : CONFIG . domain ( ) ,
"url" : CONFIG . domain ( ) ,
"img_src" : CONFIG . _smtp_img_src ( ) ,
"grantee_name" : grantee_name ,
"grantee_name" : grantee_name ,
"atype" : atype ,
"atype" : atype ,
} ) ,
} ) ,
@ -374,6 +396,7 @@ pub async fn send_invite_accepted(new_user_email: &str, address: &str, org_name:
"email/invite_accepted" ,
"email/invite_accepted" ,
json ! ( {
json ! ( {
"url" : CONFIG . domain ( ) ,
"url" : CONFIG . domain ( ) ,
"img_src" : CONFIG . _smtp_img_src ( ) ,
"email" : new_user_email ,
"email" : new_user_email ,
"org_name" : org_name ,
"org_name" : org_name ,
} ) ,
} ) ,
@ -387,6 +410,7 @@ pub async fn send_invite_confirmed(address: &str, org_name: &str) -> EmptyResult
"email/invite_confirmed" ,
"email/invite_confirmed" ,
json ! ( {
json ! ( {
"url" : CONFIG . domain ( ) ,
"url" : CONFIG . domain ( ) ,
"img_src" : CONFIG . _smtp_img_src ( ) ,
"org_name" : org_name ,
"org_name" : org_name ,
} ) ,
} ) ,
) ? ;
) ? ;
@ -403,6 +427,7 @@ pub async fn send_new_device_logged_in(address: &str, ip: &str, dt: &NaiveDateTi
"email/new_device_logged_in" ,
"email/new_device_logged_in" ,
json ! ( {
json ! ( {
"url" : CONFIG . domain ( ) ,
"url" : CONFIG . domain ( ) ,
"img_src" : CONFIG . _smtp_img_src ( ) ,
"ip" : ip ,
"ip" : ip ,
"device" : device ,
"device" : device ,
"datetime" : crate ::util ::format_naive_datetime_local ( dt , fmt ) ,
"datetime" : crate ::util ::format_naive_datetime_local ( dt , fmt ) ,
@ -421,6 +446,7 @@ pub async fn send_incomplete_2fa_login(address: &str, ip: &str, dt: &NaiveDateTi
"email/incomplete_2fa_login" ,
"email/incomplete_2fa_login" ,
json ! ( {
json ! ( {
"url" : CONFIG . domain ( ) ,
"url" : CONFIG . domain ( ) ,
"img_src" : CONFIG . _smtp_img_src ( ) ,
"ip" : ip ,
"ip" : ip ,
"device" : device ,
"device" : device ,
"datetime" : crate ::util ::format_naive_datetime_local ( dt , fmt ) ,
"datetime" : crate ::util ::format_naive_datetime_local ( dt , fmt ) ,
@ -436,6 +462,7 @@ pub async fn send_token(address: &str, token: &str) -> EmptyResult {
"email/twofactor_email" ,
"email/twofactor_email" ,
json ! ( {
json ! ( {
"url" : CONFIG . domain ( ) ,
"url" : CONFIG . domain ( ) ,
"img_src" : CONFIG . _smtp_img_src ( ) ,
"token" : token ,
"token" : token ,
} ) ,
} ) ,
) ? ;
) ? ;
@ -448,6 +475,7 @@ pub async fn send_change_email(address: &str, token: &str) -> EmptyResult {
"email/change_email" ,
"email/change_email" ,
json ! ( {
json ! ( {
"url" : CONFIG . domain ( ) ,
"url" : CONFIG . domain ( ) ,
"img_src" : CONFIG . _smtp_img_src ( ) ,
"token" : token ,
"token" : token ,
} ) ,
} ) ,
) ? ;
) ? ;
@ -460,6 +488,7 @@ pub async fn send_test(address: &str) -> EmptyResult {
"email/smtp_test" ,
"email/smtp_test" ,
json ! ( {
json ! ( {
"url" : CONFIG . domain ( ) ,
"url" : CONFIG . domain ( ) ,
"img_src" : CONFIG . _smtp_img_src ( ) ,
} ) ,
} ) ,
) ? ;
) ? ;
@ -468,12 +497,32 @@ pub async fn send_test(address: &str) -> EmptyResult {
async fn send_email ( address : & str , subject : & str , body_html : String , body_text : String ) -> EmptyResult {
async fn send_email ( address : & str , subject : & str , body_html : String , body_text : String ) -> EmptyResult {
let smtp_from = & CONFIG . smtp_from ( ) ;
let smtp_from = & CONFIG . smtp_from ( ) ;
let body = if CONFIG . smtp_embed_images ( ) {
let logo_gray_body = Body ::new ( crate ::api ::static_files ( "logo-gray.png" . to_string ( ) ) . unwrap ( ) . 1. to_vec ( ) ) ;
let mail_github_body = Body ::new ( crate ::api ::static_files ( "mail-github.png" . to_string ( ) ) . unwrap ( ) . 1. to_vec ( ) ) ;
MultiPart ::alternative ( ) . singlepart ( SinglePart ::plain ( body_text ) ) . multipart (
MultiPart ::related ( )
. singlepart ( SinglePart ::html ( body_html ) )
. singlepart (
Attachment ::new_inline ( String ::from ( "logo-gray.png" ) )
. body ( logo_gray_body , "image/png" . parse ( ) . unwrap ( ) ) ,
)
. singlepart (
Attachment ::new_inline ( String ::from ( "mail-github.png" ) )
. body ( mail_github_body , "image/png" . parse ( ) . unwrap ( ) ) ,
) ,
)
} else {
MultiPart ::alternative_plain_html ( body_text , body_html )
} ;
let email = Message ::builder ( )
let email = Message ::builder ( )
. message_id ( Some ( format! ( "<{}@{}>" , crate ::util ::get_uuid ( ) , smtp_from . split ( '@' ) . collect ::< Vec < & str > > ( ) [ 1 ] ) ) )
. message_id ( Some ( format! ( "<{}@{}>" , crate ::util ::get_uuid ( ) , smtp_from . split ( '@' ) . collect ::< Vec < & str > > ( ) [ 1 ] ) ) )
. to ( Mailbox ::new ( None , Address ::from_str ( address ) ? ) )
. to ( Mailbox ::new ( None , Address ::from_str ( address ) ? ) )
. from ( Mailbox ::new ( Some ( CONFIG . smtp_from_name ( ) ) , Address ::from_str ( smtp_from ) ? ) )
. from ( Mailbox ::new ( Some ( CONFIG . smtp_from_name ( ) ) , Address ::from_str ( smtp_from ) ? ) )
. subject ( subject )
. subject ( subject )
. multipart ( MultiPart ::alternative_plain_html ( body_text , body_html ) ) ? ;
. multipart ( body) ? ;
match mailer ( ) . send ( email ) . await {
match mailer ( ) . send ( email ) . await {
Ok ( _ ) = > Ok ( ( ) ) ,
Ok ( _ ) = > Ok ( ( ) ) ,