mirror of
https://github.com/chatmail/core.git
synced 2026-04-28 19:06:35 +03:00
Merge branch 'master' into flub/send-backup
This commit is contained in:
@@ -11,20 +11,15 @@ use anyhow::{bail, Context as _, Error, Result};
|
||||
use crate::key::{DcKey, SignedPublicKey};
|
||||
|
||||
/// Possible values for encryption preference
|
||||
#[derive(PartialEq, Eq, Debug, Clone, Copy, FromPrimitive, ToPrimitive)]
|
||||
#[derive(PartialEq, Eq, Debug, Default, Clone, Copy, FromPrimitive, ToPrimitive)]
|
||||
#[repr(u8)]
|
||||
pub enum EncryptPreference {
|
||||
#[default]
|
||||
NoPreference = 0,
|
||||
Mutual = 1,
|
||||
Reset = 20,
|
||||
}
|
||||
|
||||
impl Default for EncryptPreference {
|
||||
fn default() -> Self {
|
||||
EncryptPreference::NoPreference
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for EncryptPreference {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
|
||||
76
src/chat.rs
76
src/chat.rs
@@ -1,7 +1,5 @@
|
||||
//! # Chat module.
|
||||
|
||||
#![allow(missing_docs)]
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::convert::{TryFrom, TryInto};
|
||||
use std::fmt;
|
||||
@@ -60,8 +58,10 @@ pub enum ChatItem {
|
||||
},
|
||||
}
|
||||
|
||||
/// Chat protection status.
|
||||
#[derive(
|
||||
Debug,
|
||||
Default,
|
||||
Display,
|
||||
Clone,
|
||||
Copy,
|
||||
@@ -77,14 +77,14 @@ pub enum ChatItem {
|
||||
)]
|
||||
#[repr(u32)]
|
||||
pub enum ProtectionStatus {
|
||||
/// Chat is not protected.
|
||||
#[default]
|
||||
Unprotected = 0,
|
||||
Protected = 1,
|
||||
}
|
||||
|
||||
impl Default for ProtectionStatus {
|
||||
fn default() -> Self {
|
||||
ProtectionStatus::Unprotected
|
||||
}
|
||||
/// Chat is protected.
|
||||
///
|
||||
/// All members of the chat must be verified.
|
||||
Protected = 1,
|
||||
}
|
||||
|
||||
/// The reason why messages cannot be sent to the chat.
|
||||
@@ -295,7 +295,7 @@ impl ChatId {
|
||||
Ok(chat_id)
|
||||
}
|
||||
|
||||
pub async fn set_selfavatar_timestamp(self, context: &Context, timestamp: i64) -> Result<()> {
|
||||
async fn set_selfavatar_timestamp(self, context: &Context, timestamp: i64) -> Result<()> {
|
||||
context
|
||||
.sql
|
||||
.execute(
|
||||
@@ -680,6 +680,7 @@ impl ChatId {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns ID of the draft message, if there is one.
|
||||
async fn get_draft_msg_id(self, context: &Context) -> Result<Option<MsgId>> {
|
||||
let msg_id: Option<MsgId> = context
|
||||
.sql
|
||||
@@ -691,6 +692,7 @@ impl ChatId {
|
||||
Ok(msg_id)
|
||||
}
|
||||
|
||||
/// Returns draft message, if there is one.
|
||||
pub async fn get_draft(self, context: &Context) -> Result<Option<Message>> {
|
||||
if self.is_special() {
|
||||
return Ok(None);
|
||||
@@ -704,7 +706,7 @@ impl ChatId {
|
||||
}
|
||||
}
|
||||
|
||||
/// Delete draft message in specified chat, if there is one.
|
||||
/// Deletes draft message, if there is one.
|
||||
///
|
||||
/// Returns `true`, if message was deleted, `false` otherwise.
|
||||
async fn maybe_delete_draft(self, context: &Context) -> Result<bool> {
|
||||
@@ -826,6 +828,7 @@ impl ChatId {
|
||||
Ok(count)
|
||||
}
|
||||
|
||||
/// Returns the number of fresh messages in the chat.
|
||||
pub async fn get_fresh_msg_cnt(self, context: &Context) -> Result<usize> {
|
||||
// this function is typically used to show a badge counter beside _each_ chatlist item.
|
||||
// to make this as fast as possible, esp. on older devices, we added an combined index over the rows used for querying.
|
||||
@@ -892,7 +895,7 @@ impl ChatId {
|
||||
Ok(promoted)
|
||||
}
|
||||
|
||||
// Returns true if chat is a saved messages chat.
|
||||
/// Returns true if chat is a saved messages chat.
|
||||
pub async fn is_self_talk(self, context: &Context) -> Result<bool> {
|
||||
Ok(self.get_param(context).await?.exists(Param::Selftalk))
|
||||
}
|
||||
@@ -1130,15 +1133,29 @@ pub struct Chat {
|
||||
|
||||
/// Chat type, e.g. 1:1 chat, group chat, mailing list.
|
||||
pub typ: Chattype,
|
||||
|
||||
/// Chat name.
|
||||
pub name: String,
|
||||
|
||||
/// Whether the chat is archived or pinned.
|
||||
pub visibility: ChatVisibility,
|
||||
|
||||
/// Group ID.
|
||||
pub grpid: String,
|
||||
|
||||
/// Whether the chat is blocked, unblocked or a contact request.
|
||||
pub(crate) blocked: Blocked,
|
||||
|
||||
/// Additional chat parameters stored in the database.
|
||||
pub param: Params,
|
||||
|
||||
/// If location streaming is enabled in the chat.
|
||||
is_sending_locations: bool,
|
||||
|
||||
/// Duration of the chat being muted.
|
||||
pub mute_duration: MuteDuration,
|
||||
|
||||
/// If the chat is protected (verified).
|
||||
protected: ProtectionStatus,
|
||||
}
|
||||
|
||||
@@ -1258,7 +1275,7 @@ impl Chat {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn update_param(&mut self, context: &Context) -> Result<()> {
|
||||
pub(crate) async fn update_param(&mut self, context: &Context) -> Result<()> {
|
||||
context
|
||||
.sql
|
||||
.execute(
|
||||
@@ -1314,6 +1331,10 @@ impl Chat {
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
/// Returns chat avatar color.
|
||||
///
|
||||
/// For 1:1 chats, the color is calculated from the contact's address.
|
||||
/// For group chats the color is calculated from the chat name.
|
||||
pub async fn get_color(&self, context: &Context) -> Result<u32> {
|
||||
let mut color = 0;
|
||||
|
||||
@@ -1360,6 +1381,7 @@ impl Chat {
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns chat visibilitiy, e.g. whether it is archived or pinned.
|
||||
pub fn get_visibility(&self) -> ChatVisibility {
|
||||
self.visibility
|
||||
}
|
||||
@@ -1372,10 +1394,12 @@ impl Chat {
|
||||
self.blocked == Blocked::Request
|
||||
}
|
||||
|
||||
/// Returns true if the chat is not promoted.
|
||||
pub fn is_unpromoted(&self) -> bool {
|
||||
self.param.get_bool(Param::Unpromoted).unwrap_or_default()
|
||||
}
|
||||
|
||||
/// Returns true if the chat is promoted.
|
||||
pub fn is_promoted(&self) -> bool {
|
||||
!self.is_unpromoted()
|
||||
}
|
||||
@@ -1390,6 +1414,7 @@ impl Chat {
|
||||
self.is_sending_locations
|
||||
}
|
||||
|
||||
/// Returns true if the chat is currently muted.
|
||||
pub fn is_muted(&self) -> bool {
|
||||
match self.mute_duration {
|
||||
MuteDuration::NotMuted => false,
|
||||
@@ -1974,6 +1999,7 @@ impl ChatIdBlocked {
|
||||
}
|
||||
}
|
||||
|
||||
/// Prepares a message for sending.
|
||||
pub async fn prepare_msg(context: &Context, chat_id: ChatId, msg: &mut Message) -> Result<MsgId> {
|
||||
ensure!(
|
||||
!chat_id.is_special(),
|
||||
@@ -2331,6 +2357,9 @@ async fn create_send_msg_job(context: &Context, msg_id: MsgId) -> Result<Option<
|
||||
Ok(Some(row_id))
|
||||
}
|
||||
|
||||
/// Sends a text message to the given chat.
|
||||
///
|
||||
/// Returns database ID of the sent message.
|
||||
pub async fn send_text_msg(
|
||||
context: &Context,
|
||||
chat_id: ChatId,
|
||||
@@ -2663,6 +2692,12 @@ pub(crate) async fn mark_old_messages_as_noticed(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns all database message IDs of the given types.
|
||||
///
|
||||
/// If `chat_id` is None, return messages from any chat.
|
||||
///
|
||||
/// `Viewtype::Unknown` can be used for `msg_type2` and `msg_type3`
|
||||
/// if less than 3 viewtypes are requested.
|
||||
pub async fn get_chat_media(
|
||||
context: &Context,
|
||||
chat_id: Option<ChatId>,
|
||||
@@ -2706,10 +2741,14 @@ pub async fn get_chat_media(
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[repr(i32)]
|
||||
pub enum Direction {
|
||||
/// Search forward.
|
||||
Forward = 1,
|
||||
|
||||
/// Search backward.
|
||||
Backward = -1,
|
||||
}
|
||||
|
||||
/// Searches next/previous message based on the given message and list of types.
|
||||
pub async fn get_next_media(
|
||||
context: &Context,
|
||||
curr_msg_id: MsgId,
|
||||
@@ -3400,6 +3439,9 @@ pub async fn forward_msgs(context: &Context, msg_ids: &[MsgId], chat_id: ChatId)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Resends given messages with the same Message-ID.
|
||||
///
|
||||
/// This is primarily intended to make existing webxdcs available to new chat members.
|
||||
pub async fn resend_msgs(context: &Context, msg_ids: &[MsgId]) -> Result<()> {
|
||||
let mut chat_id = None;
|
||||
let mut msgs: Vec<Message> = Vec::new();
|
||||
@@ -3598,6 +3640,7 @@ pub async fn add_device_msg(
|
||||
add_device_msg_with_importance(context, label, msg, false).await
|
||||
}
|
||||
|
||||
/// Returns true if device message with a given label was ever added to the device chat.
|
||||
pub async fn was_device_msg_ever_added(context: &Context, label: &str) -> Result<bool> {
|
||||
ensure!(!label.is_empty(), "empty label");
|
||||
let exists = context
|
||||
@@ -5086,9 +5129,6 @@ mod tests {
|
||||
let alice = TestContext::new_alice().await;
|
||||
let bob = TestContext::new_bob().await;
|
||||
|
||||
alice.set_config(Config::ShowEmails, Some("2")).await?;
|
||||
bob.set_config(Config::ShowEmails, Some("2")).await?;
|
||||
|
||||
let alice_bob_contact = alice.add_or_lookup_contact(&bob).await;
|
||||
let contact_id = alice_bob_contact.id;
|
||||
let alice_chat_id = create_group_chat(&alice, ProtectionStatus::Unprotected, "grp").await?;
|
||||
@@ -5273,12 +5313,6 @@ mod tests {
|
||||
async fn test_classic_email_chat() -> Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
|
||||
// Alice enables receiving classic emails.
|
||||
alice
|
||||
.set_config(Config::ShowEmails, Some("2"))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Alice receives a classic (non-chat) message from Bob.
|
||||
receive_imf(
|
||||
&alice,
|
||||
|
||||
@@ -103,18 +103,26 @@ pub enum Config {
|
||||
/// Own avatar filename.
|
||||
Selfavatar,
|
||||
|
||||
/// Send BCC copy to self.
|
||||
///
|
||||
/// Should be enabled for multidevice setups.
|
||||
#[strum(props(default = "1"))]
|
||||
BccSelf,
|
||||
|
||||
/// True if encryption is preferred according to Autocrypt standard.
|
||||
#[strum(props(default = "1"))]
|
||||
E2eeEnabled,
|
||||
|
||||
/// True if Message Delivery Notifications (read receipts) should
|
||||
/// be sent and requested.
|
||||
#[strum(props(default = "1"))]
|
||||
MdnsEnabled,
|
||||
|
||||
/// True if "Sent" folder should be watched for changes.
|
||||
#[strum(props(default = "0"))]
|
||||
SentboxWatch,
|
||||
|
||||
/// True if chat messages should be moved to a separate folder.
|
||||
#[strum(props(default = "1"))]
|
||||
MvboxMove,
|
||||
|
||||
@@ -125,9 +133,11 @@ pub enum Config {
|
||||
#[strum(props(default = "0"))]
|
||||
OnlyFetchMvbox,
|
||||
|
||||
#[strum(props(default = "0"))] // also change ShowEmails.default() on changes
|
||||
/// Whether to show classic emails or only chat messages.
|
||||
#[strum(props(default = "2"))] // also change ShowEmails.default() on changes
|
||||
ShowEmails,
|
||||
|
||||
/// Quality of the media files to send.
|
||||
#[strum(props(default = "0"))] // also change MediaQuality.default() on changes
|
||||
MediaQuality,
|
||||
|
||||
@@ -142,6 +152,7 @@ pub enum Config {
|
||||
#[strum(props(default = "1"))]
|
||||
FetchedExistingMsgs,
|
||||
|
||||
/// Type of the OpenPGP key to generate.
|
||||
#[strum(props(default = "0"))]
|
||||
KeyGenType,
|
||||
|
||||
@@ -164,7 +175,9 @@ pub enum Config {
|
||||
#[strum(props(default = "0"))]
|
||||
DeleteDeviceAfter,
|
||||
|
||||
/// Save raw MIME messages with headers in the database if true.
|
||||
SaveMimeHeaders,
|
||||
|
||||
/// The primary email address. Also see `SecondaryAddrs`.
|
||||
ConfiguredAddr,
|
||||
|
||||
@@ -196,14 +209,19 @@ pub enum Config {
|
||||
/// Configured SMTP server port.
|
||||
ConfiguredSendPort,
|
||||
ConfiguredSmtpCertificateChecks,
|
||||
|
||||
/// Whether OAuth 2 is used with configured provider.
|
||||
ConfiguredServerFlags,
|
||||
ConfiguredSendSecurity,
|
||||
ConfiguredE2EEEnabled,
|
||||
ConfiguredInboxFolder,
|
||||
ConfiguredMvboxFolder,
|
||||
ConfiguredSentboxFolder,
|
||||
ConfiguredTimestamp,
|
||||
|
||||
/// ID of the configured provider from the provider database.
|
||||
ConfiguredProvider,
|
||||
|
||||
/// True if account is configured.
|
||||
Configured,
|
||||
|
||||
/// All secondary self addresses separated by spaces
|
||||
@@ -219,6 +237,7 @@ pub enum Config {
|
||||
#[strum(serialize = "sys.config_keys")]
|
||||
SysConfigKeys,
|
||||
|
||||
/// True if it is a bot account.
|
||||
Bot,
|
||||
|
||||
/// Whether we send a warning if the password is wrong (set to false when we send a warning
|
||||
@@ -293,28 +312,33 @@ impl Context {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns 32-bit signed integer configuration value for the given key.
|
||||
pub async fn get_config_int(&self, key: Config) -> Result<i32> {
|
||||
self.get_config(key)
|
||||
.await
|
||||
.map(|s: Option<String>| s.and_then(|s| s.parse().ok()).unwrap_or_default())
|
||||
}
|
||||
|
||||
/// Returns 64-bit signed integer configuration value for the given key.
|
||||
pub async fn get_config_i64(&self, key: Config) -> Result<i64> {
|
||||
self.get_config(key)
|
||||
.await
|
||||
.map(|s: Option<String>| s.and_then(|s| s.parse().ok()).unwrap_or_default())
|
||||
}
|
||||
|
||||
/// Returns 64-bit unsigned integer configuration value for the given key.
|
||||
pub async fn get_config_u64(&self, key: Config) -> Result<u64> {
|
||||
self.get_config(key)
|
||||
.await
|
||||
.map(|s: Option<String>| s.and_then(|s| s.parse().ok()).unwrap_or_default())
|
||||
}
|
||||
|
||||
/// Returns boolean configuration value for the given key.
|
||||
pub async fn get_config_bool(&self, key: Config) -> Result<bool> {
|
||||
Ok(self.get_config_int(key).await? != 0)
|
||||
}
|
||||
|
||||
/// Returns true if movebox ("DeltaChat" folder) should be watched.
|
||||
pub(crate) async fn should_watch_mvbox(&self) -> Result<bool> {
|
||||
Ok(self.get_config_bool(Config::MvboxMove).await?
|
||||
|| self.get_config_bool(Config::OnlyFetchMvbox).await?)
|
||||
|
||||
@@ -12,6 +12,7 @@ pub static DC_VERSION_STR: Lazy<String> = Lazy::new(|| env!("CARGO_PKG_VERSION")
|
||||
|
||||
#[derive(
|
||||
Debug,
|
||||
Default,
|
||||
Display,
|
||||
Clone,
|
||||
Copy,
|
||||
@@ -26,81 +27,56 @@ pub static DC_VERSION_STR: Lazy<String> = Lazy::new(|| env!("CARGO_PKG_VERSION")
|
||||
)]
|
||||
#[repr(i8)]
|
||||
pub enum Blocked {
|
||||
#[default]
|
||||
Not = 0,
|
||||
Yes = 1,
|
||||
Request = 2,
|
||||
}
|
||||
|
||||
impl Default for Blocked {
|
||||
fn default() -> Self {
|
||||
Blocked::Not
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Debug, Display, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive, FromSql, ToSql,
|
||||
Debug, Default, Display, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive, FromSql, ToSql,
|
||||
)]
|
||||
#[repr(u8)]
|
||||
pub enum ShowEmails {
|
||||
Off = 0,
|
||||
AcceptedContacts = 1,
|
||||
#[default] // also change Config.ShowEmails props(default) on changes
|
||||
All = 2,
|
||||
}
|
||||
|
||||
impl Default for ShowEmails {
|
||||
fn default() -> Self {
|
||||
ShowEmails::Off // also change Config.ShowEmails props(default) on changes
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Debug, Display, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive, FromSql, ToSql,
|
||||
Debug, Default, Display, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive, FromSql, ToSql,
|
||||
)]
|
||||
#[repr(u8)]
|
||||
pub enum MediaQuality {
|
||||
#[default] // also change Config.MediaQuality props(default) on changes
|
||||
Balanced = 0,
|
||||
Worse = 1,
|
||||
}
|
||||
|
||||
impl Default for MediaQuality {
|
||||
fn default() -> Self {
|
||||
MediaQuality::Balanced // also change Config.MediaQuality props(default) on changes
|
||||
}
|
||||
}
|
||||
|
||||
/// Type of the key to generate.
|
||||
#[derive(
|
||||
Debug, Display, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive, FromSql, ToSql,
|
||||
Debug, Default, Display, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive, FromSql, ToSql,
|
||||
)]
|
||||
#[repr(u8)]
|
||||
pub enum KeyGenType {
|
||||
#[default]
|
||||
Default = 0,
|
||||
Rsa2048 = 1,
|
||||
Ed25519 = 2,
|
||||
}
|
||||
|
||||
impl Default for KeyGenType {
|
||||
fn default() -> Self {
|
||||
KeyGenType::Default
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Debug, Display, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive, FromSql, ToSql,
|
||||
Debug, Default, Display, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive, FromSql, ToSql,
|
||||
)]
|
||||
#[repr(i8)]
|
||||
pub enum VideochatType {
|
||||
#[default]
|
||||
Unknown = 0,
|
||||
BasicWebrtc = 1,
|
||||
Jitsi = 2,
|
||||
}
|
||||
|
||||
impl Default for VideochatType {
|
||||
fn default() -> Self {
|
||||
VideochatType::Unknown
|
||||
}
|
||||
}
|
||||
|
||||
pub const DC_HANDSHAKE_CONTINUE_NORMAL_PROCESSING: i32 = 0x01;
|
||||
pub const DC_HANDSHAKE_STOP_NORMAL_PROCESSING: i32 = 0x02;
|
||||
pub const DC_HANDSHAKE_ADD_DELETE_JOB: i32 = 0x04;
|
||||
@@ -135,6 +111,7 @@ pub const DC_CHAT_ID_LAST_SPECIAL: ChatId = ChatId::new(9);
|
||||
|
||||
#[derive(
|
||||
Debug,
|
||||
Default,
|
||||
Display,
|
||||
Clone,
|
||||
Copy,
|
||||
@@ -150,6 +127,7 @@ pub const DC_CHAT_ID_LAST_SPECIAL: ChatId = ChatId::new(9);
|
||||
)]
|
||||
#[repr(u32)]
|
||||
pub enum Chattype {
|
||||
#[default]
|
||||
Undefined = 0,
|
||||
Single = 100,
|
||||
Group = 120,
|
||||
@@ -157,12 +135,6 @@ pub enum Chattype {
|
||||
Broadcast = 160,
|
||||
}
|
||||
|
||||
impl Default for Chattype {
|
||||
fn default() -> Self {
|
||||
Chattype::Undefined
|
||||
}
|
||||
}
|
||||
|
||||
pub const DC_MSG_ID_DAYMARKER: u32 = 9;
|
||||
pub const DC_MSG_ID_LAST_SPECIAL: u32 = 9;
|
||||
|
||||
@@ -244,7 +216,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_showemails_values() {
|
||||
// values may be written to disk and must not change
|
||||
assert_eq!(ShowEmails::Off, ShowEmails::default());
|
||||
assert_eq!(ShowEmails::All, ShowEmails::default());
|
||||
assert_eq!(ShowEmails::Off, ShowEmails::from_i32(0).unwrap());
|
||||
assert_eq!(
|
||||
ShowEmails::AcceptedContacts,
|
||||
|
||||
@@ -223,12 +223,24 @@ pub struct Contact {
|
||||
|
||||
/// Possible origins of a contact.
|
||||
#[derive(
|
||||
Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, FromPrimitive, ToPrimitive, FromSql, ToSql,
|
||||
Debug,
|
||||
Default,
|
||||
Clone,
|
||||
Copy,
|
||||
PartialEq,
|
||||
Eq,
|
||||
PartialOrd,
|
||||
Ord,
|
||||
FromPrimitive,
|
||||
ToPrimitive,
|
||||
FromSql,
|
||||
ToSql,
|
||||
)]
|
||||
#[repr(u32)]
|
||||
pub enum Origin {
|
||||
/// Unknown origin. Can be used as a minimum origin to specify that the caller does not care
|
||||
/// about origin of the contact.
|
||||
#[default]
|
||||
Unknown = 0,
|
||||
|
||||
/// The contact is a mailing list address, needed to unblock mailing lists
|
||||
@@ -287,12 +299,6 @@ pub enum Origin {
|
||||
ManuallyCreated = 0x0400_0000,
|
||||
}
|
||||
|
||||
impl Default for Origin {
|
||||
fn default() -> Self {
|
||||
Origin::Unknown
|
||||
}
|
||||
}
|
||||
|
||||
impl Origin {
|
||||
/// Contacts that are known, i. e. they came in via accepted contacts or
|
||||
/// themselves an accepted contact. Known contacts are shown in the
|
||||
@@ -1515,7 +1521,6 @@ fn split_address_book(book: &str) -> Vec<(&str, &str)> {
|
||||
book.lines()
|
||||
.collect::<Vec<&str>>()
|
||||
.chunks(2)
|
||||
.into_iter()
|
||||
.filter_map(|chunk| {
|
||||
let name = chunk.first()?;
|
||||
let addr = chunk.get(1)?;
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
//! Context module.
|
||||
|
||||
#![allow(missing_docs)]
|
||||
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use std::ffi::OsString;
|
||||
use std::ops::Deref;
|
||||
|
||||
@@ -35,6 +35,7 @@ pub(crate) const MIN_DELETE_SERVER_AFTER: i64 = 48 * 60 * 60;
|
||||
/// Download state of the message.
|
||||
#[derive(
|
||||
Debug,
|
||||
Default,
|
||||
Display,
|
||||
Clone,
|
||||
Copy,
|
||||
@@ -50,6 +51,7 @@ pub(crate) const MIN_DELETE_SERVER_AFTER: i64 = 48 * 60 * 60;
|
||||
#[repr(u32)]
|
||||
pub enum DownloadState {
|
||||
/// Message is fully downloaded.
|
||||
#[default]
|
||||
Done = 0,
|
||||
|
||||
/// Message is partially downloaded and can be fully downloaded at request.
|
||||
@@ -62,12 +64,6 @@ pub enum DownloadState {
|
||||
InProgress = 1000,
|
||||
}
|
||||
|
||||
impl Default for DownloadState {
|
||||
fn default() -> Self {
|
||||
DownloadState::Done
|
||||
}
|
||||
}
|
||||
|
||||
impl Context {
|
||||
// Returns validated download limit or `None` for "no limit".
|
||||
pub(crate) async fn download_limit(&self) -> Result<Option<u32>> {
|
||||
|
||||
@@ -70,7 +70,8 @@ pub enum HeaderDef {
|
||||
/// See <https://datatracker.ietf.org/doc/html/rfc8601>
|
||||
AuthenticationResults,
|
||||
|
||||
_TestHeader,
|
||||
#[cfg(test)]
|
||||
TestHeader,
|
||||
}
|
||||
|
||||
impl HeaderDef {
|
||||
@@ -103,7 +104,7 @@ mod tests {
|
||||
fn kebab_test() {
|
||||
assert_eq!(HeaderDef::From_.get_headername(), "from");
|
||||
|
||||
assert_eq!(HeaderDef::_TestHeader.get_headername(), "test-header");
|
||||
assert_eq!(HeaderDef::TestHeader.get_headername(), "test-header");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
18
src/html.rs
18
src/html.rs
@@ -240,7 +240,7 @@ fn mimepart_to_data_url(mail: &mailparse::ParsedMail<'_>) -> Result<String> {
|
||||
}
|
||||
|
||||
impl MsgId {
|
||||
/// Get HTML from a message-id.
|
||||
/// Get HTML by database message id.
|
||||
/// This requires `mime_headers` field to be set for the message;
|
||||
/// this is the case at least when `Message.has_html()` returns true
|
||||
/// (we do not save raw mime unconditionally in the database to save space).
|
||||
@@ -435,7 +435,6 @@ test some special html-characters as < > and & but also " and &#x
|
||||
async fn test_html_forwarding() {
|
||||
// alice receives a non-delta html-message
|
||||
let alice = TestContext::new_alice().await;
|
||||
alice.set_config(Config::ShowEmails, Some("2")).await.ok();
|
||||
let chat = alice
|
||||
.create_chat_with_contact("", "sender@testrun.org")
|
||||
.await;
|
||||
@@ -481,10 +480,13 @@ test some special html-characters as < > and & but also " and &#x
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_html_forwarding_encrypted() {
|
||||
// Alice receives a non-delta html-message
|
||||
// (`ShowEmails=1` lets Alice actually receive non-delta messages for known contacts,
|
||||
// the contact is marked as known by creating a chat using `chat_with_contact()`)
|
||||
// (`ShowEmails=AcceptedContacts` lets Alice actually receive non-delta messages for known
|
||||
// contacts, the contact is marked as known by creating a chat using `chat_with_contact()`)
|
||||
let alice = TestContext::new_alice().await;
|
||||
alice.set_config(Config::ShowEmails, Some("1")).await.ok();
|
||||
alice
|
||||
.set_config(Config::ShowEmails, Some("1"))
|
||||
.await
|
||||
.unwrap();
|
||||
let chat = alice
|
||||
.create_chat_with_contact("", "sender@testrun.org")
|
||||
.await;
|
||||
@@ -502,7 +504,10 @@ test some special html-characters as < > and & but also " and &#x
|
||||
|
||||
// receive the message on another device
|
||||
let alice = TestContext::new_alice().await;
|
||||
assert_eq!(alice.get_config_int(Config::ShowEmails).await.unwrap(), 0); // set to "1" above, make sure it is another db
|
||||
alice
|
||||
.set_config(Config::ShowEmails, Some("0"))
|
||||
.await
|
||||
.unwrap();
|
||||
let msg = alice.recv_msg(&msg).await;
|
||||
assert_eq!(msg.chat_id, alice.get_self_chat().await.id);
|
||||
assert_eq!(msg.get_from_id(), ContactId::SELF);
|
||||
@@ -550,7 +555,6 @@ test some special html-characters as < > and & but also " and &#x
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_cp1252_html() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.set_config(Config::ShowEmails, Some("2")).await?;
|
||||
receive_imf(
|
||||
&t,
|
||||
include_bytes!("../test-data/message/cp1252-html.eml"),
|
||||
|
||||
@@ -2534,7 +2534,6 @@ mod tests {
|
||||
t.ctx
|
||||
.set_config(Config::MvboxMove, Some(if mvbox_move { "1" } else { "0" }))
|
||||
.await?;
|
||||
t.ctx.set_config(Config::ShowEmails, Some("2")).await?;
|
||||
|
||||
if accepted_chat {
|
||||
let contact_id = Contact::create(&t.ctx, "", "bob@example.net").await?;
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
//! # Import/export module.
|
||||
|
||||
#![allow(missing_docs)]
|
||||
|
||||
use std::any::Any;
|
||||
use std::ffi::OsStr;
|
||||
use std::iter::FusedIterator;
|
||||
@@ -44,6 +42,7 @@ pub use transfer::{get_backup, BackupProvider};
|
||||
const DBFILE_BACKUP_NAME: &str = "dc_database_backup.sqlite";
|
||||
const BLOBS_BACKUP_NAME: &str = "blobs_backup";
|
||||
|
||||
/// Import/export command.
|
||||
#[derive(Debug, Display, Copy, Clone, PartialEq, Eq, FromPrimitive, ToPrimitive)]
|
||||
#[repr(u32)]
|
||||
pub enum ImexMode {
|
||||
@@ -226,6 +225,7 @@ pub async fn render_setup_file(context: &Context, passphrase: &str) -> Result<St
|
||||
))
|
||||
}
|
||||
|
||||
/// Creates a new setup code for Autocrypt Setup Message.
|
||||
pub fn create_setup_code(_context: &Context) -> String {
|
||||
let mut random_val: u16;
|
||||
let mut rng = thread_rng();
|
||||
@@ -264,6 +264,10 @@ async fn maybe_add_bcc_self_device_msg(context: &Context) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Continue key transfer via Autocrypt Setup Message.
|
||||
///
|
||||
/// `msg_id` is the ID of the received Autocrypt Setup Message.
|
||||
/// `setup_code` is the code entered by the user.
|
||||
pub async fn continue_key_transfer(
|
||||
context: &Context,
|
||||
msg_id: MsgId,
|
||||
|
||||
@@ -244,12 +244,18 @@ pub struct Message {
|
||||
|
||||
/// ID of the first contact in the `To:` header.
|
||||
pub(crate) to_id: ContactId,
|
||||
|
||||
/// ID of the chat message belongs to.
|
||||
pub(crate) chat_id: ChatId,
|
||||
|
||||
/// Type of the message.
|
||||
pub(crate) viewtype: Viewtype,
|
||||
|
||||
/// State of the message.
|
||||
pub(crate) state: MessageState,
|
||||
pub(crate) download_state: DownloadState,
|
||||
|
||||
/// Whether the message is hidden.
|
||||
pub(crate) hidden: bool,
|
||||
pub(crate) timestamp_sort: i64,
|
||||
pub(crate) timestamp_sent: i64,
|
||||
@@ -257,8 +263,14 @@ pub struct Message {
|
||||
pub(crate) ephemeral_timer: EphemeralTimer,
|
||||
pub(crate) ephemeral_timestamp: i64,
|
||||
pub(crate) text: Option<String>,
|
||||
|
||||
/// Message subject.
|
||||
pub(crate) subject: String,
|
||||
|
||||
/// `Message-ID` header value.
|
||||
pub(crate) rfc724_mid: String,
|
||||
|
||||
/// `In-Reply-To` header value.
|
||||
pub(crate) in_reply_to: Option<String>,
|
||||
pub(crate) is_dc_message: MessengerMessage,
|
||||
pub(crate) mime_modified: bool,
|
||||
@@ -969,6 +981,7 @@ impl Message {
|
||||
/// For outgoing message, the message could be pending, already delivered or confirmed.
|
||||
#[derive(
|
||||
Debug,
|
||||
Default,
|
||||
Clone,
|
||||
Copy,
|
||||
PartialEq,
|
||||
@@ -985,6 +998,7 @@ impl Message {
|
||||
#[repr(u32)]
|
||||
pub enum MessageState {
|
||||
/// Undefined message state.
|
||||
#[default]
|
||||
Undefined = 0,
|
||||
|
||||
/// Incoming *fresh* message. Fresh messages are neither noticed
|
||||
@@ -1027,12 +1041,6 @@ pub enum MessageState {
|
||||
OutMdnRcvd = 28,
|
||||
}
|
||||
|
||||
impl Default for MessageState {
|
||||
fn default() -> Self {
|
||||
MessageState::Undefined
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for MessageState {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(
|
||||
@@ -1902,6 +1910,7 @@ pub(crate) async fn rfc724_mid_exists(
|
||||
/// How a message is primarily displayed.
|
||||
#[derive(
|
||||
Debug,
|
||||
Default,
|
||||
Display,
|
||||
Clone,
|
||||
Copy,
|
||||
@@ -1917,6 +1926,7 @@ pub(crate) async fn rfc724_mid_exists(
|
||||
#[repr(u32)]
|
||||
pub enum Viewtype {
|
||||
/// Unknown message type.
|
||||
#[default]
|
||||
Unknown = 0,
|
||||
|
||||
/// Text message.
|
||||
@@ -1970,12 +1980,6 @@ pub enum Viewtype {
|
||||
Webxdc = 80,
|
||||
}
|
||||
|
||||
impl Default for Viewtype {
|
||||
fn default() -> Self {
|
||||
Viewtype::Unknown
|
||||
}
|
||||
}
|
||||
|
||||
impl Viewtype {
|
||||
/// Whether a message with this [`Viewtype`] should have a file attachment.
|
||||
pub fn has_file(&self) -> bool {
|
||||
|
||||
@@ -2009,7 +2009,7 @@ mod tests {
|
||||
"1.0"
|
||||
);
|
||||
|
||||
let _mime_msg = MimeMessage::from_bytes(context, rendered_msg.message.as_bytes())
|
||||
let _mime_msg = MimeMessage::from_bytes(context, rendered_msg.message.as_bytes(), None)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
@@ -48,13 +48,19 @@ use crate::{location, tools};
|
||||
/// It is created by parsing the raw data of an actual MIME message
|
||||
/// using the [MimeMessage::from_bytes] constructor.
|
||||
#[derive(Debug)]
|
||||
pub struct MimeMessage {
|
||||
pub(crate) struct MimeMessage {
|
||||
/// Parsed MIME parts.
|
||||
pub parts: Vec<Part>,
|
||||
|
||||
/// Message headers.
|
||||
header: HashMap<String, String>,
|
||||
|
||||
/// Addresses are normalized and lowercased:
|
||||
pub recipients: Vec<SingleInfo>,
|
||||
|
||||
/// `From:` address.
|
||||
pub from: SingleInfo,
|
||||
|
||||
/// Whether the From address was repeated in the signed part
|
||||
/// (and we know that the signer intended to send from this address)
|
||||
pub from_is_signed: bool,
|
||||
@@ -72,6 +78,8 @@ pub struct MimeMessage {
|
||||
/// The set of mail recipient addresses for which gossip headers were applied, regardless of
|
||||
/// whether they modified any peerstates.
|
||||
pub gossiped_addr: HashSet<String>,
|
||||
|
||||
/// True if the message is a forwarded message.
|
||||
pub is_forwarded: bool,
|
||||
pub is_system_message: SystemMessage,
|
||||
pub location_kml: Option<location::Kml>,
|
||||
@@ -123,15 +131,26 @@ pub(crate) enum MailinglistType {
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Debug, Display, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive, ToSql, FromSql,
|
||||
Debug, Default, Display, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive, ToSql, FromSql,
|
||||
)]
|
||||
#[repr(u32)]
|
||||
pub enum SystemMessage {
|
||||
#[default]
|
||||
Unknown = 0,
|
||||
|
||||
/// Group name changed.
|
||||
GroupNameChanged = 2,
|
||||
|
||||
/// Group avatar changed.
|
||||
GroupImageChanged = 3,
|
||||
|
||||
/// Member was added to the group.
|
||||
MemberAddedToGroup = 4,
|
||||
|
||||
/// Member was removed from the group.
|
||||
MemberRemovedFromGroup = 5,
|
||||
|
||||
/// Autocrypt Setup Message.
|
||||
AutocryptSetupMessage = 6,
|
||||
SecurejoinMessage = 7,
|
||||
LocationStreamingEnabled = 8,
|
||||
@@ -140,41 +159,33 @@ pub enum SystemMessage {
|
||||
/// Chat ephemeral message timer is changed.
|
||||
EphemeralTimerChanged = 10,
|
||||
|
||||
// Chat protection state changed
|
||||
/// Chat protection is enabled.
|
||||
ChatProtectionEnabled = 11,
|
||||
|
||||
/// Chat protection is disabled.
|
||||
ChatProtectionDisabled = 12,
|
||||
|
||||
/// Self-sent-message that contains only json used for multi-device-sync;
|
||||
/// if possible, we attach that to other messages as for locations.
|
||||
MultiDeviceSync = 20,
|
||||
|
||||
// Sync message that contains a json payload
|
||||
// sent to the other webxdc instances
|
||||
// These messages are not shown in the chat.
|
||||
/// Sync message that contains a json payload
|
||||
/// sent to the other webxdc instances
|
||||
/// These messages are not shown in the chat.
|
||||
WebxdcStatusUpdate = 30,
|
||||
|
||||
// Webxdc info added with `info` set in `send_webxdc_status_update()`.
|
||||
/// Webxdc info added with `info` set in `send_webxdc_status_update()`.
|
||||
WebxdcInfoMessage = 32,
|
||||
}
|
||||
|
||||
impl Default for SystemMessage {
|
||||
fn default() -> Self {
|
||||
SystemMessage::Unknown
|
||||
}
|
||||
}
|
||||
|
||||
const MIME_AC_SETUP_FILE: &str = "application/autocrypt-setup";
|
||||
|
||||
impl MimeMessage {
|
||||
pub async fn from_bytes(context: &Context, body: &[u8]) -> Result<Self> {
|
||||
MimeMessage::from_bytes_with_partial(context, body, None).await
|
||||
}
|
||||
|
||||
/// Parse a mime message.
|
||||
///
|
||||
/// If `partial` is set, it contains the full message size in bytes
|
||||
/// and `body` contains the header only.
|
||||
pub(crate) async fn from_bytes_with_partial(
|
||||
pub(crate) async fn from_bytes(
|
||||
context: &Context,
|
||||
body: &[u8],
|
||||
partial: Option<u32>,
|
||||
@@ -1764,16 +1775,32 @@ fn is_known(key: &str) -> bool {
|
||||
)
|
||||
}
|
||||
|
||||
/// Parsed MIME part.
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct Part {
|
||||
/// Type of the MIME part determining how it should be displayed.
|
||||
pub typ: Viewtype,
|
||||
|
||||
/// MIME type.
|
||||
pub mimetype: Option<Mime>,
|
||||
|
||||
/// Message text to be displayed in the chat.
|
||||
pub msg: String,
|
||||
|
||||
/// Message text to be displayed in message info.
|
||||
pub msg_raw: Option<String>,
|
||||
|
||||
/// Size of the MIME part in bytes.
|
||||
pub bytes: usize,
|
||||
pub param: Params,
|
||||
|
||||
/// Attachment filename.
|
||||
pub(crate) org_filename: Option<String>,
|
||||
|
||||
/// An error detected during parsing.
|
||||
pub error: Option<String>,
|
||||
|
||||
/// True if conversion from HTML to plaintext failed.
|
||||
pub(crate) dehtml_failed: bool,
|
||||
|
||||
/// the part is a child or a descendant of multipart/related.
|
||||
@@ -1957,7 +1984,6 @@ mod tests {
|
||||
use super::*;
|
||||
use crate::{
|
||||
chatlist::Chatlist,
|
||||
config::Config,
|
||||
constants::{Blocked, DC_DESIRED_TEXT_LEN, DC_ELLIPSIS},
|
||||
message::{Message, MessageState, MessengerMessage},
|
||||
receive_imf::receive_imf,
|
||||
@@ -1977,35 +2003,35 @@ mod tests {
|
||||
async fn test_mimeparser_fromheader() {
|
||||
let ctx = TestContext::new_alice().await;
|
||||
|
||||
let mimemsg = MimeMessage::from_bytes(&ctx, b"From: g@c.de\n\nhi")
|
||||
let mimemsg = MimeMessage::from_bytes(&ctx, b"From: g@c.de\n\nhi", None)
|
||||
.await
|
||||
.unwrap();
|
||||
let contact = mimemsg.from;
|
||||
assert_eq!(contact.addr, "g@c.de");
|
||||
assert_eq!(contact.display_name, None);
|
||||
|
||||
let mimemsg = MimeMessage::from_bytes(&ctx, b"From: g@c.de \n\nhi")
|
||||
let mimemsg = MimeMessage::from_bytes(&ctx, b"From: g@c.de \n\nhi", None)
|
||||
.await
|
||||
.unwrap();
|
||||
let contact = mimemsg.from;
|
||||
assert_eq!(contact.addr, "g@c.de");
|
||||
assert_eq!(contact.display_name, None);
|
||||
|
||||
let mimemsg = MimeMessage::from_bytes(&ctx, b"From: <g@c.de>\n\nhi")
|
||||
let mimemsg = MimeMessage::from_bytes(&ctx, b"From: <g@c.de>\n\nhi", None)
|
||||
.await
|
||||
.unwrap();
|
||||
let contact = mimemsg.from;
|
||||
assert_eq!(contact.addr, "g@c.de");
|
||||
assert_eq!(contact.display_name, None);
|
||||
|
||||
let mimemsg = MimeMessage::from_bytes(&ctx, b"From: Goetz C <g@c.de>\n\nhi")
|
||||
let mimemsg = MimeMessage::from_bytes(&ctx, b"From: Goetz C <g@c.de>\n\nhi", None)
|
||||
.await
|
||||
.unwrap();
|
||||
let contact = mimemsg.from;
|
||||
assert_eq!(contact.addr, "g@c.de");
|
||||
assert_eq!(contact.display_name, Some("Goetz C".to_string()));
|
||||
|
||||
let mimemsg = MimeMessage::from_bytes(&ctx, b"From: \"Goetz C\" <g@c.de>\n\nhi")
|
||||
let mimemsg = MimeMessage::from_bytes(&ctx, b"From: \"Goetz C\" <g@c.de>\n\nhi", None)
|
||||
.await
|
||||
.unwrap();
|
||||
let contact = mimemsg.from;
|
||||
@@ -2013,7 +2039,7 @@ mod tests {
|
||||
assert_eq!(contact.display_name, Some("Goetz C".to_string()));
|
||||
|
||||
let mimemsg =
|
||||
MimeMessage::from_bytes(&ctx, b"From: =?utf-8?q?G=C3=B6tz?= C <g@c.de>\n\nhi")
|
||||
MimeMessage::from_bytes(&ctx, b"From: =?utf-8?q?G=C3=B6tz?= C <g@c.de>\n\nhi", None)
|
||||
.await
|
||||
.unwrap();
|
||||
let contact = mimemsg.from;
|
||||
@@ -2022,10 +2048,13 @@ mod tests {
|
||||
|
||||
// although RFC 2047 says, encoded-words shall not appear inside quoted-string,
|
||||
// this combination is used in the wild eg. by MailMate
|
||||
let mimemsg =
|
||||
MimeMessage::from_bytes(&ctx, b"From: \"=?utf-8?q?G=C3=B6tz?= C\" <g@c.de>\n\nhi")
|
||||
.await
|
||||
.unwrap();
|
||||
let mimemsg = MimeMessage::from_bytes(
|
||||
&ctx,
|
||||
b"From: \"=?utf-8?q?G=C3=B6tz?= C\" <g@c.de>\n\nhi",
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let contact = mimemsg.from;
|
||||
assert_eq!(contact.addr, "g@c.de");
|
||||
assert_eq!(contact.display_name, Some("Götz C".to_string()));
|
||||
@@ -2035,7 +2064,7 @@ mod tests {
|
||||
async fn test_mimeparser_crash() {
|
||||
let context = TestContext::new_alice().await;
|
||||
let raw = include_bytes!("../test-data/message/issue_523.txt");
|
||||
let mimeparser = MimeMessage::from_bytes(&context.ctx, &raw[..])
|
||||
let mimeparser = MimeMessage::from_bytes(&context.ctx, &raw[..], None)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
@@ -2047,7 +2076,7 @@ mod tests {
|
||||
async fn test_get_rfc724_mid_exists() {
|
||||
let context = TestContext::new_alice().await;
|
||||
let raw = include_bytes!("../test-data/message/mail_with_message_id.txt");
|
||||
let mimeparser = MimeMessage::from_bytes(&context.ctx, &raw[..])
|
||||
let mimeparser = MimeMessage::from_bytes(&context.ctx, &raw[..], None)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
@@ -2061,7 +2090,7 @@ mod tests {
|
||||
async fn test_get_rfc724_mid_not_exists() {
|
||||
let context = TestContext::new_alice().await;
|
||||
let raw = include_bytes!("../test-data/message/issue_523.txt");
|
||||
let mimeparser = MimeMessage::from_bytes(&context.ctx, &raw[..])
|
||||
let mimeparser = MimeMessage::from_bytes(&context.ctx, &raw[..], None)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(mimeparser.get_rfc724_mid(), None);
|
||||
@@ -2258,7 +2287,7 @@ mod tests {
|
||||
test1\n\
|
||||
";
|
||||
|
||||
let mimeparser = MimeMessage::from_bytes_with_partial(&context.ctx, &raw[..], None).await;
|
||||
let mimeparser = MimeMessage::from_bytes(&context.ctx, &raw[..], None).await;
|
||||
|
||||
assert!(mimeparser.is_err());
|
||||
}
|
||||
@@ -2273,7 +2302,7 @@ mod tests {
|
||||
\n\
|
||||
Some reply\n\
|
||||
";
|
||||
let mimeparser = MimeMessage::from_bytes(&context.ctx, &raw[..])
|
||||
let mimeparser = MimeMessage::from_bytes(&context.ctx, &raw[..], None)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
@@ -2319,7 +2348,7 @@ mod tests {
|
||||
--==break==--\n\
|
||||
\n";
|
||||
|
||||
let mimeparser = MimeMessage::from_bytes(&context.ctx, &raw[..])
|
||||
let mimeparser = MimeMessage::from_bytes(&context.ctx, &raw[..], None)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
@@ -2328,7 +2357,7 @@ mod tests {
|
||||
assert_eq!(of, "no");
|
||||
|
||||
// unknown headers do not bubble upwards
|
||||
let of = mimeparser.get_header(HeaderDef::_TestHeader).unwrap();
|
||||
let of = mimeparser.get_header(HeaderDef::TestHeader).unwrap();
|
||||
assert_eq!(of, "Bar");
|
||||
|
||||
// the following fields would bubble up
|
||||
@@ -2353,26 +2382,26 @@ mod tests {
|
||||
let t = TestContext::new_alice().await;
|
||||
|
||||
let raw = include_bytes!("../test-data/message/mail_attach_txt.eml");
|
||||
let mimeparser = MimeMessage::from_bytes(&t, &raw[..]).await.unwrap();
|
||||
let mimeparser = MimeMessage::from_bytes(&t, &raw[..], None).await.unwrap();
|
||||
assert_eq!(mimeparser.user_avatar, None);
|
||||
assert_eq!(mimeparser.group_avatar, None);
|
||||
|
||||
let raw = include_bytes!("../test-data/message/mail_with_user_avatar.eml");
|
||||
let mimeparser = MimeMessage::from_bytes(&t, &raw[..]).await.unwrap();
|
||||
let mimeparser = MimeMessage::from_bytes(&t, &raw[..], None).await.unwrap();
|
||||
assert_eq!(mimeparser.parts.len(), 1);
|
||||
assert_eq!(mimeparser.parts[0].typ, Viewtype::Text);
|
||||
assert!(mimeparser.user_avatar.unwrap().is_change());
|
||||
assert_eq!(mimeparser.group_avatar, None);
|
||||
|
||||
let raw = include_bytes!("../test-data/message/mail_with_user_avatar_deleted.eml");
|
||||
let mimeparser = MimeMessage::from_bytes(&t, &raw[..]).await.unwrap();
|
||||
let mimeparser = MimeMessage::from_bytes(&t, &raw[..], None).await.unwrap();
|
||||
assert_eq!(mimeparser.parts.len(), 1);
|
||||
assert_eq!(mimeparser.parts[0].typ, Viewtype::Text);
|
||||
assert_eq!(mimeparser.user_avatar, Some(AvatarAction::Delete));
|
||||
assert_eq!(mimeparser.group_avatar, None);
|
||||
|
||||
let raw = include_bytes!("../test-data/message/mail_with_user_and_group_avatars.eml");
|
||||
let mimeparser = MimeMessage::from_bytes(&t, &raw[..]).await.unwrap();
|
||||
let mimeparser = MimeMessage::from_bytes(&t, &raw[..], None).await.unwrap();
|
||||
assert_eq!(mimeparser.parts.len(), 1);
|
||||
assert_eq!(mimeparser.parts[0].typ, Viewtype::Text);
|
||||
assert!(mimeparser.user_avatar.unwrap().is_change());
|
||||
@@ -2382,7 +2411,9 @@ mod tests {
|
||||
let raw = include_bytes!("../test-data/message/mail_with_user_and_group_avatars.eml");
|
||||
let raw = String::from_utf8_lossy(raw).to_string();
|
||||
let raw = raw.replace("Chat-User-Avatar:", "Xhat-Xser-Xvatar:");
|
||||
let mimeparser = MimeMessage::from_bytes(&t, raw.as_bytes()).await.unwrap();
|
||||
let mimeparser = MimeMessage::from_bytes(&t, raw.as_bytes(), None)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(mimeparser.parts.len(), 1);
|
||||
assert_eq!(mimeparser.parts[0].typ, Viewtype::Image);
|
||||
assert_eq!(mimeparser.user_avatar, None);
|
||||
@@ -2394,7 +2425,7 @@ mod tests {
|
||||
let t = TestContext::new_alice().await;
|
||||
|
||||
let raw = include_bytes!("../test-data/message/videochat_invitation.eml");
|
||||
let mimeparser = MimeMessage::from_bytes(&t, &raw[..]).await.unwrap();
|
||||
let mimeparser = MimeMessage::from_bytes(&t, &raw[..], None).await.unwrap();
|
||||
assert_eq!(mimeparser.parts.len(), 1);
|
||||
assert_eq!(mimeparser.parts[0].typ, Viewtype::VideochatInvitation);
|
||||
assert_eq!(
|
||||
@@ -2441,7 +2472,7 @@ Content-Disposition: attachment; filename=\"message.kml\"\n\
|
||||
--==break==--\n\
|
||||
;";
|
||||
|
||||
let mimeparser = MimeMessage::from_bytes(&context.ctx, &raw[..])
|
||||
let mimeparser = MimeMessage::from_bytes(&context.ctx, &raw[..], None)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
@@ -2490,7 +2521,7 @@ Disposition: manual-action/MDN-sent-automatically; displayed\n\
|
||||
--kJBbU58X1xeWNHgBtTbMk80M5qnV4N--\n\
|
||||
";
|
||||
|
||||
let message = MimeMessage::from_bytes(&context.ctx, &raw[..])
|
||||
let message = MimeMessage::from_bytes(&context.ctx, &raw[..], None)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
@@ -2570,7 +2601,7 @@ Disposition: manual-action/MDN-sent-automatically; displayed\n\
|
||||
--outer--\n\
|
||||
";
|
||||
|
||||
let message = MimeMessage::from_bytes(&context.ctx, &raw[..])
|
||||
let message = MimeMessage::from_bytes(&context.ctx, &raw[..], None)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
@@ -2617,7 +2648,7 @@ Additional-Message-IDs: <foo@example.com> <foo@example.net>\n\
|
||||
--kJBbU58X1xeWNHgBtTbMk80M5qnV4N--\n\
|
||||
";
|
||||
|
||||
let message = MimeMessage::from_bytes(&context.ctx, &raw[..])
|
||||
let message = MimeMessage::from_bytes(&context.ctx, &raw[..], None)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
@@ -2664,7 +2695,7 @@ MDYyMDYxNTE1RTlDOEE4Cj4+CnN0YXJ0eHJlZgo4Mjc4CiUlRU9GCg==
|
||||
------=_Part_25_46172632.1581201680436--
|
||||
"#;
|
||||
|
||||
let message = MimeMessage::from_bytes(&context.ctx, &raw[..])
|
||||
let message = MimeMessage::from_bytes(&context.ctx, &raw[..], None)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
@@ -2708,7 +2739,7 @@ MDYyMDYxNTE1RTlDOEE4Cj4+CnN0YXJ0eHJlZgo4Mjc4CiUlRU9GCg==
|
||||
------=_Part_25_46172632.1581201680436--
|
||||
"#;
|
||||
|
||||
let message = MimeMessage::from_bytes(&t, &raw[..]).await.unwrap();
|
||||
let message = MimeMessage::from_bytes(&t, &raw[..], None).await.unwrap();
|
||||
|
||||
assert_eq!(message.parts.len(), 1);
|
||||
assert_eq!(message.parts[0].typ, Viewtype::File);
|
||||
@@ -2762,7 +2793,7 @@ CWt6wx7fiLp0qS9RrX75g6Gqw7nfCs6EcBERcIPt7DTe8VStJwf3LWqVwxl4gQl46yhfoqwEO+I=
|
||||
----11019878869865180--
|
||||
"#;
|
||||
|
||||
let message = MimeMessage::from_bytes(&context.ctx, &raw[..])
|
||||
let message = MimeMessage::from_bytes(&context.ctx, &raw[..], None)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(message.get_subject(), Some("example".to_string()));
|
||||
@@ -2834,7 +2865,7 @@ CWt6wx7fiLp0qS9RrX75g6Gqw7nfCs6EcBERcIPt7DTe8VStJwf3LWqVwxl4gQl46yhfoqwEO+I=
|
||||
|
||||
--------------779C1631600DF3DB8C02E53A--"#;
|
||||
|
||||
let message = MimeMessage::from_bytes(&context.ctx, &raw[..])
|
||||
let message = MimeMessage::from_bytes(&context.ctx, &raw[..], None)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(message.get_subject(), Some("Test subject".to_string()));
|
||||
@@ -2905,7 +2936,7 @@ CWt6wx7fiLp0qS9RrX75g6Gqw7nfCs6EcBERcIPt7DTe8VStJwf3LWqVwxl4gQl46yhfoqwEO+I=
|
||||
------=_NextPart_000_0003_01D622B3.CA753E60--
|
||||
"##;
|
||||
|
||||
let message = MimeMessage::from_bytes(&context.ctx, &raw[..])
|
||||
let message = MimeMessage::from_bytes(&context.ctx, &raw[..], None)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
@@ -3003,7 +3034,7 @@ From: alice <alice@example.org>
|
||||
Reply
|
||||
"##;
|
||||
|
||||
let message = MimeMessage::from_bytes(&context.ctx, &raw[..])
|
||||
let message = MimeMessage::from_bytes(&context.ctx, &raw[..], None)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
@@ -3035,7 +3066,7 @@ From: alice <alice@example.org>
|
||||
> Just a quote.
|
||||
"##;
|
||||
|
||||
let message = MimeMessage::from_bytes(&context.ctx, &raw[..])
|
||||
let message = MimeMessage::from_bytes(&context.ctx, &raw[..], None)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
@@ -3069,7 +3100,7 @@ On 2020-10-25, Bob wrote:
|
||||
> A quote.
|
||||
"##;
|
||||
|
||||
let message = MimeMessage::from_bytes(&context.ctx, &raw[..])
|
||||
let message = MimeMessage::from_bytes(&context.ctx, &raw[..], None)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(message.get_subject(), Some("Re: top posting".to_string()));
|
||||
@@ -3087,7 +3118,7 @@ On 2020-10-25, Bob wrote:
|
||||
async fn test_attachment_quote() {
|
||||
let context = TestContext::new_alice().await;
|
||||
let raw = include_bytes!("../test-data/message/quote_attach.eml");
|
||||
let mimeparser = MimeMessage::from_bytes(&context.ctx, &raw[..])
|
||||
let mimeparser = MimeMessage::from_bytes(&context.ctx, &raw[..], None)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
@@ -3105,7 +3136,7 @@ On 2020-10-25, Bob wrote:
|
||||
async fn test_quote_div() {
|
||||
let t = TestContext::new_alice().await;
|
||||
let raw = include_bytes!("../test-data/message/gmx-quote.eml");
|
||||
let mimeparser = MimeMessage::from_bytes(&t, raw).await.unwrap();
|
||||
let mimeparser = MimeMessage::from_bytes(&t, raw, None).await.unwrap();
|
||||
assert_eq!(mimeparser.parts[0].msg, "YIPPEEEEEE\n\nMulti-line");
|
||||
assert_eq!(mimeparser.parts[0].param.get(Param::Quote).unwrap(), "Now?");
|
||||
}
|
||||
@@ -3115,7 +3146,7 @@ On 2020-10-25, Bob wrote:
|
||||
// all-inkl.com puts quotes into `<blockquote> </blockquote>`.
|
||||
let t = TestContext::new_alice().await;
|
||||
let raw = include_bytes!("../test-data/message/allinkl-quote.eml");
|
||||
let mimeparser = MimeMessage::from_bytes(&t, raw).await.unwrap();
|
||||
let mimeparser = MimeMessage::from_bytes(&t, raw, None).await.unwrap();
|
||||
assert!(mimeparser.parts[0].msg.starts_with("It's 1.0."));
|
||||
assert_eq!(
|
||||
mimeparser.parts[0].param.get(Param::Quote).unwrap(),
|
||||
@@ -3126,7 +3157,6 @@ On 2020-10-25, Bob wrote:
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_add_subj_to_multimedia_msg() {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.set_config(Config::ShowEmails, Some("2")).await.unwrap();
|
||||
receive_imf(
|
||||
&t.ctx,
|
||||
include_bytes!("../test-data/message/subj_with_multimedia_msg.eml"),
|
||||
@@ -3160,7 +3190,7 @@ On 2020-10-25, Bob wrote:
|
||||
async fn test_mime_modified_plain() {
|
||||
let t = TestContext::new_alice().await;
|
||||
let raw = include_bytes!("../test-data/message/text_plain_unspecified.eml");
|
||||
let mimeparser = MimeMessage::from_bytes(&t.ctx, raw).await.unwrap();
|
||||
let mimeparser = MimeMessage::from_bytes(&t.ctx, raw, None).await.unwrap();
|
||||
assert!(!mimeparser.is_mime_modified);
|
||||
assert_eq!(
|
||||
mimeparser.parts[0].msg,
|
||||
@@ -3172,7 +3202,7 @@ On 2020-10-25, Bob wrote:
|
||||
async fn test_mime_modified_alt_plain_html() {
|
||||
let t = TestContext::new_alice().await;
|
||||
let raw = include_bytes!("../test-data/message/text_alt_plain_html.eml");
|
||||
let mimeparser = MimeMessage::from_bytes(&t.ctx, raw).await.unwrap();
|
||||
let mimeparser = MimeMessage::from_bytes(&t.ctx, raw, None).await.unwrap();
|
||||
assert!(mimeparser.is_mime_modified);
|
||||
assert_eq!(
|
||||
mimeparser.parts[0].msg,
|
||||
@@ -3184,7 +3214,7 @@ On 2020-10-25, Bob wrote:
|
||||
async fn test_mime_modified_alt_plain() {
|
||||
let t = TestContext::new_alice().await;
|
||||
let raw = include_bytes!("../test-data/message/text_alt_plain.eml");
|
||||
let mimeparser = MimeMessage::from_bytes(&t.ctx, raw).await.unwrap();
|
||||
let mimeparser = MimeMessage::from_bytes(&t.ctx, raw, None).await.unwrap();
|
||||
assert!(!mimeparser.is_mime_modified);
|
||||
assert_eq!(
|
||||
mimeparser.parts[0].msg,
|
||||
@@ -3199,7 +3229,7 @@ On 2020-10-25, Bob wrote:
|
||||
async fn test_mime_modified_alt_html() {
|
||||
let t = TestContext::new_alice().await;
|
||||
let raw = include_bytes!("../test-data/message/text_alt_html.eml");
|
||||
let mimeparser = MimeMessage::from_bytes(&t.ctx, raw).await.unwrap();
|
||||
let mimeparser = MimeMessage::from_bytes(&t.ctx, raw, None).await.unwrap();
|
||||
assert!(mimeparser.is_mime_modified);
|
||||
assert_eq!(
|
||||
mimeparser.parts[0].msg,
|
||||
@@ -3211,7 +3241,7 @@ On 2020-10-25, Bob wrote:
|
||||
async fn test_mime_modified_html() {
|
||||
let t = TestContext::new_alice().await;
|
||||
let raw = include_bytes!("../test-data/message/text_html.eml");
|
||||
let mimeparser = MimeMessage::from_bytes(&t.ctx, raw).await.unwrap();
|
||||
let mimeparser = MimeMessage::from_bytes(&t.ctx, raw, None).await.unwrap();
|
||||
assert!(mimeparser.is_mime_modified);
|
||||
assert_eq!(
|
||||
mimeparser.parts[0].msg,
|
||||
@@ -3227,7 +3257,7 @@ On 2020-10-25, Bob wrote:
|
||||
static REPEAT_CNT: usize = 2000; // results in a text of 84k, should be more than DC_DESIRED_TEXT_LEN
|
||||
let long_txt = format!("From: alice@c.de\n\n{}", REPEAT_TXT.repeat(REPEAT_CNT));
|
||||
|
||||
let mimemsg = MimeMessage::from_bytes(&t, long_txt.as_ref())
|
||||
let mimemsg = MimeMessage::from_bytes(&t, long_txt.as_ref(), None)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(long_txt.matches("just repeated").count(), REPEAT_CNT);
|
||||
@@ -3254,7 +3284,7 @@ On 2020-10-25, Bob wrote:
|
||||
MIME-Version: 1.0\n\
|
||||
\n\
|
||||
Does it work with outlook now?\n\
|
||||
")
|
||||
", None)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
@@ -3410,7 +3440,6 @@ Message.
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_ms_exchange_mdn() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.set_config(Config::ShowEmails, Some("2")).await?;
|
||||
|
||||
let original =
|
||||
include_bytes!("../test-data/message/ms_exchange_report_original_message.eml");
|
||||
@@ -3420,7 +3449,7 @@ Message.
|
||||
// 1. Test mimeparser directly
|
||||
let mdn =
|
||||
include_bytes!("../test-data/message/ms_exchange_report_disposition_notification.eml");
|
||||
let mimeparser = MimeMessage::from_bytes(&t.ctx, mdn).await?;
|
||||
let mimeparser = MimeMessage::from_bytes(&t.ctx, mdn, None).await?;
|
||||
assert_eq!(mimeparser.mdn_reports.len(), 1);
|
||||
assert_eq!(
|
||||
mimeparser.mdn_reports[0].original_message_id.as_deref(),
|
||||
@@ -3446,6 +3475,7 @@ Message.
|
||||
let mime_message = MimeMessage::from_bytes(
|
||||
&alice,
|
||||
include_bytes!("../test-data/message/attached-eml.eml"),
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -3488,6 +3518,7 @@ Content-Disposition: reaction\n\
|
||||
\n\
|
||||
\u{1F44D}"
|
||||
.as_bytes(),
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
//! Handle plain text together with some attributes.
|
||||
|
||||
#![allow(missing_docs)]
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use crate::simplify::split_lines;
|
||||
|
||||
/// Plaintext message body together with format=flowed attributes.
|
||||
#[derive(Debug)]
|
||||
pub struct PlainText {
|
||||
/// The text itself.
|
||||
pub text: String,
|
||||
|
||||
/// Text may "flowed" as defined in [RFC 2646](https://tools.ietf.org/html/rfc2646).
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
//! [Provider database](https://providers.delta.chat/) module.
|
||||
|
||||
#![allow(missing_docs)]
|
||||
|
||||
mod data;
|
||||
|
||||
use anyhow::Result;
|
||||
@@ -12,77 +10,133 @@ use crate::config::Config;
|
||||
use crate::context::Context;
|
||||
use crate::provider::data::{PROVIDER_DATA, PROVIDER_IDS, PROVIDER_UPDATED};
|
||||
|
||||
/// Provider status according to manual testing.
|
||||
#[derive(Debug, Display, Copy, Clone, PartialEq, Eq, FromPrimitive, ToPrimitive)]
|
||||
#[repr(u8)]
|
||||
pub enum Status {
|
||||
/// Provider is known to be working with Delta Chat.
|
||||
Ok = 1,
|
||||
|
||||
/// Provider works with Delta Chat, but requires some preparation,
|
||||
/// such as changing the settings in the web interface.
|
||||
Preparation = 2,
|
||||
|
||||
/// Provider is known not to work with Delta Chat.
|
||||
Broken = 3,
|
||||
}
|
||||
|
||||
/// Server protocol.
|
||||
#[derive(Debug, Display, PartialEq, Eq, Copy, Clone, FromPrimitive, ToPrimitive)]
|
||||
#[repr(u8)]
|
||||
pub enum Protocol {
|
||||
/// SMTP protocol.
|
||||
Smtp = 1,
|
||||
|
||||
/// IMAP protocol.
|
||||
Imap = 2,
|
||||
}
|
||||
|
||||
#[derive(Debug, Display, PartialEq, Eq, Copy, Clone, FromPrimitive, ToPrimitive)]
|
||||
/// Socket security.
|
||||
#[derive(Debug, Default, Display, PartialEq, Eq, Copy, Clone, FromPrimitive, ToPrimitive)]
|
||||
#[repr(u8)]
|
||||
pub enum Socket {
|
||||
/// Unspecified socket security, select automatically.
|
||||
#[default]
|
||||
Automatic = 0,
|
||||
|
||||
/// TLS connection.
|
||||
Ssl = 1,
|
||||
|
||||
/// STARTTLS connection.
|
||||
Starttls = 2,
|
||||
|
||||
/// No TLS, plaintext connection.
|
||||
Plain = 3,
|
||||
}
|
||||
|
||||
impl Default for Socket {
|
||||
fn default() -> Self {
|
||||
Socket::Automatic
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern used to construct login usernames from email addresses.
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
#[repr(u8)]
|
||||
pub enum UsernamePattern {
|
||||
/// Whole email is used as username.
|
||||
Email = 1,
|
||||
|
||||
/// Part of address before `@` is used as username.
|
||||
Emaillocalpart = 2,
|
||||
}
|
||||
|
||||
/// Type of OAuth 2 authorization.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[repr(u8)]
|
||||
pub enum Oauth2Authorizer {
|
||||
/// Yandex.
|
||||
Yandex = 1,
|
||||
|
||||
/// Gmail.
|
||||
Gmail = 2,
|
||||
}
|
||||
|
||||
/// Email server endpoint.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Server {
|
||||
/// Server protocol, e.g. SMTP or IMAP.
|
||||
pub protocol: Protocol,
|
||||
|
||||
/// Port security, e.g. TLS or STARTTLS.
|
||||
pub socket: Socket,
|
||||
|
||||
/// Server host.
|
||||
pub hostname: &'static str,
|
||||
|
||||
/// Server port.
|
||||
pub port: u16,
|
||||
|
||||
/// Pattern used to construct login usernames from email addresses.
|
||||
pub username_pattern: UsernamePattern,
|
||||
}
|
||||
|
||||
/// Pair of key and value for default configuration.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct ConfigDefault {
|
||||
/// Configuration variable name.
|
||||
pub key: Config,
|
||||
|
||||
/// Configuration variable value.
|
||||
pub value: &'static str,
|
||||
}
|
||||
|
||||
/// Provider database entry.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct Provider {
|
||||
/// Unique ID, corresponding to provider database filename.
|
||||
pub id: &'static str,
|
||||
|
||||
/// Provider status according to manual testing.
|
||||
pub status: Status,
|
||||
|
||||
/// Hint to be shown to the user on the login screen.
|
||||
pub before_login_hint: &'static str,
|
||||
|
||||
/// Hint to be added to the device chat after provider configuration.
|
||||
pub after_login_hint: &'static str,
|
||||
|
||||
/// URL of the page with provider overview.
|
||||
pub overview_page: &'static str,
|
||||
|
||||
/// List of provider servers.
|
||||
pub server: Vec<Server>,
|
||||
|
||||
/// Default configuration values to set when provider is configured.
|
||||
pub config_defaults: Option<Vec<ConfigDefault>>,
|
||||
|
||||
/// True if provider is known to use use proper,
|
||||
/// not self-signed certificates.
|
||||
pub strict_tls: bool,
|
||||
|
||||
/// Maximum number of recipients the provider allows to send a single email to.
|
||||
pub max_smtp_rcpt_to: Option<u16>,
|
||||
|
||||
/// Type of OAuth 2 authorization if provider supports it.
|
||||
pub oauth2_authorizer: Option<Oauth2Authorizer>,
|
||||
}
|
||||
|
||||
@@ -175,8 +229,7 @@ pub async fn get_provider_by_mx(context: &Context, domain: &str) -> Option<&'sta
|
||||
None
|
||||
}
|
||||
|
||||
// TODO: uncomment when clippy starts complaining about it
|
||||
//#[allow(clippy::manual_map)] // Can't use .map() because the lifetime is not propagated
|
||||
/// Returns a provider with the given ID from the database.
|
||||
pub fn get_provider_by_id(id: &str) -> Option<&'static Provider> {
|
||||
if let Some(provider) = PROVIDER_IDS.get(id) {
|
||||
Some(provider)
|
||||
@@ -185,7 +238,7 @@ pub fn get_provider_by_id(id: &str) -> Option<&'static Provider> {
|
||||
}
|
||||
}
|
||||
|
||||
// returns update timestamp in seconds, UTC, compatible for comparison with time() and database times
|
||||
/// Returns update timestamp as a unix timestamp compatible for comparison with time() and database times.
|
||||
pub fn get_provider_update_timestamp() -> i64 {
|
||||
NaiveDateTime::new(*PROVIDER_UPDATED, NaiveTime::from_hms_opt(0, 0, 0).unwrap())
|
||||
.timestamp_millis()
|
||||
|
||||
@@ -287,7 +287,6 @@ pub async fn get_msg_reactions(context: &Context, msg_id: MsgId) -> Result<React
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::chat::get_chat_msgs;
|
||||
use crate::config::Config;
|
||||
use crate::constants::DC_CHAT_ID_TRASH;
|
||||
use crate::contact::{Contact, ContactAddress, Origin};
|
||||
use crate::download::DownloadState;
|
||||
@@ -343,7 +342,6 @@ mod tests {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_receive_reaction() -> Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
alice.set_config(Config::ShowEmails, Some("2")).await?;
|
||||
|
||||
// Alice receives BCC-self copy of a message sent to Bob.
|
||||
receive_imf(
|
||||
|
||||
@@ -103,35 +103,35 @@ pub(crate) async fn receive_imf_inner(
|
||||
);
|
||||
}
|
||||
|
||||
let mut mime_parser =
|
||||
match MimeMessage::from_bytes_with_partial(context, imf_raw, is_partial_download).await {
|
||||
Err(err) => {
|
||||
warn!(context, "receive_imf: can't parse MIME: {:#}", err);
|
||||
let msg_ids;
|
||||
if !rfc724_mid.starts_with(GENERATED_PREFIX) {
|
||||
let row_id = context
|
||||
.sql
|
||||
.execute(
|
||||
"INSERT INTO msgs(rfc724_mid, chat_id) VALUES (?,?)",
|
||||
paramsv![rfc724_mid, DC_CHAT_ID_TRASH],
|
||||
)
|
||||
.await?;
|
||||
msg_ids = vec![MsgId::new(u32::try_from(row_id)?)];
|
||||
} else {
|
||||
return Ok(None);
|
||||
// We don't have an rfc724_mid, there's no point in adding a trash entry
|
||||
}
|
||||
|
||||
return Ok(Some(ReceivedMsg {
|
||||
chat_id: DC_CHAT_ID_TRASH,
|
||||
state: MessageState::Undefined,
|
||||
sort_timestamp: 0,
|
||||
msg_ids,
|
||||
needs_delete_job: false,
|
||||
}));
|
||||
let mut mime_parser = match MimeMessage::from_bytes(context, imf_raw, is_partial_download).await
|
||||
{
|
||||
Err(err) => {
|
||||
warn!(context, "receive_imf: can't parse MIME: {:#}", err);
|
||||
let msg_ids;
|
||||
if !rfc724_mid.starts_with(GENERATED_PREFIX) {
|
||||
let row_id = context
|
||||
.sql
|
||||
.execute(
|
||||
"INSERT INTO msgs(rfc724_mid, chat_id) VALUES (?,?)",
|
||||
paramsv![rfc724_mid, DC_CHAT_ID_TRASH],
|
||||
)
|
||||
.await?;
|
||||
msg_ids = vec![MsgId::new(u32::try_from(row_id)?)];
|
||||
} else {
|
||||
return Ok(None);
|
||||
// We don't have an rfc724_mid, there's no point in adding a trash entry
|
||||
}
|
||||
Ok(mime_parser) => mime_parser,
|
||||
};
|
||||
|
||||
return Ok(Some(ReceivedMsg {
|
||||
chat_id: DC_CHAT_ID_TRASH,
|
||||
state: MessageState::Undefined,
|
||||
sort_timestamp: 0,
|
||||
msg_ids,
|
||||
needs_delete_job: false,
|
||||
}));
|
||||
}
|
||||
Ok(mime_parser) => mime_parser,
|
||||
};
|
||||
|
||||
// we can not add even an empty record if we have no info whatsoever
|
||||
if !mime_parser.has_headers() {
|
||||
|
||||
@@ -5,7 +5,7 @@ use crate::aheader::EncryptPreference;
|
||||
use crate::chat::get_chat_contacts;
|
||||
use crate::chat::{get_chat_msgs, ChatItem, ChatVisibility};
|
||||
use crate::chatlist::Chatlist;
|
||||
use crate::constants::DC_GCL_NO_SPECIALS;
|
||||
use crate::constants::{ShowEmails, DC_GCL_NO_SPECIALS};
|
||||
use crate::imap::prefetch_should_download;
|
||||
use crate::message::Message;
|
||||
use crate::test_utils::{get_chat_msg, TestContext, TestContextManager};
|
||||
@@ -20,7 +20,7 @@ async fn test_grpid_simple() {
|
||||
References: <Gr.HcxyMARjyJy.9-uvzWPTLtV@nauta.cu>\n\
|
||||
\n\
|
||||
hello\x00";
|
||||
let mimeparser = MimeMessage::from_bytes(&context.ctx, &raw[..])
|
||||
let mimeparser = MimeMessage::from_bytes(&context.ctx, &raw[..], None)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(extract_grpid(&mimeparser, HeaderDef::InReplyTo), None);
|
||||
@@ -38,7 +38,7 @@ async fn test_bad_from() {
|
||||
References: <Gr.HcxyMARjyJy.9-uvzWPTLtV@nauta.cu>\n\
|
||||
\n\
|
||||
hello\x00";
|
||||
let mimeparser = MimeMessage::from_bytes_with_partial(&context.ctx, &raw[..], None).await;
|
||||
let mimeparser = MimeMessage::from_bytes(&context.ctx, &raw[..], None).await;
|
||||
assert!(mimeparser.is_err());
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ async fn test_grpid_from_multiple() {
|
||||
References: <qweqweqwe>, <Gr.HcxyMARjyJy.9-uvzWPTLtV@nau.ca>\n\
|
||||
\n\
|
||||
hello\x00";
|
||||
let mimeparser = MimeMessage::from_bytes(&context.ctx, &raw[..])
|
||||
let mimeparser = MimeMessage::from_bytes(&context.ctx, &raw[..], None)
|
||||
.await
|
||||
.unwrap();
|
||||
let grpid = Some("HcxyMARjyJy");
|
||||
@@ -94,7 +94,7 @@ static GRP_MAIL: &[u8] =
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_adhoc_group_show_chats_only() {
|
||||
let t = TestContext::new_alice().await;
|
||||
assert_eq!(t.get_config_int(Config::ShowEmails).await.unwrap(), 0);
|
||||
t.set_config(Config::ShowEmails, Some("0")).await.unwrap();
|
||||
|
||||
let chats = Chatlist::try_load(&t, 0, None, None).await.unwrap();
|
||||
assert_eq!(chats.len(), 0);
|
||||
@@ -175,7 +175,7 @@ async fn test_adhoc_group_show_accepted_contact_accepted() {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_adhoc_group_show_all() {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.set_config(Config::ShowEmails, Some("2")).await.unwrap();
|
||||
assert_eq!(t.get_config_int(Config::ShowEmails).await.unwrap(), 2);
|
||||
receive_imf(&t, GRP_MAIL, false).await.unwrap();
|
||||
|
||||
// adhoc-group with unknown contacts with show_emails=all will show up in a single chat
|
||||
@@ -768,7 +768,6 @@ static GH_MAILINGLIST2: &str =
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_github_mailing_list() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.ctx.set_config(Config::ShowEmails, Some("2")).await?;
|
||||
|
||||
receive_imf(&t.ctx, GH_MAILINGLIST, false).await?;
|
||||
|
||||
@@ -840,10 +839,6 @@ static DC_MAILINGLIST2: &[u8] = b"Received: (Postfix, from userid 1000); Mon, 4
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_classic_mailing_list() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.ctx
|
||||
.set_config(Config::ShowEmails, Some("2"))
|
||||
.await
|
||||
.unwrap();
|
||||
receive_imf(&t.ctx, DC_MAILINGLIST, false).await.unwrap();
|
||||
let chats = Chatlist::try_load(&t.ctx, 0, None, None).await.unwrap();
|
||||
let chat_id = chats.get_chat_id(0).unwrap();
|
||||
@@ -885,7 +880,6 @@ Hello mailinglist!\r\n"
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_other_device_writes_to_mailinglist() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.set_config(Config::ShowEmails, Some("2")).await?;
|
||||
receive_imf(&t, DC_MAILINGLIST, false).await.unwrap();
|
||||
let first_msg = t.get_last_msg().await;
|
||||
let first_chat = Chat::load_from_db(&t, first_msg.chat_id).await?;
|
||||
@@ -935,10 +929,6 @@ async fn test_other_device_writes_to_mailinglist() -> Result<()> {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_block_mailing_list() {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.ctx
|
||||
.set_config(Config::ShowEmails, Some("2"))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
receive_imf(&t.ctx, DC_MAILINGLIST, false).await.unwrap();
|
||||
t.evtracker.wait_next_incoming_message().await;
|
||||
@@ -973,7 +963,6 @@ async fn test_block_mailing_list() {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_mailing_list_decide_block_then_unblock() {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.set_config(Config::ShowEmails, Some("2")).await.unwrap();
|
||||
|
||||
receive_imf(&t, DC_MAILINGLIST, false).await.unwrap();
|
||||
let blocked = Contact::get_all_blocked(&t).await.unwrap();
|
||||
@@ -1004,10 +993,6 @@ async fn test_mailing_list_decide_block_then_unblock() {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_mailing_list_decide_not_now() {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.ctx
|
||||
.set_config(Config::ShowEmails, Some("2"))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
receive_imf(&t.ctx, DC_MAILINGLIST, false).await.unwrap();
|
||||
|
||||
@@ -1035,10 +1020,6 @@ async fn test_mailing_list_decide_not_now() {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_mailing_list_decide_accept() {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.ctx
|
||||
.set_config(Config::ShowEmails, Some("2"))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
receive_imf(&t.ctx, DC_MAILINGLIST, false).await.unwrap();
|
||||
|
||||
@@ -1061,7 +1042,6 @@ async fn test_mailing_list_decide_accept() {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_mailing_list_multiple_names_in_subject() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.set_config(Config::ShowEmails, Some("2")).await?;
|
||||
receive_imf(
|
||||
&t,
|
||||
b"From: Foo Bar <foo@bar.org>\n\
|
||||
@@ -1086,7 +1066,6 @@ async fn test_mailing_list_multiple_names_in_subject() -> Result<()> {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_majordomo_mailing_list() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.set_config(Config::ShowEmails, Some("2")).await.unwrap();
|
||||
|
||||
// test mailing lists not having a `ListId:`-header
|
||||
receive_imf(
|
||||
@@ -1139,7 +1118,6 @@ async fn test_majordomo_mailing_list() -> Result<()> {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_mailchimp_mailing_list() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.set_config(Config::ShowEmails, Some("2")).await.unwrap();
|
||||
|
||||
receive_imf(
|
||||
&t,
|
||||
@@ -1173,7 +1151,6 @@ async fn test_mailchimp_mailing_list() -> Result<()> {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_dhl_mailing_list() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.set_config(Config::ShowEmails, Some("2")).await.unwrap();
|
||||
|
||||
receive_imf(
|
||||
&t,
|
||||
@@ -1202,7 +1179,6 @@ async fn test_dhl_mailing_list() -> Result<()> {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_dpd_mailing_list() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.set_config(Config::ShowEmails, Some("2")).await.unwrap();
|
||||
|
||||
receive_imf(
|
||||
&t,
|
||||
@@ -1231,7 +1207,6 @@ async fn test_dpd_mailing_list() -> Result<()> {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_xt_local_mailing_list() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.set_config(Config::ShowEmails, Some("2")).await?;
|
||||
|
||||
receive_imf(
|
||||
&t,
|
||||
@@ -1265,7 +1240,6 @@ async fn test_xt_local_mailing_list() -> Result<()> {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_xing_mailing_list() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.set_config(Config::ShowEmails, Some("2")).await?;
|
||||
|
||||
receive_imf(
|
||||
&t,
|
||||
@@ -1288,7 +1262,6 @@ async fn test_xing_mailing_list() -> Result<()> {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_ttline_mailing_list() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.set_config(Config::ShowEmails, Some("2")).await?;
|
||||
|
||||
receive_imf(
|
||||
&t,
|
||||
@@ -1309,7 +1282,6 @@ async fn test_ttline_mailing_list() -> Result<()> {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_mailing_list_with_mimepart_footer() {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.set_config(Config::ShowEmails, Some("2")).await.unwrap();
|
||||
|
||||
// the mailing list message contains two top-level texts.
|
||||
// the second text is a footer that is added by some mailing list software
|
||||
@@ -1340,7 +1312,6 @@ async fn test_mailing_list_with_mimepart_footer() {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_mailing_list_with_mimepart_footer_signed() {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.set_config(Config::ShowEmails, Some("2")).await.unwrap();
|
||||
|
||||
receive_imf(
|
||||
&t,
|
||||
@@ -1365,7 +1336,6 @@ async fn test_mailing_list_with_mimepart_footer_signed() {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_apply_mailinglist_changes_assigned_by_reply() {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.set_config(Config::ShowEmails, Some("2")).await.unwrap();
|
||||
|
||||
receive_imf(&t, GH_MAILINGLIST, false).await.unwrap();
|
||||
|
||||
@@ -1461,10 +1431,6 @@ async fn test_dont_show_noreply_in_contacts_list() {
|
||||
|
||||
async fn check_dont_show_in_contacts_list(addr: &str) {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.ctx
|
||||
.set_config(Config::ShowEmails, Some("2"))
|
||||
.await
|
||||
.unwrap();
|
||||
receive_imf(
|
||||
&t,
|
||||
format!(
|
||||
@@ -1534,7 +1500,6 @@ async fn test_pdf_filename_continuation() {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_many_images() {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.set_config(Config::ShowEmails, Some("2")).await.unwrap();
|
||||
|
||||
receive_imf(
|
||||
&t,
|
||||
@@ -1811,10 +1776,6 @@ async fn create_test_alias(chat_request: bool, group_request: bool) -> (TestCont
|
||||
};
|
||||
|
||||
let alice = TestContext::new_alice().await;
|
||||
alice
|
||||
.set_config(Config::ShowEmails, Some("2"))
|
||||
.await
|
||||
.unwrap();
|
||||
receive_imf(&alice, claire_request.as_bytes(), false)
|
||||
.await
|
||||
.unwrap();
|
||||
@@ -1834,10 +1795,6 @@ async fn create_test_alias(chat_request: bool, group_request: bool) -> (TestCont
|
||||
|
||||
let claire = TestContext::new().await;
|
||||
claire.configure_addr("claire@example.org").await;
|
||||
claire
|
||||
.set_config(Config::ShowEmails, Some("2"))
|
||||
.await
|
||||
.unwrap();
|
||||
receive_imf(&claire, claire_request.as_bytes(), false)
|
||||
.await
|
||||
.unwrap();
|
||||
@@ -1949,7 +1906,6 @@ async fn test_alias_answer_from_dc() {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_dont_assign_to_trash_by_parent() {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.set_config(Config::ShowEmails, Some("2")).await.unwrap();
|
||||
println!("\n========= Receive a message ==========");
|
||||
receive_imf(
|
||||
&t,
|
||||
@@ -2025,12 +1981,6 @@ Message content",
|
||||
async fn test_outgoing_classic_mail_creates_chat() {
|
||||
let alice = TestContext::new_alice().await;
|
||||
|
||||
// Alice enables classic emails.
|
||||
alice
|
||||
.set_config(Config::ShowEmails, Some("2"))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Alice downloads outgoing classic email.
|
||||
receive_imf(
|
||||
&alice,
|
||||
@@ -2113,7 +2063,6 @@ Second signature";
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_ignore_footer_status_from_mailinglist() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.set_config(Config::ShowEmails, Some("2")).await?;
|
||||
let bob_id = Contact::add_or_lookup(
|
||||
&t,
|
||||
"",
|
||||
@@ -2192,7 +2141,6 @@ Original signature updated",
|
||||
async fn test_chat_assignment_private_classical_reply() {
|
||||
for outgoing_is_classical in &[true, false] {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.set_config(Config::ShowEmails, Some("2")).await.unwrap();
|
||||
|
||||
receive_imf(
|
||||
&t,
|
||||
@@ -2278,7 +2226,6 @@ async fn test_chat_assignment_private_chat_reply() {
|
||||
&[(true, true), (false, true), (false, false)]
|
||||
{
|
||||
let t = TestContext::new_alice().await;
|
||||
t.set_config(Config::ShowEmails, Some("2")).await.unwrap();
|
||||
|
||||
receive_imf(
|
||||
&t,
|
||||
@@ -2372,7 +2319,6 @@ Sent with my Delta Chat Messenger: https://delta.chat
|
||||
async fn test_chat_assignment_nonprivate_classical_reply() {
|
||||
for outgoing_is_classical in &[true, false] {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.set_config(Config::ShowEmails, Some("2")).await.unwrap();
|
||||
|
||||
receive_imf(
|
||||
&t,
|
||||
@@ -2481,8 +2427,6 @@ Reply to all"#,
|
||||
async fn test_chat_assignment_adhoc() -> Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
let bob = TestContext::new_bob().await;
|
||||
alice.set_config(Config::ShowEmails, Some("2")).await?;
|
||||
bob.set_config(Config::ShowEmails, Some("2")).await?;
|
||||
|
||||
let first_thread_mime = br#"Subject: First thread
|
||||
Message-ID: first@example.org
|
||||
@@ -2588,7 +2532,6 @@ async fn test_read_receipts_dont_create_chats() -> Result<()> {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_gmx_forwarded_msg() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.set_config(Config::ShowEmails, Some("2")).await?;
|
||||
|
||||
receive_imf(
|
||||
&t,
|
||||
@@ -2633,7 +2576,6 @@ async fn test_incoming_contact_request() -> Result<()> {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_get_parent_message() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.set_config(Config::ShowEmails, Some("2")).await?;
|
||||
|
||||
let mime = br#"Subject: First
|
||||
Message-ID: first@example.net
|
||||
@@ -2672,7 +2614,7 @@ References: <second@example.net> <nonexistent@example.net> <first@example.net>
|
||||
Content-Type: text/plain; charset=utf-8; format=flowed; delsp=no
|
||||
|
||||
Message with references."#;
|
||||
let mime_parser = MimeMessage::from_bytes(&t, &mime[..]).await?;
|
||||
let mime_parser = MimeMessage::from_bytes(&t, &mime[..], None).await?;
|
||||
|
||||
let parent = get_parent_message(&t, &mime_parser).await?.unwrap();
|
||||
assert_eq!(parent.id, first.id);
|
||||
@@ -2707,8 +2649,6 @@ async fn test_rfc1847_encapsulation() -> Result<()> {
|
||||
bob.recv_msg(&first_msg).await;
|
||||
message::delete_msgs(&bob, &[bob.get_last_msg().await.id]).await?;
|
||||
|
||||
bob.set_config(Config::ShowEmails, Some("2")).await?;
|
||||
|
||||
// Alice sends a message to Bob using Thunderbird.
|
||||
let raw = include_bytes!("../../test-data/message/rfc1847_encapsulation.eml");
|
||||
receive_imf(&bob, raw, false).await?;
|
||||
@@ -2734,7 +2674,6 @@ async fn test_invalid_to_address() -> Result<()> {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_reply_from_different_addr() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.set_config(Config::ShowEmails, Some("2")).await?;
|
||||
|
||||
// Alice creates a 2-person-group with Bob
|
||||
receive_imf(
|
||||
@@ -3042,7 +2981,6 @@ async fn test_no_private_reply_to_blocked_account() -> Result<()> {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_thunderbird_autocrypt() -> Result<()> {
|
||||
let t = TestContext::new_bob().await;
|
||||
t.set_config(Config::ShowEmails, Some("2")).await?;
|
||||
|
||||
let raw = include_bytes!("../../test-data/message/thunderbird_with_autocrypt.eml");
|
||||
receive_imf(&t, raw, false).await?;
|
||||
@@ -3058,7 +2996,6 @@ async fn test_thunderbird_autocrypt() -> Result<()> {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_thunderbird_autocrypt_unencrypted() -> Result<()> {
|
||||
let t = TestContext::new_bob().await;
|
||||
t.set_config(Config::ShowEmails, Some("2")).await?;
|
||||
|
||||
let raw = include_bytes!("../../test-data/message/thunderbird_with_autocrypt_unencrypted.eml");
|
||||
receive_imf(&t, raw, false).await?;
|
||||
@@ -3084,7 +3021,6 @@ async fn test_thunderbird_autocrypt_unencrypted() -> Result<()> {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_thunderbird_unsigned() -> Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
alice.set_config(Config::ShowEmails, Some("2")).await?;
|
||||
|
||||
// Alice receives an unsigned message from Bob.
|
||||
let raw = include_bytes!("../../test-data/message/thunderbird_encrypted_unsigned.eml");
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
#![allow(missing_docs)]
|
||||
|
||||
use core::fmt;
|
||||
use std::{ops::Deref, sync::Arc};
|
||||
|
||||
@@ -29,9 +27,10 @@ pub enum Connectivity {
|
||||
// the top) take priority. This means that e.g. if any folder has an error - usually
|
||||
// because there is no internet connection - the connectivity for the whole
|
||||
// account will be `Notconnected`.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, EnumProperty, PartialOrd)]
|
||||
#[derive(Debug, Default, Clone, PartialEq, Eq, EnumProperty, PartialOrd)]
|
||||
enum DetailedConnectivity {
|
||||
Error(String),
|
||||
#[default]
|
||||
Uninitialized,
|
||||
Connecting,
|
||||
Working,
|
||||
@@ -42,12 +41,6 @@ enum DetailedConnectivity {
|
||||
NotConfigured,
|
||||
}
|
||||
|
||||
impl Default for DetailedConnectivity {
|
||||
fn default() -> Self {
|
||||
DetailedConnectivity::Uninitialized
|
||||
}
|
||||
}
|
||||
|
||||
impl DetailedConnectivity {
|
||||
fn to_basic(&self) -> Option<Connectivity> {
|
||||
match self {
|
||||
@@ -538,6 +531,7 @@ impl Context {
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
/// Returns true if all background work is done.
|
||||
pub async fn all_work_done(&self) -> bool {
|
||||
let lock = self.scheduler.read().await;
|
||||
let stores: Vec<_> = match &*lock {
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
//! Verified contact protocol implementation as [specified by countermitm project](https://countermitm.readthedocs.io/en/stable/new.html#setup-contact-protocol).
|
||||
|
||||
#![allow(missing_docs)]
|
||||
|
||||
use std::convert::TryFrom;
|
||||
|
||||
use anyhow::{bail, Context as _, Error, Result};
|
||||
@@ -35,6 +33,7 @@ use qrinvite::QrInvite;
|
||||
|
||||
use crate::token::Namespace;
|
||||
|
||||
/// Set of characters to percent-encode in email addresses and names.
|
||||
pub const NON_ALPHANUMERIC_WITHOUT_DOT: &AsciiSet = &NON_ALPHANUMERIC.remove(b'.');
|
||||
|
||||
macro_rules! inviter_progress {
|
||||
@@ -1387,7 +1386,6 @@ mod tests {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_adhoc_group_no_qr() -> Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
alice.set_config(Config::ShowEmails, Some("2")).await?;
|
||||
|
||||
let mime = br#"Subject: First thread
|
||||
Message-ID: first@example.org
|
||||
|
||||
@@ -236,7 +236,7 @@ impl BobState {
|
||||
/// stage is returned. Once [`BobHandshakeStage::Completed`] or
|
||||
/// [`BobHandshakeStage::Terminated`] are reached this [`BobState`] should be destroyed,
|
||||
/// further calling it will just result in the messages being unused by this handshake.
|
||||
pub async fn handle_message(
|
||||
pub(crate) async fn handle_message(
|
||||
&mut self,
|
||||
context: &Context,
|
||||
mime_message: &MimeMessage,
|
||||
|
||||
@@ -683,6 +683,13 @@ CREATE INDEX smtp_messageid ON imap(rfc724_mid);
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
if dbversion < 98 {
|
||||
if exists_before_update && sql.get_raw_config_int("show_emails").await?.is_none() {
|
||||
sql.set_raw_config_int("show_emails", ShowEmails::Off as i32)
|
||||
.await?;
|
||||
}
|
||||
sql.set_db_version(98).await?;
|
||||
}
|
||||
|
||||
let new_version = sql
|
||||
.get_raw_config_int(VERSION_CFG)
|
||||
|
||||
@@ -446,8 +446,8 @@ impl TestContext {
|
||||
/// peerstates will be updated. Later receiving the message using [recv_msg] is
|
||||
/// unlikely to be affected as the peerstate would be processed again in exactly the
|
||||
/// same way.
|
||||
pub async fn parse_msg(&self, msg: &SentMessage<'_>) -> MimeMessage {
|
||||
MimeMessage::from_bytes(&self.ctx, msg.payload().as_bytes())
|
||||
pub(crate) async fn parse_msg(&self, msg: &SentMessage<'_>) -> MimeMessage {
|
||||
MimeMessage::from_bytes(&self.ctx, msg.payload().as_bytes(), None)
|
||||
.await
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
@@ -13,21 +13,16 @@ use crate::tools::{create_id, time};
|
||||
|
||||
/// Token namespace
|
||||
#[derive(
|
||||
Debug, Display, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive, ToSql, FromSql,
|
||||
Debug, Default, Display, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive, ToSql, FromSql,
|
||||
)]
|
||||
#[repr(u32)]
|
||||
pub enum Namespace {
|
||||
#[default]
|
||||
Unknown = 0,
|
||||
Auth = 110,
|
||||
InviteNumber = 100,
|
||||
}
|
||||
|
||||
impl Default for Namespace {
|
||||
fn default() -> Self {
|
||||
Namespace::Unknown
|
||||
}
|
||||
}
|
||||
|
||||
/// Saves a token to the database.
|
||||
pub async fn save(
|
||||
context: &Context,
|
||||
|
||||
@@ -689,9 +689,7 @@ mod tests {
|
||||
#![allow(clippy::indexing_slicing)]
|
||||
|
||||
use super::*;
|
||||
use crate::{
|
||||
config::Config, message::get_msg_info, receive_imf::receive_imf, test_utils::TestContext,
|
||||
};
|
||||
use crate::{message::get_msg_info, receive_imf::receive_imf, test_utils::TestContext};
|
||||
|
||||
#[test]
|
||||
fn test_parse_receive_headers() {
|
||||
@@ -762,7 +760,6 @@ DKIM Results: Passed=true, Works=true, Allow_Keychange=true";
|
||||
|
||||
async fn check_parse_receive_headers_integration(raw: &[u8], expected: &str) {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.set_config(Config::ShowEmails, Some("2")).await.unwrap();
|
||||
receive_imf(&t, raw, false).await.unwrap();
|
||||
let msg = t.get_last_msg().await;
|
||||
let msg_info = get_msg_info(&t, msg.id).await.unwrap();
|
||||
|
||||
Reference in New Issue
Block a user