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