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