diff --git a/Cargo.toml b/Cargo.toml index 7cd62daac..0db069ab1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,8 @@ libsqlite3-sys = { version = "0.14.0", features = ["bundled"] } reqwest = "0.9.15" num-derive = "0.2.5" num-traits = "0.2.6" +native-tls = "0.2.3" +lettre = "0.9.0" [dev-dependencies] tempfile = "3.0.7" @@ -37,3 +39,7 @@ name = "simple" name = "repl" path = "examples/repl/main.rs" + +[features] +default = [] +vendored = ["native-tls/vendored"] \ No newline at end of file diff --git a/src/dc_chat.rs b/src/dc_chat.rs index b20361c4c..b0fb5ab15 100644 --- a/src/dc_chat.rs +++ b/src/dc_chat.rs @@ -513,7 +513,6 @@ unsafe fn prepare_msg_common<'a>( match current_block { 17281240262373992796 => { dc_unarchive_chat(context, chat_id); - context.smtp.lock().unwrap().log_connect_errors = 1i32; chat = dc_chat_new(context); if 0 != dc_chat_load_from_db(chat, chat_id) { if (*msg).state != 18i32 { @@ -2134,7 +2133,6 @@ pub unsafe fn dc_forward_msgs( let mut original_param: *mut dc_param_t = dc_param_new(); if !(msg_ids.is_null() || msg_cnt <= 0i32 || chat_id <= 9i32 as libc::c_uint) { dc_unarchive_chat(context, chat_id); - context.smtp.lock().unwrap().log_connect_errors = 1i32; if !(0 == dc_chat_load_from_db(chat, chat_id)) { curr_timestamp = dc_create_smeared_timestamps(context, msg_cnt); idsstr = dc_arr_to_string(msg_ids, msg_cnt); diff --git a/src/dc_configure.rs b/src/dc_configure.rs index 351b93a35..fb61b9d5e 100644 --- a/src/dc_configure.rs +++ b/src/dc_configure.rs @@ -9,7 +9,6 @@ use crate::dc_log::*; use crate::dc_loginparam::*; use crate::dc_oauth2::*; use crate::dc_saxparser::*; -use crate::dc_smtp::*; use crate::dc_sqlite3::*; use crate::dc_strencode::*; use crate::dc_tools::*; @@ -162,8 +161,7 @@ pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &dc_context_t, _job: *mut .lock() .unwrap(), ); - dc_smtp_disconnect(&mut context.smtp.clone().lock().unwrap()); - context.smtp.clone().lock().unwrap().log_connect_errors = 1i32; + context.smtp.clone().lock().unwrap().disconnect(); context.inbox.clone().lock().unwrap().log_connect_errors = 1i32; context .sentbox_thread @@ -1007,15 +1005,13 @@ pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &dc_context_t, _job: *mut 0i32 as uintptr_t, ); /* try to connect to SMTP - if we did not got an autoconfig, the first try was SSL-465 and we do a second try with STARTTLS-587 */ - if 0 == dc_smtp_connect( - context, - &mut context - .smtp - .clone() - .lock() - .unwrap(), - param, - ) { + if 0 == context + .smtp + .clone() + .lock() + .unwrap() + .connect(context, param) + { if !param_autoconfig.is_null() { current_block = 2927484062889439186; @@ -1056,15 +1052,13 @@ pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &dc_context_t, _job: *mut r_3, ); free(r_3 as *mut libc::c_void); - if 0 == dc_smtp_connect( - context, - &mut context - .smtp - .clone() - .lock() - .unwrap(), - param, - ) { + if 0 == context + .smtp + .clone() + .lock() + .unwrap() + .connect(context, param) + { if s.shall_stop_ongoing { current_block = 2927484062889439186; @@ -1113,15 +1107,15 @@ pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &dc_context_t, _job: *mut free(r_4 as *mut libc::c_void); - if 0 == dc_smtp_connect( - context, - &mut context - .smtp - .clone() - .lock() - .unwrap(), - param, - ) { + if 0 == context + .smtp + .clone() + .lock() + .unwrap() + .connect( + context, param, + ) + { current_block = 2927484062889439186; @@ -1312,7 +1306,7 @@ pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &dc_context_t, _job: *mut dc_imap_disconnect(context, &mut context.inbox.clone().lock().unwrap()); } if 0 != smtp_connected_here { - dc_smtp_disconnect(&mut context.smtp.clone().lock().unwrap()); + context.smtp.clone().lock().unwrap().disconnect(); } dc_loginparam_unref(param); dc_loginparam_unref(param_autoconfig); diff --git a/src/dc_context.rs b/src/dc_context.rs index a0d30b132..71c4fd886 100644 --- a/src/dc_context.rs +++ b/src/dc_context.rs @@ -34,7 +34,7 @@ pub struct dc_context_t { pub probe_imap_network: Arc>, pub sentbox_thread: Arc>, pub mvbox_thread: Arc>, - pub smtp: Arc>, + pub smtp: Arc>, pub smtp_state: Arc<(Mutex, Condvar)>, pub oauth2_critical: Arc>, pub cb: dc_callback_t, @@ -127,7 +127,7 @@ pub fn dc_context_new( os_name: unsafe { dc_strdup_keep_null(os_name) }, running_state: Arc::new(RwLock::new(Default::default())), sql: Arc::new(Mutex::new(dc_sqlite3_new())), - smtp: Arc::new(Mutex::new(dc_smtp_new())), + smtp: Arc::new(Mutex::new(Smtp::new())), smtp_state: Arc::new((Mutex::new(Default::default()), Condvar::new())), oauth2_critical: Arc::new(Mutex::new(())), bob: Arc::new(RwLock::new(Default::default())), @@ -291,7 +291,7 @@ pub unsafe fn dc_context_unref(context: &mut dc_context_t) { .lock() .unwrap(), ); - dc_smtp_unref(&mut context.smtp.clone().lock().unwrap()); + dc_sqlite3_unref(context, &mut context.sql.clone().lock().unwrap()); dc_jobthread_exit(&mut context.sentbox_thread.clone().lock().unwrap()); @@ -326,7 +326,7 @@ pub unsafe fn dc_close(context: &mut dc_context_t) { .lock() .unwrap(), ); - dc_smtp_disconnect(&mut context.smtp.clone().lock().unwrap()); + context.smtp.clone().lock().unwrap().disconnect(); if 0 != dc_sqlite3_is_open(&mut context.sql.clone().lock().unwrap()) { dc_sqlite3_close(context, &mut context.sql.clone().lock().unwrap()); diff --git a/src/dc_job.rs b/src/dc_job.rs index 008920d44..eb531139c 100644 --- a/src/dc_job.rs +++ b/src/dc_job.rs @@ -16,7 +16,6 @@ use crate::dc_loginparam::*; use crate::dc_mimefactory::*; use crate::dc_msg::*; use crate::dc_param::*; -use crate::dc_smtp::*; use crate::dc_sqlite3::*; use crate::dc_tools::*; use crate::types::*; @@ -322,10 +321,9 @@ unsafe extern "C" fn dc_job_do_DC_JOB_SEND(mut context: &dc_context_t, mut job: let mut buf: *mut libc::c_void = 0 as *mut libc::c_void; let mut buf_bytes: size_t = 0i32 as size_t; let mut recipients: *mut libc::c_char = 0 as *mut libc::c_char; - let mut recipients_list: *mut clist = 0 as *mut clist; let mut stmt: *mut sqlite3_stmt = 0 as *mut sqlite3_stmt; /* connect to SMTP server, if not yet done */ - if 0 == dc_smtp_is_connected(&context.smtp.clone().lock().unwrap()) { + if !context.smtp.clone().lock().unwrap().is_connected() { let mut loginparam: *mut dc_loginparam_t = dc_loginparam_new(); dc_loginparam_read( context, @@ -333,11 +331,12 @@ unsafe extern "C" fn dc_job_do_DC_JOB_SEND(mut context: &dc_context_t, mut job: &mut context.sql.clone().lock().unwrap(), b"configured_\x00" as *const u8 as *const libc::c_char, ); - let mut connected: libc::c_int = dc_smtp_connect( - context, - &mut context.smtp.clone().lock().unwrap(), - loginparam, - ); + let mut connected = context + .smtp + .clone() + .lock() + .unwrap() + .connect(context, loginparam); dc_loginparam_unref(loginparam); if 0 == connected { dc_job_try_again_later(job, 3i32, 0 as *const libc::c_char); @@ -368,10 +367,18 @@ unsafe extern "C" fn dc_job_do_DC_JOB_SEND(mut context: &dc_context_t, mut job: (*job).job_id, ); } else { - recipients_list = dc_str_to_clist( - recipients, - b"\x1e\x00" as *const u8 as *const libc::c_char, - ); + let recipients_list = std::ffi::CStr::from_ptr(recipients) + .to_str() + .unwrap() + .split("\x1e") + .filter_map(|addr| match lettre::EmailAddress::new(addr.to_string()) { + Ok(addr) => Some(addr), + Err(err) => { + eprintln!("WARNING: invalid recipient: {} {:?}", addr, err); + None + } + }) + .collect::>(); /* if there is a msg-id and it does not exist in the db, cancel sending. this happends if dc_delete_msgs() was called before the generated mime was sent out */ @@ -396,33 +403,19 @@ unsafe extern "C" fn dc_job_do_DC_JOB_SEND(mut context: &dc_context_t, mut job: 14216916617354591294 => {} _ => { /* send message */ - if 0 == dc_smtp_send_msg( + let body = + std::slice::from_raw_parts(buf as *const u8, buf_bytes).to_vec(); + if 0 == context.smtp.clone().lock().unwrap().send( context, - &mut context.smtp.clone().lock().unwrap(), recipients_list, - buf as *const libc::c_char, - buf_bytes, + body, ) { - if 0 != (*job).foreign_id - && (MAILSMTP_ERROR_EXCEED_STORAGE_ALLOCATION as libc::c_int - == (*&mut context.smtp.clone().lock().unwrap()).error_etpan - || MAILSMTP_ERROR_INSUFFICIENT_SYSTEM_STORAGE - as libc::c_int - == (*context.smtp.clone().lock().unwrap()).error_etpan) - { - dc_set_msg_failed( - context, - (*job).foreign_id, - (*&mut context.smtp.clone().lock().unwrap()).error, - ); - } else { - dc_smtp_disconnect(&mut context.smtp.clone().lock().unwrap()); - dc_job_try_again_later( - job, - -1i32, - (*&mut context.smtp.clone().lock().unwrap()).error, - ); - } + context.smtp.clone().lock().unwrap().disconnect(); + dc_job_try_again_later( + job, + -1i32, + (*&mut context.smtp.clone().lock().unwrap()).error, + ); } else { dc_delete_file(context, filename); if 0 != (*job).foreign_id { @@ -455,10 +448,6 @@ unsafe extern "C" fn dc_job_do_DC_JOB_SEND(mut context: &dc_context_t, mut job: _ => {} } sqlite3_finalize(stmt); - if !recipients_list.is_null() { - clist_free_content(recipients_list); - clist_free(recipients_list); - } free(recipients as *mut libc::c_void); free(buf); free(filename as *mut libc::c_void); diff --git a/src/dc_smtp.rs b/src/dc_smtp.rs index 7f9fc793d..f4538d5c8 100644 --- a/src/dc_smtp.rs +++ b/src/dc_smtp.rs @@ -1,526 +1,203 @@ +use std::ffi::{CStr, CString}; + +use lettre::smtp::client::net::*; +use lettre::*; use libc; +use native_tls::TlsConnector; use crate::constants::Event; +use crate::constants::*; use crate::dc_context::dc_context_t; use crate::dc_log::*; use crate::dc_loginparam::*; use crate::dc_oauth2::*; -use crate::dc_tools::*; -use crate::types::*; use crate::x::*; -#[derive(Copy, Clone)] -#[repr(C)] -pub struct dc_smtp_t { - pub etpan: *mut mailsmtp, - pub from: *mut libc::c_char, - pub esmtp: libc::c_int, - pub log_connect_errors: libc::c_int, +pub struct Smtp { + transport: Option, + transport_connected: bool, + /// Email address we are sending from. + from: Option, pub error: *mut libc::c_char, - pub error_etpan: libc::c_int, } -pub fn dc_smtp_new() -> dc_smtp_t { - dc_smtp_t { - etpan: std::ptr::null_mut(), - from: std::ptr::null_mut(), - esmtp: 0, - log_connect_errors: 1, - error: std::ptr::null_mut(), - error_etpan: 0, - } -} - -pub unsafe fn dc_smtp_unref(smtp: &mut dc_smtp_t) { - dc_smtp_disconnect(smtp); - free(smtp.from as *mut libc::c_void); - free(smtp.error as *mut libc::c_void); -} - -pub unsafe fn dc_smtp_disconnect(smtp: &mut dc_smtp_t) { - if !smtp.etpan.is_null() { - mailsmtp_free(smtp.etpan); - smtp.etpan = 0 as *mut mailsmtp; - } -} - -pub unsafe fn dc_smtp_is_connected(smtp: &dc_smtp_t) -> libc::c_int { - if !smtp.etpan.is_null() { - 1 - } else { - 0 - } -} - -pub unsafe fn dc_smtp_connect( - context: &dc_context_t, - smtp: &mut dc_smtp_t, - lp: *const dc_loginparam_t, -) -> libc::c_int { - let mut current_block: u64; - let mut success: libc::c_int = 0; - let mut r: libc::c_int = 0; - let mut try_esmtp: libc::c_int = 0; - if lp.is_null() { - return 0; - } - - if !smtp.etpan.is_null() { - dc_log_warning( - context, - 0, - b"SMTP already connected.\x00" as *const u8 as *const libc::c_char, - ); - success = 1; - } else if (*lp).addr.is_null() || (*lp).send_server.is_null() || (*lp).send_port == 0 { - dc_log_event_seq( - context, - Event::ERROR_NETWORK, - &mut smtp.log_connect_errors as *mut libc::c_int, - b"SMTP bad parameters.\x00" as *const u8 as *const libc::c_char, - ); - } else { - free(smtp.from as *mut libc::c_void); - smtp.from = dc_strdup((*lp).addr); - smtp.etpan = mailsmtp_new(0 as size_t, None); - if smtp.etpan.is_null() { - dc_log_error( - context, - 0, - b"SMTP-object creation failed.\x00" as *const u8 as *const libc::c_char, - ); - } else { - mailsmtp_set_timeout(smtp.etpan, 10 as time_t); - mailsmtp_set_progress_callback( - smtp.etpan, - Some(body_progress), - smtp as *mut _ as *mut libc::c_void, - ); - /* connect to SMTP server */ - if 0 != (*lp).server_flags & (0x10000 | 0x40000) { - r = mailsmtp_socket_connect( - smtp.etpan, - (*lp).send_server, - (*lp).send_port as uint16_t, - ); - if r != MAILSMTP_NO_ERROR as libc::c_int { - dc_log_event_seq( - context, - Event::ERROR_NETWORK, - &mut smtp.log_connect_errors as *mut libc::c_int, - b"SMTP-Socket connection to %s:%i failed (%s)\x00" as *const u8 - as *const libc::c_char, - (*lp).send_server, - (*lp).send_port as libc::c_int, - mailsmtp_strerror(r), - ); - current_block = 12512295087047028901; - } else { - current_block = 10043043949733653460; - } - } else { - r = mailsmtp_ssl_connect( - smtp.etpan, - (*lp).send_server, - (*lp).send_port as uint16_t, - ); - if r != MAILSMTP_NO_ERROR as libc::c_int { - dc_log_event_seq( - context, - Event::ERROR_NETWORK, - &mut smtp.log_connect_errors as *mut libc::c_int, - b"SMTP-SSL connection to %s:%i failed (%s)\x00" as *const u8 - as *const libc::c_char, - (*lp).send_server, - (*lp).send_port as libc::c_int, - mailsmtp_strerror(r), - ); - current_block = 12512295087047028901; - } else { - current_block = 10043043949733653460; - } - } - match current_block { - 12512295087047028901 => {} - _ => { - try_esmtp = 1; - smtp.esmtp = 0; - if 0 != try_esmtp && { - r = mailesmtp_ehlo(smtp.etpan); - r == MAILSMTP_NO_ERROR as libc::c_int - } { - smtp.esmtp = 1 - } else if 0 == try_esmtp || r == MAILSMTP_ERROR_NOT_IMPLEMENTED as libc::c_int { - r = mailsmtp_helo(smtp.etpan) - } - if r != MAILSMTP_NO_ERROR as libc::c_int { - dc_log_event_seq( - context, - Event::ERROR_NETWORK, - &mut smtp.log_connect_errors as *mut libc::c_int, - b"SMTP-helo failed (%s)\x00" as *const u8 as *const libc::c_char, - mailsmtp_strerror(r), - ); - } else { - if 0 != (*lp).server_flags & 0x10000 { - r = mailsmtp_socket_starttls(smtp.etpan); - if r != MAILSMTP_NO_ERROR as libc::c_int { - dc_log_event_seq( - context, - Event::ERROR_NETWORK, - &mut smtp.log_connect_errors as *mut libc::c_int, - b"SMTP-STARTTLS failed (%s)\x00" as *const u8 - as *const libc::c_char, - mailsmtp_strerror(r), - ); - current_block = 12512295087047028901; - } else { - smtp.esmtp = 0; - if 0 != try_esmtp && { - r = mailesmtp_ehlo(smtp.etpan); - r == MAILSMTP_NO_ERROR as libc::c_int - } { - smtp.esmtp = 1 - } else if 0 == try_esmtp - || r == MAILSMTP_ERROR_NOT_IMPLEMENTED as libc::c_int - { - r = mailsmtp_helo(smtp.etpan) - } - if r != MAILSMTP_NO_ERROR as libc::c_int { - dc_log_event_seq( - context, - Event::ERROR_NETWORK, - &mut smtp.log_connect_errors as *mut libc::c_int, - b"SMTP-helo failed (%s)\x00" as *const u8 - as *const libc::c_char, - mailsmtp_strerror(r), - ); - current_block = 12512295087047028901; - } else { - dc_log_info( - context, - 0, - b"SMTP-server %s:%i STARTTLS-connected.\x00" as *const u8 - as *const libc::c_char, - (*lp).send_server, - (*lp).send_port as libc::c_int, - ); - current_block = 5892776923941496671; - } - } - } else { - if 0 != (*lp).server_flags & 0x40000 { - dc_log_info( - context, - 0, - b"SMTP-server %s:%i connected.\x00" as *const u8 - as *const libc::c_char, - (*lp).send_server, - (*lp).send_port as libc::c_int, - ); - } else { - dc_log_info( - context, - 0, - b"SMTP-server %s:%i SSL-connected.\x00" as *const u8 - as *const libc::c_char, - (*lp).send_server, - (*lp).send_port as libc::c_int, - ); - } - current_block = 5892776923941496671; - } - match current_block { - 12512295087047028901 => {} - _ => { - if !(*lp).send_user.is_null() { - if 0 != (*lp).server_flags & 0x2 { - dc_log_info( - context, - 0, - b"SMTP-OAuth2 connect...\x00" as *const u8 - as *const libc::c_char, - ); - let mut access_token: *mut libc::c_char = - dc_get_oauth2_access_token( - context, - (*lp).addr, - (*lp).send_pw, - 0, - ); - r = mailsmtp_oauth2_authenticate( - smtp.etpan, - (*lp).send_user, - access_token, - ); - if r != MAILSMTP_NO_ERROR as libc::c_int { - free(access_token as *mut libc::c_void); - access_token = dc_get_oauth2_access_token( - context, - (*lp).addr, - (*lp).send_pw, - 0x1, - ); - r = mailsmtp_oauth2_authenticate( - smtp.etpan, - (*lp).send_user, - access_token, - ) - } - free(access_token as *mut libc::c_void); - current_block = 15462640364611497761; - } else { - r = mailsmtp_auth( - smtp.etpan, - (*lp).send_user, - (*lp).send_pw, - ); - if r != MAILSMTP_NO_ERROR as libc::c_int { - /* - * There are some Mailservers which do not correclty implement PLAIN auth (hMail) - * So here we try a workaround. See https://github.com/deltachat/deltachat-android/issues/67 - */ - if 0 != (*smtp.etpan).auth - & MAILSMTP_AUTH_PLAIN as libc::c_int - { - dc_log_info( - context, - 0, - b"Trying SMTP-Login workaround \"%s\"...\x00" - as *const u8 - as *const libc::c_char, - (*lp).send_user, - ); - let mut err: libc::c_int = 0; - let mut hostname: [libc::c_char; 513] = [0; 513]; - err = gethostname( - hostname.as_mut_ptr(), - ::std::mem::size_of::<[libc::c_char; 513]>(), - ); - if err < 0 { - dc_log_error( - context, - 0, - b"SMTP-Login: Cannot get hostname.\x00" - as *const u8 - as *const libc::c_char, - ); - current_block = 12512295087047028901; - } else { - r = mailesmtp_auth_sasl( - smtp.etpan, - b"PLAIN\x00" as *const u8 - as *const libc::c_char, - hostname.as_mut_ptr(), - 0 as *const libc::c_char, - 0 as *const libc::c_char, - 0 as *const libc::c_char, - (*lp).send_user, - (*lp).send_pw, - 0 as *const libc::c_char, - ); - current_block = 15462640364611497761; - } - } else { - current_block = 15462640364611497761; - } - } else { - current_block = 15462640364611497761; - } - } - match current_block { - 12512295087047028901 => {} - _ => { - if r != MAILSMTP_NO_ERROR as libc::c_int { - dc_log_event_seq( - context, - Event::ERROR_NETWORK, - &mut smtp.log_connect_errors - as *mut libc::c_int, - b"SMTP-login failed for user %s (%s)\x00" - as *const u8 - as *const libc::c_char, - (*lp).send_user, - mailsmtp_strerror(r), - ); - current_block = 12512295087047028901; - } else { - dc_log_event( - context, - Event::SMTP_CONNECTED, - 0, - b"SMTP-login as %s ok.\x00" as *const u8 - as *const libc::c_char, - (*lp).send_user, - ); - current_block = 3736434875406665187; - } - } - } - } else { - current_block = 3736434875406665187; - } - match current_block { - 12512295087047028901 => {} - _ => success = 1, - } - } - } - } - } - } +impl Smtp { + /// Create a new Smtp instances. + pub fn new() -> Self { + Smtp { + transport: None, + transport_connected: false, + from: None, + error: std::ptr::null_mut(), } } - if 0 == success { - if !smtp.etpan.is_null() { - mailsmtp_free(smtp.etpan); - smtp.etpan = 0 as *mut mailsmtp + + /// Disconnect the SMTP transport and drop it entirely. + pub fn disconnect(&mut self) { + if self.transport.is_none() || !self.transport_connected { + return; } + + let mut transport = self.transport.take().unwrap(); + transport.close(); + self.transport_connected = false; } - success -} -unsafe extern "C" fn body_progress( - _current: size_t, - _maximum: size_t, - _user_data: *mut libc::c_void, -) { -} + /// Check if a connection already exists. + pub fn is_connected(&self) -> bool { + self.transport.is_some() + } -pub unsafe fn dc_smtp_send_msg( - context: &dc_context_t, - smtp: &mut dc_smtp_t, - recipients: *const clist, - data_not_terminated: *const libc::c_char, - data_bytes: size_t, -) -> libc::c_int { - let mut current_block: u64; - let mut success: libc::c_int = 0; - let mut r: libc::c_int = 0; - let mut iter: *mut clistiter = 0 as *mut clistiter; - if recipients.is_null() - || (*recipients).count == 0 - || data_not_terminated.is_null() - || data_bytes == 0 - { - success = 1 - } else if !smtp.etpan.is_null() { - // set source - // the `etPanSMTPTest` is the ENVID from RFC 3461 (SMTP DSNs), we should probably replace it by a random value - r = if 0 != smtp.esmtp { - mailesmtp_mail( - smtp.etpan, - smtp.from, - 1, - b"etPanSMTPTest\x00" as *const u8 as *const libc::c_char, - ) - } else { - mailsmtp_mail(smtp.etpan, smtp.from) + /// Connect using the provided login params + pub fn connect(&mut self, context: &dc_context_t, lp: *const dc_loginparam_t) -> usize { + if lp.is_null() { + return 0; + } + + if self.is_connected() { + unsafe { + dc_log_warning( + context, + 0, + b"SMTP already connected.\x00" as *const u8 as *const libc::c_char, + ) + }; + + return 1; + } + + // Safe because we checked for null pointer above. + let lp = unsafe { *lp }; + + if lp.addr.is_null() || lp.send_server.is_null() || lp.send_port == 0 { + unsafe { + dc_log_event( + context, + Event::ERROR_NETWORK, + 0, + b"SMTP bad parameters.\x00" as *const u8 as *const libc::c_char, + ); + } + } + + let raw_addr = unsafe { + CStr::from_ptr(lp.addr) + .to_str() + .expect("invalid from address") + .to_string() }; - if r != MAILSMTP_NO_ERROR as libc::c_int { - log_error( - context, - smtp, - b"SMTP failed to start message\x00" as *const u8 as *const libc::c_char, - r, - ); + self.from = if let Ok(addr) = EmailAddress::new(raw_addr) { + Some(addr) } else { - // set recipients - // if the recipient is on the same server, this may fail at once. - // TODO: question is what to do if one recipient in a group fails - iter = (*recipients).first; - loop { - if iter.is_null() { - current_block = 12039483399334584727; - break; - } - let mut rcpt: *const libc::c_char = (if !iter.is_null() { - (*iter).data - } else { - 0 as *mut libc::c_void - }) as *const libc::c_char; - r = if 0 != smtp.esmtp { - mailesmtp_rcpt(smtp.etpan, rcpt, 2 | 4, 0 as *const libc::c_char) - } else { - mailsmtp_rcpt(smtp.etpan, rcpt) - }; - if r != MAILSMTP_NO_ERROR as libc::c_int { - log_error( - context, - smtp, - b"SMTP failed to add recipient\x00" as *const u8 as *const libc::c_char, - r, - ); - current_block = 5498835644851925448; - break; - } else { - iter = if !iter.is_null() { - (*iter).next - } else { - 0 as *mut clistcell_s - } - } - } - match current_block { - 5498835644851925448 => {} - _ => { - // message - r = mailsmtp_data(smtp.etpan); - if r != MAILSMTP_NO_ERROR as libc::c_int { - log_error( - context, - smtp, - b"SMTP failed to set data\x00" as *const u8 as *const libc::c_char, - r, - ); - } else { - r = mailsmtp_data_message(smtp.etpan, data_not_terminated, data_bytes); - if r != MAILSMTP_NO_ERROR as libc::c_int { - log_error( - context, - smtp, - b"SMTP failed to send message\x00" as *const u8 - as *const libc::c_char, - r, - ); - } else { - dc_log_event( - context, - Event::SMTP_MESSAGE_SENT, - 0, - b"Message was sent to SMTP server\x00" as *const u8 - as *const libc::c_char, - ); - success = 1; - } - } - } - } + None + }; + + if self.from.is_none() { + // TODO: print error + return 0; } + + let domain = unsafe { + CStr::from_ptr(lp.send_server) + .to_str() + .expect("invalid send server") + }; + let port = lp.send_port as u16; + + let mut tls_builder = TlsConnector::builder(); + tls_builder.min_protocol_version(Some(DEFAULT_TLS_PROTOCOLS[0])); + + let tls_parameters = + ClientTlsParameters::new(domain.to_string(), tls_builder.build().unwrap()); + + let creds = if 0 != lp.server_flags & (DC_LP_AUTH_OAUTH2 as i32) { + // oauth2 + + let mut access_token = + unsafe { dc_get_oauth2_access_token(context, lp.addr, lp.send_pw, 0i32) }; + if access_token.is_null() { + return 0; + } + + let user = unsafe { CStr::from_ptr(lp.send_user).to_str().unwrap().to_string() }; + let token = unsafe { CStr::from_ptr(access_token).to_str().unwrap().to_string() }; + unsafe { free(access_token as *mut libc::c_void) }; + + lettre::smtp::authentication::Credentials::new(user, token) + } else { + // plain + let user = unsafe { CStr::from_ptr(lp.send_user).to_str().unwrap().to_string() }; + let pw = unsafe { CStr::from_ptr(lp.send_pw).to_str().unwrap().to_string() }; + lettre::smtp::authentication::Credentials::new(user, pw) + }; + + let security = if 0 + != lp.server_flags & (DC_LP_SMTP_SOCKET_STARTTLS | DC_LP_SMTP_SOCKET_PLAIN) as i32 + { + lettre::smtp::ClientSecurity::Opportunistic(tls_parameters) + } else { + lettre::smtp::ClientSecurity::Wrapper(tls_parameters) + }; + + let client = lettre::smtp::SmtpClient::new((domain, port), security) + .expect("failed to construct stmp client") + // .smtp_utf8(true) + .credentials(creds) + .connection_reuse(lettre::smtp::ConnectionReuseParameters::ReuseUnlimited); + + self.transport = Some(client.transport()); + + 1 } - success -} + pub fn send<'a>( + &mut self, + context: &dc_context_t, + recipients: Vec, + body: Vec, + ) -> usize { + if let Some(ref mut transport) = self.transport { + let envelope = Envelope::new(self.from.clone(), recipients).expect("invalid envelope"); + let mail = SendableEmail::new( + envelope, + "mail-id".into(), // TODO: random id + body, + ); -unsafe fn log_error( - context: &dc_context_t, - smtp: &mut dc_smtp_t, - what_failed: *const libc::c_char, - r: libc::c_int, -) { - let mut error_msg: *mut libc::c_char = dc_mprintf( - b"%s: %s: %s\x00" as *const u8 as *const libc::c_char, - what_failed, - mailsmtp_strerror(r), - (*smtp.etpan).response, - ); - dc_log_warning( - context, - 0, - b"%s\x00" as *const u8 as *const libc::c_char, - error_msg, - ); - free(smtp.error as *mut libc::c_void); - smtp.error = error_msg; - smtp.error_etpan = r; + match transport.send(mail) { + Ok(_) => { + unsafe { + dc_log_event( + context, + Event::SMTP_MESSAGE_SENT, + 0, + b"Message was sent to SMTP server\x00" as *const u8 + as *const libc::c_char, + ); + } + self.transport_connected = true; + 1 + } + Err(err) => { + let error_msg = format!("SMTP failed to send message: {:?}", err); + let msg = CString::new(error_msg).unwrap(); + self.error = unsafe { libc::strdup(msg.as_ptr()) }; + + unsafe { + dc_log_warning( + context, + 0, + b"%s\x00" as *const u8 as *const libc::c_char, + msg, + ); + } + + 0 + } + } + } else { + // TODO: log error + 0 + } + } }