mirror of
https://github.com/chatmail/core.git
synced 2026-05-16 21:36:30 +03:00
Merge tag 'v1.124.0'
This commit is contained in:
16
.github/workflows/ci.yml
vendored
16
.github/workflows/ci.yml
vendored
@@ -180,15 +180,15 @@ jobs:
|
|||||||
include:
|
include:
|
||||||
# Currently used Rust version.
|
# Currently used Rust version.
|
||||||
- os: ubuntu-latest
|
- os: ubuntu-latest
|
||||||
python: 3.11
|
python: 3.12
|
||||||
- os: macos-latest
|
- os: macos-latest
|
||||||
python: 3.11
|
python: 3.12
|
||||||
|
|
||||||
# PyPy tests
|
# PyPy tests
|
||||||
- os: ubuntu-latest
|
- os: ubuntu-latest
|
||||||
python: pypy3.9
|
python: pypy3.10
|
||||||
- os: macos-latest
|
- os: macos-latest
|
||||||
python: pypy3.9
|
python: pypy3.10
|
||||||
|
|
||||||
# Minimum Supported Python Version = 3.7
|
# Minimum Supported Python Version = 3.7
|
||||||
# This is the minimum version for which manylinux Python wheels are
|
# This is the minimum version for which manylinux Python wheels are
|
||||||
@@ -229,6 +229,10 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
|
# Async Python bindings do not depend on Python version,
|
||||||
|
# but are tested on Python 3.11 until Python 3.12 support
|
||||||
|
# is added to `aiohttp` dependency:
|
||||||
|
# https://github.com/aio-libs/aiohttp/issues/7646
|
||||||
- os: ubuntu-latest
|
- os: ubuntu-latest
|
||||||
python: 3.11
|
python: 3.11
|
||||||
- os: macos-latest
|
- os: macos-latest
|
||||||
@@ -236,9 +240,9 @@ jobs:
|
|||||||
|
|
||||||
# PyPy tests
|
# PyPy tests
|
||||||
- os: ubuntu-latest
|
- os: ubuntu-latest
|
||||||
python: pypy3.9
|
python: pypy3.10
|
||||||
- os: macos-latest
|
- os: macos-latest
|
||||||
python: pypy3.9
|
python: pypy3.10
|
||||||
|
|
||||||
# Minimum Supported Python Version = 3.8
|
# Minimum Supported Python Version = 3.8
|
||||||
#
|
#
|
||||||
|
|||||||
34
CHANGELOG.md
34
CHANGELOG.md
@@ -1,5 +1,38 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## [1.124.0] - 2023-10-04
|
||||||
|
|
||||||
|
### API-Changes
|
||||||
|
|
||||||
|
- [**breaking**] Return `DC_CONTACT_ID_SELF` from `dc_contact_get_verifier_id()` for directly verified contacts.
|
||||||
|
- Deprecate `dc_contact_get_verifier_addr`.
|
||||||
|
- python: use `dc_contact_get_verifier_id()`. `get_verifier()` returns a Contact rather than an address now.
|
||||||
|
- Deprecate `get_next_media()`.
|
||||||
|
- Ignore public key argument in `dc_preconfigure_keypair()`. Public key is extracted from the private key.
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
|
||||||
|
- Wrap base64-encoded parts to 76 characters.
|
||||||
|
- Require valid email addresses in `dc_provider_new_from_email[_with_dns]`.
|
||||||
|
- Do not trash messages with attachments and no text when `location.kml` is attached ([#4749](https://github.com/deltachat/deltachat-core-rust/issues/4749)).
|
||||||
|
- Initialise `last_msg_id` to the highest known row id. This ensures bots migrated from older version to `dc_get_next_msgs()` API do not process all previous messages from scratch.
|
||||||
|
- Do not put the status footer into reaction MIME parts.
|
||||||
|
- Ignore special chats in `get_similar_chat_ids()`. This prevents trash chat from showing up in similar chat list ([#4756](https://github.com/deltachat/deltachat-core-rust/issues/4756)).
|
||||||
|
- Cap percentage in connectivity layout to 100% ([#4765](https://github.com/deltachat/deltachat-core-rust/pull/4765)).
|
||||||
|
- Add Let's Encrypt root certificate to `reqwest`. This should allow scanning `DCACCOUNT` QR-codes on older Android phones when the server has a Let's Encrypt certificate.
|
||||||
|
- deltachat-rpc-client: Increase stdio buffer to 64 MiB to avoid Python bots crashing when trying to load large messages via a JSON-RPC call.
|
||||||
|
- Add `protected-headers` directive to Content-Type of encrypted messages with attachments ([#2302](https://github.com/deltachat/deltachat-core-rust/issues/2302)). This makes Thunderbird show encrypted Subject for Delta Chat messages.
|
||||||
|
- webxdc: Reset `document.update` on forwarding. This fixes the test `test_forward_webxdc_instance()`.
|
||||||
|
|
||||||
|
### Features / Changes
|
||||||
|
|
||||||
|
- Remove extra members from the local list in sake of group membership consistency ([#3782](https://github.com/deltachat/deltachat-core-rust/issues/3782)).
|
||||||
|
- deltachat-rpc-client: Log exceptions when long-running tasks die.
|
||||||
|
|
||||||
|
### Build
|
||||||
|
|
||||||
|
- Build wheels for Python 3.12 and PyPy 3.10.
|
||||||
|
|
||||||
## [1.123.0] - 2023-09-22
|
## [1.123.0] - 2023-09-22
|
||||||
|
|
||||||
### API-Changes
|
### API-Changes
|
||||||
@@ -2834,3 +2867,4 @@ https://github.com/deltachat/deltachat-core-rust/pulls?q=is%3Apr+is%3Aclosed
|
|||||||
[1.121.0]: https://github.com/deltachat/deltachat-core-rust/compare/v1.120.0...v1.121.0
|
[1.121.0]: https://github.com/deltachat/deltachat-core-rust/compare/v1.120.0...v1.121.0
|
||||||
[1.122.0]: https://github.com/deltachat/deltachat-core-rust/compare/v1.121.0...v1.122.0
|
[1.122.0]: https://github.com/deltachat/deltachat-core-rust/compare/v1.121.0...v1.122.0
|
||||||
[1.123.0]: https://github.com/deltachat/deltachat-core-rust/compare/v1.122.0...v1.123.0
|
[1.123.0]: https://github.com/deltachat/deltachat-core-rust/compare/v1.122.0...v1.123.0
|
||||||
|
[1.124.0]: https://github.com/deltachat/deltachat-core-rust/compare/v1.123.0...v1.124.0
|
||||||
|
|||||||
12
Cargo.lock
generated
12
Cargo.lock
generated
@@ -1085,7 +1085,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "deltachat"
|
name = "deltachat"
|
||||||
version = "1.123.0"
|
version = "1.124.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ansi_term",
|
"ansi_term",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
@@ -1162,7 +1162,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "deltachat-jsonrpc"
|
name = "deltachat-jsonrpc"
|
||||||
version = "1.123.0"
|
version = "1.124.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-channel",
|
"async-channel",
|
||||||
@@ -1186,7 +1186,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "deltachat-repl"
|
name = "deltachat-repl"
|
||||||
version = "1.123.0"
|
version = "1.124.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ansi_term",
|
"ansi_term",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
@@ -1201,7 +1201,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "deltachat-rpc-server"
|
name = "deltachat-rpc-server"
|
||||||
version = "1.123.0"
|
version = "1.124.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"deltachat",
|
"deltachat",
|
||||||
@@ -1226,7 +1226,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "deltachat_ffi"
|
name = "deltachat_ffi"
|
||||||
version = "1.123.0"
|
version = "1.124.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"deltachat",
|
"deltachat",
|
||||||
@@ -1594,7 +1594,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "email"
|
name = "email"
|
||||||
version = "0.0.21"
|
version = "0.0.21"
|
||||||
source = "git+https://github.com/deltachat/rust-email?branch=master#25702df99254d059483b41417cd80696a258df8e"
|
source = "git+https://github.com/deltachat/rust-email?branch=master#37778c89d5eb5a94b7983f3f37ff67769bde3cf9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.11.0",
|
"base64 0.11.0",
|
||||||
"chrono",
|
"chrono",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "deltachat"
|
name = "deltachat"
|
||||||
version = "1.123.0"
|
version = "1.124.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MPL-2.0"
|
license = "MPL-2.0"
|
||||||
rust-version = "1.67"
|
rust-version = "1.67"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "deltachat_ffi"
|
name = "deltachat_ffi"
|
||||||
version = "1.123.0"
|
version = "1.124.0"
|
||||||
description = "Deltachat FFI"
|
description = "Deltachat FFI"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
|||||||
@@ -1488,6 +1488,7 @@ dc_array_t* dc_get_chat_media (dc_context_t* context, uint32_t ch
|
|||||||
* Typically used to implement the "next" and "previous" buttons
|
* Typically used to implement the "next" and "previous" buttons
|
||||||
* in a gallery or in a media player.
|
* in a gallery or in a media player.
|
||||||
*
|
*
|
||||||
|
* @deprecated Deprecated 2023-10-03, use dc_get_chat_media() and navigate the returned array instead.
|
||||||
* @memberof dc_context_t
|
* @memberof dc_context_t
|
||||||
* @param context The context object as returned from dc_context_new().
|
* @param context The context object as returned from dc_context_new().
|
||||||
* @param msg_id The ID of the current message from which the next or previous message should be searched.
|
* @param msg_id The ID of the current message from which the next or previous message should be searched.
|
||||||
|
|||||||
@@ -1422,6 +1422,7 @@ pub unsafe extern "C" fn dc_get_chat_media(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
|
#[allow(deprecated)]
|
||||||
pub unsafe extern "C" fn dc_get_next_media(
|
pub unsafe extern "C" fn dc_get_next_media(
|
||||||
context: *mut dc_context_t,
|
context: *mut dc_context_t,
|
||||||
msg_id: u32,
|
msg_id: u32,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "deltachat-jsonrpc"
|
name = "deltachat-jsonrpc"
|
||||||
version = "1.123.0"
|
version = "1.124.0"
|
||||||
description = "DeltaChat JSON-RPC API"
|
description = "DeltaChat JSON-RPC API"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
default-run = "deltachat-jsonrpc-server"
|
default-run = "deltachat-jsonrpc-server"
|
||||||
|
|||||||
@@ -1430,6 +1430,10 @@ impl CommandApi {
|
|||||||
///
|
///
|
||||||
/// one combined call for getting chat::get_next_media for both directions
|
/// one combined call for getting chat::get_next_media for both directions
|
||||||
/// the manual chat::get_next_media in only one direction is not exposed by the jsonrpc yet
|
/// the manual chat::get_next_media in only one direction is not exposed by the jsonrpc yet
|
||||||
|
///
|
||||||
|
/// Deprecated 2023-10-03, use `get_chat_media` method
|
||||||
|
/// and navigate the returned array instead.
|
||||||
|
#[allow(deprecated)]
|
||||||
async fn get_neighboring_chat_media(
|
async fn get_neighboring_chat_media(
|
||||||
&self,
|
&self,
|
||||||
account_id: u32,
|
account_id: u32,
|
||||||
|
|||||||
@@ -55,5 +55,5 @@
|
|||||||
},
|
},
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"types": "dist/deltachat.d.ts",
|
"types": "dist/deltachat.d.ts",
|
||||||
"version": "1.123.0"
|
"version": "1.124.0"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "deltachat-repl"
|
name = "deltachat-repl"
|
||||||
version = "1.123.0"
|
version = "1.124.0"
|
||||||
license = "MPL-2.0"
|
license = "MPL-2.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
import json
|
import json
|
||||||
|
import logging
|
||||||
import os
|
import os
|
||||||
from typing import Any, Dict, Optional
|
from typing import Any, Dict, Optional
|
||||||
|
|
||||||
@@ -28,10 +29,16 @@ class Rpc:
|
|||||||
self.events_task: asyncio.Task
|
self.events_task: asyncio.Task
|
||||||
|
|
||||||
async def start(self) -> None:
|
async def start(self) -> None:
|
||||||
|
# Use buffer of 64 MiB.
|
||||||
|
# Default limit as of Python 3.11 is 2**16 bytes, this is too low for some JSON-RPC responses,
|
||||||
|
# such as loading large HTML message content.
|
||||||
|
limit = 2**26
|
||||||
|
|
||||||
self.process = await asyncio.create_subprocess_exec(
|
self.process = await asyncio.create_subprocess_exec(
|
||||||
"deltachat-rpc-server",
|
"deltachat-rpc-server",
|
||||||
stdin=asyncio.subprocess.PIPE,
|
stdin=asyncio.subprocess.PIPE,
|
||||||
stdout=asyncio.subprocess.PIPE,
|
stdout=asyncio.subprocess.PIPE,
|
||||||
|
limit=limit,
|
||||||
**self._kwargs,
|
**self._kwargs,
|
||||||
)
|
)
|
||||||
self.id = 0
|
self.id = 0
|
||||||
@@ -57,16 +64,20 @@ class Rpc:
|
|||||||
await self.close()
|
await self.close()
|
||||||
|
|
||||||
async def reader_loop(self) -> None:
|
async def reader_loop(self) -> None:
|
||||||
while True:
|
try:
|
||||||
line = await self.process.stdout.readline() # noqa
|
while True:
|
||||||
if not line: # EOF
|
line = await self.process.stdout.readline() # noqa
|
||||||
break
|
if not line: # EOF
|
||||||
response = json.loads(line)
|
break
|
||||||
if "id" in response:
|
response = json.loads(line)
|
||||||
fut = self.request_events.pop(response["id"])
|
if "id" in response:
|
||||||
fut.set_result(response)
|
fut = self.request_events.pop(response["id"])
|
||||||
else:
|
fut.set_result(response)
|
||||||
print(response)
|
else:
|
||||||
|
print(response)
|
||||||
|
except Exception:
|
||||||
|
# Log an exception if the reader loop dies.
|
||||||
|
logging.exception("Exception in the reader loop")
|
||||||
|
|
||||||
async def get_queue(self, account_id: int) -> asyncio.Queue:
|
async def get_queue(self, account_id: int) -> asyncio.Queue:
|
||||||
if account_id not in self.event_queues:
|
if account_id not in self.event_queues:
|
||||||
@@ -75,13 +86,17 @@ class Rpc:
|
|||||||
|
|
||||||
async def events_loop(self) -> None:
|
async def events_loop(self) -> None:
|
||||||
"""Requests new events and distributes them between queues."""
|
"""Requests new events and distributes them between queues."""
|
||||||
while True:
|
try:
|
||||||
if self.closing:
|
while True:
|
||||||
return
|
if self.closing:
|
||||||
event = await self.get_next_event()
|
return
|
||||||
account_id = event["contextId"]
|
event = await self.get_next_event()
|
||||||
queue = await self.get_queue(account_id)
|
account_id = event["contextId"]
|
||||||
await queue.put(event["event"])
|
queue = await self.get_queue(account_id)
|
||||||
|
await queue.put(event["event"])
|
||||||
|
except Exception:
|
||||||
|
# Log an exception if the event loop dies.
|
||||||
|
logging.exception("Exception in the event loop")
|
||||||
|
|
||||||
async def wait_for_event(self, account_id: int) -> Optional[dict]:
|
async def wait_for_event(self, account_id: int) -> Optional[dict]:
|
||||||
"""Waits for the next event from the given account and returns it."""
|
"""Waits for the next event from the given account and returns it."""
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "deltachat-rpc-server"
|
name = "deltachat-rpc-server"
|
||||||
version = "1.123.0"
|
version = "1.124.0"
|
||||||
description = "DeltaChat JSON-RPC server"
|
description = "DeltaChat JSON-RPC server"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
|||||||
@@ -60,5 +60,5 @@
|
|||||||
"test:mocha": "mocha -r esm node/test/test.js --growl --reporter=spec --bail --exit"
|
"test:mocha": "mocha -r esm node/test/test.js --growl --reporter=spec --bail --exit"
|
||||||
},
|
},
|
||||||
"types": "node/dist/index.d.ts",
|
"types": "node/dist/index.d.ts",
|
||||||
"version": "1.123.0"
|
"version": "1.124.0"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,3 +24,5 @@ ignore_missing_imports = True
|
|||||||
[mypy-imap_tools.*]
|
[mypy-imap_tools.*]
|
||||||
ignore_missing_imports = True
|
ignore_missing_imports = True
|
||||||
|
|
||||||
|
[mypy-distutils.*]
|
||||||
|
ignore_missing_imports = True
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
2023-09-22
|
2023-10-04
|
||||||
@@ -31,7 +31,7 @@ unset DCC_NEW_TMP_EMAIL
|
|||||||
|
|
||||||
# Try to build wheels for a range of interpreters, but don't fail if they are not available.
|
# 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
|
# E.g. musllinux_1_1 does not have PyPy interpreters as of 2022-07-10
|
||||||
tox --workdir "$TOXWORKDIR" -e py37,py38,py39,py310,py311,pypy37,pypy38,pypy39 --skip-missing-interpreters true
|
tox --workdir "$TOXWORKDIR" -e py37,py38,py39,py310,py311,py312,pypy37,pypy38,pypy39,pypy310 --skip-missing-interpreters true
|
||||||
|
|
||||||
auditwheel repair "$TOXWORKDIR"/wheelhouse/deltachat* -w "$TOXWORKDIR/wheelhouse"
|
auditwheel repair "$TOXWORKDIR"/wheelhouse/deltachat* -w "$TOXWORKDIR/wheelhouse"
|
||||||
|
|
||||||
|
|||||||
@@ -932,9 +932,10 @@ impl ChatId {
|
|||||||
.sql
|
.sql
|
||||||
.query_map(
|
.query_map(
|
||||||
"SELECT chat_id, count(*) AS n
|
"SELECT chat_id, count(*) AS n
|
||||||
FROM chats_contacts where contact_id > 9
|
FROM chats_contacts
|
||||||
|
WHERE contact_id > ? AND chat_id > ?
|
||||||
GROUP BY chat_id",
|
GROUP BY chat_id",
|
||||||
(),
|
(ContactId::LAST_SPECIAL, DC_CHAT_ID_LAST_SPECIAL),
|
||||||
|row| {
|
|row| {
|
||||||
let chat_id: ChatId = row.get(0)?;
|
let chat_id: ChatId = row.get(0)?;
|
||||||
let size: f64 = row.get(1)?;
|
let size: f64 = row.get(1)?;
|
||||||
@@ -2951,6 +2952,9 @@ pub enum Direction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Searches next/previous message based on the given message and list of types.
|
/// Searches next/previous message based on the given message and list of types.
|
||||||
|
///
|
||||||
|
/// Deprecated since 2023-10-03.
|
||||||
|
#[deprecated(note = "use `get_chat_media` instead")]
|
||||||
pub async fn get_next_media(
|
pub async fn get_next_media(
|
||||||
context: &Context,
|
context: &Context,
|
||||||
curr_msg_id: MsgId,
|
curr_msg_id: MsgId,
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ impl ContactId {
|
|||||||
|
|
||||||
/// ID of the contact for device messages.
|
/// ID of the contact for device messages.
|
||||||
pub const DEVICE: ContactId = ContactId::new(5);
|
pub const DEVICE: ContactId = ContactId::new(5);
|
||||||
const LAST_SPECIAL: ContactId = ContactId::new(9);
|
pub(crate) const LAST_SPECIAL: ContactId = ContactId::new(9);
|
||||||
|
|
||||||
/// Address to go with [`ContactId::DEVICE`].
|
/// Address to go with [`ContactId::DEVICE`].
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -678,6 +678,12 @@ impl<'a> MimeFactory<'a> {
|
|||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let get_content_type_directives_header = || {
|
||||||
|
(
|
||||||
|
"Content-Type-Deltachat-Directives".to_string(),
|
||||||
|
"protected-headers=\"v1\"".to_string(),
|
||||||
|
)
|
||||||
|
};
|
||||||
let outer_message = if is_encrypted {
|
let outer_message = if is_encrypted {
|
||||||
headers.protected.push(from_header);
|
headers.protected.push(from_header);
|
||||||
|
|
||||||
@@ -714,10 +720,7 @@ impl<'a> MimeFactory<'a> {
|
|||||||
if !existing_ct.ends_with(';') {
|
if !existing_ct.ends_with(';') {
|
||||||
existing_ct += ";";
|
existing_ct += ";";
|
||||||
}
|
}
|
||||||
let message = message.replace_header(Header::new(
|
let message = message.header(get_content_type_directives_header());
|
||||||
"Content-Type".to_string(),
|
|
||||||
format!("{existing_ct} protected-headers=\"v1\";"),
|
|
||||||
));
|
|
||||||
|
|
||||||
// Set the appropriate Content-Type for the outer message
|
// Set the appropriate Content-Type for the outer message
|
||||||
let outer_message = PartBuilder::new().header((
|
let outer_message = PartBuilder::new().header((
|
||||||
@@ -786,11 +789,12 @@ impl<'a> MimeFactory<'a> {
|
|||||||
{
|
{
|
||||||
message
|
message
|
||||||
} else {
|
} else {
|
||||||
|
let message = message.header(get_content_type_directives_header());
|
||||||
let (payload, signature) = encrypt_helper.sign(context, message).await?;
|
let (payload, signature) = encrypt_helper.sign(context, message).await?;
|
||||||
PartBuilder::new()
|
PartBuilder::new()
|
||||||
.header((
|
.header((
|
||||||
"Content-Type".to_string(),
|
"Content-Type",
|
||||||
"multipart/signed; protocol=\"application/pgp-signature\"".to_string(),
|
"multipart/signed; protocol=\"application/pgp-signature\"",
|
||||||
))
|
))
|
||||||
.child(payload)
|
.child(payload)
|
||||||
.child(
|
.child(
|
||||||
@@ -1544,6 +1548,7 @@ fn maybe_encode_words(words: &str) -> String {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use mailparse::{addrparse_header, MailHeaderMap};
|
use mailparse::{addrparse_header, MailHeaderMap};
|
||||||
|
use std::str;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::chat::ChatId;
|
use crate::chat::ChatId;
|
||||||
@@ -1552,10 +1557,11 @@ mod tests {
|
|||||||
ProtectionStatus,
|
ProtectionStatus,
|
||||||
};
|
};
|
||||||
use crate::chatlist::Chatlist;
|
use crate::chatlist::Chatlist;
|
||||||
|
use crate::constants;
|
||||||
use crate::contact::{ContactAddress, Origin};
|
use crate::contact::{ContactAddress, Origin};
|
||||||
use crate::mimeparser::MimeMessage;
|
use crate::mimeparser::MimeMessage;
|
||||||
use crate::receive_imf::receive_imf;
|
use crate::receive_imf::receive_imf;
|
||||||
use crate::test_utils::{get_chat_msg, TestContext};
|
use crate::test_utils::{get_chat_msg, TestContext, TestContextManager};
|
||||||
#[test]
|
#[test]
|
||||||
fn test_render_email_address() {
|
fn test_render_email_address() {
|
||||||
let display_name = "ä space";
|
let display_name = "ä space";
|
||||||
@@ -2204,7 +2210,11 @@ mod tests {
|
|||||||
assert_eq!(part.match_indices("Chat-User-Avatar:").count(), 0);
|
assert_eq!(part.match_indices("Chat-User-Avatar:").count(), 0);
|
||||||
|
|
||||||
let part = payload.next().unwrap();
|
let part = payload.next().unwrap();
|
||||||
assert_eq!(part.match_indices("multipart/mixed").count(), 1);
|
assert_eq!(
|
||||||
|
part.match_indices("multipart/mixed; protected-headers=\"v1\"")
|
||||||
|
.count(),
|
||||||
|
1
|
||||||
|
);
|
||||||
assert_eq!(part.match_indices("Subject:").count(), 1);
|
assert_eq!(part.match_indices("Subject:").count(), 1);
|
||||||
assert_eq!(part.match_indices("Autocrypt:").count(), 0);
|
assert_eq!(part.match_indices("Autocrypt:").count(), 0);
|
||||||
assert_eq!(part.match_indices("Chat-User-Avatar:").count(), 0);
|
assert_eq!(part.match_indices("Chat-User-Avatar:").count(), 0);
|
||||||
@@ -2316,4 +2326,37 @@ mod tests {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||||
|
async fn test_protected_headers_directive() -> Result<()> {
|
||||||
|
let mut tcm = TestContextManager::new();
|
||||||
|
let alice = tcm.alice().await;
|
||||||
|
let bob = tcm.bob().await;
|
||||||
|
let chat = tcm
|
||||||
|
.send_recv_accept(&alice, &bob, "alice->bob")
|
||||||
|
.await
|
||||||
|
.chat_id;
|
||||||
|
|
||||||
|
// Now Bob can send an encrypted message to Alice.
|
||||||
|
let mut msg = Message::new(Viewtype::File);
|
||||||
|
// Long messages are truncated and MimeMessage::decoded_data is set for them. We need
|
||||||
|
// decoded_data to check presense of the necessary headers.
|
||||||
|
msg.set_text("a".repeat(constants::DC_DESIRED_TEXT_LEN + 1));
|
||||||
|
msg.set_file_from_bytes(&bob, "foo.bar", "content".as_bytes(), None)
|
||||||
|
.await?;
|
||||||
|
let sent = bob.send_msg(chat, &mut msg).await;
|
||||||
|
assert!(msg.get_showpadlock());
|
||||||
|
|
||||||
|
let mime = MimeMessage::from_bytes(&alice, sent.payload.as_bytes(), None).await?;
|
||||||
|
let mut payload = str::from_utf8(&mime.decoded_data)?.splitn(2, "\r\n\r\n");
|
||||||
|
let part = payload.next().unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
part.match_indices("multipart/mixed; protected-headers=\"v1\"")
|
||||||
|
.count(),
|
||||||
|
1
|
||||||
|
);
|
||||||
|
assert_eq!(part.match_indices("Subject:").count(), 1);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,12 +4,20 @@ use std::time::Duration;
|
|||||||
|
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use mime::Mime;
|
use mime::Mime;
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
|
||||||
use crate::context::Context;
|
use crate::context::Context;
|
||||||
use crate::socks::Socks5Config;
|
use crate::socks::Socks5Config;
|
||||||
|
|
||||||
const HTTP_TIMEOUT: Duration = Duration::from_secs(30);
|
const HTTP_TIMEOUT: Duration = Duration::from_secs(30);
|
||||||
|
|
||||||
|
static LETSENCRYPT_ROOT: Lazy<reqwest::tls::Certificate> = Lazy::new(|| {
|
||||||
|
reqwest::tls::Certificate::from_der(include_bytes!(
|
||||||
|
"../../assets/root-certificates/letsencrypt/isrgrootx1.der"
|
||||||
|
))
|
||||||
|
.unwrap()
|
||||||
|
});
|
||||||
|
|
||||||
/// HTTP(S) GET response.
|
/// HTTP(S) GET response.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Response {
|
pub struct Response {
|
||||||
@@ -79,7 +87,10 @@ async fn read_url_inner(context: &Context, url: &str) -> Result<reqwest::Respons
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn get_client(socks5_config: Option<Socks5Config>) -> Result<reqwest::Client> {
|
pub(crate) fn get_client(socks5_config: Option<Socks5Config>) -> Result<reqwest::Client> {
|
||||||
let builder = reqwest::ClientBuilder::new().timeout(HTTP_TIMEOUT);
|
let builder = reqwest::ClientBuilder::new()
|
||||||
|
.timeout(HTTP_TIMEOUT)
|
||||||
|
.add_root_certificate(LETSENCRYPT_ROOT.clone());
|
||||||
|
|
||||||
let builder = if let Some(socks5_config) = socks5_config {
|
let builder = if let Some(socks5_config) = socks5_config {
|
||||||
let proxy = reqwest::Proxy::all(socks5_config.to_url())?;
|
let proxy = reqwest::Proxy::all(socks5_config.to_url())?;
|
||||||
builder.proxy(proxy)
|
builder.proxy(proxy)
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use core::fmt;
|
use core::fmt;
|
||||||
|
use std::cmp::min;
|
||||||
use std::{iter::once, ops::Deref, sync::Arc};
|
use std::{iter::once, ops::Deref, sync::Arc};
|
||||||
|
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
@@ -457,7 +458,8 @@ impl Context {
|
|||||||
} else {
|
} else {
|
||||||
"green"
|
"green"
|
||||||
};
|
};
|
||||||
ret += &format!("<div class=\"bar\"><div class=\"progress {color}\" style=\"width: {percent}%\">{percent}%</div></div>");
|
let div_width_percent = min(100, percent);
|
||||||
|
ret += &format!("<div class=\"bar\"><div class=\"progress {color}\" style=\"width: {div_width_percent}%\">{percent}%</div></div>");
|
||||||
|
|
||||||
ret += "</li>";
|
ret += "</li>";
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user