use futures_util::{Stream, StreamExt};
use matrix_sdk_base::crypto::{
CancelInfo, DeviceData, VerificationRequest as BaseVerificationRequest,
};
use ruma::{RoomId, events::key::verification::VerificationMethod};
#[cfg(feature = "qrcode")]
use super::{QrVerification, QrVerificationData};
use super::{SasVerification, Verification};
use crate::{Client, Result};
#[derive(Debug, Clone)]
pub struct VerificationRequest {
pub(crate) inner: BaseVerificationRequest,
pub(crate) client: Client,
}
#[derive(Debug, Clone)]
pub enum VerificationRequestState {
Created {
our_methods: Vec<VerificationMethod>,
},
Requested {
their_methods: Vec<VerificationMethod>,
other_device_data: DeviceData,
},
Ready {
their_methods: Vec<VerificationMethod>,
our_methods: Vec<VerificationMethod>,
other_device_data: DeviceData,
},
Transitioned {
verification: Verification,
},
Done,
Cancelled(CancelInfo),
}
impl VerificationRequest {
pub fn is_done(&self) -> bool {
self.inner.is_done()
}
pub fn is_cancelled(&self) -> bool {
self.inner.is_cancelled()
}
pub fn flow_id(&self) -> &str {
self.inner.flow_id().as_str()
}
pub fn cancel_info(&self) -> Option<CancelInfo> {
self.inner.cancel_info()
}
pub fn own_user_id(&self) -> &ruma::UserId {
self.inner.own_user_id()
}
pub fn is_passive(&self) -> bool {
self.inner.is_passive()
}
pub fn is_ready(&self) -> bool {
self.inner.is_ready()
}
pub fn we_started(&self) -> bool {
self.inner.we_started()
}
pub fn other_user_id(&self) -> &ruma::UserId {
self.inner.other_user()
}
pub fn is_self_verification(&self) -> bool {
self.inner.is_self_verification()
}
pub fn their_supported_methods(&self) -> Option<Vec<VerificationMethod>> {
self.inner.their_supported_methods()
}
pub async fn accept(&self) -> Result<()> {
if let Some(request) = self.inner.accept() {
self.client.send_verification_request(request).await?;
}
Ok(())
}
pub async fn accept_with_methods(&self, methods: Vec<VerificationMethod>) -> Result<()> {
if let Some(request) = self.inner.accept_with_methods(methods) {
self.client.send_verification_request(request).await?;
}
Ok(())
}
#[cfg(feature = "qrcode")]
pub async fn generate_qr_code(&self) -> Result<Option<QrVerification>> {
Ok(self
.inner
.generate_qr_code()
.await?
.map(|qr| QrVerification { inner: Box::new(qr), client: self.client.clone() }))
}
#[cfg(feature = "qrcode")]
pub async fn scan_qr_code(&self, data: QrVerificationData) -> Result<Option<QrVerification>> {
let Some(qr) = self.inner.scan_qr_code(data).await? else { return Ok(None) };
if let Some(request) = qr.reciprocate() {
self.client.send_verification_request(request).await?;
}
Ok(Some(QrVerification { inner: Box::new(qr), client: self.client.clone() }))
}
pub async fn start_sas(&self) -> Result<Option<SasVerification>> {
let Some((sas, request)) = self.inner.start_sas().await? else { return Ok(None) };
self.client.send_verification_request(request).await?;
Ok(Some(SasVerification { inner: Box::new(sas), client: self.client.clone() }))
}
pub async fn cancel(&self) -> Result<()> {
if let Some(request) = self.inner.cancel() {
self.client.send_verification_request(request).await?;
}
Ok(())
}
fn convert_state(
client: Client,
state: matrix_sdk_base::crypto::VerificationRequestState,
) -> VerificationRequestState {
use matrix_sdk_base::crypto::VerificationRequestState::*;
match state {
Created { our_methods } => VerificationRequestState::Created { our_methods },
Requested { their_methods, other_device_data } => {
VerificationRequestState::Requested { their_methods, other_device_data }
}
Ready { their_methods, our_methods, other_device_data } => {
VerificationRequestState::Ready { their_methods, our_methods, other_device_data }
}
Transitioned { verification, .. } => VerificationRequestState::Transitioned {
verification: match verification {
matrix_sdk_base::crypto::Verification::SasV1(sas) => {
Verification::SasV1(SasVerification { inner: sas, client })
}
#[cfg(feature = "qrcode")]
matrix_sdk_base::crypto::Verification::QrV1(qr) => {
Verification::QrV1(QrVerification { inner: qr, client })
}
_ => unreachable!("We only support QR code and SAS verification"),
},
},
Done => VerificationRequestState::Done,
Cancelled(c) => VerificationRequestState::Cancelled(c),
}
}
pub fn changes(&self) -> impl Stream<Item = VerificationRequestState> + use<> {
let client = self.client.to_owned();
self.inner.changes().map(move |s| Self::convert_state(client.to_owned(), s))
}
pub fn state(&self) -> VerificationRequestState {
Self::convert_state(self.client.to_owned(), self.inner.state())
}
pub fn room_id(&self) -> Option<&RoomId> {
self.inner.room_id()
}
}