Update version
[pithos-ios] / Classes / Reachability.m
1 /*
2  
3  File: Reachability.m
4  Abstract: Basic demonstration of how to use the SystemConfiguration Reachablity APIs.
5  
6  Version: 2.0.4ddg
7  */
8
9 /*
10  Significant additions made by Andrew W. Donoho, August 11, 2009.
11  This is a derived work of Apple's Reachability v2.0 class.
12  
13  The below license is the new BSD license with the OSI recommended personalizations.
14  <http://www.opensource.org/licenses/bsd-license.php>
15
16  Extensions Copyright (C) 2009 Donoho Design Group, LLC. All Rights Reserved.
17  
18  Redistribution and use in source and binary forms, with or without
19  modification, are permitted provided that the following conditions are
20  met:
21  
22  * Redistributions of source code must retain the above copyright notice,
23  this list of conditions and the following disclaimer.
24  
25  * Redistributions in binary form must reproduce the above copyright
26  notice, this list of conditions and the following disclaimer in the
27  documentation and/or other materials provided with the distribution.
28  
29  * Neither the name of Andrew W. Donoho nor Donoho Design Group, L.L.C.
30  may be used to endorse or promote products derived from this software
31  without specific prior written permission.
32  
33  THIS SOFTWARE IS PROVIDED BY DONOHO DESIGN GROUP, L.L.C. "AS IS" AND ANY
34  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
35  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
36  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
37  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
38  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
39  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
40  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
41  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
42  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
43  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
44  
45  */
46
47
48 /*
49  
50  Apple's Original License on Reachability v2.0
51  
52  Disclaimer: IMPORTANT:  This Apple software is supplied to you by Apple Inc.
53  ("Apple") in consideration of your agreement to the following terms, and your
54  use, installation, modification or redistribution of this Apple software
55  constitutes acceptance of these terms.  If you do not agree with these terms,
56  please do not use, install, modify or redistribute this Apple software.
57  
58  In consideration of your agreement to abide by the following terms, and subject
59  to these terms, Apple grants you a personal, non-exclusive license, under
60  Apple's copyrights in this original Apple software (the "Apple Software"), to
61  use, reproduce, modify and redistribute the Apple Software, with or without
62  modifications, in source and/or binary forms; provided that if you redistribute
63  the Apple Software in its entirety and without modifications, you must retain
64  this notice and the following text and disclaimers in all such redistributions
65  of the Apple Software.
66
67  Neither the name, trademarks, service marks or logos of Apple Inc. may be used
68  to endorse or promote products derived from the Apple Software without specific
69  prior written permission from Apple.  Except as expressly stated in this notice,
70  no other rights or licenses, express or implied, are granted by Apple herein,
71  including but not limited to any patent rights that may be infringed by your
72  derivative works or by other works in which the Apple Software may be
73  incorporated.
74  
75  The Apple Software is provided by Apple on an "AS IS" basis.  APPLE MAKES NO
76  WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
77  WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
78  PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
79  COMBINATION WITH YOUR PRODUCTS.
80  
81  IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
82  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
83  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
84  ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR
85  DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF
86  CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF
87  APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
88  
89  Copyright (C) 2009 Apple Inc. All Rights Reserved.
90  
91 */
92
93 /*
94  Each reachability object now has a copy of the key used to store it in a dictionary.
95  This allows each observer to quickly determine if the event is important to them.
96 */
97
98 #import <sys/socket.h>
99 #import <netinet/in.h>
100 #import <netinet6/in6.h>
101 #import <arpa/inet.h>
102 #import <ifaddrs.h>
103 #import <netdb.h>
104
105 #import <CoreFoundation/CoreFoundation.h>
106
107 #import "Reachability.h"
108
109 NSString *const kInternetConnection  = @"InternetConnection";
110 NSString *const kLocalWiFiConnection = @"LocalWiFiConnection";
111 NSString *const kReachabilityChangedNotification = @"NetworkReachabilityChangedNotification";
112
113 #define CLASS_DEBUG 1 // Turn on logReachabilityFlags. Must also have a project wide defined DEBUG.
114
115 #if (defined DEBUG && defined CLASS_DEBUG)
116 #define logReachabilityFlags(flags) (logReachabilityFlags_(__PRETTY_FUNCTION__, __LINE__, flags))
117
118 static NSString *reachabilityFlags_(SCNetworkReachabilityFlags flags) {
119         
120 #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 30000) // Apple advises you to use the magic number instead of a symbol.
121     return [NSString stringWithFormat:@"Reachability Flags: %c%c %c%c%c%c%c%c%c",
122                         (flags & kSCNetworkReachabilityFlagsIsWWAN)               ? 'W' : '-',
123                         (flags & kSCNetworkReachabilityFlagsReachable)            ? 'R' : '-',
124                         
125                         (flags & kSCNetworkReachabilityFlagsConnectionRequired)   ? 'c' : '-',
126                         (flags & kSCNetworkReachabilityFlagsTransientConnection)  ? 't' : '-',
127                         (flags & kSCNetworkReachabilityFlagsInterventionRequired) ? 'i' : '-',
128                         (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic)  ? 'C' : '-',
129                         (flags & kSCNetworkReachabilityFlagsConnectionOnDemand)   ? 'D' : '-',
130                         (flags & kSCNetworkReachabilityFlagsIsLocalAddress)       ? 'l' : '-',
131                         (flags & kSCNetworkReachabilityFlagsIsDirect)             ? 'd' : '-'];
132 #else
133         // Compile out the v3.0 features for v2.2.1 deployment.
134     return [NSString stringWithFormat:@"Reachability Flags: %c%c %c%c%c%c%c%c",
135                         (flags & kSCNetworkReachabilityFlagsIsWWAN)               ? 'W' : '-',
136                         (flags & kSCNetworkReachabilityFlagsReachable)            ? 'R' : '-',
137                         
138                         (flags & kSCNetworkReachabilityFlagsConnectionRequired)   ? 'c' : '-',
139                         (flags & kSCNetworkReachabilityFlagsTransientConnection)  ? 't' : '-',
140                         (flags & kSCNetworkReachabilityFlagsInterventionRequired) ? 'i' : '-',
141                         // v3 kSCNetworkReachabilityFlagsConnectionOnTraffic == v2 kSCNetworkReachabilityFlagsConnectionAutomatic
142                         (flags & kSCNetworkReachabilityFlagsConnectionAutomatic)  ? 'C' : '-',
143                         // (flags & kSCNetworkReachabilityFlagsConnectionOnDemand)   ? 'D' : '-', // No v2 equivalent.
144                         (flags & kSCNetworkReachabilityFlagsIsLocalAddress)       ? 'l' : '-',
145                         (flags & kSCNetworkReachabilityFlagsIsDirect)             ? 'd' : '-'];
146 #endif
147         
148 } // reachabilityFlags_()
149
150 static void logReachabilityFlags_(const char *name, int line, SCNetworkReachabilityFlags flags) {
151         
152     NSLog(@"%s (%d) \n\t%@", name, line, reachabilityFlags_(flags));
153         
154 } // logReachabilityFlags_()
155
156 #define logNetworkStatus(status) (logNetworkStatus_(__PRETTY_FUNCTION__, __LINE__, status))
157
158 static void logNetworkStatus_(const char *name, int line, NetworkStatus status) {
159         
160         NSString *statusString = nil;
161         
162         switch (status) {
163                 case kNotReachable:
164                         statusString = [NSString stringWithString: @"Not Reachable"];
165                         break;
166                 case kReachableViaWWAN:
167                         statusString = [NSString stringWithString: @"Reachable via WWAN"];
168                         break;
169                 case kReachableViaWiFi:
170                         statusString = [NSString stringWithString: @"Reachable via WiFi"];
171                         break;
172         }
173         
174         NSLog(@"%s (%d) \n\tNetwork Status: %@", name, line, statusString);
175         
176 } // logNetworkStatus_()
177
178 #else
179 #define logReachabilityFlags(flags)
180 #define logNetworkStatus(status)
181 #endif
182
183 @interface Reachability (private)
184
185 - (NetworkStatus) networkStatusForFlags: (SCNetworkReachabilityFlags) flags;
186
187 @end
188
189 @implementation Reachability
190
191 @synthesize key = key_;
192
193 // Preclude direct access to ivars.
194 + (BOOL) accessInstanceVariablesDirectly {
195         
196         return NO;
197
198 } // accessInstanceVariablesDirectly
199
200
201 - (void) dealloc {
202         
203         [self stopNotifier];
204         if(reachabilityRef) {
205                 
206                 CFRelease(reachabilityRef); reachabilityRef = NULL;
207                 
208         }
209         
210         self.key = nil;
211         
212         [super dealloc];
213         
214 } // dealloc
215
216
217 - (Reachability *) initWithReachabilityRef: (SCNetworkReachabilityRef) ref 
218 {
219     self = [super init];
220         if (self != nil) 
221     {
222                 reachabilityRef = ref;
223         }
224         
225         return self;
226         
227 } // initWithReachabilityRef:
228
229
230 #if (defined DEBUG && defined CLASS_DEBUG)
231 - (NSString *) description {
232         
233         NSAssert(reachabilityRef, @"-description called with NULL reachabilityRef");
234         
235         SCNetworkReachabilityFlags flags = 0;
236         
237         SCNetworkReachabilityGetFlags(reachabilityRef, &flags);
238         
239         return [NSString stringWithFormat: @"%@\n\t%@", self.key, reachabilityFlags_(flags)];
240         
241 } // description
242 #endif
243
244
245 #pragma mark -
246 #pragma mark Notification Management Methods
247
248
249 //Start listening for reachability notifications on the current run loop
250 static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void* info) {
251
252         #pragma unused (target, flags)
253         NSCAssert(info, @"info was NULL in ReachabilityCallback");
254         NSCAssert([(NSObject*) info isKindOfClass: [Reachability class]], @"info was the wrong class in ReachabilityCallback");
255         
256         //We're on the main RunLoop, so an NSAutoreleasePool is not necessary, but is added defensively
257         // in case someone uses the Reachablity object in a different thread.
258         NSAutoreleasePool* pool = [NSAutoreleasePool new];
259         
260         // Post a notification to notify the client that the network reachability changed.
261         [[NSNotificationCenter defaultCenter] postNotificationName: kReachabilityChangedNotification 
262                                                                                                                 object: (Reachability *) info];
263         
264         [pool release];
265
266 } // ReachabilityCallback()
267
268
269 - (BOOL) startNotifier {
270         
271         SCNetworkReachabilityContext    context = {0, self, NULL, NULL, NULL};
272         
273         if(SCNetworkReachabilitySetCallback(reachabilityRef, ReachabilityCallback, &context)) {
274                 
275                 if(SCNetworkReachabilityScheduleWithRunLoop(reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode)) {
276
277                         return YES;
278                         
279                 }
280                 
281         }
282         
283         return NO;
284
285 } // startNotifier
286
287
288 - (void) stopNotifier {
289         
290         if(reachabilityRef) {
291                 
292                 SCNetworkReachabilityUnscheduleFromRunLoop(reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
293
294         }
295
296 } // stopNotifier
297
298
299 - (BOOL) isEqual: (Reachability *) r {
300         
301         return [r.key isEqualToString: self.key];
302         
303 } // isEqual:
304
305
306 #pragma mark -
307 #pragma mark Reachability Allocation Methods
308
309
310 + (Reachability *) reachabilityWithHostName: (NSString *) hostName {
311         
312         SCNetworkReachabilityRef ref = SCNetworkReachabilityCreateWithName(NULL, [hostName UTF8String]);
313         
314         if (ref) {
315                 
316                 Reachability *r = [[[self alloc] initWithReachabilityRef: ref] autorelease];
317                 
318                 r.key = hostName;
319
320                 return r;
321                 
322         }
323         
324         return nil;
325         
326 } // reachabilityWithHostName
327
328
329 + (NSString *) makeAddressKey: (in_addr_t) addr {
330         // addr is assumed to be in network byte order.
331         
332         static const int       highShift    = 24;
333         static const int       highMidShift = 16;
334         static const int       lowMidShift  =  8;
335         static const in_addr_t mask         = 0x000000ff;
336         
337         addr = ntohl(addr);
338         
339         return [NSString stringWithFormat: @"%d.%d.%d.%d", 
340                         (addr >> highShift)    & mask, 
341                         (addr >> highMidShift) & mask, 
342                         (addr >> lowMidShift)  & mask, 
343                          addr                  & mask];
344         
345 } // makeAddressKey:
346
347
348 + (Reachability *) reachabilityWithAddress: (const struct sockaddr_in *) hostAddress {
349         
350         SCNetworkReachabilityRef ref = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr*)hostAddress);
351
352         if (ref) {
353                 
354                 Reachability *r = [[[self alloc] initWithReachabilityRef: ref] autorelease];
355                 
356                 r.key = [self makeAddressKey: hostAddress->sin_addr.s_addr];
357                 
358                 return r;
359                 
360         }
361         
362         return nil;
363
364 } // reachabilityWithAddress
365
366
367 + (Reachability *) reachabilityForInternetConnection {
368         
369         struct sockaddr_in zeroAddress;
370         bzero(&zeroAddress, sizeof(zeroAddress));
371         zeroAddress.sin_len = sizeof(zeroAddress);
372         zeroAddress.sin_family = AF_INET;
373
374         Reachability *r = [self reachabilityWithAddress: &zeroAddress];
375
376         r.key = kInternetConnection;
377         
378         return r;
379
380 } // reachabilityForInternetConnection
381
382
383 + (Reachability *) reachabilityForLocalWiFi {
384         
385         struct sockaddr_in localWifiAddress;
386         bzero(&localWifiAddress, sizeof(localWifiAddress));
387         localWifiAddress.sin_len = sizeof(localWifiAddress);
388         localWifiAddress.sin_family = AF_INET;
389         // IN_LINKLOCALNETNUM is defined in <netinet/in.h> as 169.254.0.0
390         localWifiAddress.sin_addr.s_addr = htonl(IN_LINKLOCALNETNUM);
391
392         Reachability *r = [self reachabilityWithAddress: &localWifiAddress];
393
394         r.key = kLocalWiFiConnection;
395
396         return r;
397
398 } // reachabilityForLocalWiFi
399
400
401 #pragma mark -
402 #pragma mark Network Flag Handling Methods
403
404
405 #if USE_DDG_EXTENSIONS
406 //
407 // iPhone condition codes as reported by a 3GS running iPhone OS v3.0.
408 // Airplane Mode turned on:  Reachability Flag Status: -- -------
409 // WWAN Active:              Reachability Flag Status: WR -t-----
410 // WWAN Connection required: Reachability Flag Status: WR ct-----
411 //         WiFi turned on:   Reachability Flag Status: -R ------- Reachable.
412 // Local   WiFi turned on:   Reachability Flag Status: -R xxxxxxd Reachable.
413 //         WiFi turned on:   Reachability Flag Status: -R ct----- Connection down. (Non-intuitive, empirically determined answer.)
414 const SCNetworkReachabilityFlags kConnectionDown =  kSCNetworkReachabilityFlagsConnectionRequired | 
415                                                                                                     kSCNetworkReachabilityFlagsTransientConnection;
416 //         WiFi turned on:   Reachability Flag Status: -R ct-i--- Reachable but it will require user intervention (e.g. enter a WiFi password).
417 //         WiFi turned on:   Reachability Flag Status: -R -t----- Reachable via VPN.
418 //
419 // In the below method, an 'x' in the flag status means I don't care about its value.
420 //
421 // This method differs from Apple's by testing explicitly for empirically observed values.
422 // This gives me more confidence in it's correct behavior. Apple's code covers more cases 
423 // than mine. My code covers the cases that occur.
424 //
425 - (NetworkStatus) networkStatusForFlags: (SCNetworkReachabilityFlags) flags {
426         
427         if (flags & kSCNetworkReachabilityFlagsReachable) {
428                 
429                 // Local WiFi -- Test derived from Apple's code: -localWiFiStatusForFlags:.
430                 if (self.key == kLocalWiFiConnection) {
431
432                         // Reachability Flag Status: xR xxxxxxd Reachable.
433                         return (flags & kSCNetworkReachabilityFlagsIsDirect) ? kReachableViaWiFi : kNotReachable;
434
435                 }
436                 
437                 // Observed WWAN Values:
438                 // WWAN Active:              Reachability Flag Status: WR -t-----
439                 // WWAN Connection required: Reachability Flag Status: WR ct-----
440                 //
441                 // Test Value: Reachability Flag Status: WR xxxxxxx
442                 if (flags & kSCNetworkReachabilityFlagsIsWWAN) { return kReachableViaWWAN; }
443                 
444                 // Clear moot bits.
445                 flags &= ~kSCNetworkReachabilityFlagsReachable;
446                 flags &= ~kSCNetworkReachabilityFlagsIsDirect;
447                 flags &= ~kSCNetworkReachabilityFlagsIsLocalAddress; // kInternetConnection is local.
448                 
449                 // Reachability Flag Status: -R ct---xx Connection down.
450                 if (flags == kConnectionDown) { return kNotReachable; }
451                 
452                 // Reachability Flag Status: -R -t---xx Reachable. WiFi + VPN(is up) (Thank you Ling Wang)
453                 if (flags & kSCNetworkReachabilityFlagsTransientConnection)  { return kReachableViaWiFi; }
454                         
455                 // Reachability Flag Status: -R -----xx Reachable.
456                 if (flags == 0) { return kReachableViaWiFi; }
457                 
458                 // Apple's code tests for dynamic connection types here. I don't. 
459                 // If a connection is required, regardless of whether it is on demand or not, it is a WiFi connection.
460                 // If you care whether a connection needs to be brought up,   use -isConnectionRequired.
461                 // If you care about whether user intervention is necessary,  use -isInterventionRequired.
462                 // If you care about dynamically establishing the connection, use -isConnectionIsOnDemand.
463
464                 // Reachability Flag Status: -R cxxxxxx Reachable.
465                 if (flags & kSCNetworkReachabilityFlagsConnectionRequired) { return kReachableViaWiFi; }
466                 
467                 // Required by the compiler. Should never get here. Default to not connected.
468 #if (defined DEBUG && defined CLASS_DEBUG)
469                 NSAssert1(NO, @"Uncaught reachability test. Flags: %@", reachabilityFlags_(flags));
470 #endif
471                 return kNotReachable;
472
473                 }
474         
475         // Reachability Flag Status: x- xxxxxxx
476         return kNotReachable;
477         
478 } // networkStatusForFlags:
479
480
481 - (NetworkStatus) currentReachabilityStatus {
482         
483         NSAssert(reachabilityRef, @"currentReachabilityStatus called with NULL reachabilityRef");
484         
485         SCNetworkReachabilityFlags flags = 0;
486         NetworkStatus status = kNotReachable;
487         
488         if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags)) {
489                 
490 //              logReachabilityFlags(flags);
491                 
492                 status = [self networkStatusForFlags: flags];
493                 
494                 return status;
495                 
496         }
497         
498         return kNotReachable;
499         
500 } // currentReachabilityStatus
501
502
503 - (BOOL) isReachable {
504         
505         NSAssert(reachabilityRef, @"isReachable called with NULL reachabilityRef");
506         
507         SCNetworkReachabilityFlags flags = 0;
508         NetworkStatus status = kNotReachable;
509         
510         if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags)) {
511                 
512 //              logReachabilityFlags(flags);
513
514                 status = [self networkStatusForFlags: flags];
515
516 //              logNetworkStatus(status);
517                 
518                 return (kNotReachable != status);
519                 
520         }
521         
522         return NO;
523         
524 } // isReachable
525
526
527 - (BOOL) isConnectionRequired {
528         
529         NSAssert(reachabilityRef, @"isConnectionRequired called with NULL reachabilityRef");
530         
531         SCNetworkReachabilityFlags flags;
532         
533         if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags)) {
534                 
535                 logReachabilityFlags(flags);
536                 
537                 return (flags & kSCNetworkReachabilityFlagsConnectionRequired);
538
539         }
540         
541         return NO;
542         
543 } // isConnectionRequired
544
545
546 - (BOOL) connectionRequired {
547         
548         return [self isConnectionRequired];
549         
550 } // connectionRequired
551 #endif
552
553
554 #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 30000)
555 static const SCNetworkReachabilityFlags kOnDemandConnection = kSCNetworkReachabilityFlagsConnectionOnTraffic | 
556                                                               kSCNetworkReachabilityFlagsConnectionOnDemand;
557 #else
558 static const SCNetworkReachabilityFlags kOnDemandConnection = kSCNetworkReachabilityFlagsConnectionAutomatic;
559 #endif
560
561 - (BOOL) isConnectionOnDemand {
562         
563         NSAssert(reachabilityRef, @"isConnectionIsOnDemand called with NULL reachabilityRef");
564         
565         SCNetworkReachabilityFlags flags;
566         
567         if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags)) {
568                 
569                 logReachabilityFlags(flags);
570                 
571                 return ((flags & kSCNetworkReachabilityFlagsConnectionRequired) &&
572                                 (flags & kOnDemandConnection));
573                 
574         }
575         
576         return NO;
577         
578 } // isConnectionOnDemand
579
580
581 - (BOOL) isInterventionRequired {
582         
583         NSAssert(reachabilityRef, @"isInterventionRequired called with NULL reachabilityRef");
584         
585         SCNetworkReachabilityFlags flags;
586         
587         if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags)) {
588                 
589                 logReachabilityFlags(flags);
590                 
591                 return ((flags & kSCNetworkReachabilityFlagsConnectionRequired) &&
592                                 (flags & kSCNetworkReachabilityFlagsInterventionRequired));
593                 
594         }
595         
596         return NO;
597         
598 } // isInterventionRequired
599
600
601 - (BOOL) isReachableViaWWAN {
602         
603         NSAssert(reachabilityRef, @"isReachableViaWWAN called with NULL reachabilityRef");
604         
605         SCNetworkReachabilityFlags flags = 0;
606         NetworkStatus status = kNotReachable;
607         
608         if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags)) {
609                 
610                 logReachabilityFlags(flags);
611                 
612                 status = [self networkStatusForFlags: flags];
613                 
614                 return  (kReachableViaWWAN == status);
615                         
616         }
617         
618         return NO;
619         
620 } // isReachableViaWWAN
621
622
623 - (BOOL) isReachableViaWiFi {
624         
625         NSAssert(reachabilityRef, @"isReachableViaWiFi called with NULL reachabilityRef");
626         
627         SCNetworkReachabilityFlags flags = 0;
628         NetworkStatus status = kNotReachable;
629         
630         if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags)) {
631                 
632                 logReachabilityFlags(flags);
633                 
634                 status = [self networkStatusForFlags: flags];
635                 
636                 return  (kReachableViaWiFi == status);
637                 
638         }
639         
640         return NO;
641         
642 } // isReachableViaWiFi
643
644
645 - (SCNetworkReachabilityFlags) reachabilityFlags {
646         
647         NSAssert(reachabilityRef, @"reachabilityFlags called with NULL reachabilityRef");
648         
649         SCNetworkReachabilityFlags flags = 0;
650         
651         if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags)) {
652                 
653                 logReachabilityFlags(flags);
654                 
655                 return flags;
656                 
657         }
658         
659         return 0;
660         
661 } // reachabilityFlags
662
663
664 #pragma mark -
665 #pragma mark Apple's Network Flag Handling Methods
666
667
668 #if !USE_DDG_EXTENSIONS
669 /*
670  *
671  *  Apple's Network Status testing code.
672  *  The only changes that have been made are to use the new logReachabilityFlags macro and
673  *  test for local WiFi via the key instead of Apple's boolean. Also, Apple's code was for v3.0 only
674  *  iPhone OS. v2.2.1 and earlier conditional compiling is turned on. Hence, to mirror Apple's behavior,
675  *  set your Base SDK to v3.0 or higher.
676  *
677  */
678
679 - (NetworkStatus) localWiFiStatusForFlags: (SCNetworkReachabilityFlags) flags
680 {
681         logReachabilityFlags(flags);
682         
683         BOOL retVal = NotReachable;
684         if((flags & kSCNetworkReachabilityFlagsReachable) && (flags & kSCNetworkReachabilityFlagsIsDirect))
685         {
686                 retVal = ReachableViaWiFi;      
687         }
688         return retVal;
689 }
690
691
692 - (NetworkStatus) networkStatusForFlags: (SCNetworkReachabilityFlags) flags
693 {
694         logReachabilityFlags(flags);
695         if (!(flags & kSCNetworkReachabilityFlagsReachable))
696         {
697                 // if target host is not reachable
698                 return NotReachable;
699         }
700         
701         BOOL retVal = NotReachable;
702         
703         if (!(flags & kSCNetworkReachabilityFlagsConnectionRequired))
704         {
705                 // if target host is reachable and no connection is required
706                 //  then we'll assume (for now) that your on Wi-Fi
707                 retVal = ReachableViaWiFi;
708         }
709         
710 #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 30000) // Apple advises you to use the magic number instead of a symbol.       
711         if ((flags & kSCNetworkReachabilityFlagsConnectionOnDemand) ||
712                 (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic))
713 #else
714         if (flags & kSCNetworkReachabilityFlagsConnectionAutomatic)
715 #endif
716                 {
717                         // ... and the connection is on-demand (or on-traffic) if the
718                         //     calling application is using the CFSocketStream or higher APIs
719                         
720                         if (!(flags & kSCNetworkReachabilityFlagsInterventionRequired))
721                         {
722                                 // ... and no [user] intervention is needed
723                                 retVal = ReachableViaWiFi;
724                         }
725                 }
726         
727         if (flags & kSCNetworkReachabilityFlagsIsWWAN)
728         {
729                 // ... but WWAN connections are OK if the calling application
730                 //     is using the CFNetwork (CFSocketStream?) APIs.
731                 retVal = ReachableViaWWAN;
732         }
733         return retVal;
734 }
735
736
737 - (NetworkStatus) currentReachabilityStatus
738 {
739         NSAssert(reachabilityRef, @"currentReachabilityStatus called with NULL reachabilityRef");
740         
741         NetworkStatus retVal = NotReachable;
742         SCNetworkReachabilityFlags flags;
743         if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags))
744         {
745                 if(self.key == kLocalWiFiConnection)
746                 {
747                         retVal = [self localWiFiStatusForFlags: flags];
748                 }
749                 else
750                 {
751                         retVal = [self networkStatusForFlags: flags];
752                 }
753         }
754         return retVal;
755 }
756
757
758 - (BOOL) isReachable {
759         
760         NSAssert(reachabilityRef, @"isReachable called with NULL reachabilityRef");
761         
762         SCNetworkReachabilityFlags flags = 0;
763         NetworkStatus status = kNotReachable;
764         
765         if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags)) {
766                 
767                 logReachabilityFlags(flags);
768                 
769                 if(self.key == kLocalWiFiConnection) {
770                         
771                         status = [self localWiFiStatusForFlags: flags];
772                         
773                 } else {
774                         
775                         status = [self networkStatusForFlags: flags];
776                         
777                 }
778                 
779                 return (kNotReachable != status);
780                 
781         }
782         
783         return NO;
784         
785 } // isReachable
786
787
788 - (BOOL) isConnectionRequired {
789         
790         return [self connectionRequired];
791         
792 } // isConnectionRequired
793
794
795 - (BOOL) connectionRequired {
796         
797         NSAssert(reachabilityRef, @"connectionRequired called with NULL reachabilityRef");
798         
799         SCNetworkReachabilityFlags flags;
800         
801         if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags)) {
802                 
803                 logReachabilityFlags(flags);
804                 
805                 return (flags & kSCNetworkReachabilityFlagsConnectionRequired);
806                 
807         }
808         
809         return NO;
810         
811 } // connectionRequired
812 #endif
813
814 @end