the basics work

This commit is contained in:
dignifiedquire
2020-03-18 15:11:36 +01:00
parent 94c6a01420
commit f85b14a7f7
8 changed files with 504 additions and 533 deletions

View File

@@ -1,7 +1,5 @@
extern crate deltachat; extern crate deltachat;
use async_std::task;
use std::time; use std::time;
use tempfile::tempdir; use tempfile::tempdir;
@@ -41,13 +39,9 @@ async fn main() {
println!("info: {:#?}", info); println!("info: {:#?}", info);
let ctx1 = ctx.clone(); let ctx1 = ctx.clone();
task::spawn(async move { std::thread::spawn(move || loop {
loop {
if let Ok(event) = ctx1.get_next_event() { if let Ok(event) = ctx1.get_next_event() {
cb(event); cb(event);
} else {
task::sleep(time::Duration::from_millis(100)).await;
}
} }
}); });
@@ -62,11 +56,10 @@ async fn main() {
.await .await
.unwrap(); .unwrap();
ctx.configure().await; ctx.configure().await.unwrap();
println!("------ RUN ------"); println!("------ RUN ------");
ctx.run().await; ctx.clone().run().await;
println!("--- SENDING A MESSAGE ---"); println!("--- SENDING A MESSAGE ---");
let contact_id = Contact::create(&ctx, "dignifiedquire", "dignifiedquire@gmail.com") let contact_id = Contact::create(&ctx, "dignifiedquire", "dignifiedquire@gmail.com")

View File

@@ -10,6 +10,7 @@ use crate::config::Config;
use crate::constants::*; use crate::constants::*;
use crate::context::Context; use crate::context::Context;
use crate::dc_tools::*; use crate::dc_tools::*;
use crate::error::{Error, Result};
use crate::imap::Imap; use crate::imap::Imap;
use crate::login_param::{CertificateChecks, LoginParam}; use crate::login_param::{CertificateChecks, LoginParam};
use crate::oauth2::*; use crate::oauth2::*;
@@ -37,31 +38,28 @@ impl Context {
} }
/// Configures this account with the currently set parameters. /// Configures this account with the currently set parameters.
pub async fn configure(&self) { pub async fn configure(&self) -> Result<()> {
if self.has_ongoing().await { ensure!(
warn!(self, "There is already another ongoing process running.",); !self.has_ongoing().await,
return; "There is already another ongoing process running."
} );
ensure!(
!self.scheduler.read().await.is_running(),
"Can not configure, already running"
);
ensure!(
self.sql.is_open().await,
"Cannot configure, database not opened."
);
ensure!(
self.alloc_ongoing().await,
"Cannot allocate ongoing process"
);
if self.scheduler.read().await.is_running() {
warn!(self, "Can not configure, already running");
return;
}
if !self.sql.is_open().await {
error!(self, "Cannot configure, database not opened.",);
progress!(self, 0);
return;
}
if !self.alloc_ongoing().await {
error!(self, "Cannot allocate ongoing process");
progress!(self, 0);
return;
}
let mut success = false; let mut success = false;
let mut param_autoconfig: Option<LoginParam> = None; let mut param_autoconfig: Option<LoginParam> = None;
info!(self, "Configure ...",); info!(self, "Configure ...");
// Variables that are shared between steps: // Variables that are shared between steps:
let mut param = LoginParam::from_database(self, "").await; let mut param = LoginParam::from_database(self, "").await;
@@ -72,9 +70,6 @@ impl Context {
"Internal Error: this value should never be used".to_owned(); "Internal Error: this value should never be used".to_owned();
let mut keep_flags = 0; let mut keep_flags = 0;
const STEP_12_USE_AUTOCONFIG: u8 = 12;
const STEP_13_AFTER_AUTOCONFIG: u8 = 13;
let mut step_counter: u8 = 0; let mut step_counter: u8 = 0;
let (_s, r) = async_std::sync::channel(1); let (_s, r) = async_std::sync::channel(1);
let mut imap = Imap::new(r); let mut imap = Imap::new(r);
@@ -83,14 +78,100 @@ impl Context {
while !self.shall_stop_ongoing().await { while !self.shall_stop_ongoing().await {
step_counter += 1; step_counter += 1;
let success = match step_counter { match exec_step(
self,
&mut imap,
&mut is_imap_connected,
&mut param,
&mut param_domain,
&mut param_autoconfig,
&mut param_addr_urlencoded,
&mut keep_flags,
&mut step_counter,
)
.await
{
Ok(step) => {
success = true;
match step {
Step::Continue => {}
Step::Done => break,
}
}
Err(err) => {
error!(self, "{}", err);
success = false;
break;
}
}
}
if is_imap_connected {
imap.disconnect(self).await;
}
if let Some(provider) = provider::get_provider_info(&param.addr) {
if !provider.after_login_hint.is_empty() {
let mut msg = Message::new(Viewtype::Text);
msg.text = Some(provider.after_login_hint.to_string());
if chat::add_device_msg(self, Some("core-provider-info"), Some(&mut msg))
.await
.is_err()
{
warn!(self, "cannot add after_login_hint as core-provider-info");
}
}
}
// remember the entered parameters on success
// and restore to last-entered on failure.
// this way, the parameters visible to the ui are always in-sync with the current configuration.
if success {
assert!(self.is_configured().await, "epic fail");
LoginParam::from_database(self, "")
.await
.save_to_database(self, "configured_raw_")
.await
.ok();
self.free_ongoing().await;
progress!(self, 1000);
Ok(())
} else {
LoginParam::from_database(self, "configured_raw_")
.await
.save_to_database(self, "")
.await
.ok();
self.free_ongoing().await;
progress!(self, 0);
Err(Error::Message("Configure failed".to_string()))
}
}
}
async fn exec_step(
ctx: &Context,
imap: &mut Imap,
is_imap_connected: &mut bool,
param: &mut LoginParam,
param_domain: &mut String,
param_autoconfig: &mut Option<LoginParam>,
param_addr_urlencoded: &mut String,
keep_flags: &mut i32,
step_counter: &mut u8,
) -> Result<Step> {
const STEP_12_USE_AUTOCONFIG: u8 = 12;
const STEP_13_AFTER_AUTOCONFIG: u8 = 13;
match *step_counter {
// Read login parameters from the database // Read login parameters from the database
1 => { 1 => {
progress!(self, 1); progress!(ctx, 1);
if param.addr.is_empty() { ensure!(!param.addr.is_empty(), "Please enter an email address.");
error!(self, "Please enter an email address.",);
}
!param.addr.is_empty()
} }
// Step 1: Load the parameters and check email-address and password // Step 1: Load the parameters and check email-address and password
2 => { 2 => {
@@ -98,60 +179,55 @@ impl Context {
// the used oauth2 addr may differ, check this. // the used oauth2 addr may differ, check this.
// if dc_get_oauth2_addr() is not available in the oauth2 implementation, // if dc_get_oauth2_addr() is not available in the oauth2 implementation,
// just use the given one. // just use the given one.
progress!(self, 10); progress!(ctx, 10);
if let Some(oauth2_addr) = if let Some(oauth2_addr) = dc_get_oauth2_addr(ctx, &param.addr, &param.mail_pw)
dc_get_oauth2_addr(self, &param.addr, &param.mail_pw)
.await .await
.and_then(|e| e.parse().ok()) .and_then(|e| e.parse().ok())
{ {
info!(self, "Authorized address is {}", oauth2_addr); info!(ctx, "Authorized address is {}", oauth2_addr);
param.addr = oauth2_addr; param.addr = oauth2_addr;
self.sql ctx.sql
.set_raw_config(self, "addr", Some(param.addr.as_str())) .set_raw_config(ctx, "addr", Some(param.addr.as_str()))
.await .await?;
.ok();
} }
progress!(self, 20); progress!(ctx, 20);
} }
true // no oauth? - just continue it's no error // no oauth? - just continue it's no error
} }
3 => { 3 => {
if let Ok(parsed) = param.addr.parse() { if let Ok(parsed) = param.addr.parse() {
let parsed: EmailAddress = parsed; let parsed: EmailAddress = parsed;
param_domain = parsed.domain; *param_domain = parsed.domain;
param_addr_urlencoded = *param_addr_urlencoded =
utf8_percent_encode(&param.addr, NON_ALPHANUMERIC).to_string(); utf8_percent_encode(&param.addr, NON_ALPHANUMERIC).to_string();
true
} else { } else {
error!(self, "Bad email-address."); bail!("Bad email-address.");
false
} }
} }
// Step 2: Autoconfig // Step 2: Autoconfig
4 => { 4 => {
progress!(self, 200); progress!(ctx, 200);
if param.mail_server.is_empty() if param.mail_server.is_empty()
&& param.mail_port == 0 && param.mail_port == 0
/*&&param.mail_user.is_empty() -- the user can enter a loginname which is used by autoconfig then */ /* && param.mail_user.is_empty() -- the user can enter a loginname which is used by autoconfig then */
&& param.send_server.is_empty() && param.send_server.is_empty()
&& param.send_port == 0 && param.send_port == 0
&& param.send_user.is_empty() && param.send_user.is_empty()
/*&&param.send_pw.is_empty() -- the password cannot be auto-configured and is no criterion for autoconfig or not */ /* && param.send_pw.is_empty() -- the password cannot be auto-configured and is no criterion for autoconfig or not */
&& (param.server_flags & !DC_LP_AUTH_OAUTH2) == 0 && (param.server_flags & !DC_LP_AUTH_OAUTH2) == 0
{ {
// no advanced parameters entered by the user: query provider-database or do Autoconfig // no advanced parameters entered by the user: query provider-database or do Autoconfig
keep_flags = param.server_flags & DC_LP_AUTH_OAUTH2; *keep_flags = param.server_flags & DC_LP_AUTH_OAUTH2;
if let Some(new_param) = get_offline_autoconfig(self, &param) { if let Some(new_param) = get_offline_autoconfig(ctx, &param) {
// got parameters from our provider-database, skip Autoconfig, preserve the OAuth2 setting // got parameters from our provider-database, skip Autoconfig, preserve the OAuth2 setting
param_autoconfig = Some(new_param); *param_autoconfig = Some(new_param);
step_counter = STEP_12_USE_AUTOCONFIG - 1; // minus one as step_counter is increased on next loop *step_counter = STEP_12_USE_AUTOCONFIG - 1; // minus one as step_counter is increased on next loop
} }
} else { } else {
// advanced parameters entered by the user: skip Autoconfig // advanced parameters entered by the user: skip Autoconfig
step_counter = STEP_13_AFTER_AUTOCONFIG - 1; // minus one as step_counter is increased on next loop *step_counter = STEP_13_AFTER_AUTOCONFIG - 1; // minus one as step_counter is increased on next loop
} }
true
} }
/* A. Search configurations from the domain used in the email-address, prefer encrypted */ /* A. Search configurations from the domain used in the email-address, prefer encrypted */
5 => { 5 => {
@@ -160,85 +236,77 @@ impl Context {
"https://autoconfig.{}/mail/config-v1.1.xml?emailaddress={}", "https://autoconfig.{}/mail/config-v1.1.xml?emailaddress={}",
param_domain, param_addr_urlencoded param_domain, param_addr_urlencoded
); );
param_autoconfig = moz_autoconfigure(self, &url, &param).ok(); *param_autoconfig = moz_autoconfigure(ctx, &url, &param).ok();
} }
true
} }
6 => { 6 => {
progress!(self, 300); progress!(ctx, 300);
if param_autoconfig.is_none() { if param_autoconfig.is_none() {
// the doc does not mention `emailaddress=`, however, Thunderbird adds it, see https://releases.mozilla.org/pub/thunderbird/ , which makes some sense // the doc does not mention `emailaddress=`, however, Thunderbird adds it, see https://releases.mozilla.org/pub/thunderbird/ , which makes some sense
let url = format!( let url = format!(
"https://{}/.well-known/autoconfig/mail/config-v1.1.xml?emailaddress={}", "https://{}/.well-known/autoconfig/mail/config-v1.1.xml?emailaddress={}",
param_domain, param_addr_urlencoded param_domain, param_addr_urlencoded
); );
param_autoconfig = moz_autoconfigure(self, &url, &param).ok(); *param_autoconfig = moz_autoconfigure(ctx, &url, &param).ok();
} }
true
} }
/* Outlook section start ------------- */ /* Outlook section start ------------- */
/* Outlook uses always SSL but different domains (this comment describes the next two steps) */ /* Outlook uses always SSL but different domains (this comment describes the next two steps) */
7 => { 7 => {
progress!(self, 310); progress!(ctx, 310);
if param_autoconfig.is_none() { if param_autoconfig.is_none() {
let url = format!("https://{}/autodiscover/autodiscover.xml", param_domain); let url = format!("https://{}/autodiscover/autodiscover.xml", param_domain);
param_autoconfig = outlk_autodiscover(self, &url, &param).ok(); *param_autoconfig = outlk_autodiscover(ctx, &url, &param).ok();
} }
true
} }
8 => { 8 => {
progress!(self, 320); progress!(ctx, 320);
if param_autoconfig.is_none() { if param_autoconfig.is_none() {
let url = format!( let url = format!(
"https://{}{}/autodiscover/autodiscover.xml", "https://{}{}/autodiscover/autodiscover.xml",
"autodiscover.", param_domain "autodiscover.", param_domain
); );
param_autoconfig = outlk_autodiscover(self, &url, &param).ok(); *param_autoconfig = outlk_autodiscover(ctx, &url, &param).ok();
} }
true
} }
/* ----------- Outlook section end */ /* ----------- Outlook section end */
9 => { 9 => {
progress!(self, 330); progress!(ctx, 330);
if param_autoconfig.is_none() { if param_autoconfig.is_none() {
let url = format!( let url = format!(
"http://autoconfig.{}/mail/config-v1.1.xml?emailaddress={}", "http://autoconfig.{}/mail/config-v1.1.xml?emailaddress={}",
param_domain, param_addr_urlencoded param_domain, param_addr_urlencoded
); );
param_autoconfig = moz_autoconfigure(self, &url, &param).ok(); *param_autoconfig = moz_autoconfigure(ctx, &url, &param).ok();
} }
true
} }
10 => { 10 => {
progress!(self, 340); progress!(ctx, 340);
if param_autoconfig.is_none() { if param_autoconfig.is_none() {
// do not transfer the email-address unencrypted // do not transfer the email-address unencrypted
let url = format!( let url = format!(
"http://{}/.well-known/autoconfig/mail/config-v1.1.xml", "http://{}/.well-known/autoconfig/mail/config-v1.1.xml",
param_domain param_domain
); );
param_autoconfig = moz_autoconfigure(self, &url, &param).ok(); *param_autoconfig = moz_autoconfigure(ctx, &url, &param).ok();
} }
true
} }
/* B. If we have no configuration yet, search configuration in Thunderbird's centeral database */ /* B. If we have no configuration yet, search configuration in Thunderbird's centeral database */
11 => { 11 => {
progress!(self, 350); progress!(ctx, 350);
if param_autoconfig.is_none() { if param_autoconfig.is_none() {
/* always SSL for Thunderbird's database */ /* always SSL for Thunderbird's database */
let url = let url = format!("https://autoconfig.thunderbird.net/v1.1/{}", param_domain);
format!("https://autoconfig.thunderbird.net/v1.1/{}", param_domain); *param_autoconfig = moz_autoconfigure(ctx, &url, &param).ok();
param_autoconfig = moz_autoconfigure(self, &url, &param).ok();
} }
true
} }
/* C. Do we have any autoconfig result? /* C. Do we have any autoconfig result?
If you change the match-number here, also update STEP_12_COPY_AUTOCONFIG above If you change the match-number here, also update STEP_12_COPY_AUTOCONFIG above
*/ */
STEP_12_USE_AUTOCONFIG => { STEP_12_USE_AUTOCONFIG => {
progress!(self, 500); progress!(ctx, 500);
if let Some(ref cfg) = param_autoconfig { if let Some(ref cfg) = param_autoconfig {
info!(self, "Got autoconfig: {}", &cfg); info!(ctx, "Got autoconfig: {}", &cfg);
if !cfg.mail_user.is_empty() { if !cfg.mail_user.is_empty() {
param.mail_user = cfg.mail_user.clone(); param.mail_user = cfg.mail_user.clone();
} }
@@ -251,8 +319,7 @@ impl Context {
/* although param_autoconfig's data are no longer needed from, /* although param_autoconfig's data are no longer needed from,
it is used to later to prevent trying variations of port/server/logins */ it is used to later to prevent trying variations of port/server/logins */
} }
param.server_flags |= keep_flags; param.server_flags |= *keep_flags;
true
} }
// Step 3: Fill missing fields with defaults // Step 3: Fill missing fields with defaults
// If you change the match-number here, also update STEP_13_AFTER_AUTOCONFIG above // If you change the match-number here, also update STEP_13_AFTER_AUTOCONFIG above
@@ -277,8 +344,7 @@ impl Context {
} }
} }
if param.send_port == 0 { if param.send_port == 0 {
param.send_port = param.send_port = if 0 != param.server_flags & DC_LP_SMTP_SOCKET_STARTTLS as i32 {
if 0 != param.server_flags & DC_LP_SMTP_SOCKET_STARTTLS as i32 {
587 587
} else if 0 != param.server_flags & DC_LP_SMTP_SOCKET_PLAIN as i32 { } else if 0 != param.server_flags & DC_LP_SMTP_SOCKET_PLAIN as i32 {
25 25
@@ -296,8 +362,7 @@ impl Context {
param.server_flags &= !(DC_LP_AUTH_FLAGS as i32); param.server_flags &= !(DC_LP_AUTH_FLAGS as i32);
param.server_flags |= DC_LP_AUTH_NORMAL as i32 param.server_flags |= DC_LP_AUTH_NORMAL as i32
} }
if !dc_exactly_one_bit_set(param.server_flags & DC_LP_IMAP_SOCKET_FLAGS as i32) if !dc_exactly_one_bit_set(param.server_flags & DC_LP_IMAP_SOCKET_FLAGS as i32) {
{
param.server_flags &= !(DC_LP_IMAP_SOCKET_FLAGS as i32); param.server_flags &= !(DC_LP_IMAP_SOCKET_FLAGS as i32);
param.server_flags |= if param.send_port == 143 { param.server_flags |= if param.send_port == 143 {
DC_LP_IMAP_SOCKET_STARTTLS as i32 DC_LP_IMAP_SOCKET_STARTTLS as i32
@@ -305,9 +370,7 @@ impl Context {
DC_LP_IMAP_SOCKET_SSL as i32 DC_LP_IMAP_SOCKET_SSL as i32
} }
} }
if !dc_exactly_one_bit_set( if !dc_exactly_one_bit_set(param.server_flags & (DC_LP_SMTP_SOCKET_FLAGS as i32)) {
param.server_flags & (DC_LP_SMTP_SOCKET_FLAGS as i32),
) {
param.server_flags &= !(DC_LP_SMTP_SOCKET_FLAGS as i32); param.server_flags &= !(DC_LP_SMTP_SOCKET_FLAGS as i32);
param.server_flags |= if param.send_port == 587 { param.server_flags |= if param.send_port == 587 {
DC_LP_SMTP_SOCKET_STARTTLS as i32 DC_LP_SMTP_SOCKET_STARTTLS as i32
@@ -328,120 +391,64 @@ impl Context {
|| param.send_pw.is_empty() || param.send_pw.is_empty()
|| param.server_flags == 0 || param.server_flags == 0
{ {
error!(self, "Account settings incomplete."); bail!("Account settings incomplete.");
false
} else {
true
} }
} }
14 => { 14 => {
progress!(self, 600); progress!(ctx, 600);
/* try to connect to IMAP - if we did not got an autoconfig, /* try to connect to IMAP - if we did not got an autoconfig,
do some further tries with different settings and username variations */ do some further tries with different settings and username variations */
is_imap_connected = try_imap_connections( try_imap_connections(ctx, param, param_autoconfig.is_some(), imap).await?;
self, *is_imap_connected = true;
&mut param,
param_autoconfig.is_some(),
&mut imap,
)
.await;
is_imap_connected
} }
15 => { 15 => {
progress!(self, 800); progress!(ctx, 800);
try_smtp_connections(self, &mut param, param_autoconfig.is_some()).await try_smtp_connections(ctx, param, param_autoconfig.is_some()).await?;
} }
16 => { 16 => {
progress!(self, 900); progress!(ctx, 900);
let create_mvbox = self.get_config_bool(Config::MvboxWatch).await let create_mvbox = ctx.get_config_bool(Config::MvboxWatch).await
|| self.get_config_bool(Config::MvboxMove).await; || ctx.get_config_bool(Config::MvboxMove).await;
if let Err(err) = imap.ensure_configured_folders(self, create_mvbox).await { if let Err(err) = imap.ensure_configured_folders(ctx, create_mvbox).await {
warn!(self, "configuring folders failed: {:?}", err); bail!("configuring folders failed: {:?}", err);
false
} else {
let res = imap.select_with_uidvalidity(self, "INBOX").await;
if let Err(err) = res {
error!(self, "could not read INBOX status: {:?}", err);
false
} else {
true
} }
if let Err(err) = imap.select_with_uidvalidity(ctx, "INBOX").await {
bail!("could not read INBOX status: {:?}", err);
} }
} }
17 => { 17 => {
progress!(self, 910); progress!(ctx, 910);
/* configuration success - write back the configured parameters with the "configured_" prefix; also write the "configured"-flag */ // configuration success - write back the configured parameters with the
param // "configured_" prefix; also write the "configured"-flag */
.save_to_database( // the trailing underscore is correct
self, param.save_to_database(ctx, "configured_").await?;
"configured_", /*the trailing underscore is correct*/ println!("storing configured val");
) ctx.sql.set_raw_config_bool(ctx, "configured", true).await?;
.await println!("stored configured val");
.ok();
self.sql
.set_raw_config_bool(self, "configured", true)
.await
.ok();
true
} }
18 => { 18 => {
progress!(self, 920); progress!(ctx, 920);
// we generate the keypair just now - we could also postpone this until the first message is sent, however, // we generate the keypair just now - we could also postpone this until the first message is sent, however,
// this may result in a unexpected and annoying delay when the user sends his very first message // this may result in a unexpected and annoying delay when the user sends his very first message
// (~30 seconds on a Moto G4 play) and might looks as if message sending is always that slow. // (~30 seconds on a Moto G4 play) and might looks as if message sending is always that slow.
success = e2ee::ensure_secret_key_exists(self).await.is_ok(); e2ee::ensure_secret_key_exists(ctx).await?;
info!(self, "key generation completed"); info!(ctx, "key generation completed");
progress!(self, 940); progress!(ctx, 940);
break; // We are done here return Ok(Step::Done);
} }
_ => { _ => {
error!(self, "Internal error: step counter out of bound",); bail!("Internal error: step counter out of bound");
break;
}
};
if !success {
break;
}
}
if is_imap_connected {
imap.disconnect(self).await;
}
// remember the entered parameters on success
// and restore to last-entered on failure.
// this way, the parameters visible to the ui are always in-sync with the current configuration.
if success {
LoginParam::from_database(self, "")
.await
.save_to_database(self, "configured_raw_")
.await
.ok();
} else {
LoginParam::from_database(self, "configured_raw_")
.await
.save_to_database(self, "")
.await
.ok();
}
if let Some(provider) = provider::get_provider_info(&param.addr) {
if !provider.after_login_hint.is_empty() {
let mut msg = Message::new(Viewtype::Text);
msg.text = Some(provider.after_login_hint.to_string());
if chat::add_device_msg(self, Some("core-provider-info"), Some(&mut msg))
.await
.is_err()
{
warn!(self, "cannot add after_login_hint as core-provider-info");
}
} }
} }
self.free_ongoing().await; Ok(Step::Continue)
progress!(self, if success { 1000 } else { 0 }); }
}
#[derive(Debug)]
enum Step {
Done,
Continue,
} }
#[allow(clippy::unnecessary_unwrap)] #[allow(clippy::unnecessary_unwrap)]
@@ -507,10 +514,13 @@ async fn try_imap_connections(
mut param: &mut LoginParam, mut param: &mut LoginParam,
was_autoconfig: bool, was_autoconfig: bool,
imap: &mut Imap, imap: &mut Imap,
) -> bool { ) -> Result<()> {
// progress 650 and 660 // progress 650 and 660
if let Some(res) = try_imap_connection(context, &mut param, was_autoconfig, 0, imap).await { if try_imap_connection(context, &mut param, was_autoconfig, 0, imap)
return res; .await
.is_ok()
{
return Ok(());
} }
progress!(context, 670); progress!(context, 670);
param.server_flags &= !(DC_LP_IMAP_SOCKET_FLAGS); param.server_flags &= !(DC_LP_IMAP_SOCKET_FLAGS);
@@ -524,11 +534,9 @@ async fn try_imap_connections(
param.send_user = param.send_user.split_at(at).0.to_string(); param.send_user = param.send_user.split_at(at).0.to_string();
} }
// progress 680 and 690 // progress 680 and 690
if let Some(res) = try_imap_connection(context, &mut param, was_autoconfig, 1, imap).await { try_imap_connection(context, &mut param, was_autoconfig, 1, imap).await?;
res
} else { Ok(())
false
}
} }
async fn try_imap_connection( async fn try_imap_connection(
@@ -537,18 +545,18 @@ async fn try_imap_connection(
was_autoconfig: bool, was_autoconfig: bool,
variation: usize, variation: usize,
imap: &mut Imap, imap: &mut Imap,
) -> Option<bool> { ) -> Result<()> {
if let Some(res) = try_imap_one_param(context, &param, imap).await { if try_imap_one_param(context, &param, imap).await.is_ok() {
return Some(res); return Ok(());
} }
if was_autoconfig { if was_autoconfig {
return Some(false); bail!("autoconfig");
} }
progress!(context, 650 + variation * 30); progress!(context, 650 + variation * 30);
param.server_flags &= !(DC_LP_IMAP_SOCKET_FLAGS); param.server_flags &= !(DC_LP_IMAP_SOCKET_FLAGS);
param.server_flags |= DC_LP_IMAP_SOCKET_STARTTLS; param.server_flags |= DC_LP_IMAP_SOCKET_STARTTLS;
if let Some(res) = try_imap_one_param(context, &param, imap).await { if try_imap_one_param(context, &param, imap).await.is_ok() {
return Some(res); return Ok(());
} }
progress!(context, 660 + variation * 30); progress!(context, 660 + variation * 30);
@@ -557,11 +565,7 @@ async fn try_imap_connection(
try_imap_one_param(context, &param, imap).await try_imap_one_param(context, &param, imap).await
} }
async fn try_imap_one_param( async fn try_imap_one_param(context: &Context, param: &LoginParam, imap: &mut Imap) -> Result<()> {
context: &Context,
param: &LoginParam,
imap: &mut Imap,
) -> Option<bool> {
let inf = format!( let inf = format!(
"imap: {}@{}:{} flags=0x{:x} certificate_checks={}", "imap: {}@{}:{} flags=0x{:x} certificate_checks={}",
param.mail_user, param.mail_user,
@@ -571,78 +575,59 @@ async fn try_imap_one_param(
param.imap_certificate_checks param.imap_certificate_checks
); );
info!(context, "Trying: {}", inf); info!(context, "Trying: {}", inf);
if imap.connect(context, &param).await { if imap.connect(context, &param).await {
info!(context, "success: {}", inf); info!(context, "success: {}", inf);
return Some(true); return Ok(());
} }
if context.shall_stop_ongoing().await {
return Some(false); bail!("Could not connect: {}", inf);
}
info!(context, "Could not connect: {}", inf);
None
} }
async fn try_smtp_connections( async fn try_smtp_connections(
context: &Context, context: &Context,
mut param: &mut LoginParam, mut param: &mut LoginParam,
was_autoconfig: bool, was_autoconfig: bool,
) -> bool { ) -> Result<()> {
let mut smtp = Smtp::new(); let mut smtp = Smtp::new();
/* 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 */ /* 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 let Some(res) = try_smtp_one_param(context, &param, &mut smtp).await { if try_smtp_one_param(context, &param, &mut smtp).await.is_ok() {
return res; return Ok(());
} }
if was_autoconfig { if was_autoconfig {
return false; bail!("autoconfig");
} }
progress!(context, 850); progress!(context, 850);
param.server_flags &= !(DC_LP_SMTP_SOCKET_FLAGS as i32); param.server_flags &= !(DC_LP_SMTP_SOCKET_FLAGS as i32);
param.server_flags |= DC_LP_SMTP_SOCKET_STARTTLS as i32; param.server_flags |= DC_LP_SMTP_SOCKET_STARTTLS as i32;
param.send_port = 587; param.send_port = 587;
if let Some(res) = try_smtp_one_param(context, &param, &mut smtp).await { if try_smtp_one_param(context, &param, &mut smtp).await.is_ok() {
if res { return Ok(());
smtp.disconnect().await;
}
return res;
} }
progress!(context, 860); progress!(context, 860);
param.server_flags &= !(DC_LP_SMTP_SOCKET_FLAGS as i32); param.server_flags &= !(DC_LP_SMTP_SOCKET_FLAGS as i32);
param.server_flags |= DC_LP_SMTP_SOCKET_STARTTLS as i32; param.server_flags |= DC_LP_SMTP_SOCKET_STARTTLS as i32;
param.send_port = 25; param.send_port = 25;
if let Some(res) = try_smtp_one_param(context, &param, &mut smtp).await { try_smtp_one_param(context, &param, &mut smtp).await?;
if res {
smtp.disconnect().await; Ok(())
}
return res;
}
false
} }
async fn try_smtp_one_param( async fn try_smtp_one_param(context: &Context, param: &LoginParam, smtp: &mut Smtp) -> Result<()> {
context: &Context,
param: &LoginParam,
smtp: &mut Smtp,
) -> Option<bool> {
let inf = format!( let inf = format!(
"smtp: {}@{}:{} flags: 0x{:x}", "smtp: {}@{}:{} flags: 0x{:x}",
param.send_user, param.send_server, param.send_port, param.server_flags param.send_user, param.send_server, param.send_port, param.server_flags
); );
info!(context, "Trying: {}", inf); info!(context, "Trying: {}", inf);
match smtp.connect(context, &param).await {
Ok(()) => { if let Err(err) = smtp.connect(context, &param).await {
bail!("could not connect: {}", err);
}
info!(context, "success: {}", inf); info!(context, "success: {}", inf);
Some(true) smtp.disconnect().await;
} Ok(())
Err(err) => {
if context.shall_stop_ongoing().await {
Some(false)
} else {
warn!(context, "could not connect: {}", err);
None
}
}
}
} }
#[cfg(test)] #[cfg(test)]
@@ -663,7 +648,7 @@ mod tests {
.set_config(Config::MailPw, Some("123456")) .set_config(Config::MailPw, Some("123456"))
.await .await
.unwrap(); .unwrap();
t.ctx.configure().await; assert!(t.ctx.configure().await.is_err());
} }
#[async_std::test] #[async_std::test]

View File

@@ -128,18 +128,16 @@ impl Context {
} }
pub async fn run(&self) { pub async fn run(&self) {
let ctx = self.clone(); if self.inner.scheduler.read().await.is_running() {
println!("RUN LOCK START"); panic!("Already running");
let l = &mut *self.inner.scheduler.write().await; }
println!("RUN LOCK AQ");
l.run(ctx); let scheduler = Scheduler::run(self.clone());
println!("RUN LOCK DONE"); *self.inner.scheduler.write().await = scheduler;
} }
pub async fn stop(&self) { pub async fn stop(&self) {
if self.inner.scheduler.read().await.is_running() { self.inner.stop().await;
self.inner.scheduler.write().await.stop().await;
}
} }
/// Returns database file path. /// Returns database file path.
@@ -469,11 +467,19 @@ impl Context {
} }
} }
impl Drop for Context { impl InnerContext {
async fn stop(&self) {
if self.scheduler.read().await.is_running() {
self.scheduler.write().await.stop().await;
}
}
}
impl Drop for InnerContext {
fn drop(&mut self) { fn drop(&mut self) {
async_std::task::block_on(async move { async_std::task::block_on(async move {
self.stop().await; self.stop().await;
self.sql.close(self).await; self.sql.close().await;
}); });
} }
} }

View File

@@ -377,7 +377,7 @@ impl Imap {
if self.is_connected() && !self.should_reconnect() { if self.is_connected() && !self.should_reconnect() {
return Ok(()); return Ok(());
} }
if !context.sql.get_raw_config_bool(context, "configured").await { if !context.is_configured().await {
return Err(Error::ConnectWithoutConfigure); return Err(Error::ConnectWithoutConfigure);
} }

View File

@@ -52,13 +52,10 @@ pub enum ImexMode {
} }
/// Import/export things. /// Import/export things.
/// For this purpose, the function creates a job that is executed in the IMAP-thread then;
/// this requires to call dc_perform_inbox_jobs() regularly.
/// ///
/// What to do is defined by the *what* parameter. /// What to do is defined by the *what* parameter.
/// ///
/// While dc_imex() returns immediately, the started job may take a while, /// During execution of the job,
/// you can stop it using dc_stop_ongoing_process(). During execution of the job,
/// some events are sent out: /// some events are sent out:
/// ///
/// - A number of #DC_EVENT_IMEX_PROGRESS events are sent and may be used to create /// - A number of #DC_EVENT_IMEX_PROGRESS events are sent and may be used to create
@@ -67,7 +64,7 @@ pub enum ImexMode {
/// - For each file written on export, the function sends #DC_EVENT_IMEX_FILE_WRITTEN /// - For each file written on export, the function sends #DC_EVENT_IMEX_FILE_WRITTEN
/// ///
/// Only one import-/export-progress can run at the same time. /// Only one import-/export-progress can run at the same time.
/// To cancel an import-/export-progress, use dc_stop_ongoing_process(). /// To cancel an import-/export-progress, drop the future returned by this function.
pub async fn imex( pub async fn imex(
context: &Context, context: &Context,
what: ImexMode, what: ImexMode,
@@ -101,7 +98,7 @@ pub async fn has_backup(context: &Context, dir_name: impl AsRef<Path>) -> Result
newest_backup_time = curr_backup_time; newest_backup_time = curr_backup_time;
} }
info!(context, "backup_time of {} is {}", name, curr_backup_time); info!(context, "backup_time of {} is {}", name, curr_backup_time);
sql.close(&context).await; sql.close().await;
} }
} }
} }
@@ -423,7 +420,7 @@ async fn import_backup(context: &Context, backup_to_import: impl AsRef<Path>) ->
!context.is_configured().await, !context.is_configured().await,
"Cannot import backups to accounts in use." "Cannot import backups to accounts in use."
); );
context.sql.close(&context).await; context.sql.close().await;
dc_delete_file(context, context.get_dbfile()).await; dc_delete_file(context, context.get_dbfile()).await;
ensure!( ensure!(
!context.get_dbfile().exists().await, !context.get_dbfile().exists().await,
@@ -528,7 +525,7 @@ async fn export_backup(context: &Context, dir: impl AsRef<Path>) -> Result<()> {
context.sql.execute("VACUUM;", paramsv![]).await.ok(); context.sql.execute("VACUUM;", paramsv![]).await.ok();
// we close the database during the copy of the dbfile // we close the database during the copy of the dbfile
context.sql.close(context).await; context.sql.close().await;
info!( info!(
context, context,
"Backup '{}' to '{}'.", "Backup '{}' to '{}'.",
@@ -568,7 +565,7 @@ async fn export_backup(context: &Context, dir: impl AsRef<Path>) -> Result<()> {
Ok(()) Ok(())
} }
}; };
dest_sql.close(context).await; dest_sql.close().await;
Ok(res?) Ok(res?)
} }

View File

@@ -3,8 +3,8 @@
//! This module implements a job queue maintained in the SQLite database //! This module implements a job queue maintained in the SQLite database
//! and job types. //! and job types.
use std::fmt;
use std::future::Future; use std::future::Future;
use std::{fmt, time};
use deltachat_derive::{FromSql, ToSql}; use deltachat_derive::{FromSql, ToSql};
use itertools::Itertools; use itertools::Itertools;
@@ -581,45 +581,6 @@ async fn kill_ids(context: &Context, job_ids: &[u32]) -> sql::Result<()> {
Ok(()) Ok(())
} }
async fn get_next_wakeup_time(context: &Context, thread: Thread) -> time::Duration {
let t: i64 = context
.sql
.query_get_value(
context,
"SELECT MIN(desired_timestamp) FROM jobs WHERE thread=?;",
paramsv![thread],
)
.await
.unwrap_or_default();
let mut wakeup_time = time::Duration::new(10 * 60, 0);
let now = time();
if t > 0 {
if t > now {
wakeup_time = time::Duration::new((t - now) as u64, 0);
} else {
wakeup_time = time::Duration::new(0, 0);
}
}
wakeup_time
}
pub async fn maybe_network(context: &Context) {
unimplemented!();
// {
// context.smtp.state.write().await.probe_network = true;
// context
// .probe_imap_network
// .store(true, std::sync::atomic::Ordering::Relaxed);
// }
// interrupt_smtp_idle(context).await;
// interrupt_inbox_idle(context).await;
// interrupt_mvbox_idle(context).await;
// interrupt_sentbox_idle(context).await;
}
pub async fn action_exists(context: &Context, action: Action) -> bool { pub async fn action_exists(context: &Context, action: Action) -> bool {
context context
.sql .sql

View File

@@ -19,10 +19,16 @@ pub(crate) enum Scheduler {
mvbox: ImapConnectionState, mvbox: ImapConnectionState,
sentbox: ImapConnectionState, sentbox: ImapConnectionState,
smtp: SmtpConnectionState, smtp: SmtpConnectionState,
probe_network: bool,
}, },
} }
impl Context { impl Context {
/// Indicate that the network likely has come back.
pub async fn maybe_network(&self) {
self.scheduler.write().await.maybe_network().await;
}
pub(crate) async fn interrupt_inbox(&self) { pub(crate) async fn interrupt_inbox(&self) {
self.scheduler.read().await.interrupt_inbox().await; self.scheduler.read().await.interrupt_inbox().await;
} }
@@ -52,14 +58,14 @@ async fn inbox_loop(ctx: Context, inbox_handlers: ImapConnectionHandlers) {
connection.connect_configured(&ctx).await.unwrap(); connection.connect_configured(&ctx).await.unwrap();
loop { loop {
// TODO: correct value let probe_network = ctx.scheduler.read().await.get_probe_network();
let probe_network = false;
match job::load_next(&ctx, Thread::Imap, probe_network) match job::load_next(&ctx, Thread::Imap, probe_network)
.timeout(Duration::from_millis(200)) .timeout(Duration::from_millis(200))
.await .await
{ {
Ok(Some(job)) => { Ok(Some(job)) => {
job::perform_job(&ctx, job::Connection::Inbox(&mut connection), job).await; job::perform_job(&ctx, job::Connection::Inbox(&mut connection), job).await;
ctx.scheduler.write().await.set_probe_network(false);
} }
Ok(None) | Err(async_std::future::TimeoutError { .. }) => { Ok(None) | Err(async_std::future::TimeoutError { .. }) => {
let watch_folder = get_watch_folder(&ctx, "configured_inbox_folder") let watch_folder = get_watch_folder(&ctx, "configured_inbox_folder")
@@ -106,14 +112,14 @@ async fn smtp_loop(ctx: Context, smtp_handlers: SmtpConnectionHandlers) {
let fut = async move { let fut = async move {
loop { loop {
// TODO: correct value let probe_network = ctx.scheduler.read().await.get_probe_network();
let probe_network = false;
match job::load_next(&ctx, Thread::Smtp, probe_network) match job::load_next(&ctx, Thread::Smtp, probe_network)
.timeout(Duration::from_millis(200)) .timeout(Duration::from_millis(200))
.await .await
{ {
Ok(Some(job)) => { Ok(Some(job)) => {
job::perform_job(&ctx, job::Connection::Smtp(&mut connection), job).await; job::perform_job(&ctx, job::Connection::Smtp(&mut connection), job).await;
ctx.scheduler.write().await.set_probe_network(false);
} }
Ok(None) | Err(async_std::future::TimeoutError { .. }) => { Ok(None) | Err(async_std::future::TimeoutError { .. }) => {
use futures::future::FutureExt; use futures::future::FutureExt;
@@ -133,38 +139,63 @@ async fn smtp_loop(ctx: Context, smtp_handlers: SmtpConnectionHandlers) {
impl Scheduler { impl Scheduler {
/// Start the scheduler, panics if it is already running. /// Start the scheduler, panics if it is already running.
pub fn run(&mut self, ctx: Context) { pub fn run(ctx: Context) -> Self {
match self {
Scheduler::Stopped => {
let (mvbox, mvbox_handlers) = ImapConnectionState::new(); let (mvbox, mvbox_handlers) = ImapConnectionState::new();
let (sentbox, sentbox_handlers) = ImapConnectionState::new(); let (sentbox, sentbox_handlers) = ImapConnectionState::new();
let (smtp, smtp_handlers) = SmtpConnectionState::new(); let (smtp, smtp_handlers) = SmtpConnectionState::new();
let (inbox, inbox_handlers) = ImapConnectionState::new(); let (inbox, inbox_handlers) = ImapConnectionState::new();
let ctx1 = ctx.clone(); let ctx1 = ctx.clone();
let _ = task::spawn(async move { inbox_loop(ctx1, inbox_handlers).await }); task::spawn(async move { inbox_loop(ctx1, inbox_handlers).await });
// TODO: mvbox // TODO: mvbox
// TODO: sentbox // TODO: sentbox
let ctx1 = ctx.clone(); let ctx1 = ctx.clone();
let _ = task::spawn(async move { smtp_loop(ctx1, smtp_handlers).await }); task::spawn(async move { smtp_loop(ctx1, smtp_handlers).await });
*self = Scheduler::Running { let res = Scheduler::Running {
inbox, inbox,
mvbox, mvbox,
sentbox, sentbox,
smtp, smtp,
probe_network: false,
}; };
info!(ctx, "scheduler is running"); info!(ctx, "scheduler is running");
println!("RUN DONE"); println!("RUN DONE");
res
} }
Scheduler::Running { .. } => {
// TODO: return an error fn set_probe_network(&mut self, val: bool) {
panic!("WARN: already running"); match self {
Scheduler::Running {
ref mut probe_network,
..
} => {
*probe_network = val;
}
_ => panic!("set_probe_network can only be called when running"),
} }
} }
fn get_probe_network(&self) -> bool {
match self {
Scheduler::Running { probe_network, .. } => *probe_network,
_ => panic!("get_probe_network can only be called when running"),
}
}
async fn maybe_network(&mut self) {
if !self.is_running() {
return;
}
self.set_probe_network(true);
self.interrupt_inbox()
.join(self.interrupt_mvbox())
.join(self.interrupt_sentbox())
.join(self.interrupt_smtp())
.await;
} }
async fn interrupt_inbox(&self) { async fn interrupt_inbox(&self) {
@@ -206,6 +237,7 @@ impl Scheduler {
mvbox, mvbox,
sentbox, sentbox,
smtp, smtp,
..
} => { } => {
inbox inbox
.stop() .stop()
@@ -213,6 +245,7 @@ impl Scheduler {
.join(sentbox.stop()) .join(sentbox.stop())
.join(smtp.stop()) .join(smtp.stop())
.await; .await;
*self = Scheduler::Stopped;
} }
} }
} }

View File

@@ -87,12 +87,10 @@ impl Sql {
self.pool.read().await.is_some() self.pool.read().await.is_some()
} }
pub async fn close(&self, context: &Context) { pub async fn close(&self) {
let _ = self.pool.write().await.take(); let _ = self.pool.write().await.take();
self.in_use.remove(); self.in_use.remove();
// drop closes the connection // drop closes the connection
info!(context, "Database closed.");
} }
// return true on success, false on failure // return true on success, false on failure
@@ -101,7 +99,7 @@ impl Sql {
Ok(_) => true, Ok(_) => true,
Err(crate::error::Error::SqlError(Error::SqlAlreadyOpen)) => false, Err(crate::error::Error::SqlError(Error::SqlAlreadyOpen)) => false,
Err(_) => { Err(_) => {
self.close(context).await; self.close().await;
false false
} }
} }
@@ -374,10 +372,8 @@ impl Sql {
pub async fn get_raw_config_bool(&self, context: &Context, key: impl AsRef<str>) -> bool { pub async fn get_raw_config_bool(&self, context: &Context, key: impl AsRef<str>) -> bool {
// Not the most obvious way to encode bool as string, but it is matter // Not the most obvious way to encode bool as string, but it is matter
// of backward compatibility. // of backward compatibility.
self.get_raw_config_int(context, key) let res = self.get_raw_config_int(context, key).await;
.await res.unwrap_or_default() > 0
.unwrap_or_default()
> 0
} }
pub async fn set_raw_config_bool<T>(&self, context: &Context, key: T, value: bool) -> Result<()> pub async fn set_raw_config_bool<T>(&self, context: &Context, key: T, value: bool) -> Result<()>