diff --git a/deltachat-rpc-client/src/deltachat_rpc_client/pytestplugin.py b/deltachat-rpc-client/src/deltachat_rpc_client/pytestplugin.py index a4d111108..da92f430f 100644 --- a/deltachat-rpc-client/src/deltachat_rpc_client/pytestplugin.py +++ b/deltachat-rpc-client/src/deltachat_rpc_client/pytestplugin.py @@ -45,8 +45,8 @@ class ACFactory: """Create a new configured account.""" addr, password = self.get_credentials() account = self.get_unconfigured_account() - params = {"addr": addr, "password": password} - yield account.add_or_update_transport.future(params) + domain = os.getenv("CHATMAIL_DOMAIN") + yield account.add_transport_from_qr.future(f"dcaccount:{domain}") assert account.is_configured() return account diff --git a/deltachat-rpc-client/tests/test_something.py b/deltachat-rpc-client/tests/test_something.py index 7f437f14a..f219f2bcd 100644 --- a/deltachat-rpc-client/tests/test_something.py +++ b/deltachat-rpc-client/tests/test_something.py @@ -816,7 +816,7 @@ def test_configured_imap_certificate_checks(acfactory): alice = acfactory.new_configured_account() # Certificate checks should be configured (not None) - assert "cert_automatic" in alice.get_info().used_account_settings + assert "cert_strict" in alice.get_info().used_account_settings # "cert_old_automatic" is the value old Delta Chat core versions used # to mean user entered "imap_certificate_checks=0" (Automatic) diff --git a/src/qr.rs b/src/qr.rs index a1b780ec3..da2457822 100644 --- a/src/qr.rs +++ b/src/qr.rs @@ -9,6 +9,8 @@ pub use dclogin_scheme::LoginOptions; pub(crate) use dclogin_scheme::login_param_from_login_qr; use deltachat_contact_tools::{ContactAddress, addr_normalize, may_be_valid_addr}; use percent_encoding::{NON_ALPHANUMERIC, percent_decode_str, percent_encode}; +use rand::TryRngCore as _; +use rand::distr::{Alphanumeric, SampleString}; use serde::Deserialize; use crate::config::Config; @@ -543,21 +545,29 @@ async fn decode_ideltachat(context: &Context, prefix: &str, qr: &str) -> Result< .with_context(|| format!("failed to decode {prefix} QR code")) } -/// scheme: `DCACCOUNT:https://example.org/new_email?t=1w_7wDjgjelxeX884x96v3` +/// scheme: `DCACCOUNT:example.org` +/// or `DCACCOUNT:https://example.org/new` +/// or `DCACCOUNT:https://example.org/new_email?t=1w_7wDjgjelxeX884x96v3` fn decode_account(qr: &str) -> Result { let payload = qr .get(DCACCOUNT_SCHEME.len()..) .context("Invalid DCACCOUNT payload")?; - let url = url::Url::parse(payload).context("Invalid account URL")?; - if url.scheme() == "http" || url.scheme() == "https" { - Ok(Qr::Account { - domain: url - .host_str() - .context("can't extract account setup domain")? - .to_string(), - }) + if payload.starts_with("https://") { + let url = url::Url::parse(payload).context("Invalid account URL")?; + if url.scheme() == "https" { + Ok(Qr::Account { + domain: url + .host_str() + .context("can't extract account setup domain")? + .to_string(), + }) + } else { + bail!("Bad scheme for account URL: {:?}.", url.scheme()); + } } else { - bail!("Bad scheme for account URL: {:?}.", url.scheme()); + Ok(Qr::Account { + domain: payload.to_string(), + }) } } @@ -659,15 +669,30 @@ pub(crate) async fn login_param_from_account_qr( context: &Context, qr: &str, ) -> Result { - let url_str = qr + let payload = qr .get(DCACCOUNT_SCHEME.len()..) .context("Invalid DCACCOUNT scheme")?; - if !url_str.starts_with(HTTPS_SCHEME) { - bail!("DCACCOUNT QR codes must use HTTPS scheme"); + if !payload.starts_with(HTTPS_SCHEME) { + let rng = &mut rand::rngs::OsRng.unwrap_err(); + let username = Alphanumeric.sample_string(rng, 9); + let addr = username + "@" + payload; + let password = Alphanumeric.sample_string(rng, 50); + + let param = EnteredLoginParam { + addr, + imap: EnteredServerLoginParam { + password, + ..Default::default() + }, + smtp: Default::default(), + certificate_checks: EnteredCertificateChecks::Strict, + oauth2: false, + }; + return Ok(param); } - let (response_text, response_success) = post_empty(context, url_str).await?; + let (response_text, response_success) = post_empty(context, payload).await?; if response_success { let CreateAccountSuccessResponse { password, email } = serde_json::from_str(&response_text) .with_context(|| { diff --git a/src/qr/qr_tests.rs b/src/qr/qr_tests.rs index 4accac634..b5839a404 100644 --- a/src/qr/qr_tests.rs +++ b/src/qr/qr_tests.rs @@ -643,30 +643,20 @@ async fn test_decode_dclogin_advanced_options() -> Result<()> { async fn test_decode_account() -> Result<()> { let ctx = TestContext::new().await; - let qr = check_qr( - &ctx.ctx, + for text in [ + "DCACCOUNT:example.org", + "dcaccount:example.org", "DCACCOUNT:https://example.org/new_email?t=1w_7wDjgjelxeX884x96v3", - ) - .await?; - assert_eq!( - qr, - Qr::Account { - domain: "example.org".to_string() - } - ); - - // Test it again with lowercased "dcaccount:" uri scheme - let qr = check_qr( - &ctx.ctx, "dcaccount:https://example.org/new_email?t=1w_7wDjgjelxeX884x96v3", - ) - .await?; - assert_eq!( - qr, - Qr::Account { - domain: "example.org".to_string() - } - ); + ] { + let qr = check_qr(&ctx.ctx, text).await?; + assert_eq!( + qr, + Qr::Account { + domain: "example.org".to_string() + } + ); + } Ok(()) } @@ -734,25 +724,6 @@ async fn test_decode_tg_socks_proxy() -> Result<()> { Ok(()) } -#[tokio::test(flavor = "multi_thread", worker_threads = 2)] -async fn test_decode_account_bad_scheme() { - let ctx = TestContext::new().await; - let res = check_qr( - &ctx.ctx, - "DCACCOUNT:ftp://example.org/new_email?t=1w_7wDjgjelxeX884x96v3", - ) - .await; - assert!(res.is_err()); - - // Test it again with lowercased "dcaccount:" uri scheme - let res = check_qr( - &ctx.ctx, - "dcaccount:ftp://example.org/new_email?t=1w_7wDjgjelxeX884x96v3", - ) - .await; - assert!(res.is_err()); -} - #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_set_proxy_config_from_qr() -> Result<()> { let t = TestContext::new().await;