mirror of
https://github.com/chatmail/core.git
synced 2026-04-28 19:06:35 +03:00
A MsgId newtype
This more strongly types the ubiquitous message id type by no longer making it an integer. It keeps the actual ID opaque. Only for the generic job API the number keeps being used. Some locations also need to create it from an integer and call MsgId::new().
This commit is contained in:
committed by
holger krekel
parent
c6adbe939d
commit
c8d296ea0e
383
src/message.rs
383
src/message.rs
@@ -1,6 +1,7 @@
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use deltachat_derive::{FromSql, ToSql};
|
||||
use failure::Fail;
|
||||
|
||||
use crate::chat::{self, Chat};
|
||||
use crate::constants::*;
|
||||
@@ -17,9 +18,134 @@ use crate::pgp::*;
|
||||
use crate::sql;
|
||||
use crate::stock::StockMessage;
|
||||
|
||||
/// In practice, the user additionally cuts the string himself pixel-accurate.
|
||||
// In practice, the user additionally cuts the string themselves
|
||||
// pixel-accurate.
|
||||
const SUMMARY_CHARACTERS: usize = 160;
|
||||
|
||||
/// Message ID, including reserved IDs.
|
||||
///
|
||||
/// Some message IDs are reserved to identify special message types.
|
||||
/// This type can represent both the special as well as normal
|
||||
/// messages.
|
||||
#[derive(Debug, Copy, Clone, Default, PartialEq, Eq)]
|
||||
pub struct MsgId(u32);
|
||||
|
||||
impl MsgId {
|
||||
/// Create a new [MsgId].
|
||||
pub fn new(id: u32) -> MsgId {
|
||||
MsgId(id)
|
||||
}
|
||||
|
||||
/// Create a new unset [MsgId].
|
||||
pub fn new_unset() -> MsgId {
|
||||
MsgId(0)
|
||||
}
|
||||
|
||||
/// Whether the message ID signifies a special message.
|
||||
///
|
||||
/// This kind of message ID can not be used for real messages.
|
||||
pub fn is_special(&self) -> bool {
|
||||
match self.0 {
|
||||
0..=DC_MSG_ID_LAST_SPECIAL => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether the message ID is unset.
|
||||
///
|
||||
/// When a message is created it initially has a ID of `0`, which
|
||||
/// is filled in by a real message ID once the message is saved in
|
||||
/// the database. This returns true while the message has not
|
||||
/// been saved and thus not yet been given an actual message ID.
|
||||
///
|
||||
/// When this is `true`, [MsgId::is_special] will also always be
|
||||
/// `true`.
|
||||
pub fn is_unset(&self) -> bool {
|
||||
self.0 == 0
|
||||
}
|
||||
|
||||
/// Whether the message ID is the special marker1 marker.
|
||||
///
|
||||
/// See the docs of the `dc_get_chat_msgs` C API for details.
|
||||
pub fn is_marker1(&self) -> bool {
|
||||
self.0 == DC_MSG_ID_MARKER1
|
||||
}
|
||||
|
||||
/// Whether the message ID is the special day marker.
|
||||
///
|
||||
/// See the docs of the `dc_get_chat_msgs` C API for details.
|
||||
pub fn is_daymarker(&self) -> bool {
|
||||
self.0 == DC_MSG_ID_DAYMARKER
|
||||
}
|
||||
|
||||
/// Bad evil escape hatch.
|
||||
///
|
||||
/// Avoid using this, eventually types should be cleaned up enough
|
||||
/// that it is no longer necessary.
|
||||
pub fn to_u32(&self) -> u32 {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for MsgId {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
// Would be nice if we could use match here, but no computed values in ranges.
|
||||
if self.0 == DC_MSG_ID_MARKER1 {
|
||||
write!(f, "Msg#Marker1")
|
||||
} else if self.0 == DC_MSG_ID_DAYMARKER {
|
||||
write!(f, "Msg#DayMarker")
|
||||
} else if self.0 <= DC_MSG_ID_LAST_SPECIAL {
|
||||
write!(f, "Msg#UnknownSpecial")
|
||||
} else {
|
||||
write!(f, "Msg#{}", self.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Allow converting [MsgId] to an SQLite type.
|
||||
///
|
||||
/// This allows you to directly store [MsgId] into the database.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This **does** ensure that no special message IDs are written into
|
||||
/// the database and the conversion will fail if this is not the case.
|
||||
impl rusqlite::types::ToSql for MsgId {
|
||||
fn to_sql(&self) -> rusqlite::Result<rusqlite::types::ToSqlOutput> {
|
||||
if self.0 <= DC_MSG_ID_LAST_SPECIAL {
|
||||
return Err(rusqlite::Error::ToSqlConversionFailure(Box::new(
|
||||
InvalidMsgId.compat(),
|
||||
)));
|
||||
}
|
||||
let val = rusqlite::types::Value::Integer(self.0 as i64);
|
||||
let out = rusqlite::types::ToSqlOutput::Owned(val);
|
||||
Ok(out)
|
||||
}
|
||||
}
|
||||
|
||||
/// Allow converting an SQLite integer directly into [MsgId].
|
||||
impl rusqlite::types::FromSql for MsgId {
|
||||
fn column_result(value: rusqlite::types::ValueRef) -> rusqlite::types::FromSqlResult<Self> {
|
||||
// Would be nice if we could use match here, but alas.
|
||||
i64::column_result(value).and_then(|val| {
|
||||
if 0 <= val && val <= std::u32::MAX as i64 {
|
||||
Ok(MsgId::new(val as u32))
|
||||
} else {
|
||||
Err(rusqlite::types::FromSqlError::OutOfRange(val))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Message ID was invalid.
|
||||
///
|
||||
/// This usually occurs when trying to use a message ID of
|
||||
/// [DC_MSG_ID_LAST_SPECIAL] or below in a situation where this is not
|
||||
/// possible.
|
||||
#[derive(Debug, Fail)]
|
||||
#[fail(display = "Invalid Message ID.")]
|
||||
pub struct InvalidMsgId;
|
||||
|
||||
/// An object representing a single message in memory.
|
||||
/// The message object is not updated.
|
||||
/// If you want an update, you have to recreate the object.
|
||||
@@ -29,7 +155,7 @@ const SUMMARY_CHARACTERS: usize = 160;
|
||||
/// approx. max. length returned by dc_get_msg_info()
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct Message {
|
||||
pub(crate) id: u32,
|
||||
pub(crate) id: MsgId,
|
||||
pub(crate) from_id: u32,
|
||||
pub(crate) to_id: u32,
|
||||
pub(crate) chat_id: u32,
|
||||
@@ -61,70 +187,105 @@ impl Message {
|
||||
msg
|
||||
}
|
||||
|
||||
pub fn load_from_db(context: &Context, id: u32) -> Result<Message, Error> {
|
||||
pub fn load_from_db(context: &Context, id: MsgId) -> Result<Message, Error> {
|
||||
ensure!(
|
||||
!id.is_special(),
|
||||
"Can not load special message IDs from DB."
|
||||
);
|
||||
context.sql.query_row(
|
||||
"SELECT \
|
||||
m.id,rfc724_mid,m.mime_in_reply_to,m.server_folder,m.server_uid,m.move_state,m.chat_id, \
|
||||
m.from_id,m.to_id,m.timestamp,m.timestamp_sent,m.timestamp_rcvd, m.type,m.state,m.msgrmsg,m.txt, \
|
||||
m.param,m.starred,m.hidden,m.location_id, c.blocked \
|
||||
FROM msgs m \
|
||||
LEFT JOIN chats c ON c.id=m.chat_id WHERE m.id=?;",
|
||||
params![id as i32],
|
||||
|row| {
|
||||
let mut msg = Message::default();
|
||||
msg.id = row.get::<_, i32>(0)? as u32;
|
||||
msg.rfc724_mid = row.get::<_, String>(1)?;
|
||||
msg.in_reply_to = row.get::<_, Option<String>>(2)?;
|
||||
msg.server_folder = row.get::<_, Option<String>>(3)?;
|
||||
msg.server_uid = row.get(4)?;
|
||||
msg.move_state = row.get(5)?;
|
||||
msg.chat_id = row.get(6)?;
|
||||
msg.from_id = row.get(7)?;
|
||||
msg.to_id = row.get(8)?;
|
||||
msg.timestamp_sort = row.get(9)?;
|
||||
msg.timestamp_sent = row.get(10)?;
|
||||
msg.timestamp_rcvd = row.get(11)?;
|
||||
msg.type_0 = row.get(12)?;
|
||||
msg.state = row.get(13)?;
|
||||
msg.is_dc_message = row.get(14)?;
|
||||
concat!(
|
||||
"SELECT",
|
||||
" m.id AS id,",
|
||||
" rfc724_mid AS rfc724mid,",
|
||||
" m.mime_in_reply_to AS mime_in_reply_to,",
|
||||
" m.server_folder AS server_folder,",
|
||||
" m.server_uid AS server_uid,",
|
||||
" m.move_state as move_state,",
|
||||
" m.chat_id AS chat_id,",
|
||||
" m.from_id AS from_id,",
|
||||
" m.to_id AS to_id,",
|
||||
" m.timestamp AS timestamp,",
|
||||
" m.timestamp_sent AS timestamp_sent,",
|
||||
" m.timestamp_rcvd AS timestamp_rcvd,",
|
||||
" m.type AS type,",
|
||||
" m.state AS state,",
|
||||
" m.msgrmsg AS msgrmsg,",
|
||||
" m.txt AS txt,",
|
||||
" m.param AS param,",
|
||||
" m.starred AS starred,",
|
||||
" m.hidden AS hidden,",
|
||||
" m.location_id AS location,",
|
||||
" c.blocked AS blocked",
|
||||
" FROM msgs m LEFT JOIN chats c ON c.id=m.chat_id",
|
||||
" WHERE m.id=?;"
|
||||
),
|
||||
params![id],
|
||||
|row| {
|
||||
let mut msg = Message::default();
|
||||
// msg.id = row.get::<_, AnyMsgId>("id")?;
|
||||
msg.id = row.get("id")?;
|
||||
msg.rfc724_mid = row.get::<_, String>("rfc724mid")?;
|
||||
msg.in_reply_to = row.get::<_, Option<String>>("mime_in_reply_to")?;
|
||||
msg.server_folder = row.get::<_, Option<String>>("server_folder")?;
|
||||
msg.server_uid = row.get("server_uid")?;
|
||||
msg.move_state = row.get("move_state")?;
|
||||
msg.chat_id = row.get("chat_id")?;
|
||||
msg.from_id = row.get("from_id")?;
|
||||
msg.to_id = row.get("to_id")?;
|
||||
msg.timestamp_sort = row.get("timestamp")?;
|
||||
msg.timestamp_sent = row.get("timestamp_sent")?;
|
||||
msg.timestamp_rcvd = row.get("timestamp_rcvd")?;
|
||||
msg.type_0 = row.get("type")?;
|
||||
msg.state = row.get("state")?;
|
||||
msg.is_dc_message = row.get("msgrmsg")?;
|
||||
|
||||
let text;
|
||||
if let rusqlite::types::ValueRef::Text(buf) = row.get_raw(15) {
|
||||
if let Ok(t) = String::from_utf8(buf.to_vec()) {
|
||||
text = t;
|
||||
let text;
|
||||
if let rusqlite::types::ValueRef::Text(buf) = row.get_raw("txt") {
|
||||
if let Ok(t) = String::from_utf8(buf.to_vec()) {
|
||||
text = t;
|
||||
} else {
|
||||
warn!(
|
||||
context,
|
||||
concat!(
|
||||
"dc_msg_load_from_db: could not get ",
|
||||
"text column as non-lossy utf8 id {}"
|
||||
),
|
||||
id
|
||||
);
|
||||
text = String::from_utf8_lossy(buf).into_owned();
|
||||
}
|
||||
} else {
|
||||
warn!(context, "dc_msg_load_from_db: could not get text column as non-lossy utf8 id {}", id);
|
||||
text = String::from_utf8_lossy(buf).into_owned();
|
||||
text = "".to_string();
|
||||
}
|
||||
} else {
|
||||
text = "".to_string();
|
||||
}
|
||||
msg.text = Some(text);
|
||||
msg.text = Some(text);
|
||||
|
||||
msg.param = row.get::<_, String>(16)?.parse().unwrap_or_default();
|
||||
msg.starred = row.get(17)?;
|
||||
msg.hidden = row.get(18)?;
|
||||
msg.location_id = row.get(19)?;
|
||||
msg.chat_blocked = row.get::<_, Option<Blocked>>(20)?.unwrap_or_default();
|
||||
msg.param = row.get::<_, String>("param")?.parse().unwrap_or_default();
|
||||
msg.starred = row.get("starred")?;
|
||||
msg.hidden = row.get("hidden")?;
|
||||
msg.location_id = row.get("location")?;
|
||||
msg.chat_blocked = row
|
||||
.get::<_, Option<Blocked>>("blocked")?
|
||||
.unwrap_or_default();
|
||||
|
||||
Ok(msg)
|
||||
})
|
||||
Ok(msg)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub fn delete_from_db(context: &Context, msg_id: u32) {
|
||||
pub fn delete_from_db(context: &Context, msg_id: MsgId) {
|
||||
if let Ok(msg) = Message::load_from_db(context, msg_id) {
|
||||
sql::execute(
|
||||
context,
|
||||
&context.sql,
|
||||
"DELETE FROM msgs WHERE id=?;",
|
||||
params![msg.id as i32],
|
||||
params![msg.id],
|
||||
)
|
||||
.ok();
|
||||
sql::execute(
|
||||
context,
|
||||
&context.sql,
|
||||
"DELETE FROM msgs_mdns WHERE msg_id=?;",
|
||||
params![msg.id as i32],
|
||||
params![msg.id],
|
||||
)
|
||||
.ok();
|
||||
}
|
||||
@@ -188,7 +349,7 @@ impl Message {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_id(&self) -> u32 {
|
||||
pub fn get_id(&self) -> MsgId {
|
||||
self.id
|
||||
}
|
||||
|
||||
@@ -400,7 +561,7 @@ impl Message {
|
||||
context,
|
||||
&context.sql,
|
||||
"UPDATE msgs SET param=? WHERE id=?;",
|
||||
params![self.param.to_string(), self.id as i32],
|
||||
params![self.param.to_string(), self.id],
|
||||
)
|
||||
.is_ok()
|
||||
}
|
||||
@@ -510,7 +671,7 @@ impl Lot {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_msg_info(context: &Context, msg_id: u32) -> String {
|
||||
pub fn get_msg_info(context: &Context, msg_id: MsgId) -> String {
|
||||
let mut ret = String::new();
|
||||
|
||||
let msg = Message::load_from_db(context, msg_id);
|
||||
@@ -523,11 +684,11 @@ pub fn get_msg_info(context: &Context, msg_id: u32) -> String {
|
||||
let rawtxt: Option<String> = context.sql.query_get_value(
|
||||
context,
|
||||
"SELECT txt_raw FROM msgs WHERE id=?;",
|
||||
params![msg_id as i32],
|
||||
params![msg_id],
|
||||
);
|
||||
|
||||
if rawtxt.is_none() {
|
||||
ret += &format!("Cannot load message #{}.", msg_id as usize);
|
||||
ret += &format!("Cannot load message {}.", msg_id);
|
||||
return ret;
|
||||
}
|
||||
let rawtxt = rawtxt.unwrap_or_default();
|
||||
@@ -560,7 +721,7 @@ pub fn get_msg_info(context: &Context, msg_id: u32) -> String {
|
||||
|
||||
if let Ok(rows) = context.sql.query_map(
|
||||
"SELECT contact_id, timestamp_sent FROM msgs_mdns WHERE msg_id=?;",
|
||||
params![msg_id as i32],
|
||||
params![msg_id],
|
||||
|row| {
|
||||
let contact_id: i32 = row.get(0)?;
|
||||
let ts: i64 = row.get(1)?;
|
||||
@@ -670,21 +831,21 @@ pub fn guess_msgtype_from_suffix(path: &Path) -> Option<(Viewtype, &str)> {
|
||||
Some(info)
|
||||
}
|
||||
|
||||
pub fn get_mime_headers(context: &Context, msg_id: u32) -> Option<String> {
|
||||
pub fn get_mime_headers(context: &Context, msg_id: MsgId) -> Option<String> {
|
||||
context.sql.query_get_value(
|
||||
context,
|
||||
"SELECT mime_headers FROM msgs WHERE id=?;",
|
||||
params![msg_id as i32],
|
||||
params![msg_id],
|
||||
)
|
||||
}
|
||||
|
||||
pub fn delete_msgs(context: &Context, msg_ids: &[u32]) {
|
||||
pub fn delete_msgs(context: &Context, msg_ids: &[MsgId]) {
|
||||
for msg_id in msg_ids.iter() {
|
||||
update_msg_chat_id(context, *msg_id, DC_CHAT_ID_TRASH);
|
||||
job_add(
|
||||
context,
|
||||
Action::DeleteMsgOnImap,
|
||||
*msg_id as libc::c_int,
|
||||
msg_id.to_u32() as i32,
|
||||
Params::new(),
|
||||
0,
|
||||
);
|
||||
@@ -693,35 +854,45 @@ pub fn delete_msgs(context: &Context, msg_ids: &[u32]) {
|
||||
if !msg_ids.is_empty() {
|
||||
context.call_cb(Event::MsgsChanged {
|
||||
chat_id: 0,
|
||||
msg_id: 0,
|
||||
msg_id: MsgId::new(0),
|
||||
});
|
||||
job_kill_action(context, Action::Housekeeping);
|
||||
job_add(context, Action::Housekeeping, 0, Params::new(), 10);
|
||||
};
|
||||
}
|
||||
|
||||
fn update_msg_chat_id(context: &Context, msg_id: u32, chat_id: u32) -> bool {
|
||||
fn update_msg_chat_id(context: &Context, msg_id: MsgId, chat_id: u32) -> bool {
|
||||
sql::execute(
|
||||
context,
|
||||
&context.sql,
|
||||
"UPDATE msgs SET chat_id=? WHERE id=?;",
|
||||
params![chat_id as i32, msg_id as i32],
|
||||
params![chat_id as i32, msg_id],
|
||||
)
|
||||
.is_ok()
|
||||
}
|
||||
|
||||
pub fn markseen_msgs(context: &Context, msg_ids: &[u32]) -> bool {
|
||||
pub fn markseen_msgs(context: &Context, msg_ids: &[MsgId]) -> bool {
|
||||
if msg_ids.is_empty() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let msgs = context.sql.prepare(
|
||||
"SELECT m.state, c.blocked FROM msgs m LEFT JOIN chats c ON c.id=m.chat_id WHERE m.id=? AND m.chat_id>9",
|
||||
concat!(
|
||||
"SELECT",
|
||||
" m.state AS state,",
|
||||
" c.blocked AS blocked",
|
||||
" FROM msgs m LEFT JOIN chats c ON c.id=m.chat_id",
|
||||
" WHERE m.id=? AND m.chat_id>9"
|
||||
),
|
||||
|mut stmt, _| {
|
||||
let mut res = Vec::with_capacity(msg_ids.len());
|
||||
for id in msg_ids.iter() {
|
||||
let query_res = stmt.query_row(params![*id as i32], |row| {
|
||||
Ok((row.get::<_, MessageState>(0)?, row.get::<_, Option<Blocked>>(1)?.unwrap_or_default()))
|
||||
let query_res = stmt.query_row(params![*id], |row| {
|
||||
Ok((
|
||||
row.get::<_, MessageState>("state")?,
|
||||
row.get::<_, Option<Blocked>>("blocked")?
|
||||
.unwrap_or_default(),
|
||||
))
|
||||
});
|
||||
if let Err(rusqlite::Error::QueryReturnedNoRows) = query_res {
|
||||
continue;
|
||||
@@ -731,7 +902,7 @@ pub fn markseen_msgs(context: &Context, msg_ids: &[u32]) -> bool {
|
||||
}
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
if msgs.is_err() {
|
||||
@@ -745,12 +916,12 @@ pub fn markseen_msgs(context: &Context, msg_ids: &[u32]) -> bool {
|
||||
if curr_blocked == Blocked::Not {
|
||||
if curr_state == MessageState::InFresh || curr_state == MessageState::InNoticed {
|
||||
update_msg_state(context, *id, MessageState::InSeen);
|
||||
info!(context, "Seen message #{}.", id);
|
||||
info!(context, "Seen message {}.", id);
|
||||
|
||||
job_add(
|
||||
context,
|
||||
Action::MarkseenMsgOnImap,
|
||||
*id as i32,
|
||||
id.to_u32() as i32,
|
||||
Params::new(),
|
||||
0,
|
||||
);
|
||||
@@ -765,24 +936,24 @@ pub fn markseen_msgs(context: &Context, msg_ids: &[u32]) -> bool {
|
||||
if send_event {
|
||||
context.call_cb(Event::MsgsChanged {
|
||||
chat_id: 0,
|
||||
msg_id: 0,
|
||||
msg_id: MsgId::new(0),
|
||||
});
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
pub fn update_msg_state(context: &Context, msg_id: u32, state: MessageState) -> bool {
|
||||
pub fn update_msg_state(context: &Context, msg_id: MsgId, state: MessageState) -> bool {
|
||||
sql::execute(
|
||||
context,
|
||||
&context.sql,
|
||||
"UPDATE msgs SET state=? WHERE id=?;",
|
||||
params![state, msg_id as i32],
|
||||
params![state, msg_id],
|
||||
)
|
||||
.is_ok()
|
||||
}
|
||||
|
||||
pub fn star_msgs(context: &Context, msg_ids: &[u32], star: bool) -> bool {
|
||||
pub fn star_msgs(context: &Context, msg_ids: &[MsgId], star: bool) -> bool {
|
||||
if msg_ids.is_empty() {
|
||||
return false;
|
||||
}
|
||||
@@ -790,7 +961,7 @@ pub fn star_msgs(context: &Context, msg_ids: &[u32], star: bool) -> bool {
|
||||
.sql
|
||||
.prepare("UPDATE msgs SET starred=? WHERE id=?;", |mut stmt, _| {
|
||||
for msg_id in msg_ids.iter() {
|
||||
stmt.execute(params![star as i32, *msg_id as i32])?;
|
||||
stmt.execute(params![star as i32, *msg_id])?;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
@@ -870,8 +1041,8 @@ pub fn get_summarytext_by_raw(
|
||||
|
||||
// Context functions to work with messages
|
||||
|
||||
pub fn exists(context: &Context, msg_id: u32) -> bool {
|
||||
if msg_id <= DC_CHAT_ID_LAST_SPECIAL {
|
||||
pub fn exists(context: &Context, msg_id: MsgId) -> bool {
|
||||
if msg_id.is_special() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -900,7 +1071,7 @@ pub fn update_msg_move_state(context: &Context, rfc724_mid: &str, state: MoveSta
|
||||
.is_ok()
|
||||
}
|
||||
|
||||
pub fn set_msg_failed(context: &Context, msg_id: u32, error: Option<impl AsRef<str>>) {
|
||||
pub fn set_msg_failed(context: &Context, msg_id: MsgId, error: Option<impl AsRef<str>>) {
|
||||
if let Ok(mut msg) = Message::load_from_db(context, msg_id) {
|
||||
if msg.state.can_fail() {
|
||||
msg.state = MessageState::OutFailed;
|
||||
@@ -914,7 +1085,7 @@ pub fn set_msg_failed(context: &Context, msg_id: u32, error: Option<impl AsRef<s
|
||||
context,
|
||||
&context.sql,
|
||||
"UPDATE msgs SET state=?, param=? WHERE id=?;",
|
||||
params![msg.state, msg.param.to_string(), msg_id as i32],
|
||||
params![msg.state, msg.param.to_string(), msg_id],
|
||||
)
|
||||
.is_ok()
|
||||
{
|
||||
@@ -926,38 +1097,39 @@ pub fn set_msg_failed(context: &Context, msg_id: u32, error: Option<impl AsRef<s
|
||||
}
|
||||
}
|
||||
|
||||
/// returns true if an event should be send
|
||||
/// returns Some if an event should be send
|
||||
pub fn mdn_from_ext(
|
||||
context: &Context,
|
||||
from_id: u32,
|
||||
rfc724_mid: &str,
|
||||
timestamp_sent: i64,
|
||||
ret_chat_id: &mut u32,
|
||||
ret_msg_id: &mut u32,
|
||||
) -> bool {
|
||||
if from_id <= 9 || rfc724_mid.is_empty() || *ret_chat_id != 0 || *ret_msg_id != 0 {
|
||||
return false;
|
||||
) -> Option<(u32, MsgId)> {
|
||||
if from_id <= 9 || rfc724_mid.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut read_by_all = false;
|
||||
|
||||
if let Ok((msg_id, chat_id, chat_type, msg_state)) = context.sql.query_row(
|
||||
"SELECT m.id, c.id, c.type, m.state FROM msgs m \
|
||||
LEFT JOIN chats c ON m.chat_id=c.id \
|
||||
WHERE rfc724_mid=? AND from_id=1 \
|
||||
ORDER BY m.id;",
|
||||
concat!(
|
||||
"SELECT",
|
||||
" m.id AS msg_id,",
|
||||
" c.id AS chat_id,",
|
||||
" c.type AS type,",
|
||||
" m.state AS state",
|
||||
" FROM msgs m LEFT JOIN chats c ON m.chat_id=c.id",
|
||||
" WHERE rfc724_mid=? AND from_id=1",
|
||||
" ORDER BY m.id;"
|
||||
),
|
||||
params![rfc724_mid],
|
||||
|row| {
|
||||
Ok((
|
||||
row.get::<_, i32>(0)?,
|
||||
row.get::<_, i32>(1)?,
|
||||
row.get::<_, Chattype>(2)?,
|
||||
row.get::<_, MessageState>(3)?,
|
||||
row.get::<_, MsgId>("msg_id")?,
|
||||
row.get::<_, u32>("chat_id")?,
|
||||
row.get::<_, Chattype>("type")?,
|
||||
row.get::<_, MessageState>("state")?,
|
||||
))
|
||||
},
|
||||
) {
|
||||
*ret_msg_id = msg_id as u32;
|
||||
*ret_chat_id = chat_id as u32;
|
||||
let mut read_by_all = false;
|
||||
|
||||
// if already marked as MDNS_RCVD msgstate_can_fail() returns false.
|
||||
// however, it is important, that ret_msg_id is set above as this
|
||||
@@ -967,20 +1139,20 @@ pub fn mdn_from_ext(
|
||||
.sql
|
||||
.exists(
|
||||
"SELECT contact_id FROM msgs_mdns WHERE msg_id=? AND contact_id=?;",
|
||||
params![*ret_msg_id as i32, from_id as i32,],
|
||||
params![msg_id, from_id as i32,],
|
||||
)
|
||||
.unwrap_or_default();
|
||||
|
||||
if !mdn_already_in_table {
|
||||
context.sql.execute(
|
||||
"INSERT INTO msgs_mdns (msg_id, contact_id, timestamp_sent) VALUES (?, ?, ?);",
|
||||
params![*ret_msg_id as i32, from_id as i32, timestamp_sent],
|
||||
params![msg_id, from_id as i32, timestamp_sent],
|
||||
).unwrap_or_default(); // TODO: better error handling
|
||||
}
|
||||
|
||||
// Normal chat? that's quite easy.
|
||||
if chat_type == Chattype::Single {
|
||||
update_msg_state(context, *ret_msg_id, MessageState::OutMdnRcvd);
|
||||
update_msg_state(context, msg_id, MessageState::OutMdnRcvd);
|
||||
read_by_all = true;
|
||||
} else {
|
||||
// send event about new state
|
||||
@@ -989,7 +1161,7 @@ pub fn mdn_from_ext(
|
||||
.query_get_value::<_, isize>(
|
||||
context,
|
||||
"SELECT COUNT(*) FROM msgs_mdns WHERE msg_id=?;",
|
||||
params![*ret_msg_id as i32],
|
||||
params![msg_id],
|
||||
)
|
||||
.unwrap_or_default() as usize;
|
||||
/*
|
||||
@@ -1005,16 +1177,19 @@ pub fn mdn_from_ext(
|
||||
(S=Sender, R=Recipient)
|
||||
*/
|
||||
// for rounding, SELF is already included!
|
||||
let soll_cnt = (chat::get_chat_contact_cnt(context, *ret_chat_id) + 1) / 2;
|
||||
let soll_cnt = (chat::get_chat_contact_cnt(context, chat_id) + 1) / 2;
|
||||
if ist_cnt >= soll_cnt {
|
||||
update_msg_state(context, *ret_msg_id, MessageState::OutMdnRcvd);
|
||||
update_msg_state(context, msg_id, MessageState::OutMdnRcvd);
|
||||
read_by_all = true;
|
||||
} // else wait for more receipts
|
||||
}
|
||||
}
|
||||
return match read_by_all {
|
||||
true => Some((chat_id, msg_id)),
|
||||
false => None,
|
||||
};
|
||||
}
|
||||
|
||||
read_by_all
|
||||
None
|
||||
}
|
||||
|
||||
/// The number of messages assigned to real chat (!=deaddrop, !=trash)
|
||||
@@ -1068,7 +1243,7 @@ pub fn rfc724_mid_cnt(context: &Context, rfc724_mid: &str) -> libc::c_int {
|
||||
pub(crate) fn rfc724_mid_exists(
|
||||
context: &Context,
|
||||
rfc724_mid: &str,
|
||||
) -> Result<(String, u32, u32), Error> {
|
||||
) -> Result<(String, u32, MsgId), Error> {
|
||||
ensure!(!rfc724_mid.is_empty(), "empty rfc724_mid");
|
||||
|
||||
context.sql.query_row(
|
||||
@@ -1077,7 +1252,7 @@ pub(crate) fn rfc724_mid_exists(
|
||||
|row| {
|
||||
let server_folder = row.get::<_, Option<String>>(0)?.unwrap_or_default();
|
||||
let server_uid = row.get(1)?;
|
||||
let msg_id = row.get(2)?;
|
||||
let msg_id: MsgId = row.get(2)?;
|
||||
|
||||
Ok((server_folder, server_uid, msg_id))
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user