summaryrefslogtreecommitdiffhomepage
path: root/mullvad-daemon/src/device/api.rs
blob: 6e00b669ca3fec9b9f4f615ab2766e6879275c10 (plain)
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
use std::pin::Pin;

use futures::{future::FusedFuture, Future};
use mullvad_types::{device::Device, wireguard::WireguardData};

use super::{Error, PrivateAccountAndDevice, ResponseTx};

pub(crate) struct CurrentApiCall {
    current_call: Option<Call>,
}

impl CurrentApiCall {
    pub fn new() -> Self {
        Self { current_call: None }
    }

    pub fn clear(&mut self) {
        self.current_call = None;
    }

    pub fn set_login(&mut self, login: ApiCall<PrivateAccountAndDevice>, tx: ResponseTx<()>) {
        self.current_call = Some(Call::Login(login, Some(tx)));
    }

    pub fn set_oneshot_rotation(&mut self, rotation: ApiCall<WireguardData>) {
        self.current_call = Some(Call::OneshotKeyRotation(rotation));
    }

    pub fn set_timed_rotation(&mut self, rotation: ApiCall<WireguardData>) {
        self.current_call = Some(Call::TimerKeyRotation(rotation));
    }

    pub fn set_validation(&mut self, validation: ApiCall<Device>) {
        self.current_call = Some(Call::Validation(validation));
    }

    pub fn is_validating(&self) -> bool {
        matches!(
            &self.current_call,
            Some(Call::Validation(_)) | Some(Call::OneshotKeyRotation(_))
        )
    }

    pub fn is_running_timed_totation(&self) -> bool {
        matches!(&self.current_call, Some(Call::TimerKeyRotation(_)))
    }

    pub fn is_idle(&self) -> bool {
        self.current_call.is_none()
    }

    pub fn is_logging_in(&self) -> bool {
        use Call::*;
        matches!(&self.current_call, Some(Login(..)))
    }
}

impl Future for CurrentApiCall {
    type Output = ApiResult;

    fn poll(
        mut self: Pin<&mut Self>,
        cx: &mut std::task::Context<'_>,
    ) -> std::task::Poll<Self::Output> {
        match self.current_call.as_mut() {
            Some(call) => {
                let result = Pin::new(call).poll(cx);
                if result.is_ready() {
                    self.current_call = None;
                }
                result
            }
            None => panic!("Polled an unfinished future"),
        }
    }
}

impl FusedFuture for CurrentApiCall {
    fn is_terminated(&self) -> bool {
        self.current_call.is_none()
    }
}

type ApiCall<T> = Pin<Box<dyn Future<Output = Result<T, Error>> + Send>>;

enum Call {
    Login(ApiCall<PrivateAccountAndDevice>, Option<ResponseTx<()>>),
    TimerKeyRotation(ApiCall<WireguardData>),
    OneshotKeyRotation(ApiCall<WireguardData>),
    Validation(ApiCall<Device>),
}

impl futures::Future for Call {
    type Output = ApiResult;

    fn poll(
        mut self: std::pin::Pin<&mut Self>,
        cx: &mut std::task::Context<'_>,
    ) -> std::task::Poll<Self::Output> {
        use Call::*;
        match &mut *self {
            Login(call, tx) => {
                if let std::task::Poll::Ready(response) = Pin::new(call).poll(cx) {
                    std::task::Poll::Ready(ApiResult::Login(response, tx.take().unwrap()))
                } else {
                    std::task::Poll::Pending
                }
            }
            TimerKeyRotation(call) | OneshotKeyRotation(call) => {
                Pin::new(call).poll(cx).map(ApiResult::Rotation)
            }
            Validation(call) => Pin::new(call).poll(cx).map(ApiResult::Validation),
        }
    }
}

pub(crate) enum ApiResult {
    Login(Result<PrivateAccountAndDevice, Error>, ResponseTx<()>),
    Rotation(Result<WireguardData, Error>),
    Validation(Result<Device, Error>),
}