1/*
2 * Copyright (C) 2008 Apple Inc. All Rights Reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "NetworkStateNotifier.h"
28
29#include <SystemConfiguration/SystemConfiguration.h>
30
31#ifdef BUILDING_ON_TIGER
32// This function is available on Tiger, but not declared in the CFRunLoop.h header on Tiger.
33extern "C" CFRunLoopRef CFRunLoopGetMain();
34#endif
35
36namespace WebCore {
37
38static const double StateChangeTimerInterval = 2.0;
39
40void NetworkStateNotifier::updateState()
41{
42    // Assume that we're offline until proven otherwise.
43    m_isOnLine = false;
44
45    RetainPtr<CFStringRef> str(AdoptCF, SCDynamicStoreKeyCreateNetworkInterface(0, kSCDynamicStoreDomainState));
46
47    RetainPtr<CFPropertyListRef> propertyList(AdoptCF, SCDynamicStoreCopyValue(m_store.get(), str.get()));
48
49    if (!propertyList)
50        return;
51
52    if (CFGetTypeID(propertyList.get()) != CFDictionaryGetTypeID())
53        return;
54
55    CFArrayRef netInterfaces = (CFArrayRef)CFDictionaryGetValue((CFDictionaryRef)propertyList.get(), kSCDynamicStorePropNetInterfaces);
56    if (CFGetTypeID(netInterfaces) != CFArrayGetTypeID())
57        return;
58
59    for (CFIndex i = 0; i < CFArrayGetCount(netInterfaces); i++) {
60        CFStringRef interface = (CFStringRef)CFArrayGetValueAtIndex(netInterfaces, i);
61        if (CFGetTypeID(interface) != CFStringGetTypeID())
62            continue;
63
64        // Ignore the loopback interface.
65        if (CFStringFind(interface, CFSTR("lo"), kCFCompareAnchored).location != kCFNotFound)
66            continue;
67
68        RetainPtr<CFStringRef> key(AdoptCF, SCDynamicStoreKeyCreateNetworkInterfaceEntity(0, kSCDynamicStoreDomainState, interface, kSCEntNetIPv4));
69
70        RetainPtr<CFArrayRef> keyList(AdoptCF, SCDynamicStoreCopyKeyList(m_store.get(), key.get()));
71
72        if (keyList && CFArrayGetCount(keyList.get())) {
73            m_isOnLine = true;
74            break;
75        }
76    }
77}
78
79void NetworkStateNotifier::dynamicStoreCallback(SCDynamicStoreRef, CFArrayRef, void* info)
80{
81    NetworkStateNotifier* notifier = static_cast<NetworkStateNotifier*>(info);
82
83    // Calling updateState() could be expensive so we schedule a timer that will do it
84    // when things have cooled down.
85    notifier->m_networkStateChangeTimer.startOneShot(StateChangeTimerInterval);
86}
87
88void NetworkStateNotifier::networkStateChangeTimerFired(Timer<NetworkStateNotifier>*)
89{
90    bool oldOnLine = m_isOnLine;
91
92    updateState();
93
94    if (m_isOnLine == oldOnLine)
95        return;
96
97    if (m_networkStateChangedFunction)
98        m_networkStateChangedFunction();
99}
100
101NetworkStateNotifier::NetworkStateNotifier()
102    : m_isOnLine(false)
103    , m_networkStateChangedFunction(0)
104    , m_networkStateChangeTimer(this, &NetworkStateNotifier::networkStateChangeTimerFired)
105{
106    SCDynamicStoreContext context = { 0, this, 0, 0, 0 };
107
108    m_store.adoptCF(SCDynamicStoreCreate(0, CFSTR("com.apple.WebCore"), dynamicStoreCallback, &context));
109    if (!m_store)
110        return;
111
112    RetainPtr<CFRunLoopSourceRef> configSource = SCDynamicStoreCreateRunLoopSource(0, m_store.get(), 0);
113    if (!configSource)
114        return;
115
116    CFRunLoopAddSource(CFRunLoopGetMain(), configSource.get(), kCFRunLoopCommonModes);
117
118    RetainPtr<CFMutableArrayRef> keys(AdoptCF, CFArrayCreateMutable(0, 0, &kCFTypeArrayCallBacks));
119    RetainPtr<CFMutableArrayRef> patterns(AdoptCF, CFArrayCreateMutable(0, 0, &kCFTypeArrayCallBacks));
120
121    RetainPtr<CFStringRef> key;
122    RetainPtr<CFStringRef> pattern;
123
124    key.adoptCF(SCDynamicStoreKeyCreateNetworkGlobalEntity(0, kSCDynamicStoreDomainState, kSCEntNetIPv4));
125    CFArrayAppendValue(keys.get(), key.get());
126
127    pattern.adoptCF(SCDynamicStoreKeyCreateNetworkInterfaceEntity(0, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetIPv4));
128    CFArrayAppendValue(patterns.get(), pattern.get());
129
130    key.adoptCF(SCDynamicStoreKeyCreateNetworkGlobalEntity(0, kSCDynamicStoreDomainState, kSCEntNetDNS));
131    CFArrayAppendValue(keys.get(), key.get());
132
133    SCDynamicStoreSetNotificationKeys(m_store.get(), keys.get(), patterns.get());
134
135    updateState();
136}
137
138}
139