mirror of
https://github.com/chatmail/core.git
synced 2026-05-05 14:26:30 +03:00
Merge branch 'master' into flub/send-backup
This commit is contained in:
@@ -644,7 +644,6 @@ Authentication-Results: dkim=";
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[ignore = "Disallowing keychanges is disabled for now"]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_handle_authres_fails() -> Result<()> {
|
||||
let mut tcm = TestContextManager::new();
|
||||
@@ -822,8 +821,7 @@ Authentication-Results: dkim=";
|
||||
.insert_str(0, "Authentication-Results: example.net; dkim=fail\n");
|
||||
let rcvd = bob.recv_msg(&sent).await;
|
||||
|
||||
// Disallowing keychanges is disabled for now:
|
||||
// assert!(rcvd.error.unwrap().contains("DKIM failed"));
|
||||
assert!(rcvd.error.unwrap().contains("DKIM failed"));
|
||||
// The message info should contain a warning:
|
||||
assert!(message::get_msg_info(&bob, rcvd.id)
|
||||
.await
|
||||
|
||||
66
src/chat.rs
66
src/chat.rs
@@ -19,7 +19,7 @@ use crate::color::str_to_color;
|
||||
use crate::config::Config;
|
||||
use crate::constants::{
|
||||
Blocked, Chattype, DC_CHAT_ID_ALLDONE_HINT, DC_CHAT_ID_ARCHIVED_LINK, DC_CHAT_ID_LAST_SPECIAL,
|
||||
DC_CHAT_ID_TRASH, DC_GCM_ADDDAYMARKER, DC_GCM_INFO_ONLY, DC_RESEND_USER_AVATAR_DAYS,
|
||||
DC_CHAT_ID_TRASH, DC_RESEND_USER_AVATAR_DAYS,
|
||||
};
|
||||
use crate::contact::{Contact, ContactId, Origin, VerifiedStatus};
|
||||
use crate::context::Context;
|
||||
@@ -2376,12 +2376,40 @@ pub async fn send_videochat_invitation(context: &Context, chat_id: ChatId) -> Re
|
||||
send_msg(context, chat_id, &mut msg).await
|
||||
}
|
||||
|
||||
pub async fn get_chat_msgs(
|
||||
/// Chat message list request options.
|
||||
#[derive(Debug)]
|
||||
pub struct MessageListOptions {
|
||||
/// Return only info messages.
|
||||
pub info_only: bool,
|
||||
|
||||
/// Add day markers before each date regarding the local timezone.
|
||||
pub add_daymarker: bool,
|
||||
}
|
||||
|
||||
/// Returns all messages belonging to the chat.
|
||||
pub async fn get_chat_msgs(context: &Context, chat_id: ChatId) -> Result<Vec<ChatItem>> {
|
||||
get_chat_msgs_ex(
|
||||
context,
|
||||
chat_id,
|
||||
MessageListOptions {
|
||||
info_only: false,
|
||||
add_daymarker: false,
|
||||
},
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
/// Returns messages belonging to the chat according to the given options.
|
||||
pub async fn get_chat_msgs_ex(
|
||||
context: &Context,
|
||||
chat_id: ChatId,
|
||||
flags: u32,
|
||||
options: MessageListOptions,
|
||||
) -> Result<Vec<ChatItem>> {
|
||||
let process_row = if (flags & DC_GCM_INFO_ONLY) != 0 {
|
||||
let MessageListOptions {
|
||||
info_only,
|
||||
add_daymarker,
|
||||
} = options;
|
||||
let process_row = if info_only {
|
||||
|row: &rusqlite::Row| {
|
||||
// is_info logic taken from Message.is_info()
|
||||
let params = row.get::<_, String>("param")?;
|
||||
@@ -2431,7 +2459,7 @@ pub async fn get_chat_msgs(
|
||||
let cnv_to_local = gm2local_offset();
|
||||
|
||||
for (ts, curr_id) in sorted_rows {
|
||||
if (flags & DC_GCM_ADDDAYMARKER) != 0 {
|
||||
if add_daymarker {
|
||||
let curr_local_timestamp = ts + cnv_to_local;
|
||||
let curr_day = curr_local_timestamp / 86400;
|
||||
if curr_day != last_day {
|
||||
@@ -2446,7 +2474,7 @@ pub async fn get_chat_msgs(
|
||||
Ok(ret)
|
||||
};
|
||||
|
||||
let items = if (flags & DC_GCM_INFO_ONLY) != 0 {
|
||||
let items = if info_only {
|
||||
context
|
||||
.sql
|
||||
.query_map(
|
||||
@@ -4942,7 +4970,7 @@ mod tests {
|
||||
assert!(chat.is_protected());
|
||||
assert!(chat.is_unpromoted());
|
||||
|
||||
let msgs = get_chat_msgs(&t, chat_id, 0).await?;
|
||||
let msgs = get_chat_msgs(&t, chat_id).await?;
|
||||
assert_eq!(msgs.len(), 1);
|
||||
|
||||
let msg = t.get_last_msg_in(chat_id).await;
|
||||
@@ -4971,7 +4999,7 @@ mod tests {
|
||||
assert!(!chat.is_protected());
|
||||
assert!(!chat.is_unpromoted());
|
||||
|
||||
let msgs = get_chat_msgs(&t, chat_id, 0).await?;
|
||||
let msgs = get_chat_msgs(&t, chat_id).await?;
|
||||
assert_eq!(msgs.len(), 3);
|
||||
|
||||
// enable protection on promoted chat, the info-message is sent via send_msg() this time
|
||||
@@ -5069,7 +5097,7 @@ mod tests {
|
||||
add_contact_to_chat(&alice, alice_chat_id, contact_id).await?;
|
||||
assert_eq!(get_chat_contacts(&alice, alice_chat_id).await?.len(), 2);
|
||||
send_text_msg(&alice, alice_chat_id, "hi!".to_string()).await?;
|
||||
assert_eq!(get_chat_msgs(&alice, alice_chat_id, 0).await?.len(), 1);
|
||||
assert_eq!(get_chat_msgs(&alice, alice_chat_id).await?.len(), 1);
|
||||
|
||||
// Alice has an SMTP-server replacing the `Message-ID:`-header (as done eg. by outlook.com).
|
||||
let sent_msg = alice.pop_sent_msg().await;
|
||||
@@ -5102,7 +5130,7 @@ mod tests {
|
||||
let msg = alice.get_last_msg().await;
|
||||
assert_eq!(msg.chat_id, alice_chat_id);
|
||||
assert_eq!(msg.text, Some("ho!".to_string()));
|
||||
assert_eq!(get_chat_msgs(&alice, alice_chat_id, 0).await?.len(), 2);
|
||||
assert_eq!(get_chat_msgs(&alice, alice_chat_id).await?.len(), 2);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -5130,7 +5158,7 @@ mod tests {
|
||||
assert_eq!(chat.id.get_fresh_msg_cnt(&t).await?, 1);
|
||||
assert_eq!(t.get_fresh_msgs().await?.len(), 1);
|
||||
|
||||
let msgs = get_chat_msgs(&t, chat.id, 0).await?;
|
||||
let msgs = get_chat_msgs(&t, chat.id).await?;
|
||||
assert_eq!(msgs.len(), 1);
|
||||
let msg_id = match msgs.first().unwrap() {
|
||||
ChatItem::Message { msg_id } => *msg_id,
|
||||
@@ -5180,7 +5208,7 @@ mod tests {
|
||||
.is_contact_request());
|
||||
assert_eq!(chat_id.get_msg_cnt(&t).await?, 1);
|
||||
assert_eq!(chat_id.get_fresh_msg_cnt(&t).await?, 1);
|
||||
let msgs = get_chat_msgs(&t, chat_id, 0).await?;
|
||||
let msgs = get_chat_msgs(&t, chat_id).await?;
|
||||
assert_eq!(msgs.len(), 1);
|
||||
let msg_id = match msgs.first().unwrap() {
|
||||
ChatItem::Message { msg_id } => *msg_id,
|
||||
@@ -5268,7 +5296,7 @@ mod tests {
|
||||
let chat_id = msg.chat_id;
|
||||
assert_eq!(chat_id.get_fresh_msg_cnt(&alice).await?, 1);
|
||||
|
||||
let msgs = get_chat_msgs(&alice, chat_id, 0).await?;
|
||||
let msgs = get_chat_msgs(&alice, chat_id).await?;
|
||||
assert_eq!(msgs.len(), 1);
|
||||
|
||||
// Alice disables receiving classic emails.
|
||||
@@ -5280,7 +5308,7 @@ mod tests {
|
||||
// Already received classic email should still be in the chat.
|
||||
assert_eq!(chat_id.get_fresh_msg_cnt(&alice).await?, 1);
|
||||
|
||||
let msgs = get_chat_msgs(&alice, chat_id, 0).await?;
|
||||
let msgs = get_chat_msgs(&alice, chat_id).await?;
|
||||
assert_eq!(msgs.len(), 1);
|
||||
|
||||
Ok(())
|
||||
@@ -5427,7 +5455,7 @@ mod tests {
|
||||
assert!(msg1.get_text().unwrap().contains("bob@example.net"));
|
||||
|
||||
let chat_id2 = ChatId::create_for_contact(&t, bob_id).await?;
|
||||
assert_eq!(get_chat_msgs(&t, chat_id2, 0).await?.len(), 0);
|
||||
assert_eq!(get_chat_msgs(&t, chat_id2).await?.len(), 0);
|
||||
forward_msgs(&t, &[msg1.id], chat_id2).await?;
|
||||
let msg2 = t.get_last_msg_in(chat_id2).await;
|
||||
assert!(!msg2.is_info()); // forwarded info-messages lose their info-state
|
||||
@@ -5596,15 +5624,15 @@ mod tests {
|
||||
let msg = bob.recv_msg(&sent1).await;
|
||||
assert_eq!(msg.get_text().unwrap(), "alice->bob");
|
||||
assert_eq!(get_chat_contacts(&bob, msg.chat_id).await?.len(), 2);
|
||||
assert_eq!(get_chat_msgs(&bob, msg.chat_id, 0).await?.len(), 1);
|
||||
assert_eq!(get_chat_msgs(&bob, msg.chat_id).await?.len(), 1);
|
||||
bob.recv_msg(&sent2).await;
|
||||
assert_eq!(get_chat_contacts(&bob, msg.chat_id).await?.len(), 3);
|
||||
assert_eq!(get_chat_msgs(&bob, msg.chat_id, 0).await?.len(), 2);
|
||||
assert_eq!(get_chat_msgs(&bob, msg.chat_id).await?.len(), 2);
|
||||
let received = bob.recv_msg_opt(&sent3).await;
|
||||
// No message should actually be added since we already know this message:
|
||||
assert!(received.is_none());
|
||||
assert_eq!(get_chat_contacts(&bob, msg.chat_id).await?.len(), 3);
|
||||
assert_eq!(get_chat_msgs(&bob, msg.chat_id, 0).await?.len(), 2);
|
||||
assert_eq!(get_chat_msgs(&bob, msg.chat_id).await?.len(), 2);
|
||||
|
||||
// Claire does not receive the first message, however, due to resending, she has a similar view as Alice and Bob
|
||||
let claire = TestContext::new().await;
|
||||
@@ -5613,7 +5641,7 @@ mod tests {
|
||||
let msg = claire.recv_msg(&sent3).await;
|
||||
assert_eq!(msg.get_text().unwrap(), "alice->bob");
|
||||
assert_eq!(get_chat_contacts(&claire, msg.chat_id).await?.len(), 3);
|
||||
assert_eq!(get_chat_msgs(&claire, msg.chat_id, 0).await?.len(), 2);
|
||||
assert_eq!(get_chat_msgs(&claire, msg.chat_id).await?.len(), 2);
|
||||
let msg_from = Contact::get_by_id(&claire, msg.get_from_id()).await?;
|
||||
assert_eq!(msg_from.get_addr(), "alice@example.org");
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use anyhow::{anyhow, format_err};
|
||||
|
||||
use crate::context::Context;
|
||||
use crate::socks::Socks5Config;
|
||||
|
||||
pub async fn read_url(context: &Context, url: &str) -> anyhow::Result<String> {
|
||||
match read_url_inner(context, url).await {
|
||||
@@ -16,7 +17,8 @@ pub async fn read_url(context: &Context, url: &str) -> anyhow::Result<String> {
|
||||
}
|
||||
|
||||
pub async fn read_url_inner(context: &Context, url: &str) -> anyhow::Result<String> {
|
||||
let client = crate::http::get_client()?;
|
||||
let socks5_config = Socks5Config::from_database(&context.sql).await?;
|
||||
let client = crate::http::get_client(socks5_config)?;
|
||||
let mut url = url.to_string();
|
||||
|
||||
// Follow up to 10 http-redirects
|
||||
|
||||
@@ -99,15 +99,6 @@ impl ServerParams {
|
||||
// Try common secure combinations.
|
||||
|
||||
vec![
|
||||
// Try STARTTLS
|
||||
Self {
|
||||
socket: Socket::Starttls,
|
||||
port: match self.protocol {
|
||||
Protocol::Imap => 143,
|
||||
Protocol::Smtp => 587,
|
||||
},
|
||||
..self.clone()
|
||||
},
|
||||
// Try TLS
|
||||
Self {
|
||||
socket: Socket::Ssl,
|
||||
@@ -115,6 +106,15 @@ impl ServerParams {
|
||||
Protocol::Imap => 993,
|
||||
Protocol::Smtp => 465,
|
||||
},
|
||||
..self.clone()
|
||||
},
|
||||
// Try STARTTLS
|
||||
Self {
|
||||
socket: Socket::Starttls,
|
||||
port: match self.protocol {
|
||||
Protocol::Imap => 143,
|
||||
Protocol::Smtp => 587,
|
||||
},
|
||||
..self
|
||||
},
|
||||
]
|
||||
@@ -343,5 +343,41 @@ mod tests {
|
||||
}
|
||||
],
|
||||
);
|
||||
|
||||
// Test that TLS is preferred to STARTTLS
|
||||
// when the port and security are not set.
|
||||
let v = expand_param_vector(
|
||||
vec![ServerParams {
|
||||
protocol: Protocol::Smtp,
|
||||
hostname: "example.net".to_string(),
|
||||
port: 0,
|
||||
socket: Socket::Automatic,
|
||||
username: "foobar".to_string(),
|
||||
strict_tls: Some(true),
|
||||
}],
|
||||
"foobar@example.net",
|
||||
"example.net",
|
||||
);
|
||||
assert_eq!(
|
||||
v,
|
||||
vec![
|
||||
ServerParams {
|
||||
protocol: Protocol::Smtp,
|
||||
hostname: "example.net".to_string(),
|
||||
port: 465,
|
||||
socket: Socket::Ssl,
|
||||
username: "foobar".to_string(),
|
||||
strict_tls: Some(true)
|
||||
},
|
||||
ServerParams {
|
||||
protocol: Protocol::Smtp,
|
||||
hostname: "example.net".to_string(),
|
||||
port: 587,
|
||||
socket: Socket::Starttls,
|
||||
username: "foobar".to_string(),
|
||||
strict_tls: Some(true)
|
||||
},
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,9 +112,6 @@ pub const DC_GCL_NO_SPECIALS: usize = 0x02;
|
||||
pub const DC_GCL_ADD_ALLDONE_HINT: usize = 0x04;
|
||||
pub const DC_GCL_FOR_FORWARDING: usize = 0x08;
|
||||
|
||||
pub const DC_GCM_ADDDAYMARKER: u32 = 0x01;
|
||||
pub const DC_GCM_INFO_ONLY: u32 = 0x02;
|
||||
|
||||
pub const DC_GCL_VERIFIED_ONLY: u32 = 0x01;
|
||||
pub const DC_GCL_ADD_SELF: u32 = 0x02;
|
||||
|
||||
|
||||
@@ -985,20 +985,20 @@ mod tests {
|
||||
assert_eq!(t.get_fresh_msgs().await.unwrap().len(), 0);
|
||||
|
||||
receive_msg(&t, &bob).await;
|
||||
assert_eq!(get_chat_msgs(&t, bob.id, 0).await.unwrap().len(), 1);
|
||||
assert_eq!(get_chat_msgs(&t, bob.id).await.unwrap().len(), 1);
|
||||
assert_eq!(bob.id.get_fresh_msg_cnt(&t).await.unwrap(), 1);
|
||||
assert_eq!(t.get_fresh_msgs().await.unwrap().len(), 1);
|
||||
|
||||
receive_msg(&t, &claire).await;
|
||||
receive_msg(&t, &claire).await;
|
||||
assert_eq!(get_chat_msgs(&t, claire.id, 0).await.unwrap().len(), 2);
|
||||
assert_eq!(get_chat_msgs(&t, claire.id).await.unwrap().len(), 2);
|
||||
assert_eq!(claire.id.get_fresh_msg_cnt(&t).await.unwrap(), 2);
|
||||
assert_eq!(t.get_fresh_msgs().await.unwrap().len(), 3);
|
||||
|
||||
receive_msg(&t, &dave).await;
|
||||
receive_msg(&t, &dave).await;
|
||||
receive_msg(&t, &dave).await;
|
||||
assert_eq!(get_chat_msgs(&t, dave.id, 0).await.unwrap().len(), 3);
|
||||
assert_eq!(get_chat_msgs(&t, dave.id).await.unwrap().len(), 3);
|
||||
assert_eq!(dave.id.get_fresh_msg_cnt(&t).await.unwrap(), 3);
|
||||
assert_eq!(t.get_fresh_msgs().await.unwrap().len(), 6);
|
||||
|
||||
@@ -1013,7 +1013,7 @@ mod tests {
|
||||
receive_msg(&t, &bob).await;
|
||||
receive_msg(&t, &claire).await;
|
||||
receive_msg(&t, &dave).await;
|
||||
assert_eq!(get_chat_msgs(&t, claire.id, 0).await.unwrap().len(), 3);
|
||||
assert_eq!(get_chat_msgs(&t, claire.id).await.unwrap().len(), 3);
|
||||
assert_eq!(claire.id.get_fresh_msg_cnt(&t).await.unwrap(), 3);
|
||||
assert_eq!(t.get_fresh_msgs().await.unwrap().len(), 6); // muted claire is not counted
|
||||
|
||||
@@ -1030,7 +1030,7 @@ mod tests {
|
||||
let t = TestContext::new_alice().await;
|
||||
let bob = t.create_chat_with_contact("", "bob@g.it").await;
|
||||
receive_msg(&t, &bob).await;
|
||||
assert_eq!(get_chat_msgs(&t, bob.id, 0).await.unwrap().len(), 1);
|
||||
assert_eq!(get_chat_msgs(&t, bob.id).await.unwrap().len(), 1);
|
||||
|
||||
// chat is unmuted by default, here and in the following assert(),
|
||||
// we check mainly that the SQL-statements in is_muted() and get_fresh_msgs()
|
||||
|
||||
@@ -99,8 +99,7 @@ pub(crate) async fn prepare_decryption(
|
||||
from,
|
||||
autocrypt_header.as_ref(),
|
||||
message_time,
|
||||
// Disallowing keychanges is disabled for now:
|
||||
true, // dkim_results.allow_keychange,
|
||||
dkim_results.allow_keychange,
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
||||
@@ -450,7 +450,7 @@ mod tests {
|
||||
.await?;
|
||||
let msg = bob.get_last_msg().await;
|
||||
let chat_id = msg.chat_id;
|
||||
assert_eq!(get_chat_msgs(&bob, chat_id, 0).await?.len(), 1);
|
||||
assert_eq!(get_chat_msgs(&bob, chat_id).await?.len(), 1);
|
||||
assert_eq!(msg.download_state(), DownloadState::Available);
|
||||
|
||||
// downloading the status update afterwards expands to nothing and moves the placeholder to trash-chat
|
||||
@@ -464,7 +464,7 @@ mod tests {
|
||||
false,
|
||||
)
|
||||
.await?;
|
||||
assert_eq!(get_chat_msgs(&bob, chat_id, 0).await?.len(), 0);
|
||||
assert_eq!(get_chat_msgs(&bob, chat_id).await?.len(), 0);
|
||||
assert!(Message::load_from_db(&bob, msg.id)
|
||||
.await?
|
||||
.chat_id
|
||||
@@ -517,13 +517,13 @@ mod tests {
|
||||
.await?;
|
||||
let msg = bob.get_last_msg().await;
|
||||
let chat_id = msg.chat_id;
|
||||
assert_eq!(get_chat_msgs(&bob, chat_id, 0).await?.len(), 1);
|
||||
assert_eq!(get_chat_msgs(&bob, chat_id).await?.len(), 1);
|
||||
assert_eq!(msg.download_state(), DownloadState::Available);
|
||||
|
||||
// downloading the mdn afterwards expands to nothing and deletes the placeholder directly
|
||||
// (usually mdn are too small for not being downloaded directly)
|
||||
receive_imf_inner(&bob, "bar@example.org", raw, false, None, false).await?;
|
||||
assert_eq!(get_chat_msgs(&bob, chat_id, 0).await?.len(), 0);
|
||||
assert_eq!(get_chat_msgs(&bob, chat_id).await?.len(), 0);
|
||||
assert!(Message::load_from_db(&bob, msg.id)
|
||||
.await?
|
||||
.chat_id
|
||||
|
||||
@@ -1080,7 +1080,7 @@ mod tests {
|
||||
}
|
||||
|
||||
async fn check_msg_is_deleted(t: &TestContext, chat: &Chat, msg_id: MsgId) {
|
||||
let chat_items = chat::get_chat_msgs(t, chat.id, 0).await.unwrap();
|
||||
let chat_items = chat::get_chat_msgs(t, chat.id).await.unwrap();
|
||||
// Check that the chat is empty except for possibly info messages:
|
||||
for item in &chat_items {
|
||||
if let ChatItem::Message { msg_id } = item {
|
||||
|
||||
19
src/http.rs
19
src/http.rs
@@ -4,10 +4,21 @@ use std::time::Duration;
|
||||
|
||||
use anyhow::Result;
|
||||
|
||||
use crate::socks::Socks5Config;
|
||||
|
||||
const HTTP_TIMEOUT: Duration = Duration::from_secs(30);
|
||||
|
||||
pub(crate) fn get_client() -> Result<reqwest::Client> {
|
||||
Ok(reqwest::ClientBuilder::new()
|
||||
.timeout(HTTP_TIMEOUT)
|
||||
.build()?)
|
||||
pub(crate) fn get_client(socks5_config: Option<Socks5Config>) -> Result<reqwest::Client> {
|
||||
let builder = reqwest::ClientBuilder::new().timeout(HTTP_TIMEOUT);
|
||||
let builder = if let Some(socks5_config) = socks5_config {
|
||||
let proxy = reqwest::Proxy::all(socks5_config.to_url())?;
|
||||
builder.proxy(proxy)
|
||||
} else {
|
||||
// Disable usage of "system" proxy configured via environment variables.
|
||||
// It is enabled by default in `reqwest`, see
|
||||
// <https://docs.rs/reqwest/0.11.14/reqwest/struct.ClientBuilder.html#method.no_proxy>
|
||||
// for documentation.
|
||||
builder.no_proxy()
|
||||
};
|
||||
Ok(builder.build()?)
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ use std::fmt;
|
||||
|
||||
use anyhow::{ensure, Result};
|
||||
use async_native_tls::Certificate;
|
||||
pub use async_smtp::ServerAddress;
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use crate::constants::{DC_LP_AUTH_FLAGS, DC_LP_AUTH_NORMAL, DC_LP_AUTH_OAUTH2};
|
||||
@@ -163,7 +162,7 @@ impl LoginParam {
|
||||
.await?
|
||||
.and_then(|provider_id| get_provider_by_id(&provider_id));
|
||||
|
||||
let socks5_config = Socks5Config::from_database(context).await?;
|
||||
let socks5_config = Socks5Config::from_database(&context.sql).await?;
|
||||
|
||||
Ok(LoginParam {
|
||||
addr,
|
||||
@@ -263,7 +262,7 @@ impl fmt::Display for LoginParam {
|
||||
|
||||
write!(
|
||||
f,
|
||||
"{} imap:{}:{}:{}:{}:cert_{}:{} smtp:{}:{}:{}:{}:cert_{}:{}",
|
||||
"{} imap:{}:{}:{}:{}:{}:cert_{}:{} smtp:{}:{}:{}:{}:{}:cert_{}:{}",
|
||||
unset_empty(&self.addr),
|
||||
unset_empty(&self.imap.user),
|
||||
if !self.imap.password.is_empty() {
|
||||
@@ -273,6 +272,7 @@ impl fmt::Display for LoginParam {
|
||||
},
|
||||
unset_empty(&self.imap.server),
|
||||
self.imap.port,
|
||||
self.imap.security,
|
||||
self.imap.certificate_checks,
|
||||
if self.imap.oauth2 {
|
||||
"OAUTH2"
|
||||
@@ -287,6 +287,7 @@ impl fmt::Display for LoginParam {
|
||||
},
|
||||
unset_empty(&self.smtp.server),
|
||||
self.smtp.port,
|
||||
self.smtp.security,
|
||||
self.smtp.certificate_checks,
|
||||
if self.smtp.oauth2 {
|
||||
"OAUTH2"
|
||||
|
||||
@@ -2144,7 +2144,7 @@ mod tests {
|
||||
.unwrap();
|
||||
|
||||
let mut has_image = false;
|
||||
let chatitems = chat::get_chat_msgs(&t, device_chat_id, 0).await.unwrap();
|
||||
let chatitems = chat::get_chat_msgs(&t, device_chat_id).await.unwrap();
|
||||
for chatitem in chatitems {
|
||||
if let ChatItem::Message { msg_id } = chatitem {
|
||||
if let Ok(msg) = Message::load_from_db(&t, msg_id).await {
|
||||
@@ -2288,7 +2288,7 @@ mod tests {
|
||||
assert_eq!(msg1.chat_id, msg2.chat_id);
|
||||
let chats = Chatlist::try_load(&bob, 0, None, None).await?;
|
||||
assert_eq!(chats.len(), 1);
|
||||
let msgs = chat::get_chat_msgs(&bob, bob_chat_id, 0).await?;
|
||||
let msgs = chat::get_chat_msgs(&bob, bob_chat_id).await?;
|
||||
assert_eq!(msgs.len(), 2);
|
||||
assert_eq!(bob.get_fresh_msgs().await?.len(), 0);
|
||||
|
||||
@@ -2299,7 +2299,7 @@ mod tests {
|
||||
let bob_chat = Chat::load_from_db(&bob, bob_chat_id).await?;
|
||||
assert_eq!(bob_chat.blocked, Blocked::Request);
|
||||
|
||||
let msgs = chat::get_chat_msgs(&bob, bob_chat_id, 0).await?;
|
||||
let msgs = chat::get_chat_msgs(&bob, bob_chat_id).await?;
|
||||
assert_eq!(msgs.len(), 2);
|
||||
bob_chat_id.accept(&bob).await.unwrap();
|
||||
|
||||
|
||||
@@ -325,8 +325,7 @@ impl MimeMessage {
|
||||
if let (Some(peerstate), Ok(mail)) = (&mut decryption_info.peerstate, mail) {
|
||||
if message_time > peerstate.last_seen_autocrypt
|
||||
&& mail.ctype.mimetype != "multipart/report"
|
||||
// Disallowing keychanges is disabled for now:
|
||||
// && decryption_info.dkim_results.allow_keychange
|
||||
&& decryption_info.dkim_results.allow_keychange
|
||||
{
|
||||
peerstate.degrade_encryption(message_time);
|
||||
}
|
||||
@@ -397,12 +396,11 @@ impl MimeMessage {
|
||||
parser.heuristically_parse_ndn(context).await;
|
||||
parser.parse_headers(context).await?;
|
||||
|
||||
// Disallowing keychanges is disabled for now
|
||||
// if !decryption_info.dkim_results.allow_keychange {
|
||||
// for part in parser.parts.iter_mut() {
|
||||
// part.error = Some("Seems like DKIM failed, this either is an attack or (more likely) a bug in Authentication-Results checking. Please tell us about this at https://support.delta.chat.".to_string());
|
||||
// }
|
||||
// }
|
||||
if !parser.decryption_info.dkim_results.allow_keychange {
|
||||
for part in parser.parts.iter_mut() {
|
||||
part.error = Some("Seems like DKIM failed, this either is an attack or (more likely) a bug in Authentication-Results checking. Please tell us about this at https://support.delta.chat.".to_string());
|
||||
}
|
||||
}
|
||||
|
||||
if parser.is_mime_modified {
|
||||
parser.decoded_data = mail_raw;
|
||||
|
||||
@@ -12,6 +12,7 @@ use crate::config::Config;
|
||||
use crate::context::Context;
|
||||
use crate::provider;
|
||||
use crate::provider::Oauth2Authorizer;
|
||||
use crate::socks::Socks5Config;
|
||||
use crate::tools::time;
|
||||
|
||||
const OAUTH2_GMAIL: Oauth2 = Oauth2 {
|
||||
@@ -158,7 +159,8 @@ pub async fn get_oauth2_access_token(
|
||||
}
|
||||
|
||||
// ... and POST
|
||||
let client = crate::http::get_client()?;
|
||||
let socks5_config = Socks5Config::from_database(&context.sql).await?;
|
||||
let client = crate::http::get_client(socks5_config)?;
|
||||
|
||||
let response: Response = match client.post(post_url).form(&post_param).send().await {
|
||||
Ok(resp) => match resp.json().await {
|
||||
@@ -284,7 +286,8 @@ impl Oauth2 {
|
||||
// "verified_email": true,
|
||||
// "picture": "https://lh4.googleusercontent.com/-Gj5jh_9R0BY/AAAAAAAAAAI/AAAAAAAAAAA/IAjtjfjtjNA/photo.jpg"
|
||||
// }
|
||||
let client = match crate::http::get_client() {
|
||||
let socks5_config = Socks5Config::from_database(&context.sql).await.ok()?;
|
||||
let client = match crate::http::get_client(socks5_config) {
|
||||
Ok(cl) => cl,
|
||||
Err(err) => {
|
||||
warn!(context, "failed to get HTTP client: {}", err);
|
||||
|
||||
@@ -22,6 +22,7 @@ use crate::context::Context;
|
||||
use crate::key::Fingerprint;
|
||||
use crate::message::Message;
|
||||
use crate::peerstate::Peerstate;
|
||||
use crate::socks::Socks5Config;
|
||||
use crate::tools::time;
|
||||
use crate::{token, EventType};
|
||||
|
||||
@@ -395,7 +396,11 @@ struct CreateAccountErrorResponse {
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
async fn set_account_from_qr(context: &Context, qr: &str) -> Result<()> {
|
||||
let url_str = &qr[DCACCOUNT_SCHEME.len()..];
|
||||
let response = crate::http::get_client()?.post(url_str).send().await?;
|
||||
let socks5_config = Socks5Config::from_database(&context.sql).await?;
|
||||
let response = crate::http::get_client(socks5_config)?
|
||||
.post(url_str)
|
||||
.send()
|
||||
.await?;
|
||||
let response_status = response.status();
|
||||
let response_text = response.text().await.with_context(|| {
|
||||
format!("Cannot create account, request to {url_str:?} failed: empty response")
|
||||
|
||||
@@ -443,24 +443,24 @@ Content-Disposition: reaction\n\
|
||||
let chat_alice = alice.create_chat(&bob).await;
|
||||
let alice_msg = alice.send_text(chat_alice.id, "Hi!").await;
|
||||
let bob_msg = bob.recv_msg(&alice_msg).await;
|
||||
assert_eq!(get_chat_msgs(&alice, chat_alice.id, 0).await?.len(), 1);
|
||||
assert_eq!(get_chat_msgs(&bob, bob_msg.chat_id, 0).await?.len(), 1);
|
||||
assert_eq!(get_chat_msgs(&alice, chat_alice.id).await?.len(), 1);
|
||||
assert_eq!(get_chat_msgs(&bob, bob_msg.chat_id).await?.len(), 1);
|
||||
|
||||
let alice_msg2 = alice.send_text(chat_alice.id, "Hi again!").await;
|
||||
bob.recv_msg(&alice_msg2).await;
|
||||
assert_eq!(get_chat_msgs(&alice, chat_alice.id, 0).await?.len(), 2);
|
||||
assert_eq!(get_chat_msgs(&bob, bob_msg.chat_id, 0).await?.len(), 2);
|
||||
assert_eq!(get_chat_msgs(&alice, chat_alice.id).await?.len(), 2);
|
||||
assert_eq!(get_chat_msgs(&bob, bob_msg.chat_id).await?.len(), 2);
|
||||
|
||||
bob_msg.chat_id.accept(&bob).await?;
|
||||
|
||||
send_reaction(&bob, bob_msg.id, "👍").await.unwrap();
|
||||
expect_reactions_changed_event(&bob, bob_msg.chat_id, bob_msg.id, ContactId::SELF).await?;
|
||||
assert_eq!(get_chat_msgs(&bob, bob_msg.chat_id, 0).await?.len(), 2);
|
||||
assert_eq!(get_chat_msgs(&bob, bob_msg.chat_id).await?.len(), 2);
|
||||
|
||||
let bob_reaction_msg = bob.pop_sent_msg().await;
|
||||
let alice_reaction_msg = alice.recv_msg_opt(&bob_reaction_msg).await.unwrap();
|
||||
assert_eq!(alice_reaction_msg.chat_id, DC_CHAT_ID_TRASH);
|
||||
assert_eq!(get_chat_msgs(&alice, chat_alice.id, 0).await?.len(), 2);
|
||||
assert_eq!(get_chat_msgs(&alice, chat_alice.id).await?.len(), 2);
|
||||
|
||||
let reactions = get_msg_reactions(&alice, alice_msg.sender_msg_id).await?;
|
||||
assert_eq!(reactions.to_string(), "👍1");
|
||||
|
||||
@@ -154,12 +154,12 @@ async fn test_adhoc_group_show_accepted_contact_accepted() {
|
||||
assert_eq!(chat.typ, Chattype::Single);
|
||||
assert_eq!(chat.name, "Bob");
|
||||
assert_eq!(chat::get_chat_contacts(&t, chat_id).await.unwrap().len(), 1);
|
||||
assert_eq!(chat::get_chat_msgs(&t, chat_id, 0).await.unwrap().len(), 1);
|
||||
assert_eq!(chat::get_chat_msgs(&t, chat_id).await.unwrap().len(), 1);
|
||||
|
||||
// receive a non-delta-message from Bob, shows up because of the show_emails setting
|
||||
receive_imf(&t, ONETOONE_NOREPLY_MAIL, false).await.unwrap();
|
||||
|
||||
assert_eq!(chat::get_chat_msgs(&t, chat_id, 0).await.unwrap().len(), 2);
|
||||
assert_eq!(chat::get_chat_msgs(&t, chat_id).await.unwrap().len(), 2);
|
||||
|
||||
// let Bob create an adhoc-group by a non-delta-message, shows up because of the show_emails setting
|
||||
receive_imf(&t, GRP_MAIL, false).await.unwrap();
|
||||
@@ -208,7 +208,7 @@ async fn test_read_receipt_and_unarchive() -> Result<()> {
|
||||
// create a group with bob, archive group
|
||||
let group_id = chat::create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?;
|
||||
chat::add_contact_to_chat(&t, group_id, bob_id).await?;
|
||||
assert_eq!(chat::get_chat_msgs(&t, group_id, 0).await.unwrap().len(), 0);
|
||||
assert_eq!(chat::get_chat_msgs(&t, group_id).await.unwrap().len(), 0);
|
||||
group_id
|
||||
.set_visibility(&t, ChatVisibility::Archived)
|
||||
.await?;
|
||||
@@ -289,7 +289,7 @@ async fn test_read_receipt_and_unarchive() -> Result<()> {
|
||||
false,
|
||||
)
|
||||
.await?;
|
||||
assert_eq!(chat::get_chat_msgs(&t, group_id, 0).await?.len(), 1);
|
||||
assert_eq!(chat::get_chat_msgs(&t, group_id).await?.len(), 1);
|
||||
let msg = message::Message::load_from_db(&t, msg.id).await?;
|
||||
assert_eq!(msg.state, MessageState::OutMdnRcvd);
|
||||
|
||||
@@ -705,7 +705,7 @@ async fn test_parse_ndn_group_msg() -> Result<()> {
|
||||
|
||||
assert_eq!(msg.state, MessageState::OutFailed);
|
||||
|
||||
let msgs = chat::get_chat_msgs(&t, msg.chat_id, 0).await?;
|
||||
let msgs = chat::get_chat_msgs(&t, msg.chat_id).await?;
|
||||
let msg_id = if let ChatItem::Message { msg_id } = msgs.last().unwrap() {
|
||||
msg_id
|
||||
} else {
|
||||
@@ -966,7 +966,7 @@ async fn test_block_mailing_list() {
|
||||
assert_eq!(chats.len(), 0); // Test that the message is not shown
|
||||
|
||||
// Both messages are in the same blocked chat.
|
||||
let msgs = chat::get_chat_msgs(&t.ctx, chat_id, 0).await.unwrap();
|
||||
let msgs = chat::get_chat_msgs(&t.ctx, chat_id).await.unwrap();
|
||||
assert_eq!(msgs.len(), 2);
|
||||
}
|
||||
|
||||
@@ -997,7 +997,7 @@ async fn test_mailing_list_decide_block_then_unblock() {
|
||||
|
||||
receive_imf(&t.ctx, DC_MAILINGLIST2, false).await.unwrap();
|
||||
let msg = t.get_last_msg().await;
|
||||
let msgs = chat::get_chat_msgs(&t, msg.chat_id, 0).await.unwrap();
|
||||
let msgs = chat::get_chat_msgs(&t, msg.chat_id).await.unwrap();
|
||||
assert_eq!(msgs.len(), 2);
|
||||
}
|
||||
|
||||
@@ -1019,14 +1019,14 @@ async fn test_mailing_list_decide_not_now() {
|
||||
|
||||
let chats = Chatlist::try_load(&t.ctx, 0, None, None).await.unwrap();
|
||||
assert_eq!(chats.len(), 1); // Test that chat is still in the chatlist
|
||||
let msgs = chat::get_chat_msgs(&t.ctx, chat_id, 0).await.unwrap();
|
||||
let msgs = chat::get_chat_msgs(&t.ctx, chat_id).await.unwrap();
|
||||
assert_eq!(msgs.len(), 1); // ...and contains 1 message
|
||||
|
||||
receive_imf(&t.ctx, DC_MAILINGLIST2, false).await.unwrap();
|
||||
|
||||
let chats = Chatlist::try_load(&t.ctx, 0, None, None).await.unwrap();
|
||||
assert_eq!(chats.len(), 1); // Test that the new mailing list message got into the same chat
|
||||
let msgs = chat::get_chat_msgs(&t.ctx, chat_id, 0).await.unwrap();
|
||||
let msgs = chat::get_chat_msgs(&t.ctx, chat_id).await.unwrap();
|
||||
assert_eq!(msgs.len(), 2);
|
||||
let chat = Chat::load_from_db(&t.ctx, chat_id).await.unwrap();
|
||||
assert!(chat.is_contact_request());
|
||||
@@ -1052,7 +1052,7 @@ async fn test_mailing_list_decide_accept() {
|
||||
|
||||
receive_imf(&t.ctx, DC_MAILINGLIST2, false).await.unwrap();
|
||||
|
||||
let msgs = chat::get_chat_msgs(&t.ctx, chat_id, 0).await.unwrap();
|
||||
let msgs = chat::get_chat_msgs(&t.ctx, chat_id).await.unwrap();
|
||||
assert_eq!(msgs.len(), 2);
|
||||
let chat = chat::Chat::load_from_db(&t.ctx, chat_id).await.unwrap();
|
||||
assert!(chat.can_send(&t.ctx).await.unwrap());
|
||||
@@ -1110,7 +1110,7 @@ async fn test_majordomo_mailing_list() -> Result<()> {
|
||||
assert_eq!(chat.typ, Chattype::Mailinglist);
|
||||
assert_eq!(chat.grpid, "mylist@bar.org");
|
||||
assert_eq!(chat.name, "ola");
|
||||
assert_eq!(chat::get_chat_msgs(&t, chat.id, 0).await.unwrap().len(), 1);
|
||||
assert_eq!(chat::get_chat_msgs(&t, chat.id).await.unwrap().len(), 1);
|
||||
assert!(!chat.can_send(&t).await?);
|
||||
assert_eq!(chat.get_mailinglist_addr(), None);
|
||||
|
||||
@@ -1131,7 +1131,7 @@ async fn test_majordomo_mailing_list() -> Result<()> {
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(chat::get_chat_msgs(&t, chat.id, 0).await.unwrap().len(), 2);
|
||||
assert_eq!(chat::get_chat_msgs(&t, chat.id).await.unwrap().len(), 2);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -1330,7 +1330,7 @@ async fn test_mailing_list_with_mimepart_footer() {
|
||||
);
|
||||
assert!(msg.has_html());
|
||||
let chat = Chat::load_from_db(&t, msg.chat_id).await.unwrap();
|
||||
assert_eq!(get_chat_msgs(&t, msg.chat_id, 0).await.unwrap().len(), 1);
|
||||
assert_eq!(get_chat_msgs(&t, msg.chat_id).await.unwrap().len(), 1);
|
||||
assert_eq!(chat.typ, Chattype::Mailinglist);
|
||||
assert_eq!(chat.blocked, Blocked::Request);
|
||||
assert_eq!(chat.grpid, "intern.lists.abc.de");
|
||||
@@ -1350,7 +1350,7 @@ async fn test_mailing_list_with_mimepart_footer_signed() {
|
||||
.await
|
||||
.unwrap();
|
||||
let msg = t.get_last_msg().await;
|
||||
assert_eq!(get_chat_msgs(&t, msg.chat_id, 0).await.unwrap().len(), 1);
|
||||
assert_eq!(get_chat_msgs(&t, msg.chat_id).await.unwrap().len(), 1);
|
||||
let text = msg.text.clone().unwrap();
|
||||
assert!(text.contains("content text"));
|
||||
assert!(!text.contains("footer text"));
|
||||
@@ -1547,7 +1547,7 @@ async fn test_many_images() {
|
||||
assert_eq!(msg.viewtype, Viewtype::Image);
|
||||
assert!(msg.has_html());
|
||||
let chat = Chat::load_from_db(&t, msg.chat_id).await.unwrap();
|
||||
assert_eq!(get_chat_msgs(&t, chat.id, 0).await.unwrap().len(), 1);
|
||||
assert_eq!(get_chat_msgs(&t, chat.id).await.unwrap().len(), 1);
|
||||
}
|
||||
|
||||
/// Test that classical MUA messages are assigned to group chats based on the `In-Reply-To`
|
||||
@@ -1597,7 +1597,7 @@ async fn test_in_reply_to() {
|
||||
assert_eq!(msg.get_text().unwrap(), "reply foo");
|
||||
|
||||
// Load the first message from the same chat.
|
||||
let msgs = chat::get_chat_msgs(&t, msg.chat_id, 0).await.unwrap();
|
||||
let msgs = chat::get_chat_msgs(&t, msg.chat_id).await.unwrap();
|
||||
let msg_id = if let ChatItem::Message { msg_id } = msgs.first().unwrap() {
|
||||
msg_id
|
||||
} else {
|
||||
@@ -1824,7 +1824,7 @@ async fn create_test_alias(chat_request: bool, group_request: bool) -> (TestCont
|
||||
assert!(msg.get_text().unwrap().contains("hi support!"));
|
||||
let chat = Chat::load_from_db(&alice, msg.chat_id).await.unwrap();
|
||||
assert_eq!(chat.typ, Chattype::Group);
|
||||
assert_eq!(get_chat_msgs(&alice, chat.id, 0).await.unwrap().len(), 1);
|
||||
assert_eq!(get_chat_msgs(&alice, chat.id).await.unwrap().len(), 1);
|
||||
if group_request {
|
||||
assert_eq!(get_chat_contacts(&alice, chat.id).await.unwrap().len(), 4);
|
||||
} else {
|
||||
@@ -1857,7 +1857,7 @@ async fn create_test_alias(chat_request: bool, group_request: bool) -> (TestCont
|
||||
} else {
|
||||
assert_eq!(chat.typ, Chattype::Single);
|
||||
}
|
||||
assert_eq!(get_chat_msgs(&claire, chat.id, 0).await.unwrap().len(), 1);
|
||||
assert_eq!(get_chat_msgs(&claire, chat.id).await.unwrap().len(), 1);
|
||||
assert_eq!(msg.get_override_sender_name(), None);
|
||||
|
||||
(claire, alice)
|
||||
@@ -1972,7 +1972,7 @@ async fn test_dont_assign_to_trash_by_parent() {
|
||||
println!("\n========= Delete the message ==========");
|
||||
msg.id.trash(&t).await.unwrap();
|
||||
|
||||
let msgs = chat::get_chat_msgs(&t.ctx, chat_id, 0).await.unwrap();
|
||||
let msgs = chat::get_chat_msgs(&t.ctx, chat_id).await.unwrap();
|
||||
assert_eq!(msgs.len(), 0);
|
||||
|
||||
println!("\n========= Receive a message that is a reply to the deleted message ==========");
|
||||
@@ -2182,8 +2182,8 @@ Original signature updated",
|
||||
.await?;
|
||||
let bob = Contact::load_from_db(&t, bob_id).await?;
|
||||
assert_eq!(bob.get_status(), "Original signature updated");
|
||||
assert_eq!(get_chat_msgs(&t, one2one_chat_id, 0).await?.len(), 2);
|
||||
assert_eq!(get_chat_msgs(&t, ml_chat_id, 0).await?.len(), 1);
|
||||
assert_eq!(get_chat_msgs(&t, one2one_chat_id).await?.len(), 2);
|
||||
assert_eq!(get_chat_msgs(&t, ml_chat_id).await?.len(), 1);
|
||||
assert_eq!(Chatlist::try_load(&t, 0, None, None).await?.len(), 2);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -779,7 +779,7 @@ mod tests {
|
||||
use crate::chat;
|
||||
use crate::chat::ProtectionStatus;
|
||||
use crate::chatlist::Chatlist;
|
||||
use crate::constants::{Chattype, DC_GCM_ADDDAYMARKER};
|
||||
use crate::constants::Chattype;
|
||||
use crate::contact::ContactAddress;
|
||||
use crate::contact::VerifiedStatus;
|
||||
use crate::peerstate::Peerstate;
|
||||
@@ -922,7 +922,7 @@ mod tests {
|
||||
// Check Alice got the verified message in her 1:1 chat.
|
||||
{
|
||||
let chat = alice.create_chat(&bob).await;
|
||||
let msg_id = chat::get_chat_msgs(&alice.ctx, chat.get_id(), DC_GCM_ADDDAYMARKER)
|
||||
let msg_id = chat::get_chat_msgs(&alice.ctx, chat.get_id())
|
||||
.await
|
||||
.unwrap()
|
||||
.into_iter()
|
||||
@@ -971,7 +971,7 @@ mod tests {
|
||||
// Check Bob got the verified message in his 1:1 chat.
|
||||
{
|
||||
let chat = bob.create_chat(&alice).await;
|
||||
let msg_id = chat::get_chat_msgs(&bob.ctx, chat.get_id(), DC_GCM_ADDDAYMARKER)
|
||||
let msg_id = chat::get_chat_msgs(&bob.ctx, chat.get_id())
|
||||
.await
|
||||
.unwrap()
|
||||
.into_iter()
|
||||
@@ -1281,7 +1281,7 @@ mod tests {
|
||||
Blocked::Yes,
|
||||
"Alice's 1:1 chat with Bob is not hidden"
|
||||
);
|
||||
let msg_id = chat::get_chat_msgs(&alice.ctx, alice_chatid, DC_GCM_ADDDAYMARKER)
|
||||
let msg_id = chat::get_chat_msgs(&alice.ctx, alice_chatid)
|
||||
.await
|
||||
.unwrap()
|
||||
.into_iter()
|
||||
@@ -1326,17 +1326,14 @@ mod tests {
|
||||
Blocked::Yes,
|
||||
"Bob's 1:1 chat with Alice is not hidden"
|
||||
);
|
||||
for item in chat::get_chat_msgs(&bob.ctx, bob_chatid, DC_GCM_ADDDAYMARKER)
|
||||
.await
|
||||
.unwrap()
|
||||
{
|
||||
for item in chat::get_chat_msgs(&bob.ctx, bob_chatid).await.unwrap() {
|
||||
if let chat::ChatItem::Message { msg_id } = item {
|
||||
let msg = Message::load_from_db(&bob.ctx, msg_id).await.unwrap();
|
||||
let text = msg.get_text().unwrap();
|
||||
println!("msg {msg_id} text: {text}");
|
||||
}
|
||||
}
|
||||
let mut msg_iter = chat::get_chat_msgs(&bob.ctx, bob_chatid, DC_GCM_ADDDAYMARKER)
|
||||
let mut msg_iter = chat::get_chat_msgs(&bob.ctx, bob_chatid)
|
||||
.await
|
||||
.unwrap()
|
||||
.into_iter();
|
||||
|
||||
284
src/smtp.rs
284
src/smtp.rs
@@ -5,9 +5,9 @@ pub mod send;
|
||||
use std::time::{Duration, SystemTime};
|
||||
|
||||
use anyhow::{bail, format_err, Context as _, Error, Result};
|
||||
use async_smtp::smtp::client::net::ClientTlsParameters;
|
||||
use async_smtp::smtp::response::{Category, Code, Detail};
|
||||
use async_smtp::{smtp, EmailAddress, ServerAddress};
|
||||
use async_smtp::response::{Category, Code, Detail};
|
||||
use async_smtp::{self as smtp, EmailAddress, SmtpTransport};
|
||||
use tokio::io::BufWriter;
|
||||
use tokio::task;
|
||||
|
||||
use crate::config::Config;
|
||||
@@ -17,18 +17,21 @@ use crate::login_param::{build_tls, CertificateChecks, LoginParam, ServerLoginPa
|
||||
use crate::message::Message;
|
||||
use crate::message::{self, MsgId};
|
||||
use crate::mimefactory::MimeFactory;
|
||||
use crate::net::connect_tcp;
|
||||
use crate::net::session::SessionStream;
|
||||
use crate::oauth2::get_oauth2_access_token;
|
||||
use crate::provider::Socket;
|
||||
use crate::socks::Socks5Config;
|
||||
use crate::sql;
|
||||
use crate::{context::Context, scheduler::connectivity::ConnectivityStore};
|
||||
|
||||
/// SMTP write and read timeout in seconds.
|
||||
const SMTP_TIMEOUT: u64 = 30;
|
||||
/// SMTP write and read timeout.
|
||||
const SMTP_TIMEOUT: Duration = Duration::from_secs(30);
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct Smtp {
|
||||
transport: Option<smtp::SmtpTransport>,
|
||||
/// SMTP connection.
|
||||
transport: Option<SmtpTransport<Box<dyn SessionStream>>>,
|
||||
|
||||
/// Email address we are sending from.
|
||||
from: Option<EmailAddress>,
|
||||
@@ -56,7 +59,7 @@ impl Smtp {
|
||||
// Closing connection with a QUIT command may take some time, especially if it's a
|
||||
// stale connection and an attempt to send the command times out. Send a command in a
|
||||
// separate task to avoid waiting for reply or timeout.
|
||||
task::spawn(async move { transport.close().await });
|
||||
task::spawn(async move { transport.quit().await });
|
||||
}
|
||||
self.last_success = None;
|
||||
}
|
||||
@@ -77,10 +80,7 @@ impl Smtp {
|
||||
|
||||
/// Check whether we are connected.
|
||||
pub fn is_connected(&self) -> bool {
|
||||
self.transport
|
||||
.as_ref()
|
||||
.map(|t| t.is_connected())
|
||||
.unwrap_or_default()
|
||||
self.transport.is_some()
|
||||
}
|
||||
|
||||
/// Connect using configured parameters.
|
||||
@@ -107,6 +107,127 @@ impl Smtp {
|
||||
.await
|
||||
}
|
||||
|
||||
async fn connect_secure_socks5(
|
||||
&self,
|
||||
context: &Context,
|
||||
hostname: &str,
|
||||
port: u16,
|
||||
strict_tls: bool,
|
||||
socks5_config: Socks5Config,
|
||||
) -> Result<SmtpTransport<Box<dyn SessionStream>>> {
|
||||
let socks5_stream = socks5_config
|
||||
.connect(context, hostname, port, SMTP_TIMEOUT, strict_tls)
|
||||
.await?;
|
||||
let tls = build_tls(strict_tls);
|
||||
let tls_stream = tls.connect(hostname, socks5_stream).await?;
|
||||
let buffered_stream = BufWriter::new(tls_stream);
|
||||
let session_stream: Box<dyn SessionStream> = Box::new(buffered_stream);
|
||||
let client = smtp::SmtpClient::new().smtp_utf8(true);
|
||||
let transport = SmtpTransport::new(client, session_stream).await?;
|
||||
Ok(transport)
|
||||
}
|
||||
|
||||
async fn connect_starttls_socks5(
|
||||
&self,
|
||||
context: &Context,
|
||||
hostname: &str,
|
||||
port: u16,
|
||||
strict_tls: bool,
|
||||
socks5_config: Socks5Config,
|
||||
) -> Result<SmtpTransport<Box<dyn SessionStream>>> {
|
||||
let socks5_stream = socks5_config
|
||||
.connect(context, hostname, port, SMTP_TIMEOUT, strict_tls)
|
||||
.await?;
|
||||
|
||||
// Run STARTTLS command and convert the client back into a stream.
|
||||
let client = smtp::SmtpClient::new().smtp_utf8(true);
|
||||
let transport = SmtpTransport::new(client, socks5_stream).await?;
|
||||
let tcp_stream = transport.starttls().await?;
|
||||
let tls = build_tls(strict_tls);
|
||||
let tls_stream = tls
|
||||
.connect(hostname, tcp_stream)
|
||||
.await
|
||||
.context("STARTTLS upgrade failed")?;
|
||||
let buffered_stream = BufWriter::new(tls_stream);
|
||||
let session_stream: Box<dyn SessionStream> = Box::new(buffered_stream);
|
||||
let client = smtp::SmtpClient::new().smtp_utf8(true).without_greeting();
|
||||
let transport = SmtpTransport::new(client, session_stream).await?;
|
||||
Ok(transport)
|
||||
}
|
||||
|
||||
async fn connect_insecure_socks5(
|
||||
&self,
|
||||
context: &Context,
|
||||
hostname: &str,
|
||||
port: u16,
|
||||
socks5_config: Socks5Config,
|
||||
) -> Result<SmtpTransport<Box<dyn SessionStream>>> {
|
||||
let socks5_stream = socks5_config
|
||||
.connect(context, hostname, port, SMTP_TIMEOUT, false)
|
||||
.await?;
|
||||
let buffered_stream = BufWriter::new(socks5_stream);
|
||||
let session_stream: Box<dyn SessionStream> = Box::new(buffered_stream);
|
||||
let client = smtp::SmtpClient::new().smtp_utf8(true);
|
||||
let transport = SmtpTransport::new(client, session_stream).await?;
|
||||
Ok(transport)
|
||||
}
|
||||
|
||||
async fn connect_secure(
|
||||
&self,
|
||||
context: &Context,
|
||||
hostname: &str,
|
||||
port: u16,
|
||||
strict_tls: bool,
|
||||
) -> Result<SmtpTransport<Box<dyn SessionStream>>> {
|
||||
let tcp_stream = connect_tcp(context, hostname, port, SMTP_TIMEOUT, false).await?;
|
||||
let tls = build_tls(strict_tls);
|
||||
let tls_stream = tls.connect(hostname, tcp_stream).await?;
|
||||
let buffered_stream = BufWriter::new(tls_stream);
|
||||
let session_stream: Box<dyn SessionStream> = Box::new(buffered_stream);
|
||||
let client = smtp::SmtpClient::new().smtp_utf8(true);
|
||||
let transport = SmtpTransport::new(client, session_stream).await?;
|
||||
Ok(transport)
|
||||
}
|
||||
|
||||
async fn connect_starttls(
|
||||
&self,
|
||||
context: &Context,
|
||||
hostname: &str,
|
||||
port: u16,
|
||||
strict_tls: bool,
|
||||
) -> Result<SmtpTransport<Box<dyn SessionStream>>> {
|
||||
let tcp_stream = connect_tcp(context, hostname, port, SMTP_TIMEOUT, strict_tls).await?;
|
||||
|
||||
// Run STARTTLS command and convert the client back into a stream.
|
||||
let client = smtp::SmtpClient::new().smtp_utf8(true);
|
||||
let transport = SmtpTransport::new(client, tcp_stream).await?;
|
||||
let tcp_stream = transport.starttls().await?;
|
||||
let tls = build_tls(strict_tls);
|
||||
let tls_stream = tls
|
||||
.connect(hostname, tcp_stream)
|
||||
.await
|
||||
.context("STARTTLS upgrade failed")?;
|
||||
let buffered_stream = BufWriter::new(tls_stream);
|
||||
let session_stream: Box<dyn SessionStream> = Box::new(buffered_stream);
|
||||
let client = smtp::SmtpClient::new().smtp_utf8(true).without_greeting();
|
||||
let transport = SmtpTransport::new(client, session_stream).await?;
|
||||
Ok(transport)
|
||||
}
|
||||
|
||||
async fn connect_insecure(
|
||||
&self,
|
||||
context: &Context,
|
||||
hostname: &str,
|
||||
port: u16,
|
||||
) -> Result<SmtpTransport<Box<dyn SessionStream>>> {
|
||||
let tcp_stream = connect_tcp(context, hostname, port, SMTP_TIMEOUT, false).await?;
|
||||
let buffered_stream = BufWriter::new(tcp_stream);
|
||||
let session_stream: Box<dyn SessionStream> = Box::new(buffered_stream);
|
||||
let client = smtp::SmtpClient::new().smtp_utf8(true);
|
||||
let transport = SmtpTransport::new(client, session_stream).await?;
|
||||
Ok(transport)
|
||||
}
|
||||
|
||||
/// Connect using the provided login params.
|
||||
pub async fn connect(
|
||||
&mut self,
|
||||
@@ -139,61 +260,83 @@ impl Smtp {
|
||||
CertificateChecks::AcceptInvalidCertificates
|
||||
| CertificateChecks::AcceptInvalidCertificates2 => false,
|
||||
};
|
||||
let tls_config = build_tls(strict_tls);
|
||||
let tls_parameters = ClientTlsParameters::new(domain.to_string(), tls_config);
|
||||
|
||||
let (creds, mechanism) = if lp.oauth2 {
|
||||
// oauth2
|
||||
let send_pw = &lp.password;
|
||||
let access_token = get_oauth2_access_token(context, addr, send_pw, false).await?;
|
||||
if access_token.is_none() {
|
||||
bail!("SMTP OAuth 2 error {}", addr);
|
||||
let mut transport = if let Some(socks5_config) = socks5_config {
|
||||
match lp.security {
|
||||
Socket::Automatic => bail!("SMTP port security is not configured"),
|
||||
Socket::Ssl => {
|
||||
self.connect_secure_socks5(
|
||||
context,
|
||||
domain,
|
||||
port,
|
||||
strict_tls,
|
||||
socks5_config.clone(),
|
||||
)
|
||||
.await?
|
||||
}
|
||||
Socket::Starttls => {
|
||||
self.connect_starttls_socks5(
|
||||
context,
|
||||
domain,
|
||||
port,
|
||||
strict_tls,
|
||||
socks5_config.clone(),
|
||||
)
|
||||
.await?
|
||||
}
|
||||
Socket::Plain => {
|
||||
self.connect_insecure_socks5(context, domain, port, socks5_config.clone())
|
||||
.await?
|
||||
}
|
||||
}
|
||||
let user = &lp.user;
|
||||
(
|
||||
smtp::authentication::Credentials::new(
|
||||
user.to_string(),
|
||||
access_token.unwrap_or_default(),
|
||||
),
|
||||
vec![smtp::authentication::Mechanism::Xoauth2],
|
||||
)
|
||||
} else {
|
||||
// plain
|
||||
let user = lp.user.clone();
|
||||
let pw = lp.password.clone();
|
||||
(
|
||||
smtp::authentication::Credentials::new(user, pw),
|
||||
vec![
|
||||
smtp::authentication::Mechanism::Plain,
|
||||
smtp::authentication::Mechanism::Login,
|
||||
],
|
||||
)
|
||||
match lp.security {
|
||||
Socket::Automatic => bail!("SMTP port security is not configured"),
|
||||
Socket::Ssl => {
|
||||
self.connect_secure(context, domain, port, strict_tls)
|
||||
.await?
|
||||
}
|
||||
Socket::Starttls => {
|
||||
self.connect_starttls(context, domain, port, strict_tls)
|
||||
.await?
|
||||
}
|
||||
Socket::Plain => self.connect_insecure(context, domain, port).await?,
|
||||
}
|
||||
};
|
||||
|
||||
let security = match lp.security {
|
||||
Socket::Plain => smtp::ClientSecurity::None,
|
||||
Socket::Starttls => smtp::ClientSecurity::Required(tls_parameters),
|
||||
_ => smtp::ClientSecurity::Wrapper(tls_parameters),
|
||||
};
|
||||
|
||||
let client =
|
||||
smtp::SmtpClient::with_security(ServerAddress::new(domain.to_string(), port), security);
|
||||
|
||||
let mut client = client
|
||||
.smtp_utf8(true)
|
||||
.credentials(creds)
|
||||
.authentication_mechanism(mechanism)
|
||||
.connection_reuse(smtp::ConnectionReuseParameters::ReuseUnlimited)
|
||||
.timeout(Some(Duration::from_secs(SMTP_TIMEOUT)));
|
||||
|
||||
if let Some(socks5_config) = socks5_config {
|
||||
client = client.use_socks5(socks5_config.to_async_smtp_socks5_config());
|
||||
// Authenticate.
|
||||
{
|
||||
let (creds, mechanism) = if lp.oauth2 {
|
||||
// oauth2
|
||||
let send_pw = &lp.password;
|
||||
let access_token = get_oauth2_access_token(context, addr, send_pw, false).await?;
|
||||
if access_token.is_none() {
|
||||
bail!("SMTP OAuth 2 error {}", addr);
|
||||
}
|
||||
let user = &lp.user;
|
||||
(
|
||||
smtp::authentication::Credentials::new(
|
||||
user.to_string(),
|
||||
access_token.unwrap_or_default(),
|
||||
),
|
||||
vec![smtp::authentication::Mechanism::Xoauth2],
|
||||
)
|
||||
} else {
|
||||
// plain
|
||||
let user = lp.user.clone();
|
||||
let pw = lp.password.clone();
|
||||
(
|
||||
smtp::authentication::Credentials::new(user, pw),
|
||||
vec![
|
||||
smtp::authentication::Mechanism::Plain,
|
||||
smtp::authentication::Mechanism::Login,
|
||||
],
|
||||
)
|
||||
};
|
||||
transport.try_login(&creds, &mechanism).await?;
|
||||
}
|
||||
|
||||
let mut trans = client.into_transport();
|
||||
trans.connect().await.context("SMTP failed to connect")?;
|
||||
|
||||
self.transport = Some(trans);
|
||||
self.transport = Some(transport);
|
||||
self.last_success = Some(SystemTime::now());
|
||||
|
||||
context.emit_event(EventType::SmtpConnected(format!(
|
||||
@@ -223,7 +366,6 @@ pub(crate) async fn smtp_send(
|
||||
message: &str,
|
||||
smtp: &mut Smtp,
|
||||
msg_id: MsgId,
|
||||
rowid: i64,
|
||||
) -> SendResult {
|
||||
if std::env::var(crate::DCC_MIME_DEBUG).is_ok() {
|
||||
info!(context, "smtp-sending out mime message:");
|
||||
@@ -241,9 +383,7 @@ pub(crate) async fn smtp_send(
|
||||
return SendResult::Retry;
|
||||
}
|
||||
|
||||
let send_result = smtp
|
||||
.send(context, recipients, message.as_bytes(), rowid)
|
||||
.await;
|
||||
let send_result = smtp.send(context, recipients, message.as_bytes()).await;
|
||||
smtp.last_send_error = send_result.as_ref().err().map(|e| e.to_string());
|
||||
|
||||
let status = match send_result {
|
||||
@@ -252,7 +392,7 @@ pub(crate) async fn smtp_send(
|
||||
info!(context, "SMTP failed to send: {:?}", &err);
|
||||
|
||||
let res = match err {
|
||||
async_smtp::smtp::error::Error::Permanent(ref response) => {
|
||||
async_smtp::error::Error::Permanent(ref response) => {
|
||||
// Workaround for incorrectly configured servers returning permanent errors
|
||||
// instead of temporary ones.
|
||||
let maybe_transient = match response.code {
|
||||
@@ -287,7 +427,7 @@ pub(crate) async fn smtp_send(
|
||||
SendResult::Failure(format_err!("Permanent SMTP error: {}", err))
|
||||
}
|
||||
}
|
||||
async_smtp::smtp::error::Error::Transient(ref response) => {
|
||||
async_smtp::error::Error::Transient(ref response) => {
|
||||
// We got a transient 4xx response from SMTP server.
|
||||
// Give some time until the server-side error maybe goes away.
|
||||
|
||||
@@ -337,7 +477,7 @@ pub(crate) async fn smtp_send(
|
||||
// Local error, job is invalid, do not retry.
|
||||
smtp.disconnect().await;
|
||||
warn!(context, "SMTP job is invalid: {}", err);
|
||||
SendResult::Failure(err.into())
|
||||
SendResult::Failure(err)
|
||||
}
|
||||
Err(crate::smtp::send::Error::NoTransport) => {
|
||||
// Should never happen.
|
||||
@@ -445,15 +585,7 @@ pub(crate) async fn send_msg_to_smtp(
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let status = smtp_send(
|
||||
context,
|
||||
&recipients_list,
|
||||
body.as_str(),
|
||||
smtp,
|
||||
msg_id,
|
||||
rowid,
|
||||
)
|
||||
.await;
|
||||
let status = smtp_send(context, &recipients_list, body.as_str(), smtp, msg_id).await;
|
||||
|
||||
match status {
|
||||
SendResult::Retry => {}
|
||||
@@ -585,7 +717,7 @@ async fn send_mdn_msg_id(
|
||||
.map_err(|err| format_err!("invalid recipient: {} {:?}", addr, err))?;
|
||||
let recipients = vec![recipient];
|
||||
|
||||
match smtp_send(context, &recipients, &body, smtp, msg_id, 0).await {
|
||||
match smtp_send(context, &recipients, &body, smtp, msg_id).await {
|
||||
SendResult::Success => {
|
||||
info!(context, "Successfully sent MDN for {}", msg_id);
|
||||
context
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
//! # SMTP message sending
|
||||
|
||||
use std::time::Duration;
|
||||
|
||||
use async_smtp::{EmailAddress, Envelope, SendableEmail, Transport};
|
||||
use async_smtp::{EmailAddress, Envelope, SendableEmail};
|
||||
|
||||
use super::Smtp;
|
||||
use crate::config::Config;
|
||||
@@ -19,9 +17,9 @@ pub(crate) const DEFAULT_MAX_SMTP_RCPT_TO: usize = 50;
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum Error {
|
||||
#[error("Envelope error: {}", _0)]
|
||||
Envelope(#[from] async_smtp::error::Error),
|
||||
Envelope(anyhow::Error),
|
||||
#[error("Send error: {}", _0)]
|
||||
SmtpSend(#[from] async_smtp::smtp::error::Error),
|
||||
SmtpSend(async_smtp::error::Error),
|
||||
#[error("SMTP has no transport")]
|
||||
NoTransport,
|
||||
#[error("{}", _0)]
|
||||
@@ -36,7 +34,6 @@ impl Smtp {
|
||||
context: &Context,
|
||||
recipients: &[EmailAddress],
|
||||
message: &[u8],
|
||||
rowid: i64,
|
||||
) -> Result<()> {
|
||||
if !context.get_config_bool(Config::Bot).await? {
|
||||
// Notify ratelimiter about sent message regardless of whether quota is exceeded or not.
|
||||
@@ -62,19 +59,10 @@ impl Smtp {
|
||||
|
||||
let envelope = Envelope::new(self.from.clone(), recipients_chunk.to_vec())
|
||||
.map_err(Error::Envelope)?;
|
||||
let mail = SendableEmail::new(
|
||||
envelope,
|
||||
rowid.to_string(), // only used for internal logging
|
||||
message,
|
||||
);
|
||||
let mail = SendableEmail::new(envelope, message);
|
||||
|
||||
if let Some(ref mut transport) = self.transport {
|
||||
// The timeout is 1min + 3min per MB.
|
||||
let timeout = 60 + (180 * message_len_bytes / 1_000_000) as u64;
|
||||
transport
|
||||
.send_with_timeout(mail, Some(&Duration::from_secs(timeout)))
|
||||
.await
|
||||
.map_err(Error::SmtpSend)?;
|
||||
transport.send(mail).await.map_err(Error::SmtpSend)?;
|
||||
|
||||
context.emit_event(EventType::SmtpMessageSent(format!(
|
||||
"Message len={message_len_bytes} was smtp-sent to {recipients_display}"
|
||||
|
||||
61
src/socks.rs
61
src/socks.rs
@@ -5,16 +5,17 @@ use std::pin::Pin;
|
||||
use std::time::Duration;
|
||||
|
||||
use anyhow::Result;
|
||||
pub use async_smtp::ServerAddress;
|
||||
use fast_socks5::client::{Config, Socks5Stream};
|
||||
use fast_socks5::util::target_addr::ToTargetAddr;
|
||||
use fast_socks5::AuthenticationMethod;
|
||||
use fast_socks5::Socks5Command;
|
||||
use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC};
|
||||
use tokio::net::TcpStream;
|
||||
use tokio_io_timeout::TimeoutStream;
|
||||
|
||||
use crate::context::Context;
|
||||
use crate::net::connect_tcp;
|
||||
use crate::sql::Sql;
|
||||
|
||||
#[derive(Default, Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Socks5Config {
|
||||
@@ -25,9 +26,7 @@ pub struct Socks5Config {
|
||||
|
||||
impl Socks5Config {
|
||||
/// Reads SOCKS5 configuration from the database.
|
||||
pub async fn from_database(context: &Context) -> Result<Option<Self>> {
|
||||
let sql = &context.sql;
|
||||
|
||||
pub async fn from_database(sql: &Sql) -> Result<Option<Self>> {
|
||||
let enabled = sql.get_raw_config_bool("socks5_enabled").await?;
|
||||
if enabled {
|
||||
let host = sql.get_raw_config("socks5_host").await?.unwrap_or_default();
|
||||
@@ -56,6 +55,20 @@ impl Socks5Config {
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts SOCKS5 configuration into URL.
|
||||
pub fn to_url(&self) -> String {
|
||||
// `socks5h` means that hostname is resolved into address by the proxy
|
||||
// and DNS requests should not leak.
|
||||
let mut url = "socks5h://".to_string();
|
||||
if let Some((username, password)) = &self.user_password {
|
||||
let username_urlencoded = utf8_percent_encode(username, NON_ALPHANUMERIC).to_string();
|
||||
let password_urlencoded = utf8_percent_encode(password, NON_ALPHANUMERIC).to_string();
|
||||
url += &format!("{username_urlencoded}:{password_urlencoded}@");
|
||||
}
|
||||
url += &format!("{}:{}", self.host, self.port);
|
||||
url
|
||||
}
|
||||
|
||||
/// If `load_dns_cache` is true, loads cached DNS resolution results.
|
||||
/// Use this only if the connection is going to be protected with TLS checks.
|
||||
pub async fn connect(
|
||||
@@ -87,14 +100,6 @@ impl Socks5Config {
|
||||
|
||||
Ok(socks_stream)
|
||||
}
|
||||
|
||||
pub fn to_async_smtp_socks5_config(&self) -> async_smtp::smtp::Socks5Config {
|
||||
async_smtp::smtp::Socks5Config {
|
||||
host: self.host.clone(),
|
||||
port: self.port,
|
||||
user_password: self.user_password.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Socks5Config {
|
||||
@@ -112,3 +117,35 @@ impl fmt::Display for Socks5Config {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_socks5h_url() {
|
||||
let config = Socks5Config {
|
||||
host: "127.0.0.1".to_string(),
|
||||
port: 9050,
|
||||
user_password: None,
|
||||
};
|
||||
assert_eq!(config.to_url(), "socks5h://127.0.0.1:9050");
|
||||
|
||||
let config = Socks5Config {
|
||||
host: "example.org".to_string(),
|
||||
port: 1080,
|
||||
user_password: Some(("root".to_string(), "toor".to_string())),
|
||||
};
|
||||
assert_eq!(config.to_url(), "socks5h://root:toor@example.org:1080");
|
||||
|
||||
let config = Socks5Config {
|
||||
host: "example.org".to_string(),
|
||||
port: 1080,
|
||||
user_password: Some(("root".to_string(), "foo/?\\@".to_string())),
|
||||
};
|
||||
assert_eq!(
|
||||
config.to_url(),
|
||||
"socks5h://root:foo%2F%3F%5C%40@example.org:1080"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1449,16 +1449,10 @@ mod tests {
|
||||
};
|
||||
|
||||
// delete self-talk first; this adds a message to device-chat about how self-talk can be restored
|
||||
let device_chat_msgs_before = chat::get_chat_msgs(&t, device_chat_id, 0)
|
||||
.await
|
||||
.unwrap()
|
||||
.len();
|
||||
let device_chat_msgs_before = chat::get_chat_msgs(&t, device_chat_id).await.unwrap().len();
|
||||
self_talk_id.delete(&t).await.ok();
|
||||
assert_eq!(
|
||||
chat::get_chat_msgs(&t, device_chat_id, 0)
|
||||
.await
|
||||
.unwrap()
|
||||
.len(),
|
||||
chat::get_chat_msgs(&t, device_chat_id).await.unwrap().len(),
|
||||
device_chat_msgs_before + 1
|
||||
);
|
||||
|
||||
|
||||
@@ -476,7 +476,7 @@ mod tests {
|
||||
let chat = Chat::load_from_db(&alice, chat_id).await?;
|
||||
assert!(chat.is_self_talk());
|
||||
assert_eq!(Chatlist::try_load(&alice, 0, None, None).await?.len(), 1);
|
||||
let msgs = chat::get_chat_msgs(&alice, chat_id, 0).await?;
|
||||
let msgs = chat::get_chat_msgs(&alice, chat_id).await?;
|
||||
assert_eq!(msgs.len(), 0);
|
||||
|
||||
// let alice's other device receive and execute the sync message,
|
||||
|
||||
@@ -18,11 +18,11 @@ use tokio::runtime::Handle;
|
||||
use tokio::sync::RwLock;
|
||||
use tokio::task;
|
||||
|
||||
use crate::chat::{self, Chat, ChatId};
|
||||
use crate::chat::{self, Chat, ChatId, MessageListOptions};
|
||||
use crate::chatlist::Chatlist;
|
||||
use crate::config::Config;
|
||||
use crate::constants::Chattype;
|
||||
use crate::constants::{DC_GCL_NO_SPECIALS, DC_GCM_ADDDAYMARKER, DC_MSG_ID_DAYMARKER};
|
||||
use crate::constants::{DC_GCL_NO_SPECIALS, DC_MSG_ID_DAYMARKER};
|
||||
use crate::contact::{Contact, ContactAddress, ContactId, Modifier, Origin};
|
||||
use crate::context::Context;
|
||||
use crate::events::{Event, EventType, Events};
|
||||
@@ -469,9 +469,7 @@ impl TestContext {
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let chat_msgs = chat::get_chat_msgs(self, received.chat_id, 0)
|
||||
.await
|
||||
.unwrap();
|
||||
let chat_msgs = chat::get_chat_msgs(self, received.chat_id).await.unwrap();
|
||||
assert!(
|
||||
chat_msgs.contains(&ChatItem::Message { msg_id: msg.id }),
|
||||
"received message is not shown in chat, maybe it's hidden (you may have \
|
||||
@@ -496,7 +494,7 @@ impl TestContext {
|
||||
///
|
||||
/// Panics on errors or if the most recent message is a marker.
|
||||
pub async fn get_last_msg_in(&self, chat_id: ChatId) -> Message {
|
||||
let msgs = chat::get_chat_msgs(&self.ctx, chat_id, 0).await.unwrap();
|
||||
let msgs = chat::get_chat_msgs(&self.ctx, chat_id).await.unwrap();
|
||||
let msg_id = if let ChatItem::Message { msg_id } = msgs.last().unwrap() {
|
||||
msg_id
|
||||
} else {
|
||||
@@ -624,9 +622,16 @@ impl TestContext {
|
||||
#[allow(dead_code)]
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
pub async fn print_chat(&self, chat_id: ChatId) {
|
||||
let msglist = chat::get_chat_msgs(self, chat_id, DC_GCM_ADDDAYMARKER)
|
||||
.await
|
||||
.unwrap();
|
||||
let msglist = chat::get_chat_msgs_ex(
|
||||
self,
|
||||
chat_id,
|
||||
MessageListOptions {
|
||||
info_only: false,
|
||||
add_daymarker: true,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let msglist: Vec<MsgId> = msglist
|
||||
.into_iter()
|
||||
.map(|x| match x {
|
||||
@@ -923,7 +928,7 @@ pub(crate) async fn get_chat_msg(
|
||||
index: usize,
|
||||
asserted_msgs_count: usize,
|
||||
) -> Message {
|
||||
let msgs = chat::get_chat_msgs(&t.ctx, chat_id, 0).await.unwrap();
|
||||
let msgs = chat::get_chat_msgs(&t.ctx, chat_id).await.unwrap();
|
||||
assert_eq!(msgs.len(), asserted_msgs_count);
|
||||
let msg_id = if let ChatItem::Message { msg_id } = msgs[index] {
|
||||
msg_id
|
||||
|
||||
@@ -4,7 +4,6 @@ use anyhow::Result;
|
||||
|
||||
use crate::chat;
|
||||
use crate::chat::ChatId;
|
||||
use crate::constants;
|
||||
use crate::contact;
|
||||
use crate::contact::Contact;
|
||||
use crate::contact::ContactId;
|
||||
@@ -345,9 +344,16 @@ async fn mark_as_verified(this: &TestContext, other: &TestContext) {
|
||||
}
|
||||
|
||||
async fn get_last_info_msg(t: &TestContext, chat_id: ChatId) -> Option<Message> {
|
||||
let msgs = chat::get_chat_msgs(&t.ctx, chat_id, constants::DC_GCM_INFO_ONLY)
|
||||
.await
|
||||
.unwrap();
|
||||
let msgs = chat::get_chat_msgs_ex(
|
||||
&t.ctx,
|
||||
chat_id,
|
||||
chat::MessageListOptions {
|
||||
info_only: true,
|
||||
add_daymarker: false,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let msg_id = if let chat::ChatItem::Message { msg_id } = msgs.last()? {
|
||||
msg_id
|
||||
} else {
|
||||
|
||||
14
src/tools.rs
14
src/tools.rs
@@ -1180,7 +1180,7 @@ DKIM Results: Passed=true, Works=true, Allow_Keychange=true";
|
||||
let chats = Chatlist::try_load(&t, 0, None, None).await.unwrap();
|
||||
assert_eq!(chats.len(), 1);
|
||||
let device_chat_id = chats.get_chat_id(0).unwrap();
|
||||
let msgs = chat::get_chat_msgs(&t, device_chat_id, 0).await.unwrap();
|
||||
let msgs = chat::get_chat_msgs(&t, device_chat_id).await.unwrap();
|
||||
assert_eq!(msgs.len(), 1);
|
||||
|
||||
// the message should be added only once a day - test that an hour later and nearly a day later
|
||||
@@ -1190,7 +1190,7 @@ DKIM Results: Passed=true, Works=true, Allow_Keychange=true";
|
||||
get_provider_update_timestamp(),
|
||||
)
|
||||
.await;
|
||||
let msgs = chat::get_chat_msgs(&t, device_chat_id, 0).await.unwrap();
|
||||
let msgs = chat::get_chat_msgs(&t, device_chat_id).await.unwrap();
|
||||
assert_eq!(msgs.len(), 1);
|
||||
|
||||
maybe_warn_on_bad_time(
|
||||
@@ -1199,7 +1199,7 @@ DKIM Results: Passed=true, Works=true, Allow_Keychange=true";
|
||||
get_provider_update_timestamp(),
|
||||
)
|
||||
.await;
|
||||
let msgs = chat::get_chat_msgs(&t, device_chat_id, 0).await.unwrap();
|
||||
let msgs = chat::get_chat_msgs(&t, device_chat_id).await.unwrap();
|
||||
assert_eq!(msgs.len(), 1);
|
||||
|
||||
// next day, there should be another device message
|
||||
@@ -1212,7 +1212,7 @@ DKIM Results: Passed=true, Works=true, Allow_Keychange=true";
|
||||
let chats = Chatlist::try_load(&t, 0, None, None).await.unwrap();
|
||||
assert_eq!(chats.len(), 1);
|
||||
assert_eq!(device_chat_id, chats.get_chat_id(0).unwrap());
|
||||
let msgs = chat::get_chat_msgs(&t, device_chat_id, 0).await.unwrap();
|
||||
let msgs = chat::get_chat_msgs(&t, device_chat_id).await.unwrap();
|
||||
assert_eq!(msgs.len(), 2);
|
||||
}
|
||||
|
||||
@@ -1242,7 +1242,7 @@ DKIM Results: Passed=true, Works=true, Allow_Keychange=true";
|
||||
let chats = Chatlist::try_load(&t, 0, None, None).await.unwrap();
|
||||
assert_eq!(chats.len(), 1);
|
||||
let device_chat_id = chats.get_chat_id(0).unwrap();
|
||||
let msgs = chat::get_chat_msgs(&t, device_chat_id, 0).await.unwrap();
|
||||
let msgs = chat::get_chat_msgs(&t, device_chat_id).await.unwrap();
|
||||
assert_eq!(msgs.len(), 1);
|
||||
|
||||
// do not repeat the warning every day ...
|
||||
@@ -1262,7 +1262,7 @@ DKIM Results: Passed=true, Works=true, Allow_Keychange=true";
|
||||
let chats = Chatlist::try_load(&t, 0, None, None).await.unwrap();
|
||||
assert_eq!(chats.len(), 1);
|
||||
let device_chat_id = chats.get_chat_id(0).unwrap();
|
||||
let msgs = chat::get_chat_msgs(&t, device_chat_id, 0).await.unwrap();
|
||||
let msgs = chat::get_chat_msgs(&t, device_chat_id).await.unwrap();
|
||||
let test_len = msgs.len();
|
||||
assert!(test_len == 1 || test_len == 2);
|
||||
|
||||
@@ -1277,7 +1277,7 @@ DKIM Results: Passed=true, Works=true, Allow_Keychange=true";
|
||||
let chats = Chatlist::try_load(&t, 0, None, None).await.unwrap();
|
||||
assert_eq!(chats.len(), 1);
|
||||
let device_chat_id = chats.get_chat_id(0).unwrap();
|
||||
let msgs = chat::get_chat_msgs(&t, device_chat_id, 0).await.unwrap();
|
||||
let msgs = chat::get_chat_msgs(&t, device_chat_id).await.unwrap();
|
||||
assert_eq!(msgs.len(), test_len + 1);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user