deltachat/
update_helper.rs

1//! # Functions to update timestamps.
2
3use anyhow::Result;
4
5use crate::chat::ChatId;
6use crate::contact::ContactId;
7use crate::context::Context;
8use crate::param::{Param, Params};
9
10impl Context {
11    /// Updates a contact's timestamp, if reasonable.
12    /// Returns true if the caller shall update the settings belonging to the scope.
13    /// (if we have a ContactId type at some point, the function should go there)
14    pub(crate) async fn update_contacts_timestamp(
15        &self,
16        contact_id: ContactId,
17        scope: Param,
18        new_timestamp: i64,
19    ) -> Result<bool> {
20        self.sql
21            .transaction(|transaction| {
22                let mut param: Params = transaction.query_row(
23                    "SELECT param FROM contacts WHERE id=?",
24                    [contact_id],
25                    |row| {
26                        let param: String = row.get(0)?;
27                        Ok(param.parse().unwrap_or_default())
28                    },
29                )?;
30                let update = param.update_timestamp(scope, new_timestamp)?;
31                if update {
32                    transaction.execute(
33                        "UPDATE contacts SET param=? WHERE id=?",
34                        (param.to_string(), contact_id),
35                    )?;
36                }
37                Ok(update)
38            })
39            .await
40    }
41}
42
43impl ChatId {
44    /// Updates a chat id's timestamp on disk, if reasonable.
45    /// Returns true if the caller shall update the settings belonging to the scope.
46    pub(crate) async fn update_timestamp(
47        &self,
48        context: &Context,
49        scope: Param,
50        new_timestamp: i64,
51    ) -> Result<bool> {
52        context
53            .sql
54            .transaction(|transaction| {
55                let mut param: Params =
56                    transaction.query_row("SELECT param FROM chats WHERE id=?", [self], |row| {
57                        let param: String = row.get(0)?;
58                        Ok(param.parse().unwrap_or_default())
59                    })?;
60                let update = param.update_timestamp(scope, new_timestamp)?;
61                if update {
62                    transaction.execute(
63                        "UPDATE chats SET param=? WHERE id=?",
64                        (param.to_string(), self),
65                    )?;
66                }
67                Ok(update)
68            })
69            .await
70    }
71}
72
73impl Params {
74    /// Updates a param's timestamp in memory, if reasonable.
75    /// Returns true if the caller shall update the settings belonging to the scope.
76    pub(crate) fn update_timestamp(&mut self, scope: Param, new_timestamp: i64) -> Result<bool> {
77        let old_timestamp = self.get_i64(scope).unwrap_or_default();
78        if new_timestamp >= old_timestamp {
79            self.set_i64(scope, new_timestamp);
80            return Ok(true);
81        }
82        Ok(false)
83    }
84}
85
86#[cfg(test)]
87mod tests {
88    use super::*;
89    use crate::chat::Chat;
90    use crate::receive_imf::receive_imf;
91    use crate::test_utils::TestContext;
92    use crate::tools::time;
93
94    #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
95    async fn test_params_update_timestamp() -> Result<()> {
96        let mut params = Params::new();
97        let ts = time();
98
99        assert!(params.update_timestamp(Param::LastSubject, ts)?);
100        assert!(params.update_timestamp(Param::LastSubject, ts)?); // same timestamp -> update
101        assert!(params.update_timestamp(Param::LastSubject, ts + 10)?);
102        assert!(!params.update_timestamp(Param::LastSubject, ts)?); // `ts` is now too old
103        assert!(!params.update_timestamp(Param::LastSubject, 0)?);
104        assert_eq!(params.get_i64(Param::LastSubject).unwrap(), ts + 10);
105
106        assert!(params.update_timestamp(Param::GroupNameTimestamp, 0)?); // stay unset -> update ...
107        assert!(params.update_timestamp(Param::GroupNameTimestamp, 0)?); // ... also on multiple calls
108        assert_eq!(params.get_i64(Param::GroupNameTimestamp).unwrap(), 0);
109
110        assert!(!params.update_timestamp(Param::AvatarTimestamp, -1)?);
111        assert_eq!(params.get_i64(Param::AvatarTimestamp), None);
112
113        Ok(())
114    }
115
116    #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
117    async fn test_out_of_order_subject() -> Result<()> {
118        let t = TestContext::new_alice().await;
119
120        receive_imf(
121            &t,
122            b"From: Bob Authname <bob@example.org>\n\
123                 To: alice@example.org\n\
124                 Subject: updated subject\n\
125                 Message-ID: <msg2@example.org>\n\
126                 Chat-Version: 1.0\n\
127                 Date: Sun, 22 Mar 2021 23:37:57 +0000\n\
128                 \n\
129                 second message\n",
130            false,
131        )
132        .await?;
133        receive_imf(
134            &t,
135            b"From: Bob Authname <bob@example.org>\n\
136                 To: alice@example.org\n\
137                 Subject: original subject\n\
138                 Message-ID: <msg1@example.org>\n\
139                 Chat-Version: 1.0\n\
140                 Date: Sun, 22 Mar 2021 22:37:57 +0000\n\
141                 \n\
142                 first message\n",
143            false,
144        )
145        .await?;
146
147        let msg = t.get_last_msg().await;
148        let chat = Chat::load_from_db(&t, msg.chat_id).await?;
149        assert_eq!(
150            chat.param.get(Param::LastSubject).unwrap(),
151            "updated subject"
152        );
153
154        Ok(())
155    }
156
157    #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
158    async fn test_out_of_order_group_name() -> Result<()> {
159        let t = TestContext::new_alice().await;
160
161        receive_imf(
162            &t,
163            b"From: Bob Authname <bob@example.org>\n\
164                 To: alice@example.org\n\
165                 Message-ID: <msg1@example.org>\n\
166                 Chat-Version: 1.0\n\
167                 Chat-Group-ID: abcde123456\n\
168                 Chat-Group-Name: initial name\n\
169                 Date: Sun, 22 Mar 2021 01:00:00 +0000\n\
170                 \n\
171                 first message\n",
172            false,
173        )
174        .await?;
175        let msg = t.get_last_msg().await;
176        let chat = Chat::load_from_db(&t, msg.chat_id).await?;
177        assert_eq!(chat.name, "initial name");
178
179        receive_imf(
180            &t,
181            b"From: Bob Authname <bob@example.org>\n\
182                 To: alice@example.org\n\
183                 Message-ID: <msg3@example.org>\n\
184                 Chat-Version: 1.0\n\
185                 Chat-Group-ID: abcde123456\n\
186                 Chat-Group-Name: =?utf-8?q?another=0Aname update?=\n\
187                 Chat-Group-Name-Changed: =?utf-8?q?a=0Aname update?=\n\
188                 Date: Sun, 22 Mar 2021 03:00:00 +0000\n\
189                 \n\
190                 third message\n",
191            false,
192        )
193        .await?;
194        receive_imf(
195            &t,
196            b"From: Bob Authname <bob@example.org>\n\
197                 To: alice@example.org\n\
198                 Message-ID: <msg2@example.org>\n\
199                 Chat-Version: 1.0\n\
200                 Chat-Group-ID: abcde123456\n\
201                 Chat-Group-Name: =?utf-8?q?a=0Aname update?=\n\
202                 Chat-Group-Name-Changed: initial name\n\
203                 Date: Sun, 22 Mar 2021 02:00:00 +0000\n\
204                 \n\
205                 second message\n",
206            false,
207        )
208        .await?;
209        let msg = t.get_last_msg().await;
210        let chat = Chat::load_from_db(&t, msg.chat_id).await?;
211        assert_eq!(chat.name, "another name update");
212
213        // Assert that the \n was correctly removed from the group name also in the system message
214        assert_eq!(msg.text.contains('\n'), false);
215
216        // This doesn't update the name because Date is the same and name is greater.
217        receive_imf(
218            &t,
219            b"From: Bob Authname <bob@example.org>\n\
220                 To: alice@example.org\n\
221                 Message-ID: <msg4@example.org>\n\
222                 Chat-Version: 1.0\n\
223                 Chat-Group-ID: abcde123456\n\
224                 Chat-Group-Name: another name update 4\n\
225                 Chat-Group-Name-Changed: another name update\n\
226                 Date: Sun, 22 Mar 2021 03:00:00 +0000\n\
227                 \n\
228                 4th message\n",
229            false,
230        )
231        .await?;
232        let chat = Chat::load_from_db(&t, chat.id).await?;
233        assert_eq!(chat.name, "another name update");
234
235        // This updates the name because Date is the same and name is lower.
236        receive_imf(
237            &t,
238            b"From: Bob Authname <bob@example.org>\n\
239                 To: alice@example.org\n\
240                 Message-ID: <msg5@example.org>\n\
241                 Chat-Version: 1.0\n\
242                 Chat-Group-ID: abcde123456\n\
243                 Chat-Group-Name: another name updat\n\
244                 Chat-Group-Name-Changed: another name update\n\
245                 Date: Sun, 22 Mar 2021 03:00:00 +0000\n\
246                 \n\
247                 5th message\n",
248            false,
249        )
250        .await?;
251        let chat = Chat::load_from_db(&t, chat.id).await?;
252        assert_eq!(chat.name, "another name updat");
253
254        Ok(())
255    }
256}