mirror of
https://github.com/chatmail/core.git
synced 2026-04-17 21:46: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
@@ -22,11 +22,13 @@ use std::sync::RwLock;
|
||||
use libc::uintptr_t;
|
||||
use num_traits::{FromPrimitive, ToPrimitive};
|
||||
|
||||
use deltachat::constants::DC_MSG_ID_LAST_SPECIAL;
|
||||
use deltachat::contact::Contact;
|
||||
use deltachat::context::Context;
|
||||
use deltachat::dc_tools::{
|
||||
as_path, dc_strdup, to_opt_string_lossy, to_string_lossy, OsStrExt, StrExt,
|
||||
};
|
||||
use deltachat::message::MsgId;
|
||||
use deltachat::stock::StockMessage;
|
||||
use deltachat::*;
|
||||
|
||||
@@ -141,9 +143,12 @@ impl ContextWrapper {
|
||||
| Event::IncomingMsg { chat_id, msg_id }
|
||||
| Event::MsgDelivered { chat_id, msg_id }
|
||||
| Event::MsgFailed { chat_id, msg_id }
|
||||
| Event::MsgRead { chat_id, msg_id } => {
|
||||
ffi_cb(self, event_id, chat_id as uintptr_t, msg_id as uintptr_t)
|
||||
}
|
||||
| Event::MsgRead { chat_id, msg_id } => ffi_cb(
|
||||
self,
|
||||
event_id,
|
||||
chat_id as uintptr_t,
|
||||
msg_id.to_u32() as uintptr_t,
|
||||
),
|
||||
Event::ChatModified(chat_id) => ffi_cb(self, event_id, chat_id as uintptr_t, 0),
|
||||
Event::ContactsChanged(id) | Event::LocationChanged(id) => {
|
||||
let id = id.unwrap_or_default();
|
||||
@@ -681,7 +686,8 @@ pub unsafe extern "C" fn dc_create_chat_by_msg_id(context: *mut dc_context_t, ms
|
||||
let ffi_context = &*context;
|
||||
ffi_context
|
||||
.with_inner(|ctx| {
|
||||
chat::create_by_msg_id(ctx, msg_id).unwrap_or_log_default(ctx, "Failed to create chat")
|
||||
chat::create_by_msg_id(ctx, MsgId::new(msg_id))
|
||||
.unwrap_or_log_default(ctx, "Failed to create chat")
|
||||
})
|
||||
.unwrap_or(0)
|
||||
}
|
||||
@@ -736,6 +742,7 @@ pub unsafe extern "C" fn dc_prepare_msg(
|
||||
chat::prepare_msg(ctx, chat_id, &mut ffi_msg.message)
|
||||
.unwrap_or_log_default(ctx, "Failed to prepare message")
|
||||
})
|
||||
.map(|msg_id| msg_id.to_u32())
|
||||
.unwrap_or(0)
|
||||
}
|
||||
|
||||
@@ -756,6 +763,7 @@ pub unsafe extern "C" fn dc_send_msg(
|
||||
chat::send_msg(ctx, chat_id, &mut ffi_msg.message)
|
||||
.unwrap_or_log_default(ctx, "Failed to send message")
|
||||
})
|
||||
.map(|msg_id| msg_id.to_u32())
|
||||
.unwrap_or(0)
|
||||
}
|
||||
|
||||
@@ -774,6 +782,7 @@ pub unsafe extern "C" fn dc_send_text_msg(
|
||||
ffi_context
|
||||
.with_inner(|ctx| {
|
||||
chat::send_text_msg(ctx, chat_id, text_to_send)
|
||||
.map(|msg_id| msg_id.to_u32())
|
||||
.unwrap_or_log_default(ctx, "Failed to send text message")
|
||||
})
|
||||
.unwrap_or(0)
|
||||
@@ -838,9 +847,19 @@ pub unsafe extern "C" fn dc_get_chat_msgs(
|
||||
return ptr::null_mut();
|
||||
}
|
||||
let ffi_context = &*context;
|
||||
let marker_flag = if marker1before <= DC_MSG_ID_LAST_SPECIAL {
|
||||
None
|
||||
} else {
|
||||
Some(MsgId::new(marker1before))
|
||||
};
|
||||
ffi_context
|
||||
.with_inner(|ctx| {
|
||||
let arr = dc_array_t::from(chat::get_chat_msgs(ctx, chat_id, flags, marker1before));
|
||||
let arr = dc_array_t::from(
|
||||
chat::get_chat_msgs(ctx, chat_id, flags, marker_flag)
|
||||
.iter()
|
||||
.map(|msg_id| msg_id.to_u32())
|
||||
.collect::<Vec<u32>>(),
|
||||
);
|
||||
Box::into_raw(Box::new(arr))
|
||||
})
|
||||
.unwrap_or_else(|_| ptr::null_mut())
|
||||
@@ -884,7 +903,12 @@ pub unsafe extern "C" fn dc_get_fresh_msgs(
|
||||
let ffi_context = &*context;
|
||||
ffi_context
|
||||
.with_inner(|ctx| {
|
||||
let arr = dc_array_t::from(ctx.get_fresh_msgs());
|
||||
let arr = dc_array_t::from(
|
||||
ctx.get_fresh_msgs()
|
||||
.iter()
|
||||
.map(|msg_id| msg_id.to_u32())
|
||||
.collect::<Vec<u32>>(),
|
||||
);
|
||||
Box::into_raw(Box::new(arr))
|
||||
})
|
||||
.unwrap_or_else(|_| ptr::null_mut())
|
||||
@@ -946,13 +970,12 @@ pub unsafe extern "C" fn dc_get_chat_media(
|
||||
from_prim(or_msg_type3).expect(&format!("incorrect or_msg_type3 = {}", or_msg_type3));
|
||||
ffi_context
|
||||
.with_inner(|ctx| {
|
||||
let arr = dc_array_t::from(chat::get_chat_media(
|
||||
ctx,
|
||||
chat_id,
|
||||
msg_type,
|
||||
or_msg_type2,
|
||||
or_msg_type3,
|
||||
));
|
||||
let arr = dc_array_t::from(
|
||||
chat::get_chat_media(ctx, chat_id, msg_type, or_msg_type2, or_msg_type3)
|
||||
.iter()
|
||||
.map(|msg_id| msg_id.to_u32())
|
||||
.collect::<Vec<u32>>(),
|
||||
);
|
||||
Box::into_raw(Box::new(arr))
|
||||
})
|
||||
.unwrap_or_else(|_| ptr::null_mut())
|
||||
@@ -985,7 +1008,16 @@ pub unsafe extern "C" fn dc_get_next_media(
|
||||
from_prim(or_msg_type3).expect(&format!("incorrect or_msg_type3 = {}", or_msg_type3));
|
||||
ffi_context
|
||||
.with_inner(|ctx| {
|
||||
chat::get_next_media(ctx, msg_id, direction, msg_type, or_msg_type2, or_msg_type3)
|
||||
chat::get_next_media(
|
||||
ctx,
|
||||
MsgId::new(msg_id),
|
||||
direction,
|
||||
msg_type,
|
||||
or_msg_type2,
|
||||
or_msg_type3,
|
||||
)
|
||||
.map(|msg_id| msg_id.to_u32())
|
||||
.unwrap_or(0)
|
||||
})
|
||||
.unwrap_or(0)
|
||||
}
|
||||
@@ -1056,7 +1088,12 @@ pub unsafe extern "C" fn dc_search_msgs(
|
||||
let ffi_context = &*context;
|
||||
ffi_context
|
||||
.with_inner(|ctx| {
|
||||
let arr = dc_array_t::from(ctx.search_msgs(chat_id, to_string_lossy(query)));
|
||||
let arr = dc_array_t::from(
|
||||
ctx.search_msgs(chat_id, to_string_lossy(query))
|
||||
.iter()
|
||||
.map(|msg_id| msg_id.to_u32())
|
||||
.collect::<Vec<u32>>(),
|
||||
);
|
||||
Box::into_raw(Box::new(arr))
|
||||
})
|
||||
.unwrap_or_else(|_| ptr::null_mut())
|
||||
@@ -1208,7 +1245,7 @@ pub unsafe extern "C" fn dc_get_msg_info(
|
||||
}
|
||||
let ffi_context = &*context;
|
||||
ffi_context
|
||||
.with_inner(|ctx| message::get_msg_info(ctx, msg_id).strdup())
|
||||
.with_inner(|ctx| message::get_msg_info(ctx, MsgId::new(msg_id)).strdup())
|
||||
.unwrap_or_else(|_| ptr::null_mut())
|
||||
}
|
||||
|
||||
@@ -1224,7 +1261,7 @@ pub unsafe extern "C" fn dc_get_mime_headers(
|
||||
let ffi_context = &*context;
|
||||
ffi_context
|
||||
.with_inner(|ctx| {
|
||||
message::get_mime_headers(ctx, msg_id)
|
||||
message::get_mime_headers(ctx, MsgId::new(msg_id))
|
||||
.map(|s| s.strdup())
|
||||
.unwrap_or_else(|| ptr::null_mut())
|
||||
})
|
||||
@@ -1242,11 +1279,10 @@ pub unsafe extern "C" fn dc_delete_msgs(
|
||||
return;
|
||||
}
|
||||
let ffi_context = &*context;
|
||||
|
||||
let ids = std::slice::from_raw_parts(msg_ids, msg_cnt as usize);
|
||||
|
||||
let msg_ids: Vec<MsgId> = ids.iter().map(|id| MsgId::new(*id)).collect();
|
||||
ffi_context
|
||||
.with_inner(|ctx| message::delete_msgs(ctx, ids))
|
||||
.with_inner(|ctx| message::delete_msgs(ctx, &msg_ids[..]))
|
||||
.unwrap_or(())
|
||||
}
|
||||
|
||||
@@ -1266,11 +1302,11 @@ pub unsafe extern "C" fn dc_forward_msgs(
|
||||
return;
|
||||
}
|
||||
let ids = std::slice::from_raw_parts(msg_ids, msg_cnt as usize);
|
||||
|
||||
let msg_ids: Vec<MsgId> = ids.iter().map(|id| MsgId::new(*id)).collect();
|
||||
let ffi_context = &*context;
|
||||
ffi_context
|
||||
.with_inner(|ctx| {
|
||||
chat::forward_msgs(ctx, ids, chat_id)
|
||||
chat::forward_msgs(ctx, &msg_ids[..], chat_id)
|
||||
.unwrap_or_log_default(ctx, "Failed to forward message")
|
||||
})
|
||||
.unwrap_or_default()
|
||||
@@ -1299,10 +1335,14 @@ pub unsafe extern "C" fn dc_markseen_msgs(
|
||||
return;
|
||||
}
|
||||
let ids = std::slice::from_raw_parts(msg_ids, msg_cnt as usize);
|
||||
|
||||
let msg_ids: Vec<MsgId> = ids
|
||||
.iter()
|
||||
.filter(|id| **id > DC_MSG_ID_LAST_SPECIAL)
|
||||
.map(|id| MsgId::new(*id))
|
||||
.collect();
|
||||
let ffi_context = &*context;
|
||||
ffi_context
|
||||
.with_inner(|ctx| message::markseen_msgs(ctx, ids))
|
||||
.with_inner(|ctx| message::markseen_msgs(ctx, &msg_ids[..]))
|
||||
.ok();
|
||||
}
|
||||
|
||||
@@ -1317,12 +1357,11 @@ pub unsafe extern "C" fn dc_star_msgs(
|
||||
eprintln!("ignoring careless call to dc_star_msgs()");
|
||||
return;
|
||||
}
|
||||
|
||||
let ids = std::slice::from_raw_parts(msg_ids, msg_cnt as usize);
|
||||
|
||||
let msg_ids: Vec<MsgId> = ids.iter().map(|id| MsgId::new(*id)).collect();
|
||||
let ffi_context = &*context;
|
||||
ffi_context
|
||||
.with_inner(|ctx| message::star_msgs(ctx, ids, star == 1))
|
||||
.with_inner(|ctx| message::star_msgs(ctx, &msg_ids[..], star == 1))
|
||||
.ok();
|
||||
}
|
||||
|
||||
@@ -1335,7 +1374,7 @@ pub unsafe extern "C" fn dc_get_msg(context: *mut dc_context_t, msg_id: u32) ->
|
||||
let ffi_context = &*context;
|
||||
ffi_context
|
||||
.with_inner(|ctx| {
|
||||
let message = match message::Message::load_from_db(ctx, msg_id) {
|
||||
let message = match message::Message::load_from_db(ctx, MsgId::new(msg_id)) {
|
||||
Ok(msg) => msg,
|
||||
Err(e) => {
|
||||
error!(ctx, "Error getting msg #{}: {}", msg_id, e);
|
||||
@@ -1622,7 +1661,8 @@ pub unsafe extern "C" fn dc_continue_key_transfer(
|
||||
let ffi_context = &*context;
|
||||
ffi_context
|
||||
.with_inner(|ctx| {
|
||||
match imex::continue_key_transfer(ctx, msg_id, &to_string_lossy(setup_code)) {
|
||||
match imex::continue_key_transfer(ctx, MsgId::new(msg_id), &to_string_lossy(setup_code))
|
||||
{
|
||||
Ok(()) => 1,
|
||||
Err(err) => {
|
||||
error!(ctx, "dc_continue_key_transfer: {}", err);
|
||||
@@ -2033,7 +2073,11 @@ pub unsafe extern "C" fn dc_chatlist_get_msg_id(
|
||||
return 0;
|
||||
}
|
||||
let ffi_list = &*chatlist;
|
||||
ffi_list.list.get_msg_id(index as usize)
|
||||
ffi_list
|
||||
.list
|
||||
.get_msg_id(index as usize)
|
||||
.map(|msg_id| msg_id.to_u32())
|
||||
.unwrap_or(0)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -2275,7 +2319,7 @@ pub unsafe extern "C" fn dc_msg_get_id(msg: *mut dc_msg_t) -> u32 {
|
||||
return 0;
|
||||
}
|
||||
let ffi_msg = &*msg;
|
||||
ffi_msg.message.get_id()
|
||||
ffi_msg.message.get_id().to_u32()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
|
||||
@@ -14,7 +14,7 @@ use deltachat::imex::*;
|
||||
use deltachat::job::*;
|
||||
use deltachat::location;
|
||||
use deltachat::lot::LotState;
|
||||
use deltachat::message::{self, Message, MessageState};
|
||||
use deltachat::message::{self, Message, MessageState, MsgId};
|
||||
use deltachat::peerstate::*;
|
||||
use deltachat::qr::*;
|
||||
use deltachat::sql;
|
||||
@@ -86,7 +86,7 @@ pub unsafe fn dc_reset_tables(context: &Context, bits: i32) -> i32 {
|
||||
|
||||
context.call_cb(Event::MsgsChanged {
|
||||
chat_id: 0,
|
||||
msg_id: 0,
|
||||
msg_id: MsgId::new(0),
|
||||
});
|
||||
|
||||
1
|
||||
@@ -170,7 +170,7 @@ fn poke_spec(context: &Context, spec: *const libc::c_char) -> libc::c_int {
|
||||
if read_cnt > 0 {
|
||||
context.call_cb(Event::MsgsChanged {
|
||||
chat_id: 0,
|
||||
msg_id: 0,
|
||||
msg_id: MsgId::new(0),
|
||||
});
|
||||
}
|
||||
1
|
||||
@@ -194,7 +194,7 @@ unsafe fn log_msg(context: &Context, prefix: impl AsRef<str>, msg: &Message) {
|
||||
context,
|
||||
"{}#{}{}{}: {} (Contact#{}): {} {}{}{}{}{} [{}]",
|
||||
prefix.as_ref(),
|
||||
msg.get_id() as libc::c_int,
|
||||
msg.get_id(),
|
||||
if msg.get_showpadlock() { "🔒" } else { "" },
|
||||
if msg.has_location() { "📍" } else { "" },
|
||||
&contact_name,
|
||||
@@ -221,17 +221,17 @@ unsafe fn log_msg(context: &Context, prefix: impl AsRef<str>, msg: &Message) {
|
||||
);
|
||||
}
|
||||
|
||||
unsafe fn log_msglist(context: &Context, msglist: &Vec<u32>) -> Result<(), Error> {
|
||||
unsafe fn log_msglist(context: &Context, msglist: &Vec<MsgId>) -> Result<(), Error> {
|
||||
let mut lines_out = 0;
|
||||
for &msg_id in msglist {
|
||||
if msg_id == 9 as libc::c_uint {
|
||||
if msg_id.is_daymarker() {
|
||||
info!(
|
||||
context,
|
||||
"--------------------------------------------------------------------------------"
|
||||
);
|
||||
|
||||
lines_out += 1
|
||||
} else if msg_id > 0 {
|
||||
} else if !msg_id.is_special() {
|
||||
if lines_out == 0 {
|
||||
info!(
|
||||
context,
|
||||
@@ -418,7 +418,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
},
|
||||
"get-setupcodebegin" => {
|
||||
ensure!(!arg1.is_empty(), "Argument <msg-id> missing.");
|
||||
let msg_id: u32 = arg1.parse()?;
|
||||
let msg_id: MsgId = MsgId::new(arg1.parse()?);
|
||||
let msg = Message::load_from_db(context, msg_id)?;
|
||||
if msg.is_setupmessage() {
|
||||
let setupcodebegin = msg.get_setupcodebegin(context);
|
||||
@@ -436,7 +436,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
!arg1.is_empty() && !arg2.is_empty(),
|
||||
"Arguments <msg-id> <setup-code> expected"
|
||||
);
|
||||
continue_key_transfer(context, arg1.parse()?, &arg2)?;
|
||||
continue_key_transfer(context, MsgId::new(arg1.parse()?), &arg2)?;
|
||||
}
|
||||
"has-backup" => {
|
||||
has_backup(context, blobdir)?;
|
||||
@@ -581,7 +581,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
ensure!(sel_chat.is_some(), "Failed to select chat");
|
||||
let sel_chat = sel_chat.as_ref().unwrap();
|
||||
|
||||
let msglist = chat::get_chat_msgs(context, sel_chat.get_id(), 0x1, 0);
|
||||
let msglist = chat::get_chat_msgs(context, sel_chat.get_id(), 0x1, None);
|
||||
let temp2 = sel_chat.get_subtitle(context);
|
||||
let temp_name = sel_chat.get_name();
|
||||
info!(
|
||||
@@ -617,7 +617,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
}
|
||||
"createchatbymsg" => {
|
||||
ensure!(!arg1.is_empty(), "Argument <msg-id> missing");
|
||||
let msg_id: u32 = arg1.parse()?;
|
||||
let msg_id = MsgId::new(arg1.parse()?);
|
||||
let chat_id = chat::create_by_msg_id(context, msg_id)?;
|
||||
let chat = Chat::load_from_db(context, chat_id)?;
|
||||
|
||||
@@ -849,7 +849,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
}
|
||||
"msginfo" => {
|
||||
ensure!(!arg1.is_empty(), "Argument <msg-id> missing.");
|
||||
let id = arg1.parse()?;
|
||||
let id = MsgId::new(arg1.parse()?);
|
||||
let res = message::get_msg_info(context, id);
|
||||
println!("{}", res);
|
||||
}
|
||||
@@ -865,27 +865,27 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
"Arguments <msg-id> <chat-id> expected"
|
||||
);
|
||||
|
||||
let mut msg_ids = [0; 1];
|
||||
let mut msg_ids = [MsgId::new(0); 1];
|
||||
let chat_id = arg2.parse()?;
|
||||
msg_ids[0] = arg1.parse()?;
|
||||
msg_ids[0] = MsgId::new(arg1.parse()?);
|
||||
chat::forward_msgs(context, &msg_ids, chat_id)?;
|
||||
}
|
||||
"markseen" => {
|
||||
ensure!(!arg1.is_empty(), "Argument <msg-id> missing.");
|
||||
let mut msg_ids = [0; 1];
|
||||
msg_ids[0] = arg1.parse()?;
|
||||
let mut msg_ids = [MsgId::new(0); 1];
|
||||
msg_ids[0] = MsgId::new(arg1.parse()?);
|
||||
message::markseen_msgs(context, &msg_ids);
|
||||
}
|
||||
"star" | "unstar" => {
|
||||
ensure!(!arg1.is_empty(), "Argument <msg-id> missing.");
|
||||
let mut msg_ids = [0; 1];
|
||||
msg_ids[0] = arg1.parse()?;
|
||||
let mut msg_ids = [MsgId::new(0); 1];
|
||||
msg_ids[0] = MsgId::new(arg1.parse()?);
|
||||
message::star_msgs(context, &msg_ids, arg0 == "star");
|
||||
}
|
||||
"delmsg" => {
|
||||
ensure!(!arg1.is_empty(), "Argument <msg-id> missing.");
|
||||
let mut ids = [0; 1];
|
||||
ids[0] = arg1.parse()?;
|
||||
let mut ids = [MsgId::new(0); 1];
|
||||
ids[0] = MsgId::new(arg1.parse()?);
|
||||
message::delete_msgs(context, &ids);
|
||||
}
|
||||
"listcontacts" | "contacts" | "listverified" => {
|
||||
|
||||
@@ -193,6 +193,8 @@ impl<'a> BlobObject<'a> {
|
||||
/// the database. Do not use this unless you're about to store
|
||||
/// this string in the database or [Params]. Eventually even
|
||||
/// those conversions should be handled by the type system.
|
||||
///
|
||||
/// [Params]: crate::param::Params
|
||||
pub fn as_name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
340
src/chat.rs
340
src/chat.rs
@@ -1,5 +1,7 @@
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use itertools::Itertools;
|
||||
|
||||
use crate::blob::{BlobErrorKind, BlobObject};
|
||||
use crate::chatlist::*;
|
||||
use crate::config::*;
|
||||
@@ -11,7 +13,7 @@ use crate::dc_tools::*;
|
||||
use crate::error::Error;
|
||||
use crate::events::Event;
|
||||
use crate::job::*;
|
||||
use crate::message::{self, Message, MessageState};
|
||||
use crate::message::{self, InvalidMsgId, Message, MessageState, MsgId};
|
||||
use crate::param::*;
|
||||
use crate::sql::{self, Sql};
|
||||
use crate::stock::StockMessage;
|
||||
@@ -238,7 +240,7 @@ impl Chat {
|
||||
context: &Context,
|
||||
msg: &mut Message,
|
||||
timestamp: i64,
|
||||
) -> Result<u32, Error> {
|
||||
) -> Result<MsgId, Error> {
|
||||
let mut do_guarantee_e2ee: bool;
|
||||
let e2ee_enabled: bool;
|
||||
let mut new_references = "".into();
|
||||
@@ -252,7 +254,7 @@ impl Chat {
|
||||
|| self.typ == Chattype::VerifiedGroup)
|
||||
{
|
||||
error!(context, "Cannot send to chat type #{}.", self.typ,);
|
||||
return Ok(0);
|
||||
bail!("Cannot set to chat type #{}", self.typ);
|
||||
}
|
||||
|
||||
if (self.typ == Chattype::Group || self.typ == Chattype::VerifiedGroup)
|
||||
@@ -262,7 +264,7 @@ impl Chat {
|
||||
context,
|
||||
Event::ErrorSelfNotInGroup("Cannot send message; self not in group.".into())
|
||||
);
|
||||
return Ok(0);
|
||||
bail!("Cannot set message; self not in group.");
|
||||
}
|
||||
|
||||
if let Some(from) = context.get_config(Config::ConfiguredAddr) {
|
||||
@@ -286,7 +288,10 @@ impl Chat {
|
||||
context,
|
||||
"Cannot send message, contact for chat #{} not found.", self.id,
|
||||
);
|
||||
return Ok(0);
|
||||
bail!(
|
||||
"Cannot set message, contact for chat #{} not found.",
|
||||
self.id
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if self.typ == Chattype::Group || self.typ == Chattype::VerifiedGroup {
|
||||
@@ -472,7 +477,7 @@ impl Chat {
|
||||
error!(context, "Cannot send message, not configured.",);
|
||||
}
|
||||
|
||||
Ok(msg_id)
|
||||
Ok(MsgId::new(msg_id))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -494,7 +499,7 @@ impl Chat {
|
||||
/// to the message and, depending on the contacts origin, messages from the
|
||||
/// same group may be shown or not - so, all in all, it is fine to show the
|
||||
/// contact name only.
|
||||
pub fn create_by_msg_id(context: &Context, msg_id: u32) -> Result<u32, Error> {
|
||||
pub fn create_by_msg_id(context: &Context, msg_id: MsgId) -> Result<u32, Error> {
|
||||
let mut chat_id = 0;
|
||||
let mut send_event = false;
|
||||
|
||||
@@ -514,7 +519,7 @@ pub fn create_by_msg_id(context: &Context, msg_id: u32) -> Result<u32, Error> {
|
||||
if send_event {
|
||||
context.call_cb(Event::MsgsChanged {
|
||||
chat_id: 0,
|
||||
msg_id: 0,
|
||||
msg_id: MsgId::new(0),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -557,7 +562,7 @@ pub fn create_by_contact_id(context: &Context, contact_id: u32) -> Result<u32, E
|
||||
|
||||
context.call_cb(Event::MsgsChanged {
|
||||
chat_id: 0,
|
||||
msg_id: 0,
|
||||
msg_id: MsgId::new(0),
|
||||
});
|
||||
|
||||
Ok(chat_id)
|
||||
@@ -643,7 +648,7 @@ pub fn prepare_msg<'a>(
|
||||
context: &'a Context,
|
||||
chat_id: u32,
|
||||
msg: &mut Message,
|
||||
) -> Result<u32, Error> {
|
||||
) -> Result<MsgId, Error> {
|
||||
ensure!(
|
||||
chat_id > DC_CHAT_ID_LAST_SPECIAL,
|
||||
"Cannot prepare message for special chat"
|
||||
@@ -672,8 +677,8 @@ pub fn msgtype_has_file(msgtype: Viewtype) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
fn prepare_msg_common(context: &Context, chat_id: u32, msg: &mut Message) -> Result<u32, Error> {
|
||||
msg.id = 0;
|
||||
fn prepare_msg_common(context: &Context, chat_id: u32, msg: &mut Message) -> Result<MsgId, Error> {
|
||||
msg.id = MsgId::new_unset();
|
||||
if msg.type_0 == Viewtype::Text {
|
||||
// the caller should check if the message text is empty
|
||||
} else if msgtype_has_file(msg.type_0) {
|
||||
@@ -779,7 +784,7 @@ pub fn unarchive(context: &Context, chat_id: u32) -> Result<(), Error> {
|
||||
/// 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.
|
||||
pub fn send_msg(context: &Context, chat_id: u32, msg: &mut Message) -> Result<u32, Error> {
|
||||
pub fn send_msg(context: &Context, chat_id: u32, msg: &mut Message) -> Result<MsgId, Error> {
|
||||
// dc_prepare_msg() leaves the message state to OutPreparing, we
|
||||
// only have to change the state to OutPending in this case.
|
||||
// Otherwise we still have to prepare the message, which will set
|
||||
@@ -811,12 +816,17 @@ pub fn send_msg(context: &Context, chat_id: u32, msg: &mut Message) -> Result<u3
|
||||
let forwards = msg.param.get(Param::PrepForwards);
|
||||
if let Some(forwards) = forwards {
|
||||
for forward in forwards.split(' ') {
|
||||
let id: i32 = forward.parse().unwrap_or_default();
|
||||
if 0 == id {
|
||||
// avoid hanging if user tampers with db
|
||||
break;
|
||||
} else if let Ok(mut copy) = Message::load_from_db(context, id as u32) {
|
||||
send_msg(context, 0, &mut copy)?;
|
||||
match forward
|
||||
.parse::<u32>()
|
||||
.map_err(|_| InvalidMsgId)
|
||||
.map(|id| MsgId::new(id))
|
||||
{
|
||||
Ok(msg_id) => {
|
||||
if let Ok(mut msg) = Message::load_from_db(context, msg_id) {
|
||||
send_msg(context, 0, &mut msg)?;
|
||||
};
|
||||
}
|
||||
Err(_) => (),
|
||||
}
|
||||
}
|
||||
msg.param.remove(Param::PrepForwards);
|
||||
@@ -827,7 +837,11 @@ pub fn send_msg(context: &Context, chat_id: u32, msg: &mut Message) -> Result<u3
|
||||
Ok(msg.id)
|
||||
}
|
||||
|
||||
pub fn send_text_msg(context: &Context, chat_id: u32, text_to_send: String) -> Result<u32, Error> {
|
||||
pub fn send_text_msg(
|
||||
context: &Context,
|
||||
chat_id: u32,
|
||||
text_to_send: String,
|
||||
) -> Result<MsgId, Error> {
|
||||
ensure!(
|
||||
chat_id > DC_CHAT_ID_LAST_SPECIAL,
|
||||
"bad chat_id = {} <= 9",
|
||||
@@ -851,7 +865,10 @@ pub fn set_draft(context: &Context, chat_id: u32, msg: Option<&mut Message>) {
|
||||
};
|
||||
|
||||
if changed {
|
||||
context.call_cb(Event::MsgsChanged { chat_id, msg_id: 0 });
|
||||
context.call_cb(Event::MsgsChanged {
|
||||
chat_id,
|
||||
msg_id: MsgId::new(0),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -859,12 +876,13 @@ pub fn set_draft(context: &Context, chat_id: u32, msg: Option<&mut Message>) {
|
||||
///
|
||||
/// Return {true}, if message was deleted, {false} otherwise.
|
||||
fn maybe_delete_draft(context: &Context, chat_id: u32) -> bool {
|
||||
let draft = get_draft_msg_id(context, chat_id);
|
||||
if draft != 0 {
|
||||
Message::delete_from_db(context, draft);
|
||||
return true;
|
||||
match get_draft_msg_id(context, chat_id) {
|
||||
Some(msg_id) => {
|
||||
Message::delete_from_db(context, msg_id);
|
||||
true
|
||||
}
|
||||
None => false,
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// Set provided message as draft message for specified chat.
|
||||
@@ -916,97 +934,113 @@ fn set_draft_raw(context: &Context, chat_id: u32, msg: &mut Message) -> bool {
|
||||
deleted || set
|
||||
}
|
||||
|
||||
fn get_draft_msg_id(context: &Context, chat_id: u32) -> u32 {
|
||||
context
|
||||
.sql
|
||||
.query_get_value::<_, i32>(
|
||||
context,
|
||||
"SELECT id FROM msgs WHERE chat_id=? AND state=?;",
|
||||
params![chat_id as i32, MessageState::OutDraft],
|
||||
)
|
||||
.unwrap_or_default() as u32
|
||||
fn get_draft_msg_id(context: &Context, chat_id: u32) -> Option<MsgId> {
|
||||
context.sql.query_get_value::<_, MsgId>(
|
||||
context,
|
||||
"SELECT id FROM msgs WHERE chat_id=? AND state=?;",
|
||||
params![chat_id as i32, MessageState::OutDraft],
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_draft(context: &Context, chat_id: u32) -> Result<Option<Message>, Error> {
|
||||
if chat_id <= DC_CHAT_ID_LAST_SPECIAL {
|
||||
return Ok(None);
|
||||
}
|
||||
let draft_msg_id = get_draft_msg_id(context, chat_id);
|
||||
if draft_msg_id == 0 {
|
||||
return Ok(None);
|
||||
match get_draft_msg_id(context, chat_id) {
|
||||
Some(draft_msg_id) => Ok(Some(Message::load_from_db(context, draft_msg_id)?)),
|
||||
None => Ok(None),
|
||||
}
|
||||
Ok(Some(Message::load_from_db(context, draft_msg_id)?))
|
||||
}
|
||||
|
||||
pub fn get_chat_msgs(context: &Context, chat_id: u32, flags: u32, marker1before: u32) -> Vec<u32> {
|
||||
let mut ret = Vec::new();
|
||||
|
||||
let mut last_day = 0;
|
||||
let cnv_to_local = dc_gm2local_offset();
|
||||
|
||||
let process_row = |row: &rusqlite::Row| Ok((row.get::<_, i32>(0)?, row.get::<_, i64>(1)?));
|
||||
pub fn get_chat_msgs(
|
||||
context: &Context,
|
||||
chat_id: u32,
|
||||
flags: u32,
|
||||
marker1before: Option<MsgId>,
|
||||
) -> Vec<MsgId> {
|
||||
let process_row =
|
||||
|row: &rusqlite::Row| Ok((row.get::<_, MsgId>("id")?, row.get::<_, i64>("timestamp")?));
|
||||
let process_rows = |rows: rusqlite::MappedRows<_>| {
|
||||
let mut ret = Vec::new();
|
||||
let mut last_day = 0;
|
||||
let cnv_to_local = dc_gm2local_offset();
|
||||
for row in rows {
|
||||
let (curr_id, ts) = row?;
|
||||
if curr_id as u32 == marker1before {
|
||||
ret.push(DC_MSG_ID_MARKER1);
|
||||
if let Some(marker_id) = marker1before {
|
||||
if curr_id == marker_id {
|
||||
ret.push(MsgId::new(DC_MSG_ID_MARKER1));
|
||||
}
|
||||
}
|
||||
if 0 != flags & 0x1 {
|
||||
if (flags & DC_GCM_ADDDAYMARKER) != 0 {
|
||||
let curr_local_timestamp = ts + cnv_to_local;
|
||||
let curr_day = curr_local_timestamp / 86400;
|
||||
if curr_day != last_day {
|
||||
ret.push(DC_MSG_ID_LAST_SPECIAL);
|
||||
ret.push(MsgId::new(DC_MSG_ID_DAYMARKER));
|
||||
last_day = curr_day;
|
||||
}
|
||||
}
|
||||
ret.push(curr_id as u32);
|
||||
ret.push(curr_id);
|
||||
}
|
||||
Ok(())
|
||||
Ok(ret)
|
||||
};
|
||||
|
||||
let success = if chat_id == 1 {
|
||||
let success = if chat_id == DC_CHAT_ID_DEADDROP {
|
||||
let show_emails = context.get_config_int(Config::ShowEmails);
|
||||
context.sql.query_map(
|
||||
"SELECT m.id, m.timestamp FROM msgs m \
|
||||
LEFT JOIN chats ON m.chat_id=chats.id \
|
||||
LEFT JOIN contacts ON m.from_id=contacts.id WHERE m.from_id!=1 \
|
||||
AND m.from_id!=2 \
|
||||
AND m.hidden=0 \
|
||||
AND chats.blocked=2 \
|
||||
AND contacts.blocked=0 \
|
||||
AND m.msgrmsg>=? \
|
||||
ORDER BY m.timestamp,m.id;",
|
||||
concat!(
|
||||
"SELECT m.id AS id, m.timestamp AS timestamp",
|
||||
" FROM msgs m",
|
||||
" LEFT JOIN chats",
|
||||
" ON m.chat_id=chats.id",
|
||||
" LEFT JOIN contacts",
|
||||
" ON m.from_id=contacts.id",
|
||||
" WHERE m.from_id!=1",
|
||||
" AND m.from_id!=2",
|
||||
" AND m.hidden=0",
|
||||
" AND chats.blocked=2",
|
||||
" AND contacts.blocked=0",
|
||||
" AND m.msgrmsg>=?",
|
||||
" ORDER BY m.timestamp,m.id;"
|
||||
),
|
||||
params![if show_emails == 2 { 0 } else { 1 }],
|
||||
process_row,
|
||||
process_rows,
|
||||
)
|
||||
} else if chat_id == 5 {
|
||||
} else if chat_id == DC_CHAT_ID_STARRED {
|
||||
context.sql.query_map(
|
||||
"SELECT m.id, m.timestamp FROM msgs m \
|
||||
LEFT JOIN contacts ct ON m.from_id=ct.id WHERE m.starred=1 \
|
||||
AND m.hidden=0 \
|
||||
AND ct.blocked=0 \
|
||||
ORDER BY m.timestamp,m.id;",
|
||||
concat!(
|
||||
"SELECT m.id AS id, m.timestamp AS timestamp",
|
||||
" FROM msgs m",
|
||||
" LEFT JOIN contacts ct",
|
||||
" ON m.from_id=ct.id",
|
||||
" WHERE m.starred=1",
|
||||
" AND m.hidden=0",
|
||||
" AND ct.blocked=0",
|
||||
" ORDER BY m.timestamp,m.id;"
|
||||
),
|
||||
params![],
|
||||
process_row,
|
||||
process_rows,
|
||||
)
|
||||
} else {
|
||||
context.sql.query_map(
|
||||
"SELECT m.id, m.timestamp FROM msgs m \
|
||||
WHERE m.chat_id=? \
|
||||
AND m.hidden=0 \
|
||||
ORDER BY m.timestamp,m.id;",
|
||||
concat!(
|
||||
"SELECT m.id AS id, m.timestamp AS timestamp",
|
||||
" FROM msgs m",
|
||||
" WHERE m.chat_id=?",
|
||||
" AND m.hidden=0",
|
||||
" ORDER BY m.timestamp, m.id;"
|
||||
),
|
||||
params![chat_id as i32],
|
||||
process_row,
|
||||
process_rows,
|
||||
)
|
||||
};
|
||||
|
||||
if success.is_ok() {
|
||||
ret
|
||||
} else {
|
||||
Vec::new()
|
||||
match success {
|
||||
Ok(ret) => ret,
|
||||
Err(e) => {
|
||||
error!(context, "Failed to get chat messages: {}", e);
|
||||
Vec::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1053,7 +1087,7 @@ pub fn marknoticed_chat(context: &Context, chat_id: u32) -> Result<(), Error> {
|
||||
|
||||
context.call_cb(Event::MsgsChanged {
|
||||
chat_id: 0,
|
||||
msg_id: 0,
|
||||
msg_id: MsgId::new(0),
|
||||
});
|
||||
|
||||
Ok(())
|
||||
@@ -1077,7 +1111,7 @@ pub fn marknoticed_all_chats(context: &Context) -> Result<(), Error> {
|
||||
)?;
|
||||
|
||||
context.call_cb(Event::MsgsChanged {
|
||||
msg_id: 0,
|
||||
msg_id: MsgId::new(0),
|
||||
chat_id: 0,
|
||||
});
|
||||
|
||||
@@ -1090,31 +1124,44 @@ pub fn get_chat_media(
|
||||
msg_type: Viewtype,
|
||||
msg_type2: Viewtype,
|
||||
msg_type3: Viewtype,
|
||||
) -> Vec<u32> {
|
||||
context.sql.query_map(
|
||||
"SELECT id FROM msgs WHERE chat_id=? AND (type=? OR type=? OR type=?) ORDER BY timestamp, id;",
|
||||
params![
|
||||
chat_id as i32,
|
||||
msg_type,
|
||||
if msg_type2 != Viewtype::Unknown {
|
||||
msg_type2
|
||||
} else {
|
||||
msg_type
|
||||
}, if msg_type3 != Viewtype::Unknown {
|
||||
msg_type3
|
||||
} else {
|
||||
msg_type
|
||||
) -> Vec<MsgId> {
|
||||
context
|
||||
.sql
|
||||
.query_map(
|
||||
concat!(
|
||||
"SELECT",
|
||||
" id",
|
||||
" FROM msgs",
|
||||
" WHERE chat_id=? AND (type=? OR type=? OR type=?)",
|
||||
" ORDER BY timestamp, id;"
|
||||
),
|
||||
params![
|
||||
chat_id as i32,
|
||||
msg_type,
|
||||
if msg_type2 != Viewtype::Unknown {
|
||||
msg_type2
|
||||
} else {
|
||||
msg_type
|
||||
},
|
||||
if msg_type3 != Viewtype::Unknown {
|
||||
msg_type3
|
||||
} else {
|
||||
msg_type
|
||||
},
|
||||
],
|
||||
|row| row.get::<_, MsgId>(0),
|
||||
|ids| {
|
||||
let mut ret = Vec::new();
|
||||
for id in ids {
|
||||
match id {
|
||||
Ok(msg_id) => ret.push(msg_id),
|
||||
Err(_) => (),
|
||||
}
|
||||
}
|
||||
Ok(ret)
|
||||
},
|
||||
],
|
||||
|row| row.get::<_, i32>(0),
|
||||
|ids| {
|
||||
let mut ret = Vec::new();
|
||||
for id in ids {
|
||||
ret.push(id? as u32);
|
||||
}
|
||||
Ok(ret)
|
||||
}
|
||||
).unwrap_or_default()
|
||||
)
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
/// Indicates the direction over which to iterate.
|
||||
@@ -1127,16 +1174,16 @@ pub enum Direction {
|
||||
|
||||
pub fn get_next_media(
|
||||
context: &Context,
|
||||
curr_msg_id: u32,
|
||||
curr_msg_id: MsgId,
|
||||
direction: Direction,
|
||||
msg_type: Viewtype,
|
||||
msg_type2: Viewtype,
|
||||
msg_type3: Viewtype,
|
||||
) -> u32 {
|
||||
let mut ret = 0;
|
||||
) -> Option<MsgId> {
|
||||
let mut ret: Option<MsgId> = None;
|
||||
|
||||
if let Ok(msg) = Message::load_from_db(context, curr_msg_id) {
|
||||
let list = get_chat_media(
|
||||
let list: Vec<MsgId> = get_chat_media(
|
||||
context,
|
||||
msg.chat_id,
|
||||
if msg_type != Viewtype::Unknown {
|
||||
@@ -1152,12 +1199,12 @@ pub fn get_next_media(
|
||||
match direction {
|
||||
Direction::Forward => {
|
||||
if i + 1 < list.len() {
|
||||
ret = list[i + 1]
|
||||
ret = Some(list[i + 1]);
|
||||
}
|
||||
}
|
||||
Direction::Backward => {
|
||||
if i >= 1 {
|
||||
ret = list[i - 1];
|
||||
ret = Some(list[i - 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1196,7 +1243,7 @@ pub fn archive(context: &Context, chat_id: u32, archive: bool) -> Result<(), Err
|
||||
)?;
|
||||
|
||||
context.call_cb(Event::MsgsChanged {
|
||||
msg_id: 0,
|
||||
msg_id: MsgId::new(0),
|
||||
chat_id: 0,
|
||||
});
|
||||
|
||||
@@ -1241,7 +1288,7 @@ pub fn delete(context: &Context, chat_id: u32) -> Result<(), Error> {
|
||||
)?;
|
||||
|
||||
context.call_cb(Event::MsgsChanged {
|
||||
msg_id: 0,
|
||||
msg_id: MsgId::new(0),
|
||||
chat_id: 0,
|
||||
});
|
||||
|
||||
@@ -1310,7 +1357,7 @@ pub fn create_group_chat(
|
||||
}
|
||||
|
||||
context.call_cb(Event::MsgsChanged {
|
||||
msg_id: 0,
|
||||
msg_id: MsgId::new(0),
|
||||
chat_id: 0,
|
||||
});
|
||||
}
|
||||
@@ -1425,10 +1472,13 @@ pub(crate) fn add_contact_to_chat_ex(
|
||||
msg.id = send_msg(context, chat_id, &mut msg)?;
|
||||
context.call_cb(Event::MsgsChanged {
|
||||
chat_id,
|
||||
msg_id: msg.id,
|
||||
msg_id: MsgId::from(msg.id),
|
||||
});
|
||||
}
|
||||
context.call_cb(Event::MsgsChanged { chat_id, msg_id: 0 });
|
||||
context.call_cb(Event::MsgsChanged {
|
||||
chat_id,
|
||||
msg_id: MsgId::new(0),
|
||||
});
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
@@ -1719,39 +1769,33 @@ pub fn set_chat_profile_image(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn forward_msgs(context: &Context, msg_ids: &[u32], chat_id: u32) -> Result<(), Error> {
|
||||
ensure!(!msg_ids.is_empty(), "empty msgs_ids: no one to forward to");
|
||||
pub fn forward_msgs(context: &Context, msg_ids: &[MsgId], chat_id: u32) -> Result<(), Error> {
|
||||
ensure!(!msg_ids.is_empty(), "empty msgs_ids: nothing to forward");
|
||||
ensure!(
|
||||
chat_id > DC_CHAT_ID_LAST_SPECIAL,
|
||||
"can not forward to special chat"
|
||||
);
|
||||
|
||||
let mut created_db_entries = Vec::new();
|
||||
let mut created_chats: Vec<u32> = Vec::new();
|
||||
let mut created_msgs: Vec<MsgId> = Vec::new();
|
||||
let mut curr_timestamp: i64;
|
||||
|
||||
unarchive(context, chat_id)?;
|
||||
if let Ok(mut chat) = Chat::load_from_db(context, chat_id) {
|
||||
curr_timestamp = dc_create_smeared_timestamps(context, msg_ids.len());
|
||||
let idsstr = msg_ids
|
||||
.iter()
|
||||
.enumerate()
|
||||
.fold(String::with_capacity(2 * msg_ids.len()), |acc, (i, n)| {
|
||||
(if i == 0 { acc } else { acc + "," }) + &n.to_string()
|
||||
});
|
||||
|
||||
let ids = context.sql.query_map(
|
||||
format!(
|
||||
"SELECT id FROM msgs WHERE id IN({}) ORDER BY timestamp,id",
|
||||
idsstr
|
||||
msg_ids.iter().map(|_| "?").join(",")
|
||||
),
|
||||
params![],
|
||||
|row| row.get::<_, i32>(0),
|
||||
msg_ids,
|
||||
|row| row.get::<_, MsgId>(0),
|
||||
|ids| ids.collect::<Result<Vec<_>, _>>().map_err(Into::into),
|
||||
)?;
|
||||
|
||||
for id in ids {
|
||||
let src_msg_id = id;
|
||||
let msg = Message::load_from_db(context, src_msg_id as u32);
|
||||
let src_msg_id: MsgId = id;
|
||||
let msg = Message::load_from_db(context, src_msg_id);
|
||||
if msg.is_err() {
|
||||
break;
|
||||
}
|
||||
@@ -1767,22 +1811,21 @@ pub fn forward_msgs(context: &Context, msg_ids: &[u32], chat_id: u32) -> Result<
|
||||
msg.param.remove(Param::ForcePlaintext);
|
||||
msg.param.remove(Param::Cmd);
|
||||
|
||||
let new_msg_id: u32;
|
||||
let new_msg_id: MsgId;
|
||||
if msg.state == MessageState::OutPreparing {
|
||||
let fresh9 = curr_timestamp;
|
||||
curr_timestamp += 1;
|
||||
new_msg_id = chat
|
||||
.prepare_msg_raw(context, &mut msg, fresh9)
|
||||
.unwrap_or_default();
|
||||
new_msg_id = chat.prepare_msg_raw(context, &mut msg, fresh9)?;
|
||||
let save_param = msg.param.clone();
|
||||
msg.param = original_param;
|
||||
msg.id = src_msg_id as u32;
|
||||
msg.id = src_msg_id;
|
||||
|
||||
if let Some(old_fwd) = msg.param.get(Param::PrepForwards) {
|
||||
let new_fwd = format!("{} {}", old_fwd, new_msg_id);
|
||||
let new_fwd = format!("{} {}", old_fwd, new_msg_id.to_u32());
|
||||
msg.param.set(Param::PrepForwards, new_fwd);
|
||||
} else {
|
||||
msg.param.set(Param::PrepForwards, new_msg_id.to_string());
|
||||
msg.param
|
||||
.set(Param::PrepForwards, new_msg_id.to_u32().to_string());
|
||||
}
|
||||
|
||||
msg.save_param_to_disk(context);
|
||||
@@ -1791,23 +1834,19 @@ pub fn forward_msgs(context: &Context, msg_ids: &[u32], chat_id: u32) -> Result<
|
||||
msg.state = MessageState::OutPending;
|
||||
let fresh10 = curr_timestamp;
|
||||
curr_timestamp += 1;
|
||||
new_msg_id = chat
|
||||
.prepare_msg_raw(context, &mut msg, fresh10)
|
||||
.unwrap_or_default();
|
||||
new_msg_id = chat.prepare_msg_raw(context, &mut msg, fresh10)?;
|
||||
job_send_msg(context, new_msg_id)?;
|
||||
}
|
||||
created_db_entries.push(chat_id);
|
||||
created_db_entries.push(new_msg_id);
|
||||
created_chats.push(chat_id);
|
||||
created_msgs.push(new_msg_id);
|
||||
}
|
||||
}
|
||||
|
||||
for i in (0..created_db_entries.len()).step_by(2) {
|
||||
for (chat_id, msg_id) in created_chats.iter().zip(created_msgs.iter()) {
|
||||
context.call_cb(Event::MsgsChanged {
|
||||
chat_id: created_db_entries[i],
|
||||
msg_id: created_db_entries[i + 1],
|
||||
chat_id: *chat_id,
|
||||
msg_id: *msg_id,
|
||||
});
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1874,8 +1913,11 @@ pub fn add_device_msg(context: &Context, chat_id: u32, text: impl AsRef<str>) {
|
||||
return;
|
||||
}
|
||||
|
||||
let msg_id = sql::get_rowid(context, &context.sql, "msgs", "rfc724_mid", &rfc724_mid);
|
||||
context.call_cb(Event::MsgsChanged { chat_id, msg_id });
|
||||
let row_id = sql::get_rowid(context, &context.sql, "msgs", "rfc724_mid", &rfc724_mid);
|
||||
context.call_cb(Event::MsgsChanged {
|
||||
chat_id,
|
||||
msg_id: MsgId::new(row_id),
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
200
src/chatlist.rs
200
src/chatlist.rs
@@ -4,7 +4,7 @@ use crate::contact::*;
|
||||
use crate::context::*;
|
||||
use crate::error::Result;
|
||||
use crate::lot::Lot;
|
||||
use crate::message::Message;
|
||||
use crate::message::{Message, MsgId};
|
||||
use crate::stock::StockMessage;
|
||||
|
||||
/// An object representing a single chatlist in memory.
|
||||
@@ -34,7 +34,7 @@ use crate::stock::StockMessage;
|
||||
#[derive(Debug)]
|
||||
pub struct Chatlist {
|
||||
/// Stores pairs of `chat_id, message_id`
|
||||
ids: Vec<(u32, u32)>,
|
||||
ids: Vec<(u32, MsgId)>,
|
||||
}
|
||||
|
||||
impl Chatlist {
|
||||
@@ -86,25 +86,12 @@ impl Chatlist {
|
||||
query: Option<&str>,
|
||||
query_contact_id: Option<u32>,
|
||||
) -> Result<Self> {
|
||||
let mut add_archived_link_item = 0;
|
||||
|
||||
// select with left join and minimum:
|
||||
// - the inner select must use `hidden` and _not_ `m.hidden`
|
||||
// which would refer the outer select and take a lot of time
|
||||
// - `GROUP BY` is needed several messages may have the same timestamp
|
||||
// - the list starts with the newest chats
|
||||
// nb: the query currently shows messages from blocked contacts in groups.
|
||||
// however, for normal-groups, this is okay as the message is also returned by dc_get_chat_msgs()
|
||||
// (otherwise it would be hard to follow conversations, wa and tg do the same)
|
||||
// for the deaddrop, however, they should really be hidden, however, _currently_ the deaddrop is not
|
||||
// shown at all permanent in the chatlist.
|
||||
let mut add_archived_link_item = false;
|
||||
|
||||
let process_row = |row: &rusqlite::Row| {
|
||||
let chat_id: i32 = row.get(0)?;
|
||||
// TODO: verify that it is okay for this to be Null
|
||||
let msg_id: i32 = row.get(1).unwrap_or_default();
|
||||
|
||||
Ok((chat_id as u32, msg_id as u32))
|
||||
let chat_id: u32 = row.get(0)?;
|
||||
let msg_id: MsgId = row.get(1).unwrap_or_default();
|
||||
Ok((chat_id, msg_id))
|
||||
};
|
||||
|
||||
let process_rows = |rows: rusqlite::MappedRows<_>| {
|
||||
@@ -112,36 +99,63 @@ impl Chatlist {
|
||||
.map_err(Into::into)
|
||||
};
|
||||
|
||||
// nb: the query currently shows messages from blocked contacts in groups.
|
||||
// however, for normal-groups, this is okay as the message is also returned by dc_get_chat_msgs()
|
||||
// (otherwise it would be hard to follow conversations, wa and tg do the same)
|
||||
// for the deaddrop, however, they should really be hidden, however, _currently_ the deaddrop is not
|
||||
// select with left join and minimum:
|
||||
//
|
||||
// - the inner select must use `hidden` and _not_ `m.hidden`
|
||||
// which would refer the outer select and take a lot of time
|
||||
// - `GROUP BY` is needed several messages may have the same
|
||||
// timestamp
|
||||
// - the list starts with the newest chats
|
||||
//
|
||||
// nb: the query currently shows messages from blocked
|
||||
// contacts in groups. however, for normal-groups, this is
|
||||
// okay as the message is also returned by dc_get_chat_msgs()
|
||||
// (otherwise it would be hard to follow conversations, wa and
|
||||
// tg do the same) for the deaddrop, however, they should
|
||||
// really be hidden, however, _currently_ the deaddrop is not
|
||||
// shown at all permanent in the chatlist.
|
||||
|
||||
let mut ids = if let Some(query_contact_id) = query_contact_id {
|
||||
// show chats shared with a given contact
|
||||
context.sql.query_map(
|
||||
"SELECT c.id, m.id FROM chats c LEFT JOIN msgs m \
|
||||
ON c.id=m.chat_id \
|
||||
AND m.timestamp=( SELECT MAX(timestamp) \
|
||||
FROM msgs WHERE chat_id=c.id \
|
||||
AND (hidden=0 OR (hidden=1 AND state=19))) WHERE c.id>9 \
|
||||
AND c.blocked=0 AND c.id IN(SELECT chat_id FROM chats_contacts WHERE contact_id=?) \
|
||||
GROUP BY c.id ORDER BY IFNULL(m.timestamp,0) DESC, m.id DESC;",
|
||||
params![query_contact_id as i32],
|
||||
process_row,
|
||||
process_rows,
|
||||
)?
|
||||
concat!(
|
||||
"SELECT c.id, m.id",
|
||||
" FROM chats c",
|
||||
" LEFT JOIN msgs m",
|
||||
" ON c.id=m.chat_id",
|
||||
" AND m.timestamp=(",
|
||||
" SELECT MAX(timestamp)",
|
||||
" FROM msgs",
|
||||
" WHERE chat_id=c.id",
|
||||
" AND (hidden=0 OR (hidden=1 AND state=19)))",
|
||||
" WHERE c.id>9",
|
||||
" AND c.blocked=0",
|
||||
" AND c.id IN(SELECT chat_id FROM chats_contacts WHERE contact_id=?)",
|
||||
" GROUP BY c.id",
|
||||
" ORDER BY IFNULL(m.timestamp,0) DESC, m.id DESC;"
|
||||
),
|
||||
params![query_contact_id as i32],
|
||||
process_row,
|
||||
process_rows,
|
||||
)?
|
||||
} else if 0 != listflags & DC_GCL_ARCHIVED_ONLY {
|
||||
// show archived chats
|
||||
context.sql.query_map(
|
||||
"SELECT c.id, m.id FROM chats c LEFT JOIN msgs m \
|
||||
ON c.id=m.chat_id \
|
||||
AND m.timestamp=( SELECT MAX(timestamp) \
|
||||
FROM msgs WHERE chat_id=c.id \
|
||||
AND (hidden=0 OR (hidden=1 AND state=19))) WHERE c.id>9 \
|
||||
AND c.blocked=0 AND c.archived=1 GROUP BY c.id \
|
||||
ORDER BY IFNULL(m.timestamp,0) DESC, m.id DESC;",
|
||||
concat!(
|
||||
"SELECT c.id, m.id",
|
||||
" FROM chats c",
|
||||
" LEFT JOIN msgs m",
|
||||
" ON c.id=m.chat_id",
|
||||
" AND m.timestamp=(",
|
||||
" SELECT MAX(timestamp)",
|
||||
" FROM msgs",
|
||||
" WHERE chat_id=c.id",
|
||||
" AND (hidden=0 OR (hidden=1 AND state=19)))",
|
||||
" WHERE c.id>9",
|
||||
" AND c.blocked=0",
|
||||
" AND c.archived=1",
|
||||
" GROUP BY c.id",
|
||||
" ORDER BY IFNULL(m.timestamp,0) DESC, m.id DESC;"
|
||||
),
|
||||
params![],
|
||||
process_row,
|
||||
process_rows,
|
||||
@@ -152,13 +166,22 @@ impl Chatlist {
|
||||
|
||||
let str_like_cmd = format!("%{}%", query);
|
||||
context.sql.query_map(
|
||||
"SELECT c.id, m.id FROM chats c LEFT JOIN msgs m \
|
||||
ON c.id=m.chat_id \
|
||||
AND m.timestamp=( SELECT MAX(timestamp) \
|
||||
FROM msgs WHERE chat_id=c.id \
|
||||
AND (hidden=0 OR (hidden=1 AND state=19))) WHERE c.id>9 \
|
||||
AND c.blocked=0 AND c.name LIKE ? \
|
||||
GROUP BY c.id ORDER BY IFNULL(m.timestamp,0) DESC, m.id DESC;",
|
||||
concat!(
|
||||
"SELECT c.id, m.id",
|
||||
" FROM chats c",
|
||||
" LEFT JOIN msgs m",
|
||||
" ON c.id=m.chat_id",
|
||||
" AND m.timestamp=(",
|
||||
" SELECT MAX(timestamp)",
|
||||
" FROM msgs",
|
||||
" WHERE chat_id=c.id",
|
||||
" AND (hidden=0 OR (hidden=1 AND state=19)))",
|
||||
" WHERE c.id>9",
|
||||
" AND c.blocked=0",
|
||||
" AND c.name LIKE ?",
|
||||
" GROUP BY c.id",
|
||||
" ORDER BY IFNULL(m.timestamp,0) DESC, m.id DESC;"
|
||||
),
|
||||
params![str_like_cmd],
|
||||
process_row,
|
||||
process_rows,
|
||||
@@ -166,34 +189,40 @@ impl Chatlist {
|
||||
} else {
|
||||
// show normal chatlist
|
||||
let mut ids = context.sql.query_map(
|
||||
"SELECT c.id, m.id FROM chats c \
|
||||
LEFT JOIN msgs m \
|
||||
ON c.id=m.chat_id \
|
||||
AND m.timestamp=( SELECT MAX(timestamp) \
|
||||
FROM msgs WHERE chat_id=c.id \
|
||||
AND (hidden=0 OR (hidden=1 AND state=19))) WHERE c.id>9 \
|
||||
AND c.blocked=0 AND c.archived=0 \
|
||||
GROUP BY c.id \
|
||||
ORDER BY IFNULL(m.timestamp,0) DESC, m.id DESC;",
|
||||
concat!(
|
||||
"SELECT c.id, m.id",
|
||||
" FROM chats c",
|
||||
" LEFT JOIN msgs m",
|
||||
" ON c.id=m.chat_id",
|
||||
" AND m.timestamp=(",
|
||||
" SELECT MAX(timestamp)",
|
||||
" FROM msgs",
|
||||
" WHERE chat_id=c.id",
|
||||
" AND (hidden=0 OR (hidden=1 AND state=19)))",
|
||||
" WHERE c.id>9",
|
||||
" AND c.blocked=0",
|
||||
" AND c.archived=0",
|
||||
" GROUP BY c.id",
|
||||
" ORDER BY IFNULL(m.timestamp,0) DESC, m.id DESC;"
|
||||
),
|
||||
params![],
|
||||
process_row,
|
||||
process_rows,
|
||||
)?;
|
||||
if 0 == listflags & DC_GCL_NO_SPECIALS {
|
||||
let last_deaddrop_fresh_msg_id = get_last_deaddrop_fresh_msg(context);
|
||||
if last_deaddrop_fresh_msg_id > 0 {
|
||||
if let Some(last_deaddrop_fresh_msg_id) = get_last_deaddrop_fresh_msg(context) {
|
||||
ids.insert(0, (DC_CHAT_ID_DEADDROP, last_deaddrop_fresh_msg_id));
|
||||
}
|
||||
add_archived_link_item = 1;
|
||||
add_archived_link_item = true;
|
||||
}
|
||||
ids
|
||||
};
|
||||
|
||||
if 0 != add_archived_link_item && dc_get_archived_cnt(context) > 0 {
|
||||
if add_archived_link_item && dc_get_archived_cnt(context) > 0 {
|
||||
if ids.is_empty() && 0 != listflags & DC_GCL_ADD_ALLDONE_HINT {
|
||||
ids.push((DC_CHAT_ID_ALLDONE_HINT, 0));
|
||||
ids.push((DC_CHAT_ID_ALLDONE_HINT, MsgId::new(0)));
|
||||
}
|
||||
ids.push((DC_CHAT_ID_ARCHIVED_LINK, 0));
|
||||
ids.push((DC_CHAT_ID_ARCHIVED_LINK, MsgId::new(0)));
|
||||
}
|
||||
|
||||
Ok(Chatlist { ids })
|
||||
@@ -221,12 +250,9 @@ impl Chatlist {
|
||||
/// Get a single message ID of a chatlist.
|
||||
///
|
||||
/// To get the message object from the message ID, use dc_get_msg().
|
||||
pub fn get_msg_id(&self, index: usize) -> u32 {
|
||||
if index >= self.ids.len() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
self.ids[index].1
|
||||
pub fn get_msg_id(&self, index: usize) -> Result<MsgId> {
|
||||
ensure!(index >= self.ids.len(), "Chatlist index out of range");
|
||||
Ok(self.ids[index].1)
|
||||
}
|
||||
|
||||
/// Get a summary for a chatlist index.
|
||||
@@ -268,7 +294,7 @@ impl Chatlist {
|
||||
let lastmsg_id = self.ids[index].1;
|
||||
let mut lastcontact = None;
|
||||
|
||||
let lastmsg = if 0 != lastmsg_id {
|
||||
let lastmsg = if !lastmsg_id.is_special() {
|
||||
if let Ok(lastmsg) = Message::load_from_db(context, lastmsg_id) {
|
||||
if lastmsg.from_id != 1
|
||||
&& (chat.typ == Chattype::Group || chat.typ == Chattype::VerifiedGroup)
|
||||
@@ -308,19 +334,21 @@ pub fn dc_get_archived_cnt(context: &Context) -> u32 {
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
fn get_last_deaddrop_fresh_msg(context: &Context) -> u32 {
|
||||
// We have an index over the state-column, this should be sufficient as there are typically
|
||||
// only few fresh messages.
|
||||
context
|
||||
.sql
|
||||
.query_get_value(
|
||||
context,
|
||||
"SELECT m.id FROM msgs m LEFT JOIN chats c ON c.id=m.chat_id \
|
||||
WHERE m.state=10 \
|
||||
AND m.hidden=0 \
|
||||
AND c.blocked=2 \
|
||||
ORDER BY m.timestamp DESC, m.id DESC;",
|
||||
params![],
|
||||
)
|
||||
.unwrap_or_default()
|
||||
fn get_last_deaddrop_fresh_msg(context: &Context) -> Option<MsgId> {
|
||||
// We have an index over the state-column, this should be
|
||||
// sufficient as there are typically only few fresh messages.
|
||||
context.sql.query_get_value(
|
||||
context,
|
||||
concat!(
|
||||
"SELECT m.id",
|
||||
" FROM msgs m",
|
||||
" LEFT JOIN chats c",
|
||||
" ON c.id=m.chat_id",
|
||||
" WHERE m.state=10",
|
||||
" AND m.hidden=0",
|
||||
" AND c.blocked=2",
|
||||
" ORDER BY m.timestamp DESC, m.id DESC;"
|
||||
),
|
||||
params![],
|
||||
)
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ pub const DC_GCL_ARCHIVED_ONLY: usize = 0x01;
|
||||
pub const DC_GCL_NO_SPECIALS: usize = 0x02;
|
||||
pub const DC_GCL_ADD_ALLDONE_HINT: usize = 0x04;
|
||||
|
||||
const DC_GCM_ADDDAYMARKER: usize = 0x01;
|
||||
pub const DC_GCM_ADDDAYMARKER: u32 = 0x01;
|
||||
|
||||
pub const DC_GCL_VERIFIED_ONLY: usize = 0x01;
|
||||
pub const DC_GCL_ADD_SELF: usize = 0x02;
|
||||
@@ -120,7 +120,7 @@ impl Default for Chattype {
|
||||
}
|
||||
|
||||
pub const DC_MSG_ID_MARKER1: u32 = 1;
|
||||
const DC_MSG_ID_DAYMARKER: u32 = 9;
|
||||
pub const DC_MSG_ID_DAYMARKER: u32 = 9;
|
||||
pub const DC_MSG_ID_LAST_SPECIAL: u32 = 9;
|
||||
|
||||
/// approx. max. length returned by dc_msg_get_text()
|
||||
|
||||
@@ -14,7 +14,7 @@ use crate::error::Result;
|
||||
use crate::events::Event;
|
||||
use crate::key::*;
|
||||
use crate::login_param::LoginParam;
|
||||
use crate::message::MessageState;
|
||||
use crate::message::{MessageState, MsgId};
|
||||
use crate::peerstate::*;
|
||||
use crate::sql;
|
||||
use crate::stock::StockMessage;
|
||||
@@ -243,7 +243,7 @@ impl Contact {
|
||||
{
|
||||
context.call_cb(Event::MsgsChanged {
|
||||
chat_id: 0,
|
||||
msg_id: 0,
|
||||
msg_id: MsgId::new(0),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ use crate::job_thread::JobThread;
|
||||
use crate::key::*;
|
||||
use crate::login_param::LoginParam;
|
||||
use crate::lot::Lot;
|
||||
use crate::message::{self, Message};
|
||||
use crate::message::{self, Message, MsgId};
|
||||
use crate::param::Params;
|
||||
use crate::smtp::*;
|
||||
use crate::sql::Sql;
|
||||
@@ -313,24 +313,30 @@ impl Context {
|
||||
res
|
||||
}
|
||||
|
||||
pub fn get_fresh_msgs(&self) -> Vec<u32> {
|
||||
pub fn get_fresh_msgs(&self) -> Vec<MsgId> {
|
||||
let show_deaddrop = 0;
|
||||
|
||||
self.sql
|
||||
.query_map(
|
||||
"SELECT m.id FROM msgs m LEFT JOIN contacts ct \
|
||||
ON m.from_id=ct.id LEFT JOIN chats c ON m.chat_id=c.id WHERE m.state=? \
|
||||
AND m.hidden=0 \
|
||||
AND m.chat_id>? \
|
||||
AND ct.blocked=0 \
|
||||
AND (c.blocked=0 OR c.blocked=?) ORDER BY m.timestamp DESC,m.id DESC;",
|
||||
concat!(
|
||||
"SELECT m.id",
|
||||
" FROM msgs m",
|
||||
" LEFT JOIN contacts ct",
|
||||
" ON m.from_id=ct.id",
|
||||
" LEFT JOIN chats c",
|
||||
" ON m.chat_id=c.id",
|
||||
" WHERE m.state=?",
|
||||
" AND m.hidden=0",
|
||||
" AND m.chat_id>?",
|
||||
" AND ct.blocked=0",
|
||||
" AND (c.blocked=0 OR c.blocked=?)",
|
||||
" ORDER BY m.timestamp DESC,m.id DESC;"
|
||||
),
|
||||
&[10, 9, if 0 != show_deaddrop { 2 } else { 0 }],
|
||||
|row| row.get(0),
|
||||
|row| row.get::<_, MsgId>(0),
|
||||
|rows| {
|
||||
let mut ret = Vec::new();
|
||||
for row in rows {
|
||||
let id: u32 = row?;
|
||||
ret.push(id);
|
||||
ret.push(row?);
|
||||
}
|
||||
Ok(ret)
|
||||
},
|
||||
@@ -339,7 +345,7 @@ impl Context {
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
pub fn search_msgs(&self, chat_id: u32, query: impl AsRef<str>) -> Vec<u32> {
|
||||
pub fn search_msgs(&self, chat_id: u32, query: impl AsRef<str>) -> Vec<MsgId> {
|
||||
let real_query = query.as_ref().trim();
|
||||
if real_query.is_empty() {
|
||||
return Vec::new();
|
||||
@@ -348,25 +354,43 @@ impl Context {
|
||||
let strLikeBeg = format!("{}%", real_query);
|
||||
|
||||
let query = if 0 != chat_id {
|
||||
"SELECT m.id, m.timestamp FROM msgs m LEFT JOIN contacts ct ON m.from_id=ct.id WHERE m.chat_id=? \
|
||||
AND m.hidden=0 \
|
||||
AND ct.blocked=0 AND (txt LIKE ? OR ct.name LIKE ?) ORDER BY m.timestamp,m.id;"
|
||||
concat!(
|
||||
"SELECT m.id AS id, m.timestamp AS timestamp",
|
||||
" FROM msgs m",
|
||||
" LEFT JOIN contacts ct",
|
||||
" ON m.from_id=ct.id",
|
||||
" WHERE m.chat_id=?",
|
||||
" AND m.hidden=0",
|
||||
" AND ct.blocked=0",
|
||||
" AND (txt LIKE ? OR ct.name LIKE ?)",
|
||||
" ORDER BY m.timestamp,m.id;"
|
||||
)
|
||||
} else {
|
||||
"SELECT m.id, m.timestamp FROM msgs m LEFT JOIN contacts ct ON m.from_id=ct.id \
|
||||
LEFT JOIN chats c ON m.chat_id=c.id WHERE m.chat_id>9 AND m.hidden=0 \
|
||||
AND (c.blocked=0 OR c.blocked=?) \
|
||||
AND ct.blocked=0 AND (m.txt LIKE ? OR ct.name LIKE ?) ORDER BY m.timestamp DESC,m.id DESC;"
|
||||
concat!(
|
||||
"SELECT m.id AS id, m.timestamp AS timestamp",
|
||||
" FROM msgs m",
|
||||
" LEFT JOIN contacts ct",
|
||||
" ON m.from_id=ct.id",
|
||||
" LEFT JOIN chats c",
|
||||
" ON m.chat_id=c.id",
|
||||
" WHERE m.chat_id>9",
|
||||
" AND m.hidden=0",
|
||||
" AND (c.blocked=0 OR c.blocked=?)",
|
||||
" AND ct.blocked=0",
|
||||
" AND (m.txt LIKE ? OR ct.name LIKE ?)",
|
||||
" ORDER BY m.timestamp DESC,m.id DESC;"
|
||||
)
|
||||
};
|
||||
|
||||
self.sql
|
||||
.query_map(
|
||||
query,
|
||||
params![chat_id as i32, &strLikeInText, &strLikeBeg],
|
||||
|row| row.get::<_, i32>(0),
|
||||
|row| row.get::<_, MsgId>("id"),
|
||||
|rows| {
|
||||
let mut ret = Vec::new();
|
||||
for id in rows {
|
||||
ret.push(id? as u32);
|
||||
ret.push(id?);
|
||||
}
|
||||
Ok(ret)
|
||||
},
|
||||
@@ -397,7 +421,7 @@ impl Context {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn do_heuristics_moves(&self, folder: &str, msg_id: u32) {
|
||||
pub fn do_heuristics_moves(&self, folder: &str, msg_id: MsgId) {
|
||||
if !self.get_config_bool(Config::MvboxMove) {
|
||||
return;
|
||||
}
|
||||
@@ -422,7 +446,7 @@ impl Context {
|
||||
job_add(
|
||||
self,
|
||||
Action::MoveMsg,
|
||||
msg.id as libc::c_int,
|
||||
msg.id.to_u32() as i32,
|
||||
Params::new(),
|
||||
0,
|
||||
);
|
||||
|
||||
@@ -25,7 +25,7 @@ use crate::error::Result;
|
||||
use crate::events::Event;
|
||||
use crate::job::*;
|
||||
use crate::location;
|
||||
use crate::message::{self, MessageState};
|
||||
use crate::message::{self, MessageState, MsgId};
|
||||
use crate::param::*;
|
||||
use crate::peerstate::*;
|
||||
use crate::securejoin::handle_securejoin_handshake;
|
||||
@@ -85,7 +85,7 @@ pub unsafe fn dc_receive_imf(
|
||||
let mut hidden = 0;
|
||||
|
||||
let mut needs_delete_job = false;
|
||||
let mut insert_msg_id = 0;
|
||||
let mut insert_msg_id = MsgId::new_unset();
|
||||
|
||||
let mut sent_timestamp = 0;
|
||||
let mut created_db_entries = Vec::new();
|
||||
@@ -97,17 +97,17 @@ pub unsafe fn dc_receive_imf(
|
||||
// helper method to handle early exit and memory cleanup
|
||||
let cleanup = |context: &Context,
|
||||
create_event_to_send: &Option<CreateEvent>,
|
||||
created_db_entries: &Vec<(usize, usize)>,
|
||||
rr_event_to_send: &Vec<(u32, u32)>| {
|
||||
created_db_entries: &Vec<(usize, MsgId)>,
|
||||
rr_event_to_send: &Vec<(u32, MsgId)>| {
|
||||
if let Some(create_event_to_send) = create_event_to_send {
|
||||
for (chat_id, msg_id) in created_db_entries {
|
||||
let event = match create_event_to_send {
|
||||
CreateEvent::MsgsChanged => Event::MsgsChanged {
|
||||
msg_id: *msg_id as u32,
|
||||
msg_id: *msg_id,
|
||||
chat_id: *chat_id as u32,
|
||||
},
|
||||
CreateEvent::IncomingMsg => Event::IncomingMsg {
|
||||
msg_id: *msg_id as u32,
|
||||
msg_id: *msg_id,
|
||||
chat_id: *chat_id as u32,
|
||||
},
|
||||
};
|
||||
@@ -273,7 +273,7 @@ pub unsafe fn dc_receive_imf(
|
||||
job_add(
|
||||
context,
|
||||
Action::DeleteMsgOnImap,
|
||||
created_db_entries[0].1 as i32,
|
||||
created_db_entries[0].1.to_u32() as i32,
|
||||
Params::new(),
|
||||
0,
|
||||
);
|
||||
@@ -313,8 +313,8 @@ unsafe fn add_parts(
|
||||
flags: u32,
|
||||
needs_delete_job: &mut bool,
|
||||
to_self: i32,
|
||||
insert_msg_id: &mut u32,
|
||||
created_db_entries: &mut Vec<(usize, usize)>,
|
||||
insert_msg_id: &mut MsgId,
|
||||
created_db_entries: &mut Vec<(usize, MsgId)>,
|
||||
create_event_to_send: &mut Option<CreateEvent>,
|
||||
) -> Result<()> {
|
||||
let mut state: MessageState;
|
||||
@@ -684,9 +684,10 @@ unsafe fn add_parts(
|
||||
])?;
|
||||
|
||||
txt_raw = None;
|
||||
*insert_msg_id =
|
||||
let row_id =
|
||||
sql::get_rowid_with_conn(context, conn, "msgs", "rfc724_mid", &rfc724_mid);
|
||||
created_db_entries.push((*chat_id as usize, *insert_msg_id as usize));
|
||||
*insert_msg_id = MsgId::new(row_id);
|
||||
created_db_entries.push((*chat_id as usize, *insert_msg_id));
|
||||
}
|
||||
Ok(())
|
||||
},
|
||||
@@ -719,7 +720,7 @@ unsafe fn handle_reports(
|
||||
mime_parser: &MimeParser,
|
||||
from_id: u32,
|
||||
sent_timestamp: i64,
|
||||
rr_event_to_send: &mut Vec<(u32, u32)>,
|
||||
rr_event_to_send: &mut Vec<(u32, MsgId)>,
|
||||
server_folder: impl AsRef<str>,
|
||||
server_uid: u32,
|
||||
) {
|
||||
@@ -810,20 +811,15 @@ unsafe fn handle_reports(
|
||||
if let Ok(rfc724_mid) = wrapmime::parse_message_id(as_str(
|
||||
(*of_org_msgid).fld_value,
|
||||
)) {
|
||||
let mut chat_id_0 = 0;
|
||||
let mut msg_id = 0;
|
||||
|
||||
if message::mdn_from_ext(
|
||||
if let Some((chat_id, msg_id)) = message::mdn_from_ext(
|
||||
context,
|
||||
from_id,
|
||||
&rfc724_mid,
|
||||
sent_timestamp,
|
||||
&mut chat_id_0,
|
||||
&mut msg_id,
|
||||
) {
|
||||
rr_event_to_send.push((chat_id_0, msg_id));
|
||||
rr_event_to_send.push((chat_id, msg_id));
|
||||
mdn_consumed = 1;
|
||||
}
|
||||
mdn_consumed = (msg_id != 0) as libc::c_int;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -851,7 +847,7 @@ fn save_locations(
|
||||
mime_parser: &MimeParser,
|
||||
chat_id: u32,
|
||||
from_id: u32,
|
||||
insert_msg_id: u32,
|
||||
insert_msg_id: MsgId,
|
||||
hidden: i32,
|
||||
) {
|
||||
let mut location_id_written = false;
|
||||
|
||||
@@ -32,6 +32,8 @@ pub enum Error {
|
||||
FromUtf8(std::string::FromUtf8Error),
|
||||
#[fail(display = "{}", _0)]
|
||||
BlobError(#[cause] crate::blob::BlobError),
|
||||
#[fail(display = "Invalid Message ID.")]
|
||||
InvalidMsgId,
|
||||
}
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
@@ -102,6 +104,12 @@ impl From<crate::blob::BlobError> for Error {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<crate::message::InvalidMsgId> for Error {
|
||||
fn from(_err: crate::message::InvalidMsgId) -> Error {
|
||||
Error::InvalidMsgId
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! bail {
|
||||
($e:expr) => {
|
||||
|
||||
@@ -2,6 +2,8 @@ use std::path::PathBuf;
|
||||
|
||||
use strum::EnumProperty;
|
||||
|
||||
use crate::message::MsgId;
|
||||
|
||||
impl Event {
|
||||
/// Returns the corresponding Event id.
|
||||
pub fn as_id(&self) -> i32 {
|
||||
@@ -125,7 +127,7 @@ pub enum Event {
|
||||
///
|
||||
/// @return 0
|
||||
#[strum(props(id = "2000"))]
|
||||
MsgsChanged { chat_id: u32, msg_id: u32 },
|
||||
MsgsChanged { chat_id: u32, msg_id: MsgId },
|
||||
|
||||
/// There is a fresh message. Typically, the user will show an notification
|
||||
/// when receiving this message.
|
||||
@@ -134,28 +136,28 @@ pub enum Event {
|
||||
///
|
||||
/// @return 0
|
||||
#[strum(props(id = "2005"))]
|
||||
IncomingMsg { chat_id: u32, msg_id: u32 },
|
||||
IncomingMsg { chat_id: u32, msg_id: MsgId },
|
||||
|
||||
/// A single message is sent successfully. State changed from DC_STATE_OUT_PENDING to
|
||||
/// DC_STATE_OUT_DELIVERED, see dc_msg_get_state().
|
||||
///
|
||||
/// @return 0
|
||||
#[strum(props(id = "2010"))]
|
||||
MsgDelivered { chat_id: u32, msg_id: u32 },
|
||||
MsgDelivered { chat_id: u32, msg_id: MsgId },
|
||||
|
||||
/// A single message could not be sent. State changed from DC_STATE_OUT_PENDING or DC_STATE_OUT_DELIVERED to
|
||||
/// DC_STATE_OUT_FAILED, see dc_msg_get_state().
|
||||
///
|
||||
/// @return 0
|
||||
#[strum(props(id = "2012"))]
|
||||
MsgFailed { chat_id: u32, msg_id: u32 },
|
||||
MsgFailed { chat_id: u32, msg_id: MsgId },
|
||||
|
||||
/// A single message is read by the receiver. State changed from DC_STATE_OUT_DELIVERED to
|
||||
/// DC_STATE_OUT_MDN_RCVD, see dc_msg_get_state().
|
||||
///
|
||||
/// @return 0
|
||||
#[strum(props(id = "2015"))]
|
||||
MsgRead { chat_id: u32, msg_id: u32 },
|
||||
MsgRead { chat_id: u32, msg_id: MsgId },
|
||||
|
||||
/// Chat changed. The name or the image of a chat group was changed or members were added or removed.
|
||||
/// Or the verify state of a chat has changed.
|
||||
|
||||
@@ -1540,7 +1540,7 @@ fn precheck_imf(context: &Context, rfc724_mid: &str, server_folder: &str, server
|
||||
job_add(
|
||||
context,
|
||||
Action::MarkseenMsgOnImap,
|
||||
msg_id as libc::c_int,
|
||||
msg_id.to_u32() as i32,
|
||||
Params::new(),
|
||||
0,
|
||||
);
|
||||
|
||||
@@ -17,7 +17,7 @@ use crate::error::*;
|
||||
use crate::events::Event;
|
||||
use crate::job::*;
|
||||
use crate::key::*;
|
||||
use crate::message::Message;
|
||||
use crate::message::{Message, MsgId};
|
||||
use crate::param::*;
|
||||
use crate::pgp;
|
||||
use crate::sql::{self, Sql};
|
||||
@@ -228,8 +228,8 @@ pub fn create_setup_code(_context: &Context) -> String {
|
||||
ret
|
||||
}
|
||||
|
||||
pub fn continue_key_transfer(context: &Context, msg_id: u32, setup_code: &str) -> Result<()> {
|
||||
ensure!(msg_id > DC_MSG_ID_LAST_SPECIAL, "wrong id");
|
||||
pub fn continue_key_transfer(context: &Context, msg_id: MsgId, setup_code: &str) -> Result<()> {
|
||||
ensure!(!msg_id.is_special(), "wrong id");
|
||||
|
||||
let msg = Message::load_from_db(context, msg_id);
|
||||
if msg.is_err() {
|
||||
|
||||
27
src/job.rs
27
src/job.rs
@@ -16,6 +16,7 @@ use crate::imap::*;
|
||||
use crate::imex::*;
|
||||
use crate::location;
|
||||
use crate::login_param::LoginParam;
|
||||
use crate::message::MsgId;
|
||||
use crate::message::{self, Message, MessageState};
|
||||
use crate::mimefactory::{vec_contains_lowercase, Loaded, MimeFactory};
|
||||
use crate::param::*;
|
||||
@@ -156,7 +157,9 @@ impl Job {
|
||||
/* if there is a msg-id and it does not exist in the db, cancel sending.
|
||||
this happends if dc_delete_msgs() was called
|
||||
before the generated mime was sent out */
|
||||
if 0 != self.foreign_id && !message::exists(context, self.foreign_id) {
|
||||
if 0 != self.foreign_id
|
||||
&& !message::exists(context, MsgId::new(self.foreign_id))
|
||||
{
|
||||
warn!(
|
||||
context,
|
||||
"Not sending Message {} as it was deleted", self.foreign_id
|
||||
@@ -180,7 +183,7 @@ impl Job {
|
||||
if 0 != self.foreign_id {
|
||||
message::update_msg_state(
|
||||
context,
|
||||
self.foreign_id,
|
||||
MsgId::new(self.foreign_id),
|
||||
MessageState::OutDelivered,
|
||||
);
|
||||
let chat_id: i32 = context
|
||||
@@ -193,7 +196,7 @@ impl Job {
|
||||
.unwrap_or_default();
|
||||
context.call_cb(Event::MsgDelivered {
|
||||
chat_id: chat_id as u32,
|
||||
msg_id: self.foreign_id,
|
||||
msg_id: MsgId::new(self.foreign_id),
|
||||
});
|
||||
}
|
||||
// now also delete the generated file
|
||||
@@ -217,7 +220,7 @@ impl Job {
|
||||
fn do_DC_JOB_MOVE_MSG(&mut self, context: &Context) {
|
||||
let inbox = context.inbox.read().unwrap();
|
||||
|
||||
if let Ok(msg) = Message::load_from_db(context, self.foreign_id) {
|
||||
if let Ok(msg) = Message::load_from_db(context, MsgId::new(self.foreign_id)) {
|
||||
if context
|
||||
.sql
|
||||
.get_raw_config_int(context, "folders_configured")
|
||||
@@ -262,7 +265,7 @@ impl Job {
|
||||
fn do_DC_JOB_DELETE_MSG_ON_IMAP(&mut self, context: &Context) {
|
||||
let inbox = context.inbox.read().unwrap();
|
||||
|
||||
if let Ok(mut msg) = Message::load_from_db(context, self.foreign_id) {
|
||||
if let Ok(mut msg) = Message::load_from_db(context, MsgId::new(self.foreign_id)) {
|
||||
if !msg.rfc724_mid.is_empty() {
|
||||
/* eg. device messages have no Message-ID */
|
||||
if message::rfc724_mid_cnt(context, &msg.rfc724_mid) > 1 {
|
||||
@@ -290,7 +293,7 @@ impl Job {
|
||||
fn do_DC_JOB_MARKSEEN_MSG_ON_IMAP(&mut self, context: &Context) {
|
||||
let inbox = context.inbox.read().unwrap();
|
||||
|
||||
if let Ok(msg) = Message::load_from_db(context, self.foreign_id) {
|
||||
if let Ok(msg) = Message::load_from_db(context, MsgId::new(self.foreign_id)) {
|
||||
let folder = msg.server_folder.as_ref().unwrap();
|
||||
match inbox.set_seen(context, folder, msg.server_uid) {
|
||||
ImapResult::RetryLater => {
|
||||
@@ -561,7 +564,7 @@ pub fn job_action_exists(context: &Context, action: Action) -> bool {
|
||||
|
||||
/* special case for DC_JOB_SEND_MSG_TO_SMTP */
|
||||
#[allow(non_snake_case)]
|
||||
pub fn job_send_msg(context: &Context, msg_id: u32) -> Result<(), Error> {
|
||||
pub fn job_send_msg(context: &Context, msg_id: MsgId) -> Result<(), Error> {
|
||||
let mut mimefactory = MimeFactory::load_msg(context, msg_id)?;
|
||||
|
||||
if chat::msgtype_has_file(mimefactory.msg.type_0) {
|
||||
@@ -858,7 +861,11 @@ fn job_perform(context: &Context, thread: Thread, probe_network: bool) {
|
||||
}
|
||||
} else {
|
||||
if job.action == Action::SendMsgToSmtp {
|
||||
message::set_msg_failed(context, job.foreign_id, job.pending_error.as_ref());
|
||||
message::set_msg_failed(
|
||||
context,
|
||||
MsgId::new(job.foreign_id),
|
||||
job.pending_error.as_ref(),
|
||||
);
|
||||
}
|
||||
job.delete(context);
|
||||
}
|
||||
@@ -909,7 +916,7 @@ pub fn connect_to_inbox(context: &Context, inbox: &Imap) -> libc::c_int {
|
||||
ret_connected
|
||||
}
|
||||
|
||||
fn send_mdn(context: &Context, msg_id: u32) -> Result<(), Error> {
|
||||
fn send_mdn(context: &Context, msg_id: MsgId) -> Result<(), Error> {
|
||||
let mut mimefactory = MimeFactory::load_mdn(context, msg_id)?;
|
||||
unsafe { mimefactory.render()? };
|
||||
add_smtp_job(context, Action::SendMdn, &mut mimefactory)?;
|
||||
@@ -938,7 +945,7 @@ fn add_smtp_job(context: &Context, action: Action, mimefactory: &MimeFactory) ->
|
||||
context,
|
||||
action,
|
||||
(if mimefactory.loaded == Loaded::Message {
|
||||
mimefactory.msg.id
|
||||
mimefactory.msg.id.to_u32() as i32
|
||||
} else {
|
||||
0
|
||||
}) as libc::c_int,
|
||||
|
||||
@@ -11,7 +11,7 @@ use crate::dc_tools::*;
|
||||
use crate::error::Error;
|
||||
use crate::events::Event;
|
||||
use crate::job::*;
|
||||
use crate::message::Message;
|
||||
use crate::message::{Message, MsgId};
|
||||
use crate::param::*;
|
||||
use crate::sql;
|
||||
use crate::stock::StockMessage;
|
||||
@@ -475,12 +475,16 @@ pub fn set_kml_sent_timestamp(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_msg_location_id(context: &Context, msg_id: u32, location_id: u32) -> Result<(), Error> {
|
||||
pub fn set_msg_location_id(
|
||||
context: &Context,
|
||||
msg_id: MsgId,
|
||||
location_id: u32,
|
||||
) -> Result<(), Error> {
|
||||
sql::execute(
|
||||
context,
|
||||
&context.sql,
|
||||
"UPDATE msgs SET location_id=? WHERE id=?;",
|
||||
params![location_id, msg_id as i32],
|
||||
params![location_id, msg_id],
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
|
||||
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))
|
||||
},
|
||||
|
||||
@@ -22,6 +22,7 @@ use crate::dc_tools::*;
|
||||
use crate::e2ee::*;
|
||||
use crate::error::Error;
|
||||
use crate::location;
|
||||
use crate::message::MsgId;
|
||||
use crate::message::{self, Message};
|
||||
use crate::param::*;
|
||||
use crate::stock::StockMessage;
|
||||
@@ -107,7 +108,7 @@ impl<'a> MimeFactory<'a> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn load_mdn(context: &'a Context, msg_id: u32) -> Result<MimeFactory, Error> {
|
||||
pub fn load_mdn(context: &'a Context, msg_id: MsgId) -> Result<MimeFactory, Error> {
|
||||
if !context.get_config_bool(Config::MdnsEnabled) {
|
||||
// MDNs not enabled - check this is late, in the job. the
|
||||
// user may have changed its choice while offline ...
|
||||
@@ -653,8 +654,8 @@ impl<'a> MimeFactory<'a> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn load_msg(context: &Context, msg_id: u32) -> Result<MimeFactory, Error> {
|
||||
ensure!(msg_id > DC_CHAT_ID_LAST_SPECIAL, "Invalid chat id");
|
||||
pub fn load_msg(context: &Context, msg_id: MsgId) -> Result<MimeFactory, Error> {
|
||||
ensure!(!msg_id.is_special(), "Invalid chat id");
|
||||
|
||||
let msg = Message::load_from_db(context, msg_id)?;
|
||||
let chat = Chat::load_from_db(context, msg.chat_id)?;
|
||||
@@ -720,7 +721,7 @@ impl<'a> MimeFactory<'a> {
|
||||
}
|
||||
let row = context.sql.query_row(
|
||||
"SELECT mime_in_reply_to, mime_references FROM msgs WHERE id=?",
|
||||
params![factory.msg.id as i32],
|
||||
params![factory.msg.id],
|
||||
|row| {
|
||||
let in_reply_to: String = row.get(0)?;
|
||||
let references: String = row.get(1)?;
|
||||
|
||||
@@ -49,6 +49,14 @@ pub enum Param {
|
||||
/// For Messages
|
||||
Error = b'L',
|
||||
/// For Messages: space-separated list of messaged IDs of forwarded copies.
|
||||
///
|
||||
/// This is used when a [Message] is in the
|
||||
/// [MessageState::OutPending] state but is already forwarded.
|
||||
/// In this case the forwarded messages are written to the
|
||||
/// database and their message IDs are added to this parameter of
|
||||
/// the original message, which is also saved in the database.
|
||||
/// When the original message is then finally sent this parameter
|
||||
/// is used to also send all the forwarded messages.
|
||||
PrepForwards = b'P',
|
||||
/// For Jobs
|
||||
SetLatitude = b'l',
|
||||
|
||||
Reference in New Issue
Block a user