mirror of
https://github.com/chatmail/core.git
synced 2026-04-02 05:22:14 +03:00
Compare commits
5 Commits
v1.127.2
...
iequidoo/4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7a697d4316 | ||
|
|
dfdedf073d | ||
|
|
6aae0276da | ||
|
|
1d80659bc3 | ||
|
|
94d5e86d4f |
4
.github/workflows/ci.yml
vendored
4
.github/workflows/ci.yml
vendored
@@ -211,7 +211,7 @@ jobs:
|
||||
|
||||
- name: Run python tests
|
||||
env:
|
||||
DCC_NEW_TMP_EMAIL: ${{ secrets.DCC_NEW_TMP_EMAIL }}
|
||||
CHATMAIL_DOMAIN: ${{ secrets.CHATMAIL_DOMAIN }}
|
||||
DCC_RS_TARGET: debug
|
||||
DCC_RS_DEV: ${{ github.workspace }}
|
||||
working-directory: python
|
||||
@@ -274,6 +274,6 @@ jobs:
|
||||
|
||||
- name: Run deltachat-rpc-client tests
|
||||
env:
|
||||
DCC_NEW_TMP_EMAIL: ${{ secrets.DCC_NEW_TMP_EMAIL }}
|
||||
CHATMAIL_DOMAIN: ${{ secrets.CHATMAIL_DOMAIN }}
|
||||
working-directory: deltachat-rpc-client
|
||||
run: tox -e py
|
||||
|
||||
2
.github/workflows/jsonrpc.yml
vendored
2
.github/workflows/jsonrpc.yml
vendored
@@ -31,7 +31,7 @@ jobs:
|
||||
working-directory: deltachat-jsonrpc/typescript
|
||||
run: npm run test
|
||||
env:
|
||||
DCC_NEW_TMP_EMAIL: ${{ secrets.DCC_NEW_TMP_EMAIL }}
|
||||
CHATMAIL_DOMAIN: ${{ secrets.CHATMAIL_DOMAIN }}
|
||||
- name: make sure websocket server version still builds
|
||||
working-directory: deltachat-jsonrpc
|
||||
run: cargo build --bin deltachat-jsonrpc-server --features webserver
|
||||
|
||||
2
.github/workflows/node-tests.yml
vendored
2
.github/workflows/node-tests.yml
vendored
@@ -63,5 +63,5 @@ jobs:
|
||||
working-directory: node
|
||||
run: npm run test
|
||||
env:
|
||||
DCC_NEW_TMP_EMAIL: ${{ secrets.DCC_NEW_TMP_EMAIL }}
|
||||
CHATMAIL_DOMAIN: ${{ secrets.CHATMAIL_DOMAIN }}
|
||||
NODE_OPTIONS: "--force-node-api-uncaught-exceptions-policy=true"
|
||||
|
||||
4
Cargo.lock
generated
4
Cargo.lock
generated
@@ -3438,9 +3438,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "portable-atomic"
|
||||
version = "1.5.0"
|
||||
version = "1.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b559898e0b4931ed2d3b959ab0c2da4d99cc644c4b0b1a35b4d344027f474023"
|
||||
checksum = "3bccab0e7fd7cc19f820a1c8c91720af652d0c88dc9664dd72aef2614f04af3b"
|
||||
|
||||
[[package]]
|
||||
name = "postcard"
|
||||
|
||||
@@ -108,10 +108,10 @@ This will build the `deltachat-jsonrpc-server` binary and then run a test suite
|
||||
|
||||
The test suite includes some tests that need online connectivity and a way to create test email accounts. To run these tests, talk to DeltaChat developers to get a token for the `testrun.org` service, or use a local instance of [`mailadm`](https://github.com/deltachat/docker-mailadm).
|
||||
|
||||
Then, set the `DCC_NEW_TMP_EMAIL` environment variable to your mailadm token before running the tests.
|
||||
Then, set the `CHATMAIL_DOMAIN` environment variable to your testing email server domain.
|
||||
|
||||
```
|
||||
DCC_NEW_TMP_EMAIL=https://testrun.org/new_email?t=yourtoken npm run test
|
||||
CHATMAIL_DOMAIN=chat.example.org npm run test
|
||||
```
|
||||
|
||||
#### Test Coverage
|
||||
|
||||
@@ -13,16 +13,16 @@ describe("online tests", function () {
|
||||
|
||||
before(async function () {
|
||||
this.timeout(60000);
|
||||
if (!process.env.DCC_NEW_TMP_EMAIL) {
|
||||
if (!process.env.CHATMAIL_DOMAIN) {
|
||||
if (process.env.COVERAGE && !process.env.COVERAGE_OFFLINE) {
|
||||
console.error(
|
||||
"CAN NOT RUN COVERAGE correctly: Missing DCC_NEW_TMP_EMAIL environment variable!\n\n",
|
||||
"CAN NOT RUN COVERAGE correctly: Missing CHATMAIL_DOMAIN environment variable!\n\n",
|
||||
"You can set COVERAGE_OFFLINE=1 to circumvent this check and skip the online tests, but those coverage results will be wrong, because some functions can only be tested in the online test"
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
console.log(
|
||||
"Missing DCC_NEW_TMP_EMAIL environment variable!, skip integration tests"
|
||||
"Missing CHATMAIL_DOMAIN environment variable!, skip integration tests"
|
||||
);
|
||||
this.skip();
|
||||
}
|
||||
@@ -33,7 +33,7 @@ describe("online tests", function () {
|
||||
if (kind !== "Info") console.log(contextId, kind);
|
||||
});
|
||||
|
||||
account1 = await createTempUser(process.env.DCC_NEW_TMP_EMAIL);
|
||||
account1 = createTempUser(process.env.CHATMAIL_DOMAIN);
|
||||
if (!account1 || !account1.email || !account1.password) {
|
||||
console.log(
|
||||
"We didn't got back an account from the api, skip integration tests"
|
||||
@@ -41,7 +41,7 @@ describe("online tests", function () {
|
||||
this.skip();
|
||||
}
|
||||
|
||||
account2 = await createTempUser(process.env.DCC_NEW_TMP_EMAIL);
|
||||
account2 = createTempUser(process.env.CHATMAIL_DOMAIN);
|
||||
if (!account2 || !account2.email || !account2.password) {
|
||||
console.log(
|
||||
"We didn't got back an account2 from the api, skip integration tests"
|
||||
|
||||
@@ -57,15 +57,14 @@ export async function startServer(): Promise<RpcServerHandle> {
|
||||
};
|
||||
}
|
||||
|
||||
export async function createTempUser(url: string) {
|
||||
const response = await fetch(url, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"cache-control": "no-cache",
|
||||
},
|
||||
});
|
||||
if (!response.ok) throw new Error("Received invalid response");
|
||||
return response.json();
|
||||
export function createTempUser(chatmailDomain: String) {
|
||||
const charset = "2345789acdefghjkmnpqrstuvwxyz";
|
||||
let user = "ci-";
|
||||
for (let i = 0; i < 6; i++) {
|
||||
user += charset[Math.floor(Math.random() * charset.length)];
|
||||
}
|
||||
const email = user + "@" + chatmailDomain;
|
||||
return { email: email, password: user + "$" + user };
|
||||
}
|
||||
|
||||
function getTargetDir(): Promise<string> {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import json
|
||||
import os
|
||||
import urllib.request
|
||||
import random
|
||||
from typing import AsyncGenerator, List, Optional
|
||||
|
||||
import pytest
|
||||
@@ -10,12 +9,11 @@ from .rpc import Rpc
|
||||
|
||||
|
||||
def get_temp_credentials() -> dict:
|
||||
url = os.getenv("DCC_NEW_TMP_EMAIL")
|
||||
assert url, "Failed to get online account, DCC_NEW_TMP_EMAIL is not set"
|
||||
|
||||
request = urllib.request.Request(url, method="POST")
|
||||
with urllib.request.urlopen(request, timeout=60) as f:
|
||||
return json.load(f)
|
||||
domain = os.getenv("CHATMAIL_DOMAIN")
|
||||
username = "ci-" + "".join(random.choice("2345789acdefghjkmnpqrstuvwxyz") for i in range(6))
|
||||
password = f"{username}${username}"
|
||||
addr = f"{username}@{domain}"
|
||||
return {"email": addr, "password": password}
|
||||
|
||||
|
||||
class ACFactory:
|
||||
|
||||
@@ -11,7 +11,7 @@ setenv =
|
||||
# Avoid stack overflow when Rust core is built without optimizations.
|
||||
RUST_MIN_STACK=8388608
|
||||
passenv =
|
||||
DCC_NEW_TMP_EMAIL
|
||||
CHATMAIL_DOMAIN
|
||||
deps =
|
||||
pytest
|
||||
pytest-timeout
|
||||
|
||||
@@ -204,10 +204,10 @@ Running `npm test` ends with showing a code coverage report, which is produced b
|
||||
|
||||
The coverage report from `nyc` in the console is rather limited. To get a more detailed coverage report you can run `npm run coverage-html-report`. This will produce a html report from the `nyc` data and display it in a browser on your local machine.
|
||||
|
||||
To run the integration tests you need to set the `DCC_NEW_TMP_EMAIL` environment variables. E.g.:
|
||||
To run the integration tests you need to set the `CHATMAIL_DOMAIN` environment variables. E.g.:
|
||||
|
||||
```
|
||||
$ export DCC_NEW_TMP_EMAIL=https://testrun.org/new_email?t=[token]
|
||||
$ export CHATMAIL_DOMAIN=chat.example.org
|
||||
$ npm run test
|
||||
```
|
||||
|
||||
|
||||
@@ -12,22 +12,14 @@ import fetch from 'node-fetch'
|
||||
chai.use(chaiAsPromised)
|
||||
chai.config.truncateThreshold = 0 // Do not truncate assertion errors.
|
||||
|
||||
async function createTempUser(url) {
|
||||
async function postData(url = '') {
|
||||
// Default options are marked with *
|
||||
const response = await fetch(url, {
|
||||
method: 'POST', // *GET, POST, PUT, DELETE, etc.
|
||||
headers: {
|
||||
'cache-control': 'no-cache',
|
||||
},
|
||||
})
|
||||
if (!response.ok) {
|
||||
throw new Error('request failed: ' + response.body.read())
|
||||
}
|
||||
return response.json() // parses JSON response into native JavaScript objects
|
||||
function createTempUser(chatmailDomain) {
|
||||
const charset = "2345789acdefghjkmnpqrstuvwxyz";
|
||||
let user = "ci-";
|
||||
for (let i = 0; i < 6; i++) {
|
||||
user += charset[Math.floor(Math.random() * charset.length)];
|
||||
}
|
||||
|
||||
return await postData(url)
|
||||
const email = user + "@" + chatmailDomain;
|
||||
return { email: email, password: user + "$" + user };
|
||||
}
|
||||
|
||||
describe('static tests', function () {
|
||||
@@ -768,14 +760,7 @@ describe('Integration tests', function () {
|
||||
})
|
||||
|
||||
this.beforeAll(async function () {
|
||||
if (!process.env.DCC_NEW_TMP_EMAIL) {
|
||||
console.log(
|
||||
'Missing DCC_NEW_TMP_EMAIL environment variable!, skip integration tests'
|
||||
)
|
||||
this.skip()
|
||||
}
|
||||
|
||||
account = await createTempUser(process.env.DCC_NEW_TMP_EMAIL)
|
||||
account = createTempUser(process.env.CHATMAIL_DOMAIN)
|
||||
if (!account || !account.email || !account.password) {
|
||||
console.log(
|
||||
"We didn't got back an account from the api, skip integration tests"
|
||||
|
||||
@@ -53,14 +53,14 @@ end-to-end tests that require accounts on real e-mail servers.
|
||||
Running "live" tests with temporary accounts
|
||||
--------------------------------------------
|
||||
|
||||
If you want to run live functional tests you can set ``DCC_NEW_TMP_EMAIL`` to a URL that creates e-mail accounts. Most developers use https://testrun.org URLs created and managed by `mailadm <https://mailadm.readthedocs.io/>`_.
|
||||
If you want to run live functional tests
|
||||
you can set ``CHATMAIL_DOMAIN`` to a domain of the email server
|
||||
that creates e-mail accounts like this::
|
||||
|
||||
Please feel free to contact us through a github issue or by e-mail and we'll send you a URL that you can then use for functional tests like this::
|
||||
export CHATMAIL_DOMAIN=nine.testrun.org
|
||||
|
||||
export DCC_NEW_TMP_EMAIL=<URL you got from us>
|
||||
|
||||
With this account-creation setting, pytest runs create ephemeral e-mail accounts on the http://testrun.org server.
|
||||
These accounts are removed automatically as they expire.
|
||||
With this account-creation setting, pytest runs create ephemeral e-mail accounts on the server.
|
||||
These accounts have the pattern `ci-{6 characters}@{CHATMAIL_DOMAIN}`.
|
||||
After setting the variable, either rerun `scripts/run-python-test.sh`
|
||||
or run offline and online tests with `tox` directly::
|
||||
|
||||
@@ -159,6 +159,6 @@ find it with::
|
||||
|
||||
This docker image can be used to run tests and build Python wheels for all interpreters::
|
||||
|
||||
$ docker run -e DCC_NEW_TMP_EMAIL \
|
||||
$ docker run -e CHATMAIL_DOMAIN \
|
||||
--rm -it -v $(pwd):/mnt -w /mnt \
|
||||
deltachat/coredeps scripts/run_all.sh
|
||||
|
||||
@@ -8,11 +8,11 @@ import sys
|
||||
import threading
|
||||
import time
|
||||
import weakref
|
||||
import random
|
||||
from queue import Queue
|
||||
from typing import Callable, Dict, List, Optional, Set
|
||||
|
||||
import pytest
|
||||
import requests
|
||||
from _pytest._code import Source
|
||||
|
||||
import deltachat
|
||||
@@ -29,6 +29,12 @@ def pytest_addoption(parser):
|
||||
default=None,
|
||||
help="a file with >=2 lines where each line contains NAME=VALUE config settings for one account",
|
||||
)
|
||||
group.addoption(
|
||||
"--chatmail",
|
||||
action="store",
|
||||
default=None,
|
||||
help="chatmail server domain name",
|
||||
)
|
||||
group.addoption(
|
||||
"--ignored",
|
||||
action="store_true",
|
||||
@@ -53,10 +59,12 @@ def pytest_addoption(parser):
|
||||
|
||||
def pytest_configure(config):
|
||||
cfg = config.getoption("--liveconfig")
|
||||
|
||||
cfg = config.getoption("--chatmail")
|
||||
if not cfg:
|
||||
cfg = os.getenv("DCC_NEW_TMP_EMAIL")
|
||||
cfg = os.getenv("CHATMAIL_DOMAIN")
|
||||
if cfg:
|
||||
config.option.liveconfig = cfg
|
||||
config.option.chatmail = cfg
|
||||
|
||||
# Make sure we don't get garbled output because threads keep running
|
||||
# collect all ever created accounts in a weakref-set (so we don't
|
||||
@@ -157,13 +165,29 @@ class TestProcess:
|
||||
"""provide live account configs, cached on a per-test-process scope
|
||||
so that test functions can re-use already known live configs.
|
||||
Depending on the --liveconfig option this comes from
|
||||
a HTTP provider or a file with a line specifying each accounts config.
|
||||
a file with a line specifying each accounts config
|
||||
or a --chatmail domain.
|
||||
"""
|
||||
liveconfig_opt = self.pytestconfig.getoption("--liveconfig")
|
||||
if not liveconfig_opt:
|
||||
pytest.skip("specify DCC_NEW_TMP_EMAIL or --liveconfig to provide live accounts")
|
||||
|
||||
if not liveconfig_opt.startswith("http"):
|
||||
chatmail_opt = self.pytestconfig.getoption("--chatmail")
|
||||
if chatmail_opt:
|
||||
# Use a chatmail instance.
|
||||
domain = chatmail_opt
|
||||
MAX_LIVE_CREATED_ACCOUNTS = 10
|
||||
for index in range(MAX_LIVE_CREATED_ACCOUNTS):
|
||||
try:
|
||||
yield self._configlist[index]
|
||||
except IndexError:
|
||||
username = "ci-" + "".join(random.choice("2345789acdefghjkmnpqrstuvwxyz") for i in range(6))
|
||||
password = f"{username}${username}"
|
||||
addr = f"{username}@{domain}"
|
||||
config = {"addr": addr, "mail_pw": password}
|
||||
print("newtmpuser {}: addr={}".format(index, config["addr"]))
|
||||
self._configlist.append(config)
|
||||
yield config
|
||||
pytest.fail(f"more than {MAX_LIVE_CREATED_ACCOUNTS} live accounts requested.")
|
||||
elif liveconfig_opt:
|
||||
# Read a list of accounts from file.
|
||||
for line in open(liveconfig_opt):
|
||||
if line.strip() and not line.strip().startswith("#"):
|
||||
d = {}
|
||||
@@ -174,20 +198,9 @@ class TestProcess:
|
||||
|
||||
yield from iter(self._configlist)
|
||||
else:
|
||||
MAX_LIVE_CREATED_ACCOUNTS = 10
|
||||
for index in range(MAX_LIVE_CREATED_ACCOUNTS):
|
||||
try:
|
||||
yield self._configlist[index]
|
||||
except IndexError:
|
||||
res = requests.post(liveconfig_opt, timeout=60)
|
||||
if res.status_code != 200:
|
||||
pytest.fail(f"newtmpuser count={index} code={res.status_code}: '{res.text}'")
|
||||
d = res.json()
|
||||
config = {"addr": d["email"], "mail_pw": d["password"]}
|
||||
print("newtmpuser {}: addr={}".format(index, config["addr"]))
|
||||
self._configlist.append(config)
|
||||
yield config
|
||||
pytest.fail(f"more than {MAX_LIVE_CREATED_ACCOUNTS} live accounts requested.")
|
||||
pytest.skip(
|
||||
"specify --liveconfig, CHATMAIL_DOMAIN or --chatmail to provide live accounts",
|
||||
)
|
||||
|
||||
def cache_maybe_retrieve_configured_db_files(self, cache_addr, db_target_path):
|
||||
db_target_path = pathlib.Path(db_target_path)
|
||||
|
||||
@@ -2208,7 +2208,17 @@ def test_delete_multiple_messages(acfactory, lp):
|
||||
|
||||
def test_trash_multiple_messages(acfactory, lp):
|
||||
ac1, ac2 = acfactory.get_online_accounts(2)
|
||||
|
||||
# Create the Trash folder on IMAP server
|
||||
# and recreate the account so Trash folder is configured.
|
||||
lp.sec("Creating trash folder")
|
||||
ac2.direct_imap.create_folder("Trash")
|
||||
lp.sec("Creating new accounts")
|
||||
ac2 = acfactory.new_online_configuring_account(cloned_from=ac2)
|
||||
acfactory.bring_accounts_online()
|
||||
|
||||
ac2.set_config("delete_to_trash", "1")
|
||||
assert ac2.get_config("configured_trash_folder")
|
||||
chat12 = acfactory.get_accepted_chat(ac1, ac2)
|
||||
|
||||
lp.sec("ac1: sending 3 messages")
|
||||
@@ -2239,13 +2249,15 @@ def test_trash_multiple_messages(acfactory, lp):
|
||||
|
||||
|
||||
def test_configure_error_msgs_wrong_pw(acfactory):
|
||||
configdict = acfactory.get_next_liveconfig()
|
||||
ac1 = acfactory.get_unconfigured_account()
|
||||
ac1.update_config(configdict)
|
||||
ac1.set_config("mail_pw", "abc") # Wrong mail pw
|
||||
ac1.configure()
|
||||
(ac1,) = acfactory.get_online_accounts(1)
|
||||
|
||||
ac2 = acfactory.get_unconfigured_account()
|
||||
ac2.set_config("addr", ac1.get_config("addr"))
|
||||
ac2.set_config("mail_pw", "abc") # Wrong mail pw
|
||||
ac2.configure()
|
||||
while True:
|
||||
ev = ac1._evtracker.get_matching("DC_EVENT_CONFIGURE_PROGRESS")
|
||||
ev = ac2._evtracker.get_matching("DC_EVENT_CONFIGURE_PROGRESS")
|
||||
print(f"Configuration progress: {ev.data1}")
|
||||
if ev.data1 == 0:
|
||||
break
|
||||
# Password is wrong so it definitely has to say something about "password"
|
||||
@@ -2548,7 +2560,7 @@ class TestOnlineConfigureFails:
|
||||
def test_invalid_user(self, acfactory):
|
||||
configdict = acfactory.get_next_liveconfig()
|
||||
ac1 = acfactory.get_unconfigured_account()
|
||||
configdict["addr"] = "x" + configdict["addr"]
|
||||
configdict["addr"] = "$" + configdict["addr"]
|
||||
ac1.update_config(configdict)
|
||||
configtracker = ac1.configure()
|
||||
configtracker.wait_progress(500)
|
||||
@@ -2557,7 +2569,7 @@ class TestOnlineConfigureFails:
|
||||
def test_invalid_domain(self, acfactory):
|
||||
configdict = acfactory.get_next_liveconfig()
|
||||
ac1 = acfactory.get_unconfigured_account()
|
||||
configdict["addr"] += "x"
|
||||
configdict["addr"] += "$"
|
||||
ac1.update_config(configdict)
|
||||
configtracker = ac1.configure()
|
||||
configtracker.wait_progress(500)
|
||||
|
||||
@@ -16,7 +16,7 @@ setenv =
|
||||
passenv =
|
||||
DCC_RS_DEV
|
||||
DCC_RS_TARGET
|
||||
DCC_NEW_TMP_EMAIL
|
||||
CHATMAIL_DOMAIN
|
||||
CARGO_TARGET_DIR
|
||||
RUSTC_WRAPPER
|
||||
deps =
|
||||
|
||||
@@ -28,7 +28,7 @@ ssh $SSHTARGET <<_HERE
|
||||
export RUSTC_WRAPPER=\`which sccache\`
|
||||
cd $BUILDDIR
|
||||
export TARGET=release
|
||||
export DCC_NEW_TMP_EMAIL=$DCC_NEW_TMP_EMAIL
|
||||
export CHATMAIL_DOMAIN=$CHATMAIL_DOMAIN
|
||||
|
||||
#we rely on tox/virtualenv being available in the host
|
||||
#rm -rf virtualenv venv
|
||||
|
||||
@@ -27,7 +27,7 @@ mkdir -p $TOXWORKDIR
|
||||
# XXX we may switch on some live-tests on for better ensurances
|
||||
# Note that the independent remote_tests_python step does all kinds of
|
||||
# live-testing already.
|
||||
unset DCC_NEW_TMP_EMAIL
|
||||
unset CHATMAIL_DOMAIN
|
||||
|
||||
# Try to build wheels for a range of interpreters, but don't fail if they are not available.
|
||||
# E.g. musllinux_1_1 does not have PyPy interpreters as of 2022-07-10
|
||||
|
||||
@@ -1358,7 +1358,7 @@ impl MimeMessage {
|
||||
self.get_mailinglist_header().is_some()
|
||||
}
|
||||
|
||||
pub fn repl_msg_by_error(&mut self, error_msg: &str) {
|
||||
pub fn replace_msg_by_error(&mut self, error_msg: &str) {
|
||||
self.is_system_message = SystemMessage::Unknown;
|
||||
if let Some(part) = self.parts.first_mut() {
|
||||
part.typ = Viewtype::Text;
|
||||
|
||||
@@ -637,7 +637,7 @@ async fn add_parts(
|
||||
chat_id = None;
|
||||
} else {
|
||||
let s = stock_str::unknown_sender_for_chat(context).await;
|
||||
mime_parser.repl_msg_by_error(&s);
|
||||
mime_parser.replace_msg_by_error(&s);
|
||||
}
|
||||
} else {
|
||||
// In non-protected chats, just mark the sender as overridden. Therefore, the UI will prepend `~`
|
||||
@@ -1055,7 +1055,7 @@ async fn add_parts(
|
||||
{
|
||||
warn!(context, "Verification problem: {err:#}.");
|
||||
let s = format!("{err}. See 'Info' for more details");
|
||||
mime_parser.repl_msg_by_error(&s);
|
||||
mime_parser.replace_msg_by_error(&s);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1596,7 +1596,7 @@ async fn create_or_lookup_group(
|
||||
{
|
||||
warn!(context, "Verification problem: {err:#}.");
|
||||
let s = format!("{err}. See 'Info' for more details");
|
||||
mime_parser.repl_msg_by_error(&s);
|
||||
mime_parser.replace_msg_by_error(&s);
|
||||
}
|
||||
ProtectionStatus::Protected
|
||||
} else {
|
||||
@@ -1758,7 +1758,7 @@ async fn apply_group_changes(
|
||||
{
|
||||
warn!(context, "Verification problem: {err:#}.");
|
||||
let s = format!("{err}. See 'Info' for more details");
|
||||
mime_parser.repl_msg_by_error(&s);
|
||||
mime_parser.replace_msg_by_error(&s);
|
||||
}
|
||||
|
||||
if !chat.is_protected() {
|
||||
@@ -2311,7 +2311,7 @@ async fn has_verified_encryption(
|
||||
LEFT JOIN acpeerstates ps ON c.addr=ps.addr WHERE c.id IN({}) ",
|
||||
sql::repeat_vars(to_ids.len())
|
||||
),
|
||||
rusqlite::params_from_iter(to_ids),
|
||||
rusqlite::params_from_iter(&to_ids),
|
||||
|row| {
|
||||
let to_addr: String = row.get(0)?;
|
||||
let is_verified: i32 = row.get(1).unwrap_or(0);
|
||||
@@ -2364,8 +2364,7 @@ async fn has_verified_encryption(
|
||||
}
|
||||
if !is_verified {
|
||||
return Ok(NotVerified(format!(
|
||||
"{} is not a member of this protected chat",
|
||||
to_addr
|
||||
"{to_addr} is not a member of this protected chat member list {to_ids:?}",
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3106,6 +3106,19 @@ async fn test_blocked_contact_creates_group() -> Result<()> {
|
||||
async fn test_thunderbird_autocrypt() -> Result<()> {
|
||||
let t = TestContext::new_bob().await;
|
||||
|
||||
receive_imf(
|
||||
&t,
|
||||
b"From: alice@example.org\n\
|
||||
To: bob@example.net\n\
|
||||
Subject: foo\n\
|
||||
Message-ID: <message@example.org>\n\
|
||||
Date: Sun, 22 Mar 2020 22:37:57 +0000\n\
|
||||
\n\
|
||||
hello foo\n",
|
||||
false,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let raw = include_bytes!("../../test-data/message/thunderbird_with_autocrypt.eml");
|
||||
receive_imf(&t, raw, false).await?;
|
||||
|
||||
@@ -3117,6 +3130,34 @@ async fn test_thunderbird_autocrypt() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_thunderbird_encrypted_with_pubkey_attached() -> Result<()> {
|
||||
let t = TestContext::new_bob().await;
|
||||
|
||||
receive_imf(
|
||||
&t,
|
||||
b"From: alice@example.org\n\
|
||||
To: bob@example.net\n\
|
||||
Subject: foo\n\
|
||||
Message-ID: <message@example.org>\n\
|
||||
Date: Sun, 22 Mar 2020 22:37:57 +0000\n\
|
||||
\n\
|
||||
hello foo\n",
|
||||
false,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let raw = include_bytes!("../../test-data/message/thunderbird_encrypted_with_pubkey_attached.eml");
|
||||
receive_imf(&t, raw, false).await?;
|
||||
|
||||
let peerstate = Peerstate::from_addr(&t, "alice@example.org")
|
||||
.await?
|
||||
.unwrap();
|
||||
assert_eq!(peerstate.prefer_encrypt, EncryptPreference::Mutual);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_thunderbird_autocrypt_unencrypted() -> Result<()> {
|
||||
let t = TestContext::new_bob().await;
|
||||
@@ -3135,6 +3176,13 @@ async fn test_thunderbird_autocrypt_unencrypted() -> Result<()> {
|
||||
.unwrap();
|
||||
assert_eq!(peerstate.prefer_encrypt, EncryptPreference::Mutual);
|
||||
|
||||
let raw = include_bytes!("../../test-data/message/thunderbird_encrypted.eml");
|
||||
receive_imf(&t, raw, false).await?;
|
||||
let peerstate = Peerstate::from_addr(&t, "alice@example.org")
|
||||
.await?
|
||||
.unwrap();
|
||||
assert_eq!(peerstate.prefer_encrypt, EncryptPreference::Mutual);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
80
test-data/message/thunderbird_encrypted.eml
Normal file
80
test-data/message/thunderbird_encrypted.eml
Normal file
@@ -0,0 +1,80 @@
|
||||
From - Fri, 13 Oct 2023 05:36:46 GMT
|
||||
X-Mozilla-Status: 0801
|
||||
X-Mozilla-Status2: 00000000
|
||||
Message-ID: <19925052-21a0-530a-cb93-dd05d227ece2@example.org>
|
||||
Date: Fri, 13 Oct 2023 02:36:46 -0300
|
||||
MIME-Version: 1.0
|
||||
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101
|
||||
Thunderbird/102.13.0
|
||||
Content-Language: en-US
|
||||
To: bob@example.net
|
||||
From: Alice <alice@example.org>
|
||||
X-Mozilla-Draft-Info: internal/draft; vcard=0; receipt=0; DSN=0; uuencode=0;
|
||||
attachmentreminder=0; deliveryformat=0
|
||||
X-Identity-Key: id3
|
||||
Fcc: imap://alice%40example.org@in.example.org/Sent
|
||||
Subject: ...
|
||||
Content-Type: multipart/encrypted;
|
||||
protocol="application/pgp-encrypted";
|
||||
boundary="------------RT0xHdRXXmnS0UAhLpOJ60ps"
|
||||
|
||||
This is an OpenPGP/MIME encrypted message (RFC 4880 and 3156)
|
||||
--------------RT0xHdRXXmnS0UAhLpOJ60ps
|
||||
Content-Type: application/pgp-encrypted
|
||||
Content-Description: PGP/MIME version identification
|
||||
|
||||
Version: 1
|
||||
|
||||
--------------RT0xHdRXXmnS0UAhLpOJ60ps
|
||||
Content-Type: application/octet-stream; name="encrypted.asc"
|
||||
Content-Description: OpenPGP encrypted message
|
||||
Content-Disposition: inline; filename="encrypted.asc"
|
||||
|
||||
-----BEGIN PGP MESSAGE-----
|
||||
|
||||
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
|
||||
PbZLmbNpqVOJMuSJZZA=
|
||||
=JH2e
|
||||
-----END PGP MESSAGE-----
|
||||
|
||||
--------------RT0xHdRXXmnS0UAhLpOJ60ps--
|
||||
165
test-data/message/thunderbird_encrypted_with_pubkey_attached.eml
Normal file
165
test-data/message/thunderbird_encrypted_with_pubkey_attached.eml
Normal file
@@ -0,0 +1,165 @@
|
||||
From - Tue, 31 Oct 2023 05:04:31 GMT
|
||||
X-Mozilla-Status: 0801
|
||||
X-Mozilla-Status2: 00000000
|
||||
Message-ID: <6e2cfe71-c22f-14ef-b900-5b3b803e1d1c@example.org>
|
||||
Date: Tue, 31 Oct 2023 02:04:31 -0300
|
||||
MIME-Version: 1.0
|
||||
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101
|
||||
Thunderbird/102.13.0
|
||||
Content-Language: en-US
|
||||
To: bob@example.net
|
||||
From: Alice <alice@example.org>
|
||||
Autocrypt: addr=alice@example.org; keydata=
|
||||
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
|
||||
/qMLjKwBpKEd/w==
|
||||
X-Mozilla-Draft-Info: internal/draft; vcard=0; receipt=0; DSN=0; uuencode=0;
|
||||
attachmentreminder=0; deliveryformat=0
|
||||
X-Identity-Key: id3
|
||||
Fcc: imap://alice%40example.org@in.example.org/Sent
|
||||
Subject: ...
|
||||
Content-Type: multipart/encrypted;
|
||||
protocol="application/pgp-encrypted";
|
||||
boundary="------------KGk5Y8jKHbRMNVbglgZHajLT"
|
||||
|
||||
This is an OpenPGP/MIME encrypted message (RFC 4880 and 3156)
|
||||
--------------KGk5Y8jKHbRMNVbglgZHajLT
|
||||
Content-Type: application/pgp-encrypted
|
||||
Content-Description: PGP/MIME version identification
|
||||
|
||||
Version: 1
|
||||
|
||||
--------------KGk5Y8jKHbRMNVbglgZHajLT
|
||||
Content-Type: application/octet-stream; name="encrypted.asc"
|
||||
Content-Description: OpenPGP encrypted message
|
||||
Content-Disposition: inline; filename="encrypted.asc"
|
||||
|
||||
-----BEGIN PGP MESSAGE-----
|
||||
|
||||
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
|
||||
=Rw/C
|
||||
-----END PGP MESSAGE-----
|
||||
|
||||
--------------KGk5Y8jKHbRMNVbglgZHajLT--
|
||||
Reference in New Issue
Block a user