diff --git a/app/schemas/eu.faircode.email.DB/123.json b/app/schemas/eu.faircode.email.DB/123.json new file mode 100644 index 0000000000..2c92ab864e --- /dev/null +++ b/app/schemas/eu.faircode.email.DB/123.json @@ -0,0 +1,2012 @@ +{ + "formatVersion": 1, + "database": { + "version": 123, + "identityHash": "f409a27439c26686e4853890a6699c66", + "entities": [ + { + "tableName": "identity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `name` TEXT NOT NULL, `email` TEXT NOT NULL, `account` INTEGER NOT NULL, `display` TEXT, `color` INTEGER, `signature` TEXT, `host` TEXT NOT NULL, `starttls` INTEGER NOT NULL, `insecure` INTEGER NOT NULL, `port` INTEGER NOT NULL, `auth_type` INTEGER NOT NULL, `user` TEXT NOT NULL, `password` TEXT NOT NULL, `realm` TEXT, `fingerprint` TEXT, `use_ip` INTEGER NOT NULL, `synchronize` INTEGER NOT NULL, `primary` INTEGER NOT NULL, `sender_extra` INTEGER NOT NULL, `sender_extra_regex` TEXT, `replyto` TEXT, `bcc` TEXT, `plain_only` INTEGER NOT NULL, `encrypt` INTEGER NOT NULL, `delivery_receipt` INTEGER NOT NULL, `read_receipt` INTEGER NOT NULL, `store_sent` INTEGER NOT NULL, `sent_folder` INTEGER, `sign_key` INTEGER, `sign_key_alias` TEXT, `tbd` INTEGER, `state` TEXT, `error` TEXT, `last_connected` INTEGER, FOREIGN KEY(`account`) REFERENCES `account`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "account", + "columnName": "account", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "display", + "columnName": "display", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "signature", + "columnName": "signature", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "host", + "columnName": "host", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "starttls", + "columnName": "starttls", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "insecure", + "columnName": "insecure", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "port", + "columnName": "port", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "auth_type", + "columnName": "auth_type", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "user", + "columnName": "user", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "password", + "columnName": "password", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "realm", + "columnName": "realm", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "fingerprint", + "columnName": "fingerprint", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "use_ip", + "columnName": "use_ip", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "synchronize", + "columnName": "synchronize", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "primary", + "columnName": "primary", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sender_extra", + "columnName": "sender_extra", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sender_extra_regex", + "columnName": "sender_extra_regex", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "replyto", + "columnName": "replyto", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "bcc", + "columnName": "bcc", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "plain_only", + "columnName": "plain_only", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "encrypt", + "columnName": "encrypt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "delivery_receipt", + "columnName": "delivery_receipt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "read_receipt", + "columnName": "read_receipt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "store_sent", + "columnName": "store_sent", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sent_folder", + "columnName": "sent_folder", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sign_key", + "columnName": "sign_key", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sign_key_alias", + "columnName": "sign_key_alias", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "tbd", + "columnName": "tbd", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "state", + "columnName": "state", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "error", + "columnName": "error", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "last_connected", + "columnName": "last_connected", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_identity_account", + "unique": false, + "columnNames": [ + "account" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_identity_account` ON `${TABLE_NAME}` (`account`)" + }, + { + "name": "index_identity_account_email", + "unique": false, + "columnNames": [ + "account", + "email" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_identity_account_email` ON `${TABLE_NAME}` (`account`, `email`)" + } + ], + "foreignKeys": [ + { + "table": "account", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "account" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "account", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`order` INTEGER, `id` INTEGER PRIMARY KEY AUTOINCREMENT, `pop` INTEGER NOT NULL, `host` TEXT NOT NULL, `starttls` INTEGER NOT NULL, `insecure` INTEGER NOT NULL, `port` INTEGER NOT NULL, `auth_type` INTEGER NOT NULL, `user` TEXT NOT NULL, `password` TEXT NOT NULL, `realm` TEXT, `fingerprint` TEXT, `name` TEXT, `signature` TEXT, `color` INTEGER, `synchronize` INTEGER NOT NULL, `ondemand` INTEGER NOT NULL, `primary` INTEGER NOT NULL, `notify` INTEGER NOT NULL, `browse` INTEGER NOT NULL, `auto_seen` INTEGER NOT NULL, `separator` INTEGER, `swipe_left` INTEGER, `swipe_right` INTEGER, `move_to` INTEGER, `poll_interval` INTEGER NOT NULL, `partial_fetch` INTEGER NOT NULL, `ignore_size` INTEGER NOT NULL, `use_date` INTEGER NOT NULL, `prefix` TEXT, `created` INTEGER, `tbd` INTEGER, `state` TEXT, `warning` TEXT, `error` TEXT, `last_connected` INTEGER)", + "fields": [ + { + "fieldPath": "order", + "columnName": "order", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "protocol", + "columnName": "pop", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "host", + "columnName": "host", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "starttls", + "columnName": "starttls", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "insecure", + "columnName": "insecure", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "port", + "columnName": "port", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "auth_type", + "columnName": "auth_type", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "user", + "columnName": "user", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "password", + "columnName": "password", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "realm", + "columnName": "realm", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "fingerprint", + "columnName": "fingerprint", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "signature", + "columnName": "signature", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "synchronize", + "columnName": "synchronize", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "ondemand", + "columnName": "ondemand", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "primary", + "columnName": "primary", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "notify", + "columnName": "notify", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "browse", + "columnName": "browse", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "auto_seen", + "columnName": "auto_seen", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "separator", + "columnName": "separator", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "swipe_left", + "columnName": "swipe_left", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "swipe_right", + "columnName": "swipe_right", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "move_to", + "columnName": "move_to", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "poll_interval", + "columnName": "poll_interval", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "partial_fetch", + "columnName": "partial_fetch", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "ignore_size", + "columnName": "ignore_size", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "use_date", + "columnName": "use_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "prefix", + "columnName": "prefix", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "created", + "columnName": "created", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "tbd", + "columnName": "tbd", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "state", + "columnName": "state", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "warning", + "columnName": "warning", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "error", + "columnName": "error", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "last_connected", + "columnName": "last_connected", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "folder", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`order` INTEGER, `id` INTEGER PRIMARY KEY AUTOINCREMENT, `account` INTEGER, `parent` INTEGER, `uidv` INTEGER, `name` TEXT NOT NULL, `type` TEXT NOT NULL, `level` INTEGER NOT NULL, `synchronize` INTEGER NOT NULL, `poll` INTEGER NOT NULL, `download` INTEGER NOT NULL, `subscribed` INTEGER, `sync_days` INTEGER NOT NULL, `keep_days` INTEGER NOT NULL, `auto_delete` INTEGER NOT NULL, `display` TEXT, `color` INTEGER, `hide` INTEGER NOT NULL, `collapsed` INTEGER NOT NULL, `unified` INTEGER NOT NULL, `navigation` INTEGER NOT NULL, `notify` INTEGER NOT NULL, `total` INTEGER, `keywords` TEXT, `initialize` INTEGER NOT NULL, `tbc` INTEGER, `tbd` INTEGER, `rename` TEXT, `state` TEXT, `sync_state` TEXT, `read_only` INTEGER NOT NULL, `selectable` INTEGER NOT NULL, `error` TEXT, `last_sync` INTEGER, FOREIGN KEY(`account`) REFERENCES `account`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "order", + "columnName": "order", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "account", + "columnName": "account", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "parent", + "columnName": "parent", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "uidv", + "columnName": "uidv", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "level", + "columnName": "level", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "synchronize", + "columnName": "synchronize", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "poll", + "columnName": "poll", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "download", + "columnName": "download", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subscribed", + "columnName": "subscribed", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sync_days", + "columnName": "sync_days", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "keep_days", + "columnName": "keep_days", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "auto_delete", + "columnName": "auto_delete", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "display", + "columnName": "display", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hide", + "columnName": "hide", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "collapsed", + "columnName": "collapsed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unified", + "columnName": "unified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "navigation", + "columnName": "navigation", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "notify", + "columnName": "notify", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "total", + "columnName": "total", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "keywords", + "columnName": "keywords", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "initialize", + "columnName": "initialize", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "tbc", + "columnName": "tbc", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "tbd", + "columnName": "tbd", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "rename", + "columnName": "rename", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "state", + "columnName": "state", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sync_state", + "columnName": "sync_state", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "read_only", + "columnName": "read_only", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "selectable", + "columnName": "selectable", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "error", + "columnName": "error", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "last_sync", + "columnName": "last_sync", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_folder_account_name", + "unique": true, + "columnNames": [ + "account", + "name" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_folder_account_name` ON `${TABLE_NAME}` (`account`, `name`)" + }, + { + "name": "index_folder_account", + "unique": false, + "columnNames": [ + "account" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_folder_account` ON `${TABLE_NAME}` (`account`)" + }, + { + "name": "index_folder_name", + "unique": false, + "columnNames": [ + "name" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_folder_name` ON `${TABLE_NAME}` (`name`)" + }, + { + "name": "index_folder_type", + "unique": false, + "columnNames": [ + "type" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_folder_type` ON `${TABLE_NAME}` (`type`)" + }, + { + "name": "index_folder_unified", + "unique": false, + "columnNames": [ + "unified" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_folder_unified` ON `${TABLE_NAME}` (`unified`)" + } + ], + "foreignKeys": [ + { + "table": "account", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "account" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "message", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `account` INTEGER NOT NULL, `folder` INTEGER NOT NULL, `identity` INTEGER, `extra` TEXT, `replying` INTEGER, `forwarding` INTEGER, `uid` INTEGER, `msgid` TEXT, `references` TEXT, `deliveredto` TEXT, `inreplyto` TEXT, `thread` TEXT, `priority` INTEGER, `receipt` INTEGER, `receipt_request` INTEGER, `receipt_to` TEXT, `dkim` INTEGER, `spf` INTEGER, `dmarc` INTEGER, `mx` INTEGER, `avatar` TEXT, `sender` TEXT, `from` TEXT, `to` TEXT, `cc` TEXT, `bcc` TEXT, `reply` TEXT, `list_post` TEXT, `unsubscribe` TEXT, `headers` TEXT, `raw` INTEGER, `subject` TEXT, `size` INTEGER, `total` INTEGER, `attachments` INTEGER NOT NULL, `content` INTEGER NOT NULL, `plain_only` INTEGER, `encrypt` INTEGER, `preview` TEXT, `signature` INTEGER NOT NULL, `sent` INTEGER, `received` INTEGER NOT NULL, `stored` INTEGER NOT NULL, `seen` INTEGER NOT NULL, `answered` INTEGER NOT NULL, `flagged` INTEGER NOT NULL, `flags` TEXT, `keywords` TEXT, `notifying` INTEGER NOT NULL, `ui_seen` INTEGER NOT NULL, `ui_answered` INTEGER NOT NULL, `ui_flagged` INTEGER NOT NULL, `ui_hide` INTEGER NOT NULL, `ui_found` INTEGER NOT NULL, `ui_ignored` INTEGER NOT NULL, `ui_browsed` INTEGER NOT NULL, `ui_busy` INTEGER, `ui_snoozed` INTEGER, `color` INTEGER, `revision` INTEGER, `revisions` INTEGER, `warning` TEXT, `error` TEXT, `last_attempt` INTEGER, FOREIGN KEY(`account`) REFERENCES `account`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`folder`) REFERENCES `folder`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`identity`) REFERENCES `identity`(`id`) ON UPDATE NO ACTION ON DELETE SET NULL , FOREIGN KEY(`replying`) REFERENCES `message`(`id`) ON UPDATE NO ACTION ON DELETE SET NULL , FOREIGN KEY(`forwarding`) REFERENCES `message`(`id`) ON UPDATE NO ACTION ON DELETE SET NULL )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "account", + "columnName": "account", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "folder", + "columnName": "folder", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "identity", + "columnName": "identity", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "extra", + "columnName": "extra", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "replying", + "columnName": "replying", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "forwarding", + "columnName": "forwarding", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "msgid", + "columnName": "msgid", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "references", + "columnName": "references", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "deliveredto", + "columnName": "deliveredto", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "inreplyto", + "columnName": "inreplyto", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "thread", + "columnName": "thread", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "priority", + "columnName": "priority", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "receipt", + "columnName": "receipt", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "receipt_request", + "columnName": "receipt_request", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "receipt_to", + "columnName": "receipt_to", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "dkim", + "columnName": "dkim", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "spf", + "columnName": "spf", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "dmarc", + "columnName": "dmarc", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "mx", + "columnName": "mx", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "avatar", + "columnName": "avatar", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sender", + "columnName": "sender", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "from", + "columnName": "from", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "to", + "columnName": "to", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "cc", + "columnName": "cc", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "bcc", + "columnName": "bcc", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "reply", + "columnName": "reply", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "list_post", + "columnName": "list_post", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "unsubscribe", + "columnName": "unsubscribe", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "headers", + "columnName": "headers", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "raw", + "columnName": "raw", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "size", + "columnName": "size", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "total", + "columnName": "total", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "attachments", + "columnName": "attachments", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "plain_only", + "columnName": "plain_only", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "encrypt", + "columnName": "encrypt", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "preview", + "columnName": "preview", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "signature", + "columnName": "signature", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sent", + "columnName": "sent", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "received", + "columnName": "received", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "stored", + "columnName": "stored", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "seen", + "columnName": "seen", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "answered", + "columnName": "answered", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "flagged", + "columnName": "flagged", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "flags", + "columnName": "flags", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "keywords", + "columnName": "keywords", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "notifying", + "columnName": "notifying", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "ui_seen", + "columnName": "ui_seen", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "ui_answered", + "columnName": "ui_answered", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "ui_flagged", + "columnName": "ui_flagged", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "ui_hide", + "columnName": "ui_hide", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "ui_found", + "columnName": "ui_found", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "ui_ignored", + "columnName": "ui_ignored", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "ui_browsed", + "columnName": "ui_browsed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "ui_busy", + "columnName": "ui_busy", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "ui_snoozed", + "columnName": "ui_snoozed", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "revision", + "columnName": "revision", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "revisions", + "columnName": "revisions", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "warning", + "columnName": "warning", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "error", + "columnName": "error", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "last_attempt", + "columnName": "last_attempt", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_message_account", + "unique": false, + "columnNames": [ + "account" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_message_account` ON `${TABLE_NAME}` (`account`)" + }, + { + "name": "index_message_folder", + "unique": false, + "columnNames": [ + "folder" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_message_folder` ON `${TABLE_NAME}` (`folder`)" + }, + { + "name": "index_message_identity", + "unique": false, + "columnNames": [ + "identity" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_message_identity` ON `${TABLE_NAME}` (`identity`)" + }, + { + "name": "index_message_folder_uid", + "unique": true, + "columnNames": [ + "folder", + "uid" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_message_folder_uid` ON `${TABLE_NAME}` (`folder`, `uid`)" + }, + { + "name": "index_message_msgid", + "unique": false, + "columnNames": [ + "msgid" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_message_msgid` ON `${TABLE_NAME}` (`msgid`)" + }, + { + "name": "index_message_thread", + "unique": false, + "columnNames": [ + "thread" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_message_thread` ON `${TABLE_NAME}` (`thread`)" + }, + { + "name": "index_message_sender", + "unique": false, + "columnNames": [ + "sender" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_message_sender` ON `${TABLE_NAME}` (`sender`)" + }, + { + "name": "index_message_received", + "unique": false, + "columnNames": [ + "received" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_message_received` ON `${TABLE_NAME}` (`received`)" + }, + { + "name": "index_message_subject", + "unique": false, + "columnNames": [ + "subject" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_message_subject` ON `${TABLE_NAME}` (`subject`)" + }, + { + "name": "index_message_ui_seen", + "unique": false, + "columnNames": [ + "ui_seen" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_message_ui_seen` ON `${TABLE_NAME}` (`ui_seen`)" + }, + { + "name": "index_message_ui_flagged", + "unique": false, + "columnNames": [ + "ui_flagged" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_message_ui_flagged` ON `${TABLE_NAME}` (`ui_flagged`)" + }, + { + "name": "index_message_ui_hide", + "unique": false, + "columnNames": [ + "ui_hide" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_message_ui_hide` ON `${TABLE_NAME}` (`ui_hide`)" + }, + { + "name": "index_message_ui_found", + "unique": false, + "columnNames": [ + "ui_found" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_message_ui_found` ON `${TABLE_NAME}` (`ui_found`)" + }, + { + "name": "index_message_ui_ignored", + "unique": false, + "columnNames": [ + "ui_ignored" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_message_ui_ignored` ON `${TABLE_NAME}` (`ui_ignored`)" + }, + { + "name": "index_message_ui_browsed", + "unique": false, + "columnNames": [ + "ui_browsed" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_message_ui_browsed` ON `${TABLE_NAME}` (`ui_browsed`)" + }, + { + "name": "index_message_ui_snoozed", + "unique": false, + "columnNames": [ + "ui_snoozed" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_message_ui_snoozed` ON `${TABLE_NAME}` (`ui_snoozed`)" + } + ], + "foreignKeys": [ + { + "table": "account", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "account" + ], + "referencedColumns": [ + "id" + ] + }, + { + "table": "folder", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "folder" + ], + "referencedColumns": [ + "id" + ] + }, + { + "table": "identity", + "onDelete": "SET NULL", + "onUpdate": "NO ACTION", + "columns": [ + "identity" + ], + "referencedColumns": [ + "id" + ] + }, + { + "table": "message", + "onDelete": "SET NULL", + "onUpdate": "NO ACTION", + "columns": [ + "replying" + ], + "referencedColumns": [ + "id" + ] + }, + { + "table": "message", + "onDelete": "SET NULL", + "onUpdate": "NO ACTION", + "columns": [ + "forwarding" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "attachment", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `message` INTEGER NOT NULL, `sequence` INTEGER NOT NULL, `name` TEXT, `type` TEXT NOT NULL, `disposition` TEXT, `cid` TEXT, `encryption` INTEGER, `size` INTEGER, `progress` INTEGER, `available` INTEGER NOT NULL, `error` TEXT, FOREIGN KEY(`message`) REFERENCES `message`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "message", + "columnName": "message", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sequence", + "columnName": "sequence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "disposition", + "columnName": "disposition", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "cid", + "columnName": "cid", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "encryption", + "columnName": "encryption", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "size", + "columnName": "size", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "progress", + "columnName": "progress", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "available", + "columnName": "available", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "error", + "columnName": "error", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_attachment_message", + "unique": false, + "columnNames": [ + "message" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_attachment_message` ON `${TABLE_NAME}` (`message`)" + }, + { + "name": "index_attachment_message_sequence", + "unique": true, + "columnNames": [ + "message", + "sequence" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_attachment_message_sequence` ON `${TABLE_NAME}` (`message`, `sequence`)" + }, + { + "name": "index_attachment_message_cid", + "unique": false, + "columnNames": [ + "message", + "cid" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_attachment_message_cid` ON `${TABLE_NAME}` (`message`, `cid`)" + } + ], + "foreignKeys": [ + { + "table": "message", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "message" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "operation", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `account` INTEGER, `folder` INTEGER NOT NULL, `message` INTEGER, `name` TEXT NOT NULL, `args` TEXT NOT NULL, `created` INTEGER NOT NULL, `state` TEXT, `error` TEXT, FOREIGN KEY(`folder`) REFERENCES `folder`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`message`) REFERENCES `message`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "account", + "columnName": "account", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "folder", + "columnName": "folder", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "message", + "columnName": "message", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "args", + "columnName": "args", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "created", + "columnName": "created", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "state", + "columnName": "state", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "error", + "columnName": "error", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_operation_account", + "unique": false, + "columnNames": [ + "account" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_operation_account` ON `${TABLE_NAME}` (`account`)" + }, + { + "name": "index_operation_folder", + "unique": false, + "columnNames": [ + "folder" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_operation_folder` ON `${TABLE_NAME}` (`folder`)" + }, + { + "name": "index_operation_message", + "unique": false, + "columnNames": [ + "message" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_operation_message` ON `${TABLE_NAME}` (`message`)" + }, + { + "name": "index_operation_name", + "unique": false, + "columnNames": [ + "name" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_operation_name` ON `${TABLE_NAME}` (`name`)" + }, + { + "name": "index_operation_state", + "unique": false, + "columnNames": [ + "state" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_operation_state` ON `${TABLE_NAME}` (`state`)" + } + ], + "foreignKeys": [ + { + "table": "folder", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "folder" + ], + "referencedColumns": [ + "id" + ] + }, + { + "table": "message", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "message" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "contact", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `account` INTEGER NOT NULL, `type` INTEGER NOT NULL, `email` TEXT NOT NULL, `name` TEXT, `avatar` TEXT, `times_contacted` INTEGER NOT NULL, `first_contacted` INTEGER NOT NULL, `last_contacted` INTEGER NOT NULL, `state` INTEGER NOT NULL, FOREIGN KEY(`account`) REFERENCES `account`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "account", + "columnName": "account", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "avatar", + "columnName": "avatar", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "times_contacted", + "columnName": "times_contacted", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "first_contacted", + "columnName": "first_contacted", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "last_contacted", + "columnName": "last_contacted", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "state", + "columnName": "state", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_contact_account_type_email", + "unique": true, + "columnNames": [ + "account", + "type", + "email" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_contact_account_type_email` ON `${TABLE_NAME}` (`account`, `type`, `email`)" + }, + { + "name": "index_contact_email", + "unique": false, + "columnNames": [ + "email" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_contact_email` ON `${TABLE_NAME}` (`email`)" + }, + { + "name": "index_contact_name", + "unique": false, + "columnNames": [ + "name" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_contact_name` ON `${TABLE_NAME}` (`name`)" + }, + { + "name": "index_contact_avatar", + "unique": false, + "columnNames": [ + "avatar" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_contact_avatar` ON `${TABLE_NAME}` (`avatar`)" + }, + { + "name": "index_contact_times_contacted", + "unique": false, + "columnNames": [ + "times_contacted" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_contact_times_contacted` ON `${TABLE_NAME}` (`times_contacted`)" + }, + { + "name": "index_contact_last_contacted", + "unique": false, + "columnNames": [ + "last_contacted" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_contact_last_contacted` ON `${TABLE_NAME}` (`last_contacted`)" + }, + { + "name": "index_contact_state", + "unique": false, + "columnNames": [ + "state" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_contact_state` ON `${TABLE_NAME}` (`state`)" + } + ], + "foreignKeys": [ + { + "table": "account", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "account" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "certificate", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `fingerprint` TEXT NOT NULL, `email` TEXT NOT NULL, `subject` TEXT, `after` INTEGER, `before` INTEGER, `data` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "fingerprint", + "columnName": "fingerprint", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "after", + "columnName": "after", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "before", + "columnName": "before", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "data", + "columnName": "data", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_certificate_fingerprint_email", + "unique": true, + "columnNames": [ + "fingerprint", + "email" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_certificate_fingerprint_email` ON `${TABLE_NAME}` (`fingerprint`, `email`)" + }, + { + "name": "index_certificate_email", + "unique": false, + "columnNames": [ + "email" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_certificate_email` ON `${TABLE_NAME}` (`email`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "answer", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `name` TEXT NOT NULL, `hide` INTEGER NOT NULL, `text` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "hide", + "columnName": "hide", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "text", + "columnName": "text", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "rule", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `folder` INTEGER NOT NULL, `name` TEXT NOT NULL, `order` INTEGER NOT NULL, `enabled` INTEGER NOT NULL, `stop` INTEGER NOT NULL, `condition` TEXT NOT NULL, `action` TEXT NOT NULL, `applied` INTEGER NOT NULL, FOREIGN KEY(`folder`) REFERENCES `folder`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "folder", + "columnName": "folder", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "order", + "columnName": "order", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "enabled", + "columnName": "enabled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "stop", + "columnName": "stop", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "condition", + "columnName": "condition", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "action", + "columnName": "action", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "applied", + "columnName": "applied", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_rule_folder", + "unique": false, + "columnNames": [ + "folder" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_rule_folder` ON `${TABLE_NAME}` (`folder`)" + }, + { + "name": "index_rule_order", + "unique": false, + "columnNames": [ + "order" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_rule_order` ON `${TABLE_NAME}` (`order`)" + } + ], + "foreignKeys": [ + { + "table": "folder", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "folder" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "log", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `time` INTEGER NOT NULL, `data` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "time", + "columnName": "time", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "data", + "columnName": "data", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_log_time", + "unique": false, + "columnNames": [ + "time" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_log_time` ON `${TABLE_NAME}` (`time`)" + } + ], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'f409a27439c26686e4853890a6699c66')" + ] + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/faircode/email/ActivitySetup.java b/app/src/main/java/eu/faircode/email/ActivitySetup.java index 719419b53e..4cf65ce47d 100644 --- a/app/src/main/java/eu/faircode/email/ActivitySetup.java +++ b/app/src/main/java/eu/faircode/email/ActivitySetup.java @@ -1223,7 +1223,7 @@ public class ActivitySetup extends ActivityBase implements FragmentManager.OnBac String user = email; String password = token; try (MailService iservice = new MailService(context, "imaps", null, false, true, true)) { - iservice.connect(host, port, MailService.AUTH_TYPE_OUTLOOK, user, password); + iservice.connect(host, port, MailService.AUTH_TYPE_OUTLOOK, user, password, null); folders = iservice.getFolders(); diff --git a/app/src/main/java/eu/faircode/email/DB.java b/app/src/main/java/eu/faircode/email/DB.java index 2a531d363d..d87b1c6a8c 100644 --- a/app/src/main/java/eu/faircode/email/DB.java +++ b/app/src/main/java/eu/faircode/email/DB.java @@ -56,7 +56,7 @@ import io.requery.android.database.sqlite.RequerySQLiteOpenHelperFactory; // https://developer.android.com/topic/libraries/architecture/room.html @Database( - version = 122, + version = 123, entities = { EntityIdentity.class, EntityAccount.class, @@ -1188,6 +1188,14 @@ public abstract class DB extends RoomDatabase { file.delete(); } }) + .addMigrations(new Migration(122, 123) { + @Override + public void migrate(@NonNull SupportSQLiteDatabase db) { + Log.i("DB migration from version " + startVersion + " to " + endVersion); + db.execSQL("ALTER TABLE `account` ADD COLUMN `fingerprint` TEXT"); + db.execSQL("ALTER TABLE `identity` ADD COLUMN `fingerprint` TEXT"); + } + }) .build(); } diff --git a/app/src/main/java/eu/faircode/email/EntityAccount.java b/app/src/main/java/eu/faircode/email/EntityAccount.java index f891e30a6c..211f137e12 100644 --- a/app/src/main/java/eu/faircode/email/EntityAccount.java +++ b/app/src/main/java/eu/faircode/email/EntityAccount.java @@ -77,6 +77,7 @@ public class EntityAccount extends EntityOrder implements Serializable { @NonNull public String password; public String realm; + public String fingerprint; public String name; public String signature; // obsolete diff --git a/app/src/main/java/eu/faircode/email/EntityIdentity.java b/app/src/main/java/eu/faircode/email/EntityIdentity.java index e4b9c86f5a..287ce93476 100644 --- a/app/src/main/java/eu/faircode/email/EntityIdentity.java +++ b/app/src/main/java/eu/faircode/email/EntityIdentity.java @@ -77,6 +77,7 @@ public class EntityIdentity { @NonNull public String password; public String realm; + public String fingerprint; @NonNull public Boolean use_ip = true; // instead of domain name @NonNull diff --git a/app/src/main/java/eu/faircode/email/FragmentAccount.java b/app/src/main/java/eu/faircode/email/FragmentAccount.java index cdbaffb27b..62165b4f41 100644 --- a/app/src/main/java/eu/faircode/email/FragmentAccount.java +++ b/app/src/main/java/eu/faircode/email/FragmentAccount.java @@ -127,6 +127,7 @@ public class FragmentAccount extends FragmentBase { private ContentLoadingProgressBar pbSave; private CheckBox cbIdentity; private TextView tvError; + private CheckBox cbTrust; private Button btnHelp; private Button btnSupport; private TextView tvInstructions; @@ -229,6 +230,7 @@ public class FragmentAccount extends FragmentBase { cbIdentity = view.findViewById(R.id.cbIdentity); tvError = view.findViewById(R.id.tvError); + cbTrust = view.findViewById(R.id.cbTrust); btnHelp = view.findViewById(R.id.btnHelp); btnSupport = view.findViewById(R.id.btnSupport); tvInstructions = view.findViewById(R.id.tvInstructions); @@ -271,6 +273,7 @@ public class FragmentAccount extends FragmentBase { etUser.setText(null); tilPassword.getEditText().setText(null); etRealm.setText(null); + cbTrust.setChecked(false); etName.setText(position > 1 ? provider.name : null); etInterval.setText(provider.keepalive > 0 ? Integer.toString(provider.keepalive) : null); @@ -432,6 +435,7 @@ public class FragmentAccount extends FragmentBase { pbSave.setVisibility(View.GONE); cbIdentity.setVisibility(View.GONE); + cbTrust.setVisibility(View.GONE); btnHelp.setVisibility(View.GONE); btnSupport.setVisibility(View.GONE); tvInstructions.setVisibility(View.GONE); @@ -499,6 +503,7 @@ public class FragmentAccount extends FragmentBase { args.putString("user", etUser.getText().toString()); args.putString("password", tilPassword.getEditText().getText().toString()); args.putString("realm", etRealm.getText().toString()); + args.putString("fingerprint", cbTrust.isChecked() ? (String) cbTrust.getTag() : null); new SimpleTask() { @Override @@ -535,6 +540,7 @@ public class FragmentAccount extends FragmentBase { String user = args.getString("user"); String password = args.getString("password"); String realm = args.getString("realm"); + String fingerprint = args.getString("fingerprint"); if (host.contains(":")) { Uri h = Uri.parse(host); @@ -562,7 +568,7 @@ public class FragmentAccount extends FragmentBase { // Check IMAP server / get folders String protocol = "imap" + (starttls ? "" : "s"); try (MailService iservice = new MailService(context, protocol, realm, insecure, true, true)) { - iservice.connect(host, Integer.parseInt(port), auth, user, password); + iservice.connect(host, Integer.parseInt(port), auth, user, password, fingerprint); result.idle = iservice.hasCapability("IDLE"); @@ -639,6 +645,9 @@ public class FragmentAccount extends FragmentBase { setFolders(result.folders, result.account); + if (!cbTrust.isChecked()) + cbTrust.setVisibility(View.GONE); + new Handler().post(new Runnable() { @Override public void run() { @@ -701,6 +710,7 @@ public class FragmentAccount extends FragmentBase { args.putString("user", etUser.getText().toString()); args.putString("password", tilPassword.getEditText().getText().toString()); args.putString("realm", etRealm.getText().toString()); + args.putString("fingerprint", cbTrust.isChecked() ? (String) cbTrust.getTag() : null); args.putString("name", etName.getText().toString()); args.putInt("color", btnColor.getColor()); @@ -760,6 +770,7 @@ public class FragmentAccount extends FragmentBase { String user = args.getString("user").trim(); String password = args.getString("password"); String realm = args.getString("realm"); + String fingerprint = args.getString("fingerprint"); String name = args.getString("name"); Integer color = args.getInt("color"); @@ -846,6 +857,8 @@ public class FragmentAccount extends FragmentBase { return true; if (!Objects.equals(account.realm, realm)) return true; + if (!Objects.equals(account.fingerprint, fingerprint)) + return true; if (!Objects.equals(account.name, name)) return true; if (!Objects.equals(account.color, color)) @@ -916,7 +929,8 @@ public class FragmentAccount extends FragmentBase { !account.port.equals(Integer.parseInt(port)) || !account.user.equals(user) || !account.password.equals(password) || - !Objects.equals(realm, accountRealm))); + !Objects.equals(realm, accountRealm) || + !Objects.equals(account.fingerprint, fingerprint))); Log.i("Account check=" + check); Long last_connected = null; @@ -928,7 +942,7 @@ public class FragmentAccount extends FragmentBase { if (check) { String protocol = "imap" + (starttls ? "" : "s"); try (MailService iservice = new MailService(context, protocol, realm, insecure, true, true)) { - iservice.connect(host, Integer.parseInt(port), auth, user, password); + iservice.connect(host, Integer.parseInt(port), auth, user, password, fingerprint); for (Folder ifolder : iservice.getStore().getDefaultFolder().list("*")) { // Check folder attributes @@ -976,6 +990,7 @@ public class FragmentAccount extends FragmentBase { account.password = password; } account.realm = realm; + account.fingerprint = fingerprint; account.name = name; account.color = color; @@ -1160,6 +1175,13 @@ public class FragmentAccount extends FragmentBase { tvError.setText(Log.formatThrowable(ex, false)); grpError.setVisibility(View.VISIBLE); + if (ex instanceof MailService.UntrustedException) { + MailService.UntrustedException e = (MailService.UntrustedException) ex; + cbTrust.setTag(e.getFingerprint()); + cbTrust.setText(getString(R.string.title_trust, e.getFingerprint())); + cbTrust.setVisibility(View.VISIBLE); + } + final EmailProvider provider = (EmailProvider) spProvider.getSelectedItem(); if (provider != null && provider.link != null) { Uri uri = Uri.parse(provider.link); @@ -1251,6 +1273,15 @@ public class FragmentAccount extends FragmentBase { tilPassword.getEditText().setText(account == null ? null : account.password); etRealm.setText(account == null ? null : account.realm); + if (account == null || account.fingerprint == null) { + cbTrust.setTag(null); + cbTrust.setChecked(false); + } else { + cbTrust.setTag(account.fingerprint); + cbTrust.setChecked(true); + cbTrust.setText(getString(R.string.title_trust, account.fingerprint)); + } + etName.setText(account == null ? null : account.name); btnColor.setColor(account == null ? null : account.color); @@ -1556,10 +1587,14 @@ public class FragmentAccount extends FragmentBase { } } + cbIdentity.setChecked(account == null); + grpFolders.setVisibility(View.VISIBLE); btnSave.setVisibility(View.VISIBLE); cbIdentity.setVisibility(View.VISIBLE); - cbIdentity.setChecked(account == null); + + if (cbTrust.isChecked()) + cbTrust.setVisibility(View.VISIBLE); } private class CheckResult { diff --git a/app/src/main/java/eu/faircode/email/FragmentGmail.java b/app/src/main/java/eu/faircode/email/FragmentGmail.java index baa4e4e381..b7780ddd04 100644 --- a/app/src/main/java/eu/faircode/email/FragmentGmail.java +++ b/app/src/main/java/eu/faircode/email/FragmentGmail.java @@ -285,7 +285,7 @@ public class FragmentGmail extends FragmentBase { String aprotocol = provider.imap.starttls ? "imap" : "imaps"; try (MailService iservice = new MailService(context, aprotocol, null, false, true, true)) { - iservice.connect(provider.imap.host, provider.imap.port, MailService.AUTH_TYPE_GMAIL, user, password); + iservice.connect(provider.imap.host, provider.imap.port, MailService.AUTH_TYPE_GMAIL, user, password, null); folders = iservice.getFolders(); @@ -295,7 +295,7 @@ public class FragmentGmail extends FragmentBase { String iprotocol = provider.smtp.starttls ? "smtp" : "smtps"; try (MailService iservice = new MailService(context, iprotocol, null, false, true, true)) { - iservice.connect(provider.smtp.host, provider.smtp.port, MailService.AUTH_TYPE_GMAIL, user, password); + iservice.connect(provider.smtp.host, provider.smtp.port, MailService.AUTH_TYPE_GMAIL, user, password, null); } DB db = DB.getInstance(context); diff --git a/app/src/main/java/eu/faircode/email/FragmentIdentity.java b/app/src/main/java/eu/faircode/email/FragmentIdentity.java index 115328d900..18fbecddf7 100644 --- a/app/src/main/java/eu/faircode/email/FragmentIdentity.java +++ b/app/src/main/java/eu/faircode/email/FragmentIdentity.java @@ -110,6 +110,7 @@ public class FragmentIdentity extends FragmentBase { private Button btnSave; private ContentLoadingProgressBar pbSave; private TextView tvError; + private CheckBox cbTrust; private Button btnHelp; private Button btnSupport; private TextView tvInstructions; @@ -191,6 +192,7 @@ public class FragmentIdentity extends FragmentBase { btnSave = view.findViewById(R.id.btnSave); pbSave = view.findViewById(R.id.pbSave); tvError = view.findViewById(R.id.tvError); + cbTrust = view.findViewById(R.id.cbTrust); btnHelp = view.findViewById(R.id.btnHelp); btnSupport = view.findViewById(R.id.btnSupport); tvInstructions = view.findViewById(R.id.tvInstructions); @@ -255,6 +257,7 @@ public class FragmentIdentity extends FragmentBase { etUser.setText(account.user); tilPassword.getEditText().setText(account.password); etRealm.setText(account.realm); + cbTrust.setChecked(false); } @Override @@ -426,6 +429,7 @@ public class FragmentIdentity extends FragmentBase { btnSave.setVisibility(View.GONE); pbSave.setVisibility(View.GONE); + cbTrust.setVisibility(View.GONE); btnHelp.setVisibility(View.GONE); btnSupport.setVisibility(View.GONE); tvInstructions.setVisibility(View.GONE); @@ -514,6 +518,7 @@ public class FragmentIdentity extends FragmentBase { args.putString("user", etUser.getText().toString()); args.putString("password", tilPassword.getEditText().getText().toString()); args.putString("realm", etRealm.getText().toString()); + args.putString("fingerprint", cbTrust.isChecked() ? (String) cbTrust.getTag() : null); args.putBoolean("use_ip", cbUseIp.isChecked()); args.putString("signature", (String) etSignature.getTag()); args.putBoolean("synchronize", cbSynchronize.isChecked()); @@ -561,6 +566,7 @@ public class FragmentIdentity extends FragmentBase { String user = args.getString("user").trim(); String password = args.getString("password"); String realm = args.getString("realm"); + String fingerprint = args.getString("fingerprint"); boolean use_ip = args.getBoolean("use_ip"); boolean synchronize = args.getBoolean("synchronize"); boolean primary = args.getBoolean("primary"); @@ -687,6 +693,8 @@ public class FragmentIdentity extends FragmentBase { return true; if (!Objects.equals(identity.realm, realm)) return true; + if (!Objects.equals(identity.fingerprint, fingerprint)) + return true; if (!Objects.equals(identity.use_ip, use_ip)) return true; if (!Objects.equals(identity.synchronize, synchronize)) @@ -715,6 +723,7 @@ public class FragmentIdentity extends FragmentBase { !host.equals(identity.host) || Integer.parseInt(port) != identity.port || !user.equals(identity.user) || !password.equals(identity.password) || !Objects.equals(realm, identityRealm) || + !Objects.equals(identity.fingerprint, fingerprint) || use_ip != identity.use_ip)); Log.i("Identity check=" + check); @@ -728,7 +737,7 @@ public class FragmentIdentity extends FragmentBase { String protocol = (starttls ? "smtp" : "smtps"); try (MailService iservice = new MailService(context, protocol, realm, insecure, true, true)) { iservice.setUseIp(use_ip); - iservice.connect(host, Integer.parseInt(port), auth, user, password); + iservice.connect(host, Integer.parseInt(port), auth, user, password, fingerprint); } } @@ -763,6 +772,7 @@ public class FragmentIdentity extends FragmentBase { identity.password = password; } identity.realm = realm; + identity.fingerprint = fingerprint; identity.use_ip = use_ip; identity.synchronize = synchronize; identity.primary = (identity.synchronize && primary); @@ -828,6 +838,13 @@ public class FragmentIdentity extends FragmentBase { tvError.setText(Log.formatThrowable(ex, false)); grpError.setVisibility(View.VISIBLE); + if (ex instanceof MailService.UntrustedException) { + MailService.UntrustedException e = (MailService.UntrustedException) ex; + cbTrust.setTag(e.getFingerprint()); + cbTrust.setText(getString(R.string.title_trust, e.getFingerprint())); + cbTrust.setVisibility(View.VISIBLE); + } + final EmailProvider provider = (EmailProvider) spProvider.getSelectedItem(); if (provider != null && provider.link != null) { Uri uri = Uri.parse(provider.link); @@ -898,6 +915,18 @@ public class FragmentIdentity extends FragmentBase { etUser.setText(identity == null ? null : identity.user); tilPassword.getEditText().setText(identity == null ? null : identity.password); etRealm.setText(identity == null ? null : identity.realm); + + if (identity == null || identity.fingerprint == null) { + cbTrust.setTag(null); + cbTrust.setChecked(false); + cbTrust.setVisibility(View.GONE); + } else { + cbTrust.setTag(identity.fingerprint); + cbTrust.setChecked(true); + cbTrust.setText(getString(R.string.title_trust, identity.fingerprint)); + cbTrust.setVisibility(View.VISIBLE); + } + cbUseIp.setChecked(identity == null ? true : identity.use_ip); cbSynchronize.setChecked(identity == null ? true : identity.synchronize); cbPrimary.setChecked(identity == null ? true : identity.primary); diff --git a/app/src/main/java/eu/faircode/email/FragmentPop.java b/app/src/main/java/eu/faircode/email/FragmentPop.java index a43af71409..02e823baa0 100644 --- a/app/src/main/java/eu/faircode/email/FragmentPop.java +++ b/app/src/main/java/eu/faircode/email/FragmentPop.java @@ -278,7 +278,7 @@ public class FragmentPop extends FragmentBase { if (check) { String protocol = "pop3" + (starttls ? "" : "s"); try (MailService iservice = new MailService(context, protocol, null, insecure, true, true)) { - iservice.connect(host, Integer.parseInt(port), MailService.AUTH_TYPE_PASSWORD, user, password); + iservice.connect(host, Integer.parseInt(port), MailService.AUTH_TYPE_PASSWORD, user, password, null); } } diff --git a/app/src/main/java/eu/faircode/email/FragmentQuickSetup.java b/app/src/main/java/eu/faircode/email/FragmentQuickSetup.java index 8f5647857a..b88efe81ba 100644 --- a/app/src/main/java/eu/faircode/email/FragmentQuickSetup.java +++ b/app/src/main/java/eu/faircode/email/FragmentQuickSetup.java @@ -250,13 +250,13 @@ public class FragmentQuickSetup extends FragmentBase { String aprotocol = provider.imap.starttls ? "imap" : "imaps"; try (MailService iservice = new MailService(context, aprotocol, null, false, true, true)) { try { - iservice.connect(provider.imap.host, provider.imap.port, MailService.AUTH_TYPE_PASSWORD, user, password); + iservice.connect(provider.imap.host, provider.imap.port, MailService.AUTH_TYPE_PASSWORD, user, password, null); } catch (AuthenticationFailedException ex) { if (!user.equals(username)) { Log.w(ex); user = username; Log.i("Retry with user=" + user); - iservice.connect(provider.imap.host, provider.imap.port, MailService.AUTH_TYPE_PASSWORD, user, password); + iservice.connect(provider.imap.host, provider.imap.port, MailService.AUTH_TYPE_PASSWORD, user, password, null); } else throw ex; } @@ -270,7 +270,7 @@ public class FragmentQuickSetup extends FragmentBase { String iprotocol = provider.smtp.starttls ? "smtp" : "smtps"; try (MailService iservice = new MailService(context, iprotocol, null, false, true, true)) { iservice.setUseIp(provider.useip); - iservice.connect(provider.smtp.host, provider.smtp.port, MailService.AUTH_TYPE_PASSWORD, user, password); + iservice.connect(provider.smtp.host, provider.smtp.port, MailService.AUTH_TYPE_PASSWORD, user, password, null); } if (check) diff --git a/app/src/main/java/eu/faircode/email/MailService.java b/app/src/main/java/eu/faircode/email/MailService.java index ea7ed0df70..872711dd4c 100644 --- a/app/src/main/java/eu/faircode/email/MailService.java +++ b/app/src/main/java/eu/faircode/email/MailService.java @@ -6,6 +6,7 @@ import android.content.Context; import android.content.SharedPreferences; import android.text.TextUtils; +import androidx.annotation.NonNull; import androidx.preference.PreferenceManager; import com.sun.mail.imap.IMAPFolder; @@ -15,7 +16,6 @@ import com.sun.mail.util.MailConnectException; import com.sun.mail.util.MailSSLSocketFactory; import org.bouncycastle.asn1.x509.GeneralName; -import org.jetbrains.annotations.NotNull; import java.io.IOException; import java.net.Inet4Address; @@ -121,6 +121,7 @@ public class MailService implements AutoCloseable { } } }; + properties.put("mail." + protocol + ".ssl.socketFactory", sf); properties.put("mail." + protocol + ".socketFactory.fallback", "false"); } catch (GeneralSecurityException ex) { @@ -160,8 +161,7 @@ public class MailService implements AutoCloseable { // https://javaee.github.io/javamail/docs/api/com/sun/mail/pop3/package-summary.html#properties properties.put("mail." + protocol + ".ssl.checkserveridentity", Boolean.toString(!insecure)); - if (!BuildConfig.DEBUG) - properties.put("mail." + protocol + ".ssl.trust", "*"); + properties.put("mail." + protocol + ".ssl.trust", "*"); properties.put("mail.pop3s.starttls.enable", "false"); @@ -170,10 +170,6 @@ public class MailService implements AutoCloseable { } else if ("imap".equals(protocol) || "imaps".equals(protocol)) { // https://javaee.github.io/javamail/docs/api/com/sun/mail/imap/package-summary.html#properties - properties.put("mail." + protocol + ".ssl.checkserveridentity", Boolean.toString(!insecure)); - if (!BuildConfig.DEBUG) - properties.put("mail." + protocol + ".ssl.trust", "*"); - properties.put("mail.imaps.starttls.enable", "false"); properties.put("mail.imap.starttls.enable", "true"); @@ -200,10 +196,6 @@ public class MailService implements AutoCloseable { } else if ("smtp".equals(protocol) || "smtps".equals(protocol)) { // https://javaee.github.io/javamail/docs/api/com/sun/mail/smtp/package-summary.html#properties - properties.put("mail." + protocol + ".ssl.checkserveridentity", Boolean.toString(!insecure)); - if (!BuildConfig.DEBUG) - properties.put("mail." + protocol + ".ssl.trust", "*"); - properties.put("mail.smtps.starttls.enable", "false"); properties.put("mail.smtp.starttls.enable", "true"); @@ -235,12 +227,8 @@ public class MailService implements AutoCloseable { this.listener = listener; } - void setTrustedFingerPrint(String value) { - trustedFingerprint = value; - } - public void connect(EntityAccount account) throws MessagingException { - String password = connect(account.host, account.port, account.auth_type, account.user, account.password); + String password = connect(account.host, account.port, account.auth_type, account.user, account.password, account.fingerprint); if (password != null) { DB db = DB.getInstance(context); int count = db.account().setAccountPassword(account.id, account.password); @@ -249,7 +237,7 @@ public class MailService implements AutoCloseable { } public void connect(EntityIdentity identity) throws MessagingException { - String password = connect(identity.host, identity.port, identity.auth_type, identity.user, identity.password); + String password = connect(identity.host, identity.port, identity.auth_type, identity.user, identity.password, identity.fingerprint); if (password != null) { DB db = DB.getInstance(context); int count = db.identity().setIdentityPassword(identity.id, identity.password); @@ -257,7 +245,8 @@ public class MailService implements AutoCloseable { } } - public String connect(String host, int port, int auth, String user, String password) throws MessagingException { + public String connect(String host, int port, int auth, String user, String password, String fingerprint) throws MessagingException { + this.trustedFingerprint = fingerprint; try { if (auth == AUTH_TYPE_GMAIL || auth == AUTH_TYPE_OUTLOOK) properties.put("mail." + protocol + ".auth.mechanisms", "XOAUTH2"); @@ -317,16 +306,14 @@ public class MailService implements AutoCloseable { ex.getCause() instanceof IOException && ex.getCause().getMessage() != null && ex.getCause().getMessage().startsWith("Server is not trusted:")) { - String name; - String fingerprint; + String sfingerprint; try { - name = getDnsName(certificate); - fingerprint = getFingerPrint(certificate); + sfingerprint = getFingerPrint(certificate); } catch (Throwable ex1) { Log.e(ex1); throw ex; } - throw new UntrustedException(name, fingerprint); + throw new UntrustedException(sfingerprint, ex); } else throw ex; } @@ -516,26 +503,20 @@ public class MailService implements AutoCloseable { } class UntrustedException extends MessagingException { - private String name; private String fingerprint; - UntrustedException(String name, String fingerprint) { - this.name = name; + UntrustedException(@NonNull String fingerprint, @NonNull Exception cause) { + super(cause.getMessage(), cause); this.fingerprint = fingerprint; } - String getName() { - return name; - } - String getFingerprint() { return fingerprint; } - @NotNull @Override public synchronized String toString() { - return this.getClass().getName() + " name=" + name + " fingerprint=" + fingerprint; + return getCause().toString(); } } } \ No newline at end of file diff --git a/app/src/main/java/eu/faircode/email/TupleAccountState.java b/app/src/main/java/eu/faircode/email/TupleAccountState.java index a5731fcce5..376a6b6ddd 100644 --- a/app/src/main/java/eu/faircode/email/TupleAccountState.java +++ b/app/src/main/java/eu/faircode/email/TupleAccountState.java @@ -37,6 +37,7 @@ public class TupleAccountState extends EntityAccount { this.user.equals(other.user) && this.password.equals(other.password) && Objects.equals(this.realm, other.realm) && + Objects.equals(this.fingerprint, other.fingerprint) && this.notify.equals(other.notify) && this.poll_interval.equals(other.poll_interval) && this.partial_fetch.equals(other.partial_fetch) && diff --git a/app/src/main/res/layout/fragment_account.xml b/app/src/main/res/layout/fragment_account.xml index 19e9e9ec74..ded0b075a5 100644 --- a/app/src/main/res/layout/fragment_account.xml +++ b/app/src/main/res/layout/fragment_account.xml @@ -814,6 +814,17 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/tvErrorTitle" /> + +