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
//! A list of helpers making the response phase simple and intuitive
//!
//! Generally, when making a response, the response must be sent outbound through
//! the [NodeRemote](crate::prelude::NodeRemote) with a custom ticket equivalent to the ticket receieved by
//! the external request in order for the peer to listen for a response. Additionally,
//! [PeerConnectionType](crate::prelude::PeerConnectionType) data must be `.reverse()`'d. Finally, some response types
//! require the manual input of usernames, and as such, this helper library enforces
//! all these requirements
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(
    input_signal: PeerSignal,
    accept: bool,
    remote: &impl Remote,
) -> 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 {
                    implicated_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(
    input_signal: PeerSignal,
    accept: bool,
    remote: &impl Remote,
    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 {
            implicated_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(
    invite_signal: NodeResult,
    accept: bool,
    remote: &impl Remote,
) -> Result<Ticket, NetworkError> {
    if let NodeResult::GroupEvent(GroupEvent {
        implicated_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 {
            implicated_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",
    ))
}