Skip to main content

systemconfiguration/
network_connection.rs

1use std::{ffi::c_void, sync::{Arc, Mutex}};
2
3use crate::{
4    bridge,
5    error::Result,
6    ffi,
7    PropertyList, ReachabilityFlags,
8};
9
10pub type NetworkConnectionFlags = ReachabilityFlags;
11
12#[derive(Clone, Copy, Debug, Eq, PartialEq)]
13pub enum NetworkConnectionStatus {
14    Invalid,
15    Disconnected,
16    Connecting,
17    Connected,
18    Disconnecting,
19    Unknown(i32),
20}
21
22impl NetworkConnectionStatus {
23    pub const fn from_raw(raw: i32) -> Self {
24        match raw {
25            -1 => Self::Invalid,
26            0 => Self::Disconnected,
27            1 => Self::Connecting,
28            2 => Self::Connected,
29            3 => Self::Disconnecting,
30            other => Self::Unknown(other),
31        }
32    }
33
34    pub const fn raw_value(self) -> i32 {
35        match self {
36            Self::Invalid => -1,
37            Self::Disconnected => 0,
38            Self::Connecting => 1,
39            Self::Connected => 2,
40            Self::Disconnecting => 3,
41            Self::Unknown(raw) => raw,
42        }
43    }
44}
45
46#[derive(Clone, Copy, Debug, Eq, PartialEq)]
47pub enum NetworkConnectionPppStatus {
48    Disconnected,
49    Initializing,
50    ConnectingLink,
51    DialOnTraffic,
52    NegotiatingLink,
53    Authenticating,
54    WaitingForCallback,
55    NegotiatingNetwork,
56    Connected,
57    Terminating,
58    DisconnectingLink,
59    HoldingLinkOff,
60    Suspended,
61    WaitingForRedial,
62    Unknown(i32),
63}
64
65impl NetworkConnectionPppStatus {
66    pub const fn from_raw(raw: i32) -> Self {
67        match raw {
68            0 => Self::Disconnected,
69            1 => Self::Initializing,
70            2 => Self::ConnectingLink,
71            3 => Self::DialOnTraffic,
72            4 => Self::NegotiatingLink,
73            5 => Self::Authenticating,
74            6 => Self::WaitingForCallback,
75            7 => Self::NegotiatingNetwork,
76            8 => Self::Connected,
77            9 => Self::Terminating,
78            10 => Self::DisconnectingLink,
79            11 => Self::HoldingLinkOff,
80            12 => Self::Suspended,
81            13 => Self::WaitingForRedial,
82            other => Self::Unknown(other),
83        }
84    }
85
86    pub const fn raw_value(self) -> i32 {
87        match self {
88            Self::Disconnected => 0,
89            Self::Initializing => 1,
90            Self::ConnectingLink => 2,
91            Self::DialOnTraffic => 3,
92            Self::NegotiatingLink => 4,
93            Self::Authenticating => 5,
94            Self::WaitingForCallback => 6,
95            Self::NegotiatingNetwork => 7,
96            Self::Connected => 8,
97            Self::Terminating => 9,
98            Self::DisconnectingLink => 10,
99            Self::HoldingLinkOff => 11,
100            Self::Suspended => 12,
101            Self::WaitingForRedial => 13,
102            Self::Unknown(raw) => raw,
103        }
104    }
105}
106
107#[derive(Clone, Debug)]
108pub struct NetworkConnectionUserPreferences {
109    pub service_id: String,
110    pub user_options: Option<PropertyList>,
111}
112
113struct CallbackState {
114    callback: Box<dyn FnMut(NetworkConnectionStatus) + Send>,
115}
116
117unsafe extern "C" fn network_connection_callback(status: i32, info: *mut c_void) {
118    if info.is_null() {
119        return;
120    }
121
122    let mutex = &*info.cast::<Mutex<CallbackState>>();
123    if let Ok(mut state) = mutex.lock() {
124        (state.callback)(NetworkConnectionStatus::from_raw(status));
125    }
126}
127
128#[derive(Clone)]
129pub struct NetworkConnection {
130    raw: bridge::OwnedHandle,
131    _callback: Option<Arc<Mutex<CallbackState>>>,
132}
133
134impl std::fmt::Debug for NetworkConnection {
135    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
136        f.debug_struct("NetworkConnection").finish_non_exhaustive()
137    }
138}
139
140impl NetworkConnection {
141    pub fn type_id() -> u64 {
142        unsafe { ffi::network_connection::sc_network_connection_get_type_id() }
143    }
144
145    pub fn with_service_id(service_id: &str) -> Result<Self> {
146        Self::create(service_id, None)
147    }
148
149    pub fn with_service_id_and_callback<F>(service_id: &str, callback: F) -> Result<Self>
150    where
151        F: FnMut(NetworkConnectionStatus) + Send + 'static,
152    {
153        let state = Arc::new(Mutex::new(CallbackState {
154            callback: Box::new(callback),
155        }));
156        Self::create(service_id, Some(state))
157    }
158
159    fn create(service_id: &str, callback: Option<Arc<Mutex<CallbackState>>>) -> Result<Self> {
160        let service_id = bridge::cstring(service_id, "sc_network_connection_create_with_service_id")?;
161        let raw = unsafe {
162            ffi::network_connection::sc_network_connection_create_with_service_id(
163                service_id.as_ptr(),
164                callback.as_ref().map(|_| network_connection_callback as unsafe extern "C" fn(i32, *mut c_void)),
165                callback
166                    .as_ref()
167                    .map_or(std::ptr::null_mut(), |state| Arc::as_ptr(state).cast_mut().cast::<c_void>()),
168            )
169        };
170        let raw = bridge::owned_handle_or_last("sc_network_connection_create_with_service_id", raw)?;
171        Ok(Self {
172            raw,
173            _callback: callback,
174        })
175    }
176
177    pub fn copy_user_preferences() -> Result<NetworkConnectionUserPreferences> {
178        let service_id = bridge::take_optional_string(unsafe {
179            ffi::network_connection::sc_network_connection_copy_user_preferences_service_id()
180        })
181        .ok_or_else(|| {
182            crate::SystemConfigurationError::last("sc_network_connection_copy_user_preferences_service_id")
183        })?;
184        let user_options = unsafe {
185            bridge::OwnedHandle::from_raw(
186                ffi::network_connection::sc_network_connection_copy_user_preferences_user_options(),
187            )
188        }
189        .map(PropertyList::from_owned_handle);
190        Ok(NetworkConnectionUserPreferences {
191            service_id,
192            user_options,
193        })
194    }
195
196    pub fn service_id(&self) -> Result<Option<String>> {
197        Ok(bridge::take_optional_string(unsafe {
198            ffi::network_connection::sc_network_connection_copy_service_id(self.raw.as_ptr())
199        }))
200    }
201
202    pub fn status(&self) -> NetworkConnectionStatus {
203        NetworkConnectionStatus::from_raw(unsafe {
204            ffi::network_connection::sc_network_connection_get_status(self.raw.as_ptr())
205        })
206    }
207
208    pub fn extended_status(&self) -> Option<PropertyList> {
209        unsafe {
210            bridge::OwnedHandle::from_raw(ffi::network_connection::sc_network_connection_copy_extended_status(
211                self.raw.as_ptr(),
212            ))
213        }
214        .map(PropertyList::from_owned_handle)
215    }
216
217    pub fn statistics(&self) -> Option<PropertyList> {
218        unsafe {
219            bridge::OwnedHandle::from_raw(ffi::network_connection::sc_network_connection_copy_statistics(
220                self.raw.as_ptr(),
221            ))
222        }
223        .map(PropertyList::from_owned_handle)
224    }
225
226    pub fn user_options(&self) -> Option<PropertyList> {
227        unsafe {
228            bridge::OwnedHandle::from_raw(ffi::network_connection::sc_network_connection_copy_user_options(
229                self.raw.as_ptr(),
230            ))
231        }
232        .map(PropertyList::from_owned_handle)
233    }
234
235    pub fn start(&self, user_options: Option<&PropertyList>, linger: bool) -> Result<()> {
236        let ok = unsafe {
237            ffi::network_connection::sc_network_connection_start(
238                self.raw.as_ptr(),
239                user_options.map_or(std::ptr::null_mut(), PropertyList::as_ptr),
240                u8::from(linger),
241            )
242        };
243        bridge::bool_result("sc_network_connection_start", ok)
244    }
245
246    pub fn stop(&self, force_disconnect: bool) -> Result<()> {
247        let ok = unsafe {
248            ffi::network_connection::sc_network_connection_stop(self.raw.as_ptr(), u8::from(force_disconnect))
249        };
250        bridge::bool_result("sc_network_connection_stop", ok)
251    }
252
253    pub fn schedule_with_run_loop_current(&self) -> Result<()> {
254        let ok = unsafe {
255            ffi::network_connection::sc_network_connection_schedule_with_run_loop_current(self.raw.as_ptr())
256        };
257        bridge::bool_result("sc_network_connection_schedule_with_run_loop_current", ok)
258    }
259
260    pub fn unschedule_from_run_loop_current(&self) -> Result<()> {
261        let ok = unsafe {
262            ffi::network_connection::sc_network_connection_unschedule_from_run_loop_current(self.raw.as_ptr())
263        };
264        bridge::bool_result("sc_network_connection_unschedule_from_run_loop_current", ok)
265    }
266
267    pub fn set_dispatch_queue_global(&self) -> Result<()> {
268        let ok = unsafe {
269            ffi::network_connection::sc_network_connection_set_dispatch_queue_global(self.raw.as_ptr())
270        };
271        bridge::bool_result("sc_network_connection_set_dispatch_queue_global", ok)
272    }
273
274    pub fn clear_dispatch_queue(&self) -> Result<()> {
275        let ok = unsafe {
276            ffi::network_connection::sc_network_connection_clear_dispatch_queue(self.raw.as_ptr())
277        };
278        bridge::bool_result("sc_network_connection_clear_dispatch_queue", ok)
279    }
280}