mirror of
https://github.com/chatmail/core.git
synced 2026-05-02 04:46:29 +03:00
refactor: Move logins into SQL table (#6724)
Move all `configured_*` parameters into a new SQL table `transports`. All `configured_*` parameters are deprecated; the only exception is `configured_addr`, which is used to store the address of the primary transport. Currently, there can only ever be one primary transport (i.e. the `transports` table only ever has one row); this PR is not supposed to change DC's behavior in any meaningful way. This is a preparation for mt. --------- Co-authored-by: l <link2xt@testrun.org>
This commit is contained in:
@@ -4,7 +4,7 @@ use std::env;
|
||||
use std::path::Path;
|
||||
use std::str::FromStr;
|
||||
|
||||
use anyhow::{ensure, Context as _, Result};
|
||||
use anyhow::{bail, ensure, Context as _, Result};
|
||||
use base64::Engine as _;
|
||||
use deltachat_contact_tools::{addr_cmp, sanitize_single_line};
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -13,10 +13,12 @@ use strum_macros::{AsRefStr, Display, EnumIter, EnumString};
|
||||
use tokio::fs;
|
||||
|
||||
use crate::blob::BlobObject;
|
||||
use crate::configure::EnteredLoginParam;
|
||||
use crate::constants;
|
||||
use crate::context::Context;
|
||||
use crate::events::EventType;
|
||||
use crate::log::LogExt;
|
||||
use crate::login_param::ConfiguredLoginParam;
|
||||
use crate::mimefactory::RECOMMENDED_FILE_SIZE;
|
||||
use crate::provider::{get_provider_by_id, Provider};
|
||||
use crate::sync::{self, Sync::*, SyncData};
|
||||
@@ -525,21 +527,22 @@ impl Context {
|
||||
// Default values
|
||||
let val = match key {
|
||||
Config::BccSelf => match Box::pin(self.is_chatmail()).await? {
|
||||
false => Some("1"),
|
||||
true => Some("0"),
|
||||
false => Some("1".to_string()),
|
||||
true => Some("0".to_string()),
|
||||
},
|
||||
Config::ConfiguredInboxFolder => Some("INBOX"),
|
||||
Config::ConfiguredInboxFolder => Some("INBOX".to_string()),
|
||||
Config::DeleteServerAfter => {
|
||||
match !Box::pin(self.get_config_bool(Config::BccSelf)).await?
|
||||
&& Box::pin(self.is_chatmail()).await?
|
||||
{
|
||||
true => Some("1"),
|
||||
false => Some("0"),
|
||||
true => Some("1".to_string()),
|
||||
false => Some("0".to_string()),
|
||||
}
|
||||
}
|
||||
_ => key.get_str("default"),
|
||||
Config::Addr => self.get_config_opt(Config::ConfiguredAddr).await?,
|
||||
_ => key.get_str("default").map(|s| s.to_string()),
|
||||
};
|
||||
Ok(val.map(|s| s.to_string()))
|
||||
Ok(val)
|
||||
}
|
||||
|
||||
/// Returns Some(T) if a value for the given key is set and was successfully parsed.
|
||||
@@ -805,6 +808,19 @@ impl Context {
|
||||
.set_raw_config(constants::DC_FOLDERS_CONFIGURED_KEY, None)
|
||||
.await?;
|
||||
}
|
||||
Config::ConfiguredAddr => {
|
||||
if self.is_configured().await? {
|
||||
bail!("Cannot change ConfiguredAddr");
|
||||
}
|
||||
if let Some(addr) = value {
|
||||
info!(self, "Creating a pseudo configured account which will not be able to send or receive messages. Only meant for tests!");
|
||||
ConfiguredLoginParam::from_json(&format!(
|
||||
r#"{{"addr":"{addr}","imap":[],"imap_user":"","imap_password":"","smtp":[],"smtp_user":"","smtp_password":"","certificate_checks":"Automatic","oauth2":false}}"#
|
||||
))?
|
||||
.save_to_transports_table(self, &EnteredLoginParam::default())
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
self.sql.set_raw_config(key.as_ref(), value).await?;
|
||||
}
|
||||
@@ -891,6 +907,7 @@ impl Context {
|
||||
/// primary address (if exists) as a secondary address.
|
||||
///
|
||||
/// This should only be used by test code and during configure.
|
||||
#[cfg(test)] // AEAP is disabled, but there are still tests for it
|
||||
pub(crate) async fn set_primary_self_addr(&self, primary_new: &str) -> Result<()> {
|
||||
self.quota.write().await.take();
|
||||
|
||||
@@ -904,7 +921,8 @@ impl Context {
|
||||
)
|
||||
.await?;
|
||||
|
||||
self.set_config_internal(Config::ConfiguredAddr, Some(primary_new))
|
||||
self.sql
|
||||
.set_raw_config(Config::ConfiguredAddr.as_ref(), Some(primary_new))
|
||||
.await?;
|
||||
self.emit_event(EventType::ConnectivityChanged);
|
||||
Ok(())
|
||||
|
||||
@@ -35,7 +35,7 @@ use crate::login_param::{
|
||||
};
|
||||
use crate::message::Message;
|
||||
use crate::oauth2::get_oauth2_addr;
|
||||
use crate::provider::{Protocol, Socket, UsernamePattern};
|
||||
use crate::provider::{Protocol, Provider, Socket, UsernamePattern};
|
||||
use crate::qr::set_account_from_qr;
|
||||
use crate::smtp::Smtp;
|
||||
use crate::sync::Sync::*;
|
||||
@@ -63,7 +63,7 @@ macro_rules! progress {
|
||||
impl Context {
|
||||
/// Checks if the context is already configured.
|
||||
pub async fn is_configured(&self) -> Result<bool> {
|
||||
self.sql.get_raw_config_bool("configured").await
|
||||
self.sql.exists("SELECT COUNT(*) FROM transports", ()).await
|
||||
}
|
||||
|
||||
/// Configures this account with the currently provided parameters.
|
||||
@@ -181,9 +181,21 @@ impl Context {
|
||||
/// Use [Self::add_transport()] to add or change a transport
|
||||
/// and [Self::delete_transport()] to delete a transport.
|
||||
pub async fn list_transports(&self) -> Result<Vec<EnteredLoginParam>> {
|
||||
let param = EnteredLoginParam::load(self).await?;
|
||||
let transports = self
|
||||
.sql
|
||||
.query_map(
|
||||
"SELECT entered_param FROM transports",
|
||||
(),
|
||||
|row| row.get::<_, String>(0),
|
||||
|rows| {
|
||||
rows.flatten()
|
||||
.map(|s| Ok(serde_json::from_str(&s)?))
|
||||
.collect::<Result<Vec<EnteredLoginParam>>>()
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(vec![param])
|
||||
Ok(transports)
|
||||
}
|
||||
|
||||
/// Removes the transport with the specified email address
|
||||
@@ -197,20 +209,20 @@ impl Context {
|
||||
info!(self, "Configure ...");
|
||||
|
||||
let old_addr = self.get_config(Config::ConfiguredAddr).await?;
|
||||
let configured_param = configure(self, param).await?;
|
||||
let provider = configure(self, param).await?;
|
||||
self.set_config_internal(Config::NotifyAboutWrongPw, Some("1"))
|
||||
.await?;
|
||||
on_configure_completed(self, configured_param, old_addr).await?;
|
||||
on_configure_completed(self, provider, old_addr).await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
async fn on_configure_completed(
|
||||
context: &Context,
|
||||
param: ConfiguredLoginParam,
|
||||
provider: Option<&'static Provider>,
|
||||
old_addr: Option<String>,
|
||||
) -> Result<()> {
|
||||
if let Some(provider) = param.provider {
|
||||
if let Some(provider) = provider {
|
||||
if let Some(config_defaults) = provider.config_defaults {
|
||||
for def in config_defaults {
|
||||
if !context.config_exists(def.key).await? {
|
||||
@@ -446,7 +458,7 @@ async fn get_configured_param(
|
||||
Ok(configured_login_param)
|
||||
}
|
||||
|
||||
async fn configure(ctx: &Context, param: &EnteredLoginParam) -> Result<ConfiguredLoginParam> {
|
||||
async fn configure(ctx: &Context, param: &EnteredLoginParam) -> Result<Option<&'static Provider>> {
|
||||
progress!(ctx, 1);
|
||||
|
||||
let ctx2 = ctx.clone();
|
||||
@@ -556,7 +568,11 @@ async fn configure(ctx: &Context, param: &EnteredLoginParam) -> Result<Configure
|
||||
}
|
||||
}
|
||||
|
||||
configured_param.save_as_configured_params(ctx).await?;
|
||||
let provider = configured_param.provider;
|
||||
configured_param
|
||||
.save_to_transports_table(ctx, param)
|
||||
.await?;
|
||||
|
||||
ctx.set_config_internal(Config::ConfiguredTimestamp, Some(&time().to_string()))
|
||||
.await?;
|
||||
|
||||
@@ -572,7 +588,7 @@ async fn configure(ctx: &Context, param: &EnteredLoginParam) -> Result<Configure
|
||||
ctx.sql.set_raw_config_bool("configured", true).await?;
|
||||
ctx.emit_event(EventType::AccountsItemChanged);
|
||||
|
||||
Ok(configured_param)
|
||||
Ok(provider)
|
||||
}
|
||||
|
||||
/// Retrieve available autoconfigurations.
|
||||
|
||||
@@ -2,26 +2,38 @@
|
||||
|
||||
use std::fmt;
|
||||
|
||||
use anyhow::{format_err, Context as _, Result};
|
||||
use deltachat_contact_tools::EmailAddress;
|
||||
use anyhow::{bail, ensure, format_err, Context as _, Result};
|
||||
use deltachat_contact_tools::{addr_cmp, addr_normalize, EmailAddress};
|
||||
use num_traits::ToPrimitive as _;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::config::Config;
|
||||
use crate::configure::server_params::{expand_param_vector, ServerParams};
|
||||
use crate::constants::{DC_LP_AUTH_FLAGS, DC_LP_AUTH_NORMAL, DC_LP_AUTH_OAUTH2};
|
||||
use crate::constants::{DC_LP_AUTH_FLAGS, DC_LP_AUTH_OAUTH2};
|
||||
use crate::context::Context;
|
||||
use crate::net::load_connection_timestamp;
|
||||
pub use crate::net::proxy::ProxyConfig;
|
||||
pub use crate::provider::Socket;
|
||||
use crate::provider::{Protocol, Provider, UsernamePattern};
|
||||
use crate::provider::{get_provider_by_id, Protocol, Provider, UsernamePattern};
|
||||
use crate::sql::Sql;
|
||||
use crate::tools::ToOption;
|
||||
|
||||
/// User-entered setting for certificate checks.
|
||||
///
|
||||
/// Should be saved into `imap_certificate_checks` before running configuration.
|
||||
#[derive(Copy, Clone, Debug, Default, Display, FromPrimitive, ToPrimitive, PartialEq, Eq)]
|
||||
#[derive(
|
||||
Copy,
|
||||
Clone,
|
||||
Debug,
|
||||
Default,
|
||||
Display,
|
||||
FromPrimitive,
|
||||
ToPrimitive,
|
||||
PartialEq,
|
||||
Eq,
|
||||
Serialize,
|
||||
Deserialize,
|
||||
)]
|
||||
#[repr(u32)]
|
||||
#[strum(serialize_all = "snake_case")]
|
||||
pub enum EnteredCertificateChecks {
|
||||
@@ -44,7 +56,9 @@ pub enum EnteredCertificateChecks {
|
||||
}
|
||||
|
||||
/// Values saved into `imap_certificate_checks`.
|
||||
#[derive(Copy, Clone, Debug, Display, FromPrimitive, ToPrimitive, PartialEq, Eq)]
|
||||
#[derive(
|
||||
Copy, Clone, Debug, Display, FromPrimitive, ToPrimitive, PartialEq, Eq, Serialize, Deserialize,
|
||||
)]
|
||||
#[repr(u32)]
|
||||
#[strum(serialize_all = "snake_case")]
|
||||
pub(crate) enum ConfiguredCertificateChecks {
|
||||
@@ -81,7 +95,7 @@ pub(crate) enum ConfiguredCertificateChecks {
|
||||
}
|
||||
|
||||
/// Login parameters for a single server, either IMAP or SMTP
|
||||
#[derive(Debug, Default, Clone, PartialEq, Eq)]
|
||||
#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct EnteredServerLoginParam {
|
||||
/// Server hostname or IP address.
|
||||
pub server: String,
|
||||
@@ -104,7 +118,7 @@ pub struct EnteredServerLoginParam {
|
||||
}
|
||||
|
||||
/// Login parameters entered by the user.
|
||||
#[derive(Debug, Default, Clone, PartialEq, Eq)]
|
||||
#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct EnteredLoginParam {
|
||||
/// Email address.
|
||||
pub addr: String,
|
||||
@@ -451,6 +465,22 @@ pub(crate) struct ConfiguredLoginParam {
|
||||
pub oauth2: bool,
|
||||
}
|
||||
|
||||
/// The representation of ConfiguredLoginParam in the database,
|
||||
/// saved as Json.
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
struct ConfiguredLoginParamJson {
|
||||
pub addr: String,
|
||||
pub imap: Vec<ConfiguredServerLoginParam>,
|
||||
pub imap_user: String,
|
||||
pub imap_password: String,
|
||||
pub smtp: Vec<ConfiguredServerLoginParam>,
|
||||
pub smtp_user: String,
|
||||
pub smtp_password: String,
|
||||
pub provider_id: Option<String>,
|
||||
pub certificate_checks: ConfiguredCertificateChecks,
|
||||
pub oauth2: bool,
|
||||
}
|
||||
|
||||
impl fmt::Display for ConfiguredLoginParam {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let addr = &self.addr;
|
||||
@@ -487,6 +517,26 @@ impl ConfiguredLoginParam {
|
||||
///
|
||||
/// Returns `None` if account is not configured.
|
||||
pub(crate) async fn load(context: &Context) -> Result<Option<Self>> {
|
||||
let Some(self_addr) = context.get_config(Config::ConfiguredAddr).await? else {
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
let json: Option<String> = context
|
||||
.sql
|
||||
.query_get_value(
|
||||
"SELECT configured_param FROM transports WHERE addr=?",
|
||||
(&self_addr,),
|
||||
)
|
||||
.await?;
|
||||
if let Some(json) = json {
|
||||
Ok(Some(Self::from_json(&json)?))
|
||||
} else {
|
||||
bail!("Self address {self_addr} doesn't have a corresponding transport");
|
||||
}
|
||||
}
|
||||
|
||||
/// Loads legacy configured param. Only used for tests and the migration.
|
||||
pub(crate) async fn load_legacy(context: &Context) -> Result<Option<Self>> {
|
||||
if !context.get_config_bool(Config::Configured).await? {
|
||||
return Ok(None);
|
||||
}
|
||||
@@ -753,84 +803,82 @@ impl ConfiguredLoginParam {
|
||||
}))
|
||||
}
|
||||
|
||||
/// Save this loginparam to the database.
|
||||
pub(crate) async fn save_as_configured_params(&self, context: &Context) -> Result<()> {
|
||||
context.set_primary_self_addr(&self.addr).await?;
|
||||
|
||||
pub(crate) async fn save_to_transports_table(
|
||||
self,
|
||||
context: &Context,
|
||||
entered_param: &EnteredLoginParam,
|
||||
) -> Result<()> {
|
||||
let addr = addr_normalize(&self.addr);
|
||||
let configured_addr = context.get_config(Config::ConfiguredAddr).await?;
|
||||
if let Some(configured_addr) = configured_addr {
|
||||
ensure!(
|
||||
addr_cmp(&configured_addr, &addr,),
|
||||
"Adding a second transport is not supported right now."
|
||||
);
|
||||
}
|
||||
context
|
||||
.set_config(
|
||||
Config::ConfiguredImapServers,
|
||||
Some(&serde_json::to_string(&self.imap)?),
|
||||
)
|
||||
.await?;
|
||||
context
|
||||
.set_config(
|
||||
Config::ConfiguredSmtpServers,
|
||||
Some(&serde_json::to_string(&self.smtp)?),
|
||||
)
|
||||
.await?;
|
||||
|
||||
context
|
||||
.set_config(Config::ConfiguredMailUser, Some(&self.imap_user))
|
||||
.await?;
|
||||
context
|
||||
.set_config(Config::ConfiguredMailPw, Some(&self.imap_password))
|
||||
.await?;
|
||||
|
||||
context
|
||||
.set_config(Config::ConfiguredSendUser, Some(&self.smtp_user))
|
||||
.await?;
|
||||
context
|
||||
.set_config(Config::ConfiguredSendPw, Some(&self.smtp_password))
|
||||
.await?;
|
||||
|
||||
context
|
||||
.set_config_u32(
|
||||
Config::ConfiguredImapCertificateChecks,
|
||||
self.certificate_checks as u32,
|
||||
)
|
||||
.await?;
|
||||
context
|
||||
.set_config_u32(
|
||||
Config::ConfiguredSmtpCertificateChecks,
|
||||
self.certificate_checks as u32,
|
||||
)
|
||||
.await?;
|
||||
|
||||
// Remove legacy settings.
|
||||
context
|
||||
.set_config(Config::ConfiguredMailServer, None)
|
||||
.await?;
|
||||
context.set_config(Config::ConfiguredMailPort, None).await?;
|
||||
context
|
||||
.set_config(Config::ConfiguredMailSecurity, None)
|
||||
.await?;
|
||||
context
|
||||
.set_config(Config::ConfiguredSendServer, None)
|
||||
.await?;
|
||||
context.set_config(Config::ConfiguredSendPort, None).await?;
|
||||
context
|
||||
.set_config(Config::ConfiguredSendSecurity, None)
|
||||
.await?;
|
||||
|
||||
let server_flags = match self.oauth2 {
|
||||
true => DC_LP_AUTH_OAUTH2,
|
||||
false => DC_LP_AUTH_NORMAL,
|
||||
};
|
||||
context
|
||||
.set_config_u32(Config::ConfiguredServerFlags, server_flags as u32)
|
||||
.await?;
|
||||
|
||||
context
|
||||
.set_config(
|
||||
Config::ConfiguredProvider,
|
||||
.sql
|
||||
.set_raw_config(
|
||||
Config::ConfiguredProvider.as_ref(),
|
||||
self.provider.map(|provider| provider.id),
|
||||
)
|
||||
.await?;
|
||||
|
||||
context
|
||||
.sql
|
||||
.execute(
|
||||
"INSERT INTO transports (addr, entered_param, configured_param)
|
||||
VALUES (?, ?, ?)
|
||||
ON CONFLICT (addr)
|
||||
DO UPDATE SET entered_param=excluded.entered_param, configured_param=excluded.configured_param",
|
||||
(
|
||||
self.addr.clone(),
|
||||
serde_json::to_string(entered_param)?,
|
||||
self.into_json()?,
|
||||
),
|
||||
)
|
||||
.await?;
|
||||
context
|
||||
.sql
|
||||
.set_raw_config(Config::ConfiguredAddr.as_ref(), Some(&addr))
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn from_json(json: &str) -> Result<Self> {
|
||||
let json: ConfiguredLoginParamJson = serde_json::from_str(json)?;
|
||||
|
||||
let provider = json.provider_id.and_then(|id| get_provider_by_id(&id));
|
||||
|
||||
Ok(ConfiguredLoginParam {
|
||||
addr: json.addr,
|
||||
imap: json.imap,
|
||||
imap_user: json.imap_user,
|
||||
imap_password: json.imap_password,
|
||||
smtp: json.smtp,
|
||||
smtp_user: json.smtp_user,
|
||||
smtp_password: json.smtp_password,
|
||||
provider,
|
||||
certificate_checks: json.certificate_checks,
|
||||
oauth2: json.oauth2,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn into_json(self) -> Result<String> {
|
||||
let json = ConfiguredLoginParamJson {
|
||||
addr: self.addr,
|
||||
imap: self.imap,
|
||||
imap_user: self.imap_user,
|
||||
imap_password: self.imap_password,
|
||||
smtp: self.smtp,
|
||||
smtp_user: self.smtp_user,
|
||||
smtp_password: self.smtp_password,
|
||||
provider_id: self.provider.map(|p| p.id.to_string()),
|
||||
certificate_checks: self.certificate_checks,
|
||||
oauth2: self.oauth2,
|
||||
};
|
||||
Ok(serde_json::to_string(&json)?)
|
||||
}
|
||||
|
||||
pub(crate) fn strict_tls(&self, connected_through_proxy: bool) -> bool {
|
||||
let provider_strict_tls = self.provider.map(|provider| provider.opt.strict_tls);
|
||||
match self.certificate_checks {
|
||||
@@ -848,8 +896,10 @@ impl ConfiguredLoginParam {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::log::LogExt as _;
|
||||
use crate::provider::get_provider_by_id;
|
||||
use crate::test_utils::TestContext;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
#[test]
|
||||
fn test_certificate_checks_display() {
|
||||
@@ -961,18 +1011,33 @@ mod tests {
|
||||
oauth2: false,
|
||||
};
|
||||
|
||||
param.save_as_configured_params(&t).await?;
|
||||
param
|
||||
.clone()
|
||||
.save_to_transports_table(&t, &EnteredLoginParam::default())
|
||||
.await?;
|
||||
let expected_param = r#"{"addr":"alice@example.org","imap":[{"connection":{"host":"imap.example.com","port":123,"security":"Starttls"},"user":"alice"}],"imap_user":"","imap_password":"foo","smtp":[{"connection":{"host":"smtp.example.com","port":456,"security":"Tls"},"user":"alice@example.org"}],"smtp_user":"","smtp_password":"bar","provider_id":null,"certificate_checks":"Strict","oauth2":false}"#;
|
||||
assert_eq!(
|
||||
t.get_config(Config::ConfiguredImapServers).await?.unwrap(),
|
||||
r#"[{"connection":{"host":"imap.example.com","port":123,"security":"Starttls"},"user":"alice"}]"#
|
||||
t.sql
|
||||
.query_get_value::<String>("SELECT configured_param FROM transports", ())
|
||||
.await?
|
||||
.unwrap(),
|
||||
expected_param
|
||||
);
|
||||
t.set_config(Config::Configured, Some("1")).await?;
|
||||
assert_eq!(t.is_configured().await?, true);
|
||||
let loaded = ConfiguredLoginParam::load(&t).await?.unwrap();
|
||||
assert_eq!(param, loaded);
|
||||
|
||||
// Test that we don't panic on unknown ConfiguredImapCertificateChecks values.
|
||||
// Legacy ConfiguredImapCertificateChecks config is ignored
|
||||
t.set_config(Config::ConfiguredImapCertificateChecks, Some("999"))
|
||||
.await?;
|
||||
assert!(ConfiguredLoginParam::load(&t).await.is_ok());
|
||||
|
||||
// Test that we don't panic on unknown ConfiguredImapCertificateChecks values.
|
||||
let wrong_param = expected_param.replace("Strict", "Stricct");
|
||||
assert_ne!(expected_param, wrong_param);
|
||||
t.sql
|
||||
.execute("UPDATE transports SET configured_param=?", (wrong_param,))
|
||||
.await?;
|
||||
assert!(ConfiguredLoginParam::load(&t).await.is_err());
|
||||
|
||||
Ok(())
|
||||
@@ -989,7 +1054,8 @@ mod tests {
|
||||
t.set_config(Config::Configured, Some("1")).await?;
|
||||
t.set_config(Config::ConfiguredProvider, Some("posteo"))
|
||||
.await?;
|
||||
t.set_config(Config::ConfiguredAddr, Some("alice@posteo.at"))
|
||||
t.sql
|
||||
.set_raw_config(Config::ConfiguredAddr.as_ref(), Some("alice@posteo.at"))
|
||||
.await?;
|
||||
t.set_config(Config::ConfiguredMailServer, Some("posteo.de"))
|
||||
.await?;
|
||||
@@ -1063,12 +1129,68 @@ mod tests {
|
||||
oauth2: false,
|
||||
};
|
||||
|
||||
let loaded = ConfiguredLoginParam::load_legacy(&t).await?.unwrap();
|
||||
assert_eq!(loaded, param);
|
||||
|
||||
migrate_configured_login_param(&t).await;
|
||||
let loaded = ConfiguredLoginParam::load(&t).await?.unwrap();
|
||||
assert_eq!(loaded, param);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_empty_server_list_legacy() -> Result<()> {
|
||||
// Find a provider that does not have server list set.
|
||||
//
|
||||
// There is at least one such provider in the provider database.
|
||||
let (domain, provider) = crate::provider::data::PROVIDER_DATA
|
||||
.iter()
|
||||
.find(|(_domain, provider)| provider.server.is_empty())
|
||||
.unwrap();
|
||||
|
||||
let t = TestContext::new().await;
|
||||
|
||||
let addr = format!("alice@{domain}");
|
||||
|
||||
t.set_config(Config::Configured, Some("1")).await?;
|
||||
t.set_config(Config::ConfiguredProvider, Some(provider.id))
|
||||
.await?;
|
||||
t.sql
|
||||
.set_raw_config(Config::ConfiguredAddr.as_ref(), Some(&addr))
|
||||
.await?;
|
||||
t.set_config(Config::ConfiguredMailPw, Some("foobarbaz"))
|
||||
.await?;
|
||||
t.set_config(Config::ConfiguredImapCertificateChecks, Some("1"))
|
||||
.await?; // Strict
|
||||
t.set_config(Config::ConfiguredSendPw, Some("foobarbaz"))
|
||||
.await?;
|
||||
t.set_config(Config::ConfiguredSmtpCertificateChecks, Some("1"))
|
||||
.await?; // Strict
|
||||
t.set_config(Config::ConfiguredServerFlags, Some("0"))
|
||||
.await?;
|
||||
|
||||
let loaded = ConfiguredLoginParam::load_legacy(&t).await?.unwrap();
|
||||
assert_eq!(loaded.provider, Some(*provider));
|
||||
assert_eq!(loaded.imap.is_empty(), false);
|
||||
assert_eq!(loaded.smtp.is_empty(), false);
|
||||
|
||||
migrate_configured_login_param(&t).await;
|
||||
|
||||
let loaded = ConfiguredLoginParam::load(&t).await?.unwrap();
|
||||
assert_eq!(loaded.provider, Some(*provider));
|
||||
assert_eq!(loaded.imap.is_empty(), false);
|
||||
assert_eq!(loaded.smtp.is_empty(), false);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn migrate_configured_login_param(t: &TestContext) {
|
||||
t.sql.execute("DROP TABLE transports;", ()).await.unwrap();
|
||||
t.sql.set_raw_config_int("dbversion", 130).await.unwrap();
|
||||
t.sql.run_migrations(t).await.log_err(t).ok();
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_empty_server_list() -> Result<()> {
|
||||
// Find a provider that does not have server list set.
|
||||
@@ -1083,25 +1205,41 @@ mod tests {
|
||||
|
||||
let addr = format!("alice@{domain}");
|
||||
|
||||
t.set_config(Config::Configured, Some("1")).await?;
|
||||
t.set_config(Config::ConfiguredProvider, Some(provider.id))
|
||||
.await?;
|
||||
t.set_config(Config::ConfiguredAddr, Some(&addr)).await?;
|
||||
t.set_config(Config::ConfiguredMailPw, Some("foobarbaz"))
|
||||
.await?;
|
||||
t.set_config(Config::ConfiguredImapCertificateChecks, Some("1"))
|
||||
.await?; // Strict
|
||||
t.set_config(Config::ConfiguredSendPw, Some("foobarbaz"))
|
||||
.await?;
|
||||
t.set_config(Config::ConfiguredSmtpCertificateChecks, Some("1"))
|
||||
.await?; // Strict
|
||||
t.set_config(Config::ConfiguredServerFlags, Some("0"))
|
||||
.await?;
|
||||
ConfiguredLoginParam {
|
||||
addr: addr.clone(),
|
||||
imap: vec![ConfiguredServerLoginParam {
|
||||
connection: ConnectionCandidate {
|
||||
host: "example.org".to_string(),
|
||||
port: 100,
|
||||
security: ConnectionSecurity::Tls,
|
||||
},
|
||||
user: addr.clone(),
|
||||
}],
|
||||
imap_user: addr.clone(),
|
||||
imap_password: "foobarbaz".to_string(),
|
||||
smtp: vec![ConfiguredServerLoginParam {
|
||||
connection: ConnectionCandidate {
|
||||
host: "example.org".to_string(),
|
||||
port: 100,
|
||||
security: ConnectionSecurity::Tls,
|
||||
},
|
||||
user: addr.clone(),
|
||||
}],
|
||||
smtp_user: addr.clone(),
|
||||
smtp_password: "foobarbaz".to_string(),
|
||||
provider: Some(provider),
|
||||
certificate_checks: ConfiguredCertificateChecks::Automatic,
|
||||
oauth2: false,
|
||||
}
|
||||
.save_to_transports_table(&t, &EnteredLoginParam::default())
|
||||
.await?;
|
||||
|
||||
let loaded = ConfiguredLoginParam::load(&t).await?.unwrap();
|
||||
assert_eq!(loaded.provider, Some(*provider));
|
||||
assert_eq!(loaded.imap.is_empty(), false);
|
||||
assert_eq!(loaded.smtp.is_empty(), false);
|
||||
assert_eq!(t.get_configured_provider().await?, Some(*provider));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ pub(crate) mod data;
|
||||
use anyhow::Result;
|
||||
use deltachat_contact_tools::EmailAddress;
|
||||
use hickory_resolver::{config, Resolver, TokioResolver};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::config::Config;
|
||||
use crate::context::Context;
|
||||
@@ -37,7 +38,19 @@ pub enum Protocol {
|
||||
}
|
||||
|
||||
/// Socket security.
|
||||
#[derive(Debug, Default, Display, PartialEq, Eq, Copy, Clone, FromPrimitive, ToPrimitive)]
|
||||
#[derive(
|
||||
Debug,
|
||||
Default,
|
||||
Display,
|
||||
PartialEq,
|
||||
Eq,
|
||||
Copy,
|
||||
Clone,
|
||||
FromPrimitive,
|
||||
ToPrimitive,
|
||||
Serialize,
|
||||
Deserialize,
|
||||
)]
|
||||
#[repr(u8)]
|
||||
pub enum Socket {
|
||||
/// Unspecified socket security, select automatically.
|
||||
|
||||
@@ -3113,7 +3113,6 @@ Message with references."#;
|
||||
async fn test_rfc1847_encapsulation() -> Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
let bob = TestContext::new_bob().await;
|
||||
alice.configure_addr("alice@example.org").await;
|
||||
|
||||
// Alice sends an Autocrypt message to Bob so Bob gets Alice's key.
|
||||
let chat_alice = alice.create_chat(&bob).await;
|
||||
|
||||
@@ -5,9 +5,11 @@ use deltachat_contact_tools::EmailAddress;
|
||||
use rusqlite::OptionalExtension;
|
||||
|
||||
use crate::config::Config;
|
||||
use crate::configure::EnteredLoginParam;
|
||||
use crate::constants::ShowEmails;
|
||||
use crate::context::Context;
|
||||
use crate::imap;
|
||||
use crate::login_param::ConfiguredLoginParam;
|
||||
use crate::message::MsgId;
|
||||
use crate::provider::get_provider_by_domain;
|
||||
use crate::sql::Sql;
|
||||
@@ -1186,6 +1188,42 @@ CREATE INDEX gossip_timestamp_index ON gossip_timestamp (chat_id, fingerprint);
|
||||
.await?;
|
||||
}
|
||||
|
||||
inc_and_check(&mut migration_version, 131)?;
|
||||
if dbversion < migration_version {
|
||||
let entered_param = EnteredLoginParam::load(context).await?;
|
||||
let configured_param = ConfiguredLoginParam::load_legacy(context).await?;
|
||||
|
||||
sql.execute_migration_transaction(
|
||||
|transaction| {
|
||||
transaction.execute(
|
||||
"CREATE TABLE transports (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
addr TEXT NOT NULL,
|
||||
entered_param TEXT NOT NULL,
|
||||
configured_param TEXT NOT NULL,
|
||||
UNIQUE(addr)
|
||||
)",
|
||||
(),
|
||||
)?;
|
||||
if let Some(configured_param) = configured_param {
|
||||
transaction.execute(
|
||||
"INSERT INTO transports (addr, entered_param, configured_param)
|
||||
VALUES (?, ?, ?)",
|
||||
(
|
||||
configured_param.addr.clone(),
|
||||
serde_json::to_string(&entered_param)?,
|
||||
configured_param.into_json()?,
|
||||
),
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
},
|
||||
migration_version,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
|
||||
let new_version = sql
|
||||
.get_raw_config_int(VERSION_CFG)
|
||||
.await?
|
||||
@@ -1230,6 +1268,21 @@ impl Sql {
|
||||
}
|
||||
|
||||
async fn execute_migration(&self, query: &str, version: i32) -> Result<()> {
|
||||
self.execute_migration_transaction(
|
||||
|transaction| {
|
||||
transaction.execute_batch(query)?;
|
||||
Ok(())
|
||||
},
|
||||
version,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn execute_migration_transaction(
|
||||
&self,
|
||||
migration: impl Send + FnOnce(&mut rusqlite::Transaction) -> Result<()>,
|
||||
version: i32,
|
||||
) -> Result<()> {
|
||||
self.transaction(move |transaction| {
|
||||
let curr_version: String = transaction.query_row(
|
||||
"SELECT IFNULL(value, ?) FROM config WHERE keyname=?;",
|
||||
@@ -1239,7 +1292,7 @@ impl Sql {
|
||||
let curr_version: i32 = curr_version.parse()?;
|
||||
ensure!(curr_version < version, "Db version must be increased");
|
||||
Self::set_db_version_trans(transaction, version)?;
|
||||
transaction.execute_batch(query)?;
|
||||
migration(transaction)?;
|
||||
|
||||
Ok(())
|
||||
})
|
||||
|
||||
@@ -477,15 +477,11 @@ impl TestContext {
|
||||
/// The context will be configured but the key will not be pre-generated so if a key is
|
||||
/// used the fingerprint will be different every time.
|
||||
pub async fn configure_addr(&self, addr: &str) {
|
||||
self.ctx.set_config(Config::Addr, Some(addr)).await.unwrap();
|
||||
self.ctx
|
||||
.set_config(Config::ConfiguredAddr, Some(addr))
|
||||
.await
|
||||
.unwrap();
|
||||
self.ctx
|
||||
.set_config(Config::Configured, Some("1"))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
if let Some(name) = addr.split('@').next() {
|
||||
self.set_name(name);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user