mirror of
https://github.com/chatmail/core.git
synced 2026-04-02 05:22:14 +03:00
Compare commits
4 Commits
v1.157.2
...
smooth-pro
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
db5cb45b9c | ||
|
|
36046f5f2c | ||
|
|
1f9c0ef7d9 | ||
|
|
efd62a7c04 |
@@ -2084,7 +2084,6 @@ class TestOnlineConfigureFails:
|
||||
ac1, configdict = acfactory.get_online_config()
|
||||
ac1.update_config(dict(addr=configdict["addr"], mail_pw="123"))
|
||||
configtracker = ac1.configure()
|
||||
configtracker.wait_progress(500)
|
||||
configtracker.wait_progress(0)
|
||||
ac1._evtracker.ensure_event_not_queued("DC_EVENT_ERROR_NETWORK")
|
||||
|
||||
@@ -2092,7 +2091,6 @@ class TestOnlineConfigureFails:
|
||||
ac1, configdict = acfactory.get_online_config()
|
||||
ac1.update_config(dict(addr="x" + configdict["addr"], mail_pw=configdict["mail_pw"]))
|
||||
configtracker = ac1.configure()
|
||||
configtracker.wait_progress(500)
|
||||
configtracker.wait_progress(0)
|
||||
ac1._evtracker.ensure_event_not_queued("DC_EVENT_ERROR_NETWORK")
|
||||
|
||||
@@ -2100,6 +2098,5 @@ class TestOnlineConfigureFails:
|
||||
ac1, configdict = acfactory.get_online_config()
|
||||
ac1.update_config((dict(addr=configdict["addr"] + "x", mail_pw=configdict["mail_pw"])))
|
||||
configtracker = ac1.configure()
|
||||
configtracker.wait_progress(500)
|
||||
configtracker.wait_progress(0)
|
||||
ac1._evtracker.ensure_event_not_queued("DC_EVENT_ERROR_NETWORK")
|
||||
|
||||
@@ -5,46 +5,31 @@ mod auto_outlook;
|
||||
mod read_url;
|
||||
mod server_params;
|
||||
|
||||
use anyhow::{bail, ensure, Context as _, Result};
|
||||
use async_std::prelude::*;
|
||||
use async_std::task;
|
||||
use itertools::Itertools;
|
||||
use job::Action;
|
||||
use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC};
|
||||
|
||||
use crate::config::Config;
|
||||
use crate::constants::*;
|
||||
use crate::context::Context;
|
||||
use crate::dc_tools::*;
|
||||
use crate::imap::Imap;
|
||||
use crate::job;
|
||||
use crate::login_param::{LoginParam, ServerLoginParam};
|
||||
use crate::message::Message;
|
||||
use crate::oauth2::*;
|
||||
use crate::param::Params;
|
||||
use crate::provider::{Protocol, Socket, UsernamePattern};
|
||||
use crate::smtp::Smtp;
|
||||
use crate::stock::StockMessage;
|
||||
use crate::EventType;
|
||||
use crate::{chat, e2ee, provider};
|
||||
use crate::{constants::*, job};
|
||||
use crate::{context::Context, param::Params};
|
||||
|
||||
use anyhow::{bail, ensure, Context as _, Result};
|
||||
use async_std::prelude::*;
|
||||
use async_std::task;
|
||||
use auto_mozilla::moz_autoconfigure;
|
||||
use auto_outlook::outlk_autodiscover;
|
||||
use itertools::Itertools;
|
||||
use job::Action;
|
||||
use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC};
|
||||
use server_params::{expand_param_vector, ServerParams};
|
||||
|
||||
macro_rules! progress {
|
||||
($context:tt, $progress:expr, $comment:expr) => {
|
||||
assert!(
|
||||
$progress <= 1000,
|
||||
"value in range 0..1000 expected with: 0=error, 1..999=progress, 1000=success"
|
||||
);
|
||||
$context.emit_event($crate::events::EventType::ConfigureProgress {
|
||||
progress: $progress,
|
||||
comment: $comment,
|
||||
});
|
||||
};
|
||||
($context:tt, $progress:expr) => {
|
||||
progress!($context, $progress, None);
|
||||
};
|
||||
}
|
||||
|
||||
impl Context {
|
||||
/// Checks if the context is already configured.
|
||||
pub async fn is_configured(&self) -> bool {
|
||||
@@ -65,10 +50,18 @@ impl Context {
|
||||
);
|
||||
let cancel_channel = self.alloc_ongoing().await?;
|
||||
|
||||
let ctx2 = self.clone();
|
||||
let progress = ProgressHandler::new(15.0, move |p| {
|
||||
ctx2.emit_event(EventType::ConfigureProgress {
|
||||
progress: p,
|
||||
comment: None,
|
||||
});
|
||||
});
|
||||
|
||||
let res = self
|
||||
.inner_configure()
|
||||
.inner_configure(&progress)
|
||||
.race(cancel_channel.recv().map(|_| {
|
||||
progress!(self, 0);
|
||||
progress.p(0);
|
||||
Ok(())
|
||||
}))
|
||||
.await;
|
||||
@@ -78,11 +71,11 @@ impl Context {
|
||||
res
|
||||
}
|
||||
|
||||
async fn inner_configure(&self) -> Result<()> {
|
||||
async fn inner_configure(&self, progress: &impl Progress) -> Result<()> {
|
||||
info!(self, "Configure ...");
|
||||
|
||||
let mut param = LoginParam::from_database(self, "").await;
|
||||
let success = configure(self, &mut param).await;
|
||||
let success = configure(self, &mut param, progress).await;
|
||||
self.set_config(Config::NotifyAboutWrongPw, None).await?;
|
||||
|
||||
if let Some(provider) = provider::get_provider_info(¶m.addr) {
|
||||
@@ -116,21 +109,24 @@ impl Context {
|
||||
Ok(_) => {
|
||||
self.set_config(Config::NotifyAboutWrongPw, Some("1"))
|
||||
.await?;
|
||||
progress!(self, 1000);
|
||||
progress.p(1000);
|
||||
Ok(())
|
||||
}
|
||||
Err(err) => {
|
||||
progress!(
|
||||
progress.kill().await;
|
||||
emit_event!(
|
||||
self,
|
||||
0,
|
||||
Some(
|
||||
self.stock_string_repl_str(
|
||||
StockMessage::ConfigurationFailed,
|
||||
// We are using Anyhow's .context() and to show the inner error, too, we need the {:#}:
|
||||
format!("{:#}", err),
|
||||
EventType::ConfigureProgress {
|
||||
progress: 0,
|
||||
comment: Some(
|
||||
self.stock_string_repl_str(
|
||||
StockMessage::ConfigurationFailed,
|
||||
// We are using Anyhow's .context() and to show the inner error too, we need the {:#}:
|
||||
format!("{:#}", err),
|
||||
)
|
||||
.await
|
||||
)
|
||||
.await
|
||||
)
|
||||
}
|
||||
);
|
||||
Err(err)
|
||||
}
|
||||
@@ -138,8 +134,8 @@ impl Context {
|
||||
}
|
||||
}
|
||||
|
||||
async fn configure(ctx: &Context, param: &mut LoginParam) -> Result<()> {
|
||||
progress!(ctx, 1);
|
||||
async fn configure(ctx: &Context, param: &mut LoginParam, progress: &impl Progress) -> Result<()> {
|
||||
progress.p(1);
|
||||
|
||||
// Check basic settings.
|
||||
ensure!(!param.addr.is_empty(), "Please enter an email address.");
|
||||
@@ -168,7 +164,7 @@ async fn configure(ctx: &Context, param: &mut LoginParam) -> Result<()> {
|
||||
if oauth2 {
|
||||
// the used oauth2 addr may differ, check this.
|
||||
// if dc_get_oauth2_addr() is not available in the oauth2 implementation, just use the given one.
|
||||
progress!(ctx, 10);
|
||||
progress.p(10);
|
||||
if let Some(oauth2_addr) = dc_get_oauth2_addr(ctx, ¶m.addr, ¶m.imap.password)
|
||||
.await
|
||||
.and_then(|e| e.parse().ok())
|
||||
@@ -179,7 +175,7 @@ async fn configure(ctx: &Context, param: &mut LoginParam) -> Result<()> {
|
||||
.set_raw_config(ctx, "addr", Some(param.addr.as_str()))
|
||||
.await?;
|
||||
}
|
||||
progress!(ctx, 20);
|
||||
progress.p(20);
|
||||
}
|
||||
// no oauth? - just continue it's no error
|
||||
|
||||
@@ -188,7 +184,7 @@ async fn configure(ctx: &Context, param: &mut LoginParam) -> Result<()> {
|
||||
let param_addr_urlencoded = utf8_percent_encode(¶m.addr, NON_ALPHANUMERIC).to_string();
|
||||
|
||||
// Step 2: Autoconfig
|
||||
progress!(ctx, 200);
|
||||
progress.p(200);
|
||||
|
||||
let param_autoconfig;
|
||||
if param.imap.server.is_empty()
|
||||
@@ -206,13 +202,13 @@ async fn configure(ctx: &Context, param: &mut LoginParam) -> Result<()> {
|
||||
param_autoconfig = Some(servers);
|
||||
} else {
|
||||
param_autoconfig =
|
||||
get_autoconfig(ctx, param, ¶m_domain, ¶m_addr_urlencoded).await;
|
||||
get_autoconfig(ctx, param, ¶m_domain, ¶m_addr_urlencoded, progress).await;
|
||||
}
|
||||
} else {
|
||||
param_autoconfig = None;
|
||||
}
|
||||
|
||||
progress!(ctx, 500);
|
||||
progress.p(500);
|
||||
|
||||
let servers = expand_param_vector(
|
||||
param_autoconfig.unwrap_or_else(|| {
|
||||
@@ -237,7 +233,7 @@ async fn configure(ctx: &Context, param: &mut LoginParam) -> Result<()> {
|
||||
¶m_domain,
|
||||
);
|
||||
|
||||
progress!(ctx, 550);
|
||||
progress.p(550);
|
||||
|
||||
// Spawn SMTP configuration task
|
||||
let mut smtp = Smtp::new();
|
||||
@@ -278,7 +274,7 @@ async fn configure(ctx: &Context, param: &mut LoginParam) -> Result<()> {
|
||||
}
|
||||
});
|
||||
|
||||
progress!(ctx, 600);
|
||||
progress.p(600);
|
||||
|
||||
// Configure IMAP
|
||||
let (_s, r) = async_std::sync::channel(1);
|
||||
@@ -304,16 +300,13 @@ async fn configure(ctx: &Context, param: &mut LoginParam) -> Result<()> {
|
||||
}
|
||||
Err(e) => errors.push(e),
|
||||
}
|
||||
progress!(
|
||||
ctx,
|
||||
600 + (800 - 600) * (1 + imap_server_index) / imap_servers_count
|
||||
);
|
||||
progress.p(600 + (800 - 600) * (1 + imap_server_index) / imap_servers_count);
|
||||
}
|
||||
if !imap_configured {
|
||||
bail!(nicer_configuration_error(ctx, errors).await);
|
||||
}
|
||||
|
||||
progress!(ctx, 850);
|
||||
progress.p(850);
|
||||
|
||||
// Wait for SMTP configuration
|
||||
match smtp_config_task.await {
|
||||
@@ -325,7 +318,7 @@ async fn configure(ctx: &Context, param: &mut LoginParam) -> Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
progress!(ctx, 900);
|
||||
progress.p(900);
|
||||
|
||||
let create_mvbox = ctx.get_config_bool(Config::MvboxWatch).await
|
||||
|| ctx.get_config_bool(Config::MvboxMove).await;
|
||||
@@ -338,14 +331,14 @@ async fn configure(ctx: &Context, param: &mut LoginParam) -> Result<()> {
|
||||
|
||||
drop(imap);
|
||||
|
||||
progress!(ctx, 910);
|
||||
progress.p(910);
|
||||
// configuration success - write back the configured parameters with the
|
||||
// "configured_" prefix; also write the "configured"-flag */
|
||||
// the trailing underscore is correct
|
||||
param.save_to_database(ctx, "configured_").await?;
|
||||
ctx.sql.set_raw_config_bool(ctx, "configured", true).await?;
|
||||
|
||||
progress!(ctx, 920);
|
||||
progress.p(920);
|
||||
|
||||
e2ee::ensure_secret_key_exists(ctx).await?;
|
||||
info!(ctx, "key generation completed");
|
||||
@@ -356,7 +349,7 @@ async fn configure(ctx: &Context, param: &mut LoginParam) -> Result<()> {
|
||||
)
|
||||
.await;
|
||||
|
||||
progress!(ctx, 940);
|
||||
progress.p(940);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -430,14 +423,15 @@ async fn get_autoconfig(
|
||||
param: &LoginParam,
|
||||
param_domain: &str,
|
||||
param_addr_urlencoded: &str,
|
||||
progress: &impl Progress,
|
||||
) -> Option<Vec<ServerParams>> {
|
||||
let sources = AutoconfigSource::all(param_domain, param_addr_urlencoded);
|
||||
|
||||
let mut progress = 300;
|
||||
let mut p = 300;
|
||||
for source in &sources {
|
||||
let res = source.fetch(ctx, param).await;
|
||||
progress!(ctx, progress);
|
||||
progress += 10;
|
||||
progress.p(p);
|
||||
p += 10;
|
||||
if let Ok(res) = res {
|
||||
return Some(res);
|
||||
}
|
||||
|
||||
@@ -2,15 +2,20 @@
|
||||
//! no references to Context and other "larger" entities here.
|
||||
|
||||
use core::cmp::{max, min};
|
||||
use std::borrow::Cow;
|
||||
use std::fmt;
|
||||
use std::io::Cursor;
|
||||
use std::str::FromStr;
|
||||
use std::time::{Duration, SystemTime};
|
||||
use std::{borrow::Cow, sync::Arc};
|
||||
|
||||
use async_std::path::{Path, PathBuf};
|
||||
use async_std::prelude::*;
|
||||
use async_std::{fs, io};
|
||||
use async_std::{
|
||||
path::{Path, PathBuf},
|
||||
sync::Mutex,
|
||||
task,
|
||||
};
|
||||
use async_trait::async_trait;
|
||||
|
||||
use chrono::{Local, TimeZone};
|
||||
use rand::{thread_rng, Rng};
|
||||
@@ -24,6 +29,94 @@ use crate::message::Message;
|
||||
use crate::provider::get_provider_update_timestamp;
|
||||
use crate::stock::StockMessage;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ProgressHandlerInner<F: Fn(usize) + Send> {
|
||||
progress_limit: usize,
|
||||
emitted_progress: f64,
|
||||
step_fraction: f64,
|
||||
f: F,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ProgressHandler<F: 'static + Fn(usize) + Send> {
|
||||
inner: Arc<Mutex<ProgressHandlerInner<F>>>,
|
||||
}
|
||||
|
||||
impl<F> ProgressHandler<F>
|
||||
where
|
||||
F: 'static + Fn(usize) + Send,
|
||||
{
|
||||
/// If step_fraction is e.g. 15, then every 100ms we will step by 1/15th of the remaining interval.
|
||||
/// The bigger this value, the slower the progress bar will move in the beginning.
|
||||
/// f is the function that is invoked when progress is made.
|
||||
pub fn new(step_fraction: f64, f: F) -> Self {
|
||||
let ret = Arc::new(Mutex::new(ProgressHandlerInner {
|
||||
progress_limit: 1,
|
||||
emitted_progress: 0f64,
|
||||
step_fraction,
|
||||
f,
|
||||
}));
|
||||
let cloned = ret.clone();
|
||||
task::spawn(async move {
|
||||
loop {
|
||||
task::sleep(Duration::from_millis(100)).await;
|
||||
{
|
||||
let mut lock = cloned.lock().await;
|
||||
let limit = lock.progress_limit;
|
||||
if limit == 1000 || limit == 0 {
|
||||
return;
|
||||
}
|
||||
let last = lock.emitted_progress;
|
||||
|
||||
let next = last + ((limit as f64 - last) / lock.step_fraction);
|
||||
|
||||
if (next / 10f64).ceil() - (last / 10f64).ceil() > 0f64 {
|
||||
(lock.f)(next.ceil() as usize);
|
||||
}
|
||||
lock.emitted_progress = next;
|
||||
|
||||
drop(lock);
|
||||
};
|
||||
}
|
||||
});
|
||||
Self { inner: ret }
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait Progress {
|
||||
/// Report actually made progress 0-1000 promille. The progress bar will slowly move toward the value set by this function.
|
||||
/// Set rather high values as the progress bar will stay lower first,
|
||||
/// i.e. don't start with values near 0 and end with values near 1000
|
||||
fn p(&self, progress: usize);
|
||||
/// Stops the progress handler without emitting any other events
|
||||
async fn kill(&self);
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<F> Progress for ProgressHandler<F>
|
||||
where
|
||||
F: 'static + Fn(usize) + Send,
|
||||
{
|
||||
fn p(&self, progress: usize) {
|
||||
assert!(
|
||||
progress <= 1000,
|
||||
"value in range 0..1000 expected with: 0=error, 1..999=progress, 1000=success"
|
||||
);
|
||||
let inner = self.inner.clone();
|
||||
task::spawn(async move {
|
||||
if progress == 1000 || progress == 0 {
|
||||
let inner = &inner.lock().await;
|
||||
(inner.f)(progress);
|
||||
}
|
||||
inner.lock().await.progress_limit = progress;
|
||||
});
|
||||
}
|
||||
async fn kill(&self) {
|
||||
self.inner.lock().await.progress_limit = 0usize;
|
||||
}
|
||||
}
|
||||
|
||||
/// Shortens a string to a specified length and adds "[...]" to the
|
||||
/// end of the shortened string.
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
|
||||
Reference in New Issue
Block a user