citadel_sdk/responses.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
//! Protocol Response Helpers
//!
//! This module provides helper functions for handling responses to various protocol
//! operations in the Citadel Protocol. It simplifies the process of sending responses
//! to peer registration, connection, and group invitation requests.
//!
//! # Features
//! - Peer registration response handling
//! - Peer connection response management
//! - Group invitation response processing
//! - Automatic ticket management
//! - Connection type reversal handling
//! - Username resolution and validation
//!
//! # Example
//! ```rust
//! use citadel_sdk::prelude::*;
//! use citadel_sdk::responses;
//!
//! async fn handle_peer_request<R: Ratchet>(
//! signal: PeerSignal,
//! remote: &impl Remote<R>
//! ) -> Result<(), NetworkError> {
//! // Accept a peer registration request
//! let ticket = responses::peer_register(signal, true, remote).await?;
//!
//! Ok(())
//! }
//! ```
//!
//! # Important Notes
//! - Responses must match request tickets
//! - Connection types are automatically reversed
//! - Username resolution is handled internally
//! - Group responses require server connection
//!
//! # Related Components
//! - [`Remote`]: Network communication interface
//! - [`PeerSignal`]: Peer communication events
//! - [`NodeResult`]: Network operation results
//! - [`Ticket`]: Request/response correlation
//!
//! [`Remote`]: crate::prelude::Remote
//! [`PeerSignal`]: crate::prelude::PeerSignal
//! [`NodeResult`]: crate::prelude::NodeResult
//! [`Ticket`]: crate::prelude::Ticket
use crate::prelude::*;
/// Given the `input_signal` from the peer, this function sends a register response to the target peer
pub async fn peer_register<R: Ratchet>(
input_signal: PeerSignal,
accept: bool,
remote: &impl Remote<R>,
) -> Result<Ticket, NetworkError> {
if let PeerSignal::PostRegister {
peer_conn_type: v_conn,
inviter_username: username,
invitee_username: username_opt,
ticket_opt: ticket,
invitee_response: None,
} = input_signal
{
let this_cid = v_conn.get_original_target_cid();
let ticket = get_ticket(ticket)?;
let resp = if accept {
let username = remote
.account_manager()
.get_username_by_cid(this_cid)
.await
.map_err(|err| NetworkError::Generic(err.into_string()))?
.ok_or(NetworkError::InvalidRequest(
"Unable to find local username implied by signal",
))?;
PeerResponse::Accept(Some(username))
} else {
PeerResponse::Decline
};
// v_conn must be reversed when rebounding a signal
let signal = PeerSignal::PostRegister {
peer_conn_type: v_conn.reverse(),
inviter_username: username,
invitee_username: username_opt,
ticket_opt: Some(ticket),
invitee_response: Some(resp),
};
remote
.send_with_custom_ticket(
ticket,
NodeRequest::PeerCommand(PeerCommand {
session_cid: this_cid,
command: signal,
}),
)
.await
.map(|_| ticket)
} else {
Err(NetworkError::InternalError(
"Input signal is not a valid PostRegister",
))
}
}
/// Given the `input_signal` from the peer, this function sends a connect response to the target peer
pub async fn peer_connect<R: Ratchet>(
input_signal: PeerSignal,
accept: bool,
remote: &impl Remote<R>,
peer_session_password: Option<PreSharedKey>,
) -> Result<Ticket, NetworkError> {
if let PeerSignal::PostConnect {
peer_conn_type: v_conn,
ticket_opt: ticket,
invitee_response: None,
session_security_settings: sess_sec,
udp_mode,
session_password: None,
} = input_signal
{
let this_cid = v_conn.get_original_target_cid();
let ticket = get_ticket(ticket)?;
let resp = if accept {
// we do not need a username here, unlike in postregister
PeerResponse::Accept(None)
} else {
PeerResponse::Decline
};
let signal = NodeRequest::PeerCommand(PeerCommand {
session_cid: this_cid,
command: PeerSignal::PostConnect {
peer_conn_type: v_conn.reverse(),
ticket_opt: Some(ticket),
invitee_response: Some(resp),
session_security_settings: sess_sec,
udp_mode,
session_password: peer_session_password,
},
});
remote
.send_with_custom_ticket(ticket, signal)
.await
.map(|_| ticket)
} else {
Err(NetworkError::InternalError(
"Input signal is not a valid PostConnect",
))
}
}
/// Given a group invite signal, this function sends a response to the server
pub async fn group_invite<R: Ratchet>(
invite_signal: NodeResult<R>,
accept: bool,
remote: &impl Remote<R>,
) -> Result<Ticket, NetworkError> {
if let NodeResult::GroupEvent(GroupEvent {
session_cid: cid,
ticket,
event: GroupBroadcast::Invitation { sender: _, key },
}) = invite_signal
{
let resp = if accept {
GroupBroadcast::AcceptMembership { target: cid, key }
} else {
GroupBroadcast::DeclineMembership { target: cid, key }
};
let request = NodeRequest::GroupBroadcastCommand(GroupBroadcastCommand {
session_cid: cid,
command: resp,
});
remote
.send_with_custom_ticket(ticket, request)
.await
.map(|_| ticket)
} else {
Err(NetworkError::InternalError(
"Input signal is not a group invitation",
))
}
}
fn get_ticket(ticket: Option<Ticket>) -> Result<Ticket, NetworkError> {
ticket.ok_or(NetworkError::InvalidPacket(
"This event was improperly formed",
))
}