Compare commits

...

5 Commits

Author SHA1 Message Date
iequidoo
7a697d4316 try to reproduce (#4707) 2023-10-31 02:12:33 -03:00
iequidoo
dfdedf073d try to reproduce (#4707) 2023-10-31 02:11:30 -03:00
link2xt
6aae0276da test: use instant accounts instead of mailadm 2023-10-30 00:15:22 +00:00
link2xt
1d80659bc3 chore: update portable-atomic dependency
Version 1.5.0 is yanked.
2023-10-29 23:36:26 +00:00
link2xt
94d5e86d4f refactor: rename repl_msg_by_error into replace_msg_by_error
This function has been named like this since it was a C function.
`repl` is unclear because it may stand for `reply`
as well as `replace`.
2023-10-29 17:09:59 +00:00
22 changed files with 403 additions and 104 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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
View File

@@ -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"

View File

@@ -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

View File

@@ -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"

View File

@@ -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> {

View File

@@ -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:

View File

@@ -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

View File

@@ -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
```

View File

@@ -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"

View File

@@ -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

View File

@@ -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)

View File

@@ -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)

View File

@@ -16,7 +16,7 @@ setenv =
passenv =
DCC_RS_DEV
DCC_RS_TARGET
DCC_NEW_TMP_EMAIL
CHATMAIL_DOMAIN
CARGO_TARGET_DIR
RUSTC_WRAPPER
deps =

View File

@@ -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

View File

@@ -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

View File

@@ -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;

View File

@@ -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:?}",
)));
}
}

View File

@@ -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(())
}

View 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--

View 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--