deltachat/securejoin/
qrinvite.rs

1//! Supporting code for the QR-code invite.
2//!
3//! QR-codes are decoded into a more general-purpose [`Qr`] struct normally.  This makes working
4//! with it rather hard, so here we have a wrapper type that specifically deals with Secure-Join
5//! QR-codes so that the Secure-Join code can have more guarantees when dealing with this.
6
7use anyhow::{bail, Error, Result};
8
9use crate::contact::ContactId;
10use crate::key::Fingerprint;
11use crate::qr::Qr;
12
13/// Represents the data from a QR-code scan.
14///
15/// There are methods to conveniently access fields present in both variants.
16#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
17pub enum QrInvite {
18    Contact {
19        contact_id: ContactId,
20        fingerprint: Fingerprint,
21        invitenumber: String,
22        authcode: String,
23    },
24    Group {
25        contact_id: ContactId,
26        fingerprint: Fingerprint,
27        name: String,
28        grpid: String,
29        invitenumber: String,
30        authcode: String,
31    },
32}
33
34impl QrInvite {
35    /// The contact ID of the inviter.
36    ///
37    /// The actual QR-code contains a URL-encoded email address, but upon scanning this is
38    /// translated to a contact ID.
39    pub fn contact_id(&self) -> ContactId {
40        match self {
41            Self::Contact { contact_id, .. } | Self::Group { contact_id, .. } => *contact_id,
42        }
43    }
44
45    /// The fingerprint of the inviter.
46    pub fn fingerprint(&self) -> &Fingerprint {
47        match self {
48            Self::Contact { fingerprint, .. } | Self::Group { fingerprint, .. } => fingerprint,
49        }
50    }
51
52    /// The `INVITENUMBER` of the setup-contact/secure-join protocol.
53    pub fn invitenumber(&self) -> &str {
54        match self {
55            Self::Contact { invitenumber, .. } | Self::Group { invitenumber, .. } => invitenumber,
56        }
57    }
58
59    /// The `AUTH` code of the setup-contact/secure-join protocol.
60    pub fn authcode(&self) -> &str {
61        match self {
62            Self::Contact { authcode, .. } | Self::Group { authcode, .. } => authcode,
63        }
64    }
65}
66
67impl TryFrom<Qr> for QrInvite {
68    type Error = Error;
69
70    fn try_from(qr: Qr) -> Result<Self> {
71        match qr {
72            Qr::AskVerifyContact {
73                contact_id,
74                fingerprint,
75                invitenumber,
76                authcode,
77            } => Ok(QrInvite::Contact {
78                contact_id,
79                fingerprint,
80                invitenumber,
81                authcode,
82            }),
83            Qr::AskVerifyGroup {
84                grpname,
85                grpid,
86                contact_id,
87                fingerprint,
88                invitenumber,
89                authcode,
90            } => Ok(QrInvite::Group {
91                contact_id,
92                fingerprint,
93                name: grpname,
94                grpid,
95                invitenumber,
96                authcode,
97            }),
98            _ => bail!("Unsupported QR type"),
99        }
100    }
101}
102
103impl rusqlite::types::ToSql for QrInvite {
104    fn to_sql(&self) -> rusqlite::Result<rusqlite::types::ToSqlOutput<'_>> {
105        let json = serde_json::to_string(self)
106            .map_err(|err| rusqlite::Error::ToSqlConversionFailure(Box::new(err)))?;
107        let val = rusqlite::types::Value::Text(json);
108        let out = rusqlite::types::ToSqlOutput::Owned(val);
109        Ok(out)
110    }
111}
112
113impl rusqlite::types::FromSql for QrInvite {
114    fn column_result(value: rusqlite::types::ValueRef<'_>) -> rusqlite::types::FromSqlResult<Self> {
115        String::column_result(value).and_then(|val| {
116            serde_json::from_str(&val)
117                .map_err(|err| rusqlite::types::FromSqlError::Other(Box::new(err)))
118        })
119    }
120}