Skip to content

Commit 05ba206

Browse files
committed
feat: allow plain domain in dcaccount: scheme
This is similar to old `dcaccount:` with URL, but creates a 9-character username on the client and avoids making an HTTPS request. The scheme is reused to avoid the apps needing to register for the new scheme. `http` support is removed because it was not working already, there is a check that the scheme is `https` when the URL is actually used and the core has no way to make HTTP requests without TLS.
1 parent 9f0d106 commit 05ba206

File tree

4 files changed

+53
-57
lines changed

4 files changed

+53
-57
lines changed

deltachat-rpc-client/src/deltachat_rpc_client/pytestplugin.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,8 @@ def new_configured_account(self):
4545
"""Create a new configured account."""
4646
addr, password = self.get_credentials()
4747
account = self.get_unconfigured_account()
48-
params = {"addr": addr, "password": password}
49-
yield account.add_or_update_transport.future(params)
48+
domain = os.getenv("CHATMAIL_DOMAIN")
49+
yield account.add_transport_from_qr.future(f"dcaccount:{domain}")
5050

5151
assert account.is_configured()
5252
return account

deltachat-rpc-client/tests/test_something.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -816,7 +816,7 @@ def test_configured_imap_certificate_checks(acfactory):
816816
alice = acfactory.new_configured_account()
817817

818818
# Certificate checks should be configured (not None)
819-
assert "cert_automatic" in alice.get_info().used_account_settings
819+
assert "cert_strict" in alice.get_info().used_account_settings
820820

821821
# "cert_old_automatic" is the value old Delta Chat core versions used
822822
# to mean user entered "imap_certificate_checks=0" (Automatic)

src/qr.rs

Lines changed: 38 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ pub use dclogin_scheme::LoginOptions;
99
pub(crate) use dclogin_scheme::login_param_from_login_qr;
1010
use deltachat_contact_tools::{ContactAddress, addr_normalize, may_be_valid_addr};
1111
use percent_encoding::{NON_ALPHANUMERIC, percent_decode_str, percent_encode};
12+
use rand::TryRngCore as _;
13+
use rand::distr::{Alphanumeric, SampleString};
1214
use serde::Deserialize;
1315

1416
use crate::config::Config;
@@ -543,21 +545,29 @@ async fn decode_ideltachat(context: &Context, prefix: &str, qr: &str) -> Result<
543545
.with_context(|| format!("failed to decode {prefix} QR code"))
544546
}
545547

546-
/// scheme: `DCACCOUNT:https://example.org/new_email?t=1w_7wDjgjelxeX884x96v3`
548+
/// scheme: `DCACCOUNT:example.org`
549+
/// or `DCACCOUNT:https://example.org/new`
550+
/// or `DCACCOUNT:https://example.org/new_email?t=1w_7wDjgjelxeX884x96v3`
547551
fn decode_account(qr: &str) -> Result<Qr> {
548552
let payload = qr
549553
.get(DCACCOUNT_SCHEME.len()..)
550554
.context("Invalid DCACCOUNT payload")?;
551-
let url = url::Url::parse(payload).context("Invalid account URL")?;
552-
if url.scheme() == "http" || url.scheme() == "https" {
555+
if payload.starts_with("https://") {
556+
let url = url::Url::parse(payload).context("Invalid account URL")?;
557+
if url.scheme() == "https" {
558+
Ok(Qr::Account {
559+
domain: url
560+
.host_str()
561+
.context("can't extract account setup domain")?
562+
.to_string(),
563+
})
564+
} else {
565+
bail!("Bad scheme for account URL: {:?}.", url.scheme());
566+
}
567+
} else {
553568
Ok(Qr::Account {
554-
domain: url
555-
.host_str()
556-
.context("can't extract account setup domain")?
557-
.to_string(),
569+
domain: payload.to_string(),
558570
})
559-
} else {
560-
bail!("Bad scheme for account URL: {:?}.", url.scheme());
561571
}
562572
}
563573

@@ -659,15 +669,30 @@ pub(crate) async fn login_param_from_account_qr(
659669
context: &Context,
660670
qr: &str,
661671
) -> Result<EnteredLoginParam> {
662-
let url_str = qr
672+
let payload = qr
663673
.get(DCACCOUNT_SCHEME.len()..)
664674
.context("Invalid DCACCOUNT scheme")?;
665675

666-
if !url_str.starts_with(HTTPS_SCHEME) {
667-
bail!("DCACCOUNT QR codes must use HTTPS scheme");
676+
if !payload.starts_with(HTTPS_SCHEME) {
677+
let rng = &mut rand::rngs::OsRng.unwrap_err();
678+
let username = Alphanumeric.sample_string(rng, 9);
679+
let addr = username + "@" + payload;
680+
let password = Alphanumeric.sample_string(rng, 50);
681+
682+
let param = EnteredLoginParam {
683+
addr,
684+
imap: EnteredServerLoginParam {
685+
password,
686+
..Default::default()
687+
},
688+
smtp: Default::default(),
689+
certificate_checks: EnteredCertificateChecks::Strict,
690+
oauth2: false,
691+
};
692+
return Ok(param);
668693
}
669694

670-
let (response_text, response_success) = post_empty(context, url_str).await?;
695+
let (response_text, response_success) = post_empty(context, payload).await?;
671696
if response_success {
672697
let CreateAccountSuccessResponse { password, email } = serde_json::from_str(&response_text)
673698
.with_context(|| {

src/qr/qr_tests.rs

Lines changed: 12 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -643,30 +643,20 @@ async fn test_decode_dclogin_advanced_options() -> Result<()> {
643643
async fn test_decode_account() -> Result<()> {
644644
let ctx = TestContext::new().await;
645645

646-
let qr = check_qr(
647-
&ctx.ctx,
646+
for text in [
647+
"DCACCOUNT:example.org",
648+
"dcaccount:example.org",
648649
"DCACCOUNT:https://example.org/new_email?t=1w_7wDjgjelxeX884x96v3",
649-
)
650-
.await?;
651-
assert_eq!(
652-
qr,
653-
Qr::Account {
654-
domain: "example.org".to_string()
655-
}
656-
);
657-
658-
// Test it again with lowercased "dcaccount:" uri scheme
659-
let qr = check_qr(
660-
&ctx.ctx,
661650
"dcaccount:https://example.org/new_email?t=1w_7wDjgjelxeX884x96v3",
662-
)
663-
.await?;
664-
assert_eq!(
665-
qr,
666-
Qr::Account {
667-
domain: "example.org".to_string()
668-
}
669-
);
651+
] {
652+
let qr = check_qr(&ctx.ctx, text).await?;
653+
assert_eq!(
654+
qr,
655+
Qr::Account {
656+
domain: "example.org".to_string()
657+
}
658+
);
659+
}
670660

671661
Ok(())
672662
}
@@ -734,25 +724,6 @@ async fn test_decode_tg_socks_proxy() -> Result<()> {
734724
Ok(())
735725
}
736726

737-
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
738-
async fn test_decode_account_bad_scheme() {
739-
let ctx = TestContext::new().await;
740-
let res = check_qr(
741-
&ctx.ctx,
742-
"DCACCOUNT:ftp://example.org/new_email?t=1w_7wDjgjelxeX884x96v3",
743-
)
744-
.await;
745-
assert!(res.is_err());
746-
747-
// Test it again with lowercased "dcaccount:" uri scheme
748-
let res = check_qr(
749-
&ctx.ctx,
750-
"dcaccount:ftp://example.org/new_email?t=1w_7wDjgjelxeX884x96v3",
751-
)
752-
.await;
753-
assert!(res.is_err());
754-
}
755-
756727
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
757728
async fn test_set_proxy_config_from_qr() -> Result<()> {
758729
let t = TestContext::new().await;

0 commit comments

Comments
 (0)