@@ -114,18 +114,20 @@ impl MsgId {
114
114
. unwrap_or_default ( ) )
115
115
}
116
116
117
- /// Put message into trash chat and delete message text .
117
+ /// Put message into trash chat or delete it .
118
118
///
119
119
/// It means the message is deleted locally, but not on the server.
120
- /// We keep some infos to
121
- /// 1. not download the same message again
122
- /// 2. be able to delete the message on the server if we want to
123
120
///
124
- /// * `on_server`: Delete the message on the server also if it is seen on IMAP later, but only
125
- /// if all parts of the message are trashed with this flag. `true` if the user explicitly
126
- /// deletes the message. As for trashing a partially downloaded message when replacing it with
127
- /// a fully downloaded one, see `receive_imf::add_parts()`.
121
+ /// * `on_server`: Keep some info to delete the message on the server also if it is seen on IMAP
122
+ /// later.
128
123
pub ( crate ) async fn trash ( self , context : & Context , on_server : bool ) -> Result < ( ) > {
124
+ if !on_server {
125
+ context
126
+ . sql
127
+ . execute ( "DELETE FROM msgs WHERE id=?1" , ( self , ) )
128
+ . await ?;
129
+ return Ok ( ( ) ) ;
130
+ }
129
131
context
130
132
. sql
131
133
. execute (
@@ -134,7 +136,7 @@ impl MsgId {
134
136
// still adds to the db if chat_id is TRASH.
135
137
"INSERT OR REPLACE INTO msgs (id, rfc724_mid, timestamp, chat_id, deleted)
136
138
SELECT ?1, rfc724_mid, timestamp, ?, ? FROM msgs WHERE id=?1" ,
137
- ( self , DC_CHAT_ID_TRASH , on_server ) ,
139
+ ( self , DC_CHAT_ID_TRASH , true ) ,
138
140
)
139
141
. await ?;
140
142
@@ -1668,11 +1670,15 @@ pub(crate) async fn get_mime_headers(context: &Context, msg_id: MsgId) -> Result
1668
1670
1669
1671
/// Delete a single message from the database, including references in other tables.
1670
1672
/// This may be called in batches; the final events are emitted in delete_msgs_locally_done() then.
1671
- pub ( crate ) async fn delete_msg_locally ( context : & Context , msg : & Message ) -> Result < ( ) > {
1673
+ pub ( crate ) async fn delete_msg_locally (
1674
+ context : & Context ,
1675
+ msg : & Message ,
1676
+ keep_tombstone : bool ,
1677
+ ) -> Result < ( ) > {
1672
1678
if msg. location_id > 0 {
1673
1679
delete_poi_location ( context, msg. location_id ) . await ?;
1674
1680
}
1675
- let on_server = true ;
1681
+ let on_server = keep_tombstone ;
1676
1682
msg. id
1677
1683
. trash ( context, on_server)
1678
1684
. await
@@ -1740,6 +1746,7 @@ pub async fn delete_msgs_ex(
1740
1746
let mut modified_chat_ids = HashSet :: new ( ) ;
1741
1747
let mut deleted_rfc724_mid = Vec :: new ( ) ;
1742
1748
let mut res = Ok ( ( ) ) ;
1749
+ let mut msgs = Vec :: with_capacity ( msg_ids. len ( ) ) ;
1743
1750
1744
1751
for & msg_id in msg_ids {
1745
1752
let msg = Message :: load_from_db ( context, msg_id) . await ?;
@@ -1757,18 +1764,24 @@ pub async fn delete_msgs_ex(
1757
1764
1758
1765
let target = context. get_delete_msgs_target ( ) . await ?;
1759
1766
let update_db = |trans : & mut rusqlite:: Transaction | {
1760
- trans. execute (
1767
+ // NB: If the message isn't sent yet, keeping its tombstone is unnecessary, but safe.
1768
+ let keep_tombstone = trans. execute (
1761
1769
"UPDATE imap SET target=? WHERE rfc724_mid=?" ,
1762
- ( target, msg. rfc724_mid ) ,
1763
- ) ?;
1770
+ ( & target, msg. rfc724_mid ) ,
1771
+ ) ? == 0
1772
+ || !target. is_empty ( ) ;
1764
1773
trans. execute ( "DELETE FROM smtp WHERE msg_id=?" , ( msg_id, ) ) ?;
1765
- Ok ( ( ) )
1774
+ Ok ( keep_tombstone )
1766
1775
} ;
1767
- if let Err ( e) = context. sql . transaction ( update_db) . await {
1768
- error ! ( context, "delete_msgs: failed to update db: {e:#}." ) ;
1769
- res = Err ( e) ;
1770
- continue ;
1771
- }
1776
+ let keep_tombstone = match context. sql . transaction ( update_db) . await {
1777
+ Ok ( v) => v,
1778
+ Err ( e) => {
1779
+ error ! ( context, "delete_msgs: failed to update db: {e:#}." ) ;
1780
+ res = Err ( e) ;
1781
+ continue ;
1782
+ }
1783
+ } ;
1784
+ msgs. push ( ( msg_id, keep_tombstone) ) ;
1772
1785
}
1773
1786
res?;
1774
1787
@@ -1797,9 +1810,9 @@ pub async fn delete_msgs_ex(
1797
1810
. await ?;
1798
1811
}
1799
1812
1800
- for & msg_id in msg_ids {
1813
+ for ( msg_id, keep_tombstone ) in msgs {
1801
1814
let msg = Message :: load_from_db ( context, msg_id) . await ?;
1802
- delete_msg_locally ( context, & msg) . await ?;
1815
+ delete_msg_locally ( context, & msg, keep_tombstone ) . await ?;
1803
1816
}
1804
1817
delete_msgs_locally_done ( context, msg_ids, modified_chat_ids) . await ?;
1805
1818
@@ -1809,6 +1822,23 @@ pub async fn delete_msgs_ex(
1809
1822
Ok ( ( ) )
1810
1823
}
1811
1824
1825
+ /// Removes from the database a locally deleted message that also doesn't have a server UID.
1826
+ pub ( crate ) async fn prune_tombstone ( context : & Context , rfc724_mid : & str ) -> Result < ( ) > {
1827
+ context
1828
+ . sql
1829
+ . execute (
1830
+ "DELETE FROM msgs
1831
+ WHERE rfc724_mid=?
1832
+ AND chat_id=?
1833
+ AND NOT EXISTS (
1834
+ SELECT * FROM imap WHERE msgs.rfc724_mid=rfc724_mid AND target!=''
1835
+ )" ,
1836
+ ( rfc724_mid, DC_CHAT_ID_TRASH ) ,
1837
+ )
1838
+ . await ?;
1839
+ Ok ( ( ) )
1840
+ }
1841
+
1812
1842
/// Marks requested messages as seen.
1813
1843
pub async fn markseen_msgs ( context : & Context , msg_ids : Vec < MsgId > ) -> Result < ( ) > {
1814
1844
if msg_ids. is_empty ( ) {
0 commit comments