mirror of
https://github.com/chatmail/core.git
synced 2026-07-14 23:13:05 +03:00
Compare commits
5 Commits
v1.150.0
...
link2xt/pg
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b0994381c0 | ||
|
|
a1bb0c6111 | ||
|
|
26a44075b2 | ||
|
|
db9470c54c | ||
|
|
5a9a125bfd |
72
CHANGELOG.md
72
CHANGELOG.md
@@ -1,76 +1,5 @@
|
||||
# Changelog
|
||||
|
||||
## [1.150.0] - 2024-11-21
|
||||
|
||||
### API-Changes
|
||||
|
||||
- Correct `DC_CERTCK_ACCEPT_*` values and docs ([#6176](https://github.com/deltachat/deltachat-core-rust/pull/6176)).
|
||||
|
||||
### Features / Changes
|
||||
|
||||
- Use Rustls for connections with strict TLS ([#6186](https://github.com/deltachat/deltachat-core-rust/pull/6186)).
|
||||
- Experimental header protection for Autocrypt.
|
||||
- Tune down io-not-started info in connectivity-html.
|
||||
- Clear config cache in start_io() ([#6228](https://github.com/deltachat/deltachat-core-rust/pull/6228)).
|
||||
- Line-before-quote may be up to 120 character long instead of 80.
|
||||
- Use i.delta.chat in qr codes ([#6223](https://github.com/deltachat/deltachat-core-rust/pull/6223)).
|
||||
|
||||
### Fixes
|
||||
|
||||
- Prevent accidental wrong-password-notifications ([#6122](https://github.com/deltachat/deltachat-core-rust/pull/6122)).
|
||||
- Remove footers from "Show Full Message...".
|
||||
- `send_msg_to_smtp`: Return Ok if `smtp` row is deleted in parallel.
|
||||
- Only add "member added/removed" messages if they actually do that ([#5992](https://github.com/deltachat/deltachat-core-rust/pull/5992)).
|
||||
- Do not fail to load chatlist summary if the message got removed.
|
||||
- deltachat-jsonrpc: Do not fail `get_chatlist_items_by_entries` if the message got deleted.
|
||||
- deltachat-jsonrpc: Do not fail `get_draft` if draft is deleted.
|
||||
- `markseen_msgs`: Limit not yet downloaded messages state to `InNoticed` ([#2970](https://github.com/deltachat/deltachat-core-rust/pull/2970)).
|
||||
- Update state of message when fully downloading it.
|
||||
- Dont overwrite equal drafts ([#6212](https://github.com/deltachat/deltachat-core-rust/pull/6212)).
|
||||
|
||||
### Build system
|
||||
|
||||
- Silence RUSTSEC-2024-0384.
|
||||
- cargo: Update rPGP from 0.13.2 to 0.14.0.
|
||||
- cargo: Update futures-concurrency from 7.6.1 to 7.6.2.
|
||||
- Update flake.nix ([#6200](https://github.com/deltachat/deltachat-core-rust/pull/6200))
|
||||
|
||||
### CI
|
||||
|
||||
- Ensure flake is formatted.
|
||||
|
||||
### Documentation
|
||||
|
||||
- Scanned proxies are added and normalized.
|
||||
|
||||
### Refactor
|
||||
|
||||
- Fix nightly clippy warnings.
|
||||
- Remove slicing from `is_file_in_use`.
|
||||
- Remove unnecessary `allow(clippy::indexing_slicing)`.
|
||||
- Don't use slicing in `remove_nonstandard_footer`.
|
||||
- Do not use slicing in `qr` module.
|
||||
- Eliminate indexing in `compute_mailinglist_name`.
|
||||
- Remove unused `allow(clippy::indexing_slicing)`.
|
||||
- Remove indexing/slicing from `remove_message_footer`.
|
||||
- Remove indexing/slicing from `squash_attachment_parts`.
|
||||
- Remove unused allow(clippy::indexing_slicing) for heuristically_parse_ndn.
|
||||
- Remove indexing/slicing from `parse_message_ids`.
|
||||
- Remove slicing from `remove_bottom_quote`.
|
||||
- Get rid of slicing in `remove_top_quote`.
|
||||
- Remove unused allow(clippy::indexing_slicing) from 'truncate'.
|
||||
- Forbid clippy::indexing_slicing.
|
||||
- Forbid clippy::string_slice.
|
||||
- Delete chat in a transaction.
|
||||
- Fix typo in `context.rs`.
|
||||
|
||||
### Tests
|
||||
|
||||
- Remove all calls to print() from deltachat-rpc-client tests.
|
||||
- Reply to protected group from MUA.
|
||||
- Mark not downloaded message as seen ([#2970](https://github.com/deltachat/deltachat-core-rust/pull/2970)).
|
||||
- Mark `receive_imf()` as only for tests and "internals" feature ([#6235](https://github.com/deltachat/deltachat-core-rust/pull/6235)).
|
||||
|
||||
## [1.149.0] - 2024-11-05
|
||||
|
||||
### Build system
|
||||
@@ -5300,4 +5229,3 @@ https://github.com/deltachat/deltachat-core-rust/pulls?q=is%3Apr+is%3Aclosed
|
||||
[1.148.6]: https://github.com/deltachat/deltachat-core-rust/compare/v1.148.5..v1.148.6
|
||||
[1.148.7]: https://github.com/deltachat/deltachat-core-rust/compare/v1.148.6..v1.148.7
|
||||
[1.149.0]: https://github.com/deltachat/deltachat-core-rust/compare/v1.148.7..v1.149.0
|
||||
[1.150.0]: https://github.com/deltachat/deltachat-core-rust/compare/v1.149.0..v1.150.0
|
||||
|
||||
10
Cargo.lock
generated
10
Cargo.lock
generated
@@ -1306,7 +1306,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "deltachat"
|
||||
version = "1.150.0"
|
||||
version = "1.149.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-broadcast",
|
||||
@@ -1406,7 +1406,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "deltachat-jsonrpc"
|
||||
version = "1.150.0"
|
||||
version = "1.149.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-channel 2.3.1",
|
||||
@@ -1431,7 +1431,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "deltachat-repl"
|
||||
version = "1.150.0"
|
||||
version = "1.149.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"deltachat",
|
||||
@@ -1447,7 +1447,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "deltachat-rpc-server"
|
||||
version = "1.150.0"
|
||||
version = "1.149.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"deltachat",
|
||||
@@ -1476,7 +1476,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "deltachat_ffi"
|
||||
version = "1.150.0"
|
||||
version = "1.149.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"deltachat",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "deltachat"
|
||||
version = "1.150.0"
|
||||
version = "1.149.0"
|
||||
edition = "2021"
|
||||
license = "MPL-2.0"
|
||||
rust-version = "1.77"
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
clippy::cloned_instead_of_copied
|
||||
)]
|
||||
#![cfg_attr(not(test), forbid(clippy::indexing_slicing))]
|
||||
#![cfg_attr(not(test), forbid(clippy::string_slice))]
|
||||
#![allow(
|
||||
clippy::match_bool,
|
||||
clippy::mixed_read_write_in_expression,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "deltachat_ffi"
|
||||
version = "1.150.0"
|
||||
version = "1.149.0"
|
||||
description = "Deltachat FFI"
|
||||
edition = "2018"
|
||||
readme = "README.md"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "deltachat-jsonrpc"
|
||||
version = "1.150.0"
|
||||
version = "1.149.0"
|
||||
description = "DeltaChat JSON-RPC API"
|
||||
edition = "2021"
|
||||
default-run = "deltachat-jsonrpc-server"
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#![recursion_limit = "256"]
|
||||
#![cfg_attr(not(test), forbid(clippy::indexing_slicing))]
|
||||
#![cfg_attr(not(test), forbid(clippy::string_slice))]
|
||||
pub mod api;
|
||||
pub use yerpc;
|
||||
|
||||
|
||||
@@ -58,5 +58,5 @@
|
||||
},
|
||||
"type": "module",
|
||||
"types": "dist/deltachat.d.ts",
|
||||
"version": "1.150.0"
|
||||
"version": "1.149.0"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "deltachat-repl"
|
||||
version = "1.150.0"
|
||||
version = "1.149.0"
|
||||
license = "MPL-2.0"
|
||||
edition = "2021"
|
||||
repository = "https://github.com/deltachat/deltachat-core-rust"
|
||||
|
||||
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "deltachat-rpc-client"
|
||||
version = "1.150.0"
|
||||
version = "1.149.0"
|
||||
description = "Python client for Delta Chat core JSON-RPC interface"
|
||||
classifiers = [
|
||||
"Development Status :: 5 - Production/Stable",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "deltachat-rpc-server"
|
||||
version = "1.150.0"
|
||||
version = "1.149.0"
|
||||
description = "DeltaChat JSON-RPC server"
|
||||
edition = "2021"
|
||||
readme = "README.md"
|
||||
|
||||
@@ -15,5 +15,5 @@
|
||||
},
|
||||
"type": "module",
|
||||
"types": "index.d.ts",
|
||||
"version": "1.150.0"
|
||||
"version": "1.149.0"
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
//!
|
||||
//! For received messages, DelSp parameter is honoured.
|
||||
#![cfg_attr(not(test), forbid(clippy::indexing_slicing))]
|
||||
#![cfg_attr(not(test), forbid(clippy::string_slice))]
|
||||
|
||||
/// Wraps line to 72 characters using format=flowed soft breaks.
|
||||
///
|
||||
|
||||
@@ -55,5 +55,5 @@
|
||||
"test:mocha": "mocha node/test/test.mjs --growl --reporter=spec --bail --exit"
|
||||
},
|
||||
"types": "node/dist/index.d.ts",
|
||||
"version": "1.150.0"
|
||||
"version": "1.149.0"
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "deltachat"
|
||||
version = "1.150.0"
|
||||
version = "1.149.0"
|
||||
description = "Python bindings for the Delta Chat Core library using CFFI against the Rust-implemented libdeltachat"
|
||||
readme = "README.rst"
|
||||
requires-python = ">=3.7"
|
||||
|
||||
@@ -705,7 +705,7 @@ class TestOfflineChat:
|
||||
ac1 = acfactory.get_pseudo_configured_account()
|
||||
ac2 = acfactory.get_pseudo_configured_account()
|
||||
qr = ac1.get_setup_contact_qr()
|
||||
assert qr.startswith("https://i.delta.chat")
|
||||
assert qr.startswith("OPENPGP4FPR:")
|
||||
res = ac2.check_qr(qr)
|
||||
assert res.is_ask_verifycontact()
|
||||
assert not res.is_ask_verifygroup()
|
||||
|
||||
@@ -1 +1 @@
|
||||
2024-11-21
|
||||
2024-11-05
|
||||
28
src/chat.rs
28
src/chat.rs
@@ -765,19 +765,27 @@ impl ChatId {
|
||||
);
|
||||
|
||||
let chat = Chat::load_from_db(context, self).await?;
|
||||
context
|
||||
.sql
|
||||
.execute(
|
||||
"DELETE FROM msgs_mdns WHERE msg_id IN (SELECT id FROM msgs WHERE chat_id=?);",
|
||||
(self,),
|
||||
)
|
||||
.await?;
|
||||
|
||||
context
|
||||
.sql
|
||||
.transaction(|transaction| {
|
||||
transaction.execute(
|
||||
"DELETE FROM msgs_mdns WHERE msg_id IN (SELECT id FROM msgs WHERE chat_id=?)",
|
||||
(self,),
|
||||
)?;
|
||||
transaction.execute("DELETE FROM msgs WHERE chat_id=?", (self,))?;
|
||||
transaction.execute("DELETE FROM chats_contacts WHERE chat_id=?", (self,))?;
|
||||
transaction.execute("DELETE FROM chats WHERE id=?", (self,))?;
|
||||
Ok(())
|
||||
})
|
||||
.execute("DELETE FROM msgs WHERE chat_id=?;", (self,))
|
||||
.await?;
|
||||
|
||||
context
|
||||
.sql
|
||||
.execute("DELETE FROM chats_contacts WHERE chat_id=?;", (self,))
|
||||
.await?;
|
||||
|
||||
context
|
||||
.sql
|
||||
.execute("DELETE FROM chats WHERE id=?;", (self,))
|
||||
.await?;
|
||||
|
||||
context.emit_msgs_changed_without_ids();
|
||||
|
||||
@@ -472,14 +472,6 @@ impl Context {
|
||||
// Allow at least 1 message every second + a burst of 3.
|
||||
*lock = Ratelimit::new(Duration::new(3, 0), 3.0);
|
||||
}
|
||||
|
||||
// The next line is mainly for iOS:
|
||||
// iOS starts a separate process for receiving notifications and if the user concurrently
|
||||
// starts the app, the UI process opens the database but waits with calling start_io()
|
||||
// until the notifications process finishes.
|
||||
// Now, some configs may have changed, so, we need to invalidate the cache.
|
||||
self.sql.config_cache.write().await.clear();
|
||||
|
||||
self.scheduler.start(self.clone()).await;
|
||||
}
|
||||
|
||||
@@ -2072,41 +2064,4 @@ mod tests {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_cache_is_cleared_when_io_is_started() -> Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
assert_eq!(
|
||||
alice.get_config(Config::ShowEmails).await?,
|
||||
Some("2".to_string())
|
||||
);
|
||||
|
||||
// Change the config circumventing the cache
|
||||
// This simulates what the notification plugin on iOS might do
|
||||
// because it runs in a different process
|
||||
alice
|
||||
.sql
|
||||
.execute(
|
||||
"INSERT OR REPLACE INTO config (keyname, value) VALUES ('show_emails', '0')",
|
||||
(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
// Alice's Delta Chat doesn't know about it yet:
|
||||
assert_eq!(
|
||||
alice.get_config(Config::ShowEmails).await?,
|
||||
Some("2".to_string())
|
||||
);
|
||||
|
||||
// Starting IO will fail of course because no server settings are configured,
|
||||
// but it should invalidate the caches:
|
||||
alice.start_io().await;
|
||||
|
||||
assert_eq!(
|
||||
alice.get_config(Config::ShowEmails).await?,
|
||||
Some("0".to_string())
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
10
src/key.rs
10
src/key.rs
@@ -382,9 +382,9 @@ pub async fn preconfigure_keypair(context: &Context, secret_data: &str) -> Resul
|
||||
pub struct Fingerprint(Vec<u8>);
|
||||
|
||||
impl Fingerprint {
|
||||
/// Creates new 160-bit (20 bytes) fingerprint.
|
||||
/// Creates new 160-bit (20 bytes) or 256-bit (32 bytes) fingerprint.
|
||||
pub fn new(v: Vec<u8>) -> Fingerprint {
|
||||
debug_assert_eq!(v.len(), 20);
|
||||
debug_assert!(v.len() == 20 || v.len() == 32);
|
||||
Fingerprint(v)
|
||||
}
|
||||
|
||||
@@ -438,7 +438,11 @@ impl std::str::FromStr for Fingerprint {
|
||||
.filter(|&c| c.is_ascii_hexdigit())
|
||||
.collect();
|
||||
let v: Vec<u8> = hex::decode(&hex_repr)?;
|
||||
ensure!(v.len() == 20, "wrong fingerprint length: {}", hex_repr);
|
||||
ensure!(
|
||||
v.len() == 20 || v.len() == 32,
|
||||
"wrong fingerprint length: {}",
|
||||
hex_repr
|
||||
);
|
||||
let fp = Fingerprint::new(v);
|
||||
Ok(fp)
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
clippy::cloned_instead_of_copied
|
||||
)]
|
||||
#![cfg_attr(not(test), forbid(clippy::indexing_slicing))]
|
||||
#![cfg_attr(not(test), forbid(clippy::string_slice))]
|
||||
#![allow(
|
||||
clippy::match_bool,
|
||||
clippy::mixed_read_write_in_expression,
|
||||
|
||||
117
src/message.rs
117
src/message.rs
@@ -1693,7 +1693,6 @@ pub async fn markseen_msgs(context: &Context, msg_ids: Vec<MsgId>) -> Result<()>
|
||||
m.id AS id,
|
||||
m.chat_id AS chat_id,
|
||||
m.state AS state,
|
||||
m.download_state as download_state,
|
||||
m.ephemeral_timer AS ephemeral_timer,
|
||||
m.param AS param,
|
||||
m.from_id AS from_id,
|
||||
@@ -1709,7 +1708,6 @@ pub async fn markseen_msgs(context: &Context, msg_ids: Vec<MsgId>) -> Result<()>
|
||||
let id: MsgId = row.get("id")?;
|
||||
let chat_id: ChatId = row.get("chat_id")?;
|
||||
let state: MessageState = row.get("state")?;
|
||||
let download_state: DownloadState = row.get("download_state")?;
|
||||
let param: Params = row.get::<_, String>("param")?.parse().unwrap_or_default();
|
||||
let from_id: ContactId = row.get("from_id")?;
|
||||
let rfc724_mid: String = row.get("rfc724_mid")?;
|
||||
@@ -1721,7 +1719,6 @@ pub async fn markseen_msgs(context: &Context, msg_ids: Vec<MsgId>) -> Result<()>
|
||||
id,
|
||||
chat_id,
|
||||
state,
|
||||
download_state,
|
||||
param,
|
||||
from_id,
|
||||
rfc724_mid,
|
||||
@@ -1751,7 +1748,6 @@ pub async fn markseen_msgs(context: &Context, msg_ids: Vec<MsgId>) -> Result<()>
|
||||
id,
|
||||
curr_chat_id,
|
||||
curr_state,
|
||||
curr_download_state,
|
||||
curr_param,
|
||||
curr_from_id,
|
||||
curr_rfc724_mid,
|
||||
@@ -1761,14 +1757,7 @@ pub async fn markseen_msgs(context: &Context, msg_ids: Vec<MsgId>) -> Result<()>
|
||||
_curr_ephemeral_timer,
|
||||
) in msgs
|
||||
{
|
||||
if curr_download_state != DownloadState::Done {
|
||||
if curr_state == MessageState::InFresh {
|
||||
// Don't mark partially downloaded messages as seen or send a read receipt since
|
||||
// they are not really seen by the user.
|
||||
update_msg_state(context, id, MessageState::InNoticed).await?;
|
||||
updated_chat_ids.insert(curr_chat_id);
|
||||
}
|
||||
} else if curr_state == MessageState::InFresh || curr_state == MessageState::InNoticed {
|
||||
if curr_state == MessageState::InFresh || curr_state == MessageState::InNoticed {
|
||||
update_msg_state(context, id, MessageState::InSeen).await?;
|
||||
info!(context, "Seen message {}.", id);
|
||||
|
||||
@@ -2594,110 +2583,6 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_markseen_not_downloaded_msg() -> Result<()> {
|
||||
let mut tcm = TestContextManager::new();
|
||||
let alice = &tcm.alice().await;
|
||||
alice.set_config(Config::DownloadLimit, Some("1")).await?;
|
||||
let bob = &tcm.bob().await;
|
||||
let bob_chat_id = tcm.send_recv_accept(alice, bob, "hi").await.chat_id;
|
||||
|
||||
let file_bytes = include_bytes!("../test-data/image/screenshot.png");
|
||||
let mut msg = Message::new(Viewtype::Image);
|
||||
msg.set_file_from_bytes(bob, "a.jpg", file_bytes, None)
|
||||
.await?;
|
||||
let sent_msg = bob.send_msg(bob_chat_id, &mut msg).await;
|
||||
let msg = alice.recv_msg(&sent_msg).await;
|
||||
assert_eq!(msg.download_state, DownloadState::Available);
|
||||
assert!(!msg.param.get_bool(Param::WantsMdn).unwrap_or_default());
|
||||
assert_eq!(msg.state, MessageState::InFresh);
|
||||
markseen_msgs(alice, vec![msg.id]).await?;
|
||||
// A not downloaded message can be seen only if it's seen on another device.
|
||||
assert_eq!(msg.id.get_state(alice).await?, MessageState::InNoticed);
|
||||
// Marking the message as seen again is a no op.
|
||||
markseen_msgs(alice, vec![msg.id]).await?;
|
||||
assert_eq!(msg.id.get_state(alice).await?, MessageState::InNoticed);
|
||||
|
||||
msg.id
|
||||
.update_download_state(alice, DownloadState::InProgress)
|
||||
.await?;
|
||||
markseen_msgs(alice, vec![msg.id]).await?;
|
||||
assert_eq!(msg.id.get_state(alice).await?, MessageState::InNoticed);
|
||||
msg.id
|
||||
.update_download_state(alice, DownloadState::Failure)
|
||||
.await?;
|
||||
markseen_msgs(alice, vec![msg.id]).await?;
|
||||
assert_eq!(msg.id.get_state(alice).await?, MessageState::InNoticed);
|
||||
msg.id
|
||||
.update_download_state(alice, DownloadState::Undecipherable)
|
||||
.await?;
|
||||
markseen_msgs(alice, vec![msg.id]).await?;
|
||||
assert_eq!(msg.id.get_state(alice).await?, MessageState::InNoticed);
|
||||
|
||||
assert!(
|
||||
!alice
|
||||
.sql
|
||||
.exists("SELECT COUNT(*) FROM smtp_mdns", ())
|
||||
.await?
|
||||
);
|
||||
|
||||
alice.set_config(Config::DownloadLimit, None).await?;
|
||||
// Let's assume that Alice and Bob resolved the problem with encryption.
|
||||
let old_msg = msg;
|
||||
let msg = alice.recv_msg(&sent_msg).await;
|
||||
assert_eq!(msg.chat_id, old_msg.chat_id);
|
||||
assert_eq!(msg.download_state, DownloadState::Done);
|
||||
assert!(msg.param.get_bool(Param::WantsMdn).unwrap_or_default());
|
||||
assert!(msg.get_showpadlock());
|
||||
// The message state mustn't be downgraded to `InFresh`.
|
||||
assert_eq!(msg.state, MessageState::InNoticed);
|
||||
markseen_msgs(alice, vec![msg.id]).await?;
|
||||
let msg = Message::load_from_db(alice, msg.id).await?;
|
||||
assert_eq!(msg.state, MessageState::InSeen);
|
||||
assert_eq!(
|
||||
alice
|
||||
.sql
|
||||
.count("SELECT COUNT(*) FROM smtp_mdns", ())
|
||||
.await?,
|
||||
1
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_msg_seen_on_imap_when_downloaded() -> Result<()> {
|
||||
let mut tcm = TestContextManager::new();
|
||||
let alice = &tcm.alice().await;
|
||||
alice.set_config(Config::DownloadLimit, Some("1")).await?;
|
||||
let bob = &tcm.bob().await;
|
||||
let bob_chat_id = tcm.send_recv_accept(alice, bob, "hi").await.chat_id;
|
||||
|
||||
let file_bytes = include_bytes!("../test-data/image/screenshot.png");
|
||||
let mut msg = Message::new(Viewtype::Image);
|
||||
msg.set_file_from_bytes(bob, "a.jpg", file_bytes, None)
|
||||
.await?;
|
||||
let sent_msg = bob.send_msg(bob_chat_id, &mut msg).await;
|
||||
let msg = alice.recv_msg(&sent_msg).await;
|
||||
assert_eq!(msg.download_state, DownloadState::Available);
|
||||
assert_eq!(msg.state, MessageState::InFresh);
|
||||
|
||||
alice.set_config(Config::DownloadLimit, None).await?;
|
||||
let seen = true;
|
||||
let rcvd_msg = receive_imf(alice, sent_msg.payload().as_bytes(), seen)
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
assert_eq!(rcvd_msg.chat_id, msg.chat_id);
|
||||
let msg = Message::load_from_db(alice, *rcvd_msg.msg_ids.last().unwrap())
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(msg.download_state, DownloadState::Done);
|
||||
assert!(msg.param.get_bool(Param::WantsMdn).unwrap_or_default());
|
||||
assert!(msg.get_showpadlock());
|
||||
assert_eq!(msg.state, MessageState::InSeen);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_get_state() -> Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
|
||||
@@ -11,7 +11,6 @@ use pgp::composed::{
|
||||
Deserializable, KeyType as PgpKeyType, Message, SecretKeyParamsBuilder, SignedPublicKey,
|
||||
SignedPublicSubKey, SignedSecretKey, StandaloneSignature, SubkeyParamsBuilder,
|
||||
};
|
||||
use pgp::crypto::ecc_curve::ECCCurve;
|
||||
use pgp::crypto::hash::HashAlgorithm;
|
||||
use pgp::crypto::sym::SymmetricKeyAlgorithm;
|
||||
use pgp::types::{CompressionAlgorithm, PublicKeyTrait, SignatureBytes, StringToKey};
|
||||
@@ -187,14 +186,12 @@ pub(crate) fn create_keypair(addr: EmailAddress, keygen_type: KeyGenType) -> Res
|
||||
let (signing_key_type, encryption_key_type) = match keygen_type {
|
||||
KeyGenType::Rsa2048 => (PgpKeyType::Rsa(2048), PgpKeyType::Rsa(2048)),
|
||||
KeyGenType::Rsa4096 => (PgpKeyType::Rsa(4096), PgpKeyType::Rsa(4096)),
|
||||
KeyGenType::Ed25519 | KeyGenType::Default => (
|
||||
PgpKeyType::EdDSALegacy,
|
||||
PgpKeyType::ECDH(ECCCurve::Curve25519),
|
||||
),
|
||||
KeyGenType::Ed25519 | KeyGenType::Default => (PgpKeyType::Ed25519, PgpKeyType::X25519),
|
||||
};
|
||||
|
||||
let user_id = format!("<{addr}>");
|
||||
let key_params = SecretKeyParamsBuilder::default()
|
||||
.version(pgp::types::KeyVersion::V6)
|
||||
.key_type(signing_key_type)
|
||||
.can_certify(true)
|
||||
.can_sign(true)
|
||||
@@ -218,6 +215,7 @@ pub(crate) fn create_keypair(addr: EmailAddress, keygen_type: KeyGenType) -> Res
|
||||
])
|
||||
.subkey(
|
||||
SubkeyParamsBuilder::default()
|
||||
.version(pgp::types::KeyVersion::V6)
|
||||
.key_type(encryption_key_type)
|
||||
.can_encrypt(true)
|
||||
.passphrase(None)
|
||||
|
||||
15
src/qr.rs
15
src/qr.rs
@@ -807,7 +807,11 @@ async fn decode_mailto(context: &Context, qr: &str) -> Result<Qr> {
|
||||
.get(MAILTO_SCHEME.len()..)
|
||||
.context("Invalid mailto: scheme")?;
|
||||
|
||||
let (addr, query) = payload.split_once('?').unwrap_or((payload, ""));
|
||||
let (addr, query) = if let Some(query_index) = payload.find('?') {
|
||||
(&payload[..query_index], &payload[query_index + 1..])
|
||||
} else {
|
||||
(payload, "")
|
||||
};
|
||||
|
||||
let param: BTreeMap<&str, &str> = query
|
||||
.split('&')
|
||||
@@ -857,9 +861,12 @@ async fn decode_mailto(context: &Context, qr: &str) -> Result<Qr> {
|
||||
async fn decode_smtp(context: &Context, qr: &str) -> Result<Qr> {
|
||||
let payload = qr.get(SMTP_SCHEME.len()..).context("Invalid SMTP scheme")?;
|
||||
|
||||
let (addr, _rest) = payload
|
||||
.split_once(':')
|
||||
.context("Invalid SMTP scheme payload")?;
|
||||
let addr = if let Some(query_index) = payload.find(':') {
|
||||
&payload[..query_index]
|
||||
} else {
|
||||
bail!("Invalid SMTP found");
|
||||
};
|
||||
|
||||
let addr = normalize_address(addr)?;
|
||||
let name = "";
|
||||
Qr::from_address(context, name, &addr, None).await
|
||||
|
||||
@@ -6,7 +6,7 @@ use std::str::FromStr;
|
||||
use anyhow::{Context as _, Result};
|
||||
use deltachat_contact_tools::{addr_cmp, may_be_valid_addr, sanitize_single_line, ContactAddress};
|
||||
use iroh_gossip::proto::TopicId;
|
||||
use mailparse::SingleInfo;
|
||||
use mailparse::{parse_mail, SingleInfo};
|
||||
use num_traits::FromPrimitive;
|
||||
use once_cell::sync::Lazy;
|
||||
use regex::Regex;
|
||||
@@ -73,13 +73,12 @@ pub struct ReceivedMsg {
|
||||
///
|
||||
/// This method returns errors on a failure to parse the mail or extract Message-ID. It's only used
|
||||
/// for tests and REPL tool, not actual message reception pipeline.
|
||||
#[cfg(any(test, feature = "internals"))]
|
||||
pub async fn receive_imf(
|
||||
context: &Context,
|
||||
imf_raw: &[u8],
|
||||
seen: bool,
|
||||
) -> Result<Option<ReceivedMsg>> {
|
||||
let mail = mailparse::parse_mail(imf_raw).context("can't parse mail")?;
|
||||
let mail = parse_mail(imf_raw).context("can't parse mail")?;
|
||||
let rfc724_mid =
|
||||
imap::prefetch_get_message_id(&mail.headers).unwrap_or_else(imap::create_message_id);
|
||||
if let Some(download_limit) = context.download_limit().await? {
|
||||
@@ -106,7 +105,6 @@ pub async fn receive_imf(
|
||||
/// Emulates reception of a message from "INBOX".
|
||||
///
|
||||
/// Only used for tests and REPL tool, not actual message reception pipeline.
|
||||
#[cfg(any(test, feature = "internals"))]
|
||||
pub(crate) async fn receive_imf_from_inbox(
|
||||
context: &Context,
|
||||
rfc724_mid: &str,
|
||||
@@ -1577,7 +1575,7 @@ INSERT INTO msgs
|
||||
ON CONFLICT (id) DO UPDATE
|
||||
SET rfc724_mid=excluded.rfc724_mid, chat_id=excluded.chat_id,
|
||||
from_id=excluded.from_id, to_id=excluded.to_id, timestamp_sent=excluded.timestamp_sent,
|
||||
type=excluded.type, state=max(state,excluded.state), msgrmsg=excluded.msgrmsg,
|
||||
type=excluded.type, msgrmsg=excluded.msgrmsg,
|
||||
txt=excluded.txt, txt_normalized=excluded.txt_normalized, subject=excluded.subject,
|
||||
txt_raw=excluded.txt_raw, param=excluded.param,
|
||||
hidden=excluded.hidden,bytes=excluded.bytes, mime_headers=excluded.mime_headers,
|
||||
|
||||
@@ -104,7 +104,7 @@ pub async fn get_securejoin_qr(context: &Context, group: Option<ChatId>) -> Resu
|
||||
context.scheduler.interrupt_inbox().await;
|
||||
}
|
||||
format!(
|
||||
"https://i.delta.chat/#{}&a={}&g={}&x={}&i={}&s={}",
|
||||
"OPENPGP4FPR:{}#a={}&g={}&x={}&i={}&s={}",
|
||||
fingerprint.hex(),
|
||||
self_addr_urlencoded,
|
||||
&group_name_urlencoded,
|
||||
@@ -119,7 +119,7 @@ pub async fn get_securejoin_qr(context: &Context, group: Option<ChatId>) -> Resu
|
||||
context.scheduler.interrupt_inbox().await;
|
||||
}
|
||||
format!(
|
||||
"https://i.delta.chat/#{}&a={}&n={}&i={}&s={}",
|
||||
"OPENPGP4FPR:{}#a={}&n={}&i={}&s={}",
|
||||
fingerprint.hex(),
|
||||
self_addr_urlencoded,
|
||||
self_name_urlencoded,
|
||||
@@ -244,10 +244,10 @@ async fn verify_sender_by_fingerprint(
|
||||
|
||||
/// What to do with a Secure-Join handshake message after it was handled.
|
||||
///
|
||||
/// This status is returned to [`receive_imf_inner`] which will use it to decide what to do
|
||||
/// This status is returned to [`receive_imf`] which will use it to decide what to do
|
||||
/// next with this incoming setup-contact/secure-join handshake message.
|
||||
///
|
||||
/// [`receive_imf_inner`]: crate::receive_imf::receive_imf_inner
|
||||
/// [`receive_imf`]: crate::receive_imf::receive_imf
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub(crate) enum HandshakeMessage {
|
||||
/// The message has been fully handled and should be removed/delete.
|
||||
@@ -347,7 +347,7 @@ pub(crate) async fn handle_securejoin_handshake(
|
||||
send_alice_handshake_msg(
|
||||
context,
|
||||
contact_id,
|
||||
&format!("{}-auth-required", &step.get(..2).unwrap_or_default()),
|
||||
&format!("{}-auth-required", &step[..2]),
|
||||
)
|
||||
.await
|
||||
.context("failed sending auth-required handshake message")?;
|
||||
|
||||
31
src/tools.rs
31
src/tools.rs
@@ -47,27 +47,20 @@ use crate::stock_str;
|
||||
/// end of the shortened string.
|
||||
pub(crate) fn truncate(buf: &str, approx_chars: usize) -> Cow<str> {
|
||||
let count = buf.chars().count();
|
||||
if count <= approx_chars + DC_ELLIPSIS.len() {
|
||||
return Cow::Borrowed(buf);
|
||||
}
|
||||
let end_pos = buf
|
||||
.char_indices()
|
||||
.nth(approx_chars)
|
||||
.map(|(n, _)| n)
|
||||
.unwrap_or_default();
|
||||
if count > approx_chars + DC_ELLIPSIS.len() {
|
||||
let end_pos = buf
|
||||
.char_indices()
|
||||
.nth(approx_chars)
|
||||
.map(|(n, _)| n)
|
||||
.unwrap_or_default();
|
||||
|
||||
if let Some(index) = buf.get(..end_pos).and_then(|s| s.rfind([' ', '\n'])) {
|
||||
Cow::Owned(format!(
|
||||
"{}{}",
|
||||
&buf.get(..=index).unwrap_or_default(),
|
||||
DC_ELLIPSIS
|
||||
))
|
||||
if let Some(index) = buf[..end_pos].rfind([' ', '\n']) {
|
||||
Cow::Owned(format!("{}{}", &buf[..=index], DC_ELLIPSIS))
|
||||
} else {
|
||||
Cow::Owned(format!("{}{}", &buf[..end_pos], DC_ELLIPSIS))
|
||||
}
|
||||
} else {
|
||||
Cow::Owned(format!(
|
||||
"{}{}",
|
||||
&buf.get(..end_pos).unwrap_or_default(),
|
||||
DC_ELLIPSIS
|
||||
))
|
||||
Cow::Borrowed(buf)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user