Skip to main content

matrix_sdk/authentication/oauth/qrcode/rendezvous_channel/
mod.rs

1// Copyright 2024 The Matrix.org Foundation C.I.C.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use tracing::instrument;
16use url::Url;
17
18use crate::{
19    HttpError, authentication::oauth::qrcode::SecureChannelError, http_client::HttpClient,
20};
21
22mod msc_4108;
23
24/// The result of the [`RendezvousChannel::create_inbound()`] method.
25pub(super) struct InboundChannelCreationResult {
26    /// The connected [`RendezvousChannel`].
27    pub channel: RendezvousChannel,
28    /// The initial message we received when we connected to the
29    /// [`RendezvousChannel`].
30    ///
31    /// This is currently unused, but left in for completeness sake.
32    #[allow(dead_code)]
33    pub initial_message: Vec<u8>,
34}
35
36#[derive(Debug, PartialEq, Eq)]
37pub(super) enum RendezvousInfo<'a> {
38    Msc4108 { rendezvous_url: &'a Url },
39}
40
41pub(super) enum RendezvousChannel {
42    Msc4108(msc_4108::Channel),
43}
44
45impl RendezvousChannel {
46    /// Create a new outbound [`RendezvousChannel`].
47    ///
48    /// By outbound we mean that we're going to tell the Matrix server to create
49    /// a new rendezvous session. We're going to send an initial empty message
50    /// through the channel.
51    pub(super) async fn create_outbound(
52        client: HttpClient,
53        rendezvous_server: &Url,
54    ) -> Result<Self, HttpError> {
55        Ok(Self::Msc4108(msc_4108::Channel::create_outbound(client, rendezvous_server).await?))
56    }
57
58    /// Create a new inbound [`RendezvousChannel`].
59    ///
60    /// By inbound we mean that we're going to attempt to read an initial
61    /// message from the rendezvous session on the given [`rendezvous_url`].
62    pub(super) async fn create_inbound(
63        client: HttpClient,
64        rendezvous_url: &Url,
65    ) -> Result<InboundChannelCreationResult, HttpError> {
66        let msc_4108::InboundChannelCreationResult { channel, initial_message } =
67            msc_4108::Channel::create_inbound(client, rendezvous_url).await?;
68
69        Ok(InboundChannelCreationResult { channel: Self::Msc4108(channel), initial_message })
70    }
71
72    /// Get MSC-specific information about the rendezvous session we're using to
73    /// exchange messages through the channel.
74    pub(super) fn rendezvous_info(&self) -> RendezvousInfo<'_> {
75        match self {
76            RendezvousChannel::Msc4108(channel) => {
77                RendezvousInfo::Msc4108 { rendezvous_url: channel.rendezvous_url() }
78            }
79        }
80    }
81
82    /// Send the given `message` through the [`RendezvousChannel`] to the other
83    /// device.
84    ///
85    /// The message must be of the `text/plain` content type.
86    #[instrument(skip_all)]
87    pub(super) async fn send(&mut self, message: String) -> Result<(), HttpError> {
88        match self {
89            RendezvousChannel::Msc4108(channel) => channel.send(message.into_bytes()).await,
90        }
91    }
92
93    /// Attempt to receive a message from the [`RendezvousChannel`] from the
94    /// other device.
95    ///
96    /// The content should be of the `text/plain` content type but the parsing
97    /// and verification of this fact is left up to the caller.
98    ///
99    /// This method will wait in a loop for the channel to give us a new
100    /// message.
101    pub(super) async fn receive(&mut self) -> Result<String, SecureChannelError> {
102        let message = match self {
103            RendezvousChannel::Msc4108(channel) => channel.receive().await?,
104        };
105
106        Ok(String::from_utf8(message).map_err(|e| e.utf8_error())?)
107    }
108}