mirror of
https://github.com/chatmail/core.git
synced 2026-04-19 14:36:29 +03:00
fix python lint errors receive pre-mesages, start with changes to imap loop. refactor: move download code from `scheduler.rs` to `download.rs`, also move `get_msg_id_by_rfc724_mid` to `MsgId::get_by_rfc724_mid` `MAX_FETCH_MSG_SIZE` is no longer unused Parse if it is a pre-message or full-message start with receiving logic get rid of `MsgId::get_by_rfc724_mid` because it was a duplicate of `message::rfc724_mid_exists` docs: add hint to `MimeMessage::from_bytes` stating that it has side-effects. receiving full message send and receive `attachment_size` and set viewtype to text in pre_message metadata as struct in pre-message in header. And fill params that we can already fill from the metadata. Also add a new api to check what viewtype the message will have once downloaded. api: jsonrpc: add `full_message_view_type` to `Message` and `MessageInfo` make PreMsgMetadata.to_header_value not consume self/PreMsgMetadata add api to merge params on download full message: merge new params into old params and remove full-message metadata params move tests to `src/tests/pre_messages.rs` dynamically allocate test attachment bytes fix detection of pre-messages. (it looked for the ChatFullMessageId header in the unencrypted headers before) fix setting dl state to avaiable on pre-messages fix: save pre message with rfc724_mid of full message als disable replacement for full messages add some receiving tests and update test todo for premessage metadata test: process full message before pre-message test receive normal message some serialization tests for PreMsgMetadata remove outdated todo comment test that pre-message contains message text PreMsgMetadata: test_build_from_file_msg and test_build_from_file_msg test: test_receive_pre_message_image Test receiving the full message after receiving an edit after receiving the pre-message test_reaction_on_pre_message test_full_download_after_trashed test_webxdc_update_for_not_downloaded_instance simplify fake webxdc generation in test_webxdc_update_for_not_downloaded_instance test_markseen_pre_msg test_pre_msg_can_start_chat and test_full_msg_can_start_chat test_download_later_keeps_message_order test_chatlist_event_on_full_msg_download fix download not working log splitting into pre-message add pre-message info to text when loading from db. this can be disabled with config key `hide_pre_message_metadata_text` if ui wants to display it in a prettier way. update `download_limit` documentation more logging: log size of pre and post messages rename full message to Post-Message split up the pre-message tests into multiple files dedup test code by extracting code to create test messages into util methods remove post_message_view_type from api, now it is only used internally for tests remove `hide_pre_message_metadata_text` config option, as there currently is no way to get the full message viewtype anymore Update src/download.rs resolve comment use `parse_message_id` instead of removing `<>`parenthesis it manually fix available_post_msgs gets no entries handle forwarding and add a test for it. convert comment to log warning event on unexpected download failure add doc comment to `simple_imap_loop` more logging handle saving pre-message to self messages and test.
249 lines
8.2 KiB
Rust
249 lines
8.2 KiB
Rust
use anyhow::{Context as _, Result};
|
|
use num_traits::ToPrimitive;
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
use crate::context::Context;
|
|
use crate::log::warn;
|
|
use crate::message::Message;
|
|
use crate::message::Viewtype;
|
|
use crate::param::{Param, Params};
|
|
|
|
/// Metadata contained in Pre-Message that describes the Post-Message.
|
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
|
pub struct PreMsgMetadata {
|
|
/// size of the attachment in bytes
|
|
pub(crate) size: u64,
|
|
/// Real viewtype of message
|
|
pub(crate) viewtype: Viewtype,
|
|
/// the original file name
|
|
pub(crate) filename: String,
|
|
/// Dimensions: width and height of image or video
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub(crate) dimensions: Option<(i32, i32)>,
|
|
/// Duration of audio file or video in milliseconds
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub(crate) duration: Option<i32>,
|
|
}
|
|
|
|
impl PreMsgMetadata {
|
|
// Returns PreMsgMetadata for messages with files and None for messages without file attachment
|
|
pub(crate) async fn from_msg(context: &Context, message: &Message) -> Result<Option<Self>> {
|
|
if !message.viewtype.has_file() {
|
|
return Ok(None);
|
|
}
|
|
|
|
let size = message
|
|
.get_filebytes(context)
|
|
.await?
|
|
.context("unexpected: file has no size")?;
|
|
let filename = message
|
|
.param
|
|
.get(Param::Filename)
|
|
.unwrap_or_default()
|
|
.to_owned();
|
|
let dimensions = {
|
|
match (
|
|
message.param.get_int(Param::Width),
|
|
message.param.get_int(Param::Height),
|
|
) {
|
|
(None, None) => None,
|
|
(Some(width), Some(height)) => Some((width, height)),
|
|
_ => {
|
|
warn!(context, "Message has misses either width or height");
|
|
None
|
|
}
|
|
}
|
|
};
|
|
let duration = message.param.get_int(Param::Duration);
|
|
|
|
Ok(Some(Self {
|
|
size,
|
|
filename,
|
|
viewtype: message.viewtype,
|
|
dimensions,
|
|
duration,
|
|
}))
|
|
}
|
|
|
|
pub(crate) fn to_header_value(&self) -> Result<String> {
|
|
Ok(serde_json::to_string(&self)?)
|
|
}
|
|
|
|
pub(crate) fn try_from_header_value(value: &str) -> Result<Self> {
|
|
Ok(serde_json::from_str(value)?)
|
|
}
|
|
}
|
|
|
|
impl Params {
|
|
/// Applies data from pre_msg_metadata to Params
|
|
pub(crate) fn apply_from_pre_msg_metadata(
|
|
&mut self,
|
|
pre_msg_metadata: &PreMsgMetadata,
|
|
) -> &mut Self {
|
|
self.set(Param::PostMessageFileBytes, pre_msg_metadata.size);
|
|
if !pre_msg_metadata.filename.is_empty() {
|
|
self.set(Param::Filename, &pre_msg_metadata.filename);
|
|
}
|
|
self.set_i64(
|
|
Param::PostMessageViewtype,
|
|
pre_msg_metadata.viewtype.to_i64().unwrap_or_default(),
|
|
);
|
|
if let Some((width, height)) = pre_msg_metadata.dimensions {
|
|
self.set(Param::Width, width);
|
|
self.set(Param::Height, height);
|
|
}
|
|
if let Some(duration) = pre_msg_metadata.duration {
|
|
self.set(Param::Duration, duration);
|
|
}
|
|
|
|
self
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use anyhow::Result;
|
|
use pretty_assertions::assert_eq;
|
|
|
|
use crate::{
|
|
message::{Message, Viewtype},
|
|
test_utils::{TestContextManager, create_test_image},
|
|
};
|
|
|
|
use super::PreMsgMetadata;
|
|
|
|
/// Build from message with file attachment
|
|
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
|
async fn test_build_from_file_msg() -> Result<()> {
|
|
let mut tcm = TestContextManager::new();
|
|
let alice = &tcm.alice().await;
|
|
|
|
let mut file_msg = Message::new(Viewtype::File);
|
|
file_msg.set_file_from_bytes(alice, "test.bin", &vec![0u8; 1_000_000], None)?;
|
|
let pre_mesage_metadata = PreMsgMetadata::from_msg(alice, &file_msg).await?;
|
|
assert_eq!(
|
|
pre_mesage_metadata,
|
|
Some(PreMsgMetadata {
|
|
size: 1_000_000,
|
|
viewtype: Viewtype::File,
|
|
filename: "test.bin".to_string(),
|
|
dimensions: None,
|
|
duration: None,
|
|
})
|
|
);
|
|
Ok(())
|
|
}
|
|
|
|
/// Build from message with image attachment
|
|
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
|
async fn test_build_from_image_msg() -> Result<()> {
|
|
let mut tcm = TestContextManager::new();
|
|
let alice = &tcm.alice().await;
|
|
let mut image_msg = Message::new(Viewtype::Image);
|
|
|
|
let (width, height) = (1080, 1920);
|
|
let test_img = create_test_image(width, height)?;
|
|
image_msg.set_file_from_bytes(alice, "vacation.png", &test_img, None)?;
|
|
// this is usually done while sending,
|
|
// but we don't send it here, so we need to call it ourself
|
|
image_msg.try_calc_and_set_dimensions(alice).await?;
|
|
let pre_mesage_metadata = PreMsgMetadata::from_msg(alice, &image_msg).await?;
|
|
assert_eq!(
|
|
pre_mesage_metadata,
|
|
Some(PreMsgMetadata {
|
|
size: 1816098,
|
|
viewtype: Viewtype::Image,
|
|
filename: "vacation.png".to_string(),
|
|
dimensions: Some((width as i32, height as i32)),
|
|
duration: None,
|
|
})
|
|
);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Test that serialisation results in expected format
|
|
#[test]
|
|
fn test_serialize_to_header() -> Result<()> {
|
|
assert_eq!(
|
|
PreMsgMetadata {
|
|
size: 1_000_000,
|
|
viewtype: Viewtype::File,
|
|
filename: "test.bin".to_string(),
|
|
dimensions: None,
|
|
duration: None,
|
|
}
|
|
.to_header_value()?,
|
|
"{\"size\":1000000,\"viewtype\":\"File\",\"filename\":\"test.bin\"}"
|
|
);
|
|
assert_eq!(
|
|
PreMsgMetadata {
|
|
size: 5_342_765,
|
|
viewtype: Viewtype::Image,
|
|
filename: "vacation.png".to_string(),
|
|
dimensions: Some((1080, 1920)),
|
|
duration: None,
|
|
}
|
|
.to_header_value()?,
|
|
"{\"size\":5342765,\"viewtype\":\"Image\",\"filename\":\"vacation.png\",\"dimensions\":[1080,1920]}"
|
|
);
|
|
assert_eq!(
|
|
PreMsgMetadata {
|
|
size: 5_000,
|
|
viewtype: Viewtype::Audio,
|
|
filename: "audio-DD-MM-YY.ogg".to_string(),
|
|
dimensions: None,
|
|
duration: Some(152_310),
|
|
}
|
|
.to_header_value()?,
|
|
"{\"size\":5000,\"viewtype\":\"Audio\",\"filename\":\"audio-DD-MM-YY.ogg\",\"duration\":152310}"
|
|
);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Test that deserialisation from expected format works
|
|
/// This test will become important for compatibility between versions in the future
|
|
#[test]
|
|
fn test_deserialize_from_header() -> Result<()> {
|
|
assert_eq!(
|
|
serde_json::from_str::<PreMsgMetadata>(
|
|
"{\"size\":1000000,\"viewtype\":\"File\",\"filename\":\"test.bin\",\"dimensions\":null,\"duration\":null}"
|
|
)?,
|
|
PreMsgMetadata {
|
|
size: 1_000_000,
|
|
viewtype: Viewtype::File,
|
|
filename: "test.bin".to_string(),
|
|
dimensions: None,
|
|
duration: None,
|
|
}
|
|
);
|
|
assert_eq!(
|
|
serde_json::from_str::<PreMsgMetadata>(
|
|
"{\"size\":5342765,\"viewtype\":\"Image\",\"filename\":\"vacation.png\",\"dimensions\":[1080,1920]}"
|
|
)?,
|
|
PreMsgMetadata {
|
|
size: 5_342_765,
|
|
viewtype: Viewtype::Image,
|
|
filename: "vacation.png".to_string(),
|
|
dimensions: Some((1080, 1920)),
|
|
duration: None,
|
|
}
|
|
);
|
|
assert_eq!(
|
|
serde_json::from_str::<PreMsgMetadata>(
|
|
"{\"size\":5000,\"viewtype\":\"Audio\",\"filename\":\"audio-DD-MM-YY.ogg\",\"duration\":152310}"
|
|
)?,
|
|
PreMsgMetadata {
|
|
size: 5_000,
|
|
viewtype: Viewtype::Audio,
|
|
filename: "audio-DD-MM-YY.ogg".to_string(),
|
|
dimensions: None,
|
|
duration: Some(152_310),
|
|
}
|
|
);
|
|
|
|
Ok(())
|
|
}
|
|
}
|