@@ -13,21 +13,61 @@ @interface CWInterface()
1313@property (readonly ) struct Apple80211* device;
1414@end
1515
16- static NSDictionary * getWifiInfoByApple80211 (CWInterface* inf)
16+ inline static NSDictionary * getWifiInfoByApple80211 (CWInterface* inf)
1717{
18- if (!Apple80211GetInfoCopy) return NULL ;
18+ if (!inf. device || ! Apple80211GetInfoCopy) return NULL ;
1919 CFDictionaryRef result = NULL ;
2020 if (Apple80211GetInfoCopy (inf.device , &result) < 0 ) return NULL ;
2121 return CFBridgingRelease (result);
2222}
2323
24- static const char * detectWifiByCoreWlan (FFlist* result)
24+ static NSDictionary * getWifiInfoBySystemProfiler (NSString * ifName)
25+ {
26+ // Warning: costs about 2s on my machine
27+ static NSArray * spData;
28+ static bool inited;
29+ if (!inited)
30+ {
31+ inited = true ;
32+ FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate ();
33+ if (ffProcessAppendStdOut (&buffer, (char * const []) {
34+ " system_profiler" ,
35+ " SPAirPortDataType" ,
36+ " -xml" ,
37+ " -detailLevel" ,
38+ " basic" ,
39+ NULL
40+ }) != NULL )
41+ return nil ;
42+
43+ spData = [NSPropertyListSerialization propertyListWithData: [NSData dataWithBytes: buffer.chars length: buffer.length]
44+ options: NSPropertyListImmutable
45+ format: nil
46+ error: nil ];
47+ }
48+
49+ if (spData)
50+ {
51+ for (NSDictionary * data in spData[0 ][@" _items" ])
52+ {
53+ for (NSDictionary * inf in data[@" spairport_airport_interfaces" ])
54+ {
55+ if ([ifName isEqualToString: inf[@" _name" ]])
56+ return inf[@" spairport_current_network_information" ];
57+ }
58+ }
59+ }
60+
61+ return NULL ;
62+ }
63+
64+ const char * ffDetectWifi (FFlist* result)
2565{
2666 NSArray <CWInterface*>* interfaces = CWWiFiClient.sharedWiFiClient .interfaces ;
27- if (!interfaces)
67+ if (!interfaces)
2868 return " CWWiFiClient.sharedWiFiClient.interfaces is nil" ;
2969
30- for (CWInterface* inf in interfaces)
70+ for (CWInterface* inf in interfaces)
3171 {
3272 FFWifiResult* item = (FFWifiResult*)ffListAdd (result);
3373 ffStrbufInit (&item->inf .description );
@@ -47,22 +87,25 @@ @interface CWInterface()
4787 continue ;
4888
4989 ffStrbufSetStatic (&item->conn .status , inf.interfaceMode != kCWInterfaceModeNone ? " Active" : " Inactive" );
50- if (! inf.serviceActive )
90+ if (inf.interfaceMode == kCWInterfaceModeNone )
5191 continue ;
5292
53- NSDictionary * apple = NULL ;
93+ NSDictionary * apple = nil ; // For getWifiInfoByApple80211
94+ NSDictionary * sp = nil ; // For getWifiInfoBySystemProfiler
5495
5596 if (inf.ssid ) // https://developer.apple.com/forums/thread/732431
5697 ffStrbufAppendS (&item->conn .ssid , inf.ssid .UTF8String );
5798 else if (apple || (apple = getWifiInfoByApple80211 (inf)))
58- ffStrbufAppendS (&item->conn .ssid , [[apple valueForKey: @" SSID_STR" ] UTF8String ]);
99+ ffStrbufAppendS (&item->conn .ssid , [apple[@" SSID_STR" ] UTF8String ]);
100+ else if (sp || (sp = getWifiInfoBySystemProfiler (inf.interfaceName )))
101+ ffStrbufAppendS (&item->conn .ssid , [sp[@" _name" ] UTF8String ]);
59102 else
60103 ffStrbufSetStatic (&item->conn .ssid , " <unknown ssid>" ); // https://developer.apple.com/forums/thread/732431
61104
62105 if (inf.bssid )
63106 ffStrbufAppendS (&item->conn .bssid , inf.bssid .UTF8String );
64107 else if (apple || (apple = getWifiInfoByApple80211 (inf)))
65- ffStrbufAppendS (&item->conn .bssid , [[ apple valueForKey: @" BSSID" ] UTF8String ]);
108+ ffStrbufAppendS (&item->conn .bssid , [apple[ @" BSSID" ] UTF8String ]);
66109
67110 switch (inf.activePHYMode )
68111 {
@@ -91,7 +134,24 @@ @interface CWInterface()
91134 ffStrbufSetStatic (&item->conn .protocol , " 802.11be (Wi-Fi 7)" );
92135 break ;
93136 default :
94- ffStrbufAppendF (&item->conn .protocol , " Unknown (%ld )" , inf.activePHYMode );
137+ if (inf.activePHYMode < 64 )
138+ ffStrbufAppendF (&item->conn .protocol , " Unknown (%ld )" , inf.activePHYMode );
139+ else if (sp || (sp = getWifiInfoBySystemProfiler (inf.interfaceName )))
140+ {
141+ ffStrbufSetS (&item->conn .protocol , [sp[@" spairport_network_phymode" ] UTF8String ]);
142+ if (ffStrbufStartsWithS (&item->conn .protocol , " 802.11" ))
143+ {
144+ const char * subProtocol = item->conn .protocol .chars + strlen (" 802.11" );
145+ if (ffStrEquals (subProtocol, " be" ))
146+ ffStrbufSetStatic (&item->conn .protocol , " 802.11be (Wi-Fi 7)" );
147+ else if (ffStrEquals (subProtocol, " ax" ))
148+ ffStrbufSetStatic (&item->conn .protocol , " 802.11ax (Wi-Fi 6)" );
149+ else if (ffStrEquals (subProtocol, " ac" ))
150+ ffStrbufSetStatic (&item->conn .protocol , " 802.11ac (Wi-Fi 5)" );
151+ else if (ffStrEquals (subProtocol, " n" ))
152+ ffStrbufSetStatic (&item->conn .protocol , " 802.11n (Wi-Fi 4)" );
153+ }
154+ }
95155 break ;
96156 }
97157 item->conn .signalQuality = (double ) (inf.rssiValue >= -50 ? 100 : inf.rssiValue <= -100 ? 0 : (inf.rssiValue + 100 ) * 2 );
@@ -151,17 +211,17 @@ @interface CWInterface()
151211 // Sonoma...
152212 if (apple || (apple = getWifiInfoByApple80211 (inf)))
153213 {
154- NSDictionary * authType = [ apple valueForKey: @" AUTH_TYPE" ];
214+ NSDictionary * authType = apple[ @" AUTH_TYPE" ];
155215 if (authType)
156216 {
157217 // AUTH_LOWER seems useless. `airport` verifies if its value is between 1 and 3, and prints `unknown` if not
158218
159- NSNumber * authUpper = [ authType valueForKey: @" AUTH_UPPER" ];
219+ NSNumber * authUpper = authType[ @" AUTH_UPPER" ];
160220 if (!authUpper)
161221 ffStrbufSetStatic (&item->conn .security , " Insecure" );
162222 else
163223 {
164- int authUpperValue = [ authUpper intValue ] ;
224+ int authUpperValue = authUpper. intValue ;
165225 switch (authUpperValue)
166226 {
167227 case 1 :
@@ -204,6 +264,26 @@ @interface CWInterface()
204264 }
205265 }
206266 }
267+ else if (sp || (sp = getWifiInfoBySystemProfiler (inf.interfaceName )))
268+ {
269+ ffStrbufSetS (&item->conn .security , [sp[@" spairport_security_mode" ] UTF8String ]);
270+ ffStrbufSubstrAfterFirstS (&item->conn .security , " _mode_" );
271+ if (ffStrbufEqualS (&item->conn .security , " none" ))
272+ ffStrbufSetStatic (&item->conn .security , " Insecure" );
273+ else
274+ {
275+ ffStrbufReplaceAllC (&item->conn .security , ' _' , ' ' );
276+ if (ffStrbufStartsWithS (&item->conn .security , " wpa" ))
277+ {
278+ item->conn .security .chars [0 ] = ' W' ;
279+ item->conn .security .chars [1 ] = ' P' ;
280+ item->conn .security .chars [2 ] = ' A' ;
281+ char * sub = strchr (item->conn .security .chars , ' ' );
282+ if (sub && sub[1 ])
283+ sub[1 ] = (char ) toupper (sub[1 ]);
284+ }
285+ }
286+ }
207287 break ;
208288 default :
209289 ffStrbufAppendF (&item->conn .security , " Unknown (%ld )" , inf.security );
@@ -212,104 +292,3 @@ @interface CWInterface()
212292 }
213293 return NULL ;
214294}
215-
216- static const char * detectWifiBySystemProfiler (FFlist* result)
217- {
218- // Warning: costs about 2s on my machine
219-
220- FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate ();
221- if (ffProcessAppendStdOut (&buffer, (char * const []) {
222- " system_profiler" ,
223- " SPAirPortDataType" ,
224- " -xml" ,
225- " -detailLevel" ,
226- " basic" ,
227- NULL
228- }) != NULL )
229- return " Starting `system_profiler SPAirPortDataType -xml -detailLevel basic` failed" ;
230-
231- NSArray * arr = [NSPropertyListSerialization propertyListWithData: [NSData dataWithBytes: buffer.chars length: buffer.length]
232- options: NSPropertyListImmutable
233- format: nil
234- error: nil ];
235- if (!arr || !arr.count )
236- return " system_profiler SPAirPortDataType returned an empty array" ;
237-
238- for (NSDictionary * data in arr[0 ][@" _items" ])
239- {
240- for (NSDictionary * inf in data[@" spairport_airport_interfaces" ])
241- {
242- if (![inf[@" spairport_wireless_card_type" ] isEqualToString: @" spairport_wireless_card_type_wifi (0x14E4, 0x4387)" ]) continue ;
243-
244- FFWifiResult* item = (FFWifiResult*)ffListAdd (result);
245- ffStrbufInitS (&item->inf .description , [inf[@" _name" ] UTF8String ]);
246- ffStrbufInit (&item->inf .status );
247- ffStrbufInit (&item->conn .status );
248- ffStrbufInit (&item->conn .ssid );
249- ffStrbufInit (&item->conn .bssid );
250- ffStrbufInit (&item->conn .protocol );
251- ffStrbufInit (&item->conn .security );
252- item->conn .signalQuality = 0.0 /0.0 ;
253- item->conn .rxRate = 0.0 /0.0 ;
254- item->conn .txRate = 0.0 /0.0 ;
255-
256- NSString * status = inf[@" spairport_status_information" ]; // spairport_status_connected spairport_status_disassociated spairport_status_off
257-
258- ffStrbufSetStatic (&item->inf .status , [status isEqualToString: @" spairport_status_off" ] ? " Power Off" : " Power On" );
259-
260- NSDictionary * station = inf[@" spairport_current_network_information" ];
261- if (!station) continue ;
262-
263- ffStrbufSetS (&item->conn .status , status.UTF8String + strlen (" spairport_status_" ));
264- item->conn .status .chars [0 ] = (char ) toupper (item->conn .status .chars [0 ]);
265-
266- ffStrbufSetS (&item->conn .ssid , [station[@" _name" ] UTF8String ]);
267-
268- ffStrbufSetS (&item->conn .protocol , [station[@" spairport_network_phymode" ] UTF8String ]);
269- if (ffStrbufStartsWithS (&item->conn .protocol , " 802.11" ))
270- {
271- const char * subProtocol = item->conn .protocol .chars + strlen (" 802.11" );
272- if (ffStrEquals (subProtocol, " be" ))
273- ffStrbufSetStatic (&item->conn .protocol , " 802.11be (Wi-Fi 7)" );
274- else if (ffStrEquals (subProtocol, " ax" ))
275- ffStrbufSetStatic (&item->conn .protocol , " 802.11ax (Wi-Fi 6)" );
276- else if (ffStrEquals (subProtocol, " ac" ))
277- ffStrbufSetStatic (&item->conn .protocol , " 802.11ac (Wi-Fi 5)" );
278- else if (ffStrEquals (subProtocol, " n" ))
279- ffStrbufSetStatic (&item->conn .protocol , " 802.11n (Wi-Fi 4)" );
280- }
281-
282- ffStrbufSetS (&item->conn .security , [station[@" spairport_security_mode" ] UTF8String ]);
283- ffStrbufSubstrAfterFirstS (&item->conn .security , " _mode_" );
284- if (ffStrbufEqualS (&item->conn .security , " none" ))
285- ffStrbufSetStatic (&item->conn .security , " Insecure" );
286- else
287- {
288- ffStrbufReplaceAllC (&item->conn .security , ' _' , ' ' );
289- if (ffStrbufStartsWithS (&item->conn .security , " wpa" ))
290- {
291- item->conn .security .chars [0 ] = ' W' ;
292- item->conn .security .chars [1 ] = ' P' ;
293- item->conn .security .chars [2 ] = ' A' ;
294- char * sub = strchr (item->conn .security .chars , ' ' );
295- if (sub && sub[1 ])
296- sub[1 ] = (char ) toupper (sub[1 ]);
297- }
298- }
299-
300- double rssiValue = strtod ([station[@" spairport_signal_noise" ] UTF8String ], nil );
301- item->conn .signalQuality = (double ) (rssiValue >= -50 ? 100 : rssiValue <= -100 ? 0 : (rssiValue + 100 ) * 2 );
302- item->conn .txRate = (double ) [station[@" spairport_network_rate" ] longValue ];
303- }
304- }
305-
306- return NULL ;
307- }
308-
309- const char * ffDetectWifi (FFlist* result)
310- {
311- if (@available (macOS 15.0 , *))
312- return detectWifiBySystemProfiler (result);
313- else
314- return detectWifiByCoreWlan (result);
315- }
0 commit comments