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::{Error, Result, bail};
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    Broadcast {
33        contact_id: ContactId,
34        fingerprint: Fingerprint,
35        name: String,
36        grpid: String,
37        invitenumber: String,
38        authcode: String,
39    },
40}
41
42impl QrInvite {
43    /// The contact ID of the inviter.
44    ///
45    /// The actual QR-code contains a URL-encoded email address, but upon scanning this is
46    /// translated to a contact ID.
47    pub fn contact_id(&self) -> ContactId {
48        match self {
49            Self::Contact { contact_id, .. }
50            | Self::Group { contact_id, .. }
51            | Self::Broadcast { contact_id, .. } => *contact_id,
52        }
53    }
54
55    /// The fingerprint of the inviter.
56    pub fn fingerprint(&self) -> &Fingerprint {
57        match self {
58            Self::Contact { fingerprint, .. }
59            | Self::Group { fingerprint, .. }
60            | Self::Broadcast { fingerprint, .. } => fingerprint,
61        }
62    }
63
64    /// The `INVITENUMBER` of the setup-contact/secure-join protocol.
65    pub fn invitenumber(&self) -> &str {
66        match self {
67            Self::Contact { invitenumber, .. }
68            | Self::Group { invitenumber, .. }
69            | Self::Broadcast { invitenumber, .. } => invitenumber,
70        }
71    }
72
73    /// The `AUTH` code of the setup-contact/secure-join protocol.
74    pub fn authcode(&self) -> &str {
75        match self {
76            Self::Contact { authcode, .. }
77            | Self::Group { authcode, .. }
78            | Self::Broadcast { authcode, .. } => authcode,
79        }
80    }
81}
82
83impl TryFrom<Qr> for QrInvite {
84    type Error = Error;
85
86    fn try_from(qr: Qr) -> Result<Self> {
87        match qr {
88            Qr::AskVerifyContact {
89                contact_id,
90                fingerprint,
91                invitenumber,
92                authcode,
93            } => Ok(QrInvite::Contact {
94                contact_id,
95                fingerprint,
96                invitenumber,
97                authcode,
98            }),
99            Qr::AskVerifyGroup {
100                grpname,
101                grpid,
102                contact_id,
103                fingerprint,
104                invitenumber,
105                authcode,
106            } => Ok(QrInvite::Group {
107                contact_id,
108                fingerprint,
109                name: grpname,
110                grpid,
111                invitenumber,
112                authcode,
113            }),
114            Qr::AskJoinBroadcast {
115                name,
116                grpid,
117                contact_id,
118                fingerprint,
119                authcode,
120                invitenumber,
121            } => Ok(QrInvite::Broadcast {
122                name,
123                grpid,
124                contact_id,
125                fingerprint,
126                authcode,
127                invitenumber,
128            }),
129            _ => bail!("Unsupported QR type"),
130        }
131    }
132}
133
134impl rusqlite::types::ToSql for QrInvite {
135    fn to_sql(&self) -> rusqlite::Result<rusqlite::types::ToSqlOutput<'_>> {
136        let json = serde_json::to_string(self)
137            .map_err(|err| rusqlite::Error::ToSqlConversionFailure(Box::new(err)))?;
138        let val = rusqlite::types::Value::Text(json);
139        let out = rusqlite::types::ToSqlOutput::Owned(val);
140        Ok(out)
141    }
142}
143
144impl rusqlite::types::FromSql for QrInvite {
145    fn column_result(value: rusqlite::types::ValueRef<'_>) -> rusqlite::types::FromSqlResult<Self> {
146        String::column_result(value).and_then(|val| {
147            serde_json::from_str(&val)
148                .map_err(|err| rusqlite::types::FromSqlError::Other(Box::new(err)))
149        })
150    }
151}