mirror of
https://github.com/chatmail/core.git
synced 2026-04-03 22:12:11 +03:00
Compare commits
15 Commits
hpk-gh-pyt
...
expand-tab
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
25159e7ed0 | ||
|
|
d808bfe400 | ||
|
|
1b30078c09 | ||
|
|
0278875e03 | ||
|
|
d0ccf28678 | ||
|
|
5023255ebc | ||
|
|
545376875a | ||
|
|
a9fe77b62e | ||
|
|
b42d8799b4 | ||
|
|
a42e197634 | ||
|
|
fc32c85608 | ||
|
|
dabd431b1f | ||
|
|
85b4817a1e | ||
|
|
84c6113271 | ||
|
|
9506f8c38e |
@@ -15,7 +15,7 @@ restore-workspace: &restore-workspace
|
||||
restore-cache: &restore-cache
|
||||
restore_cache:
|
||||
keys:
|
||||
- cargo-v3-{{ checksum "rust-toolchain" }}-{{ checksum "Cargo.toml" }}-{{ checksum "Cargo.lock" }}-{{ arch }}
|
||||
- cargo-v2-{{ checksum "rust-toolchain" }}-{{ checksum "Cargo.toml" }}-{{ checksum "Cargo.lock" }}-{{ arch }}
|
||||
- repo-source-{{ .Branch }}-{{ .Revision }}
|
||||
|
||||
commands:
|
||||
@@ -44,7 +44,7 @@ jobs:
|
||||
command: cargo generate-lockfile
|
||||
- restore_cache:
|
||||
keys:
|
||||
- cargo-v3-{{ checksum "rust-toolchain" }}-{{ checksum "Cargo.toml" }}-{{ checksum "Cargo.lock" }}-{{ arch }}
|
||||
- cargo-v2-{{ checksum "rust-toolchain" }}-{{ checksum "Cargo.toml" }}-{{ checksum "Cargo.lock" }}-{{ arch }}
|
||||
- run: rustup install $(cat rust-toolchain)
|
||||
- run: rustup default $(cat rust-toolchain)
|
||||
- run: rustup component add --toolchain $(cat rust-toolchain) rustfmt
|
||||
@@ -60,7 +60,7 @@ jobs:
|
||||
paths:
|
||||
- crate
|
||||
- save_cache:
|
||||
key: cargo-v3-{{ checksum "rust-toolchain" }}-{{ checksum "Cargo.toml" }}-{{ checksum "Cargo.lock" }}-{{ arch }}
|
||||
key: cargo-v2-{{ checksum "rust-toolchain" }}-{{ checksum "Cargo.toml" }}-{{ checksum "Cargo.lock" }}-{{ arch }}
|
||||
paths:
|
||||
- "~/.cargo"
|
||||
- "~/.rustup"
|
||||
@@ -121,7 +121,7 @@ jobs:
|
||||
steps:
|
||||
- checkout
|
||||
- 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/
|
||||
- persist_to_workspace:
|
||||
root: workspace
|
||||
@@ -189,7 +189,7 @@ workflows:
|
||||
- upload_docs_wheels:
|
||||
requires:
|
||||
- build_test_docs_wheel
|
||||
- build_doxygen
|
||||
- build_doxygen
|
||||
- rustfmt:
|
||||
requires:
|
||||
- cargo_fetch
|
||||
|
||||
33
.github/workflows/rust.yml
vendored
33
.github/workflows/rust.yml
vendored
@@ -1,30 +1,15 @@
|
||||
name: CI
|
||||
name: Rust
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
|
||||
env:
|
||||
RUSTFLAGS: -Dwarnings
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: 3.7 python tests against core
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: nightly
|
||||
override: true
|
||||
components: rustfmt
|
||||
|
||||
- name: Setup python
|
||||
uses: actions/setup-python@v1
|
||||
with:
|
||||
python-version: 3.x
|
||||
architecture: x64
|
||||
|
||||
- run: bash ci_scripts/run-python.sh
|
||||
- uses: actions/checkout@v1
|
||||
- name: Build
|
||||
run: cargo build --verbose
|
||||
- name: Run tests
|
||||
run: cargo test --verbose
|
||||
|
||||
954
Cargo.lock
generated
954
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
15
Cargo.toml
15
Cargo.toml
@@ -15,13 +15,12 @@ hex = "0.3.2"
|
||||
sha2 = "0.8.0"
|
||||
rand = "0.6.5"
|
||||
smallvec = "0.6.9"
|
||||
reqwest = { version = "0.9.15", default-features = false, features = ["rustls-tls"] }
|
||||
reqwest = "0.9.15"
|
||||
num-derive = "0.2.5"
|
||||
num-traits = "0.2.6"
|
||||
lettre = { git = "https://github.com/deltachat/lettre", branch = "feat/rustls" }
|
||||
async-imap = "0.1"
|
||||
async-tls = "0.6"
|
||||
async-std = { version = "1.0", features = ["unstable"] }
|
||||
native-tls = "0.2.3"
|
||||
lettre = { git = "https://github.com/deltachat/lettre", branch = "master" }
|
||||
imap = { git = "https://github.com/deltachat/rust-imap", branch = "master" }
|
||||
base64 = "0.10"
|
||||
charset = "0.1"
|
||||
percent-encoding = "2.0"
|
||||
@@ -50,10 +49,6 @@ bitflags = "1.1.0"
|
||||
jetscii = "0.4.4"
|
||||
debug_stub_derive = "0.3.0"
|
||||
sanitize-filename = "0.2.1"
|
||||
stop-token = { version = "0.1.1", features = ["unstable"] }
|
||||
rustls = "0.16.0"
|
||||
webpki-roots = "0.18.0"
|
||||
webpki = "0.21.0"
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile = "3.0"
|
||||
@@ -79,6 +74,6 @@ path = "examples/repl/main.rs"
|
||||
|
||||
[features]
|
||||
default = ["nightly", "ringbuf"]
|
||||
vendored = []
|
||||
vendored = ["native-tls/vendored", "reqwest/default-tls-vendored"]
|
||||
nightly = ["pgp/nightly"]
|
||||
ringbuf = ["pgp/ringbuf"]
|
||||
|
||||
@@ -5,6 +5,16 @@ RUN echo /usr/local/lib64 > /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 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
|
||||
ADD deps/build_perl.sh /builder/build_perl.sh
|
||||
RUN mkdir tmp1 && cd tmp1 && bash /builder/build_perl.sh && cd .. && rm -r tmp1
|
||||
@@ -13,12 +23,3 @@ RUN mkdir tmp1 && cd tmp1 && bash /builder/build_perl.sh && cd .. && rm -r tmp1
|
||||
ADD deps/build_openssl.sh /builder/build_openssl.sh
|
||||
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
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
#!/bin/bash
|
||||
|
||||
PERL_VERSION=5.30.0
|
||||
# PERL_SHA256=7e929f64d4cb0e9d1159d4a59fc89394e27fa1f7004d0836ca0d514685406ea8
|
||||
PERL_VERSION=5.28.0
|
||||
PERL_SHA256=7e929f64d4cb0e9d1159d4a59fc89394e27fa1f7004d0836ca0d514685406ea8
|
||||
curl -O https://www.cpan.org/src/5.0/perl-${PERL_VERSION}.tar.gz
|
||||
# echo "${PERL_SHA256} perl-${PERL_VERSION}.tar.gz" | sha256sum -c -
|
||||
tar -xzf perl-${PERL_VERSION}.tar.gz
|
||||
cd perl-${PERL_VERSION}
|
||||
echo "${PERL_SHA256} perl-${PERL_VERSION}.tar.gz" | sha256sum -c -
|
||||
tar xzf perl-${PERL_VERSION}.tar.gz
|
||||
cd perl-${PERL_VERSION}
|
||||
|
||||
./Configure -de
|
||||
make
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e -x
|
||||
set -e -x
|
||||
|
||||
# Install Rust
|
||||
curl https://sh.rustup.rs -sSf | sh -s -- --default-toolchain nightly-2019-09-12 -y
|
||||
# Install Rust
|
||||
curl https://sh.rustup.rs -sSf | sh -s -- --default-toolchain nightly-2019-07-10 -y
|
||||
export PATH=/root/.cargo/bin:$PATH
|
||||
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/
|
||||
|
||||
@@ -13,6 +13,7 @@ export TOXWORKDIR=.docker-tox
|
||||
|
||||
export PATH=/root/.cargo/bin:$PATH
|
||||
cargo build --release -p deltachat_ffi
|
||||
# cargo test --all --all-features
|
||||
|
||||
# Statically link against libdeltachat.a.
|
||||
export DCC_RS_DEV=$(pwd)
|
||||
@@ -21,27 +22,36 @@ export DCC_RS_DEV=$(pwd)
|
||||
# needed by tox below.
|
||||
export PATH=$PATH:/opt/python/cp35-cp35m/bin
|
||||
export PYTHONDONTWRITEBYTECODE=1
|
||||
|
||||
pushd python
|
||||
# prepare a clean tox run
|
||||
rm -rf tests/__pycache__
|
||||
rm -rf src/deltachat/__pycache__
|
||||
export PYTHONDONTWRITEBYTECODE=1
|
||||
|
||||
# run tox. The circle-ci project env-var-setting DCC_PY_LIVECONFIG
|
||||
# allows running of "liveconfig" tests but for speed reasons
|
||||
# we run them only for the highest python version we support
|
||||
|
||||
# we split out qr-tests run to minimize likelyness of flaky tests
|
||||
# (some qr tests are pretty heavy in terms of send/received
|
||||
# messages and rust's imap code likely has concurrency problems)
|
||||
tox --workdir "$TOXWORKDIR" -e py37 -- --reruns 3 -k "not qr"
|
||||
tox --workdir "$TOXWORKDIR" -e py37 -- --reruns 3 -k "qr"
|
||||
unset DCC_PY_LIVECONFIG
|
||||
#tox --workdir "$TOXWORKDIR" -p4 -e lint,py35,py36,doc
|
||||
#tox --workdir "$TOXWORKDIR" -e auditwheels
|
||||
pushd /bin
|
||||
ln -s /opt/python/cp27-cp27m/bin/python2.7
|
||||
ln -s /opt/python/cp36-cp36m/bin/python3.6
|
||||
ln -s /opt/python/cp37-cp37m/bin/python3.7
|
||||
popd
|
||||
|
||||
if [ -n "$TESTS" ]; then
|
||||
|
||||
pushd python
|
||||
# prepare a clean tox run
|
||||
rm -rf tests/__pycache__
|
||||
rm -rf src/deltachat/__pycache__
|
||||
export PYTHONDONTWRITEBYTECODE=1
|
||||
|
||||
# run tox. The circle-ci project env-var-setting DCC_PY_LIVECONFIG
|
||||
# allows running of "liveconfig" tests but for speed reasons
|
||||
# we run them only for the highest python version we support
|
||||
|
||||
# we split out qr-tests run to minimize likelyness of flaky tests
|
||||
# (some qr tests are pretty heavy in terms of send/received
|
||||
# messages and rust's imap code likely has concurrency problems)
|
||||
tox --workdir "$TOXWORKDIR" -e py37 -- --reruns 3 -k "not qr"
|
||||
tox --workdir "$TOXWORKDIR" -e py37 -- --reruns 3 -k "qr"
|
||||
unset DCC_PY_LIVECONFIG
|
||||
tox --workdir "$TOXWORKDIR" -p4 -e lint,py35,py36,doc
|
||||
tox --workdir "$TOXWORKDIR" -e auditwheels
|
||||
popd
|
||||
fi
|
||||
|
||||
|
||||
# if [ -n "$DOCS" ]; then
|
||||
# echo -----------------------
|
||||
# echo generating python docs
|
||||
|
||||
@@ -1113,6 +1113,28 @@ void dc_set_draft (dc_context_t* context, uint32_t ch
|
||||
uint32_t dc_add_device_msg (dc_context_t* context, dc_msg_t* msg);
|
||||
|
||||
|
||||
/**
|
||||
* Add a message only one time to the device-chat.
|
||||
* The device-message is defined by a name.
|
||||
* If a message with the same name was added before,
|
||||
* the message is not added again.
|
||||
* Use dc_add_device_msg() to add device-messages unconditionally.
|
||||
*
|
||||
* Sends the event #DC_EVENT_MSGS_CHANGED on success.
|
||||
*
|
||||
* @memberof dc_context_t
|
||||
* @param context The context as created by dc_context_new().
|
||||
* @param label A unique name for the message to add.
|
||||
* The label is typically not displayed to the user and
|
||||
* must be created from the characters `A-Z`, `a-z`, `0-9`, `_` or `-`.
|
||||
* @param msg Message to be added to the device-chat.
|
||||
* The message appears to the user as an incoming message.
|
||||
* @return The ID of the added message,
|
||||
* this might be the id of an older message with the same name.
|
||||
*/
|
||||
uint32_t dc_add_device_msg_once (dc_context_t* context, const char* label, dc_msg_t* msg);
|
||||
|
||||
|
||||
/**
|
||||
* Get draft for a chat, if any.
|
||||
* See dc_set_draft() for more details about drafts.
|
||||
|
||||
@@ -828,6 +828,27 @@ pub unsafe extern "C" fn dc_add_device_msg(context: *mut dc_context_t, msg: *mut
|
||||
.unwrap_or(0)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_add_device_msg_once(
|
||||
context: *mut dc_context_t,
|
||||
label: *const libc::c_char,
|
||||
msg: *mut dc_msg_t,
|
||||
) -> u32 {
|
||||
if context.is_null() || label.is_null() || msg.is_null() {
|
||||
eprintln!("ignoring careless call to dc_add_device_msg_once()");
|
||||
return 0;
|
||||
}
|
||||
let ffi_context = &mut *context;
|
||||
let ffi_msg = &mut *msg;
|
||||
ffi_context
|
||||
.with_inner(|ctx| {
|
||||
chat::add_device_msg_once(ctx, &to_string_lossy(label), &mut ffi_msg.message)
|
||||
.unwrap_or_log_default(ctx, "Failed to add device message once")
|
||||
})
|
||||
.map(|msg_id| msg_id.to_u32())
|
||||
.unwrap_or(0)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_get_draft(context: *mut dc_context_t, chat_id: u32) -> *mut dc_msg_t {
|
||||
if context.is_null() {
|
||||
|
||||
@@ -21,7 +21,7 @@ class TestOfflineAccountBasic:
|
||||
d = ac1.get_info()
|
||||
assert d["arch"]
|
||||
assert d["number_of_chats"] == "0"
|
||||
assert d["bcc_self"] == "1"
|
||||
assert d["bcc_self"] == "0"
|
||||
|
||||
def test_is_not_configured(self, acfactory):
|
||||
ac1 = acfactory.get_unconfigured_account()
|
||||
@@ -43,7 +43,7 @@ class TestOfflineAccountBasic:
|
||||
def test_has_bccself(self, acfactory):
|
||||
ac1 = acfactory.get_unconfigured_account()
|
||||
assert "bcc_self" in ac1.get_config("sys.config_keys").split()
|
||||
assert ac1.get_config("bcc_self") == "1"
|
||||
assert ac1.get_config("bcc_self") == "0"
|
||||
|
||||
def test_selfcontact_if_unconfigured(self, acfactory):
|
||||
ac1 = acfactory.get_unconfigured_account()
|
||||
@@ -405,6 +405,9 @@ class TestOnlineAccount:
|
||||
wait_successful_IMAP_SMTP_connection(ac1)
|
||||
wait_configuration_progress(ac1, 1000)
|
||||
|
||||
lp.sec("ac1: setting bcc_self=1")
|
||||
ac1.set_config("bcc_self", "1")
|
||||
|
||||
lp.sec("send out message with bcc to ourselves")
|
||||
msg_out = chat.send_text("message2")
|
||||
ev = ac1._evlogger.get_matching("DC_EVENT_MSGS_CHANGED")
|
||||
@@ -675,6 +678,7 @@ class TestOnlineAccount:
|
||||
assert len(messages) == 1
|
||||
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
|
||||
# because get_latest_backupfile() shall return the latest backup
|
||||
# from a UI it's unlikely anyone manages to export two
|
||||
@@ -895,7 +899,7 @@ class TestOnlineConfigureFails:
|
||||
ac1.start_threads()
|
||||
wait_configuration_progress(ac1, 500)
|
||||
ev1 = ac1._evlogger.get_matching("DC_EVENT_ERROR_NETWORK")
|
||||
assert "cannot login" in ev1[2].lower()
|
||||
assert "authentication failed" in ev1[2].lower()
|
||||
wait_configuration_progress(ac1, 0, 0)
|
||||
|
||||
def test_invalid_user(self, acfactory):
|
||||
@@ -904,7 +908,7 @@ class TestOnlineConfigureFails:
|
||||
ac1.start_threads()
|
||||
wait_configuration_progress(ac1, 500)
|
||||
ev1 = ac1._evlogger.get_matching("DC_EVENT_ERROR_NETWORK")
|
||||
assert "cannot login" in ev1[2].lower()
|
||||
assert "authentication failed" in ev1[2].lower()
|
||||
wait_configuration_progress(ac1, 0, 0)
|
||||
|
||||
def test_invalid_domain(self, acfactory):
|
||||
|
||||
@@ -1 +1 @@
|
||||
nightly-2019-11-06
|
||||
nightly-2019-08-13
|
||||
|
||||
214
src/chat.rs
214
src/chat.rs
@@ -1,6 +1,7 @@
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use itertools::Itertools;
|
||||
use num_traits::FromPrimitive;
|
||||
|
||||
use crate::blob::{BlobErrorKind, BlobObject};
|
||||
use crate::chatlist::*;
|
||||
@@ -796,7 +797,8 @@ pub fn is_contact_in_chat(context: &Context, chat_id: u32, contact_id: u32) -> b
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
// Should return Result
|
||||
// note that unarchive() is not the same as archive(false) -
|
||||
// eg. unarchive() does not send events as done for archive(false).
|
||||
pub fn unarchive(context: &Context, chat_id: u32) -> Result<(), Error> {
|
||||
sql::execute(
|
||||
context,
|
||||
@@ -1012,7 +1014,8 @@ pub fn get_chat_msgs(
|
||||
Ok(ret)
|
||||
};
|
||||
let success = if chat_id == DC_CHAT_ID_DEADDROP {
|
||||
let show_emails = context.get_config_int(Config::ShowEmails);
|
||||
let show_emails =
|
||||
ShowEmails::from_i32(context.get_config_int(Config::ShowEmails)).unwrap_or_default();
|
||||
context.sql.query_map(
|
||||
concat!(
|
||||
"SELECT m.id AS id, m.timestamp AS timestamp",
|
||||
@@ -1029,7 +1032,7 @@ pub fn get_chat_msgs(
|
||||
" AND m.msgrmsg>=?",
|
||||
" ORDER BY m.timestamp,m.id;"
|
||||
),
|
||||
params![if show_emails == 2 { 0 } else { 1 }],
|
||||
params![if show_emails == ShowEmails::All { 0 } else { 1 }],
|
||||
process_row,
|
||||
process_rows,
|
||||
)
|
||||
@@ -1927,16 +1930,50 @@ pub fn get_chat_id_by_grpid(context: &Context, grpid: impl AsRef<str>) -> (u32,
|
||||
}
|
||||
|
||||
pub fn add_device_msg(context: &Context, msg: &mut Message) -> Result<MsgId, Error> {
|
||||
add_device_msg_maybe_labelled(context, None, msg)
|
||||
}
|
||||
|
||||
pub fn add_device_msg_once(
|
||||
context: &Context,
|
||||
label: &str,
|
||||
msg: &mut Message,
|
||||
) -> Result<MsgId, Error> {
|
||||
add_device_msg_maybe_labelled(context, Some(label), msg)
|
||||
}
|
||||
|
||||
fn add_device_msg_maybe_labelled(
|
||||
context: &Context,
|
||||
label: Option<&str>,
|
||||
msg: &mut Message,
|
||||
) -> Result<MsgId, Error> {
|
||||
let (chat_id, _blocked) =
|
||||
create_or_lookup_by_contact_id(context, DC_CONTACT_ID_DEVICE, Blocked::Not)?;
|
||||
let rfc724_mid = dc_create_outgoing_rfc724_mid(None, "@device");
|
||||
|
||||
// chat_id has an sql-index so it makes sense to add this although redundant
|
||||
if let Some(label) = label {
|
||||
if let Ok(msg_id) = context.sql.query_row(
|
||||
"SELECT id FROM msgs WHERE chat_id=? AND label=?",
|
||||
params![chat_id, label],
|
||||
|row| {
|
||||
let msg_id: MsgId = row.get(0)?;
|
||||
Ok(msg_id)
|
||||
},
|
||||
) {
|
||||
info!(
|
||||
context,
|
||||
"device-message {} already exist as {}", label, msg_id
|
||||
);
|
||||
return Ok(msg_id);
|
||||
}
|
||||
}
|
||||
|
||||
prepare_msg_blob(context, msg)?;
|
||||
unarchive(context, chat_id)?;
|
||||
|
||||
context.sql.execute(
|
||||
"INSERT INTO msgs (chat_id,from_id,to_id, timestamp,type,state, txt,param,rfc724_mid) \
|
||||
VALUES (?,?,?, ?,?,?, ?,?,?);",
|
||||
"INSERT INTO msgs (chat_id,from_id,to_id, timestamp,type,state, txt,param,rfc724_mid,label) \
|
||||
VALUES (?,?,?, ?,?,?, ?,?,?,?);",
|
||||
params![
|
||||
chat_id,
|
||||
DC_CONTACT_ID_DEVICE,
|
||||
@@ -1947,12 +1984,19 @@ pub fn add_device_msg(context: &Context, msg: &mut Message) -> Result<MsgId, Err
|
||||
msg.text.as_ref().map_or("", String::as_str),
|
||||
msg.param.to_string(),
|
||||
rfc724_mid,
|
||||
label.unwrap_or_default(),
|
||||
],
|
||||
)?;
|
||||
|
||||
let row_id = sql::get_rowid(context, &context.sql, "msgs", "rfc724_mid", &rfc724_mid);
|
||||
let msg_id = MsgId::new(row_id);
|
||||
context.call_cb(Event::IncomingMsg { chat_id, msg_id });
|
||||
info!(
|
||||
context,
|
||||
"device-message {} added as {}",
|
||||
label.unwrap_or("without label"),
|
||||
msg_id
|
||||
);
|
||||
|
||||
Ok(msg_id)
|
||||
}
|
||||
@@ -2034,4 +2078,164 @@ mod tests {
|
||||
let added = add_contact_to_chat_ex(&t.ctx, chat_id, DC_CONTACT_ID_SELF, false).unwrap();
|
||||
assert_eq!(added, false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_self_talk() {
|
||||
let t = dummy_context();
|
||||
let chat_id = create_by_contact_id(&t.ctx, DC_CONTACT_ID_SELF).unwrap();
|
||||
assert_eq!(DC_CONTACT_ID_SELF, 1);
|
||||
assert!(chat_id > DC_CHAT_ID_LAST_SPECIAL);
|
||||
let chat = Chat::load_from_db(&t.ctx, chat_id).unwrap();
|
||||
assert_eq!(chat.id, chat_id);
|
||||
assert!(chat.is_self_talk());
|
||||
assert!(!chat.archived);
|
||||
assert!(!chat.is_device_talk());
|
||||
assert!(chat.can_send());
|
||||
assert_eq!(chat.name, t.ctx.stock_str(StockMessage::SelfMsg));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_deaddrop_chat() {
|
||||
let t = dummy_context();
|
||||
let chat = Chat::load_from_db(&t.ctx, DC_CHAT_ID_DEADDROP).unwrap();
|
||||
assert_eq!(DC_CHAT_ID_DEADDROP, 1);
|
||||
assert_eq!(chat.id, DC_CHAT_ID_DEADDROP);
|
||||
assert!(!chat.is_self_talk());
|
||||
assert!(!chat.archived);
|
||||
assert!(!chat.is_device_talk());
|
||||
assert!(!chat.can_send());
|
||||
assert_eq!(chat.name, t.ctx.stock_str(StockMessage::DeadDrop));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add_device_msg() {
|
||||
let t = test_context(Some(Box::new(logging_cb)));
|
||||
|
||||
// add two device-messages
|
||||
let mut msg1 = Message::new(Viewtype::Text);
|
||||
msg1.text = Some("first message".to_string());
|
||||
let msg1_id = add_device_msg(&t.ctx, &mut msg1);
|
||||
assert!(msg1_id.is_ok());
|
||||
|
||||
let mut msg2 = Message::new(Viewtype::Text);
|
||||
msg2.text = Some("second message".to_string());
|
||||
let msg2_id = add_device_msg(&t.ctx, &mut msg2);
|
||||
assert!(msg2_id.is_ok());
|
||||
assert_ne!(msg1_id.as_ref().unwrap(), msg2_id.as_ref().unwrap());
|
||||
|
||||
// check added messages
|
||||
let msg1 = message::Message::load_from_db(&t.ctx, msg1_id.unwrap());
|
||||
assert!(msg1.is_ok());
|
||||
let msg1 = msg1.unwrap();
|
||||
assert_eq!(msg1.text.as_ref().unwrap(), "first message");
|
||||
assert_eq!(msg1.from_id, DC_CONTACT_ID_DEVICE);
|
||||
assert_eq!(msg1.to_id, DC_CONTACT_ID_SELF);
|
||||
assert!(!msg1.is_info());
|
||||
assert!(!msg1.is_setupmessage());
|
||||
|
||||
let msg2 = message::Message::load_from_db(&t.ctx, msg2_id.unwrap());
|
||||
assert!(msg2.is_ok());
|
||||
let msg2 = msg2.unwrap();
|
||||
assert_eq!(msg2.text.as_ref().unwrap(), "second message");
|
||||
|
||||
// check device chat
|
||||
assert_eq!(get_msg_cnt(&t.ctx, msg2.chat_id), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add_device_msg_once() {
|
||||
let t = test_context(Some(Box::new(logging_cb)));
|
||||
|
||||
// add two device-messages with the same label (second attempt is not added)
|
||||
let mut msg1 = Message::new(Viewtype::Text);
|
||||
msg1.text = Some("first message".to_string());
|
||||
let msg1_id = add_device_msg_once(&t.ctx, "any-label", &mut msg1);
|
||||
assert!(msg1_id.is_ok());
|
||||
|
||||
let mut msg2 = Message::new(Viewtype::Text);
|
||||
msg2.text = Some("second message".to_string());
|
||||
let msg2_id = add_device_msg_once(&t.ctx, "any-label", &mut msg2);
|
||||
assert!(msg2_id.is_ok());
|
||||
assert_eq!(msg1_id.as_ref().unwrap(), msg2_id.as_ref().unwrap());
|
||||
|
||||
// check added message
|
||||
let msg2 = message::Message::load_from_db(&t.ctx, msg2_id.unwrap());
|
||||
assert!(msg2.is_ok());
|
||||
let msg2 = msg2.unwrap();
|
||||
assert_eq!(msg1_id.unwrap(), msg2.id);
|
||||
assert_eq!(msg2.text.as_ref().unwrap(), "first message");
|
||||
assert_eq!(msg2.from_id, DC_CONTACT_ID_DEVICE);
|
||||
assert_eq!(msg2.to_id, DC_CONTACT_ID_SELF);
|
||||
assert!(!msg2.is_info());
|
||||
assert!(!msg2.is_setupmessage());
|
||||
|
||||
// check device chat
|
||||
let chat_id = msg2.chat_id;
|
||||
assert_eq!(get_msg_cnt(&t.ctx, chat_id), 1);
|
||||
assert!(chat_id > DC_CHAT_ID_LAST_SPECIAL);
|
||||
let chat = Chat::load_from_db(&t.ctx, chat_id);
|
||||
assert!(chat.is_ok());
|
||||
let chat = chat.unwrap();
|
||||
assert!(chat.is_device_talk());
|
||||
assert!(!chat.is_self_talk());
|
||||
assert!(!chat.can_send());
|
||||
assert_eq!(chat.name, t.ctx.stock_str(StockMessage::DeviceMessages));
|
||||
assert!(chat.get_profile_image(&t.ctx).is_some());
|
||||
}
|
||||
|
||||
fn chatlist_len(ctx: &Context, listflags: usize) -> usize {
|
||||
Chatlist::try_load(ctx, listflags, None, None)
|
||||
.unwrap()
|
||||
.len()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_archive() {
|
||||
// create two chats
|
||||
let t = dummy_context();
|
||||
let mut msg = Message::new(Viewtype::Text);
|
||||
msg.text = Some("foo".to_string());
|
||||
let msg_id = add_device_msg(&t.ctx, &mut msg).unwrap();
|
||||
let chat_id1 = message::Message::load_from_db(&t.ctx, msg_id)
|
||||
.unwrap()
|
||||
.chat_id;
|
||||
let chat_id2 = create_by_contact_id(&t.ctx, DC_CONTACT_ID_SELF).unwrap();
|
||||
assert!(chat_id1 > DC_CHAT_ID_LAST_SPECIAL);
|
||||
assert!(chat_id2 > DC_CHAT_ID_LAST_SPECIAL);
|
||||
assert_eq!(get_chat_cnt(&t.ctx), 2);
|
||||
assert_eq!(chatlist_len(&t.ctx, 0), 2);
|
||||
assert_eq!(chatlist_len(&t.ctx, DC_GCL_NO_SPECIALS), 2);
|
||||
assert_eq!(chatlist_len(&t.ctx, DC_GCL_ARCHIVED_ONLY), 0);
|
||||
assert_eq!(DC_GCL_ARCHIVED_ONLY, 0x01);
|
||||
assert_eq!(DC_GCL_NO_SPECIALS, 0x02);
|
||||
|
||||
// archive first chat
|
||||
assert!(archive(&t.ctx, chat_id1, true).is_ok());
|
||||
assert!(Chat::load_from_db(&t.ctx, chat_id1).unwrap().is_archived());
|
||||
assert!(!Chat::load_from_db(&t.ctx, chat_id2).unwrap().is_archived());
|
||||
assert_eq!(get_chat_cnt(&t.ctx), 2);
|
||||
assert_eq!(chatlist_len(&t.ctx, 0), 2); // including DC_CHAT_ID_ARCHIVED_LINK now
|
||||
assert_eq!(chatlist_len(&t.ctx, DC_GCL_NO_SPECIALS), 1);
|
||||
assert_eq!(chatlist_len(&t.ctx, DC_GCL_ARCHIVED_ONLY), 1);
|
||||
|
||||
// archive second chat
|
||||
assert!(archive(&t.ctx, chat_id2, true).is_ok());
|
||||
assert!(Chat::load_from_db(&t.ctx, chat_id1).unwrap().is_archived());
|
||||
assert!(Chat::load_from_db(&t.ctx, chat_id2).unwrap().is_archived());
|
||||
assert_eq!(get_chat_cnt(&t.ctx), 2);
|
||||
assert_eq!(chatlist_len(&t.ctx, 0), 1); // only DC_CHAT_ID_ARCHIVED_LINK now
|
||||
assert_eq!(chatlist_len(&t.ctx, DC_GCL_NO_SPECIALS), 0);
|
||||
assert_eq!(chatlist_len(&t.ctx, DC_GCL_ARCHIVED_ONLY), 2);
|
||||
|
||||
// archive already archived first chat, unarchive second chat two times
|
||||
assert!(archive(&t.ctx, chat_id1, true).is_ok());
|
||||
assert!(archive(&t.ctx, chat_id2, false).is_ok());
|
||||
assert!(archive(&t.ctx, chat_id2, false).is_ok());
|
||||
assert!(Chat::load_from_db(&t.ctx, chat_id1).unwrap().is_archived());
|
||||
assert!(!Chat::load_from_db(&t.ctx, chat_id2).unwrap().is_archived());
|
||||
assert_eq!(get_chat_cnt(&t.ctx), 2);
|
||||
assert_eq!(chatlist_len(&t.ctx, 0), 2);
|
||||
assert_eq!(chatlist_len(&t.ctx, DC_GCL_NO_SPECIALS), 1);
|
||||
assert_eq!(chatlist_len(&t.ctx, DC_GCL_ARCHIVED_ONLY), 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ pub enum Config {
|
||||
Displayname,
|
||||
Selfstatus,
|
||||
Selfavatar,
|
||||
#[strum(props(default = "1"))]
|
||||
#[strum(props(default = "0"))]
|
||||
BccSelf,
|
||||
#[strum(props(default = "1"))]
|
||||
E2eeEnabled,
|
||||
|
||||
@@ -569,7 +569,7 @@ fn try_smtp_one_param(context: &Context, param: &LoginParam) -> Option<bool> {
|
||||
pub fn dc_connect_to_configured_imap(context: &Context, imap: &Imap) -> libc::c_int {
|
||||
let mut ret_connected = 0;
|
||||
|
||||
if async_std::task::block_on(async move { imap.is_connected().await }) {
|
||||
if imap.is_connected() {
|
||||
ret_connected = 1
|
||||
} else if !context.sql.get_raw_config_bool(context, "configured") {
|
||||
warn!(context, "Not configured, cannot connect.",);
|
||||
|
||||
@@ -498,7 +498,7 @@ fn decrypt_if_autocrypt_message(
|
||||
public_keyring_for_validate: &Keyring,
|
||||
ret_valid_signatures: &mut HashSet<String>,
|
||||
ret_gossip_headers: *mut *mut mailimf_fields,
|
||||
) -> Result<bool> {
|
||||
) -> Result<(bool)> {
|
||||
/* The returned bool is true if we detected an Autocrypt-encrypted
|
||||
message and successfully decrypted it. Decryption then modifies the
|
||||
passed in mime structure in place. The returned bool is false
|
||||
|
||||
1636
src/imap.rs
1636
src/imap.rs
File diff suppressed because it is too large
Load Diff
@@ -1,294 +0,0 @@
|
||||
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_std::sync::Arc;
|
||||
use async_tls::client::TlsStream;
|
||||
|
||||
use crate::login_param::{dc_build_tls_config, 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_config = dc_build_tls_config(certificate_checks);
|
||||
let tls_connector: async_tls::TlsConnector = Arc::new(tls_config).into();
|
||||
let tls_stream = tls_connector.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(())
|
||||
}
|
||||
}
|
||||
21
src/imex.rs
21
src/imex.rs
@@ -155,6 +155,11 @@ fn do_initiate_key_transfer(context: &Context) -> Result<String> {
|
||||
}
|
||||
}
|
||||
}
|
||||
// no maybe_add_bcc_self_device_msg() here.
|
||||
// the ui shows the dialog with the setup code on this device,
|
||||
// it would be too much noise to have two things popping up at the same time.
|
||||
// maybe_add_bcc_self_device_msg() is called on the other device
|
||||
// once the transfer is completed.
|
||||
Ok(setup_code)
|
||||
}
|
||||
|
||||
@@ -230,6 +235,21 @@ pub fn create_setup_code(_context: &Context) -> String {
|
||||
ret
|
||||
}
|
||||
|
||||
fn maybe_add_bcc_self_device_msg(context: &Context) -> Result<()> {
|
||||
if !context.sql.get_raw_config_bool(context, "bcc_self") {
|
||||
let mut msg = Message::new(Viewtype::Text);
|
||||
// TODO: define this as a stockstring once the wording is settled.
|
||||
msg.text = Some(
|
||||
"It seems you are using multiple devices with Delta Chat. Great!\n\n\
|
||||
If you also want to synchronize outgoing messages accross all devices, \
|
||||
go to the settings and enable \"Send copy to self\"."
|
||||
.to_string(),
|
||||
);
|
||||
chat::add_device_msg_once(context, "bcc-self-hint", &mut msg)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn continue_key_transfer(context: &Context, msg_id: MsgId, setup_code: &str) -> Result<()> {
|
||||
ensure!(!msg_id.is_special(), "wrong id");
|
||||
|
||||
@@ -244,6 +264,7 @@ pub fn continue_key_transfer(context: &Context, msg_id: MsgId, setup_code: &str)
|
||||
let sc = normalize_setup_code(setup_code);
|
||||
let armored_key = decrypt_setup_file(context, &sc, file)?;
|
||||
set_self_key(context, &armored_key, true, true)?;
|
||||
maybe_add_bcc_self_device_msg(context)?;
|
||||
|
||||
Ok(())
|
||||
} else {
|
||||
|
||||
18
src/job.rs
18
src/job.rs
@@ -245,10 +245,10 @@ impl Job {
|
||||
&dest_folder,
|
||||
&mut dest_uid,
|
||||
) {
|
||||
ImapActionResult::RetryLater => {
|
||||
ImapResult::RetryLater => {
|
||||
self.try_again_later(3i32, None);
|
||||
}
|
||||
ImapActionResult::Success => {
|
||||
ImapResult::Success => {
|
||||
message::update_server_uid(
|
||||
context,
|
||||
&msg.rfc724_mid,
|
||||
@@ -256,7 +256,7 @@ impl Job {
|
||||
dest_uid,
|
||||
);
|
||||
}
|
||||
ImapActionResult::Failed | ImapActionResult::AlreadyDone => {}
|
||||
ImapResult::Failed | ImapResult::AlreadyDone => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -280,7 +280,7 @@ impl Job {
|
||||
let mid = msg.rfc724_mid;
|
||||
let server_folder = msg.server_folder.as_ref().unwrap();
|
||||
let res = inbox.delete_msg(context, &mid, server_folder, &mut msg.server_uid);
|
||||
if res == ImapActionResult::RetryLater {
|
||||
if res == ImapResult::RetryLater {
|
||||
self.try_again_later(-1i32, None);
|
||||
return;
|
||||
}
|
||||
@@ -313,11 +313,11 @@ impl Job {
|
||||
if let Ok(msg) = Message::load_from_db(context, MsgId::new(self.foreign_id)) {
|
||||
let folder = msg.server_folder.as_ref().unwrap();
|
||||
match inbox.set_seen(context, folder, msg.server_uid) {
|
||||
ImapActionResult::RetryLater => {
|
||||
ImapResult::RetryLater => {
|
||||
self.try_again_later(3i32, None);
|
||||
}
|
||||
ImapActionResult::AlreadyDone => {}
|
||||
ImapActionResult::Success | ImapActionResult::Failed => {
|
||||
ImapResult::AlreadyDone => {}
|
||||
ImapResult::Success | ImapResult::Failed => {
|
||||
// XXX the message might just have been moved
|
||||
// we want to send out an MDN anyway
|
||||
// The job will not be retried so locally
|
||||
@@ -343,7 +343,7 @@ impl Job {
|
||||
.to_string();
|
||||
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);
|
||||
return;
|
||||
}
|
||||
@@ -361,7 +361,7 @@ impl Job {
|
||||
.get_raw_config(context, "configured_mvbox_folder");
|
||||
if let Some(dest_folder) = dest_folder {
|
||||
let mut dest_uid = 0;
|
||||
if ImapActionResult::RetryLater
|
||||
if ImapResult::RetryLater
|
||||
== inbox.mv(context, &folder, uid, &dest_folder, &mut dest_uid)
|
||||
{
|
||||
self.try_again_later(3, None);
|
||||
|
||||
@@ -107,17 +107,12 @@ impl JobThread {
|
||||
}
|
||||
|
||||
fn connect_to_imap(&self, context: &Context) -> bool {
|
||||
if async_std::task::block_on(async move { self.imap.is_connected().await }) {
|
||||
if self.imap.is_connected() {
|
||||
return true;
|
||||
}
|
||||
let watch_folder_name = match context.sql.get_raw_config(context, self.folder_config_name) {
|
||||
Some(name) => name,
|
||||
None => {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
let ret_connected = dc_connect_to_configured_imap(context, &self.imap) != 0;
|
||||
let mut ret_connected = dc_connect_to_configured_imap(context, &self.imap) != 0;
|
||||
|
||||
if ret_connected {
|
||||
if context
|
||||
.sql
|
||||
@@ -128,7 +123,12 @@ impl JobThread {
|
||||
self.imap.configure_folders(context, 0x1);
|
||||
}
|
||||
|
||||
self.imap.set_watch_folder(watch_folder_name);
|
||||
if let Some(mvbox_name) = context.sql.get_raw_config(context, self.folder_config_name) {
|
||||
self.imap.set_watch_folder(mvbox_name);
|
||||
} else {
|
||||
self.imap.disconnect(context);
|
||||
ret_connected = false;
|
||||
}
|
||||
}
|
||||
|
||||
ret_connected
|
||||
@@ -170,18 +170,10 @@ impl JobThread {
|
||||
}
|
||||
}
|
||||
|
||||
if self.connect_to_imap(context) {
|
||||
info!(context, "{}-IDLE started...", self.name,);
|
||||
self.imap.idle(context);
|
||||
info!(context, "{}-IDLE ended.", self.name);
|
||||
} else {
|
||||
// It's probably wrong that the thread even runs
|
||||
// but let's call fake_idle and tell it to not try network at all.
|
||||
// (once we move to rust-managed threads this problem goes away)
|
||||
info!(context, "{}-IDLE not connected, fake-idling", self.name);
|
||||
async_std::task::block_on(async move { self.imap.fake_idle(context, false).await });
|
||||
info!(context, "{}-IDLE fake-idling finished", self.name);
|
||||
}
|
||||
self.connect_to_imap(context);
|
||||
info!(context, "{}-IDLE started...", self.name,);
|
||||
self.imap.idle(context);
|
||||
info!(context, "{}-IDLE ended.", self.name);
|
||||
|
||||
self.state.0.lock().unwrap().using_handle = false;
|
||||
}
|
||||
|
||||
@@ -40,7 +40,6 @@ pub mod contact;
|
||||
pub mod context;
|
||||
mod e2ee;
|
||||
mod imap;
|
||||
mod imap_client;
|
||||
pub mod imex;
|
||||
pub mod job;
|
||||
mod job_thread;
|
||||
|
||||
@@ -3,10 +3,6 @@ use std::fmt;
|
||||
|
||||
use crate::context::Context;
|
||||
use crate::error::Error;
|
||||
use async_std::sync::Arc;
|
||||
use rustls;
|
||||
use webpki;
|
||||
use webpki_roots;
|
||||
|
||||
#[derive(Copy, Clone, Debug, Display, FromPrimitive)]
|
||||
#[repr(i32)]
|
||||
@@ -255,49 +251,27 @@ fn get_readable_flags(flags: i32) -> String {
|
||||
res
|
||||
}
|
||||
|
||||
pub struct NoCertificateVerification {}
|
||||
|
||||
impl rustls::ServerCertVerifier for NoCertificateVerification {
|
||||
fn verify_server_cert(
|
||||
&self,
|
||||
_roots: &rustls::RootCertStore,
|
||||
_presented_certs: &[rustls::Certificate],
|
||||
_dns_name: webpki::DNSNameRef<'_>,
|
||||
_ocsp: &[u8],
|
||||
) -> Result<rustls::ServerCertVerified, rustls::TLSError> {
|
||||
Ok(rustls::ServerCertVerified::assertion())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dc_build_tls_config(certificate_checks: CertificateChecks) -> rustls::ClientConfig {
|
||||
let mut config = rustls::ClientConfig::new();
|
||||
config
|
||||
.root_store
|
||||
.add_server_trust_anchors(&webpki_roots::TLS_SERVER_ROOTS);
|
||||
|
||||
pub fn dc_build_tls(
|
||||
certificate_checks: CertificateChecks,
|
||||
) -> Result<native_tls::TlsConnector, native_tls::Error> {
|
||||
let mut tls_builder = native_tls::TlsConnector::builder();
|
||||
match certificate_checks {
|
||||
CertificateChecks::Strict => {}
|
||||
CertificateChecks::Automatic => {
|
||||
// Same as AcceptInvalidCertificates for now.
|
||||
// TODO: use provider database when it becomes available
|
||||
config
|
||||
.dangerous()
|
||||
.set_certificate_verifier(Arc::new(NoCertificateVerification {}));
|
||||
}
|
||||
CertificateChecks::AcceptInvalidCertificates => {
|
||||
// TODO: only accept invalid certs
|
||||
config
|
||||
.dangerous()
|
||||
.set_certificate_verifier(Arc::new(NoCertificateVerification {}));
|
||||
tls_builder
|
||||
.danger_accept_invalid_hostnames(true)
|
||||
.danger_accept_invalid_certs(true)
|
||||
}
|
||||
CertificateChecks::Strict => &mut tls_builder,
|
||||
CertificateChecks::AcceptInvalidHostnames => {
|
||||
// TODO: only accept invalid hostnames
|
||||
config
|
||||
.dangerous()
|
||||
.set_certificate_verifier(Arc::new(NoCertificateVerification {}));
|
||||
tls_builder.danger_accept_invalid_hostnames(true)
|
||||
}
|
||||
CertificateChecks::AcceptInvalidCertificates => tls_builder
|
||||
.danger_accept_invalid_hostnames(true)
|
||||
.danger_accept_invalid_certs(true),
|
||||
}
|
||||
config
|
||||
.build()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -382,11 +382,11 @@ impl<'a> Peerstate<'a> {
|
||||
sql::execute(
|
||||
self.context,
|
||||
sql,
|
||||
"UPDATE acpeerstates \
|
||||
SET last_seen=?, last_seen_autocrypt=?, prefer_encrypted=?, \
|
||||
public_key=?, gossip_timestamp=?, gossip_key=?, public_key_fingerprint=?, gossip_key_fingerprint=?, \
|
||||
"UPDATE acpeerstates \
|
||||
SET last_seen=?, last_seen_autocrypt=?, prefer_encrypted=?, \
|
||||
public_key=?, gossip_timestamp=?, gossip_key=?, public_key_fingerprint=?, gossip_key_fingerprint=?, \
|
||||
verified_key=?, verified_key_fingerprint=? \
|
||||
WHERE addr=?;",
|
||||
WHERE addr=?;",
|
||||
params![
|
||||
self.last_seen,
|
||||
self.last_seen_autocrypt,
|
||||
|
||||
@@ -5,7 +5,7 @@ use crate::constants::*;
|
||||
use crate::context::Context;
|
||||
use crate::error::Error;
|
||||
use crate::events::Event;
|
||||
use crate::login_param::{dc_build_tls_config, LoginParam};
|
||||
use crate::login_param::{dc_build_tls, LoginParam};
|
||||
use crate::oauth2::*;
|
||||
|
||||
#[derive(DebugStub)]
|
||||
@@ -65,8 +65,8 @@ impl Smtp {
|
||||
let domain = &lp.send_server;
|
||||
let port = lp.send_port as u16;
|
||||
|
||||
let tls_config = dc_build_tls_config(lp.smtp_certificate_checks);
|
||||
let tls_parameters = ClientTlsParameters::new(domain.to_string(), tls_config);
|
||||
let tls = dc_build_tls(lp.smtp_certificate_checks).unwrap();
|
||||
let tls_parameters = ClientTlsParameters::new(domain.to_string(), tls);
|
||||
|
||||
let (creds, mechanism) = if 0 != lp.server_flags & (DC_LP_AUTH_OAUTH2 as i32) {
|
||||
// oauth2
|
||||
|
||||
41
src/sql.rs
41
src/sql.rs
@@ -5,6 +5,7 @@ use std::time::Duration;
|
||||
use rusqlite::{Connection, OpenFlags, Statement, NO_PARAMS};
|
||||
use thread_local_object::ThreadLocal;
|
||||
|
||||
use crate::constants::ShowEmails;
|
||||
use crate::context::Context;
|
||||
use crate::dc_tools::*;
|
||||
use crate::error::{Error, Result};
|
||||
@@ -348,7 +349,7 @@ fn open(
|
||||
}
|
||||
|
||||
if !readonly {
|
||||
let mut exists_before_update = 0;
|
||||
let mut exists_before_update = false;
|
||||
let mut dbversion_before_update = 0;
|
||||
/* Init tables to dbversion=0 */
|
||||
if !sql.table_exists("config") {
|
||||
@@ -478,7 +479,7 @@ fn open(
|
||||
sql.set_raw_config_int(context, "dbversion", 0)?;
|
||||
}
|
||||
} else {
|
||||
exists_before_update = 1;
|
||||
exists_before_update = true;
|
||||
dbversion_before_update = sql
|
||||
.get_raw_config_int(context, "dbversion")
|
||||
.unwrap_or_default();
|
||||
@@ -488,6 +489,7 @@ fn open(
|
||||
// this should be done before updates that use high-level objects that
|
||||
// rely themselves on the low-level structure.
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
let mut dbversion = dbversion_before_update;
|
||||
let mut recalc_fingerprints = 0;
|
||||
let mut update_file_paths = 0;
|
||||
@@ -588,6 +590,8 @@ fn open(
|
||||
}
|
||||
if dbversion < 27 {
|
||||
info!(context, "[migration] v27");
|
||||
// chat.id=1 and chat.id=2 are the old deaddrops,
|
||||
// the current ones are defined by chats.blocked=2
|
||||
sql.execute("DELETE FROM msgs WHERE chat_id=1 OR chat_id=2;", params![])?;
|
||||
sql.execute(
|
||||
"CREATE INDEX chats_contacts_index2 ON chats_contacts (contact_id);",
|
||||
@@ -653,6 +657,9 @@ fn open(
|
||||
params![],
|
||||
)?;
|
||||
if dbversion_before_update == 34 {
|
||||
// migrate database from the use of verified-flags to verified_key,
|
||||
// _only_ version 34 (0.17.0) has the fields public_key_verified and gossip_key_verified
|
||||
// this block can be deleted in half a year or so (created 5/2018)
|
||||
sql.execute(
|
||||
"UPDATE acpeerstates SET verified_key=gossip_key, verified_key_fingerprint=gossip_key_fingerprint WHERE gossip_key_verified=2;",
|
||||
params![]
|
||||
@@ -682,6 +689,8 @@ fn open(
|
||||
}
|
||||
if dbversion < 42 {
|
||||
info!(context, "[migration] v42");
|
||||
// older versions set the txt-field to the filenames, for debugging and fulltext search.
|
||||
// to allow text+attachment compound messages, we need to reset these fields.
|
||||
sql.execute("UPDATE msgs SET txt='' WHERE type!=10", params![])?;
|
||||
dbversion = 42;
|
||||
sql.set_raw_config_int(context, "dbversion", 42)?;
|
||||
@@ -735,14 +744,19 @@ fn open(
|
||||
}
|
||||
if dbversion < 50 {
|
||||
info!(context, "[migration] v50");
|
||||
if 0 != exists_before_update {
|
||||
sql.set_raw_config_int(context, "show_emails", 2)?;
|
||||
// installations <= 0.100.1 used DC_SHOW_EMAILS_ALL implicitly;
|
||||
// keep this default and use DC_SHOW_EMAILS_NO
|
||||
// only for new installations
|
||||
if exists_before_update {
|
||||
sql.set_raw_config_int(context, "show_emails", ShowEmails::All as i32)?;
|
||||
}
|
||||
dbversion = 50;
|
||||
sql.set_raw_config_int(context, "dbversion", 50)?;
|
||||
}
|
||||
if dbversion < 53 {
|
||||
info!(context, "[migration] v53");
|
||||
// the messages containing _only_ locations
|
||||
// are also added to the database as _hidden_.
|
||||
sql.execute(
|
||||
"CREATE TABLE locations ( id INTEGER PRIMARY KEY AUTOINCREMENT, latitude REAL DEFAULT 0.0, longitude REAL DEFAULT 0.0, accuracy REAL DEFAULT 0.0, timestamp INTEGER DEFAULT 0, chat_id INTEGER DEFAULT 0, from_id INTEGER DEFAULT 0);",
|
||||
params![]
|
||||
@@ -790,9 +804,26 @@ fn open(
|
||||
"ALTER TABLE locations ADD COLUMN independent INTEGER DEFAULT 0;",
|
||||
params![],
|
||||
)?;
|
||||
|
||||
sql.set_raw_config_int(context, "dbversion", 55)?;
|
||||
}
|
||||
if dbversion < 57 {
|
||||
info!(context, "[migration] v57");
|
||||
// label is a unique name and is currently used for device-messages only.
|
||||
// in contrast to rfc724_mid and other fields, the label is generated on the device
|
||||
// and allows reliable identifications this way.
|
||||
sql.execute(
|
||||
"ALTER TABLE msgs ADD COLUMN label TEXT DEFAULT '';",
|
||||
params![],
|
||||
)?;
|
||||
if exists_before_update && sql.get_raw_config_int(context, "bcc_self").is_none() {
|
||||
sql.set_raw_config_int(context, "bcc_self", 1)?;
|
||||
}
|
||||
sql.set_raw_config_int(context, "dbversion", 57)?;
|
||||
}
|
||||
|
||||
// (2) updates that require high-level objects
|
||||
// (the structure is complete now and all objects are usable)
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
if 0 != recalc_fingerprints {
|
||||
info!(context, "[migration] recalc fingerprints");
|
||||
|
||||
Reference in New Issue
Block a user