Skip to main content

systemconfiguration/
network_connection.rs

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