api!: get rid of Qr::FprWithoutAddr variant

We don't look at the address in OPENPGP4FPR QR codes anymore.
If we cannot find a contact by fingerprint,
then we return Qr::FprMismatch without trying to look up
the contact by address.

BREAKING CHANGE: removed DC_QR_FPR_WITHOUT_ADDR constant from CFFI
BREAKING CHANGE: removed FprWithoutAddr variant from JSON-RPC QrObject returned by check_qr
This commit is contained in:
link2xt
2026-09-16 00:23:32 +00:00
parent fae6a63023
commit 527570be8e
5 changed files with 38 additions and 60 deletions

View File

@@ -2456,7 +2456,6 @@ void dc_stop_ongoing_process (dc_context_t* context);
#define DC_QR_ASK_VERIFYBROADCAST 204 // text1=broadcast name
#define DC_QR_FPR_OK 210 // id=contact
#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_BACKUP2 252
#define DC_QR_BACKUP_TOO_NEW 255
@@ -2498,11 +2497,8 @@ void dc_stop_ongoing_process (dc_context_t* context);
* if so, call dc_create_chat_by_contact_id().
*
* - DC_QR_FPR_MISMATCH with dc_lot_t::id=Contact ID:
* scanned fingerprint does not match last seen fingerprint.
*
* - DC_QR_FPR_WITHOUT_ADDR with dc_lot_t::text1=Formatted fingerprint
* the scanned QR code contains a fingerprint but no e-mail address;
* suggest the user to establish an encrypted connection first.
* scanned fingerprint does not match any contact
* or the key for this contact is not available.
*
* - DC_QR_ACCOUNT dc_lot_t::text1=domain:
* ask the user if they want to create an account on the given domain,

View File

@@ -48,7 +48,6 @@ impl Lot {
Qr::AskJoinBroadcast { name, .. } => Some(Cow::Borrowed(name)),
Qr::FprOk { .. } => None,
Qr::FprMismatch { .. } => None,
Qr::FprWithoutAddr { fingerprint, .. } => Some(Cow::Borrowed(fingerprint)),
Qr::Account { domain } => Some(Cow::Borrowed(domain)),
Qr::Backup2 { .. } => None,
Qr::BackupTooNew { .. } => None,
@@ -104,7 +103,6 @@ impl Lot {
Qr::AskJoinBroadcast { .. } => LotState::QrAskJoinBroadcast,
Qr::FprOk { .. } => LotState::QrFprOk,
Qr::FprMismatch { .. } => LotState::QrFprMismatch,
Qr::FprWithoutAddr { .. } => LotState::QrFprWithoutAddr,
Qr::Account { .. } => LotState::QrAccount,
Qr::Backup2 { .. } => LotState::QrBackup2,
Qr::BackupTooNew { .. } => LotState::QrBackupTooNew,
@@ -133,7 +131,6 @@ impl Lot {
Qr::AskJoinBroadcast { .. } => Default::default(),
Qr::FprOk { contact_id } => contact_id.to_u32(),
Qr::FprMismatch { contact_id } => contact_id.unwrap_or_default().to_u32(),
Qr::FprWithoutAddr { .. } => Default::default(),
Qr::Account { .. } => Default::default(),
Qr::Backup2 { .. } => Default::default(),
Qr::BackupTooNew { .. } => Default::default(),
@@ -184,9 +181,6 @@ pub enum LotState {
/// id=contact
QrFprMismatch = 220,
/// text1=formatted fingerprint
QrFprWithoutAddr = 230,
/// text1=domain
QrAccount = 250,

View File

@@ -73,11 +73,6 @@ pub enum QrObject {
/// Contact ID.
contact_id: Option<u32>,
},
/// The scanned QR code contains a fingerprint but no e-mail address.
FprWithoutAddr {
/// Key fingerprint.
fingerprint: String,
},
/// Ask the user if they want to create an account on the given domain.
Account {
/// Server domain name.
@@ -300,7 +295,6 @@ impl From<Qr> for QrObject {
let contact_id = contact_id.map(|contact_id| contact_id.to_u32());
QrObject::FprMismatch { contact_id }
}
Qr::FprWithoutAddr { fingerprint } => QrObject::FprWithoutAddr { fingerprint },
Qr::Account { domain } => QrObject::Account { domain },
Qr::Backup2 {
ref node_addr,

View File

@@ -133,18 +133,15 @@ pub enum Qr {
contact_id: ContactId,
},
/// Scanned fingerprint does not match the last seen fingerprint.
/// Scanned fingerprint does not match any contact
/// or the key for this contact is not available.
FprMismatch {
/// Contact ID.
/// Contact ID if key-contact exists, but we have no key.
///
/// `None` if there is no contact corresponding to the scanned fingerprint.
contact_id: Option<ContactId>,
},
/// The scanned QR code contains a fingerprint but no e-mail address.
FprWithoutAddr {
/// Key fingerprint.
fingerprint: String,
},
/// Ask the user if they want to create an account on the given domain.
Account {
/// Server domain name.
@@ -651,24 +648,28 @@ async fn decode_openpgp(context: &Context, qr: &str) -> Result<Qr> {
is_v3,
})
}
} else if let Some(addr) = addrs.first() {
let fingerprint = fingerprint.hex();
let (contact_id, _) =
Contact::add_or_lookup_ext(context, "", addr, &fingerprint, Origin::UnhandledQrScan)
.await?;
let contact = Contact::get_by_id(context, contact_id).await?;
if contact.public_key(context).await?.is_some() {
Ok(Qr::FprOk { contact_id })
} else {
Ok(Qr::FprMismatch {
contact_id: Some(contact_id),
})
}
} else {
Ok(Qr::FprWithoutAddr {
fingerprint: fingerprint.human_readable(),
})
let fingerprint = fingerprint.hex();
let contact_id: Option<ContactId> = context
.sql
.query_get_value(
"SELECT id FROM contacts WHERE fingerprint=?",
(fingerprint,),
)
.await?;
if let Some(contact_id) = contact_id {
let contact = Contact::get_by_id(context, contact_id).await?;
if contact.public_key(context).await?.is_some() {
Ok(Qr::FprOk { contact_id })
} else {
Ok(Qr::FprMismatch {
contact_id: Some(contact_id),
})
}
} else {
Ok(Qr::FprMismatch { contact_id: None })
}
}
}

View File

@@ -373,16 +373,15 @@ async fn test_decode_openpgp_fingerprint() -> Result<()> {
let alice_contact = bob.add_or_lookup_contact(alice).await;
let alice_contact_id = alice_contact.id;
// OPENPGP4FPR may have an address,
// but it is not used anymore since key contacts are introduced.
// We lookup the contact only by fingerprint and ignore the address.
let qr = check_qr(
bob,
"OPENPGP4FPR:1234567890123456789012345678901234567890#a=alice@example.org",
)
.await?;
if let Qr::FprMismatch { contact_id, .. } = qr {
assert_ne!(contact_id.unwrap(), alice_contact_id);
} else {
bail!("Wrong QR code type");
}
assert_eq!(qr, Qr::FprMismatch { contact_id: None });
let qr = check_qr(
bob,
@@ -410,6 +409,10 @@ async fn test_decode_openpgp_fingerprint() -> Result<()> {
Ok(())
}
/// Tests OPENPGP4FPR QR codes without an email address.
///
/// Email address in OPENPGP4FPR QR codes was an extension
/// used before switch to identifying contacts by fingerprint.
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_decode_openpgp_without_addr() -> Result<()> {
let ctx = TestContext::new().await;
@@ -419,12 +422,7 @@ async fn test_decode_openpgp_without_addr() -> Result<()> {
"OPENPGP4FPR:1234567890123456789012345678901234567890",
)
.await?;
assert_eq!(
qr,
Qr::FprWithoutAddr {
fingerprint: "1234 5678 9012 3456 7890\n1234 5678 9012 3456 7890".to_string()
}
);
assert_eq!(qr, Qr::FprMismatch { contact_id: None });
// Test it again with lowercased "openpgp4fpr:" uri scheme
@@ -433,12 +431,7 @@ async fn test_decode_openpgp_without_addr() -> Result<()> {
"openpgp4fpr:1234567890123456789012345678901234567890",
)
.await?;
assert_eq!(
qr,
Qr::FprWithoutAddr {
fingerprint: "1234 5678 9012 3456 7890\n1234 5678 9012 3456 7890".to_string()
}
);
assert_eq!(qr, Qr::FprMismatch { contact_id: None });
let res = check_qr(&ctx.ctx, "OPENPGP4FPR:12345678901234567890").await;
assert!(res.is_err());