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:
@@ -427,7 +427,7 @@ impl Config {
|
||||
Ok(cfg)
|
||||
}
|
||||
|
||||
/// Removes an existing acccount entirely.
|
||||
/// Removes an existing account entirely.
|
||||
pub async fn remove_account(&mut self, id: u32) -> Result<()> {
|
||||
{
|
||||
if let Some(idx) = self.inner.accounts.iter().position(|e| e.id == id) {
|
||||
@@ -487,7 +487,7 @@ struct AccountConfig {
|
||||
}
|
||||
|
||||
impl AccountConfig {
|
||||
/// Get the canoncial dbfile name for this configuration.
|
||||
/// Get the canonical dbfile name for this configuration.
|
||||
pub fn dbfile(&self, accounts_dir: &Path) -> std::path::PathBuf {
|
||||
accounts_dir.join(&self.dir).join(DB_NAME)
|
||||
}
|
||||
|
||||
@@ -235,7 +235,7 @@ mod tests {
|
||||
assert!(Aheader::from_str("foo").is_err());
|
||||
assert!(Aheader::from_str("\n\n\n").is_err());
|
||||
assert!(Aheader::from_str(" ;;").is_err());
|
||||
assert!(Aheader::from_str("addr=a@t.de; unknwon=1; keydata=jau").is_err());
|
||||
assert!(Aheader::from_str("addr=a@t.de; unknown=1; keydata=jau").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
15
src/chat.rs
15
src/chat.rs
@@ -46,7 +46,7 @@ use crate::{location, sql};
|
||||
pub enum ChatItem {
|
||||
/// Chat message stored in the database.
|
||||
Message {
|
||||
/// Database ID of the messsage.
|
||||
/// Database ID of the message.
|
||||
msg_id: MsgId,
|
||||
},
|
||||
|
||||
@@ -1206,7 +1206,10 @@ impl Chat {
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
error!(context, "faild to load contacts for {}: {:#}", chat.id, err);
|
||||
error!(
|
||||
context,
|
||||
"failed to load contacts for {}: {:#}", chat.id, err
|
||||
);
|
||||
}
|
||||
}
|
||||
chat.name = chat_name;
|
||||
@@ -2147,7 +2150,7 @@ pub async fn is_contact_in_chat(
|
||||
|
||||
/// Sends a message object to a chat.
|
||||
///
|
||||
/// Sends the event #DC_EVENT_MSGS_CHANGED on succcess.
|
||||
/// Sends the event #DC_EVENT_MSGS_CHANGED on success.
|
||||
/// However, this does not imply, the message really reached the recipient -
|
||||
/// sending may be delayed eg. due to network problems. However, from your
|
||||
/// view, you're done with the message. Sooner or later it will find its way.
|
||||
@@ -2511,7 +2514,7 @@ pub async fn get_chat_msgs_ex(
|
||||
context
|
||||
.sql
|
||||
.query_map(
|
||||
// GLOB is used here instead of LIKE becase it is case-sensitive
|
||||
// GLOB is used here instead of LIKE because it is case-sensitive
|
||||
"SELECT m.id AS id, m.timestamp AS timestamp, m.param AS param, m.from_id AS from_id, m.to_id AS to_id
|
||||
FROM msgs m
|
||||
WHERE m.chat_id=?
|
||||
@@ -3662,7 +3665,7 @@ pub async fn was_device_msg_ever_added(context: &Context, label: &str) -> Result
|
||||
// - deletion in `msgs` with `ContactId::DEVICE` makes sure,
|
||||
// no wrong information are shown in the device chat
|
||||
// - deletion in `devmsglabels` makes sure,
|
||||
// deleted messages are resetted and useful messages can be added again
|
||||
// deleted messages are reset and useful messages can be added again
|
||||
// - we reset the config-option `QuotaExceeding`
|
||||
// that is used as a helper to drive the corresponding device message.
|
||||
pub(crate) async fn delete_and_reset_all_device_msgs(context: &Context) -> Result<()> {
|
||||
@@ -4431,7 +4434,7 @@ mod tests {
|
||||
.unwrap();
|
||||
assert!(msg_id2.is_unset());
|
||||
|
||||
// ... unless everything is deleted and resetted - as needed eg. on device switch
|
||||
// ... unless everything is deleted and reset - as needed eg. on device switch
|
||||
delete_and_reset_all_device_msgs(&t).await.unwrap();
|
||||
assert!(!was_device_msg_ever_added(&t, "some-label").await.unwrap());
|
||||
let msg_id3 = add_device_msg(&t, Some("some-label"), Some(&mut msg))
|
||||
|
||||
@@ -246,7 +246,7 @@ pub enum Config {
|
||||
Configured,
|
||||
|
||||
/// All secondary self addresses separated by spaces
|
||||
/// (`addr1@example.org addr2@exapmle.org addr3@example.org`)
|
||||
/// (`addr1@example.org addr2@example.org addr3@example.org`)
|
||||
SecondaryAddrs,
|
||||
|
||||
/// Read-only core version string.
|
||||
|
||||
@@ -4,7 +4,7 @@ use crate::provider::{Protocol, Socket};
|
||||
|
||||
/// Set of variable parameters to try during configuration.
|
||||
///
|
||||
/// Can be loaded from offline provider database, online configuraiton
|
||||
/// Can be loaded from offline provider database, online configuration
|
||||
/// or derived from user entered parameters.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub(crate) struct ServerParams {
|
||||
|
||||
@@ -103,7 +103,7 @@ pub(crate) const DC_RESEND_USER_AVATAR_DAYS: i64 = 14;
|
||||
// warn about an outdated app after a given number of days.
|
||||
// as we use the "provider-db generation date" as reference (that might not be updated very often)
|
||||
// and as not all system get speedy updates,
|
||||
// do not use too small value that will annoy users checking for nonexistant updates.
|
||||
// do not use too small value that will annoy users checking for nonexistent updates.
|
||||
pub(crate) const DC_OUTDATED_WARNING_DAYS: i64 = 365;
|
||||
|
||||
/// messages that should be deleted get this chat_id; the messages are deleted from the working thread later then. This is also needed as rfc724_mid should be preset as long as the message is not deleted on the server (otherwise it is downloaded again)
|
||||
|
||||
@@ -115,7 +115,7 @@ impl ContextBuilder {
|
||||
/// Sets the event channel for this [`Context`].
|
||||
///
|
||||
/// Mostly useful when using multiple [`Context`]s, this allows creating one [`Events`]
|
||||
/// channel and passing it to all [`Context`]s so all events are recieved on the same
|
||||
/// channel and passing it to all [`Context`]s so all events are received on the same
|
||||
/// channel.
|
||||
///
|
||||
/// Note that the [account manager](crate::accounts::Accounts) is designed to handle the
|
||||
@@ -201,7 +201,7 @@ pub struct InnerContext {
|
||||
pub(crate) generating_key_mutex: Mutex<()>,
|
||||
/// Mutex to enforce only a single running oauth2 is running.
|
||||
pub(crate) oauth2_mutex: Mutex<()>,
|
||||
/// Mutex to prevent a race condition when a "your pw is wrong" warning is sent, resulting in multiple messeges being sent.
|
||||
/// Mutex to prevent a race condition when a "your pw is wrong" warning is sent, resulting in multiple messages being sent.
|
||||
pub(crate) wrong_pw_warning_mutex: Mutex<()>,
|
||||
pub(crate) translated_stockstrings: StockStrings,
|
||||
pub(crate) events: Events,
|
||||
@@ -239,7 +239,7 @@ pub struct InnerContext {
|
||||
/// `last_error` should be used to avoid races with the event thread.
|
||||
pub(crate) last_error: std::sync::RwLock<String>,
|
||||
|
||||
/// If debug logging is enabled, this contains all neccesary information
|
||||
/// If debug logging is enabled, this contains all necessary information
|
||||
pub(crate) debug_logging: RwLock<Option<DebugLogging>>,
|
||||
|
||||
/// QR code for currently running [`BackupProvider`].
|
||||
@@ -255,7 +255,7 @@ pub struct InnerContext {
|
||||
pub(crate) struct DebugLogging {
|
||||
/// The message containing the logging xdc
|
||||
pub(crate) msg_id: MsgId,
|
||||
/// Handle to the background task responisble for sending
|
||||
/// Handle to the background task responsible for sending
|
||||
pub(crate) loop_handle: task::JoinHandle<()>,
|
||||
/// Channel that log events should be send to
|
||||
/// A background loop will receive and handle them
|
||||
|
||||
@@ -22,7 +22,7 @@ use crate::{job_try, stock_str, EventType};
|
||||
/// need to be downloaded completely to handle them correctly,
|
||||
/// eg. to assign them to the correct chat.
|
||||
/// As these messages are typically small,
|
||||
/// they're catched by `MIN_DOWNLOAD_LIMIT`.
|
||||
/// they're caught by `MIN_DOWNLOAD_LIMIT`.
|
||||
const MIN_DOWNLOAD_LIMIT: u32 = 32768;
|
||||
|
||||
/// If a message is downloaded only partially
|
||||
|
||||
@@ -1273,7 +1273,7 @@ mod tests {
|
||||
// protection.
|
||||
//
|
||||
// Previously Delta Chat fallen back to using <first@example.com> in this case and
|
||||
// compared received timer value to the timer value of the <first@examle.com>. Because
|
||||
// compared received timer value to the timer value of the <first@example.com>. Because
|
||||
// their timer values are the same ("disabled"), Delta Chat assumed that the timer was not
|
||||
// changed explicitly and the change should be ignored.
|
||||
//
|
||||
|
||||
@@ -70,7 +70,7 @@ impl Events {
|
||||
pub struct EventEmitter(Receiver<Event>);
|
||||
|
||||
impl EventEmitter {
|
||||
/// Async recv of an event. Return `None` if the `Sender` has been droped.
|
||||
/// Async recv of an event. Return `None` if the `Sender` has been dropped.
|
||||
pub async fn recv(&self) -> Option<Event> {
|
||||
self.0.recv().await.ok()
|
||||
}
|
||||
@@ -89,7 +89,7 @@ impl futures::stream::Stream for EventEmitter {
|
||||
|
||||
/// The event emitted by a [`Context`] from an [`EventEmitter`].
|
||||
///
|
||||
/// Events are documented on the C/FFI API in `deltachat.h` as `DC_EVENT_*` contants. The
|
||||
/// Events are documented on the C/FFI API in `deltachat.h` as `DC_EVENT_*` constants. The
|
||||
/// context emits them in relation to various operations happening, a lot of these are again
|
||||
/// documented in `deltachat.h`.
|
||||
///
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
/// Fuzzing target for simplify().
|
||||
///
|
||||
/// Calls simplify() and panics if simplify() panics.
|
||||
/// Does not return any vaule to avoid exposing internal crate types.
|
||||
/// Does not return any value to avoid exposing internal crate types.
|
||||
#[cfg(fuzzing)]
|
||||
pub fn simplify(input: String, is_chat_message: bool) {
|
||||
crate::simplify::simplify(input, is_chat_message);
|
||||
|
||||
@@ -1394,7 +1394,7 @@ impl Imap {
|
||||
// Check if FETCH response is already in `uid_msgs`.
|
||||
let mut fetch_response = uid_msgs.remove(&request_uid);
|
||||
|
||||
// Try to find a requsted UID in returned FETCH responses.
|
||||
// Try to find a requested UID in returned FETCH responses.
|
||||
while fetch_response.is_none() {
|
||||
let next_fetch_response =
|
||||
if let Some(next_fetch_response) = fetch_responses.next().await {
|
||||
@@ -2204,11 +2204,11 @@ async fn mark_seen_by_uid(
|
||||
.with_context(|| format!("failed to start ephemeral timer for message {msg_id}"))?;
|
||||
Ok(Some(chat_id))
|
||||
} else {
|
||||
// Message state has not chnaged.
|
||||
// Message state has not changed.
|
||||
Ok(None)
|
||||
}
|
||||
} else {
|
||||
// There is no message is `msgs` table matchng the given UID.
|
||||
// There is no message is `msgs` table matching the given UID.
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ pub use transfer::{get_backup, BackupProvider};
|
||||
|
||||
// Name of the database file in the backup.
|
||||
const DBFILE_BACKUP_NAME: &str = "dc_database_backup.sqlite";
|
||||
const BLOBS_BACKUP_NAME: &str = "blobs_backup";
|
||||
pub(crate) const BLOBS_BACKUP_NAME: &str = "blobs_backup";
|
||||
|
||||
/// Import/export command.
|
||||
#[derive(Debug, Display, Copy, Clone, PartialEq, Eq, FromPrimitive, ToPrimitive)]
|
||||
@@ -253,7 +253,7 @@ async fn maybe_add_bcc_self_device_msg(context: &Context) -> Result<()> {
|
||||
// TODO: define this as a stockstring once the wording is settled.
|
||||
msg.text = Some(
|
||||
"It seems you are using multiple devices with Delta Chat. Great!\n\n\
|
||||
If you also want to synchronize outgoing messages accross all devices, \
|
||||
If you also want to synchronize outgoing messages across all devices, \
|
||||
go to the settings and enable \"Send copy to self\"."
|
||||
.to_string(),
|
||||
);
|
||||
@@ -799,7 +799,7 @@ mod tests {
|
||||
println!("{}", &msg);
|
||||
// Check some substrings, indicating things got substituted.
|
||||
// In particular note the mixing of `\r\n` and `\n` depending
|
||||
// on who generated the stings.
|
||||
// on who generated the strings.
|
||||
assert!(msg.contains("<title>Autocrypt Setup Message</title"));
|
||||
assert!(msg.contains("<h1>Autocrypt Setup Message</h1>"));
|
||||
assert!(msg.contains("<p>This is the Autocrypt Setup Message used to"));
|
||||
|
||||
@@ -610,7 +610,7 @@ impl Message {
|
||||
// It's a little unfortunate that the UI has to first call `dc_msg_get_override_sender_name` and then if it was `NULL`, call
|
||||
// `dc_contact_get_display_name` but this was the best solution:
|
||||
// - We could load a Contact struct from the db here to call `dc_get_display_name` instead of returning `None`, but then we had a db
|
||||
// call everytime (and this fn is called a lot while the user is scrolling through a group), so performance would be bad
|
||||
// call every time (and this fn is called a lot while the user is scrolling through a group), so performance would be bad
|
||||
// - We could pass both a Contact struct and a Message struct in the FFI, but at least on Android we would need to handle raw
|
||||
// C-data in the Java code (i.e. a `long` storing a C pointer)
|
||||
// - We can't make a param `SenderDisplayname` for messages as sometimes the display name of a contact changes, and we want to show
|
||||
|
||||
@@ -1932,7 +1932,7 @@ mod tests {
|
||||
|
||||
// These two combinations are different: If `message_arrives_inbetween` is true, but
|
||||
// `reply` is false, the core is actually expected to use the subject of the message
|
||||
// that arrived inbetween.
|
||||
// that arrived in between.
|
||||
assert_eq!(
|
||||
"Re: Some other, completely unrelated subject",
|
||||
msg_to_subject_str_inner(imf_raw, false, false, true).await
|
||||
|
||||
@@ -357,7 +357,7 @@ impl MimeMessage {
|
||||
// where this would be useful.
|
||||
warn!(
|
||||
context,
|
||||
"From header in signed part does't match the outer one",
|
||||
"From header in signed part doesn't match the outer one",
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -525,7 +525,7 @@ impl MimeMessage {
|
||||
}
|
||||
}
|
||||
|
||||
/// Squashes mutlipart chat messages with attachment into single-part messages.
|
||||
/// Squashes mutitpart chat messages with attachment into single-part messages.
|
||||
///
|
||||
/// Delta Chat sends attachments, such as images, in two-part messages, with the first message
|
||||
/// containing a description. If such a message is detected, text from the first part can be
|
||||
@@ -855,9 +855,9 @@ impl MimeMessage {
|
||||
let mut any_part_added = false;
|
||||
let mimetype = get_mime_type(mail)?.0;
|
||||
match (mimetype.type_(), mimetype.subtype().as_str()) {
|
||||
/* Most times, mutlipart/alternative contains true alternatives
|
||||
/* Most times, multipart/alternative contains true alternatives
|
||||
as text/plain and text/html. If we find a multipart/mixed
|
||||
inside mutlipart/alternative, we use this (happens eg in
|
||||
inside multipart/alternative, we use this (happens eg in
|
||||
apple mail: "plaintext" as an alternative to "html+PDF attachment") */
|
||||
(mime::MULTIPART, "alternative") => {
|
||||
for cur_data in &mail.subparts {
|
||||
@@ -1682,7 +1682,7 @@ impl MimeMessage {
|
||||
|
||||
/// Parses `Autocrypt-Gossip` headers from the email and applies them to peerstates.
|
||||
/// Params:
|
||||
/// from: The address which sent the message currently beeing parsed
|
||||
/// from: The address which sent the message currently being parsed
|
||||
///
|
||||
/// Returns the set of mail recipient addresses for which valid gossip headers were found.
|
||||
async fn update_gossip_peerstates(
|
||||
|
||||
10
src/param.rs
10
src/param.rs
@@ -23,7 +23,7 @@ pub enum Param {
|
||||
|
||||
/// For messages: This name should be shown instead of contact.get_display_name()
|
||||
/// (used if this is a mailinglist
|
||||
/// or explictly set using set_override_sender_name(), eg. by bots)
|
||||
/// or explicitly set using set_override_sender_name(), eg. by bots)
|
||||
OverrideSenderDisplayname = b'O',
|
||||
|
||||
/// For Messages
|
||||
@@ -129,7 +129,7 @@ pub enum Param {
|
||||
ProfileImage = b'i',
|
||||
|
||||
/// For Chats
|
||||
/// Signals wheter the chat is the `saved messages` chat
|
||||
/// Signals whether the chat is the `saved messages` chat
|
||||
Selftalk = b'K',
|
||||
|
||||
/// For Chats: On sending a new message we set the subject to `Re: <last subject>`.
|
||||
@@ -341,7 +341,7 @@ impl Params {
|
||||
/// returned.
|
||||
///
|
||||
/// Note that in the [ParamsFile::FsPath] case the blob can be
|
||||
/// created without copying if the path already referes to a valid
|
||||
/// created without copying if the path already refers to a valid
|
||||
/// blob. If so a [BlobObject] will be returned regardless of the
|
||||
/// `create` argument.
|
||||
#[allow(clippy::needless_lifetimes)]
|
||||
@@ -389,13 +389,13 @@ impl Params {
|
||||
.map(MsgId::new)
|
||||
}
|
||||
|
||||
/// Set the given paramter to the passed in `i32`.
|
||||
/// Set the given parameter to the passed in `i32`.
|
||||
pub fn set_int(&mut self, key: Param, value: i32) -> &mut Self {
|
||||
self.set(key, format!("{value}"));
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the given paramter to the passed in `i64`.
|
||||
/// Set the given parameter to the passed in `i64`.
|
||||
pub fn set_i64(&mut self, key: Param, value: i64) -> &mut Self {
|
||||
self.set(key, value.to_string());
|
||||
self
|
||||
|
||||
@@ -595,7 +595,7 @@ impl Peerstate {
|
||||
Err(err) => {
|
||||
warn!(
|
||||
context,
|
||||
"New address {:?} is not vaild, not doing AEAP: {:#}.",
|
||||
"New address {:?} is not valid, not doing AEAP: {:#}.",
|
||||
new_addr,
|
||||
err
|
||||
)
|
||||
|
||||
@@ -480,7 +480,7 @@ mod tests {
|
||||
static CTEXT_SIGNED: OnceCell<String> = OnceCell::const_new();
|
||||
static CTEXT_UNSIGNED: OnceCell<String> = OnceCell::const_new();
|
||||
|
||||
/// A cyphertext encrypted to Alice & Bob, signed by Alice.
|
||||
/// A ciphertext encrypted to Alice & Bob, signed by Alice.
|
||||
async fn ctext_signed() -> &'static String {
|
||||
CTEXT_SIGNED
|
||||
.get_or_init(|| async {
|
||||
@@ -495,7 +495,7 @@ mod tests {
|
||||
.await
|
||||
}
|
||||
|
||||
/// A cyphertext encrypted to Alice & Bob, not signed.
|
||||
/// A ciphertext encrypted to Alice & Bob, not signed.
|
||||
async fn ctext_unsigned() -> &'static String {
|
||||
CTEXT_UNSIGNED
|
||||
.get_or_init(|| async {
|
||||
|
||||
@@ -124,7 +124,7 @@ fn inner_generate_secure_join_qr_code(
|
||||
Ok(())
|
||||
})?
|
||||
.build(|w| {
|
||||
// White Background apears like a card
|
||||
// White Background appears like a card
|
||||
w.single("rect", |d| {
|
||||
d.attr("x", card_border_size)?;
|
||||
d.attr("y", card_border_size)?;
|
||||
|
||||
@@ -68,7 +68,7 @@ async fn get_unique_quota_roots_and_usage(
|
||||
.cloned()
|
||||
.context("quota_root should have a quota")?;
|
||||
// replace old quotas, because between fetching quotaroots for folders,
|
||||
// messages could be recieved and so the usage could have been changed
|
||||
// messages could be received and so the usage could have been changed
|
||||
*unique_quota_roots
|
||||
.entry(quota_root_name.clone())
|
||||
.or_insert_with(Vec::new) = quota.resources;
|
||||
|
||||
@@ -30,7 +30,7 @@ use crate::message::{rfc724_mid_exists, Message, MsgId, Viewtype};
|
||||
/// It is guaranteed to have all emojis sorted and deduplicated inside.
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct Reaction {
|
||||
/// Canonical represntation of reaction as a string of space-separated emojis.
|
||||
/// Canonical representation of reaction as a string of space-separated emojis.
|
||||
reaction: String,
|
||||
}
|
||||
|
||||
@@ -195,7 +195,7 @@ pub async fn send_reaction(context: &Context, msg_id: MsgId, reaction: &str) ->
|
||||
reaction_msg.in_reply_to = Some(msg.rfc724_mid);
|
||||
reaction_msg.hidden = true;
|
||||
|
||||
// Send messsage first.
|
||||
// Send message first.
|
||||
let reaction_msg_id = send_msg(context, chat_id, &mut reaction_msg).await?;
|
||||
|
||||
// Only set reaction if we successfully sent the message.
|
||||
|
||||
@@ -150,7 +150,7 @@ pub(crate) async fn receive_imf_inner(
|
||||
if let Some(old_msg_id) = message::rfc724_mid_exists(context, rfc724_mid).await? {
|
||||
let msg = Message::load_from_db(context, old_msg_id).await?;
|
||||
if msg.download_state() != DownloadState::Done && is_partial_download.is_none() {
|
||||
// the mesage was partially downloaded before and is fully downloaded now.
|
||||
// the message was partially downloaded before and is fully downloaded now.
|
||||
info!(
|
||||
context,
|
||||
"Message already partly in DB, replacing by full message."
|
||||
@@ -422,7 +422,7 @@ pub async fn from_field_to_contact_id(
|
||||
}
|
||||
|
||||
/// Creates a `ReceivedMsg` from given parts which might consist of
|
||||
/// mulitple messages (if there are multiple attachments).
|
||||
/// multiple messages (if there are multiple attachments).
|
||||
/// Every entry in `mime_parser.parts` produces a new row in the `msgs` table.
|
||||
#[allow(clippy::too_many_arguments, clippy::cognitive_complexity)]
|
||||
async fn add_parts(
|
||||
@@ -550,7 +550,7 @@ async fn add_parts(
|
||||
}
|
||||
}
|
||||
|
||||
// signals wether the current user is a bot
|
||||
// signals whether the current user is a bot
|
||||
let is_bot = context.get_config_bool(Config::Bot).await?;
|
||||
|
||||
if chat_id.is_none() {
|
||||
@@ -1981,7 +1981,7 @@ async fn apply_mailinglist_changes(
|
||||
if let Some(old_list_post) = chat.param.get(Param::ListPost) {
|
||||
if list_post.as_ref() != old_list_post {
|
||||
// Apparently the mailing list is using a different List-Post header in each message.
|
||||
// Make the mailing list read-only because we would't know which message the user wants to reply to.
|
||||
// Make the mailing list read-only because we wouldn't know which message the user wants to reply to.
|
||||
chat.param.remove(Param::ListPost);
|
||||
chat.update_param(context).await?;
|
||||
}
|
||||
@@ -2296,7 +2296,7 @@ pub(crate) async fn get_prefetch_parent_message(
|
||||
///
|
||||
/// * param `prevent_rename`: if true, the display_name of this contact will not be changed. Useful for
|
||||
/// mailing lists: In some mailing lists, many users write from the same address but with different
|
||||
/// display names. We don't want the display name to change everytime the user gets a new email from
|
||||
/// display names. We don't want the display name to change every time the user gets a new email from
|
||||
/// a mailing list.
|
||||
async fn add_or_lookup_contacts_by_address_list(
|
||||
context: &Context,
|
||||
|
||||
@@ -2485,7 +2485,7 @@ Reply to all"#,
|
||||
|
||||
/// Tests that replies to similar ad hoc groups are correctly assigned to chats.
|
||||
///
|
||||
/// The difficutly here is that ad hoc groups don't have unique group IDs, because both
|
||||
/// The difficulty here is that ad hoc groups don't have unique group IDs, because both
|
||||
/// messages have the same recipient lists and only differ in the subject and message contents.
|
||||
/// The messages can be properly assigned to chats only using the In-Reply-To or References
|
||||
/// headers.
|
||||
|
||||
@@ -539,7 +539,7 @@ impl SecureJoinStep {
|
||||
false
|
||||
}
|
||||
SecureJoinStep::Completed => {
|
||||
warn!(context, "Complted state for next securejoin step");
|
||||
warn!(context, "Completed state for next securejoin step");
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -436,7 +436,7 @@ pub(crate) async fn smtp_send(
|
||||
// Any extended smtp status codes like x.1.1, x.1.2 or x.1.3 that we
|
||||
// receive as a transient error are misconfigurations of the smtp server.
|
||||
// See <https://tools.ietf.org/html/rfc3463#section-3.2>
|
||||
info!(context, "Received extended status code {} for a transient error. This looks like a misconfigured smtp server, let's fail immediatly", first_word);
|
||||
info!(context, "Received extended status code {} for a transient error. This looks like a misconfigured SMTP server, let's fail immediately", first_word);
|
||||
SendResult::Failure(format_err!("Permanent SMTP error: {}", err))
|
||||
} else {
|
||||
info!(
|
||||
@@ -656,7 +656,7 @@ pub(crate) async fn send_smtp_messages(context: &Context, connection: &mut Smtp)
|
||||
}
|
||||
|
||||
// although by slow sending, ratelimit may have been expired meanwhile,
|
||||
// do not attempt to send MDNs if ratelimited happend before on status-updates/sync:
|
||||
// do not attempt to send MDNs if ratelimited happened before on status-updates/sync:
|
||||
// instead, let the caller recall this function so that more important status-updates/sync are sent out.
|
||||
if !ratelimited {
|
||||
send_mdns(context, connection)
|
||||
|
||||
@@ -9,7 +9,7 @@ use crate::events::EventType;
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
// if more recipients are needed in SMTP's `RCPT TO:` header, recipient-list is splitted to chunks.
|
||||
// if more recipients are needed in SMTP's `RCPT TO:` header, recipient-list is split to chunks.
|
||||
// this does not affect MIME'e `To:` header.
|
||||
// can be overwritten by the setting `max_smtp_rcpt_to` in provider-db.
|
||||
pub(crate) const DEFAULT_MAX_SMTP_RCPT_TO: usize = 50;
|
||||
|
||||
154
src/sql.rs
154
src/sql.rs
@@ -15,6 +15,7 @@ use crate::constants::DC_CHAT_ID_TRASH;
|
||||
use crate::context::Context;
|
||||
use crate::debug_logging::set_debug_logging_xdc;
|
||||
use crate::ephemeral::start_ephemeral_timers;
|
||||
use crate::imex::BLOBS_BACKUP_NAME;
|
||||
use crate::log::LogExt;
|
||||
use crate::message::{Message, MsgId, Viewtype};
|
||||
use crate::param::{Param, Params};
|
||||
@@ -686,7 +687,7 @@ pub async fn housekeeping(context: &Context) -> Result<()> {
|
||||
if let Err(err) = remove_unused_files(context).await {
|
||||
warn!(
|
||||
context,
|
||||
"Housekeeping: cannot remove unusued files: {:#}", err
|
||||
"Housekeeping: cannot remove unused files: {:#}", err
|
||||
);
|
||||
}
|
||||
|
||||
@@ -792,74 +793,93 @@ pub async fn remove_unused_files(context: &Context) -> Result<()> {
|
||||
.context("housekeeping: failed to SELECT value FROM config")?;
|
||||
|
||||
info!(context, "{} files in use.", files_in_use.len(),);
|
||||
/* go through directory and delete unused files */
|
||||
let p = context.get_blobdir();
|
||||
match tokio::fs::read_dir(p).await {
|
||||
Ok(mut dir_handle) => {
|
||||
/* avoid deletion of files that are just created to build a message object */
|
||||
let diff = std::time::Duration::from_secs(60 * 60);
|
||||
let keep_files_newer_than = std::time::SystemTime::now()
|
||||
.checked_sub(diff)
|
||||
.unwrap_or(std::time::SystemTime::UNIX_EPOCH);
|
||||
/* go through directories and delete unused files */
|
||||
let blobdir = context.get_blobdir();
|
||||
for p in [&blobdir.join(BLOBS_BACKUP_NAME), blobdir] {
|
||||
match tokio::fs::read_dir(p).await {
|
||||
Ok(mut dir_handle) => {
|
||||
/* avoid deletion of files that are just created to build a message object */
|
||||
let diff = std::time::Duration::from_secs(60 * 60);
|
||||
let keep_files_newer_than = std::time::SystemTime::now()
|
||||
.checked_sub(diff)
|
||||
.unwrap_or(std::time::SystemTime::UNIX_EPOCH);
|
||||
|
||||
while let Ok(Some(entry)) = dir_handle.next_entry().await {
|
||||
let name_f = entry.file_name();
|
||||
let name_s = name_f.to_string_lossy();
|
||||
while let Ok(Some(entry)) = dir_handle.next_entry().await {
|
||||
let name_f = entry.file_name();
|
||||
let name_s = name_f.to_string_lossy();
|
||||
|
||||
if is_file_in_use(&files_in_use, None, &name_s)
|
||||
|| is_file_in_use(&files_in_use, Some(".increation"), &name_s)
|
||||
|| is_file_in_use(&files_in_use, Some(".waveform"), &name_s)
|
||||
|| is_file_in_use(&files_in_use, Some("-preview.jpg"), &name_s)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
unreferenced_count += 1;
|
||||
|
||||
if let Ok(stats) = tokio::fs::metadata(entry.path()).await {
|
||||
let recently_created =
|
||||
stats.created().map_or(false, |t| t > keep_files_newer_than);
|
||||
let recently_modified = stats
|
||||
.modified()
|
||||
.map_or(false, |t| t > keep_files_newer_than);
|
||||
let recently_accessed = stats
|
||||
.accessed()
|
||||
.map_or(false, |t| t > keep_files_newer_than);
|
||||
|
||||
if recently_created || recently_modified || recently_accessed {
|
||||
info!(
|
||||
context,
|
||||
"Housekeeping: Keeping new unreferenced file #{}: {:?}",
|
||||
unreferenced_count,
|
||||
entry.file_name(),
|
||||
);
|
||||
if p == blobdir
|
||||
&& (is_file_in_use(&files_in_use, None, &name_s)
|
||||
|| is_file_in_use(&files_in_use, Some(".increation"), &name_s)
|
||||
|| is_file_in_use(&files_in_use, Some(".waveform"), &name_s)
|
||||
|| is_file_in_use(&files_in_use, Some("-preview.jpg"), &name_s))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
info!(
|
||||
context,
|
||||
"Housekeeping: Deleting unreferenced file #{}: {:?}",
|
||||
unreferenced_count,
|
||||
entry.file_name()
|
||||
);
|
||||
let path = entry.path();
|
||||
if let Err(err) = delete_file(context, &path).await {
|
||||
error!(
|
||||
|
||||
if let Ok(stats) = tokio::fs::metadata(entry.path()).await {
|
||||
if stats.is_dir() {
|
||||
if let Err(e) = tokio::fs::remove_dir(entry.path()).await {
|
||||
// The dir could be created not by a user, but by a desktop
|
||||
// environment f.e. So, no warning.
|
||||
info!(
|
||||
context,
|
||||
"Housekeeping: Cannot rmdir {}: {:#}",
|
||||
entry.path().display(),
|
||||
e
|
||||
);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
unreferenced_count += 1;
|
||||
let recently_created =
|
||||
stats.created().map_or(false, |t| t > keep_files_newer_than);
|
||||
let recently_modified = stats
|
||||
.modified()
|
||||
.map_or(false, |t| t > keep_files_newer_than);
|
||||
let recently_accessed = stats
|
||||
.accessed()
|
||||
.map_or(false, |t| t > keep_files_newer_than);
|
||||
|
||||
if p == blobdir
|
||||
&& (recently_created || recently_modified || recently_accessed)
|
||||
{
|
||||
info!(
|
||||
context,
|
||||
"Housekeeping: Keeping new unreferenced file #{}: {:?}",
|
||||
unreferenced_count,
|
||||
entry.file_name(),
|
||||
);
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
unreferenced_count += 1;
|
||||
}
|
||||
info!(
|
||||
context,
|
||||
"Failed to delete unused file {}: {:#}.",
|
||||
path.display(),
|
||||
err
|
||||
"Housekeeping: Deleting unreferenced file #{}: {:?}",
|
||||
unreferenced_count,
|
||||
entry.file_name()
|
||||
);
|
||||
let path = entry.path();
|
||||
if let Err(err) = delete_file(context, &path).await {
|
||||
error!(
|
||||
context,
|
||||
"Failed to delete unused file {}: {:#}.",
|
||||
path.display(),
|
||||
err
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
warn!(
|
||||
context,
|
||||
"Housekeeping: Cannot open {}. ({})",
|
||||
context.get_blobdir().display(),
|
||||
err
|
||||
);
|
||||
Err(err) => {
|
||||
warn!(
|
||||
context,
|
||||
"Housekeeping: Cannot read dir {}: {:#}",
|
||||
p.display(),
|
||||
err
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1061,6 +1081,18 @@ mod tests {
|
||||
assert_eq!(loaded_draft.unwrap().text.unwrap(), "This is my draft");
|
||||
}
|
||||
|
||||
/// Tests that `housekeeping` deletes the blobs backup dir which is created normally by
|
||||
/// `imex::import_backup`.
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_housekeeping_delete_blobs_backup_dir() {
|
||||
let t = TestContext::new_alice().await;
|
||||
let dir = t.get_blobdir().join(BLOBS_BACKUP_NAME);
|
||||
tokio::fs::create_dir(&dir).await.unwrap();
|
||||
tokio::fs::write(dir.join("f"), "").await.unwrap();
|
||||
housekeeping(&t).await.unwrap();
|
||||
tokio::fs::create_dir(&dir).await.unwrap();
|
||||
}
|
||||
|
||||
/// Regression test.
|
||||
///
|
||||
/// Previously the code checking for existence of `config` table
|
||||
|
||||
@@ -804,7 +804,7 @@ pub(crate) async fn sync_msg_subject(context: &Context) -> String {
|
||||
translated(context, StockMessage::SyncMsgSubject).await
|
||||
}
|
||||
|
||||
/// Stock string: `This message is used to synchronize data betweeen your devices.`.
|
||||
/// Stock string: `This message is used to synchronize data between your devices.`.
|
||||
pub(crate) async fn sync_msg_body(context: &Context) -> String {
|
||||
translated(context, StockMessage::SyncMsgBody).await
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ pub struct Summary {
|
||||
}
|
||||
|
||||
impl Summary {
|
||||
/// Constucts chatlist summary
|
||||
/// Constructs chatlist summary
|
||||
/// from the provided message, chat and message author contact snapshots.
|
||||
pub async fn new(
|
||||
context: &Context,
|
||||
|
||||
@@ -434,9 +434,9 @@ mod tests {
|
||||
.parse_sync_items(
|
||||
r#"{"items":[
|
||||
{"timestamp":1631781316,"data":{"AddQrToken":{"invitenumber":"yip-in","auth":"a"}}},
|
||||
{"timestamp":1631781316,"data":{"DeleteQrToken":{"invitenumber":"in","auth":"delete unexistant, shall continue"}}},
|
||||
{"timestamp":1631781316,"data":{"DeleteQrToken":{"invitenumber":"in","auth":"delete unexistent, shall continue"}}},
|
||||
{"timestamp":1631781316,"data":{"AddQrToken":{"invitenumber":"in","auth":"yip-auth"}}},
|
||||
{"timestamp":1631781316,"data":{"AddQrToken":{"invitenumber":"in","auth":"foo","grpid":"non-existant"}}},
|
||||
{"timestamp":1631781316,"data":{"AddQrToken":{"invitenumber":"in","auth":"foo","grpid":"non-existent"}}},
|
||||
{"timestamp":1631781316,"data":{"AddQrToken":{"invitenumber":"in","auth":"directly deleted"}}},
|
||||
{"timestamp":1631781316,"data":{"DeleteQrToken":{"invitenumber":"in","auth":"directly deleted"}}}
|
||||
]}"#
|
||||
@@ -447,7 +447,7 @@ mod tests {
|
||||
|
||||
assert!(token::exists(&t, Namespace::InviteNumber, "yip-in").await);
|
||||
assert!(token::exists(&t, Namespace::Auth, "yip-auth").await);
|
||||
assert!(!token::exists(&t, Namespace::Auth, "non-existant").await);
|
||||
assert!(!token::exists(&t, Namespace::Auth, "non-existent").await);
|
||||
assert!(!token::exists(&t, Namespace::Auth, "directly deleted").await);
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//! # Time smearing.
|
||||
//!
|
||||
//! As e-mails typically only use a second-based-resolution for timestamps,
|
||||
//! the order of two mails sent withing one second is unclear.
|
||||
//! the order of two mails sent within one second is unclear.
|
||||
//! This is bad e.g. when forwarding some messages from a chat -
|
||||
//! these messages will appear at the recipient easily out of order.
|
||||
//!
|
||||
|
||||
@@ -387,7 +387,7 @@ pub(crate) async fn create_folder(
|
||||
}
|
||||
}
|
||||
|
||||
/// Write a the given content to provied file path.
|
||||
/// Write a the given content to provided file path.
|
||||
pub(crate) async fn write_file(
|
||||
context: &Context,
|
||||
path: impl AsRef<Path>,
|
||||
|
||||
@@ -658,7 +658,7 @@ async fn get_blob(archive: &mut async_zip::read::fs::ZipFileReader, name: &str)
|
||||
|
||||
impl Message {
|
||||
/// Get handle to a webxdc ZIP-archive.
|
||||
/// To check for file existance use archive.by_name(), to read a file, use get_blob(archive).
|
||||
/// To check for file existence use archive.by_name(), to read a file, use get_blob(archive).
|
||||
async fn get_webxdc_archive(
|
||||
&self,
|
||||
context: &Context,
|
||||
|
||||
Reference in New Issue
Block a user