Merge pull request #1779 from deltachat/share-webrtc-instance

share webrtc-instance via qr-code
This commit is contained in:
bjoern
2020-07-30 16:00:22 +02:00
committed by GitHub
3 changed files with 105 additions and 5 deletions

View File

@@ -375,12 +375,13 @@ int dc_set_stock_translation(dc_context_t* context, uint32_t stock_i
/**
* Set configuration values from a QR code containing an account.
* Set configuration values from a QR code.
* Before this function is called, dc_check_qr() should confirm the type of the
* QR code is DC_QR_ACCOUNT.
* QR code is DC_QR_ACCOUNT or DC_QR_WEBRTC_INSTANCE.
*
* Internally, the function will call dc_set_config()
* at least with the keys `addr` and `mail_pw`.
* Internally, the function will call dc_set_config() with the appropriate keys,
* eg. `addr` and `mail_pw` for DC_QR_ACCOUNT
* or `webrtc_instance` for DC_QR_WEBRTC_INSTANCE.
*
* @memberof dc_context_t
* @param context The context object
@@ -1927,6 +1928,7 @@ void dc_stop_ongoing_process (dc_context_t* context);
#define DC_QR_FPR_MISMATCH 220 // id=contact
#define DC_QR_FPR_WITHOUT_ADDR 230 // test1=formatted fingerprint
#define DC_QR_ACCOUNT 250 // text1=domain
#define DC_QR_WEBRTC_INSTANCE 260 // text1=domain
#define DC_QR_ADDR 320 // id=contact
#define DC_QR_TEXT 330 // text1=text
#define DC_QR_URL 332 // text1=URL
@@ -1945,6 +1947,9 @@ void dc_stop_ongoing_process (dc_context_t* context);
* - DC_QR_FPR_MISMATCH with dc_lot_t::id=Contact ID
* - DC_QR_FPR_WITHOUT_ADDR with dc_lot_t::test1=Formatted fingerprint
* - DC_QR_ACCOUNT allows creation of an account, dc_lot_t::text1=domain
* - DC_QR_WEBRTC_INSTANCE - a shared webrtc-instance
* that will be set if dc_set_config_from_qr() is called with the qr-code,
* dc_lot_t::text1=domain could be used to ask the user
* - DC_QR_ADDR with dc_lot_t::id=Contact ID
* - DC_QR_TEXT with dc_lot_t::text1=Text
* - DC_QR_URL with dc_lot_t::text1=URL

View File

@@ -91,6 +91,9 @@ pub enum LotState {
/// text1=domain
QrAccount = 250,
/// text1=domain, text2=instance pattern
QrWebrtcInstance = 260,
/// id=contact
QrAddr = 320,

View File

@@ -12,11 +12,13 @@ use crate::context::Context;
use crate::error::{bail, ensure, format_err, Error};
use crate::key::Fingerprint;
use crate::lot::{Lot, LotState};
use crate::message::Message;
use crate::param::*;
use crate::peerstate::*;
const OPENPGP4FPR_SCHEME: &str = "OPENPGP4FPR:"; // yes: uppercase
const DCACCOUNT_SCHEME: &str = "DCACCOUNT:";
const DCWEBRTC_SCHEME: &str = "DCWEBRTC:";
const MAILTO_SCHEME: &str = "mailto:";
const MATMSG_SCHEME: &str = "MATMSG:";
const VCARD_SCHEME: &str = "BEGIN:VCARD";
@@ -51,6 +53,8 @@ pub async fn check_qr(context: &Context, qr: impl AsRef<str>) -> Lot {
decode_openpgp(context, qr).await
} else if starts_with_ignore_case(qr, DCACCOUNT_SCHEME) {
decode_account(context, qr)
} else if starts_with_ignore_case(qr, DCWEBRTC_SCHEME) {
decode_webrtc_instance(context, qr)
} else if qr.starts_with(MAILTO_SCHEME) {
decode_mailto(context, qr).await
} else if qr.starts_with(SMTP_SCHEME) {
@@ -210,6 +214,31 @@ fn decode_account(_context: &Context, qr: &str) -> Lot {
lot
}
/// scheme: `DCWEBRTC:https://meet.jit.si/$ROOM`
#[allow(clippy::indexing_slicing)]
fn decode_webrtc_instance(_context: &Context, qr: &str) -> Lot {
let payload = &qr[DCWEBRTC_SCHEME.len()..];
let mut lot = Lot::new();
let (_type, url) = Message::parse_webrtc_instance(payload);
if let Ok(url) = url::Url::parse(&url) {
if url.scheme() == "http" || url.scheme() == "https" {
lot.state = LotState::QrWebrtcInstance;
lot.text1 = url.host_str().map(|x| x.to_string());
lot.text2 = Some(payload.to_string())
} else {
lot.state = LotState::QrError;
lot.text1 = Some(format!("Bad scheme for webrtc instance: {}", payload));
}
} else {
lot.state = LotState::QrError;
lot.text1 = Some(format!("Invalid webrtc instance: {}", payload));
}
lot
}
#[derive(Debug, Deserialize)]
struct CreateAccountResponse {
email: String,
@@ -220,7 +249,7 @@ struct CreateAccountResponse {
/// download additional information from the contained url and set the parameters.
/// on success, a configure::configure() should be able to log in to the account
#[allow(clippy::indexing_slicing)]
pub async fn set_config_from_qr(context: &Context, qr: &str) -> Result<(), Error> {
async fn set_account_from_qr(context: &Context, qr: &str) -> Result<(), Error> {
let url_str = &qr[DCACCOUNT_SCHEME.len()..];
let response: Result<CreateAccountResponse, surf::Error> =
@@ -240,6 +269,20 @@ pub async fn set_config_from_qr(context: &Context, qr: &str) -> Result<(), Error
Ok(())
}
pub async fn set_config_from_qr(context: &Context, qr: &str) -> Result<(), Error> {
match check_qr(context, &qr).await.state {
LotState::QrAccount => set_account_from_qr(context, qr).await,
LotState::QrWebrtcInstance => {
let val = decode_webrtc_instance(context, qr).text2;
context
.set_config(Config::WebrtcInstance, val.as_deref())
.await?;
Ok(())
}
_ => bail!("qr code does not contain config: {}", qr),
}
}
/// Extract address for the mailto scheme.
///
/// Scheme: `mailto:addr...?subject=...&body=..`
@@ -618,6 +661,25 @@ mod tests {
assert_eq!(res.get_text1().unwrap(), "example.org");
}
#[async_std::test]
async fn test_decode_webrtc_instance() {
let ctx = TestContext::new().await;
let res = check_qr(&ctx.ctx, "DCWEBRTC:basicwebrtc:https://basicurl.com/$ROOM").await;
assert_eq!(res.get_state(), LotState::QrWebrtcInstance);
assert_eq!(res.get_text1().unwrap(), "basicurl.com");
assert_eq!(
res.get_text2().unwrap(),
"basicwebrtc:https://basicurl.com/$ROOM"
);
// Test it again with mixcased "dcWebRTC:" uri scheme
let res = check_qr(&ctx.ctx, "dcWebRTC:https://example.org/").await;
assert_eq!(res.get_state(), LotState::QrWebrtcInstance);
assert_eq!(res.get_text1().unwrap(), "example.org");
assert_eq!(res.get_text2().unwrap(), "https://example.org/");
}
#[async_std::test]
async fn test_decode_account_bad_scheme() {
let ctx = TestContext::new().await;
@@ -638,4 +700,34 @@ mod tests {
assert_eq!(res.get_state(), LotState::QrError);
assert!(res.get_text1().is_some());
}
#[async_std::test]
async fn test_set_config_from_qr() {
let ctx = TestContext::new().await;
assert!(ctx.ctx.get_config(Config::WebrtcInstance).await.is_none());
let res = set_config_from_qr(&ctx.ctx, "badqr:https://example.org/").await;
assert!(!res.is_ok());
assert!(ctx.ctx.get_config(Config::WebrtcInstance).await.is_none());
let res = set_config_from_qr(&ctx.ctx, "https://no.qr").await;
assert!(!res.is_ok());
assert!(ctx.ctx.get_config(Config::WebrtcInstance).await.is_none());
let res = set_config_from_qr(&ctx.ctx, "dcwebrtc:https://example.org/").await;
assert!(res.is_ok());
assert_eq!(
ctx.ctx.get_config(Config::WebrtcInstance).await.unwrap(),
"https://example.org/"
);
let res =
set_config_from_qr(&ctx.ctx, "DCWEBRTC:basicwebrtc:https://foo.bar/?$ROOM&test").await;
assert!(res.is_ok());
assert_eq!(
ctx.ctx.get_config(Config::WebrtcInstance).await.unwrap(),
"basicwebrtc:https://foo.bar/?$ROOM&test"
);
}
}