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}