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
32namespace WebCore {
33
34static const double StateChangeTimerInterval = 2.0;
35
36void NetworkStateNotifier::updateState()
37{
38    // Assume that we're offline until proven otherwise.
39    m_isOnLine = false;
40
41    RetainPtr<CFStringRef> str = adoptCF(SCDynamicStoreKeyCreateNetworkInterface(0, kSCDynamicStoreDomainState));
42
43    RetainPtr<CFPropertyListRef> propertyList = adoptCF(SCDynamicStoreCopyValue(m_store.get(), str.get()));
44
45    if (!propertyList)
46        return;
47
48    if (CFGetTypeID(propertyList.get()) != CFDictionaryGetTypeID())
49        return;
50
51    CFArrayRef netInterfaces = (CFArrayRef)CFDictionaryGetValue((CFDictionaryRef)propertyList.get(), kSCDynamicStorePropNetInterfaces);
52    if (CFGetTypeID(netInterfaces) != CFArrayGetTypeID())
53        return;
54
55    for (CFIndex i = 0; i < CFArrayGetCount(netInterfaces); i++) {
56        CFStringRef interface = (CFStringRef)CFArrayGetValueAtIndex(netInterfaces, i);
57        if (CFGetTypeID(interface) != CFStringGetTypeID())
58            continue;
59
60        // Ignore the loopback interface.
61        if (CFStringFind(interface, CFSTR("lo"), kCFCompareAnchored).location != kCFNotFound)
62            continue;
63
64        RetainPtr<CFStringRef> key = adoptCF(SCDynamicStoreKeyCreateNetworkInterfaceEntity(0, kSCDynamicStoreDomainState, interface, kSCEntNetIPv4));
65
66        RetainPtr<CFArrayRef> keyList = adoptCF(SCDynamicStoreCopyKeyList(m_store.get(), key.get()));
67
68        if (keyList && CFArrayGetCount(keyList.get())) {
69            m_isOnLine = true;
70            break;
71        }
72    }
73}
74
75void NetworkStateNotifier::dynamicStoreCallback(SCDynamicStoreRef, CFArrayRef, void* info)
76{
77    NetworkStateNotifier* notifier = static_cast<NetworkStateNotifier*>(info);
78
79    // Calling updateState() could be expensive so we schedule a timer that will do it
80    // when things have cooled down.
81    notifier->m_networkStateChangeTimer.startOneShot(StateChangeTimerInterval);
82}
83
84void NetworkStateNotifier::networkStateChangeTimerFired(Timer<NetworkStateNotifier>*)
85{
86    bool oldOnLine = m_isOnLine;
87
88    updateState();
89
90    if (m_isOnLine == oldOnLine)
91        return;
92
93    if (m_networkStateChangedFunction)
94        m_networkStateChangedFunction();
95}
96
97NetworkStateNotifier::NetworkStateNotifier()
98    : m_isOnLine(false)
99    , m_networkStateChangedFunction(0)
100    , m_networkStateChangeTimer(this, &NetworkStateNotifier::networkStateChangeTimerFired)
101{
102    SCDynamicStoreContext context = { 0, this, 0, 0, 0 };
103
104    m_store = adoptCF(SCDynamicStoreCreate(0, CFSTR("com.apple.WebCore"), dynamicStoreCallback, &context));
105    if (!m_store)
106        return;
107
108    RetainPtr<CFRunLoopSourceRef> configSource = adoptCF(SCDynamicStoreCreateRunLoopSource(0, m_store.get(), 0));
109    if (!configSource)
110        return;
111
112    CFRunLoopAddSource(CFRunLoopGetMain(), configSource.get(), kCFRunLoopCommonModes);
113
114    RetainPtr<CFMutableArrayRef> keys = adoptCF(CFArrayCreateMutable(0, 0, &kCFTypeArrayCallBacks));
115    RetainPtr<CFMutableArrayRef> patterns = adoptCF(CFArrayCreateMutable(0, 0, &kCFTypeArrayCallBacks));
116
117    RetainPtr<CFStringRef> key;
118    RetainPtr<CFStringRef> pattern;
119
120    key = adoptCF(SCDynamicStoreKeyCreateNetworkGlobalEntity(0, kSCDynamicStoreDomainState, kSCEntNetIPv4));
121    CFArrayAppendValue(keys.get(), key.get());
122
123    pattern = adoptCF(SCDynamicStoreKeyCreateNetworkInterfaceEntity(0, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetIPv4));
124    CFArrayAppendValue(patterns.get(), pattern.get());
125
126    key = adoptCF(SCDynamicStoreKeyCreateNetworkGlobalEntity(0, kSCDynamicStoreDomainState, kSCEntNetDNS));
127    CFArrayAppendValue(keys.get(), key.get());
128
129    SCDynamicStoreSetNotificationKeys(m_store.get(), keys.get(), patterns.get());
130
131    updateState();
132}
133
134}
135