Compare commits

...

21 Commits

Author SHA1 Message Date
dignifiedquire
9396a09505 small fixes 2019-11-11 21:47:10 +01:00
dignifiedquire
cfba76d600 extract more 2019-11-11 21:41:43 +01:00
dignifiedquire
0e953d18d0 wip: stop sharing the inbox across threads 2019-11-11 20:15:21 +01:00
dignifiedquire
50aa68e047 update to released versions 2019-11-11 18:57:26 +01:00
holger krekel
0bf32dada8 use AtomicBool for skip_next_idle_wait 2019-11-11 17:16:34 +01:00
Friedel Ziegelmayer
b5075a7122 cleanup select_folder and fix idle/termination issues (#822)
cleanup select_folder and fix idle/termination issues
2019-11-11 16:47:06 +01:00
holger krekel
3dc589788c actually this fixes the double import issue 2019-11-11 15:43:21 +01:00
holger krekel
ba2b66d07a fix tests for failed logins 2019-11-11 15:36:14 +01:00
holger krekel
a5a12d1f72 * fix interrupt_idle by signalling "skip_next_idle_wait" to the potentially concurrently "fn idle" function 2019-11-11 15:33:19 +01:00
holger krekel
131f54fbf1 make select_folder return ImapActionResult's and early-return from idle if there is no selected folder 2019-11-11 14:07:11 +01:00
dignifiedquire
01fe782fa0 remove rustup install 2019-11-11 13:19:13 +01:00
dignifiedquire
1077cf5e99 try use a different rust version 2019-11-10 21:22:57 +01:00
dignifiedquire
ca698f9164 update deps 2019-11-10 21:22:57 +01:00
dignifiedquire
99201027e2 bust ci cache 2019-11-10 21:22:57 +01:00
dignifiedquire
d607d35abc refactor: drop native-tls 2019-11-10 21:22:57 +01:00
dignifiedquire
3d790cbfca update docker image 2019-11-10 21:22:57 +01:00
dignifiedquire
68b2707d12 cleanup imap impl 2019-11-10 21:22:57 +01:00
dignifiedquire
6066821b50 update async-imap 2019-11-10 21:22:57 +01:00
dignifiedquire
fe695c0f95 implement idle again 2019-11-10 21:22:57 +01:00
dignifiedquire
7bf13f3f89 remove local dependency 2019-11-10 21:22:57 +01:00
dignifiedquire
3b3992daed it compiles with async-imap 2019-11-10 21:22:57 +01:00
22 changed files with 2113 additions and 1479 deletions

View File

@@ -15,7 +15,7 @@ restore-workspace: &restore-workspace
restore-cache: &restore-cache restore-cache: &restore-cache
restore_cache: restore_cache:
keys: keys:
- cargo-v2-{{ checksum "rust-toolchain" }}-{{ checksum "Cargo.toml" }}-{{ checksum "Cargo.lock" }}-{{ arch }} - cargo-v3-{{ checksum "rust-toolchain" }}-{{ checksum "Cargo.toml" }}-{{ checksum "Cargo.lock" }}-{{ arch }}
- repo-source-{{ .Branch }}-{{ .Revision }} - repo-source-{{ .Branch }}-{{ .Revision }}
commands: commands:
@@ -44,7 +44,7 @@ jobs:
command: cargo generate-lockfile command: cargo generate-lockfile
- restore_cache: - restore_cache:
keys: keys:
- cargo-v2-{{ checksum "rust-toolchain" }}-{{ checksum "Cargo.toml" }}-{{ checksum "Cargo.lock" }}-{{ arch }} - cargo-v3-{{ checksum "rust-toolchain" }}-{{ checksum "Cargo.toml" }}-{{ checksum "Cargo.lock" }}-{{ arch }}
- run: rustup install $(cat rust-toolchain) - run: rustup install $(cat rust-toolchain)
- run: rustup default $(cat rust-toolchain) - run: rustup default $(cat rust-toolchain)
- run: rustup component add --toolchain $(cat rust-toolchain) rustfmt - run: rustup component add --toolchain $(cat rust-toolchain) rustfmt
@@ -60,7 +60,7 @@ jobs:
paths: paths:
- crate - crate
- save_cache: - save_cache:
key: cargo-v2-{{ checksum "rust-toolchain" }}-{{ checksum "Cargo.toml" }}-{{ checksum "Cargo.lock" }}-{{ arch }} key: cargo-v3-{{ checksum "rust-toolchain" }}-{{ checksum "Cargo.toml" }}-{{ checksum "Cargo.lock" }}-{{ arch }}
paths: paths:
- "~/.cargo" - "~/.cargo"
- "~/.rustup" - "~/.rustup"
@@ -121,7 +121,7 @@ jobs:
steps: steps:
- checkout - checkout
- run: bash ci_scripts/run-doxygen.sh - run: bash ci_scripts/run-doxygen.sh
- run: mkdir -p workspace/c-docs - run: mkdir -p workspace/c-docs
- run: cp -av deltachat-ffi/{html,xml} workspace/c-docs/ - run: cp -av deltachat-ffi/{html,xml} workspace/c-docs/
- persist_to_workspace: - persist_to_workspace:
root: workspace root: workspace
@@ -189,7 +189,7 @@ workflows:
- upload_docs_wheels: - upload_docs_wheels:
requires: requires:
- build_test_docs_wheel - build_test_docs_wheel
- build_doxygen - build_doxygen
- rustfmt: - rustfmt:
requires: requires:
- cargo_fetch - cargo_fetch

953
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -15,12 +15,13 @@ hex = "0.3.2"
sha2 = "0.8.0" sha2 = "0.8.0"
rand = "0.6.5" rand = "0.6.5"
smallvec = "0.6.9" smallvec = "0.6.9"
reqwest = "0.9.15" reqwest = { version = "0.9.15", default-features = false, features = ["rustls-tls"] }
num-derive = "0.2.5" num-derive = "0.2.5"
num-traits = "0.2.6" num-traits = "0.2.6"
native-tls = "0.2.3" lettre = { git = "https://github.com/deltachat/lettre", branch = "feat/rustls" }
lettre = { git = "https://github.com/deltachat/lettre", branch = "master" } async-imap = "0.1"
imap = { git = "https://github.com/deltachat/rust-imap", branch = "master" } async-tls = "0.6"
async-std = { version = "1.0", features = ["unstable"] }
base64 = "0.10" base64 = "0.10"
charset = "0.1" charset = "0.1"
percent-encoding = "2.0" percent-encoding = "2.0"
@@ -49,6 +50,9 @@ bitflags = "1.1.0"
jetscii = "0.4.4" jetscii = "0.4.4"
debug_stub_derive = "0.3.0" debug_stub_derive = "0.3.0"
sanitize-filename = "0.2.1" sanitize-filename = "0.2.1"
stop-token = { version = "0.1.1", features = ["unstable"] }
rustls = "0.16.0"
webpki-roots = "0.18.0"
[dev-dependencies] [dev-dependencies]
tempfile = "3.0" tempfile = "3.0"
@@ -74,6 +78,6 @@ path = "examples/repl/main.rs"
[features] [features]
default = ["nightly", "ringbuf"] default = ["nightly", "ringbuf"]
vendored = ["native-tls/vendored", "reqwest/default-tls-vendored"] vendored = []
nightly = ["pgp/nightly"] nightly = ["pgp/nightly"]
ringbuf = ["pgp/ringbuf"] ringbuf = ["pgp/ringbuf"]

View File

@@ -5,16 +5,6 @@ RUN echo /usr/local/lib64 > /etc/ld.so.conf.d/local.conf && \
echo /usr/local/lib >> /etc/ld.so.conf.d/local.conf echo /usr/local/lib >> /etc/ld.so.conf.d/local.conf
ENV PKG_CONFIG_PATH /usr/local/lib64/pkgconfig:/usr/local/lib/pkgconfig ENV PKG_CONFIG_PATH /usr/local/lib64/pkgconfig:/usr/local/lib/pkgconfig
ENV PIP_DISABLE_PIP_VERSION_CHECK 1
# Install python tools (auditwheels,tox, ...)
ADD deps/build_python.sh /builder/build_python.sh
RUN mkdir tmp1 && cd tmp1 && bash /builder/build_python.sh && cd .. && rm -r tmp1
# Install Rust nightly
ADD deps/build_rust.sh /builder/build_rust.sh
RUN mkdir tmp1 && cd tmp1 && bash /builder/build_rust.sh && cd .. && rm -r tmp1
# Install a recent Perl, needed to install OpenSSL # Install a recent Perl, needed to install OpenSSL
ADD deps/build_perl.sh /builder/build_perl.sh ADD deps/build_perl.sh /builder/build_perl.sh
RUN mkdir tmp1 && cd tmp1 && bash /builder/build_perl.sh && cd .. && rm -r tmp1 RUN mkdir tmp1 && cd tmp1 && bash /builder/build_perl.sh && cd .. && rm -r tmp1
@@ -23,3 +13,12 @@ RUN mkdir tmp1 && cd tmp1 && bash /builder/build_perl.sh && cd .. && rm -r tmp1
ADD deps/build_openssl.sh /builder/build_openssl.sh ADD deps/build_openssl.sh /builder/build_openssl.sh
RUN mkdir tmp1 && cd tmp1 && bash /builder/build_openssl.sh && cd .. && rm -r tmp1 RUN mkdir tmp1 && cd tmp1 && bash /builder/build_openssl.sh && cd .. && rm -r tmp1
ENV PIP_DISABLE_PIP_VERSION_CHECK 1
# Install python tools (auditwheels,tox, ...)
ADD deps/build_python.sh /builder/build_python.sh
RUN mkdir tmp1 && cd tmp1 && bash /builder/build_python.sh && cd .. && rm -r tmp1
# Install Rust nightly
ADD deps/build_rust.sh /builder/build_rust.sh
RUN mkdir tmp1 && cd tmp1 && bash /builder/build_rust.sh && cd .. && rm -r tmp1

View File

@@ -1,11 +1,11 @@
#!/bin/bash #!/bin/bash
PERL_VERSION=5.28.0 PERL_VERSION=5.30.0
PERL_SHA256=7e929f64d4cb0e9d1159d4a59fc89394e27fa1f7004d0836ca0d514685406ea8 # PERL_SHA256=7e929f64d4cb0e9d1159d4a59fc89394e27fa1f7004d0836ca0d514685406ea8
curl -O https://www.cpan.org/src/5.0/perl-${PERL_VERSION}.tar.gz curl -O https://www.cpan.org/src/5.0/perl-${PERL_VERSION}.tar.gz
echo "${PERL_SHA256} perl-${PERL_VERSION}.tar.gz" | sha256sum -c - # echo "${PERL_SHA256} perl-${PERL_VERSION}.tar.gz" | sha256sum -c -
tar xzf perl-${PERL_VERSION}.tar.gz tar -xzf perl-${PERL_VERSION}.tar.gz
cd perl-${PERL_VERSION} cd perl-${PERL_VERSION}
./Configure -de ./Configure -de
make make

View File

@@ -1,11 +1,8 @@
#!/bin/bash #!/bin/bash
set -e -x set -e -x
# Install Rust # Install Rust
curl https://sh.rustup.rs -sSf | sh -s -- --default-toolchain nightly-2019-07-10 -y curl https://sh.rustup.rs -sSf | sh -s -- --default-toolchain nightly-2019-09-12 -y
export PATH=/root/.cargo/bin:$PATH export PATH=/root/.cargo/bin:$PATH
rustc --version rustc --version
# remove some 300-400 MB that we don't need for automated builds
rm -rf /root/.rustup/toolchains/nightly-2019-07-10-x86_64-unknown-linux-gnu/share/

View File

@@ -1,5 +1,6 @@
use std::path::Path; use std::path::Path;
use std::str::FromStr; use std::str::FromStr;
use std::sync::{Arc, Mutex};
use deltachat::chat::{self, Chat}; use deltachat::chat::{self, Chat};
use deltachat::chatlist::*; use deltachat::chatlist::*;
@@ -10,6 +11,7 @@ use deltachat::context::*;
use deltachat::dc_receive_imf::*; use deltachat::dc_receive_imf::*;
use deltachat::dc_tools::*; use deltachat::dc_tools::*;
use deltachat::error::Error; use deltachat::error::Error;
use deltachat::imap::Imap;
use deltachat::imex::*; use deltachat::imex::*;
use deltachat::job::*; use deltachat::job::*;
use deltachat::location; use deltachat::location;
@@ -304,7 +306,11 @@ fn chat_prefix(chat: &Chat) -> &'static str {
chat.typ.into() chat.typ.into()
} }
pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::Error> { pub unsafe fn dc_cmdline(
context: &Context,
inbox: Arc<Mutex<Imap>>,
line: &str,
) -> Result<(), failure::Error> {
let chat_id = *context.cmdline_sel_chat_id.read().unwrap(); let chat_id = *context.cmdline_sel_chat_id.read().unwrap();
let mut sel_chat = if chat_id > 0 { let mut sel_chat = if chat_id > 0 {
Chat::load_from_db(context, chat_id).ok() Chat::load_from_db(context, chat_id).ok()
@@ -496,10 +502,12 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
println!("{:#?}", context.get_info()); println!("{:#?}", context.get_info());
} }
"interrupt" => { "interrupt" => {
interrupt_imap_idle(context); interrupt_imap_idle(context, &mut inbox.lock().unwrap());
unimplemented!()
} }
"maybenetwork" => { "maybenetwork" => {
maybe_network(context); // maybe_network(context);
unimplemented!()
} }
"housekeeping" => { "housekeeping" => {
sql::housekeeping(context); sql::housekeeping(context);

View File

@@ -23,6 +23,7 @@ use std::sync::{Arc, Mutex, RwLock};
use deltachat::config; use deltachat::config;
use deltachat::configure::*; use deltachat::configure::*;
use deltachat::context::*; use deltachat::context::*;
use deltachat::imap::Imap;
use deltachat::job::*; use deltachat::job::*;
use deltachat::oauth2::*; use deltachat::oauth2::*;
use deltachat::securejoin::*; use deltachat::securejoin::*;
@@ -139,42 +140,51 @@ macro_rules! while_running {
}; };
} }
fn start_threads(c: Arc<RwLock<Context>>) { fn start_threads(c: Arc<RwLock<Context>>) -> Option<Arc<Mutex<Imap>>> {
if HANDLE.clone().lock().unwrap().is_some() { if HANDLE.clone().lock().unwrap().is_some() {
return; return None;
} }
println!("Starting threads"); println!("Starting threads");
IS_RUNNING.store(true, Ordering::Relaxed); IS_RUNNING.store(true, Ordering::Relaxed);
let inbox = Arc::new(Mutex::new(c.read().unwrap().create_inbox()));
let inbox2 = inbox.clone();
let ctx = c.clone(); let ctx = c.clone();
let handle_imap = std::thread::spawn(move || loop { let handle_imap = std::thread::spawn(move || {
while_running!({ let inbox = inbox2;
perform_imap_jobs(&ctx.read().unwrap());
perform_imap_fetch(&ctx.read().unwrap()); loop {
while_running!({ while_running!({
let context = ctx.read().unwrap(); perform_imap_jobs(&ctx.read().unwrap(), &mut inbox.lock().unwrap());
perform_imap_idle(&context); perform_imap_fetch(&ctx.read().unwrap(), &mut inbox.lock().unwrap());
}); while_running!({
}); let context = ctx.read().unwrap();
perform_imap_idle(&context, &mut inbox.lock().unwrap());
});
})
}
}); });
let ctx = c.clone(); let ctx = c.clone();
let handle_mvbox = std::thread::spawn(move || loop { let handle_mvbox = std::thread::spawn(move || loop {
let mut mvbox = ctx.read().unwrap().create_inbox();
while_running!({ while_running!({
perform_mvbox_fetch(&ctx.read().unwrap()); perform_mvbox_fetch(&ctx.read().unwrap(), &mut mvbox);
while_running!({ while_running!({
perform_mvbox_idle(&ctx.read().unwrap()); perform_mvbox_idle(&ctx.read().unwrap(), &mut mvbox);
}); });
}); });
}); });
let ctx = c.clone(); let ctx = c.clone();
let handle_sentbox = std::thread::spawn(move || loop { let handle_sentbox = std::thread::spawn(move || loop {
let mut sentbox = ctx.read().unwrap().create_inbox();
while_running!({ while_running!({
perform_sentbox_fetch(&ctx.read().unwrap()); perform_sentbox_fetch(&ctx.read().unwrap(), &mut sentbox);
while_running!({ while_running!({
perform_sentbox_idle(&ctx.read().unwrap()); perform_sentbox_idle(&ctx.read().unwrap(), &mut sentbox);
}); });
}); });
}); });
@@ -195,18 +205,15 @@ fn start_threads(c: Arc<RwLock<Context>>) {
handle_sentbox: Some(handle_sentbox), handle_sentbox: Some(handle_sentbox),
handle_smtp: Some(handle_smtp), handle_smtp: Some(handle_smtp),
}); });
Some(inbox)
} }
fn stop_threads(context: &Context) { fn stop_threads(_context: &Context) {
if let Some(ref mut handle) = *HANDLE.clone().lock().unwrap() { if let Some(ref mut handle) = *HANDLE.clone().lock().unwrap() {
println!("Stopping threads"); println!("Stopping threads");
IS_RUNNING.store(false, Ordering::Relaxed); IS_RUNNING.store(false, Ordering::Relaxed);
interrupt_imap_idle(context);
interrupt_mvbox_idle(context);
interrupt_sentbox_idle(context);
interrupt_smtp_idle(context);
handle.handle_imap.take().unwrap().join().unwrap(); handle.handle_imap.take().unwrap().join().unwrap();
handle.handle_mvbox.take().unwrap().join().unwrap(); handle.handle_mvbox.take().unwrap().join().unwrap();
handle.handle_sentbox.take().unwrap().join().unwrap(); handle.handle_sentbox.take().unwrap().join().unwrap();
@@ -439,11 +446,16 @@ unsafe fn handle_cmd(line: &str, ctx: Arc<RwLock<Context>>) -> Result<ExitResult
let arg0 = args.next().unwrap_or_default(); let arg0 = args.next().unwrap_or_default();
let arg1 = args.next().unwrap_or_default(); let arg1 = args.next().unwrap_or_default();
let mut inbox = None;
match arg0 { match arg0 {
"connect" => { "connect" => {
start_threads(ctx); if let Some(i) = start_threads(ctx.clone()) {
inbox = Some(i);
};
} }
"disconnect" => { "disconnect" => {
let _ = inbox.take();
stop_threads(&ctx.read().unwrap()); stop_threads(&ctx.read().unwrap());
} }
"smtp-jobs" => { "smtp-jobs" => {
@@ -457,11 +469,16 @@ unsafe fn handle_cmd(line: &str, ctx: Arc<RwLock<Context>>) -> Result<ExitResult
if HANDLE.clone().lock().unwrap().is_some() { if HANDLE.clone().lock().unwrap().is_some() {
println!("imap-jobs are already running in a thread."); println!("imap-jobs are already running in a thread.");
} else { } else {
perform_imap_jobs(&ctx.read().unwrap()); perform_imap_jobs(
&ctx.read().unwrap(),
&mut inbox.expect("not connected").lock().unwrap(),
);
} }
} }
"configure" => { "configure" => {
start_threads(ctx.clone()); if let Some(i) = start_threads(ctx.clone()) {
inbox = Some(i);
};
configure(&ctx.read().unwrap()); configure(&ctx.read().unwrap());
} }
"oauth2" => { "oauth2" => {
@@ -485,7 +502,9 @@ unsafe fn handle_cmd(line: &str, ctx: Arc<RwLock<Context>>) -> Result<ExitResult
print!("\x1b[1;1H\x1b[2J"); print!("\x1b[1;1H\x1b[2J");
} }
"getqr" | "getbadqr" => { "getqr" | "getbadqr" => {
start_threads(ctx.clone()); if let Some(i) = start_threads(ctx.clone()) {
inbox = Some(i);
};
if let Some(mut qr) = if let Some(mut qr) =
dc_get_securejoin_qr(&ctx.read().unwrap(), arg1.parse().unwrap_or_default()) dc_get_securejoin_qr(&ctx.read().unwrap(), arg1.parse().unwrap_or_default())
{ {
@@ -504,13 +523,19 @@ unsafe fn handle_cmd(line: &str, ctx: Arc<RwLock<Context>>) -> Result<ExitResult
} }
} }
"joinqr" => { "joinqr" => {
start_threads(ctx.clone()); if let Some(i) = start_threads(ctx.clone()) {
inbox = Some(i);
};
if !arg0.is_empty() { if !arg0.is_empty() {
dc_join_securejoin(&ctx.read().unwrap(), arg1); dc_join_securejoin(&ctx.read().unwrap(), arg1);
} }
} }
"exit" | "quit" => return Ok(ExitResult::Exit), "exit" | "quit" => return Ok(ExitResult::Exit),
_ => dc_cmdline(&ctx.read().unwrap(), line)?, _ => dc_cmdline(
&ctx.read().unwrap(),
inbox.expect("not started").clone(),
line,
)?,
} }
Ok(ExitResult::Continue) Ok(ExitResult::Continue)

View File

@@ -1,6 +1,6 @@
extern crate deltachat; extern crate deltachat;
use std::sync::{Arc, RwLock}; use std::sync::{Arc, Mutex, RwLock};
use std::{thread, time}; use std::{thread, time};
use tempfile::tempdir; use tempfile::tempdir;
@@ -11,7 +11,8 @@ use deltachat::configure::*;
use deltachat::contact::*; use deltachat::contact::*;
use deltachat::context::*; use deltachat::context::*;
use deltachat::job::{ use deltachat::job::{
perform_imap_fetch, perform_imap_idle, perform_imap_jobs, perform_smtp_idle, perform_smtp_jobs, interrupt_imap_idle, perform_imap_fetch, perform_imap_idle, perform_imap_jobs,
perform_smtp_idle, perform_smtp_jobs,
}; };
use deltachat::Event; use deltachat::Event;
@@ -45,17 +46,22 @@ fn main() {
let duration = time::Duration::from_millis(4000); let duration = time::Duration::from_millis(4000);
println!("info: {:#?}", info); println!("info: {:#?}", info);
let inbox = Arc::new(Mutex::new(ctx.create_inbox()));
let inbox2 = inbox.clone();
let ctx = Arc::new(ctx); let ctx = Arc::new(ctx);
let ctx1 = ctx.clone(); let ctx1 = ctx.clone();
let r1 = running.clone(); let r1 = running.clone();
let t1 = thread::spawn(move || { let t1 = thread::spawn(move || {
let inbox = inbox2;
while *r1.read().unwrap() { while *r1.read().unwrap() {
perform_imap_jobs(&ctx1); perform_imap_jobs(&ctx1, &mut inbox.lock().unwrap());
if *r1.read().unwrap() { if *r1.read().unwrap() {
perform_imap_fetch(&ctx1); perform_imap_fetch(&ctx1, &mut inbox.lock().unwrap());
if *r1.read().unwrap() { if *r1.read().unwrap() {
perform_imap_idle(&ctx1); perform_imap_idle(&ctx1, &mut inbox.lock().unwrap());
} }
} }
} }
@@ -79,6 +85,8 @@ fn main() {
ctx.set_config(config::Config::Addr, Some("d@testrun.org")) ctx.set_config(config::Config::Addr, Some("d@testrun.org"))
.unwrap(); .unwrap();
ctx.set_config(config::Config::MailPw, Some(&pw)).unwrap(); ctx.set_config(config::Config::MailPw, Some(&pw)).unwrap();
interrupt_imap_idle(&ctx, &mut inbox.lock().unwrap());
configure(&ctx); configure(&ctx);
thread::sleep(duration); thread::sleep(duration);
@@ -113,7 +121,7 @@ fn main() {
println!("stopping threads"); println!("stopping threads");
*running.clone().write().unwrap() = false; *running.clone().write().unwrap() = false;
deltachat::job::interrupt_imap_idle(&ctx); // not needed anymore I believe. deltachat::job::interrupt_imap_idle(&ctx);
deltachat::job::interrupt_smtp_idle(&ctx); deltachat::job::interrupt_smtp_idle(&ctx);
println!("joining"); println!("joining");

View File

@@ -675,7 +675,6 @@ class TestOnlineAccount:
assert len(messages) == 1 assert len(messages) == 1
assert messages[0].text == "msg1" assert messages[0].text == "msg1"
pytest.xfail("cannot export twice yet, probably due to interrupt_idle failing")
# wait until a second passed since last backup # wait until a second passed since last backup
# because get_latest_backupfile() shall return the latest backup # because get_latest_backupfile() shall return the latest backup
# from a UI it's unlikely anyone manages to export two # from a UI it's unlikely anyone manages to export two
@@ -896,7 +895,7 @@ class TestOnlineConfigureFails:
ac1.start_threads() ac1.start_threads()
wait_configuration_progress(ac1, 500) wait_configuration_progress(ac1, 500)
ev1 = ac1._evlogger.get_matching("DC_EVENT_ERROR_NETWORK") ev1 = ac1._evlogger.get_matching("DC_EVENT_ERROR_NETWORK")
assert "authentication failed" in ev1[2].lower() assert "cannot login" in ev1[2].lower()
wait_configuration_progress(ac1, 0, 0) wait_configuration_progress(ac1, 0, 0)
def test_invalid_user(self, acfactory): def test_invalid_user(self, acfactory):
@@ -905,7 +904,7 @@ class TestOnlineConfigureFails:
ac1.start_threads() ac1.start_threads()
wait_configuration_progress(ac1, 500) wait_configuration_progress(ac1, 500)
ev1 = ac1._evlogger.get_matching("DC_EVENT_ERROR_NETWORK") ev1 = ac1._evlogger.get_matching("DC_EVENT_ERROR_NETWORK")
assert "authentication failed" in ev1[2].lower() assert "cannot login" in ev1[2].lower()
wait_configuration_progress(ac1, 0, 0) wait_configuration_progress(ac1, 0, 0)
def test_invalid_domain(self, acfactory): def test_invalid_domain(self, acfactory):

View File

@@ -1 +1 @@
nightly-2019-08-13 nightly-2019-11-06

View File

@@ -5,7 +5,7 @@ use crate::constants::DC_VERSION_STR;
use crate::context::Context; use crate::context::Context;
use crate::dc_tools::*; use crate::dc_tools::*;
use crate::error::Error; use crate::error::Error;
use crate::job::*; // use crate::job::*;
use crate::stock::StockMessage; use crate::stock::StockMessage;
/// The available configuration keys. /// The available configuration keys.
@@ -119,17 +119,17 @@ impl Context {
} }
Config::InboxWatch => { Config::InboxWatch => {
let ret = self.sql.set_raw_config(self, key, value); let ret = self.sql.set_raw_config(self, key, value);
interrupt_imap_idle(self); // interrupt_imap_idle(self);
ret ret
} }
Config::SentboxWatch => { Config::SentboxWatch => {
let ret = self.sql.set_raw_config(self, key, value); let ret = self.sql.set_raw_config(self, key, value);
interrupt_sentbox_idle(self); // interrupt_sentbox_idle(self);
ret ret
} }
Config::MvboxWatch => { Config::MvboxWatch => {
let ret = self.sql.set_raw_config(self, key, value); let ret = self.sql.set_raw_config(self, key, value);
interrupt_mvbox_idle(self); // interrupt_mvbox_idle(self);
ret ret
} }
Config::Selfstatus => { Config::Selfstatus => {

View File

@@ -46,7 +46,7 @@ pub fn dc_is_configured(context: &Context) -> bool {
******************************************************************************/ ******************************************************************************/
// the other dc_job_do_DC_JOB_*() functions are declared static in the c-file // the other dc_job_do_DC_JOB_*() functions are declared static in the c-file
#[allow(non_snake_case, unused_must_use)] #[allow(non_snake_case, unused_must_use)]
pub fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context) { pub fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context, inbox: &mut Imap) {
if !context.sql.is_open() { if !context.sql.is_open() {
error!(context, "Cannot configure, database not opened.",); error!(context, "Cannot configure, database not opened.",);
progress!(context, 0); progress!(context, 0);
@@ -62,19 +62,11 @@ pub fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context) {
let mut param_autoconfig: Option<LoginParam> = None; let mut param_autoconfig: Option<LoginParam> = None;
context.inbox.read().unwrap().disconnect(context); // TODO: these need to be disconnected manually now, before starting configure
context // inbox.disconnect();
.sentbox_thread // context.sentbox_thread.write().unwrap().imap.disconnect();
.read() // context.mvbox_thread.write().unwrap().imap.disconnect();
.unwrap()
.imap
.disconnect(context);
context
.mvbox_thread
.read()
.unwrap()
.imap
.disconnect(context);
context.smtp.clone().lock().unwrap().disconnect(); context.smtp.clone().lock().unwrap().disconnect();
info!(context, "Configure ...",); info!(context, "Configure ...",);
@@ -337,7 +329,7 @@ pub fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context) {
/* 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 */
imap_connected_here = imap_connected_here =
try_imap_connections(context, &mut param, param_autoconfig.is_some()); try_imap_connections(context, &mut param, param_autoconfig.is_some(), inbox);
imap_connected_here imap_connected_here
} }
15 => { 15 => {
@@ -355,11 +347,7 @@ pub fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context) {
} else { } else {
0 0
}; };
context inbox.configure_folders(context, flags);
.inbox
.read()
.unwrap()
.configure_folders(context, flags);
true true
} }
17 => { 17 => {
@@ -398,7 +386,7 @@ pub fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context) {
} }
} }
if imap_connected_here { if imap_connected_here {
context.inbox.read().unwrap().disconnect(context); inbox.disconnect();
} }
if smtp_connected_here { if smtp_connected_here {
context.smtp.clone().lock().unwrap().disconnect(); context.smtp.clone().lock().unwrap().disconnect();
@@ -439,9 +427,10 @@ fn try_imap_connections(
context: &Context, context: &Context,
mut param: &mut LoginParam, mut param: &mut LoginParam,
was_autoconfig: bool, was_autoconfig: bool,
inbox: &mut Imap,
) -> bool { ) -> bool {
// progress 650 and 660 // progress 650 and 660
if let Some(res) = try_imap_connection(context, &mut param, was_autoconfig, 0) { if let Some(res) = try_imap_connection(context, &mut param, was_autoconfig, 0, inbox) {
return res; return res;
} }
progress!(context, 670); progress!(context, 670);
@@ -456,7 +445,7 @@ 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) { if let Some(res) = try_imap_connection(context, &mut param, was_autoconfig, 1, inbox) {
res res
} else { } else {
false false
@@ -468,8 +457,9 @@ fn try_imap_connection(
param: &mut LoginParam, param: &mut LoginParam,
was_autoconfig: bool, was_autoconfig: bool,
variation: usize, variation: usize,
inbox: &mut Imap,
) -> Option<bool> { ) -> Option<bool> {
if let Some(res) = try_imap_one_param(context, &param) { if let Some(res) = try_imap_one_param(context, &param, inbox) {
return Some(res); return Some(res);
} }
if was_autoconfig { if was_autoconfig {
@@ -478,23 +468,23 @@ fn try_imap_connection(
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) { if let Some(res) = try_imap_one_param(context, &param, inbox) {
return Some(res); return Some(res);
} }
progress!(context, 660 + variation * 30); progress!(context, 660 + variation * 30);
param.mail_port = 143; param.mail_port = 143;
try_imap_one_param(context, &param) try_imap_one_param(context, &param, inbox)
} }
fn try_imap_one_param(context: &Context, param: &LoginParam) -> Option<bool> { fn try_imap_one_param(context: &Context, param: &LoginParam, inbox: &mut Imap) -> Option<bool> {
let inf = format!( let inf = format!(
"imap: {}@{}:{} flags=0x{:x}", "imap: {}@{}:{} flags=0x{:x}",
param.mail_user, param.mail_server, param.mail_port, param.server_flags param.mail_user, param.mail_server, param.mail_port, param.server_flags
); );
info!(context, "Trying: {}", inf); info!(context, "Trying: {}", inf);
if context.inbox.read().unwrap().connect(context, &param) { if inbox.connect(context, &param) {
info!(context, "success: {}", inf); info!(context, "success: {}", inf);
return Some(true); return Some(true);
} }
@@ -566,23 +556,25 @@ fn try_smtp_one_param(context: &Context, param: &LoginParam) -> Option<bool> {
/******************************************************************************* /*******************************************************************************
* Connect to configured account * Connect to configured account
******************************************************************************/ ******************************************************************************/
pub fn dc_connect_to_configured_imap(context: &Context, imap: &Imap) -> libc::c_int { pub fn dc_connect_to_configured_imap(context: &Context, imap: &mut Imap) -> libc::c_int {
let mut ret_connected = 0; async_std::task::block_on(async move {
let mut ret_connected = 0;
if imap.is_connected() { if imap.is_connected().await {
ret_connected = 1 ret_connected = 1
} else if !context.sql.get_raw_config_bool(context, "configured") { } else if !context.sql.get_raw_config_bool(context, "configured") {
warn!(context, "Not configured, cannot connect.",); warn!(context, "Not configured, cannot connect.",);
} else { } else {
let param = LoginParam::from_database(context, "configured_"); let param = LoginParam::from_database(context, "configured_");
// the trailing underscore is correct // the trailing underscore is correct
if imap.connect(context, &param) { if imap.connect(context, &param) {
ret_connected = 2; ret_connected = 2;
}
} }
}
ret_connected ret_connected
})
} }
/******************************************************************************* /*******************************************************************************
@@ -620,6 +612,8 @@ mod tests {
.set_config(Config::Addr, Some("probably@unexistant.addr")) .set_config(Config::Addr, Some("probably@unexistant.addr"))
.unwrap(); .unwrap();
t.ctx.set_config(Config::MailPw, Some("123456")).unwrap(); t.ctx.set_config(Config::MailPw, Some("123456")).unwrap();
dc_job_do_DC_JOB_CONFIGURE_IMAP(&t.ctx);
let mut inbox = t.ctx.create_inbox();
dc_job_do_DC_JOB_CONFIGURE_IMAP(&t.ctx, &mut inbox);
} }
} }

View File

@@ -42,7 +42,6 @@ pub struct Context {
dbfile: PathBuf, dbfile: PathBuf,
blobdir: PathBuf, blobdir: PathBuf,
pub sql: Sql, pub sql: Sql,
pub inbox: Arc<RwLock<Imap>>,
pub perform_inbox_jobs_needed: Arc<RwLock<bool>>, pub perform_inbox_jobs_needed: Arc<RwLock<bool>>,
pub probe_imap_network: Arc<RwLock<bool>>, pub probe_imap_network: Arc<RwLock<bool>>,
pub sentbox_thread: Arc<RwLock<JobThread>>, pub sentbox_thread: Arc<RwLock<JobThread>>,
@@ -118,7 +117,6 @@ impl Context {
let ctx = Context { let ctx = Context {
blobdir, blobdir,
dbfile, dbfile,
inbox: Arc::new(RwLock::new(Imap::new())),
cb, cb,
os_name: Some(os_name), os_name: Some(os_name),
running_state: Arc::new(RwLock::new(Default::default())), running_state: Arc::new(RwLock::new(Default::default())),
@@ -132,12 +130,10 @@ impl Context {
sentbox_thread: Arc::new(RwLock::new(JobThread::new( sentbox_thread: Arc::new(RwLock::new(JobThread::new(
"SENTBOX", "SENTBOX",
"configured_sentbox_folder", "configured_sentbox_folder",
Imap::new(),
))), ))),
mvbox_thread: Arc::new(RwLock::new(JobThread::new( mvbox_thread: Arc::new(RwLock::new(JobThread::new(
"MVBOX", "MVBOX",
"configured_mvbox_folder", "configured_mvbox_folder",
Imap::new(),
))), ))),
probe_imap_network: Arc::new(RwLock::new(false)), probe_imap_network: Arc::new(RwLock::new(false)),
perform_inbox_jobs_needed: Arc::new(RwLock::new(false)), perform_inbox_jobs_needed: Arc::new(RwLock::new(false)),
@@ -153,6 +149,18 @@ impl Context {
Ok(ctx) Ok(ctx)
} }
pub fn create_inbox(&self) -> Imap {
Imap::new()
}
pub fn create_mvbox(&self) -> Imap {
Imap::new()
}
pub fn create_sentbox(&self) -> Imap {
Imap::new()
}
pub fn get_dbfile(&self) -> &Path { pub fn get_dbfile(&self) -> &Path {
self.dbfile.as_path() self.dbfile.as_path()
} }
@@ -458,12 +466,6 @@ impl Context {
impl Drop for Context { impl Drop for Context {
fn drop(&mut self) { fn drop(&mut self) {
info!(self, "disconnecting INBOX-watch",);
self.inbox.read().unwrap().disconnect(self);
info!(self, "disconnecting sentbox-thread",);
self.sentbox_thread.read().unwrap().imap.disconnect(self);
info!(self, "disconnecting mvbox-thread",);
self.mvbox_thread.read().unwrap().imap.disconnect(self);
info!(self, "disconnecting SMTP"); info!(self, "disconnecting SMTP");
self.smtp.clone().lock().unwrap().disconnect(); self.smtp.clone().lock().unwrap().disconnect();
self.sql.close(self); self.sql.close(self);

View File

@@ -498,7 +498,7 @@ fn decrypt_if_autocrypt_message(
public_keyring_for_validate: &Keyring, public_keyring_for_validate: &Keyring,
ret_valid_signatures: &mut HashSet<String>, ret_valid_signatures: &mut HashSet<String>,
ret_gossip_headers: *mut *mut mailimf_fields, ret_gossip_headers: *mut *mut mailimf_fields,
) -> Result<(bool)> { ) -> Result<bool> {
/* The returned bool is true if we detected an Autocrypt-encrypted /* The returned bool is true if we detected an Autocrypt-encrypted
message and successfully decrypted it. Decryption then modifies the message and successfully decrypted it. Decryption then modifies the
passed in mime structure in place. The returned bool is false passed in mime structure in place. The returned bool is false

File diff suppressed because it is too large Load Diff

294
src/imap_client.rs Normal file
View File

@@ -0,0 +1,294 @@
use async_imap::{
error::{Error as ImapError, Result as ImapResult},
extensions::idle::Handle as ImapIdleHandle,
types::{Capabilities, Fetch, Mailbox, Name},
Client as ImapClient, Session as ImapSession,
};
use async_std::net::{self, TcpStream};
use async_std::prelude::*;
use async_tls::client::TlsStream;
use crate::login_param::CertificateChecks;
const DCC_IMAP_DEBUG: &str = "DCC_IMAP_DEBUG";
#[derive(Debug)]
pub(crate) enum Client {
Secure(ImapClient<TlsStream<TcpStream>>),
Insecure(ImapClient<TcpStream>),
}
#[derive(Debug)]
pub(crate) enum Session {
Secure(ImapSession<TlsStream<TcpStream>>),
Insecure(ImapSession<TcpStream>),
}
#[derive(Debug)]
pub(crate) enum IdleHandle {
Secure(ImapIdleHandle<TlsStream<TcpStream>>),
Insecure(ImapIdleHandle<TcpStream>),
}
impl Client {
pub async fn connect_secure<A: net::ToSocketAddrs, S: AsRef<str>>(
addr: A,
domain: S,
_certificate_checks: CertificateChecks,
) -> ImapResult<Self> {
let stream = TcpStream::connect(addr).await?;
let tls = async_tls::TlsConnector::new();
let tls_stream = tls.connect(domain.as_ref(), stream)?.await?;
let mut client = ImapClient::new(tls_stream);
if std::env::var(DCC_IMAP_DEBUG).is_ok() {
client.debug = true;
}
let _greeting = client
.read_response()
.await
.expect("failed to read greeting");
Ok(Client::Secure(client))
}
pub async fn connect_insecure<A: net::ToSocketAddrs>(addr: A) -> ImapResult<Self> {
let stream = TcpStream::connect(addr).await?;
let mut client = ImapClient::new(stream);
if std::env::var(DCC_IMAP_DEBUG).is_ok() {
client.debug = true;
}
let _greeting = client
.read_response()
.await
.expect("failed to read greeting");
Ok(Client::Insecure(client))
}
pub async fn secure<S: AsRef<str>>(
self,
domain: S,
_certificate_checks: CertificateChecks,
) -> ImapResult<Client> {
match self {
Client::Insecure(client) => {
let tls = async_tls::TlsConnector::new();
let client_sec = client.secure(domain, &tls).await?;
Ok(Client::Secure(client_sec))
}
// Nothing to do
Client::Secure(_) => Ok(self),
}
}
pub async fn authenticate<A: async_imap::Authenticator, S: AsRef<str>>(
self,
auth_type: S,
authenticator: &A,
) -> Result<Session, (ImapError, Client)> {
match self {
Client::Secure(i) => match i.authenticate(auth_type, authenticator).await {
Ok(session) => Ok(Session::Secure(session)),
Err((err, c)) => Err((err, Client::Secure(c))),
},
Client::Insecure(i) => match i.authenticate(auth_type, authenticator).await {
Ok(session) => Ok(Session::Insecure(session)),
Err((err, c)) => Err((err, Client::Insecure(c))),
},
}
}
pub async fn login<U: AsRef<str>, P: AsRef<str>>(
self,
username: U,
password: P,
) -> Result<Session, (ImapError, Client)> {
match self {
Client::Secure(i) => match i.login(username, password).await {
Ok(session) => Ok(Session::Secure(session)),
Err((err, c)) => Err((err, Client::Secure(c))),
},
Client::Insecure(i) => match i.login(username, password).await {
Ok(session) => Ok(Session::Insecure(session)),
Err((err, c)) => Err((err, Client::Insecure(c))),
},
}
}
}
impl Session {
pub async fn capabilities(&mut self) -> ImapResult<Capabilities> {
let res = match self {
Session::Secure(i) => i.capabilities().await?,
Session::Insecure(i) => i.capabilities().await?,
};
Ok(res)
}
pub async fn list(
&mut self,
reference_name: Option<&str>,
mailbox_pattern: Option<&str>,
) -> ImapResult<Vec<Name>> {
let res = match self {
Session::Secure(i) => {
i.list(reference_name, mailbox_pattern)
.await?
.collect::<ImapResult<_>>()
.await?
}
Session::Insecure(i) => {
i.list(reference_name, mailbox_pattern)
.await?
.collect::<ImapResult<_>>()
.await?
}
};
Ok(res)
}
pub async fn create<S: AsRef<str>>(&mut self, mailbox_name: S) -> ImapResult<()> {
match self {
Session::Secure(i) => i.create(mailbox_name).await?,
Session::Insecure(i) => i.create(mailbox_name).await?,
}
Ok(())
}
pub async fn subscribe<S: AsRef<str>>(&mut self, mailbox: S) -> ImapResult<()> {
match self {
Session::Secure(i) => i.subscribe(mailbox).await?,
Session::Insecure(i) => i.subscribe(mailbox).await?,
}
Ok(())
}
pub async fn close(&mut self) -> ImapResult<()> {
match self {
Session::Secure(i) => i.close().await?,
Session::Insecure(i) => i.close().await?,
}
Ok(())
}
pub async fn select<S: AsRef<str>>(&mut self, mailbox_name: S) -> ImapResult<Mailbox> {
let mbox = match self {
Session::Secure(i) => i.select(mailbox_name).await?,
Session::Insecure(i) => i.select(mailbox_name).await?,
};
Ok(mbox)
}
pub async fn fetch<S1, S2>(&mut self, sequence_set: S1, query: S2) -> ImapResult<Vec<Fetch>>
where
S1: AsRef<str>,
S2: AsRef<str>,
{
let res = match self {
Session::Secure(i) => {
i.fetch(sequence_set, query)
.await?
.collect::<ImapResult<_>>()
.await?
}
Session::Insecure(i) => {
i.fetch(sequence_set, query)
.await?
.collect::<ImapResult<_>>()
.await?
}
};
Ok(res)
}
pub async fn uid_fetch<S1, S2>(&mut self, uid_set: S1, query: S2) -> ImapResult<Vec<Fetch>>
where
S1: AsRef<str>,
S2: AsRef<str>,
{
let res = match self {
Session::Secure(i) => {
i.uid_fetch(uid_set, query)
.await?
.collect::<ImapResult<_>>()
.await?
}
Session::Insecure(i) => {
i.uid_fetch(uid_set, query)
.await?
.collect::<ImapResult<_>>()
.await?
}
};
Ok(res)
}
pub fn idle(self) -> IdleHandle {
match self {
Session::Secure(i) => {
let h = i.idle();
IdleHandle::Secure(h)
}
Session::Insecure(i) => {
let h = i.idle();
IdleHandle::Insecure(h)
}
}
}
pub async fn uid_store<S1, S2>(&mut self, uid_set: S1, query: S2) -> ImapResult<Vec<Fetch>>
where
S1: AsRef<str>,
S2: AsRef<str>,
{
let res = match self {
Session::Secure(i) => {
i.uid_store(uid_set, query)
.await?
.collect::<ImapResult<_>>()
.await?
}
Session::Insecure(i) => {
i.uid_store(uid_set, query)
.await?
.collect::<ImapResult<_>>()
.await?
}
};
Ok(res)
}
pub async fn uid_mv<S1: AsRef<str>, S2: AsRef<str>>(
&mut self,
uid_set: S1,
mailbox_name: S2,
) -> ImapResult<()> {
match self {
Session::Secure(i) => i.uid_mv(uid_set, mailbox_name).await?,
Session::Insecure(i) => i.uid_mv(uid_set, mailbox_name).await?,
}
Ok(())
}
pub async fn uid_copy<S1: AsRef<str>, S2: AsRef<str>>(
&mut self,
uid_set: S1,
mailbox_name: S2,
) -> ImapResult<()> {
match self {
Session::Secure(i) => i.uid_copy(uid_set, mailbox_name).await?,
Session::Insecure(i) => i.uid_copy(uid_set, mailbox_name).await?,
}
Ok(())
}
}

View File

@@ -218,9 +218,7 @@ impl Job {
} }
#[allow(non_snake_case)] #[allow(non_snake_case)]
fn do_DC_JOB_MOVE_MSG(&mut self, context: &Context) { fn do_DC_JOB_MOVE_MSG(&mut self, context: &Context, inbox: &mut Imap) {
let inbox = context.inbox.read().unwrap();
if let Ok(msg) = Message::load_from_db(context, MsgId::new(self.foreign_id)) { if let Ok(msg) = Message::load_from_db(context, MsgId::new(self.foreign_id)) {
if context if context
.sql .sql
@@ -245,10 +243,10 @@ impl Job {
&dest_folder, &dest_folder,
&mut dest_uid, &mut dest_uid,
) { ) {
ImapResult::RetryLater => { ImapActionResult::RetryLater => {
self.try_again_later(3i32, None); self.try_again_later(3i32, None);
} }
ImapResult::Success => { ImapActionResult::Success => {
message::update_server_uid( message::update_server_uid(
context, context,
&msg.rfc724_mid, &msg.rfc724_mid,
@@ -256,16 +254,14 @@ impl Job {
dest_uid, dest_uid,
); );
} }
ImapResult::Failed | ImapResult::AlreadyDone => {} ImapActionResult::Failed | ImapActionResult::AlreadyDone => {}
} }
} }
} }
} }
#[allow(non_snake_case)] #[allow(non_snake_case)]
fn do_DC_JOB_DELETE_MSG_ON_IMAP(&mut self, context: &Context) { fn do_DC_JOB_DELETE_MSG_ON_IMAP(&mut self, context: &Context, inbox: &mut Imap) {
let inbox = context.inbox.read().unwrap();
if let Ok(mut msg) = Message::load_from_db(context, MsgId::new(self.foreign_id)) { if let Ok(mut msg) = Message::load_from_db(context, MsgId::new(self.foreign_id)) {
if !msg.rfc724_mid.is_empty() { if !msg.rfc724_mid.is_empty() {
/* eg. device messages have no Message-ID */ /* eg. device messages have no Message-ID */
@@ -280,7 +276,7 @@ impl Job {
let mid = msg.rfc724_mid; let mid = msg.rfc724_mid;
let server_folder = msg.server_folder.as_ref().unwrap(); let server_folder = msg.server_folder.as_ref().unwrap();
let res = inbox.delete_msg(context, &mid, server_folder, &mut msg.server_uid); let res = inbox.delete_msg(context, &mid, server_folder, &mut msg.server_uid);
if res == ImapResult::RetryLater { if res == ImapActionResult::RetryLater {
self.try_again_later(-1i32, None); self.try_again_later(-1i32, None);
return; return;
} }
@@ -291,8 +287,7 @@ impl Job {
} }
#[allow(non_snake_case)] #[allow(non_snake_case)]
fn do_DC_JOB_EMPTY_SERVER(&mut self, context: &Context) { fn do_DC_JOB_EMPTY_SERVER(&mut self, context: &Context, inbox: &mut Imap) {
let inbox = context.inbox.read().unwrap();
if self.foreign_id & DC_EMPTY_MVBOX > 0 { if self.foreign_id & DC_EMPTY_MVBOX > 0 {
if let Some(mvbox_folder) = context if let Some(mvbox_folder) = context
.sql .sql
@@ -307,17 +302,15 @@ impl Job {
} }
#[allow(non_snake_case)] #[allow(non_snake_case)]
fn do_DC_JOB_MARKSEEN_MSG_ON_IMAP(&mut self, context: &Context) { fn do_DC_JOB_MARKSEEN_MSG_ON_IMAP(&mut self, context: &Context, inbox: &mut Imap) {
let inbox = context.inbox.read().unwrap();
if let Ok(msg) = Message::load_from_db(context, MsgId::new(self.foreign_id)) { if let Ok(msg) = Message::load_from_db(context, MsgId::new(self.foreign_id)) {
let folder = msg.server_folder.as_ref().unwrap(); let folder = msg.server_folder.as_ref().unwrap();
match inbox.set_seen(context, folder, msg.server_uid) { match inbox.set_seen(context, folder, msg.server_uid) {
ImapResult::RetryLater => { ImapActionResult::RetryLater => {
self.try_again_later(3i32, None); self.try_again_later(3i32, None);
} }
ImapResult::AlreadyDone => {} ImapActionResult::AlreadyDone => {}
ImapResult::Success | ImapResult::Failed => { ImapActionResult::Success | ImapActionResult::Failed => {
// XXX the message might just have been moved // XXX the message might just have been moved
// we want to send out an MDN anyway // we want to send out an MDN anyway
// The job will not be retried so locally // The job will not be retried so locally
@@ -335,15 +328,14 @@ impl Job {
} }
#[allow(non_snake_case)] #[allow(non_snake_case)]
fn do_DC_JOB_MARKSEEN_MDN_ON_IMAP(&mut self, context: &Context) { fn do_DC_JOB_MARKSEEN_MDN_ON_IMAP(&mut self, context: &Context, inbox: &mut Imap) {
let folder = self let folder = self
.param .param
.get(Param::ServerFolder) .get(Param::ServerFolder)
.unwrap_or_default() .unwrap_or_default()
.to_string(); .to_string();
let uid = self.param.get_int(Param::ServerUid).unwrap_or_default() as u32; let uid = self.param.get_int(Param::ServerUid).unwrap_or_default() as u32;
let inbox = context.inbox.read().unwrap(); if inbox.set_seen(context, &folder, uid) == ImapActionResult::RetryLater {
if inbox.set_seen(context, &folder, uid) == ImapResult::RetryLater {
self.try_again_later(3i32, None); self.try_again_later(3i32, None);
return; return;
} }
@@ -361,7 +353,7 @@ impl Job {
.get_raw_config(context, "configured_mvbox_folder"); .get_raw_config(context, "configured_mvbox_folder");
if let Some(dest_folder) = dest_folder { if let Some(dest_folder) = dest_folder {
let mut dest_uid = 0; let mut dest_uid = 0;
if ImapResult::RetryLater if ImapActionResult::RetryLater
== inbox.mv(context, &folder, uid, &dest_folder, &mut dest_uid) == inbox.mv(context, &folder, uid, &dest_folder, &mut dest_uid)
{ {
self.try_again_later(3, None); self.try_again_later(3, None);
@@ -382,11 +374,10 @@ pub fn job_kill_action(context: &Context, action: Action) -> bool {
.is_ok() .is_ok()
} }
pub fn perform_imap_fetch(context: &Context) { pub fn perform_imap_fetch(context: &Context, inbox: &mut Imap) {
let inbox = context.inbox.read().unwrap();
let start = std::time::Instant::now(); let start = std::time::Instant::now();
if 0 == connect_to_inbox(context, &inbox) { if 0 == connect_to_inbox(context, inbox) {
return; return;
} }
if !context.get_config_bool(Config::InboxWatch) { if !context.get_config_bool(Config::InboxWatch) {
@@ -406,10 +397,8 @@ pub fn perform_imap_fetch(context: &Context) {
); );
} }
pub fn perform_imap_idle(context: &Context) { pub fn perform_imap_idle(context: &Context, inbox: &mut Imap) {
let inbox = context.inbox.read().unwrap(); connect_to_inbox(context, inbox);
connect_to_inbox(context, &inbox);
if *context.perform_inbox_jobs_needed.clone().read().unwrap() { if *context.perform_inbox_jobs_needed.clone().read().unwrap() {
info!( info!(
@@ -423,56 +412,60 @@ pub fn perform_imap_idle(context: &Context) {
info!(context, "INBOX-IDLE ended."); info!(context, "INBOX-IDLE ended.");
} }
pub fn perform_mvbox_fetch(context: &Context) { pub fn perform_mvbox_fetch(context: &Context, imap: &mut Imap) {
let use_network = context.get_config_bool(Config::MvboxWatch);
context
.mvbox_thread
.write()
.unwrap()
.fetch(context, use_network);
}
pub fn perform_mvbox_idle(context: &Context) {
let use_network = context.get_config_bool(Config::MvboxWatch); let use_network = context.get_config_bool(Config::MvboxWatch);
context context
.mvbox_thread .mvbox_thread
.read() .read()
.unwrap() .unwrap()
.idle(context, use_network); .fetch(context, use_network, imap);
} }
pub fn interrupt_mvbox_idle(context: &Context) { pub fn perform_mvbox_idle(context: &Context, imap: &mut Imap) {
context.mvbox_thread.read().unwrap().interrupt_idle(context); let use_network = context.get_config_bool(Config::MvboxWatch);
}
pub fn perform_sentbox_fetch(context: &Context) {
let use_network = context.get_config_bool(Config::SentboxWatch);
context context
.sentbox_thread .mvbox_thread
.write() .read()
.unwrap() .unwrap()
.fetch(context, use_network); .idle(context, use_network, imap);
} }
pub fn perform_sentbox_idle(context: &Context) { pub fn interrupt_mvbox_idle(context: &Context, imap: &mut Imap) {
context
.mvbox_thread
.read()
.unwrap()
.interrupt_idle(context, imap);
}
pub fn perform_sentbox_fetch(context: &Context, imap: &mut Imap) {
let use_network = context.get_config_bool(Config::SentboxWatch); let use_network = context.get_config_bool(Config::SentboxWatch);
context context
.sentbox_thread .sentbox_thread
.read() .read()
.unwrap() .unwrap()
.idle(context, use_network); .fetch(context, use_network, imap);
} }
pub fn interrupt_sentbox_idle(context: &Context) { pub fn perform_sentbox_idle(context: &Context, imap: &mut Imap) {
let use_network = context.get_config_bool(Config::SentboxWatch);
context context
.sentbox_thread .sentbox_thread
.read() .read()
.unwrap() .unwrap()
.interrupt_idle(context); .idle(context, use_network, imap);
}
pub fn interrupt_sentbox_idle(context: &Context, imap: &mut Imap) {
context
.sentbox_thread
.read()
.unwrap()
.interrupt_idle(context, imap);
} }
pub fn perform_smtp_jobs(context: &Context) { pub fn perform_smtp_jobs(context: &Context) {
@@ -493,7 +486,7 @@ pub fn perform_smtp_jobs(context: &Context) {
}; };
info!(context, "SMTP-jobs started...",); info!(context, "SMTP-jobs started...",);
job_perform(context, Thread::Smtp, probe_smtp_network); job_perform(context, Thread::Smtp, probe_smtp_network, None);
info!(context, "SMTP-jobs ended."); info!(context, "SMTP-jobs ended.");
{ {
@@ -557,7 +550,7 @@ fn get_next_wakeup_time(context: &Context, thread: Thread) -> Duration {
wakeup_time wakeup_time
} }
pub fn maybe_network(context: &Context) { pub fn maybe_network(context: &Context, _inbox: &mut Imap) {
{ {
let &(ref lock, _) = &*context.smtp_state.clone(); let &(ref lock, _) = &*context.smtp_state.clone();
let mut state = lock.lock().unwrap(); let mut state = lock.lock().unwrap();
@@ -567,9 +560,10 @@ pub fn maybe_network(context: &Context) {
} }
interrupt_smtp_idle(context); interrupt_smtp_idle(context);
interrupt_imap_idle(context); // TODO: manually
interrupt_mvbox_idle(context); // interrupt_imap_idle(context, inbox);
interrupt_sentbox_idle(context); // interrupt_mvbox_idle(context);
// interrupt_sentbox_idle(context);
} }
pub fn job_action_exists(context: &Context, action: Action) -> bool { pub fn job_action_exists(context: &Context, action: Action) -> bool {
@@ -681,14 +675,14 @@ pub fn job_send_msg(context: &Context, msg_id: MsgId) -> Result<(), Error> {
Ok(()) Ok(())
} }
pub fn perform_imap_jobs(context: &Context) { pub fn perform_imap_jobs(context: &Context, inbox: &mut Imap) {
info!(context, "dc_perform_imap_jobs starting.",); info!(context, "dc_perform_imap_jobs starting.",);
let probe_imap_network = *context.probe_imap_network.clone().read().unwrap(); let probe_imap_network = *context.probe_imap_network.clone().read().unwrap();
*context.probe_imap_network.write().unwrap() = false; *context.probe_imap_network.write().unwrap() = false;
*context.perform_inbox_jobs_needed.write().unwrap() = false; *context.perform_inbox_jobs_needed.write().unwrap() = false;
job_perform(context, Thread::Imap, probe_imap_network); job_perform(context, Thread::Imap, probe_imap_network, Some(inbox));
info!(context, "dc_perform_imap_jobs ended.",); info!(context, "dc_perform_imap_jobs ended.",);
} }
@@ -700,7 +694,12 @@ pub fn perform_sentbox_jobs(context: &Context) {
info!(context, "dc_perform_sentbox_jobs EMPTY (for now).",); info!(context, "dc_perform_sentbox_jobs EMPTY (for now).",);
} }
fn job_perform(context: &Context, thread: Thread, probe_network: bool) { fn job_perform(
context: &Context,
thread: Thread,
probe_network: bool,
mut inbox: Option<&mut Imap>,
) {
let query = if !probe_network { let query = if !probe_network {
// processing for first-try and after backoff-timeouts: // processing for first-try and after backoff-timeouts:
// process jobs in the order they were added. // process jobs in the order they were added.
@@ -768,18 +767,9 @@ fn job_perform(context: &Context, thread: Thread, probe_network: bool) {
// - they can be re-executed one time AT_ONCE, but they are not save in the database for later execution // - they can be re-executed one time AT_ONCE, but they are not save in the database for later execution
if Action::ConfigureImap == job.action || Action::ImexImap == job.action { if Action::ConfigureImap == job.action || Action::ImexImap == job.action {
job_kill_action(context, job.action); job_kill_action(context, job.action);
context // TODO: figure out better way
.sentbox_thread // context.sentbox_thread.write().unwrap().suspend(context);
.clone() // context.mvbox_thread.write().unwrap().suspend(context);
.read()
.unwrap()
.suspend(context);
context
.mvbox_thread
.clone()
.read()
.unwrap()
.suspend(context);
suspend_smtp_thread(context, true); suspend_smtp_thread(context, true);
} }
@@ -793,13 +783,21 @@ fn job_perform(context: &Context, thread: Thread, probe_network: bool) {
warn!(context, "Unknown job id found"); warn!(context, "Unknown job id found");
} }
Action::SendMsgToSmtp => job.do_DC_JOB_SEND(context), Action::SendMsgToSmtp => job.do_DC_JOB_SEND(context),
Action::EmptyServer => job.do_DC_JOB_EMPTY_SERVER(context), Action::EmptyServer => job.do_DC_JOB_EMPTY_SERVER(context, inbox.as_mut().unwrap()),
Action::DeleteMsgOnImap => job.do_DC_JOB_DELETE_MSG_ON_IMAP(context), Action::DeleteMsgOnImap => {
Action::MarkseenMsgOnImap => job.do_DC_JOB_MARKSEEN_MSG_ON_IMAP(context), job.do_DC_JOB_DELETE_MSG_ON_IMAP(context, inbox.as_mut().unwrap())
Action::MarkseenMdnOnImap => job.do_DC_JOB_MARKSEEN_MDN_ON_IMAP(context), }
Action::MoveMsg => job.do_DC_JOB_MOVE_MSG(context), Action::MarkseenMsgOnImap => {
job.do_DC_JOB_MARKSEEN_MSG_ON_IMAP(context, inbox.as_mut().unwrap())
}
Action::MarkseenMdnOnImap => {
job.do_DC_JOB_MARKSEEN_MDN_ON_IMAP(context, inbox.as_mut().unwrap())
}
Action::MoveMsg => job.do_DC_JOB_MOVE_MSG(context, inbox.as_mut().unwrap()),
Action::SendMdn => job.do_DC_JOB_SEND(context), Action::SendMdn => job.do_DC_JOB_SEND(context),
Action::ConfigureImap => dc_job_do_DC_JOB_CONFIGURE_IMAP(context), Action::ConfigureImap => {
dc_job_do_DC_JOB_CONFIGURE_IMAP(context, inbox.as_mut().unwrap())
}
Action::ImexImap => match job_do_DC_JOB_IMEX_IMAP(context, &job) { Action::ImexImap => match job_do_DC_JOB_IMEX_IMAP(context, &job) {
Ok(()) => {} Ok(()) => {}
Err(err) => { Err(err) => {
@@ -926,7 +924,7 @@ fn suspend_smtp_thread(context: &Context, suspend: bool) {
} }
} }
pub fn connect_to_inbox(context: &Context, inbox: &Imap) -> libc::c_int { pub fn connect_to_inbox(context: &Context, inbox: &mut Imap) -> libc::c_int {
let ret_connected = dc_connect_to_configured_imap(context, inbox); let ret_connected = dc_connect_to_configured_imap(context, inbox);
if 0 != ret_connected { if 0 != ret_connected {
inbox.set_watch_folder("INBOX".into()); inbox.set_watch_folder("INBOX".into());
@@ -1004,7 +1002,7 @@ pub fn job_add(
).ok(); ).ok();
match thread { match thread {
Thread::Imap => interrupt_imap_idle(context), Thread::Imap => {} //FIXME interrupt_imap_idle(context),
Thread::Smtp => interrupt_smtp_idle(context), Thread::Smtp => interrupt_smtp_idle(context),
Thread::Unknown => {} Thread::Unknown => {}
} }
@@ -1021,10 +1019,9 @@ pub fn interrupt_smtp_idle(context: &Context) {
cvar.notify_one(); cvar.notify_one();
} }
pub fn interrupt_imap_idle(context: &Context) { pub fn interrupt_imap_idle(context: &Context, inbox: &mut Imap) {
info!(context, "Interrupting INBOX-IDLE...",); info!(context, "Interrupting INBOX-IDLE...",);
*context.perform_inbox_jobs_needed.write().unwrap() = true; *context.perform_inbox_jobs_needed.write().unwrap() = true;
inbox.interrupt_idle();
context.inbox.read().unwrap().interrupt_idle();
} }

View File

@@ -8,7 +8,6 @@ use crate::imap::Imap;
pub struct JobThread { pub struct JobThread {
pub name: &'static str, pub name: &'static str,
pub folder_config_name: &'static str, pub folder_config_name: &'static str,
pub imap: Imap,
pub state: Arc<(Mutex<JobState>, Condvar)>, pub state: Arc<(Mutex<JobState>, Condvar)>,
} }
@@ -21,21 +20,20 @@ pub struct JobState {
} }
impl JobThread { impl JobThread {
pub fn new(name: &'static str, folder_config_name: &'static str, imap: Imap) -> Self { pub fn new(name: &'static str, folder_config_name: &'static str) -> Self {
JobThread { JobThread {
name, name,
folder_config_name, folder_config_name,
imap,
state: Arc::new((Mutex::new(Default::default()), Condvar::new())), state: Arc::new((Mutex::new(Default::default()), Condvar::new())),
} }
} }
pub fn suspend(&self, context: &Context) { pub fn suspend(&self, context: &Context, imap: &mut Imap) {
info!(context, "Suspending {}-thread.", self.name,); info!(context, "Suspending {}-thread.", self.name,);
{ {
self.state.0.lock().unwrap().suspended = true; self.state.0.lock().unwrap().suspended = true;
} }
self.interrupt_idle(context); self.interrupt_idle(context, imap);
loop { loop {
let using_handle = self.state.0.lock().unwrap().using_handle; let using_handle = self.state.0.lock().unwrap().using_handle;
if !using_handle { if !using_handle {
@@ -56,14 +54,14 @@ impl JobThread {
cvar.notify_one(); cvar.notify_one();
} }
pub fn interrupt_idle(&self, context: &Context) { pub fn interrupt_idle(&self, context: &Context, imap: &mut Imap) {
{ {
self.state.0.lock().unwrap().jobs_needed = true; self.state.0.lock().unwrap().jobs_needed = true;
} }
info!(context, "Interrupting {}-IDLE...", self.name); info!(context, "Interrupting {}-IDLE...", self.name);
self.imap.interrupt_idle(); imap.interrupt_idle();
let &(ref lock, ref cvar) = &*self.state.clone(); let &(ref lock, ref cvar) = &*self.state.clone();
let mut state = lock.lock().unwrap(); let mut state = lock.lock().unwrap();
@@ -72,7 +70,7 @@ impl JobThread {
cvar.notify_one(); cvar.notify_one();
} }
pub fn fetch(&mut self, context: &Context, use_network: bool) { pub fn fetch(&self, context: &Context, use_network: bool, imap: &mut Imap) {
{ {
let &(ref lock, _) = &*self.state.clone(); let &(ref lock, _) = &*self.state.clone();
let mut state = lock.lock().unwrap(); let mut state = lock.lock().unwrap();
@@ -86,13 +84,13 @@ impl JobThread {
if use_network { if use_network {
let start = std::time::Instant::now(); let start = std::time::Instant::now();
if self.connect_to_imap(context) { if self.connect_to_imap(context, imap) {
info!(context, "{}-fetch started...", self.name); info!(context, "{}-fetch started...", self.name);
self.imap.fetch(context); imap.fetch(context);
if self.imap.should_reconnect() { if imap.should_reconnect() {
info!(context, "{}-fetch aborted, starting over...", self.name,); info!(context, "{}-fetch aborted, starting over...", self.name,);
self.imap.fetch(context); imap.fetch(context);
} }
info!( info!(
context, context,
@@ -106,35 +104,39 @@ impl JobThread {
self.state.0.lock().unwrap().using_handle = false; self.state.0.lock().unwrap().using_handle = false;
} }
fn connect_to_imap(&self, context: &Context) -> bool { fn connect_to_imap(&self, context: &Context, imap: &mut Imap) -> bool {
if self.imap.is_connected() { async_std::task::block_on(async move {
return true; if imap.is_connected().await {
} return true;
let mut ret_connected = dc_connect_to_configured_imap(context, &self.imap) != 0;
if ret_connected {
if context
.sql
.get_raw_config_int(context, "folders_configured")
.unwrap_or_default()
< 3
{
self.imap.configure_folders(context, 0x1);
} }
if let Some(mvbox_name) = context.sql.get_raw_config(context, self.folder_config_name) { let mut ret_connected = dc_connect_to_configured_imap(context, imap) != 0;
self.imap.set_watch_folder(mvbox_name);
} else {
self.imap.disconnect(context);
ret_connected = false;
}
}
ret_connected if ret_connected {
if context
.sql
.get_raw_config_int(context, "folders_configured")
.unwrap_or_default()
< 3
{
imap.configure_folders(context, 0x1);
}
if let Some(mvbox_name) =
context.sql.get_raw_config(context, self.folder_config_name)
{
imap.set_watch_folder(mvbox_name);
} else {
imap.disconnect();
ret_connected = false;
}
}
ret_connected
})
} }
pub fn idle(&self, context: &Context, use_network: bool) { pub fn idle(&self, context: &Context, use_network: bool, imap: &mut Imap) {
{ {
let &(ref lock, ref cvar) = &*self.state.clone(); let &(ref lock, ref cvar) = &*self.state.clone();
let mut state = lock.lock().unwrap(); let mut state = lock.lock().unwrap();
@@ -170,9 +172,9 @@ impl JobThread {
} }
} }
self.connect_to_imap(context); self.connect_to_imap(context, imap);
info!(context, "{}-IDLE started...", self.name,); info!(context, "{}-IDLE started...", self.name);
self.imap.idle(context); imap.idle(context);
info!(context, "{}-IDLE ended.", self.name); info!(context, "{}-IDLE ended.", self.name);
self.state.0.lock().unwrap().using_handle = false; self.state.0.lock().unwrap().using_handle = false;

View File

@@ -39,7 +39,8 @@ pub mod constants;
pub mod contact; pub mod contact;
pub mod context; pub mod context;
mod e2ee; mod e2ee;
mod imap; pub mod imap;
mod imap_client;
pub mod imex; pub mod imex;
pub mod job; pub mod job;
mod job_thread; mod job_thread;

View File

@@ -251,28 +251,28 @@ fn get_readable_flags(flags: i32) -> String {
res res
} }
pub fn dc_build_tls( // pub fn dc_build_tls(
certificate_checks: CertificateChecks, // certificate_checks: CertificateChecks,
) -> Result<native_tls::TlsConnector, native_tls::Error> { // ) -> Result<native_tls::TlsConnector, native_tls::Error> {
let mut tls_builder = native_tls::TlsConnector::builder(); // let mut tls_builder = native_tls::TlsConnector::builder();
match certificate_checks { // match certificate_checks {
CertificateChecks::Automatic => { // CertificateChecks::Automatic => {
// Same as AcceptInvalidCertificates for now. // // Same as AcceptInvalidCertificates for now.
// TODO: use provider database when it becomes available // // TODO: use provider database when it becomes available
tls_builder // tls_builder
.danger_accept_invalid_hostnames(true) // .danger_accept_invalid_hostnames(true)
.danger_accept_invalid_certs(true) // .danger_accept_invalid_certs(true)
} // }
CertificateChecks::Strict => &mut tls_builder, // CertificateChecks::Strict => &mut tls_builder,
CertificateChecks::AcceptInvalidHostnames => { // CertificateChecks::AcceptInvalidHostnames => {
tls_builder.danger_accept_invalid_hostnames(true) // tls_builder.danger_accept_invalid_hostnames(true)
} // }
CertificateChecks::AcceptInvalidCertificates => tls_builder // CertificateChecks::AcceptInvalidCertificates => tls_builder
.danger_accept_invalid_hostnames(true) // .danger_accept_invalid_hostnames(true)
.danger_accept_invalid_certs(true), // .danger_accept_invalid_certs(true),
} // }
.build() // .build()
} // }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {

View File

@@ -5,7 +5,7 @@ use crate::constants::*;
use crate::context::Context; use crate::context::Context;
use crate::error::Error; use crate::error::Error;
use crate::events::Event; use crate::events::Event;
use crate::login_param::{dc_build_tls, LoginParam}; use crate::login_param::LoginParam;
use crate::oauth2::*; use crate::oauth2::*;
#[derive(DebugStub)] #[derive(DebugStub)]
@@ -65,8 +65,11 @@ impl Smtp {
let domain = &lp.send_server; let domain = &lp.send_server;
let port = lp.send_port as u16; let port = lp.send_port as u16;
let tls = dc_build_tls(lp.smtp_certificate_checks).unwrap(); let mut tls_config = rustls::ClientConfig::new();
let tls_parameters = ClientTlsParameters::new(domain.to_string(), tls); tls_config
.root_store
.add_server_trust_anchors(&webpki_roots::TLS_SERVER_ROOTS);
let tls_parameters = ClientTlsParameters::new(domain.to_string(), tls_config);
let (creds, mechanism) = if 0 != lp.server_flags & (DC_LP_AUTH_OAUTH2 as i32) { let (creds, mechanism) = if 0 != lp.server_flags & (DC_LP_AUTH_OAUTH2 as i32) {
// oauth2 // oauth2