1use std::collections::BTreeMap;
4use std::collections::BTreeSet;
5use std::time::Duration;
6
7use anyhow::{Context as _, Result, ensure};
8use deltachat_contact_tools::EmailAddress;
9use deltachat_contact_tools::addr_cmp;
10use pgp::composed::SignedPublicKey;
11use rusqlite::OptionalExtension;
12
13use crate::config::Config;
14use crate::configure::EnteredLoginParam;
15use crate::constants::ShowEmails;
16use crate::context::Context;
17use crate::imap;
18use crate::key::DcKey;
19use crate::log::{info, warn};
20use crate::message::MsgId;
21use crate::provider::get_provider_info;
22use crate::sql::Sql;
23use crate::tools::{Time, inc_and_check, time_elapsed};
24use crate::transport::ConfiguredLoginParam;
25
26const DBVERSION: i32 = 68;
27const VERSION_CFG: &str = "dbversion";
28const TABLES: &str = include_str!("./tables.sql");
29
30#[cfg(test)]
31tokio::task_local! {
32 static STOP_MIGRATIONS_AT: i32;
33}
34
35pub async fn run(context: &Context, sql: &Sql) -> Result<(bool, bool, bool)> {
36 let mut exists_before_update = false;
37 let mut dbversion_before_update = DBVERSION;
38
39 if !sql
40 .table_exists("config")
41 .await
42 .context("failed to check if config table exists")?
43 {
44 sql.transaction(move |transaction| {
45 transaction.execute_batch(TABLES)?;
46
47 transaction.execute(
49 "INSERT INTO config (keyname, value) VALUES (?, ?);",
50 (VERSION_CFG, format!("{dbversion_before_update}")),
51 )?;
52 Ok(())
53 })
54 .await
55 .context("Creating tables failed")?;
56
57 let mut lock = context.sql.config_cache.write().await;
58 lock.insert(
59 VERSION_CFG.to_string(),
60 Some(format!("{dbversion_before_update}")),
61 );
62 drop(lock);
63 } else {
64 exists_before_update = true;
65 dbversion_before_update = sql
66 .get_raw_config_int(VERSION_CFG)
67 .await?
68 .unwrap_or_default();
69 }
70
71 let dbversion = dbversion_before_update;
72 let mut update_icons = !exists_before_update;
73 let mut disable_server_delete = false;
74 let mut recode_avatar = false;
75
76 if dbversion < 1 {
77 sql.execute_migration(
78 r#"
79CREATE TABLE leftgrps ( id INTEGER PRIMARY KEY, grpid TEXT DEFAULT '');
80CREATE INDEX leftgrps_index1 ON leftgrps (grpid);"#,
81 1,
82 )
83 .await?;
84 }
85 if dbversion < 2 {
86 sql.execute_migration(
87 "ALTER TABLE contacts ADD COLUMN authname TEXT DEFAULT '';",
88 2,
89 )
90 .await?;
91 }
92 if dbversion < 7 {
93 sql.execute_migration(
94 "CREATE TABLE keypairs (\
95 id INTEGER PRIMARY KEY, \
96 addr TEXT DEFAULT '' COLLATE NOCASE, \
97 is_default INTEGER DEFAULT 0, \
98 private_key, \
99 public_key, \
100 created INTEGER DEFAULT 0);",
101 7,
102 )
103 .await?;
104 }
105 if dbversion < 10 {
106 sql.execute_migration(
107 "CREATE TABLE acpeerstates (\
108 id INTEGER PRIMARY KEY, \
109 addr TEXT DEFAULT '' COLLATE NOCASE, \
110 last_seen INTEGER DEFAULT 0, \
111 last_seen_autocrypt INTEGER DEFAULT 0, \
112 public_key, \
113 prefer_encrypted INTEGER DEFAULT 0); \
114 CREATE INDEX acpeerstates_index1 ON acpeerstates (addr);",
115 10,
116 )
117 .await?;
118 }
119 if dbversion < 12 {
120 sql.execute_migration(
121 r#"
122CREATE TABLE msgs_mdns ( msg_id INTEGER, contact_id INTEGER);
123CREATE INDEX msgs_mdns_index1 ON msgs_mdns (msg_id);"#,
124 12,
125 )
126 .await?;
127 }
128 if dbversion < 17 {
129 sql.execute_migration(
130 r#"
131ALTER TABLE chats ADD COLUMN archived INTEGER DEFAULT 0;
132CREATE INDEX chats_index2 ON chats (archived);
133ALTER TABLE msgs ADD COLUMN starred INTEGER DEFAULT 0;
134CREATE INDEX msgs_index5 ON msgs (starred);"#,
135 17,
136 )
137 .await?;
138 }
139 if dbversion < 18 {
140 sql.execute_migration(
141 r#"
142ALTER TABLE acpeerstates ADD COLUMN gossip_timestamp INTEGER DEFAULT 0;
143ALTER TABLE acpeerstates ADD COLUMN gossip_key;"#,
144 18,
145 )
146 .await?;
147 }
148 if dbversion < 27 {
149 sql.execute_migration(
152 r#"
153DELETE FROM msgs WHERE chat_id=1 OR chat_id=2;
154CREATE INDEX chats_contacts_index2 ON chats_contacts (contact_id);
155ALTER TABLE msgs ADD COLUMN timestamp_sent INTEGER DEFAULT 0;
156ALTER TABLE msgs ADD COLUMN timestamp_rcvd INTEGER DEFAULT 0;"#,
157 27,
158 )
159 .await?;
160 }
161 if dbversion < 34 {
162 sql.execute_migration(
163 r#"
164ALTER TABLE msgs ADD COLUMN hidden INTEGER DEFAULT 0;
165ALTER TABLE msgs_mdns ADD COLUMN timestamp_sent INTEGER DEFAULT 0;
166ALTER TABLE acpeerstates ADD COLUMN public_key_fingerprint TEXT DEFAULT '';
167ALTER TABLE acpeerstates ADD COLUMN gossip_key_fingerprint TEXT DEFAULT '';
168CREATE INDEX acpeerstates_index3 ON acpeerstates (public_key_fingerprint);
169CREATE INDEX acpeerstates_index4 ON acpeerstates (gossip_key_fingerprint);"#,
170 34,
171 )
172 .await?;
173 }
174 if dbversion < 39 {
175 sql.execute_migration(
176 r#"
177CREATE TABLE tokens (
178 id INTEGER PRIMARY KEY,
179 namespc INTEGER DEFAULT 0,
180 foreign_id INTEGER DEFAULT 0,
181 token TEXT DEFAULT '',
182 timestamp INTEGER DEFAULT 0
183);
184ALTER TABLE acpeerstates ADD COLUMN verified_key;
185ALTER TABLE acpeerstates ADD COLUMN verified_key_fingerprint TEXT DEFAULT '';
186CREATE INDEX acpeerstates_index5 ON acpeerstates (verified_key_fingerprint);"#,
187 39,
188 )
189 .await?;
190 }
191 if dbversion < 40 {
192 sql.execute_migration("ALTER TABLE jobs ADD COLUMN thread INTEGER DEFAULT 0;", 40)
193 .await?;
194 }
195 if dbversion < 44 {
196 sql.execute_migration("ALTER TABLE msgs ADD COLUMN mime_headers TEXT;", 44)
197 .await?;
198 }
199 if dbversion < 46 {
200 sql.execute_migration(
201 r#"
202ALTER TABLE msgs ADD COLUMN mime_in_reply_to TEXT;
203ALTER TABLE msgs ADD COLUMN mime_references TEXT;"#,
204 46,
205 )
206 .await?;
207 }
208 if dbversion < 47 {
209 sql.execute_migration("ALTER TABLE jobs ADD COLUMN tries INTEGER DEFAULT 0;", 47)
210 .await?;
211 }
212 if dbversion < 48 {
213 sql.execute_migration(
215 "ALTER TABLE msgs ADD COLUMN move_state INTEGER DEFAULT 1;",
216 48,
217 )
218 .await?;
219 }
220 if dbversion < 49 {
221 sql.execute_migration(
222 "ALTER TABLE chats ADD COLUMN gossiped_timestamp INTEGER DEFAULT 0;",
223 49,
224 )
225 .await?;
226 }
227 if dbversion < 50 {
228 if exists_before_update {
232 sql.set_raw_config_int("show_emails", ShowEmails::All as i32)
233 .await?;
234 }
235 sql.set_db_version(50).await?;
236 }
237 if dbversion < 53 {
238 sql.execute_migration(
241 r#"
242CREATE TABLE locations (
243 id INTEGER PRIMARY KEY AUTOINCREMENT,
244 latitude REAL DEFAULT 0.0,
245 longitude REAL DEFAULT 0.0,
246 accuracy REAL DEFAULT 0.0,
247 timestamp INTEGER DEFAULT 0,
248 chat_id INTEGER DEFAULT 0,
249 from_id INTEGER DEFAULT 0
250);"
251CREATE INDEX locations_index1 ON locations (from_id);
252CREATE INDEX locations_index2 ON locations (timestamp);
253ALTER TABLE chats ADD COLUMN locations_send_begin INTEGER DEFAULT 0;
254ALTER TABLE chats ADD COLUMN locations_send_until INTEGER DEFAULT 0;
255ALTER TABLE chats ADD COLUMN locations_last_sent INTEGER DEFAULT 0;
256CREATE INDEX chats_index3 ON chats (locations_send_until);"#,
257 53,
258 )
259 .await?;
260 }
261 if dbversion < 54 {
262 sql.execute_migration(
263 r#"
264ALTER TABLE msgs ADD COLUMN location_id INTEGER DEFAULT 0;
265CREATE INDEX msgs_index6 ON msgs (location_id);"#,
266 54,
267 )
268 .await?;
269 }
270 if dbversion < 55 {
271 sql.execute_migration(
272 "ALTER TABLE locations ADD COLUMN independent INTEGER DEFAULT 0;",
273 55,
274 )
275 .await?;
276 }
277 if dbversion < 59 {
278 sql.execute_migration(
281 r#"
282CREATE TABLE devmsglabels (id INTEGER PRIMARY KEY AUTOINCREMENT, label TEXT, msg_id INTEGER DEFAULT 0);
283CREATE INDEX devmsglabels_index1 ON devmsglabels (label);"#, 59)
284 .await?;
285 if exists_before_update && sql.get_raw_config_int("bcc_self").await?.is_none() {
286 sql.set_raw_config_int("bcc_self", 1).await?;
287 }
288 }
289
290 if dbversion < 60 {
291 sql.execute_migration(
292 "ALTER TABLE chats ADD COLUMN created_timestamp INTEGER DEFAULT 0;",
293 60,
294 )
295 .await?;
296 }
297 if dbversion < 61 {
298 sql.execute_migration(
299 "ALTER TABLE contacts ADD COLUMN selfavatar_sent INTEGER DEFAULT 0;",
300 61,
301 )
302 .await?;
303 update_icons = true;
304 }
305 if dbversion < 62 {
306 sql.execute_migration(
307 "ALTER TABLE chats ADD COLUMN muted_until INTEGER DEFAULT 0;",
308 62,
309 )
310 .await?;
311 }
312 if dbversion < 63 {
313 sql.execute_migration("UPDATE chats SET grpid='' WHERE type=100", 63)
314 .await?;
315 }
316 if dbversion < 64 {
317 sql.execute_migration("ALTER TABLE msgs ADD COLUMN error TEXT DEFAULT '';", 64)
318 .await?;
319 }
320 if dbversion < 65 {
321 sql.execute_migration(
322 r#"
323ALTER TABLE chats ADD COLUMN ephemeral_timer INTEGER;
324ALTER TABLE msgs ADD COLUMN ephemeral_timer INTEGER DEFAULT 0;
325ALTER TABLE msgs ADD COLUMN ephemeral_timestamp INTEGER DEFAULT 0;"#,
326 65,
327 )
328 .await?;
329 }
330 if dbversion < 66 {
331 update_icons = true;
332 sql.set_db_version(66).await?;
333 }
334 if dbversion < 67 {
335 for prefix in &["", "configured_"] {
336 if let Some(server_flags) = sql
337 .get_raw_config_int(&format!("{prefix}server_flags"))
338 .await?
339 {
340 let imap_socket_flags = server_flags & 0x700;
341 let key = &format!("{prefix}mail_security");
342 match imap_socket_flags {
343 0x100 => sql.set_raw_config_int(key, 2).await?, 0x200 => sql.set_raw_config_int(key, 1).await?, 0x400 => sql.set_raw_config_int(key, 3).await?, _ => sql.set_raw_config_int(key, 0).await?,
347 }
348 let smtp_socket_flags = server_flags & 0x70000;
349 let key = &format!("{prefix}send_security");
350 match smtp_socket_flags {
351 0x10000 => sql.set_raw_config_int(key, 2).await?, 0x20000 => sql.set_raw_config_int(key, 1).await?, 0x40000 => sql.set_raw_config_int(key, 3).await?, _ => sql.set_raw_config_int(key, 0).await?,
355 }
356 }
357 }
358 sql.set_db_version(67).await?;
359 }
360 if dbversion < 68 {
361 sql.execute_migration(
363 "CREATE INDEX IF NOT EXISTS msgs_index7 ON msgs (state, hidden, chat_id);",
364 68,
365 )
366 .await?;
367 }
368 if dbversion < 69 {
369 sql.execute_migration(
370 r#"
371ALTER TABLE chats ADD COLUMN protected INTEGER DEFAULT 0;
372-- 120=group, 130=old verified group
373UPDATE chats SET protected=1, type=120 WHERE type=130;"#,
374 69,
375 )
376 .await?;
377 }
378
379 if dbversion < 71 {
380 if let Ok(addr) = context.get_primary_self_addr().await {
381 if let Ok(domain) = EmailAddress::new(&addr).map(|email| email.domain) {
382 context
383 .set_config_internal(
384 Config::ConfiguredProvider,
385 get_provider_info(&domain).map(|provider| provider.id),
386 )
387 .await?;
388 } else {
389 warn!(context, "Can't parse configured address: {:?}", addr);
390 }
391 }
392
393 sql.set_db_version(71).await?;
394 }
395 if dbversion < 72 && !sql.col_exists("msgs", "mime_modified").await? {
396 sql.execute_migration(
397 r#"
398 ALTER TABLE msgs ADD COLUMN mime_modified INTEGER DEFAULT 0;"#,
399 72,
400 )
401 .await?;
402 }
403 if dbversion < 73 {
404 sql.execute(
405 r#"
406CREATE TABLE imap_sync (folder TEXT PRIMARY KEY, uidvalidity INTEGER DEFAULT 0, uid_next INTEGER DEFAULT 0);"#,
407()
408 )
409 .await?;
410 for c in [
411 "configured_inbox_folder",
412 "configured_sentbox_folder",
413 "configured_mvbox_folder",
414 ] {
415 if let Some(folder) = context.sql.get_raw_config(c).await? {
416 let (uid_validity, last_seen_uid) =
417 imap::get_config_last_seen_uid(context, &folder).await?;
418 if last_seen_uid > 0 {
419 imap::set_uid_next(context, &folder, last_seen_uid + 1).await?;
420 imap::set_uidvalidity(context, &folder, uid_validity).await?;
421 }
422 }
423 }
424 if exists_before_update {
425 disable_server_delete = true;
426
427 if let Some(provider) = context.get_configured_provider().await? {
429 if let Some(defaults) = &provider.config_defaults {
430 if defaults.iter().any(|d| d.key == Config::DeleteServerAfter) {
431 disable_server_delete = false;
432 }
433 }
434 }
435 }
436 sql.set_db_version(73).await?;
437 }
438 if dbversion < 74 {
439 sql.execute_migration("UPDATE contacts SET name='' WHERE name=authname", 74)
440 .await?;
441 }
442 if dbversion < 75 {
443 sql.execute_migration(
444 "ALTER TABLE contacts ADD COLUMN status TEXT DEFAULT '';",
445 75,
446 )
447 .await?;
448 }
449 if dbversion < 76 {
450 sql.execute_migration("ALTER TABLE msgs ADD COLUMN subject TEXT DEFAULT '';", 76)
451 .await?;
452 }
453 if dbversion < 77 {
454 recode_avatar = true;
455 sql.set_db_version(77).await?;
456 }
457 if dbversion < 78 {
458 sql.execute_migration("UPDATE chats SET archived=1 WHERE blocked=2;", 78)
461 .await?;
462 }
463 if dbversion < 79 {
464 sql.execute_migration(
465 r#"
466 ALTER TABLE msgs ADD COLUMN download_state INTEGER DEFAULT 0;
467 "#,
468 79,
469 )
470 .await?;
471 }
472 if dbversion < 80 {
473 sql.execute_migration(
474 r#"CREATE TABLE multi_device_sync (
475id INTEGER PRIMARY KEY AUTOINCREMENT,
476item TEXT DEFAULT '');"#,
477 80,
478 )
479 .await?;
480 }
481 if dbversion < 81 {
482 sql.execute_migration("ALTER TABLE msgs ADD COLUMN hop_info TEXT;", 81)
483 .await?;
484 }
485 if dbversion < 82 {
486 sql.execute_migration(
487 r#"CREATE TABLE imap (
488id INTEGER PRIMARY KEY AUTOINCREMENT,
489rfc724_mid TEXT DEFAULT '', -- Message-ID header
490folder TEXT DEFAULT '', -- IMAP folder
491target TEXT DEFAULT '', -- Destination folder, empty to delete.
492uid INTEGER DEFAULT 0, -- UID
493uidvalidity INTEGER DEFAULT 0,
494UNIQUE (folder, uid, uidvalidity)
495);
496CREATE INDEX imap_folder ON imap(folder);
497CREATE INDEX imap_messageid ON imap(rfc724_mid);
498
499INSERT INTO imap
500(rfc724_mid, folder, target, uid, uidvalidity)
501SELECT
502rfc724_mid,
503server_folder AS folder,
504server_folder AS target,
505server_uid AS uid,
506(SELECT uidvalidity FROM imap_sync WHERE folder=server_folder) AS uidvalidity
507FROM msgs
508WHERE server_uid>0
509ON CONFLICT (folder, uid, uidvalidity)
510DO UPDATE SET rfc724_mid=excluded.rfc724_mid,
511 target=excluded.target;
512"#,
513 82,
514 )
515 .await?;
516 }
517 if dbversion < 83 {
518 sql.execute_migration(
519 "ALTER TABLE imap_sync
520 ADD COLUMN modseq -- Highest modification sequence
521 INTEGER DEFAULT 0",
522 83,
523 )
524 .await?;
525 }
526 if dbversion < 84 {
527 sql.execute_migration(
528 r#"CREATE TABLE msgs_status_updates (
529id INTEGER PRIMARY KEY AUTOINCREMENT,
530msg_id INTEGER,
531update_item TEXT DEFAULT '',
532update_item_read INTEGER DEFAULT 0 -- XXX unused
533);
534CREATE INDEX msgs_status_updates_index1 ON msgs_status_updates (msg_id);"#,
535 84,
536 )
537 .await?;
538 }
539 if dbversion < 85 {
540 sql.execute_migration(
541 r#"CREATE TABLE smtp (
542id INTEGER PRIMARY KEY,
543rfc724_mid TEXT NOT NULL, -- Message-ID
544mime TEXT NOT NULL, -- SMTP payload
545msg_id INTEGER NOT NULL, -- ID of the message in `msgs` table
546recipients TEXT NOT NULL, -- List of recipients separated by space
547retries INTEGER NOT NULL DEFAULT 0 -- Number of failed attempts to send the message
548);
549CREATE INDEX smtp_messageid ON imap(rfc724_mid);
550"#,
551 85,
552 )
553 .await?;
554 }
555 if dbversion < 86 {
556 sql.execute_migration(
557 r#"CREATE TABLE bobstate (
558 id INTEGER PRIMARY KEY AUTOINCREMENT,
559 invite TEXT NOT NULL,
560 next_step INTEGER NOT NULL,
561 chat_id INTEGER NOT NULL
562 );"#,
563 86,
564 )
565 .await?;
566 }
567 if dbversion < 87 {
568 sql.execute_migration(
570 "CREATE INDEX IF NOT EXISTS msgs_index8 ON msgs (ephemeral_timestamp);",
571 87,
572 )
573 .await?;
574 }
575 if dbversion < 88 {
576 sql.execute_migration("DROP TABLE IF EXISTS backup_blobs;", 88)
577 .await?;
578 }
579 if dbversion < 89 {
580 sql.execute_migration(
581 r#"CREATE TABLE imap_markseen (
582 id INTEGER,
583 FOREIGN KEY(id) REFERENCES imap(id) ON DELETE CASCADE
584 );"#,
585 89,
586 )
587 .await?;
588 }
589 if dbversion < 90 {
590 sql.execute_migration(
591 r#"CREATE TABLE smtp_mdns (
592 msg_id INTEGER NOT NULL, -- id of the message in msgs table which requested MDN (DEPRECATED 2024-06-21)
593 from_id INTEGER NOT NULL, -- id of the contact that sent the message, MDN destination
594 rfc724_mid TEXT NOT NULL, -- Message-ID header
595 retries INTEGER NOT NULL DEFAULT 0 -- Number of failed attempts to send MDN
596 );"#,
597 90,
598 )
599 .await?;
600 }
601 if dbversion < 91 {
602 sql.execute_migration(
603 r#"CREATE TABLE smtp_status_updates (
604 msg_id INTEGER NOT NULL UNIQUE, -- msg_id of the webxdc instance with pending updates
605 first_serial INTEGER NOT NULL, -- id in msgs_status_updates
606 last_serial INTEGER NOT NULL, -- id in msgs_status_updates
607 descr TEXT NOT NULL -- text to send along with the updates
608 );"#,
609 91,
610 )
611 .await?;
612 }
613 if dbversion < 92 {
614 sql.execute_migration(
615 r#"CREATE TABLE reactions (
616 msg_id INTEGER NOT NULL, -- id of the message reacted to
617 contact_id INTEGER NOT NULL, -- id of the contact reacting to the message
618 reaction TEXT DEFAULT '' NOT NULL, -- a sequence of emojis separated by spaces
619 PRIMARY KEY(msg_id, contact_id),
620 FOREIGN KEY(msg_id) REFERENCES msgs(id) ON DELETE CASCADE -- delete reactions when message is deleted
621 FOREIGN KEY(contact_id) REFERENCES contacts(id) ON DELETE CASCADE -- delete reactions when contact is deleted
622 )"#,
623 92
624 ).await?;
625 }
626 if dbversion < 93 {
627 sql.execute_migration(
629 "CREATE TABLE sending_domains(domain TEXT PRIMARY KEY, dkim_works INTEGER DEFAULT 0);",
630 93,
631 )
632 .await?;
633 }
634 if dbversion < 94 {
635 sql.execute_migration(
636 "CREATE TABLE new_acpeerstates (
641 id INTEGER PRIMARY KEY,
642 addr TEXT DEFAULT '' COLLATE NOCASE,
643 last_seen INTEGER DEFAULT 0,
644 last_seen_autocrypt INTEGER DEFAULT 0,
645 public_key,
646 prefer_encrypted INTEGER DEFAULT 0,
647 gossip_timestamp INTEGER DEFAULT 0,
648 gossip_key,
649 public_key_fingerprint TEXT DEFAULT '',
650 gossip_key_fingerprint TEXT DEFAULT '',
651 verified_key,
652 verified_key_fingerprint TEXT DEFAULT '',
653 UNIQUE (addr) -- Only one peerstate per address
654 );
655 INSERT OR IGNORE INTO new_acpeerstates SELECT
656 id, addr, last_seen, last_seen_autocrypt, public_key, prefer_encrypted,
657 gossip_timestamp, gossip_key, public_key_fingerprint,
658 gossip_key_fingerprint, verified_key, verified_key_fingerprint
659 FROM acpeerstates;
660 DROP TABLE acpeerstates;
661 ALTER TABLE new_acpeerstates RENAME TO acpeerstates;
662 CREATE INDEX acpeerstates_index1 ON acpeerstates (addr);
663 CREATE INDEX acpeerstates_index3 ON acpeerstates (public_key_fingerprint);
664 CREATE INDEX acpeerstates_index4 ON acpeerstates (gossip_key_fingerprint);
665 CREATE INDEX acpeerstates_index5 ON acpeerstates (verified_key_fingerprint);
666 ",
667 94,
668 )
669 .await?;
670 }
671 if dbversion < 95 {
672 sql.execute_migration(
673 "CREATE TABLE new_chats_contacts (chat_id INTEGER, contact_id INTEGER, UNIQUE(chat_id, contact_id));\
674 INSERT OR IGNORE INTO new_chats_contacts SELECT chat_id, contact_id FROM chats_contacts;\
675 DROP TABLE chats_contacts;\
676 ALTER TABLE new_chats_contacts RENAME TO chats_contacts;\
677 CREATE INDEX chats_contacts_index1 ON chats_contacts (chat_id);\
678 CREATE INDEX chats_contacts_index2 ON chats_contacts (contact_id);",
679 95
680 ).await?;
681 }
682 if dbversion < 96 {
683 sql.execute_migration(
684 "ALTER TABLE acpeerstates ADD COLUMN verifier TEXT DEFAULT '';",
685 96,
686 )
687 .await?;
688 }
689 if dbversion < 97 {
690 sql.execute_migration(
691 "CREATE TABLE dns_cache (
692 hostname TEXT NOT NULL,
693 address TEXT NOT NULL, -- IPv4 or IPv6 address
694 timestamp INTEGER NOT NULL,
695 UNIQUE (hostname, address)
696 )",
697 97,
698 )
699 .await?;
700 }
701 if dbversion < 98 {
702 if exists_before_update && sql.get_raw_config_int("show_emails").await?.is_none() {
703 sql.set_raw_config_int("show_emails", ShowEmails::Off as i32)
704 .await?;
705 }
706 sql.set_db_version(98).await?;
707 }
708 if dbversion < 99 {
709 sql.set_db_version(99).await?;
721 }
722 if dbversion < 100 {
723 sql.execute_migration(
724 "ALTER TABLE msgs ADD COLUMN mime_compressed INTEGER NOT NULL DEFAULT 0",
725 100,
726 )
727 .await?;
728 }
729 if dbversion < 101 {
730 sql.execute_migration(
733 "DROP TABLE smtp;
734 CREATE TABLE smtp (
735 id INTEGER PRIMARY KEY AUTOINCREMENT,
736 rfc724_mid TEXT NOT NULL, -- Message-ID
737 mime TEXT NOT NULL, -- SMTP payload
738 msg_id INTEGER NOT NULL, -- ID of the message in `msgs` table
739 recipients TEXT NOT NULL, -- List of recipients separated by space
740 retries INTEGER NOT NULL DEFAULT 0 -- Number of failed attempts to send the message
741 );
742 ",
743 101,
744 )
745 .await?;
746 }
747
748 if dbversion < 102 {
749 sql.execute_migration(
750 "CREATE TABLE download (
751 msg_id INTEGER NOT NULL -- id of the message stub in msgs table
752 )",
753 102,
754 )
755 .await?;
756 }
757
758 if dbversion < 103 {
760 sql.execute_migration(
761 "ALTER TABLE contacts ADD COLUMN is_bot INTEGER NOT NULL DEFAULT 0",
762 103,
763 )
764 .await?;
765 }
766
767 if dbversion < 104 {
768 sql.execute_migration(
769 "ALTER TABLE acpeerstates
770 ADD COLUMN secondary_verified_key;
771 ALTER TABLE acpeerstates
772 ADD COLUMN secondary_verified_key_fingerprint TEXT DEFAULT '';
773 ALTER TABLE acpeerstates
774 ADD COLUMN secondary_verifier TEXT DEFAULT ''",
775 104,
776 )
777 .await?;
778 }
779
780 if dbversion < 105 {
781 sql.execute_migration(
783 r#"CREATE TABLE new_msgs_status_updates (
784id INTEGER PRIMARY KEY AUTOINCREMENT,
785msg_id INTEGER,
786update_item TEXT DEFAULT '',
787uid TEXT UNIQUE
788);
789INSERT OR IGNORE INTO new_msgs_status_updates SELECT
790 id, msg_id, update_item, NULL
791FROM msgs_status_updates;
792DROP TABLE msgs_status_updates;
793ALTER TABLE new_msgs_status_updates RENAME TO msgs_status_updates;
794CREATE INDEX msgs_status_updates_index1 ON msgs_status_updates (msg_id);
795CREATE INDEX msgs_status_updates_index2 ON msgs_status_updates (uid);
796"#,
797 105,
798 )
799 .await?;
800 }
801
802 if dbversion < 106 {
803 sql.execute_migration(
805 "CREATE TABLE new_config (
806 id INTEGER PRIMARY KEY,
807 keyname TEXT UNIQUE,
808 value TEXT NOT NULL
809 );
810 INSERT OR IGNORE INTO new_config SELECT
811 id, keyname, value
812 FROM config;
813 DROP TABLE config;
814 ALTER TABLE new_config RENAME TO config;
815 CREATE INDEX config_index1 ON config (keyname);",
816 106,
817 )
818 .await?;
819 }
820
821 if dbversion < 107 {
822 sql.execute_migration(
823 "CREATE TABLE new_keypairs (
824 id INTEGER PRIMARY KEY AUTOINCREMENT,
825 private_key UNIQUE NOT NULL,
826 public_key UNIQUE NOT NULL
827 );
828 INSERT OR IGNORE INTO new_keypairs SELECT id, private_key, public_key FROM keypairs;
829
830 INSERT OR IGNORE
831 INTO config (keyname, value)
832 VALUES
833 ('key_id', (SELECT id FROM new_keypairs
834 WHERE private_key=
835 (SELECT private_key FROM keypairs
836 WHERE addr=(SELECT value FROM config WHERE keyname='configured_addr')
837 AND is_default=1)));
838
839 -- We do not drop the old `keypairs` table for now,
840 -- but move it to `old_keypairs`. We can remove it later
841 -- in next migrations. This may be needed for recovery
842 -- in case something is wrong with the migration.
843 ALTER TABLE keypairs RENAME TO old_keypairs;
844 ALTER TABLE new_keypairs RENAME TO keypairs;
845 ",
846 107,
847 )
848 .await?;
849 }
850
851 if dbversion < 108 {
852 let version = 108;
853 let chunk_size = context.get_max_smtp_rcpt_to().await?;
854 sql.transaction(move |trans| {
855 Sql::set_db_version_trans(trans, version)?;
856 let id_max =
857 trans.query_row("SELECT IFNULL((SELECT MAX(id) FROM smtp), 0)", (), |row| {
858 let id_max: i64 = row.get(0)?;
859 Ok(id_max)
860 })?;
861 while let Some((id, rfc724_mid, mime, msg_id, recipients, retries)) = trans
862 .query_row(
863 "SELECT id, rfc724_mid, mime, msg_id, recipients, retries FROM smtp \
864 WHERE id<=? LIMIT 1",
865 (id_max,),
866 |row| {
867 let id: i64 = row.get(0)?;
868 let rfc724_mid: String = row.get(1)?;
869 let mime: String = row.get(2)?;
870 let msg_id: MsgId = row.get(3)?;
871 let recipients: String = row.get(4)?;
872 let retries: i64 = row.get(5)?;
873 Ok((id, rfc724_mid, mime, msg_id, recipients, retries))
874 },
875 )
876 .optional()?
877 {
878 trans.execute("DELETE FROM smtp WHERE id=?", (id,))?;
879 let recipients = recipients.split(' ').collect::<Vec<_>>();
880 for recipients in recipients.chunks(chunk_size) {
881 let recipients = recipients.join(" ");
882 trans.execute(
883 "INSERT INTO smtp (rfc724_mid, mime, msg_id, recipients, retries) \
884 VALUES (?, ?, ?, ?, ?)",
885 (&rfc724_mid, &mime, msg_id, recipients, retries),
886 )?;
887 }
888 }
889 Ok(())
890 })
891 .await
892 .with_context(|| format!("migration failed for version {version}"))?;
893
894 sql.set_db_version_in_cache(version).await?;
895 }
896
897 if dbversion < 109 {
898 sql.execute_migration(
899 r#"ALTER TABLE acpeerstates
900 ADD COLUMN backward_verified_key_id -- What we think the contact has as our verified key
901 INTEGER;
902 UPDATE acpeerstates
903 SET backward_verified_key_id=(SELECT value FROM config WHERE keyname='key_id')
904 WHERE verified_key IS NOT NULL
905 "#,
906 109,
907 )
908 .await?;
909 }
910
911 if dbversion < 110 {
912 sql.execute_migration(
913 "ALTER TABLE keypairs ADD COLUMN addr TEXT DEFAULT '' COLLATE NOCASE;
914 ALTER TABLE keypairs ADD COLUMN is_default INTEGER DEFAULT 0;
915 ALTER TABLE keypairs ADD COLUMN created INTEGER DEFAULT 0;
916 UPDATE keypairs SET addr=(SELECT value FROM config WHERE keyname='configured_addr'), is_default=1;",
917 110,
918 )
919 .await?;
920 }
921
922 if dbversion < 111 {
923 sql.execute_migration(
924 "CREATE TABLE iroh_gossip_peers (msg_id TEXT not NULL, topic TEXT NOT NULL, public_key TEXT NOT NULL)",
925 111,
926 )
927 .await?;
928 }
929
930 if dbversion < 112 {
931 sql.execute_migration(
932 "DROP TABLE iroh_gossip_peers; CREATE TABLE iroh_gossip_peers (msg_id INTEGER not NULL, topic BLOB NOT NULL, public_key BLOB NOT NULL, relay_server TEXT, UNIQUE (public_key, topic)) STRICT",
933 112,
934 )
935 .await?;
936 }
937
938 if dbversion < 113 {
939 sql.execute_migration(
940 "DROP TABLE iroh_gossip_peers; CREATE TABLE iroh_gossip_peers (msg_id INTEGER not NULL, topic BLOB NOT NULL, public_key BLOB NOT NULL, relay_server TEXT, UNIQUE (topic, public_key), PRIMARY KEY(topic, public_key)) STRICT",
941 113,
942 )
943 .await?;
944 }
945
946 if dbversion < 114 {
947 sql.execute_migration("CREATE INDEX reactions_index1 ON reactions (msg_id)", 114)
948 .await?;
949 }
950
951 if dbversion < 115 {
952 sql.execute_migration("ALTER TABLE msgs ADD COLUMN txt_normalized TEXT", 115)
953 .await?;
954 }
955 let mut migration_version: i32 = 115;
956
957 inc_and_check(&mut migration_version, 116)?;
958 if dbversion < migration_version {
959 sql.execute_migration(
962 "ALTER TABLE msgs ADD COLUMN deleted INTEGER NOT NULL DEFAULT 0",
963 migration_version,
964 )
965 .await?;
966 }
967
968 inc_and_check(&mut migration_version, 117)?;
969 if dbversion < migration_version {
970 sql.execute_migration(
971 "CREATE TABLE connection_history (
972 host TEXT NOT NULL, -- server hostname
973 port INTEGER NOT NULL, -- server port
974 alpn TEXT NOT NULL, -- ALPN such as smtp or imap
975 addr TEXT NOT NULL, -- IP address
976 timestamp INTEGER NOT NULL, -- timestamp of the most recent successful connection
977 UNIQUE (host, port, alpn, addr)
978 ) STRICT",
979 migration_version,
980 )
981 .await?;
982 }
983
984 inc_and_check(&mut migration_version, 118)?;
985 if dbversion < migration_version {
986 sql.execute_migration(
987 "CREATE TABLE tokens_new (
988 id INTEGER PRIMARY KEY,
989 namespc INTEGER DEFAULT 0,
990 foreign_key TEXT DEFAULT '',
991 token TEXT DEFAULT '',
992 timestamp INTEGER DEFAULT 0
993 ) STRICT;
994 INSERT INTO tokens_new
995 SELECT t.id, t.namespc, IFNULL(c.grpid, ''), t.token, t.timestamp
996 FROM tokens t LEFT JOIN chats c ON t.foreign_id=c.id;
997 DROP TABLE tokens;
998 ALTER TABLE tokens_new RENAME TO tokens;",
999 migration_version,
1000 )
1001 .await?;
1002 }
1003
1004 inc_and_check(&mut migration_version, 119)?;
1005 if dbversion < migration_version {
1006 sql.execute_migration(
1007 "CREATE TABLE imap_send (
1008 id INTEGER PRIMARY KEY AUTOINCREMENT,
1009 mime TEXT NOT NULL, -- Message content
1010 msg_id INTEGER NOT NULL, -- ID of the message in the `msgs` table
1011 attempts INTEGER NOT NULL DEFAULT 0 -- Number of failed attempts to send the message
1012 )",
1013 migration_version,
1014 )
1015 .await?;
1016 }
1017
1018 inc_and_check(&mut migration_version, 120)?;
1019 if dbversion < migration_version {
1020 sql.execute_migration(
1032 "INSERT OR IGNORE INTO config (keyname, value)
1033 SELECT 'delete_server_after', '0'
1034 FROM config WHERE keyname='configured'
1035 ",
1036 migration_version,
1037 )
1038 .await?;
1039 }
1040
1041 inc_and_check(&mut migration_version, 121)?;
1042 if dbversion < migration_version {
1043 sql.execute_migration(
1044 "CREATE INDEX chats_index4 ON chats (name)",
1045 migration_version,
1046 )
1047 .await?;
1048 }
1049
1050 inc_and_check(&mut migration_version, 122)?;
1051 if dbversion < migration_version {
1052 sql.execute_migration(
1053 "ALTER TABLE tokens ADD COLUMN foreign_id INTEGER NOT NULL DEFAULT 0",
1054 migration_version,
1055 )
1056 .await?;
1057 }
1058
1059 inc_and_check(&mut migration_version, 123)?;
1060 if dbversion < migration_version {
1061 sql.execute_migration(
1063 "CREATE TABLE new_msgs_status_updates (
1064 id INTEGER PRIMARY KEY AUTOINCREMENT,
1065 msg_id INTEGER,
1066 update_item TEXT DEFAULT '',
1067 uid TEXT UNIQUE,
1068 FOREIGN KEY(msg_id) REFERENCES msgs(id) ON DELETE CASCADE
1069 );
1070 INSERT OR IGNORE INTO new_msgs_status_updates SELECT
1071 id, msg_id, update_item, uid
1072 FROM msgs_status_updates;
1073 DROP TABLE msgs_status_updates;
1074 ALTER TABLE new_msgs_status_updates RENAME TO msgs_status_updates;
1075 CREATE INDEX msgs_status_updates_index1 ON msgs_status_updates (msg_id);
1076 CREATE INDEX msgs_status_updates_index2 ON msgs_status_updates (uid);
1077 ",
1078 migration_version,
1079 )
1080 .await?;
1081 }
1082
1083 inc_and_check(&mut migration_version, 124)?;
1084 if dbversion < migration_version {
1085 sql.execute_migration(
1087 "UPDATE chats
1088 SET protected=1 -- ProtectionStatus::Protected
1089 WHERE type==100 -- Chattype::Single
1090 AND EXISTS (
1091 SELECT 1 FROM chats_contacts cc
1092 WHERE cc.chat_id==chats.id
1093 AND cc.contact_id=1
1094 )
1095 ",
1096 migration_version,
1097 )
1098 .await?;
1099 }
1100
1101 inc_and_check(&mut migration_version, 125)?;
1102 if dbversion < migration_version {
1103 sql.execute_migration(
1104 "CREATE TABLE http_cache (
1105 url TEXT PRIMARY KEY,
1106 expires INTEGER NOT NULL, -- When the cache entry is considered expired, timestamp in seconds.
1107 blobname TEXT NOT NULL,
1108 mimetype TEXT NOT NULL DEFAULT '', -- MIME type extracted from Content-Type header.
1109 encoding TEXT NOT NULL DEFAULT '' -- Encoding from Content-Type header.
1110 ) STRICT",
1111 migration_version,
1112 )
1113 .await?;
1114 }
1115
1116 inc_and_check(&mut migration_version, 126)?;
1117 if dbversion < migration_version {
1118 sql.execute_migration(
1120 "DROP TABLE http_cache;
1121 CREATE TABLE http_cache (
1122 url TEXT PRIMARY KEY,
1123 expires INTEGER NOT NULL, -- When the cache entry is considered expired, timestamp in seconds.
1124 stale INTEGER NOT NULL, -- When the cache entry is considered stale, timestamp in seconds.
1125 blobname TEXT NOT NULL,
1126 mimetype TEXT NOT NULL DEFAULT '', -- MIME type extracted from Content-Type header.
1127 encoding TEXT NOT NULL DEFAULT '' -- Encoding from Content-Type header.
1128 ) STRICT",
1129 migration_version,
1130 )
1131 .await?;
1132 }
1133
1134 inc_and_check(&mut migration_version, 127)?;
1135 if dbversion < migration_version {
1136 sql.execute_migration(
1138 "INSERT OR IGNORE INTO config (keyname, value)
1139 SELECT 'bcc_self', '1'
1140 FROM config WHERE keyname='delete_server_after' AND value='0'
1141 ",
1142 migration_version,
1143 )
1144 .await?;
1145 }
1146
1147 inc_and_check(&mut migration_version, 128)?;
1148 if dbversion < migration_version {
1149 sql.execute_migration(
1155 "ALTER TABLE chats_contacts
1156 ADD COLUMN add_timestamp NOT NULL DEFAULT 0;
1157 ALTER TABLE chats_contacts
1158 ADD COLUMN remove_timestamp NOT NULL DEFAULT 0;
1159 ",
1160 migration_version,
1161 )
1162 .await?;
1163 }
1164
1165 inc_and_check(&mut migration_version, 129)?;
1166 if dbversion < migration_version {
1167 sql.execute_migration(
1175 "INSERT OR IGNORE INTO config (keyname, value)
1176 SELECT 'bcc_self', '1'
1177 FROM config WHERE keyname='delete_server_after' AND value!='1'
1178 ",
1179 migration_version,
1180 )
1181 .await?;
1182 }
1183
1184 inc_and_check(&mut migration_version, 130)?;
1185 if dbversion < migration_version {
1186 sql.execute_migration(
1187 "
1188CREATE TABLE gossip_timestamp (
1189 chat_id INTEGER NOT NULL,
1190 fingerprint TEXT NOT NULL, -- Upper-case fingerprint of the key.
1191 timestamp INTEGER NOT NULL,
1192 UNIQUE (chat_id, fingerprint)
1193) STRICT;
1194CREATE INDEX gossip_timestamp_index ON gossip_timestamp (chat_id, fingerprint);
1195",
1196 migration_version,
1197 )
1198 .await?;
1199 }
1200
1201 inc_and_check(&mut migration_version, 131)?;
1202 if dbversion < migration_version {
1203 let entered_param = EnteredLoginParam::load(context).await?;
1204 let configured_param = ConfiguredLoginParam::load_legacy(context).await?;
1205
1206 sql.execute_migration_transaction(
1207 |transaction| {
1208 transaction.execute(
1209 "CREATE TABLE transports (
1210 id INTEGER PRIMARY KEY AUTOINCREMENT,
1211 addr TEXT NOT NULL,
1212 entered_param TEXT NOT NULL,
1213 configured_param TEXT NOT NULL,
1214 UNIQUE(addr)
1215 )",
1216 (),
1217 )?;
1218 if let Some(configured_param) = configured_param {
1219 transaction.execute(
1220 "INSERT INTO transports (addr, entered_param, configured_param)
1221 VALUES (?, ?, ?)",
1222 (
1223 configured_param.addr.clone(),
1224 serde_json::to_string(&entered_param)?,
1225 configured_param.into_json()?,
1226 ),
1227 )?;
1228 }
1229
1230 Ok(())
1231 },
1232 migration_version,
1233 )
1234 .await?;
1235 }
1236
1237 inc_and_check(&mut migration_version, 132)?;
1238 if dbversion < migration_version {
1239 let start = Time::now();
1240 sql.execute_migration_transaction(|t| migrate_key_contacts(context, t), migration_version)
1241 .await?;
1242 info!(
1243 context,
1244 "key-contacts migration took {:?} in total.",
1245 time_elapsed(&start),
1246 );
1247 context
1249 .set_config_internal(Config::LastHousekeeping, None)
1250 .await?;
1251 }
1252
1253 inc_and_check(&mut migration_version, 133)?;
1254 if dbversion < migration_version {
1255 sql.execute_migration(
1257 "UPDATE chats SET protected=0 WHERE protected!=1",
1258 migration_version,
1259 )
1260 .await?;
1261 }
1262
1263 inc_and_check(&mut migration_version, 134)?; inc_and_check(&mut migration_version, 135)?;
1266 if dbversion < migration_version {
1267 sql.execute_migration(
1268 "CREATE TABLE stats_securejoin_sources(
1269 source INTEGER PRIMARY KEY,
1270 count INTEGER NOT NULL DEFAULT 0
1271 ) STRICT;
1272 CREATE TABLE stats_securejoin_uipaths(
1273 uipath INTEGER PRIMARY KEY,
1274 count INTEGER NOT NULL DEFAULT 0
1275 ) STRICT;
1276 CREATE TABLE stats_securejoin_invites(
1277 already_existed INTEGER NOT NULL,
1278 already_verified INTEGER NOT NULL,
1279 type TEXT NOT NULL
1280 ) STRICT;
1281 CREATE TABLE stats_msgs(
1282 chattype INTEGER PRIMARY KEY,
1283 verified INTEGER NOT NULL DEFAULT 0,
1284 unverified_encrypted INTEGER NOT NULL DEFAULT 0,
1285 unencrypted INTEGER NOT NULL DEFAULT 0,
1286 only_to_self INTEGER NOT NULL DEFAULT 0,
1287 last_counted_msg_id INTEGER NOT NULL DEFAULT 0
1288 ) STRICT;",
1289 migration_version,
1290 )
1291 .await?;
1292 }
1293
1294 inc_and_check(&mut migration_version, 136)?;
1295 if dbversion < migration_version {
1296 sql.execute_migration(
1297 "CREATE TABLE stats_sending_enabled_events(timestamp INTEGER NOT NULL) STRICT;
1298 CREATE TABLE stats_sending_disabled_events(timestamp INTEGER NOT NULL) STRICT;",
1299 migration_version,
1300 )
1301 .await?;
1302 }
1303
1304 inc_and_check(&mut migration_version, 137)?;
1305 if dbversion < migration_version {
1306 sql.execute_migration(
1307 "DELETE FROM config WHERE keyname IN (
1308 'configured',
1309 'configured_imap_certificate_checks',
1310 'configured_imap_servers',
1311 'configured_mail_port',
1312 'configured_mail_pw',
1313 'configured_mail_security',
1314 'configured_mail_server',
1315 'configured_mail_user',
1316 'configured_send_port',
1317 'configured_send_pw',
1318 'configured_send_security',
1319 'configured_send_server',
1320 'configured_send_user',
1321 'configured_server_flags',
1322 'configured_smtp_certificate_checks',
1323 'configured_smtp_servers'
1324 )",
1325 migration_version,
1326 )
1327 .await?;
1328 }
1329
1330 inc_and_check(&mut migration_version, 138)?;
1331 if dbversion < migration_version {
1332 sql.execute_migration(
1333 "CREATE TABLE broadcast_secrets(
1334 chat_id INTEGER PRIMARY KEY NOT NULL,
1335 secret TEXT NOT NULL
1336 ) STRICT",
1337 migration_version,
1338 )
1339 .await?;
1340 }
1341
1342 let new_version = sql
1343 .get_raw_config_int(VERSION_CFG)
1344 .await?
1345 .unwrap_or_default();
1346 if new_version != dbversion || !exists_before_update {
1347 let created_db = if exists_before_update {
1348 ""
1349 } else {
1350 "Created new database. "
1351 };
1352 info!(context, "{}Migration done from v{}.", created_db, dbversion);
1353 }
1354 info!(context, "Database version: v{new_version}.");
1355
1356 Ok((update_icons, disable_server_delete, recode_avatar))
1357}
1358
1359fn migrate_key_contacts(
1360 context: &Context,
1361 transaction: &mut rusqlite::Transaction<'_>,
1362) -> std::result::Result<(), anyhow::Error> {
1363 info!(context, "Starting key-contact transition.");
1364
1365 transaction.execute_batch(
1368 "ALTER TABLE contacts ADD COLUMN fingerprint TEXT NOT NULL DEFAULT '';
1369
1370 -- Verifier is an ID of the verifier contact.
1371 -- 0 if the contact is not verified.
1372 ALTER TABLE contacts ADD COLUMN verifier INTEGER NOT NULL DEFAULT 0;
1373
1374 CREATE INDEX contacts_fingerprint_index ON contacts (fingerprint);
1375
1376 CREATE TABLE public_keys (
1377 id INTEGER PRIMARY KEY AUTOINCREMENT,
1378 fingerprint TEXT NOT NULL UNIQUE, -- Upper-case fingerprint of the key.
1379 public_key BLOB NOT NULL -- Binary key, not ASCII-armored
1380 ) STRICT;
1381 CREATE INDEX public_key_index ON public_keys (fingerprint);
1382
1383 INSERT OR IGNORE INTO public_keys (fingerprint, public_key)
1384 SELECT public_key_fingerprint, public_key FROM acpeerstates
1385 WHERE public_key_fingerprint IS NOT NULL AND public_key IS NOT NULL;
1386
1387 INSERT OR IGNORE INTO public_keys (fingerprint, public_key)
1388 SELECT gossip_key_fingerprint, gossip_key FROM acpeerstates
1389 WHERE gossip_key_fingerprint IS NOT NULL AND gossip_key IS NOT NULL;
1390
1391 INSERT OR IGNORE INTO public_keys (fingerprint, public_key)
1392 SELECT verified_key_fingerprint, verified_key FROM acpeerstates
1393 WHERE verified_key_fingerprint IS NOT NULL AND verified_key IS NOT NULL;
1394
1395 INSERT OR IGNORE INTO public_keys (fingerprint, public_key)
1396 SELECT secondary_verified_key_fingerprint, secondary_verified_key FROM acpeerstates
1397 WHERE secondary_verified_key_fingerprint IS NOT NULL AND secondary_verified_key IS NOT NULL;",
1398 )
1399 .context("Creating key-contact tables")?;
1400
1401 let Some(self_addr): Option<String> = transaction
1402 .query_row(
1403 "SELECT value FROM config WHERE keyname='configured_addr'",
1404 (),
1405 |row| row.get(0),
1406 )
1407 .optional()
1408 .context("Step 0")?
1409 else {
1410 info!(
1411 context,
1412 "Not yet configured, no need to migrate key-contacts"
1413 );
1414 return Ok(());
1415 };
1416
1417 let mut autocrypt_key_contacts: BTreeMap<u32, u32> = BTreeMap::new();
1424 let mut autocrypt_key_contacts_with_reset_peerstate: BTreeMap<u32, u32> = BTreeMap::new();
1425 let mut verified_key_contacts: BTreeMap<u32, u32> = BTreeMap::new();
1426 {
1427 let mut verifications: BTreeMap<u32, u32> = BTreeMap::new();
1431
1432 let mut load_contacts_stmt = transaction
1433 .prepare(
1434 "SELECT c.id, c.name, c.addr, c.origin, c.blocked, c.last_seen,
1435 c.authname, c.param, c.status, c.is_bot, c.selfavatar_sent,
1436 IFNULL(p.public_key, p.gossip_key),
1437 p.verified_key, IFNULL(p.verifier, ''),
1438 p.secondary_verified_key, p.secondary_verifier, p.prefer_encrypted
1439 FROM contacts c
1440 INNER JOIN acpeerstates p ON c.addr=p.addr
1441 WHERE c.id > 9
1442 ORDER BY p.last_seen DESC",
1443 )
1444 .context("Step 2")?;
1445
1446 let all_address_contacts: rusqlite::Result<Vec<_>> = load_contacts_stmt
1447 .query_map((), |row| {
1448 let id: i64 = row.get(0)?;
1449 let name: String = row.get(1)?;
1450 let addr: String = row.get(2)?;
1451 let origin: i64 = row.get(3)?;
1452 let blocked: Option<bool> = row.get(4)?;
1453 let last_seen: i64 = row.get(5)?;
1454 let authname: String = row.get(6)?;
1455 let param: String = row.get(7)?;
1456 let status: Option<String> = row.get(8)?;
1457 let is_bot: bool = row.get(9)?;
1458 let selfavatar_sent: i64 = row.get(10)?;
1459 let autocrypt_key = row
1460 .get(11)
1461 .ok()
1462 .and_then(|blob: Vec<u8>| SignedPublicKey::from_slice(&blob).ok());
1463 let verified_key = row
1464 .get(12)
1465 .ok()
1466 .and_then(|blob: Vec<u8>| SignedPublicKey::from_slice(&blob).ok());
1467 let verifier: String = row.get(13)?;
1468 let secondary_verified_key = row
1469 .get(12)
1470 .ok()
1471 .and_then(|blob: Vec<u8>| SignedPublicKey::from_slice(&blob).ok());
1472 let secondary_verifier: String = row.get(15)?;
1473 let prefer_encrypt: u8 = row.get(16)?;
1474 Ok((
1475 id,
1476 name,
1477 addr,
1478 origin,
1479 blocked,
1480 last_seen,
1481 authname,
1482 param,
1483 status,
1484 is_bot,
1485 selfavatar_sent,
1486 autocrypt_key,
1487 verified_key,
1488 verifier,
1489 secondary_verified_key,
1490 secondary_verifier,
1491 prefer_encrypt,
1492 ))
1493 })
1494 .context("Step 3")?
1495 .collect();
1496
1497 let mut insert_contact_stmt = transaction
1498 .prepare(
1499 "INSERT INTO contacts (name, addr, origin, blocked, last_seen,
1500 authname, param, status, is_bot, selfavatar_sent, fingerprint)
1501 VALUES(?,?,?,?,?,?,?,?,?,?,?)",
1502 )
1503 .context("Step 4")?;
1504 let mut fingerprint_to_id_stmt = transaction
1505 .prepare("SELECT id FROM contacts WHERE fingerprint=? AND id>9")
1506 .context("Step 5")?;
1507 let mut original_contact_id_from_addr_stmt = transaction
1508 .prepare("SELECT id FROM contacts WHERE addr=? AND fingerprint='' AND id>9")
1509 .context("Step 6")?;
1510
1511 for row in all_address_contacts? {
1512 let (
1513 original_id,
1514 name,
1515 addr,
1516 origin,
1517 blocked,
1518 last_seen,
1519 authname,
1520 param,
1521 status,
1522 is_bot,
1523 selfavatar_sent,
1524 autocrypt_key,
1525 verified_key,
1526 verifier,
1527 secondary_verified_key,
1528 secondary_verifier,
1529 prefer_encrypt,
1530 ) = row;
1531 let mut insert_contact = |key: SignedPublicKey| -> Result<u32> {
1532 let fingerprint = key.dc_fingerprint().hex();
1533 let existing_contact_id: Option<u32> = fingerprint_to_id_stmt
1534 .query_row((&fingerprint,), |row| row.get(0))
1535 .optional()
1536 .context("Step 7")?;
1537 if let Some(existing_contact_id) = existing_contact_id {
1538 return Ok(existing_contact_id);
1539 }
1540 insert_contact_stmt
1541 .execute((
1542 &name,
1543 &addr,
1544 origin,
1545 blocked,
1546 last_seen,
1547 &authname,
1548 ¶m,
1549 &status,
1550 is_bot,
1551 selfavatar_sent,
1552 fingerprint.clone(),
1553 ))
1554 .context("Step 8")?;
1555 let id = transaction
1556 .last_insert_rowid()
1557 .try_into()
1558 .context("Step 9")?;
1559 info!(
1560 context,
1561 "Inserted new contact id={id} name='{name}' addr='{addr}' fingerprint={fingerprint}"
1562 );
1563 Ok(id)
1564 };
1565 let mut original_contact_id_from_addr = |addr: &str, default: u32| -> Result<u32> {
1566 if addr_cmp(addr, &self_addr) {
1567 Ok(1) } else if addr.is_empty() {
1569 Ok(default)
1570 } else {
1571 Ok(original_contact_id_from_addr_stmt
1572 .query_row((addr,), |row| row.get(0))
1573 .optional()
1574 .with_context(|| format!("Original contact '{addr}' not found"))?
1575 .unwrap_or(default))
1576 }
1577 };
1578
1579 let Some(autocrypt_key) = autocrypt_key else {
1580 continue;
1581 };
1582 let new_id = insert_contact(autocrypt_key).context("Step 10")?;
1583
1584 if prefer_encrypt != 20 {
1587 autocrypt_key_contacts.insert(original_id.try_into().context("Step 11")?, new_id);
1588 } else {
1589 autocrypt_key_contacts_with_reset_peerstate
1590 .insert(original_id.try_into().context("Step 12")?, new_id);
1591 }
1592
1593 let Some(verified_key) = verified_key else {
1594 continue;
1595 };
1596 let new_id = insert_contact(verified_key).context("Step 13")?;
1597 verified_key_contacts.insert(original_id.try_into().context("Step 14")?, new_id);
1598
1599 let verifier_id = if addr_cmp(&verifier, &addr) {
1600 1 } else {
1604 original_contact_id_from_addr(&verifier, new_id).context("Step 15")?
1608 };
1609 verifications.insert(new_id, verifier_id);
1610
1611 let Some(secondary_verified_key) = secondary_verified_key else {
1612 continue;
1613 };
1614 let new_id = insert_contact(secondary_verified_key).context("Step 16")?;
1615 let verifier_id: u32 = if addr_cmp(&secondary_verifier, &addr) {
1616 1 } else {
1618 original_contact_id_from_addr(&secondary_verifier, new_id).context("Step 17")?
1619 };
1620 verifications.entry(new_id).or_insert(verifier_id);
1622 }
1623 info!(
1624 context,
1625 "Created key-contacts identified by autocrypt key: {autocrypt_key_contacts:?}"
1626 );
1627 info!(
1628 context,
1629 "Created key-contacts with 'reset' peerstate identified by autocrypt key: {autocrypt_key_contacts_with_reset_peerstate:?}"
1630 );
1631 info!(
1632 context,
1633 "Created key-contacts identified by verified key: {verified_key_contacts:?}"
1634 );
1635
1636 for (&new_contact, &verifier_original_contact) in &verifications {
1637 let verifier = if verifier_original_contact == 1 {
1638 1 } else if verifier_original_contact == new_contact {
1640 new_contact } else {
1642 match verified_key_contacts.get(&verifier_original_contact) {
1645 Some(v) => *v,
1646 None => {
1647 warn!(
1648 context,
1649 "Couldn't find key-contact for {verifier_original_contact} who verified {new_contact}"
1650 );
1651 continue;
1652 }
1653 }
1654 };
1655 transaction
1656 .execute(
1657 "UPDATE contacts SET verifier=? WHERE id=?",
1658 (verifier, new_contact),
1659 )
1660 .context("Step 18")?;
1661 }
1662 info!(context, "Migrated verifications: {verifications:?}");
1663 }
1664
1665 let mut orphaned_contacts: BTreeSet<u32> = transaction
1670 .prepare("SELECT id FROM contacts WHERE id>9")
1671 .context("Step 19")?
1672 .query_map((), |row| row.get::<usize, u32>(0))
1673 .context("Step 20")?
1674 .collect::<Result<BTreeSet<u32>, rusqlite::Error>>()
1675 .context("Step 21")?;
1676
1677 {
1678 let mut stmt = transaction
1679 .prepare(
1680 "SELECT c.id, c.type, c.grpid, c.protected
1681 FROM chats c
1682 WHERE id>9",
1683 )
1684 .context("Step 22")?;
1685 let all_chats = stmt
1686 .query_map((), |row| {
1687 let id: u32 = row.get(0)?;
1688 let typ: u32 = row.get(1)?;
1689 let grpid: String = row.get(2)?;
1690 let protected: u32 = row.get(3)?;
1691 Ok((id, typ, grpid, protected))
1692 })
1693 .context("Step 23")?;
1694 let mut load_chat_contacts_stmt = transaction.prepare(
1695 "SELECT contact_id, add_timestamp>=remove_timestamp FROM chats_contacts
1696 WHERE chat_id=? AND contact_id>9",
1697 )?;
1698 let is_chatmail: Option<String> = transaction
1699 .query_row(
1700 "SELECT value FROM config WHERE keyname='is_chatmail'",
1701 (),
1702 |row| row.get(0),
1703 )
1704 .optional()
1705 .context("Step 23.1")?;
1706 let is_chatmail = is_chatmail
1707 .and_then(|s| s.parse::<i32>().ok())
1708 .unwrap_or_default()
1709 != 0;
1710 let map_to_key_contact = |old_member: &u32| {
1711 autocrypt_key_contacts
1712 .get(old_member)
1713 .or_else(|| {
1714 if is_chatmail {
1719 autocrypt_key_contacts_with_reset_peerstate.get(old_member)
1720 } else {
1721 None
1722 }
1723 })
1724 .copied()
1725 };
1726
1727 let mut update_member_stmt = transaction
1728 .prepare("UPDATE chats_contacts SET contact_id=? WHERE contact_id=? AND chat_id=?")?;
1729 let mut addr_cmp_stmt = transaction
1730 .prepare("SELECT c.addr=d.addr FROM contacts c, contacts d WHERE c.id=? AND d.id=?")?;
1731 for chat in all_chats {
1732 let (chat_id, typ, grpid, protected) = chat.context("Step 24")?;
1733 let old_members: Vec<(u32, bool)> = load_chat_contacts_stmt
1735 .query_map((chat_id,), |row| {
1736 let id: u32 = row.get(0)?;
1737 let present: bool = row.get(1)?;
1738 Ok((id, present))
1739 })
1740 .context("Step 25")?
1741 .collect::<Result<Vec<_>, _>>()
1742 .context("Step 26")?;
1743
1744 let mut keep_address_contacts = |reason: &str| -> Result<()> {
1745 info!(
1746 context,
1747 "Chat {chat_id} will be an unencrypted chat with contacts identified by email address: {reason}."
1748 );
1749 for (m, _) in &old_members {
1750 orphaned_contacts.remove(m);
1751 }
1752
1753 transaction
1757 .execute("UPDATE chats SET protected=0 WHERE id=?", (chat_id,))
1758 .context("Step 26.0")?;
1759
1760 Ok(())
1761 };
1762 let old_and_new_members: Vec<(u32, bool, Option<u32>)> = match typ {
1763 100 => {
1771 let Some((old_member, _)) = old_members.first() else {
1772 info!(
1773 context,
1774 "1:1 chat {chat_id} doesn't contain contact, probably it's self or device chat."
1775 );
1776 continue;
1777 };
1778
1779 let Some(new_contact) = map_to_key_contact(old_member) else {
1780 keep_address_contacts("No peerstate, or peerstate in 'reset' state")?;
1781 continue;
1782 };
1783 if !addr_cmp_stmt
1784 .query_row((old_member, new_contact), |row| row.get::<_, bool>(0))?
1785 {
1786 keep_address_contacts("key contact has different email")?;
1787 continue;
1788 }
1789 vec![(*old_member, true, Some(new_contact))]
1790 }
1791
1792 120 => {
1794 if grpid.is_empty() {
1795 keep_address_contacts("Empty chat-Group-ID")?;
1798 continue;
1799 } else if protected == 1 {
1800 old_members
1801 .iter()
1802 .map(|&(id, present)| {
1803 (id, present, verified_key_contacts.get(&id).copied())
1804 })
1805 .collect()
1806 } else {
1807 old_members
1808 .iter()
1809 .map(|&(id, present)| (id, present, map_to_key_contact(&id)))
1810 .collect::<Vec<(u32, bool, Option<u32>)>>()
1811 }
1812 }
1813
1814 140 => {
1816 keep_address_contacts("Mailinglist")?;
1817 continue;
1818 }
1819
1820 160 => old_members
1822 .iter()
1823 .map(|(original, _)| {
1824 (
1825 *original,
1826 true,
1827 autocrypt_key_contacts
1828 .get(original)
1829 .or_else(|| {
1833 autocrypt_key_contacts_with_reset_peerstate.get(original)
1834 })
1835 .copied(),
1836 )
1837 })
1838 .collect::<Vec<(u32, bool, Option<u32>)>>(),
1839 _ => {
1840 warn!(context, "Invalid chat type {typ}");
1841 continue;
1842 }
1843 };
1844
1845 if typ == 120
1848 && old_and_new_members
1849 .iter()
1850 .any(|&(_old, present, new)| present && new.is_none())
1851 {
1852 transaction
1853 .execute("UPDATE chats SET grpid='' WHERE id=?", (chat_id,))
1854 .context("Step 26.1")?;
1855 keep_address_contacts("Group contains contact without peerstate")?;
1856 continue;
1857 }
1858
1859 let human_readable_transitions = old_and_new_members
1860 .iter()
1861 .map(|(old, _, new)| format!("{old}->{}", new.unwrap_or_default()))
1862 .collect::<Vec<String>>()
1863 .join(" ");
1864 info!(
1865 context,
1866 "Migrating chat {chat_id} to key-contacts: {human_readable_transitions}"
1867 );
1868
1869 for (old_member, _, new_member) in old_and_new_members {
1870 if let Some(new_member) = new_member {
1871 orphaned_contacts.remove(&new_member);
1872 let res = update_member_stmt.execute((new_member, old_member, chat_id));
1873 if res.is_err() {
1874 let member_to_delete: u32 = transaction
1881 .query_row(
1882 "SELECT contact_id
1883 FROM chats_contacts
1884 WHERE chat_id=? AND contact_id IN (?,?)
1885 ORDER BY add_timestamp>=remove_timestamp, add_timestamp LIMIT 1",
1886 (chat_id, new_member, old_member),
1887 |row| row.get(0),
1888 )
1889 .context("Step 27")?;
1890 info!(
1891 context,
1892 "Chat partner is in the chat {chat_id} multiple times. \
1893 Deleting {member_to_delete}, then trying to update \
1894 {old_member}->{new_member} again"
1895 );
1896 transaction
1897 .execute(
1898 "DELETE FROM chats_contacts WHERE chat_id=? AND contact_id=?",
1899 (chat_id, member_to_delete),
1900 )
1901 .context("Step 28")?;
1902 update_member_stmt.execute((new_member, old_member, chat_id))?;
1905 }
1906 } else {
1907 info!(
1908 context,
1909 "Old member {old_member} in chat {chat_id} can't be upgraded to key-contact, removing them"
1910 );
1911 transaction
1912 .execute(
1913 "DELETE FROM chats_contacts WHERE contact_id=? AND chat_id=?",
1914 (old_member, chat_id),
1915 )
1916 .context("Step 29")?;
1917 }
1918 }
1919 }
1920 }
1921
1922 {
1924 info!(
1925 context,
1926 "Marking contacts which remained in no chat at all as hidden: {orphaned_contacts:?}"
1927 );
1928 let mut mark_as_hidden_stmt = transaction
1929 .prepare("UPDATE contacts SET origin=? WHERE id=?")
1930 .context("Step 30")?;
1931 for contact in orphaned_contacts {
1932 mark_as_hidden_stmt
1933 .execute((0x8, contact))
1934 .context("Step 31")?;
1935 }
1936 }
1937
1938 {
1941 let mut contacts_map = autocrypt_key_contacts_with_reset_peerstate;
1942 for (old, new) in autocrypt_key_contacts {
1943 contacts_map.insert(old, new);
1944 }
1945 transaction
1946 .execute(
1947 "CREATE TABLE key_contacts_map (
1948 old_id INTEGER PRIMARY KEY NOT NULL,
1949 new_id INTEGER NOT NULL
1950 ) STRICT",
1951 (),
1952 )
1953 .context("Step 32")?;
1954 {
1955 let mut stmt = transaction
1956 .prepare("INSERT INTO key_contacts_map (old_id, new_id) VALUES (?, ?)")
1957 .context("Step 33")?;
1958 for ids in contacts_map {
1959 stmt.execute(ids).context("Step 34")?;
1960 }
1961 }
1962 transaction
1963 .execute(
1964 "INSERT INTO config (keyname, value) VALUES (
1965 'first_key_contacts_msg_id',
1966 IFNULL((SELECT MAX(id)+1 FROM msgs), 0)
1967 )",
1968 (),
1969 )
1970 .context("Step 35")?;
1971 }
1972
1973 Ok(())
1974}
1975
1976pub(crate) async fn msgs_to_key_contacts(context: &Context) -> Result<()> {
1978 let sql = &context.sql;
1979 if sql
1980 .get_raw_config_int64("first_key_contacts_msg_id")
1981 .await?
1982 <= Some(0)
1983 {
1984 return Ok(());
1985 }
1986 let trans_fn = |t: &mut rusqlite::Transaction| {
1987 let mut first_key_contacts_msg_id: u64 = t
1988 .query_one(
1989 "SELECT CAST(value AS INTEGER) FROM config WHERE keyname='first_key_contacts_msg_id'",
1990 (),
1991 |row| row.get(0),
1992 )
1993 .context("Get first_key_contacts_msg_id")?;
1994 let mut stmt = t
1995 .prepare(
1996 "UPDATE msgs SET
1997 from_id=IFNULL(
1998 (SELECT new_id FROM key_contacts_map WHERE old_id=msgs.from_id),
1999 from_id
2000 ),
2001 to_id=IFNULL(
2002 (SELECT new_id FROM key_contacts_map WHERE old_id=msgs.to_id),
2003 to_id
2004 )
2005 WHERE id>=? AND id<?
2006 AND chat_id>9
2007 AND (param GLOB '*\nc=1*' OR param GLOB 'c=1*')",
2008 )
2009 .context("Prepare stmt")?;
2010 let msgs_to_migrate = 1000;
2011 let mut msgs_migrated: u64 = 0;
2012 while first_key_contacts_msg_id > 0 && msgs_migrated < msgs_to_migrate {
2013 let start_msg_id = first_key_contacts_msg_id.saturating_sub(msgs_to_migrate);
2014 let cnt: u64 = stmt
2015 .execute((start_msg_id, first_key_contacts_msg_id))
2016 .context("UPDATE msgs")?
2017 .try_into()?;
2018 msgs_migrated += cnt;
2019 first_key_contacts_msg_id = start_msg_id;
2020 }
2021 t.execute(
2022 "UPDATE config SET value=? WHERE keyname='first_key_contacts_msg_id'",
2023 (first_key_contacts_msg_id,),
2024 )
2025 .context("Update first_key_contacts_msg_id")?;
2026 Ok((msgs_migrated, first_key_contacts_msg_id))
2027 };
2028 let start = Time::now();
2029 let mut msgs_migrated = 0;
2030 loop {
2031 let (n, first_key_contacts_msg_id) = sql.transaction(trans_fn).await?;
2032 msgs_migrated += n;
2033 if first_key_contacts_msg_id == 0 || time_elapsed(&start) >= Duration::from_millis(500) {
2034 break;
2035 }
2036 }
2037 sql.uncache_raw_config("first_key_contacts_msg_id").await;
2038 info!(
2039 context,
2040 "Rewriting {msgs_migrated} msgs to key-contacts took {:?}.",
2041 time_elapsed(&start),
2042 );
2043 Ok(())
2044}
2045
2046impl Sql {
2047 async fn set_db_version(&self, version: i32) -> Result<()> {
2048 self.set_raw_config_int(VERSION_CFG, version).await?;
2049 Ok(())
2050 }
2051
2052 fn set_db_version_trans(transaction: &mut rusqlite::Transaction, version: i32) -> Result<()> {
2054 transaction.execute(
2055 "UPDATE config SET value=? WHERE keyname=?;",
2056 (format!("{version}"), VERSION_CFG),
2057 )?;
2058 Ok(())
2059 }
2060
2061 async fn set_db_version_in_cache(&self, version: i32) -> Result<()> {
2062 let mut lock = self.config_cache.write().await;
2063 lock.insert(VERSION_CFG.to_string(), Some(format!("{version}")));
2064 Ok(())
2065 }
2066
2067 async fn execute_migration(&self, query: &str, version: i32) -> Result<()> {
2068 self.execute_migration_transaction(
2069 |transaction| {
2070 transaction.execute_batch(query)?;
2071 Ok(())
2072 },
2073 version,
2074 )
2075 .await
2076 }
2077
2078 async fn execute_migration_transaction(
2079 &self,
2080 migration: impl Send + FnOnce(&mut rusqlite::Transaction) -> Result<()>,
2081 version: i32,
2082 ) -> Result<()> {
2083 #[cfg(test)]
2084 if STOP_MIGRATIONS_AT.try_with(|stop_migrations_at| version > *stop_migrations_at)
2085 == Ok(true)
2086 {
2087 println!("Not running migration {version}, because STOP_MIGRATIONS_AT is set");
2088 return Ok(());
2089 }
2090
2091 self.transaction(move |transaction| {
2092 let curr_version: String = transaction.query_row(
2093 "SELECT IFNULL(value, ?) FROM config WHERE keyname=?;",
2094 ("0", VERSION_CFG),
2095 |row| row.get(0),
2096 )?;
2097 let curr_version: i32 = curr_version.parse()?;
2098 ensure!(curr_version < version, "Db version must be increased");
2099 Self::set_db_version_trans(transaction, version)?;
2100 migration(transaction)?;
2101
2102 Ok(())
2103 })
2104 .await
2105 .with_context(|| format!("execute_migration failed for version {version}"))?;
2106
2107 self.config_cache.write().await.clear();
2108
2109 Ok(())
2110 }
2111}
2112
2113#[cfg(test)]
2114mod migrations_tests;