tizen 2.3.1 release tizen_2.3.1 submit/tizen_2.3.1/20150915.080603 tizen_2.3.1_release
authorjk7744.park <jk7744.park@samsung.com>
Tue, 8 Sep 2015 13:20:00 +0000 (22:20 +0900)
committerjk7744.park <jk7744.park@samsung.com>
Tue, 8 Sep 2015 13:20:00 +0000 (22:20 +0900)
198 files changed:
.gitignore
AUTHORS
ChangeLog
Makefile.am
Makefile.plugins
README
TODO
autogen.sh [deleted file]
client/main.c
configure.ac
connman.manifest [new file with mode: 0644]
debian/changelog [deleted file]
debian/compat [deleted file]
debian/connman-dev.install.in [deleted file]
debian/connman.install.in [deleted file]
debian/connman.postinst [deleted file]
debian/control [deleted file]
debian/rules [deleted file]
doc/agent-api.txt
doc/clock-api.txt
doc/config-format.txt
doc/manager-api.txt
doc/overview-api.txt
doc/service-api.txt
doc/session-api.txt
doc/technology-api.txt
gdbus/client.c [new file with mode: 0644]
gdbus/gdbus.h
gdbus/mainloop.c
gdbus/object.c
gdbus/watch.c
gdhcp/client.c [changed mode: 0644->0755]
gdhcp/common.c
gdhcp/common.h
gdhcp/gdhcp.h
gdhcp/ipv4ll.c
gdhcp/server.c
gdhcp/unaligned.h [new file with mode: 0644]
gsupplicant/dbus.c
gsupplicant/dbus.h
gsupplicant/gsupplicant.h
gsupplicant/supplicant.c
gweb/giognutls.c
gweb/giognutls.h
gweb/gionotls.c
gweb/gresolv.c
gweb/gresolv.h
gweb/gweb.c
gweb/gweb.h
include/dbus.h
include/device.h
include/inet.h
include/ipconfig.h
include/log.h
include/network.h
include/notifier.h
include/option.h
include/plugin.h
include/provider.h
include/proxy.h
include/resolver.h
include/rtnl.h
include/service.h
include/setting.h
include/storage.h
include/task.h
include/technology.h
include/timeserver.h
include/types.h
include/utsname.h
include/version.h.in
packaging/connman.changes [deleted file]
packaging/connman.spec
plugins/bluetooth.c
plugins/bluetooth_legacy.c [new file with mode: 0644]
plugins/dundee.c [new file with mode: 0644]
plugins/ethernet.c
plugins/fake.c [deleted file]
plugins/google.c [deleted file]
plugins/hh2serial-gps.c
plugins/iospm.c
plugins/iwmx.c [deleted file]
plugins/iwmx.h [deleted file]
plugins/iwmxsdk.c [deleted file]
plugins/l2tp.c
plugins/loopback.c
plugins/nmcompat.c
plugins/ntpd.c [deleted file]
plugins/ofono.c
plugins/openconnect.c
plugins/openvpn.c
plugins/pacrunner.c
plugins/polkit.c
plugins/pptp.c
plugins/telephony.c
plugins/tist.c
plugins/vpn.c
plugins/vpnc.c
plugins/wifi.c
resources/etc/rc.d/init.d/connman [deleted file]
resources/usr/sbin/connman.service [new file with mode: 0755]
resources/usr/share/dbus-1/services/net.connman.service
resources/var/lib/connman/settings
scripts/libppp-plugin.c
scripts/openconnect-script.c
scripts/openvpn-script.c
src/6to4.c
src/agent.c
src/bridge.c [new file with mode: 0644]
src/clock.c
src/config.c
src/connection.c
src/connman-dbus.conf
src/connman.h
src/connman.service.in
src/counter.c
src/dbus.c
src/detect.c
src/device.c
src/dhcp.c
src/dhcpv6.c [new file with mode: 0644]
src/dnsproxy.c
src/error.c
src/inet.c
src/ipconfig.c
src/ippool.c [new file with mode: 0644]
src/iptables.c
src/log.c
src/main.c
src/main.conf
src/manager.c
src/nat.c [new file with mode: 0644]
src/network.c
src/notifier.c
src/ntp.c
src/plugin.c
src/provider.c
src/proxy.c
src/resolver.c
src/rfkill.c
src/rtnl.c
src/service.c
src/session.c
src/stats.c
src/storage.c
src/task.c
src/technology.c
src/tethering.c
src/timeserver.c
src/timezone.c
src/utsname.c
src/wispr.c
src/wpad.c
test/connect-service [deleted file]
test/disable-tethering
test/enable-tethering
test/find-service [deleted file]
test/list-profiles [deleted file]
test/list-services
test/monitor-connman
test/monitor-manager [deleted file]
test/monitor-services
test/provision-service [deleted file]
test/set-address [deleted file]
test/set-ipv4-method
test/set-ipv6-method
test/set-timeservers [new file with mode: 0755]
test/show-introspection
test/simple-agent
test/test-connman
test/test-manager
test/test-session
tizen/rtctimer.c [new file with mode: 0644]
tizen/rtctimer.h [moved from plugins/meego.c with 55% similarity]
tools/addr-test.c
tools/alg-test.c [deleted file]
tools/dbus-test.c
tools/dhcp-server-test.c
tools/dhcp-test.c
tools/iptables-test.c
tools/polkit-test.c
tools/private-network-test.c
tools/resolv-test.c
tools/supplicant-dbus.c
tools/supplicant-dbus.h
tools/supplicant-test.c
tools/supplicant.c
tools/supplicant.h
tools/tap-test.c
tools/web-test.c
tools/wispr.c
tools/wpad-test.c
unit/session-api.c
unit/test-connman.h
unit/test-ippool.c [new file with mode: 0644]
unit/test-nat.c [new file with mode: 0644]
unit/test-session.c
unit/utils.c

index 0570e4a..1154876 100644 (file)
@@ -1,5 +1,9 @@
 *.o
 *.a
+*.os
+*.exe
+*.ipk
+*~
 *.lo
 *.la
 .deps
@@ -8,6 +12,7 @@
 Makefile
 Makefile.in
 aclocal.m4
+build-stamp
 config.guess
 config.h
 config.h.in
@@ -15,8 +20,22 @@ config.log
 config.status
 config.sub
 configure
+connman.pc
+debian/connman-dev.install
+debian/connman.install
+debian/connman-dbg.substvars
+debian/connman-dbg
+debian/connman-dev.substvars
+debian/connman-dev
+debian/connman.substvars
+debian/connman
+debian/files
+debian/tmp
 depcomp
 compile
+doc/version.xml
+include/connman/
+include/version.h
 install-sh
 libtool
 ltmain.sh
@@ -36,7 +55,7 @@ plugins/connman.policy
 scripts/connman
 scripts/openconnect-script
 scripts/openvpn-script
-client/cm
+client/connmanctl
 tools/wispr
 tools/dhcp-test
 tools/dhcp-server-test
@@ -59,8 +78,17 @@ unit/test-nat
 doc/*.bak
 doc/*.stamp
 doc/connman.*
+!doc/connman.8
+!doc/connman.conf.5
 doc/connman-*.txt
 doc/*.sgml
 doc/version.xml
 doc/xml
 doc/html
+
+vpn/builtin.h
+vpn/connman-vpnd
+vpn/connman-vpn.service
+
+.cproject
+.project
diff --git a/AUTHORS b/AUTHORS
index 28b7f40..33404c4 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -11,19 +11,19 @@ Forest Bond <forest@alittletooquiet.net>
 Kalle Valo <kalle.valo@canonical.com>
 Fabien Marotte <fabienx.marotte@linux.intel.com>
 Pekka Pessi <pekka.pessi@nokia.com>
-Tomasz Bursztyka <tomasz.bursztyka@nokia.com>
+Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>
 Cristiano Fernandes <cristiano.fernandes@hp.com>
 Joey Lee <jlee@novell.com>
 Leena Gunda <leena.gunda@wipro.com>
-Patrik Flykt <patrik.flykt@nokia.com>
+Patrik Flykt <patrik.flykt@linux.intel.com>
 David Woodhouse <dwmw2@infradead.org>
-Gustavo F. Padovan <padovan@profusion.mobi>
+Gustavo Padovan <gustavo@padovan.org>
 Julien Massot <jmassot@aldebaran-robotics.com>
-Jukka Rissanen <jukka.rissanen@nokia.com>
+Jukka Rissanen <jukka.rissanen@linux.intel.com>
 Grant Erickson <marathon96@gmail.com>
 Guillaume Lucas <guillaumex.lucas@intel.com>
 Henri Bragge <henri.bragge@ixonos.com>
-Alok Barsode <alok.barsode@nokia.com>
+Alok Barsode <alok.barsode@intel.com>
 Sébastien Bianti <sebastien.bianti@linux.intel.com>
 Yu A Wang <yu.a.wang@intel.com>
 Thierry Boureille <thierry.boureille@gmail.com>
@@ -32,4 +32,17 @@ Bertrand Aygon <bertrand.aygon@intel.com>
 Jeff Zheng <jeff.zheng@intel.com>
 Philippe Nunes <philippe.nunes@linux.intel.com>
 Danny Jeongseok Seo <s.seo@samsung.com>
+Flávio Ceolin <flavio.ceolin@profusion.mobi>
+Arjan van de Ven <arjan@linux.intel.com>
+Daniel Mack <zonque@gmail.com>
+Guillaume Zajac <guillaume.zajac@linux.intel.com>
 Manfred Kober <manfred.kober@gmx.de>
+Mario Domenech Goulart <mario.goulart@gmail.com>
+Otavio Salvador <otavio@ossystems.com.br>
+Tim Sander <tim01@iss.tu-darmstadt.de>
+Adrien Bustany <adrien.bustany@nokia.com>
+Henrique Dante de Almeida <hdante@profusion.mobi>
+Lucas De Marchi <lucas.demarchi@profusion.mobi>
+Elena Tebesoi <elena.tebesoi@gmail.com>
+Mikel Astiz <mikel.astiz@bmw-carit.de>
+Paulo Pizarro <paulo.pizarro@gmail.com>
index 8e05e85..a069a74 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
-ver 0.78.4
-       Apply BMC fix 24943
-
-ver 0.78.3:
-       Update ofono plugin.
-       Apply BMC fixes 24592, 23741, 23740, 24549, 24702, 24651, 24737,
-       24712, 24942, 24965 and 25026.
-
-ver 0.78.2:
-       Backport ofono with CDMA support
-
-ver 0.78.1:
-       Rebased for connman-stable
+ver 1.3:
+       Fix issue with default configuration values.
+       Fix issue with timeserver canonical name entries.
+       Fix issue with crash from cellular dummy context.
+       Fix issue with incorrect index for private networks.
+
+ver 1.2:
+       Fix issue with not handling WiFi security changes.
+       Fix issue with not stopping WiFi scanning on shutdown.
+       Fix issue with auto-scanning and network discovery.
+       Fix issue with D-Bus reply for hidden WiFi networks.
+       Fix issue with overlapping memory areas and DNS requests.
+       Add support for randomized DNS transaction identifiers.
+       Add support for DNS caching over TCP connections.
+       Add support for using default IPv6 privacy setting.
+       Add support for providing previous passphrase to agent.
+       Add support for configuration unprovisioning handling.
+       Add support for NetworkInterfaceBlacklist configuration.
+       Add support for Bluetooth DUN daemon (dundee).
+
+ver 1.1:
+       Fix issue with missing message type and DHCPv4 support.
+       Fix issue with potential NULL pointer in DHCPv6 handling.
+       Fix issue with potential NULL pointer in VPN handling.
+       Fix issue with potential NULL pointer for WiFi SSID.
+       Fix issue with missing conversion of raw WiFi PSK input.
+       Fix issue with missing stop for WiFi auto-scanning handling.
+       Fix issue with uninitialized IPv6 prefix length in oFono plugin.
+       Fix issue with domain search list handling according to RFC 6106.
+       Fix issue with domain name list notifications.
+       Fix issue with nameserver list notifications.
+       Fix issue with incorrect fixed IP configuration.
+       Fix issue with incorrect cleanup of resolver timers.
+       Fix issue with handling of RDNSS lifetime expiration.
+       Fix issue with crash on wrong domain length information.
+       Add support for favorite service database migration.
+       Add support for disabling WISPr functionality.
+       Add support for configurable agent timeouts.
+
+ver 1.0:
+       Fix issue with missing WiFi disconnecting flag.
+       Fix issue with missing GPRS context attached check.
+       Fix issue with potential crash and supplicant handling.
+       Fix issue with potential crash and VPN provider.
+       Fix issue with potential crash and host routes.
+
+ver 0.85:
+       Fix issue with duplicate service timeservers.
+       Fix issue with failure state when aborting agent request.
+       Fix issue with automatic scanning for hidden WiFi networks.
+       Fix issue with missing hostname/domainname validity checks.
+       Fix issue with missing DHCP packet length checks.
+       Add support for browser launching from WISPr login.
+
+ver 0.84:
+       Fix issue with handling changed WiFi security of access points.
+       Fix issue with state notification of NetworkManager compatibility.
+
+ver 0.83:
+       Fix issue with Ethernet not being enabled by default.
+       Fix issue with missing host routes for WISPr request.
+       Fix issue with missing HTTPS handling for WISPr support.
+       Fix issue with agent asking for passphrase for open service.
+       Fix issue with endless online check for preferred technology.
+       Fix issue with IPv6 RDNSS and DNS server creation.
+       Fix issue with WiFi roaming state change handling.
+       Fix issue with broken handling of WPS PBC method.
+
+ver 0.82:
+       Fix issue with global online state handling.
+       Fix issue with timeserver handling in case of VPN.
+       Fix issue with automatic WiFi scanning handling.
+       Fix issue with WPS PIN length of zero and PBC.
+
+ver 0.81:
+       Update default configuration options.
+       Add support for WPS PBC advertising handling.
+       Add support for wpa_supplicant background scanning.
+       Fix issue with showing cellular services without APN.
+       Fix issue with missing timeservers changed signal.
+
+ver 0.80:
+       Update to support additional D-Bus API changes.
+       Add support for preferred technologies switching.
+       Add support for default auto connect technologies option.
+       Add support for service specific timeserver configuration.
+       Add support for extended timeserver fallback functionality.
+       Add support for extended nameserver fallback functionality.
+       Add support for user supplied routes for providers.
+       Add support for checking WiFi passphrase validity.
+       Fix issue with WISPr support and proxy handling.
+       Fix issue with Ethernet and auto connect handling.
+       Fix issue with too early IPv6 auto connection.
+       Fix issue with too early 6to4 connection checks.
+       Fix issue with oFono interaction on disconnect.
+       Fix issue with DNS servers behind point-to-point links.
+       Fix issue with pending DNS proxy requests.
+       Fix multiple issues with VPN handling.
+       Fix multiple issues with default gateway handling.
+
+ver 0.79:
+       Update to support changed D-Bus API.
+       Add support for WiFi background scanning.
+       Add support for showing hidden WiFi networks as services.
+       Add support for generic stateless DHCPv6 handling.
+       Add support for DHCPv4 client identifier handling.
+       Add support for generic IP address pool handling.
+       Add support for global DNS cache handling.
+       Add support for internal NTP time handling.
+       Add support for updated oFono handling.
 
 ver 0.78:
        Fix multiple issues with service connection states.
index 3bca19e..f5bf62a 100644 (file)
@@ -22,13 +22,18 @@ local_headers = $(foreach file,$(include_HEADERS) $(nodist_include_HEADERS) \
 
 
 gdbus_sources = gdbus/gdbus.h gdbus/mainloop.c gdbus/watch.c \
-                                       gdbus/object.c gdbus/polkit.c
+                               gdbus/object.c gdbus/client.c gdbus/polkit.c
 
 gdhcp_sources = gdhcp/gdhcp.h gdhcp/common.h gdhcp/common.c gdhcp/client.c \
-               gdhcp/server.c gdhcp/ipv4ll.h gdhcp/ipv4ll.c
+               gdhcp/server.c gdhcp/ipv4ll.h gdhcp/ipv4ll.c gdhcp/unaligned.h
 
-gweb_sources = gweb/gweb.h gweb/gweb.c gweb/gresolv.h gweb/gresolv.c \
-                                       gweb/giognutls.h gweb/giognutls.c
+gweb_sources = gweb/gweb.h gweb/gweb.c gweb/gresolv.h gweb/gresolv.c
+
+if WISPR
+gweb_sources += gweb/giognutls.h gweb/giognutls.c
+else
+gweb_sources += gweb/giognutls.h gweb/gionotls.c
+endif
 
 if DATAFILES
 
@@ -62,10 +67,14 @@ unit_objects =
 
 sbin_PROGRAMS = src/connmand
 
-src_connmand_SOURCES = $(gdbus_sources) $(gdhcp_sources) \
-                       gweb/gweb.h gweb/gweb.c \
-                       gweb/gresolv.h gweb/gresolv.c \
-                       gweb/giognutls.h gweb/gionotls.c \
+if TIZEN_RTC
+tizen_rtc_timer_sources = tizen/rtctimer.h tizen/rtctimer.c
+else
+tizen_rtc_timer_sources =
+endif
+
+src_connmand_SOURCES = $(gdbus_sources) $(gdhcp_sources) $(gweb_sources) \
+                       $(tizen_rtc_timer_sources) \
                        $(builtin_sources) src/connman.ver \
                        src/main.c src/connman.h src/log.c \
                        src/error.c src/plugin.c src/task.c \
@@ -74,15 +83,16 @@ src_connmand_SOURCES = $(gdbus_sources) $(gdhcp_sources) \
                        src/clock.c src/timezone.c \
                        src/agent.c src/notifier.c src/provider.c \
                        src/resolver.c src/ipconfig.c src/detect.c src/inet.c \
-                       src/dhcp.c src/rtnl.c src/proxy.c \
+                       src/dhcp.c src/dhcpv6.c src/rtnl.c src/proxy.c \
                        src/utsname.c src/timeserver.c src/rfkill.c \
                        src/storage.c src/dbus.c src/config.c \
                        src/technology.c src/counter.c src/ntp.c \
                        src/session.c src/tethering.c src/wpad.c src/wispr.c \
-                       src/stats.c src/iptables.c src/dnsproxy.c src/6to4.c
+                       src/stats.c src/iptables.c src/dnsproxy.c src/6to4.c \
+                       src/ippool.c src/bridge.c src/nat.c
 
 src_connmand_LDADD = $(builtin_libadd) @GLIB_LIBS@ @DBUS_LIBS@ \
-                               @CAPNG_LIBS@ @XTABLES_LIBS@ -lresolv -ldl
+                               @XTABLES_LIBS@ @GNUTLS_LIBS@ -lresolv -ldl
 
 src_connmand_LDFLAGS = -Wl,--export-dynamic \
                                -Wl,--version-script=$(srcdir)/src/connman.ver
@@ -109,19 +119,20 @@ build_plugindir = $(plugindir)
 build_scriptdir = $(scriptdir)
 endif
 
-AM_CFLAGS = @DBUS_CFLAGS@ @GLIB_CFLAGS@ @CAPNG_CFLAGS@ @XTABLES_CFLAGS@ \
+AM_CFLAGS = @DBUS_CFLAGS@ @GLIB_CFLAGS@ @XTABLES_CFLAGS@ \
                                @GNUTLS_CFLAGS@ $(builtin_cflags) \
                                -DCONNMAN_PLUGIN_BUILTIN \
                                -DSTATEDIR=\""$(statedir)"\" \
                                -DPLUGINDIR=\""$(build_plugindir)"\" \
                                -DSCRIPTDIR=\""$(build_scriptdir)"\" \
                                -DSTORAGEDIR=\""$(storagedir)\"" \
+                               -DSYSCONFDIR=\""$(sysconfdir)\"" \
                                -DCONFIGDIR=\""$(configdir)\""
 
 INCLUDES = -I$(builddir)/include -I$(builddir)/src -I$(srcdir)/gdbus
 
 EXTRA_DIST = src/genbuiltin src/connman-dbus.conf src/connman-polkit.conf \
-               plugins/connman-nmcompat.conf
+                                               plugins/connman-nmcompat.conf
 
 
 script_DATA =
@@ -137,17 +148,21 @@ client_cm_SOURCES = client/main.c
 client_cm_LDADD = @DBUS_LIBS@
 endif
 
+if WISPR
+noinst_PROGRAMS += tools/wispr
+
+tools_wispr_SOURCES = $(gweb_sources) tools/wispr.c
+tools_wispr_LDADD = @GLIB_LIBS@ @GNUTLS_LIBS@ -lresolv
+endif
+
 if TOOLS
-noinst_PROGRAMS += tools/wispr tools/supplicant-test \
+noinst_PROGRAMS += tools/supplicant-test \
                        tools/dhcp-test tools/dhcp-server-test \
                        tools/addr-test tools/web-test tools/resolv-test \
                        tools/dbus-test tools/polkit-test \
                        tools/iptables-test tools/tap-test tools/wpad-test \
                        tools/stats-tool tools/private-network-test \
-                       tools/alg-test unit/test-session
-
-tools_wispr_SOURCES = $(gweb_sources) tools/wispr.c
-tools_wispr_LDADD = @GLIB_LIBS@ @GNUTLS_LIBS@ -lresolv
+                       unit/test-session unit/test-ippool unit/test-nat
 
 tools_supplicant_test_SOURCES = $(gdbus_sources) tools/supplicant-test.c \
                        tools/supplicant-dbus.h tools/supplicant-dbus.c \
@@ -180,28 +195,35 @@ tools_iptables_test_LDADD = @GLIB_LIBS@ @XTABLES_LIBS@
 
 tools_private_network_test_LDADD = @GLIB_LIBS@ @DBUS_LIBS@
 
-tools_alg_test_LDADD = @GLIB_LIBS@
-
 unit_test_session_SOURCES = $(gdbus_sources) src/log.c src/dbus.c \
                unit/test-session.c unit/utils.c unit/manager-api.c \
                unit/session-api.c unit/test-connman.h
 unit_test_session_LDADD = @GLIB_LIBS@ @DBUS_LIBS@ -ldl
 unit_objects += $(unit_test_session_OBJECTS)
+
+unit_test_ippool_SOURCES = $(gdbus_sources) src/log.c src/dbus.c \
+                src/ippool.c unit/test-ippool.c
+unit_test_ippool_LDADD = @GLIB_LIBS@ @DBUS_LIBS@ -ldl
+unit_objects += $(unit_test_ippool_OBJECTS)
+
+unit_test_nat_SOURCES = $(gdbus_sources) src/log.c src/dbus.c \
+               src/iptables.c  src/nat.c unit/test-nat.c
+unit_test_nat_LDADD = @GLIB_LIBS@ @DBUS_LIBS@  @XTABLES_LIBS@ -ldl
+unit_objects += $(unit_nat_ippool_OBJECTS)
 endif
 
-test_scripts = test/get-state test/list-profiles test/list-services \
-               test/connect-service test/monitor-services test/set-address \
+test_scripts = test/get-state test/list-services \
+               test/monitor-services test/test-clock \
                test/simple-agent test/show-introspection test/test-compat \
                test/test-manager test/test-connman test/monitor-connman \
-               test/connect-vpn test/disconnect-vpn test/monitor-manager \
+               test/connect-vpn test/disconnect-vpn \
                test/test-counter test/set-ipv4-method test/set-ipv6-method \
-               test/set-nameservers test/set-domains test/find-service \
                test/get-services test/get-proxy-autoconfig test/set-proxy \
                test/enable-tethering test/disable-tethering test/backtrace \
-               test/test-session test/provision-service test/test-supplicant \
+               test/test-session test/test-supplicant \
                test/test-new-supplicant test/service-move-before \
                test/set-global-timeservers test/get-global-timeservers \
-               test/test-clock
+               test/set-nameservers test/set-domains
 
 if TEST
 testdir = $(pkglibdir)/test
@@ -215,6 +237,8 @@ EXTRA_DIST += doc/overview-api.txt doc/behavior-api.txt \
                                doc/manager-api.txt doc/agent-api.txt \
                                doc/service-api.txt doc/technology-api.txt \
                                doc/counter-api.txt doc/config-format.txt \
+                               doc/clock-api.txt doc/session-api.txt \
+                               doc/session-overview.txt doc/backtrace.txt \
                                doc/advanced-configuration.txt
 
 
@@ -224,21 +248,12 @@ pkgconfig_DATA = connman.pc
 
 DISTCHECK_CONFIGURE_FLAGS = --disable-gtk-doc \
                                --disable-datafiles \
-                               --enable-loopback \
-                               --enable-ethernet \
-                               --enable-wifi \
-                               --enable-bluetooth \
-                               --enable-ofono \
-                               --enable-telephony \
-                               --enable-tizen-ext \
-                               --enable-pacrunner \
-                               --enable-google \
-                               --enable-meego \
-                               --enable-client \
                                --enable-hh2serial-gps \
-                               --enable-ntpd \
                                --enable-openconnect \
-                               --enable-tools
+                               --enable-openvpn \
+                               --enable-vpnc \
+                               --enable-nmcompat \
+                               --enable-polkit
 
 DISTCLEANFILES = $(pkgconfig_DATA)
 
index bcbae8c..74a1c4a 100644 (file)
@@ -7,57 +7,26 @@ script_cflags = -fvisibility=hidden -I$(srcdir)/gdbus \
                                        @DBUS_CFLAGS@
 
 if LOOPBACK
-if LOOPBACK_BUILTIN
 builtin_modules += loopback
 builtin_sources += plugins/loopback.c
-else
-plugin_LTLIBRARIES += plugins/loopback.la
-plugin_objects += $(plugins_loopback_la_OBJECTS)
-plugins_loopback_la_CFLAGS = $(plugin_cflags)
-plugins_loopback_la_LDFLAGS = $(plugin_ldflags)
-endif
 endif
 
 if ETHERNET
-if ETHERNET_BUILTIN
 builtin_modules += ethernet
 builtin_sources += plugins/ethernet.c
-else
-plugin_LTLIBRARIES += plugins/ethernet.la
-plugin_objects += $(plugins_ethernet_la_OBJECTS)
-plugins_ethernet_la_CFLAGS = $(plugin_cflags)
-plugins_ethernet_la_LDFLAGS = $(plugin_ldflags)
-endif
 endif
 
 gsupplicant_sources = gsupplicant/gsupplicant.h gsupplicant/dbus.h \
                        gsupplicant/supplicant.c gsupplicant/dbus.c
 
 if WIFI
-if WIFI_BUILTIN
 builtin_modules += wifi
 builtin_sources += plugins/wifi.c $(gsupplicant_sources)
-
-else
-plugin_LTLIBRARIES += plugins/wifi.la
-plugin_objects += $(plugins_wifi_la_OBJECTS)
-plugins_wifi_la_SOURCES = plugins/wifi.c $(gsupplicant_sources)
-plugins_wifi_la_CFLAGS = $(plugin_cflags)
-plugins_wifi_la_LDFLAGS = $(plugin_ldflags)
-
-endif
 endif
 
 if BLUETOOTH
-if BLUETOOTH_BUILTIN
 builtin_modules += bluetooth
 builtin_sources += plugins/bluetooth.c
-else
-plugin_LTLIBRARIES += plugins/bluetooth.la
-plugin_objects += $(plugins_bluetooth_la_OBJECTS)
-plugins_bluetooth_la_CFLAGS = $(plugin_cflags)
-plugins_bluetooth_la_LDFLAGS = $(plugin_ldflags)
-endif
 endif
 
 if HH2SERIAL_GPS
@@ -72,29 +41,19 @@ plugins_hh2serial_gps_la_LDFLAGS = $(plugin_ldflags)
 endif
 endif
 
+if TELEPHONY
+builtin_modules += telephony
+builtin_sources += plugins/telephony.c
+endif
+
 if OFONO
-if OFONO_BUILTIN
 builtin_modules += ofono
 builtin_sources += plugins/mcc.h plugins/ofono.c
-else
-plugin_LTLIBRARIES += plugins/ofono.la
-plugin_objects += $(plugins_ofono_la_OBJECTS)
-plugins_ofono_la_SOURCES = plugins/mcc.h plugins/ofono.c
-plugins_ofono_la_CFLAGS = $(plugin_cflags)
-plugins_ofono_la_LDFLAGS = $(plugin_ldflags)
-endif
 endif
 
-if TELEPHONY
-if TELEPHONY_BUILTIN
-builtin_modules += telephony
-builtin_sources += plugins/telephony.c
-else
-plugin_LTLIBRARIES += plugins/telephony.la
-plugin_objects += $(plugins_telephony_la_OBJECTS)
-plugins_telephony_la_CFLAGS = $(plugin_cflags)
-plugins_telephony_la_LDFLAGS = $(plugin_ldflags)
-endif
+if DUNDEE
+builtin_modules += dundee
+builtin_sources += plugins/dundee.c
 endif
 
 if OPENCONNECT
@@ -203,51 +162,13 @@ endif
 builtin_sources += $(builtin_vpn_sources)
 
 if PACRUNNER
-if PACRUNNER_BUILTIN
 builtin_modules += pacrunner
 builtin_sources += plugins/pacrunner.c
-else
-plugin_LTLIBRARIES += plugins/pacrunner.la
-plugin_objects += $(plugins_pacrunner_la_OBJECTS)
-plugins_pacrunner_la_CFLAGS = $(plugin_cflags)
-plugins_pacrunner_la_LDFLAGS = $(plugin_ldflags)
-endif
-endif
-
-if GOOGLE
-if GOOGLE_BUILTIN
-builtin_modules += google
-builtin_sources += plugins/google.c
-else
-plugin_LTLIBRARIES += plugins/google.la
-plugin_objects += $(plugins_google_la_OBJECTS)
-plugins_google_la_CFLAGS = $(plugin_cflags)
-plugins_google_la_LDFLAGS = $(plugin_ldflags)
-endif
-endif
-
-if MEEGO
-if MEEGO_BUILTIN
-builtin_modules += meego
-builtin_sources += plugins/meego.c
-else
-plugin_LTLIBRARIES += plugins/meego.la
-plugin_objects += $(plugins_meego_la_OBJECTS)
-plugins_meego_la_CFLAGS = $(plugin_cflags)
-plugins_meego_la_LDFLAGS = $(plugin_ldflags)
-endif
 endif
 
 if POLKIT
-if POLKIT_BUILTIN
 builtin_modules += polkit
 builtin_sources += plugins/polkit.c
-else
-plugin_LTLIBRARIES += plugins/polkit.la
-plugin_objects += $(plugins_polkit_la_OBJECTS)
-plugins_polkit_la_CFLAGS = $(plugin_cflags)
-plugins_polkit_la_LDFLAGS = $(plugin_ldflags)
-endif
 
 if DATAFILES
 policydir = @POLKIT_DATADIR@
@@ -256,15 +177,6 @@ policy_DATA = plugins/net.connman.policy
 endif
 endif
 
-if IWMX
-plugin_LTLIBRARIES += plugins/iwmxsdk.la
-plugin_objects += $(plugins_iwmxsdk_la_OBJECTS)
-plugins_iwmxsdk_la_SOURCES = plugins/iwmx.h plugins/iwmx.c plugins/iwmxsdk.c
-plugins_iwmxsdk_la_CFLAGS = $(plugin_cflags) @IWMXSDK_CFLAGS@
-plugins_iwmxsdk_la_LIBADD = @IWMXSDK_LIBS@ @GLIB_LIBS@
-plugins_iwmxsdk_la_LDFLAGS = $(plugin_ldflags)
-endif
-
 if IOSPM
 plugin_LTLIBRARIES += plugins/iospm.la
 plugin_objects += $(plugins_iospm_la_OBJECTS)
@@ -272,13 +184,6 @@ plugins_iospm_la_CFLAGS = $(plugin_cflags)
 plugins_iospm_la_LDFLAGS = $(plugin_ldflags)
 endif
 
-if FAKE
-plugin_LTLIBRARIES += plugins/fake.la
-plugin_objects += $(plugins_fake_la_OBJECTS)
-plugins_fake_la_CFLAGS = $(plugin_cflags)
-plugins_fake_la_LDFLAGS = $(plugin_ldflags)
-endif
-
 if OPENCONNECT
 script_PROGRAMS += scripts/openconnect-script
 
@@ -297,29 +202,9 @@ script_PROGRAMS += scripts/openvpn-script
 scripts_openvpn_script_LDADD = @DBUS_LIBS@
 endif
 
-if NTPD
-if NTPD_BUILTIN
-builtin_modules += ntpd
-builtin_sources += plugins/ntpd.c
-builtin_cflags += -DNTPD=\"@NTPD@\"
-else
-plugin_LTLIBRARIES += plugins/ntpd.la
-plugin_objects += $(plugins_ntpd_la_OBJECTS)
-plugins_ntpd_la_CFLAGS = $(plugin_cflags) -DNTPD=\"@NTPD@\"
-plugins_ntpd_la_LDFLAGS = $(plugin_ldflags)
-endif
-endif
-
 if NMCOMPAT
-if NMCOMPAT_BUILTIN
 builtin_modules += nmcompat
 builtin_sources += plugins/nmcompat.c
-else
-plugin_LTLIBRARIES += plugins/nmcompat.la
-plugin_objects += $(plugins_nmcompat_la_OBJECTS)
-plugins_nmcompat_la_CFLAGS = $(plugin_cflags)
-plugins_nmcompat_la_LDFLAGS = $(plugin_ldflags)
-endif
 endif
 
 if TIST
diff --git a/README b/README
index 532e327..6215e96 100644 (file)
--- a/README
+++ b/README
@@ -1,7 +1,7 @@
 Connection Manager
 ******************
 
-Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
 
 
 Functionality and features
@@ -10,17 +10,26 @@ Functionality and features
 The following features are built-in into Connection Manager:
        - Generic plugin infrastructure
        - Device and network abstraction (with basic storage support)
-       - IPv4, routing and DNS configuration
-       - DNS Proxy
+       - IPv4, IPv4-LL (link-local) and DHCP
+       - IPv6, DHCPv6 and 6to4 tunnels
+       - Advanced routing and DNS configuration
+       - Built-in DNS proxy and intelligent caching
+       - Built-in WISPr hotspot logins and portal detection
+       - Time and timezone configuration (manual and automatic with NTP)
+       - Proxy handling (manual and automatic with WPAD)
+       - Tethering support (USB, Bluetooth and WiFi AP mode)
+       - Detailed statistics handling (home and roaming)
 
 Various plugins can be enabled for networking support:
        - Ethernet plugin
-       - WiFi plugin with WEP40/WEP128 and WPA/WPA2 (personal only) support
-       - Bluetooth plugin
+       - WiFi plugin with WEP40/WEP128 and WPA/WPA2 (personal and enterprise)
+       - Bluetooth plugin (using BlueZ)
+       - 2G/3G/4G plugin (using oFono)
 
 Also plugins with additional features are available:
-       - Loopback setup
-       - PolicyKit support
+       - Loopback interface setup
+       - PACrunner proxy handling
+       - PolicyKit authorization support
 
 
 Compilation and installation
@@ -30,6 +39,8 @@ In order to compile Connection Manager you need following software packages:
        - GCC compiler
        - GLib library
        - D-Bus library
+       - IP-Tables library
+       - GnuTLS library (optional)
        - PolicyKit (optional)
 
 To configure run:
@@ -41,50 +52,113 @@ To compile and install run:
        make && make install
 
 
-VPN
-===
+Configuration and options
+=========================
 
-In order to compile pptp and l2tp VPN plugins, you need ppp development
-package.
+For a working system, certain configuration options need to be enabled:
 
-To run l2tp you will need
-       - xl2tpd, http://www.xelerance.com/services/software/xl2tpd
+       --disable-ethernet
 
-To run pptp you will need
-       - pptp client, http://pptpclient.sourceforge.net
+               Disable support for Ethernet network cards
 
-Both l2tp and pptp also need pppd.
+               By default Ethernet technology support is built-in and
+               enabled. This option can be used to build a small daemon
+               for a specific system if Ethernet support is not required.
 
+       --disable-wifi
 
-Configuration and options
-=========================
+               Disable support for WiFi devices
 
-For a working system, certain configuration options need to be enabled:
+               By default WiFi technology support is built-in and
+               enabled. This option can be used to build a small daemon
+               for a specific system if WiFi support is not required.
+
+               It is safe to build a daemon with WiFi support and no
+               running wpa_supplicant. The start of wpa_supplicant is
+               automatically detected and only a runtime dependency. It
+               is not needed to build ConnMan.
 
-       --enable-ethernet
+       --disable-bluetooth
 
-               Enable support for Ethernet network cards
+               Disable support for Bluetooth devices
 
-       --enable-wifi
+               By default Bluetooth technology support is built-in and
+               enabled. This option can be used to build a small daemon
+               for a specific system if Bluetooth support is not required.
 
-               Enable support for WiFi devices (requires wpa_supplicant)
+               It is safe to build a daemon with Bluetooth support and no
+               running bluetoothd. The start of bluetoothd is automatically
+               detected and only a runtime dependency. It is not needed to
+               build ConnMan.
 
+       --disable-ofono
 
-       --enable-bluetooth
+               Disable support for cellular 2G/3G/4G devices
 
-               Enable support for Bluetooth devices (requires BlueZ)
+               By default oFono technology support is built-in and
+               enabled. This option can be used to build a small daemon
+               for a specific system where oFono is not used.
 
-       --enable-loopback
+               It is safe to build a daemon with oFono support and no
+               running ofonod. That start of ofonod is automatically
+               detected and only a runtime dependency. It is not needed to
+               build ConnMan.
 
-               Enable setup of loopback device
+       --disable-dundee
+
+               Disable support for Bluetooth DUN devices
+
+               By default Bluetooth DUN technology (dundee) support is
+               built-in and enabled. This option can be used to build a
+               small daemon for a specific system where dundee is not used.
+
+               It is safe to build a daemon with dundee support and no
+               running dundee. That start of dundee is automatically
+               detected and only a runtime dependency. It is not needed to
+               build ConnMan.
+
+       --disable-pacrunner
+
+               Disable support for PACrunner proxy handling
+
+               By default PACrunner support is built-in and enabled. This
+               option can be used to build a small daemon for a specific
+               system where PACrunner is not used.
+
+               It is safe to build a daemon with PACrunner support and no
+               pacrunner daemon. It will detect and start a PACrunner
+               process if needed at runtime. The presence is not needed
+               to build ConnMan.
+
+       --disable-loopback
+
+               Disable setup of loopback device
 
                For distributions with a really minimal init system and no
                networking scripts this can take care of setting up the
                loopback device and enabling it.
 
-               It is safe to select this option even if networking scripts
-               are in place. It detects an already configured loopback
-               device and leaves it as it is.
+               It is safe to leave this selected even if networking
+               scripts are in place. It detects an already configured
+               loopback device and leaves it as it is.
+
+       --disable-wispr
+
+               Disable support for WISPr hotspot logins
+
+               For systems with really minimal memory requirements, this
+               will disable the support for WISPr hotspot logins. The code
+               for WISPr will be still compiled into the daemon, but its
+               requirement on GnuTLS for secure connections will be lifted.
+
+               The missing GnuTLS support shrinks the memory requirements
+               by about 30% and for systems that are more stationary and do
+               not log into hotspots this might be a better trade off.
+
+               Disabling WISPr support is not disabling the portal detection
+               support. A portal will still be detected, but instead of being
+               asked for login credentials, the request for a browser session
+               will be made through the agent.
 
        --enable-polkit
 
@@ -93,3 +167,66 @@ For a working system, certain configuration options need to be enabled:
                This allows to check every D-Bus access against a security
                policy and so restrict access to certain functionality.
 
+       --enable-nmcompat
+
+               Enable support for NetworkManager compatibility interfaces
+
+               This allows to expose a minimal set of NetworkManager
+               interfaces. It is useful for systems with applications
+               written to use NetworkManager to detect online/offline
+               status and have not yet been converted to use ConnMan.
+
+
+wpa_supplicant configuration
+============================
+
+In order to get wpa_supplicant and Connection Manager working properly
+together you should edit wpa_supplicant .config file and set:
+
+CONFIG_WPS=y
+CONFIG_AP=y
+CONFIG_CTRL_IFACE_DBUS_NEW=y
+
+and, add:
+
+CONFIG_BGSCAN_SIMPLE=y
+
+This last option will enable the support of background scanning while being
+connected, which is necessary when roaming on wifi.
+
+It is recommended to use wpa_supplicant 0.8.x or 1.x or later.
+
+
+VPN
+===
+
+In order to compile pptp and l2tp VPN plugins, you need ppp development
+package.
+
+To run l2tp you will need
+       - xl2tpd, http://www.xelerance.com/services/software/xl2tpd
+
+To run pptp you will need
+       - pptp client, http://pptpclient.sourceforge.net
+
+Both l2tp and pptp also need pppd.
+
+
+OpenVPN
+=======
+
+Up to version 2.2 of OpenVPN, pushing additional routes from the
+server will not always work. Some of the symptons are that additional
+routes will not be set by ConnMan if the uplink is a cellular
+network. While the same setup works well for a WiFi or ethernet
+uplink.
+
+
+Information
+===========
+
+Mailing list:
+       connman@connman.net
+
+For additional information about the project visit ConnMan web site:
+       http://www.connman.net
diff --git a/TODO b/TODO
index 74bbcdc..97bec13 100644 (file)
--- a/TODO
+++ b/TODO
@@ -8,6 +8,7 @@ Background
    lowest complexity. Complexity is a function of both task 'complexity'
    and task 'scope'.
 
+
 Core
 ====
 
@@ -16,7 +17,7 @@ Core
    Priority: High
    Complexity: C4
    Owner: Daniel Wagner <daniel.wagner@bmw-carit.de>
-   Owner: Samuel Ortiz <sameo@linux.intel.com>
+   Owner: Patrik Flykt <patrik.flykt@linux.intel.com>
 
    The session API should provide a connection abstraction in order to
    prioritize applications network accesses, prevent or allow network
@@ -26,37 +27,6 @@ Core
    See http://www.mail-archive.com/connman@connman.net/msg01653.html
 
 
-- DNS caching
-
-   Priority: Low
-   Complexity: C4
-
-   A simple initial implementation would see ConnMan's dnsproxy
-   caching the DNS record based on their TTL.
-
-
-- Power management
-
-   Priority: Medium
-   Complexity: C4
-   Owner: Samuel Ortiz <sameo@linux.intel.com>
-
-   Implement a simple device pm hook that ConnMan's core code would
-   use whenever it decides to put devices in power save mode. Although
-   the kernel runtime power management code should take care of that,
-   not all driver (especially WiFi ones) implement runtime PM hooks.
-
-
-- IP ranges allocation and check
-
-   Priority: High
-   Complexity: C2
-
-   For both tethering and private networks, but also to detect invalid
-   static IP configurations, we need to have a core IP range layer
-   that manages all currently used IP blocks.
-
-
 - Personal firewall
 
    Priority: Low
@@ -76,23 +46,18 @@ Core
    security integration.
 
 
+- Favorite service migration removal
 
-WiFi
-====
-
-- Ad-Hoc support
-
-   Priority: Medium
-   Complexity: C2
-   Owner: Samuel Ortiz <sameo@linux.intel.com>
-
+  Priority: Medium
+  Complexity: C1
+  When: 12/2012
 
-- Fast Connect
+  Remove service migration code that moves services from default.profile
+  to the current directory-based structure.
 
-   Priority: Low
-   Complexity: C4
-   Owner: Samuel Ortiz <sameo@linux.intel.com>
 
+WiFi
+====
 
 - EAP-AKA/SIM
 
@@ -118,16 +83,16 @@ WiFi
    Complexity: C2
 
 
+- Previous WPS pin code sending
 
-Bluetooth
-=========
+  Priority: Medium
+  Complexity: C2
 
-- DUN client
+  Provide previous WPS pin code in PreviousPassphrase field if WPS was used.
 
-   Priority: Low
-   Complexity: C4
-   Owner: Mario Tokarz <mario.tokarz@bmw-carit.de>
 
+Bluetooth
+=========
 
 
 Cellular
@@ -139,18 +104,32 @@ VPN
 
 - IPsec
 
+   Priority: Medium
+   Complexity: C4
+   Owner: Jukka Rissanen <jukka.rissanen@linux.intel.com>
+
+
+Tools
+=====
+
+- Command line tool
+
    Priority: Low
    Complexity: C4
+   Owner: Patrik Flykt <patrik.flykt@linux.intel.com>
+
+   For platforms not running python, it could prove useful to provide them
+   with a native single binary command line tool.
 
 
-- Split tunnelling
+User Interface
+==============
+
+- GNOME3 UI
 
    Priority: Low
-   Complexity: C8
-   Dependencies: Core:Private networks
+   Complexity: C4
+   Owner: Alok Barsode <alok.barsode@linux.intel.com>
 
-   The current VPN support puts the VPN interface at the top of the
-   service list, giving VPNs the default route. When doing split
-   tunneling, the system routes packet to the VPN interface for
-   private IPs, while going through the default interface for the rest
-   of the traffic.
+   A GNOME3 shell user interface would make it easier for mainstream distros
+   users to use ConnMan.
diff --git a/autogen.sh b/autogen.sh
deleted file mode 100755 (executable)
index 4efb58a..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-aclocal
-libtoolize
-autoheader
-autoconf
-automake --add-missing
index 6719436..4a88d6e 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
index 2bf7e17..fa69daa 100644 (file)
@@ -1,8 +1,8 @@
 AC_PREREQ(2.60)
-AC_INIT(connman, 0.78.4)
+AC_INIT(connman, 1.3)
 
 AM_INIT_AUTOMAKE([foreign subdir-objects color-tests])
-AM_CONFIG_HEADER(config.h)
+AC_CONFIG_HEADERS([config.h])
 
 m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
 
@@ -23,7 +23,7 @@ AC_PROG_CC
 AM_PROG_CC_C_O
 AC_PROG_CC_PIE
 AC_PROG_INSTALL
-AM_PROG_MKDIR_P
+AC_PROG_MKDIR_P
 
 m4_define([_LT_AC_TAGCONFIG], [])
 m4_ifdef([AC_LIBTOOL_TAGS], [AC_LIBTOOL_TAGS([])])
@@ -34,7 +34,7 @@ AC_PROG_LIBTOOL
 AC_ARG_ENABLE(optimization, AC_HELP_STRING([--disable-optimization],
                        [disable code optimization through compiler]), [
        if (test "${enableval}" = "no"); then
-               CFLAGS="$CFLAGS -O0"
+               CFLAGS="$CFLAGS -O0 -U_FORTIFY_SOURCE"
        fi
 ])
 
@@ -59,29 +59,7 @@ AC_ARG_ENABLE(pie, AC_HELP_STRING([--enable-pie],
 
 AC_ARG_ENABLE(threads,
        AC_HELP_STRING([--enable-threads], [enable threading support]),
-                       [enable_threads=${enableval}], [enable_threads="yes"])
-
-AC_ARG_ENABLE(ethernet,
-       AC_HELP_STRING([--enable-ethernet], [enable Ethernet support]),
-                       [enable_ethernet=${enableval}], [enable_ethernet="yes"])
-AM_CONDITIONAL(ETHERNET, test "${enable_ethernet}" != "no")
-AM_CONDITIONAL(ETHERNET_BUILTIN, test "${enable_ethernet}" = "builtin")
-
-AC_ARG_ENABLE(wifi,
-       AC_HELP_STRING([--enable-wifi], [enable WiFi support]),
-                       [enable_wifi=${enableval}], [enable_wifi="yes"])
-if (test "${enable_wifi}" != "no"); then
-       AC_PATH_PROG(WPASUPPLICANT, [wpa_supplicant], [],
-                                               $PATH:/sbin:/usr/sbin)
-fi
-AM_CONDITIONAL(WIFI, test "${enable_wifi}" != "no")
-AM_CONDITIONAL(WIFI_BUILTIN, test "${enable_wifi}" = "builtin")
-
-AC_ARG_ENABLE(bluetooth,
-       AC_HELP_STRING([--enable-bluetooth], [enable Bluetooth support]),
-                       [enable_bluetooth=${enableval}], [enable_bluetooth="no"])
-AM_CONDITIONAL(BLUETOOTH, test "${enable_bluetooth}" != "no")
-AM_CONDITIONAL(BLUETOOTH_BUILTIN, test "${enable_bluetooth}" = "builtin")
+                       [enable_threads=${enableval}], [enable_threads="no"])
 
 AC_ARG_ENABLE(hh2serial-gps,
        AC_HELP_STRING([--enable-hh2serial-gps], [enable hh2serial GPS support]),
@@ -89,29 +67,20 @@ AC_ARG_ENABLE(hh2serial-gps,
 AM_CONDITIONAL(HH2SERIAL_GPS, test "${enable_hh2serial_gps}" != "no")
 AM_CONDITIONAL(HH2SERIAL_GPS_BUILTIN, test "${enable_hh2serial_gps}" = "builtin")
 
-AC_ARG_ENABLE(ofono,
-       AC_HELP_STRING([--enable-ofono], [enable oFono support]),
-                       [enable_ofono=${enableval}], [enable_ofono="no"])
-AM_CONDITIONAL(OFONO, test "${enable_ofono}" != "no")
-AM_CONDITIONAL(OFONO_BUILTIN, test "${enable_ofono}" = "builtin")
-
-AC_ARG_ENABLE(telephony,
-       AC_HELP_STRING([--enable-telephony], [enable Telephony support]),
-                       [enable_telephony=${enableval}], [enable_telephony="yes"])
-AM_CONDITIONAL(TELEPHONY, test "${enable_telephony}" != "no")
-AM_CONDITIONAL(TELEPHONY_BUILTIN, test "${enable_telephony}" = "builtin")
-
-AC_ARG_ENABLE(tizen-ext, AC_HELP_STRING([--enable-tizen-ext],
-                       [enable TIZEN extensions]), [
-       if (test "${enableval}" = "yes"); then
-               CFLAGS="$CFLAGS -DTIZEN_EXT"
-               PKG_CHECK_MODULES(IPTC, libiptc, dummy=yes,
-                       AC_MSG_ERROR(libiptc is required for tizen-ext))
-               IPTC_LIBS="$IPTC_LIBS -lip4tc"
-               AC_SUBST(IPTC_CFLAGS)
-               AC_SUBST(IPTC_LIBS)
-       fi
-])
+AC_ARG_ENABLE(tizen-ext,
+       AC_HELP_STRING([--enable-tizen-ext], [enable TIZEN extensions]),
+                       [if (test "${enableval}" = "yes"); then
+                               CFLAGS="$CFLAGS -DTIZEN_EXT"
+                               LIBS="$LIBS -lsmack"
+                       fi])
+
+AC_ARG_ENABLE(tizen-rtc-timer,
+       AC_HELP_STRING([--enable-tizen-rtc-timer], [enable TIZEN RTC timer]),
+                       [if (test "${enableval}" = "yes"); then
+                               CFLAGS="$CFLAGS -DTIZEN_RTC_TIMER"
+                               [enable_tizen_rtc_timer="yes"]
+                       fi])
+AM_CONDITIONAL(TIZEN_RTC, test "${enable_tizen_rtc_timer}" = "yes")
 
 AC_ARG_WITH(openconnect, AC_HELP_STRING([--with-openconnect=PROGRAM],
         [specify location of openconnect binary]), [path_openconnect=${withval}])
@@ -217,38 +186,6 @@ fi
 AM_CONDITIONAL(PPTP, test "${enable_pptp}" != "no")
 AM_CONDITIONAL(PPTP_BUILTIN, test "${enable_pptp}" = "builtin")
 
-AC_ARG_ENABLE(loopback,
-       AC_HELP_STRING([--enable-loopback], [enable loopback support]),
-                       [enable_loopback=${enableval}], [enable_loopback="yes"])
-if (test "${enable_loopback}" != "no"); then
-       AC_CHECK_HEADERS(sys/inotify.h, dummy=yes,
-                       AC_MSG_ERROR(inotify header files are required))
-
-       AC_CHECK_LIB(c, inotify_init, dummy=yes,
-                       AC_MSG_ERROR(inotify library support is required))
-fi
-AM_CONDITIONAL(LOOPBACK, test "${enable_loopback}" != "no")
-AM_CONDITIONAL(LOOPBACK_BUILTIN, test "${enable_loopback}" = "builtin")
-
-AC_ARG_ENABLE(pacrunner,
-       AC_HELP_STRING([--enable-pacrunner], [enable PAC runner support]),
-                       [enable_pacrunner=${enableval}], [enable_pacrunner="no"])
-
-AM_CONDITIONAL(PACRUNNER, test "${enable_pacrunner}" != "no")
-AM_CONDITIONAL(PACRUNNER_BUILTIN, test "${enable_pacrunner}" = "builtin")
-
-AC_ARG_ENABLE(google,
-       AC_HELP_STRING([--enable-google], [enable Google Public DNS support]),
-                       [enable_google=${enableval}], [enable_google="no"])
-AM_CONDITIONAL(GOOGLE, test "${enable_google}" != "no")
-AM_CONDITIONAL(GOOGLE_BUILTIN, test "${enable_google}" = "builtin")
-
-AC_ARG_ENABLE(meego,
-       AC_HELP_STRING([--enable-meego], [enable MeeGo features support]),
-                       [enable_meego=${enableval}], [enable_meego="no"])
-AM_CONDITIONAL(MEEGO, test "${enable_meego}" != "no")
-AM_CONDITIONAL(MEEGO_BUILTIN, test "${enable_meego}" = "builtin")
-
 AC_CHECK_HEADERS(resolv.h, dummy=yes,
        AC_MSG_ERROR(resolver header files are required))
 AC_CHECK_LIB(resolv, ns_initparse, dummy=yes, [
@@ -262,92 +199,10 @@ AC_CHECK_FUNC(signalfd, dummy=yes,
 AC_CHECK_LIB(dl, dlopen, dummy=yes,
                        AC_MSG_ERROR(dynamic linking loader is required))
 
-AC_ARG_WITH(iwmxsdk, AC_HELP_STRING([--with-iwmxsdk=PATH],
-                                               [path to Intel WiMAX SDK]),
-                               [pkgconfig_iwmxsdk=${withval}/lib/pkgconfig])
-
-AC_ARG_ENABLE(iwmx, AC_HELP_STRING([--enable-iwmx],
-               [enable Intel WiMAX support]), [enable_iwmx=${enableval}])
-if (test "${enable_iwmx}" = "yes"); then
-       enable_threads="yes"
-       export PKG_CONFIG_PATH="${pkgconfig_iwmxsdk}"
-       PKG_CHECK_MODULES(IWMXSDK, libiWmxSdk-0, dummy=yes,
-                               AC_MSG_ERROR(Intel WiMAX SDK is required))
-       PKG_CONFIG_PATH=""
-       AC_SUBST(IWMXSDK_CFLAGS)
-       AC_SUBST(IWMXSDK_LIBS)
-
-        # Fix API compat breakage from 1.4 to 1.5...
-        CPPFLAGS_save=$CPPFLAGS
-        CPPFLAGS="$IWMXSDK_CFLAGS $CPPFLAGS"
-        AH_TEMPLATE([HAVE_IWMXSDK_STATUS_IDLE],
-                    [WIMAX_API_DEVICE_STATUS_Connection_Idle is present])
-        AC_CHECK_DECL(WIMAX_API_DEVICE_STATUS_Connection_Idle,
-                      [AC_DEFINE([HAVE_IWMXSDK_STATUS_IDLE], [1], [])],
-                      [],
-                      [[#include <WiMaxType.h>]])
-
-        AH_TEMPLATE([HAVE_WIMAX_API_DEVICE_ID],
-                    [WIMAX_API_DEVICE_ID is present])
-        AC_CHECK_TYPE(WIMAX_API_DEVICE_ID,
-                      [AC_DEFINE([HAVE_WIMAX_API_DEVICE_ID], [1], [])],
-                      [],
-                      [[#include <WiMaxType.h>]])
-
-        AH_TEMPLATE([HAVE_WIMAX_API_HW_DEVICE_ID],
-                    [WIMAX_API_HW_DEVICE_ID is present])
-        AC_CHECK_TYPE(WIMAX_API_HW_DEVICE_ID,
-                      [AC_DEFINE([HAVE_WIMAX_API_HW_DEVICE_ID], [1], [])],
-                      [],
-                      [[#include <WiMaxType.h>]])
-
-        AH_TEMPLATE([HAVE_WIMAX_API_NSP_INFO_EX],
-                    [WIMAX_API_NSP_INFO_EX is present])
-        AC_CHECK_TYPE(WIMAX_API_NSP_INFO_EX,
-                      [AC_DEFINE([HAVE_WIMAX_API_NSP_INFO_EX], [1], [])],
-                      [],
-                      [[#include <WiMaxType.h>] 
-                       [#include <WiMaxTypesEx.h>]])
-
-        AH_TEMPLATE([HAVE_WIMAX_API_CONNECTED_NSP_INFO],
-                    [WIMAX_API_CONNECTED_NSP_INFO is present])
-        AC_CHECK_TYPE(WIMAX_API_CONNECTED_NSP_INFO,
-                      [AC_DEFINE([HAVE_WIMAX_API_CONNECTED_NSP_INFO], [1], [])],
-                      [],
-                      [[#include <WiMaxType.h>]])
-
-        CPPFLAGS=$CPPFLAGS_save
-fi
-AM_CONDITIONAL(IWMX, test "${enable_iwmx}" = "yes")
-
 AC_ARG_ENABLE(iospm, AC_HELP_STRING([--enable-iospm],
                [enable Intel OSPM support]), [enable_iospm=${enableval}])
 AM_CONDITIONAL(IOSPM, test "${enable_iospm}" = "yes")
 
-AC_ARG_WITH(ntpd, AC_HELP_STRING([--with-ntpd=PROGRAM],
-       [specify ntpd binary location]), [path_ntpd=${withval}])
-
-AC_ARG_ENABLE(ntpd,
-       AC_HELP_STRING([--enable-ntpd], [enable ntpd support]),
-                       [enable_ntpd=${enableval}], [enable_ntpd="no"])
-
-if (test "${enable_ntpd}" != "no"); then
-       if (test -z "${path_ntpd}"); then
-               AC_PATH_PROG(NTPD, [ntpd], [], $PATH:/sbin:/usr/sbin)
-       else
-               NTPD="${path_ntpd}"
-               AC_SUBST(NTPD)
-       fi
-fi
-AM_CONDITIONAL(NTPD, test "${enable_ntpd}" != "no")
-AM_CONDITIONAL(NTPD_BUILTIN, test "${enable_ntpd}" = "builtin")
-
-AC_ARG_ENABLE(nmcompat,
-       AC_HELP_STRING([--enable-nmcompat], [enable nmcompat support]),
-                       [enable_nmcompat=${enableval}], [enable_nmcompat="no"])
-AM_CONDITIONAL(NMCOMPAT, test "${enable_nmcompat}" != "no")
-AM_CONDITIONAL(NMCOMPAT_BUILTIN, test "${enable_nmcompat}" = "builtin")
-
 AC_ARG_ENABLE(tist,
        AC_HELP_STRING([--enable-tist], [enable TI Shared Transport support]),
                        [enable_tist=${enableval}], [enable_tist="no"])
@@ -365,8 +220,8 @@ fi
 
 AC_DEFINE_UNQUOTED([STATS_MAX_FILE_SIZE], (${stats_max_file_size}), [Maximal size of a statistics round robin file])
 
-PKG_CHECK_MODULES(GLIB, glib-2.0 >= 2.22, dummy=yes,
-                               AC_MSG_ERROR(GLib >= 2.22 is required))
+PKG_CHECK_MODULES(GLIB, glib-2.0 >= 2.28, dummy=yes,
+                               AC_MSG_ERROR(GLib >= 2.28 is required))
 AC_SUBST(GLIB_CFLAGS)
 AC_SUBST(GLIB_LIBS)
 
@@ -378,21 +233,8 @@ if (test "${enable_threads}" = "yes"); then
        GLIB_LIBS="$GLIB_LIBS $GTHREAD_LIBS"
 fi
 
-PKG_CHECK_MODULES(DBUS, dbus-1 >= 1.0, dummy=yes,
-                               AC_MSG_ERROR(D-Bus >= 1.0 is required))
-saved_CFLAGS="$CFLAGS"
-saved_LIBS="$LIBS"
-CFLAGS="$CFLAGS $DBUS_CFLAGS"
-LIBS="$LIBS $DBUS_LIBS"
-AC_CHECK_LIB(dbus-1, dbus_watch_get_unix_fd, dummy=yes,
-       AC_DEFINE(NEED_DBUS_WATCH_GET_UNIX_FD, 1,
-               [Define to 1 if you need the dbus_watch_get_unix_fd() function.]))
-AC_CHECK_LIB(dbus-1, dbus_connection_can_send_type, dummy=yes,
-       AC_DEFINE(NEED_DBUS_CONNECTION_CAN_SEND_TYPE, 1,
-               [Define to 1 if you need the dbus_connection_can_send_type() function.]
-))
-CFLAGS="$saved_CFLAGS"
-LIBS="$saved_LIBS"
+PKG_CHECK_MODULES(DBUS, dbus-1 >= 1.4, dummy=yes,
+                               AC_MSG_ERROR(D-Bus >= 1.4 is required))
 AC_SUBST(DBUS_CFLAGS)
 AC_SUBST(DBUS_LIBS)
 
@@ -430,8 +272,17 @@ PKG_CHECK_MODULES(XTABLES, xtables, dummy=yes,
 AC_SUBST(XTABLES_CFLAGS)
 AC_SUBST(XTABLES_LIBS)
 
-AC_ARG_ENABLE(polkit,
-       AC_HELP_STRING([--enable-polkit], [enable PolicyKit support]),
+AC_ARG_ENABLE(test, AC_HELP_STRING([--enable-test],
+               [enable test/example scripts]), [enable_test=${enableval}])
+AM_CONDITIONAL(TEST, test "${enable_test}" = "yes")
+
+AC_ARG_ENABLE(nmcompat, AC_HELP_STRING([--enable-nmcompat],
+                               [enable Network Manager support]),
+                       [enable_nmcompat=${enableval}], [enable_nmcompat="no"])
+AM_CONDITIONAL(NMCOMPAT, test "${enable_nmcompat}" != "no")
+
+AC_ARG_ENABLE(polkit, AC_HELP_STRING([--enable-polkit],
+                               [enable PolicyKit support]),
                        [enable_polkit=${enableval}], [enable_polkit="no"])
 if (test "${enable_polkit}" != "no"); then
        POLKIT_DATADIR="`$PKG_CONFIG --variable=actiondir polkit`"
@@ -442,43 +293,92 @@ if (test "${enable_polkit}" != "no"); then
        AC_SUBST(POLKIT_DATADIR)
 fi
 AM_CONDITIONAL(POLKIT, test "${enable_polkit}" != "no")
-AM_CONDITIONAL(POLKIT_BUILTIN, test "${enable_polkit}" = "builtin")
 
-AC_ARG_ENABLE(client, AC_HELP_STRING([--enable-client],
-               [enable command line client]), [enable_client=${enableval}])
-AM_CONDITIONAL(CLIENT, test "${enable_client}" = "yes")
+AC_ARG_ENABLE(loopback, AC_HELP_STRING([--disable-loopback],
+                               [disable loopback support]),
+                                       [enable_loopback=${enableval}])
+AM_CONDITIONAL(LOOPBACK, test "${enable_loopback}" != "no")
+
+AC_ARG_ENABLE(ethernet, AC_HELP_STRING([--disable-ethernet],
+                               [disable Ethernet support]),
+                                       [enable_ethernet=${enableval}])
+AM_CONDITIONAL(ETHERNET, test "${enable_ethernet}" != "no")
+
+AC_ARG_ENABLE(wifi, AC_HELP_STRING([--disable-wifi],
+                               [disable WiFi support]),
+                                       [enable_wifi=${enableval}])
+AM_CONDITIONAL(WIFI, test "${enable_wifi}" != "no")
+
+AC_ARG_ENABLE(bluetooth, AC_HELP_STRING([--disable-bluetooth],
+                               [disable Bluetooth support]),
+                                       [enable_bluetooth=${enableval}])
+AM_CONDITIONAL(BLUETOOTH, test "${enable_bluetooth}" != "no")
+
+AC_ARG_ENABLE(telephony, AC_HELP_STRING([--disable-telephony],
+                               [disable telephony support]),
+                                       [enable_telephony=${enableval}])
+AM_CONDITIONAL(TELEPHONY, test "${enable_telephony}" != "no")
+
+AC_ARG_ENABLE(ofono, AC_HELP_STRING([--disable-ofono],
+                               [disable oFono support]),
+                                       [enable_ofono=${enableval}])
+AM_CONDITIONAL(OFONO, test "${enable_ofono}" != "no")
+
+AC_ARG_ENABLE(dundee, AC_HELP_STRING([--disable-dundee],
+                               [disable dundee support (Bluetooth DUN)]),
+                                       [enable_dundee=${enableval}])
+AM_CONDITIONAL(DUNDEE, test "${enable_dundee}" != "no")
+
+AC_ARG_ENABLE(pacrunner, AC_HELP_STRING([--disable-pacrunner],
+                               [disable PACrunner support]),
+                                       [enable_pacrunner=${enableval}])
+AM_CONDITIONAL(PACRUNNER, test "${enable_pacrunner}" != "no")
+
+AC_ARG_ENABLE(wispr, AC_HELP_STRING([--disable-wispr],
+                               [disable WISPr support]),
+                                       [enable_wispr=${enableval}])
+AM_CONDITIONAL(WISPR, test "${enable_wispr}" != "no")
+
+AC_ARG_ENABLE(tools, AC_HELP_STRING([--disable-tools],
+                               [disable testing tools]),
+                                       [enable_tools=${enableval}])
+AM_CONDITIONAL(TOOLS, test "${enable_tools}" != "no")
 
-AC_ARG_ENABLE(tools, AC_HELP_STRING([--enable-tools],
-               [enable testing tools]), [enable_tools=${enableval}])
-if (test "${enable_tools}" = "yes"); then
+AC_ARG_ENABLE(client, AC_HELP_STRING([--disable-client],
+                               [disable command line client]),
+                                       [disable_client=${enableval}])
+AM_CONDITIONAL(CLIENT, test "${enable_client}" != "no")
+
+if (test "${enable_wispr}" != "no"); then
        PKG_CHECK_MODULES(GNUTLS, gnutls, dummy=yes,
                                AC_MSG_ERROR(GnuTLS library is required))
-       AC_SUBST(GNUTLS_CFLAGS)
-       AC_SUBST(GNUTLS_LIBS)
-
-       AC_CHECK_HEADERS(linux/if_alg.h, dummy=yes,
-               AC_MSG_ERROR(User-space algorithm header files are required))
+else
+       GNUTLS_CFLAGS=""
+       GNUTLS_LIBS=""
 fi
-AM_CONDITIONAL(TOOLS, test "${enable_tools}" = "yes")
+AC_SUBST(GNUTLS_CFLAGS)
+AC_SUBST(GNUTLS_LIBS)
 
-AC_ARG_ENABLE(test, AC_HELP_STRING([--enable-test],
-               [enable test/example scripts]), [enable_test=${enableval}])
-AM_CONDITIONAL(TEST, test "${enable_test}" = "yes")
+if (test "${enable_loopback}" != "no"); then
+       AC_CHECK_HEADERS(sys/inotify.h, dummy=yes,
+                       AC_MSG_ERROR(inotify header files are required))
+
+       AC_CHECK_LIB(c, inotify_init, dummy=yes,
+                       AC_MSG_ERROR(inotify library support is required))
+fi
 
-AC_ARG_ENABLE(fake, AC_HELP_STRING([--enable-fake],
-               [enable fake device support]), [enable_fake=${enableval}])
-AM_CONDITIONAL(FAKE, test "${enable_fake}" = "yes")
-
-AC_ARG_ENABLE(capng, AC_HELP_STRING([--enable-capng],
-               [enable capabilities support]), [enable_capng=${enableval}])
-if (test "${enable_capng}" = "yes"); then
-       PKG_CHECK_MODULES(CAPNG, libcap-ng, dummy=yes,
-                               AC_MSG_ERROR(Capabilities library is required))
-       AC_SUBST(CAPNG_CFLAGS)
-       AC_SUBST(CAPNG_LIBS)
-       AC_DEFINE(HAVE_CAPNG, 1, [Define to 1 if you have capabilities library.])
+if (test "${enable_wifi}" != "no"); then
+       AC_PATH_PROG(WPASUPPLICANT, [wpa_supplicant], [],
+                                               $PATH:/sbin:/usr/sbin)
 fi
 
+AC_ARG_ENABLE(linklocaladdr, AC_HELP_STRING([--disable-linklocaladdr],
+                       [disable link local address when dhcp failed]), [
+       if (test "${enableval}" = "no"); then
+               CFLAGS="$CFLAGS -DDISABLE_LINK_LOCAL_ADDR"
+       fi
+])
+
 AC_ARG_ENABLE(datafiles, AC_HELP_STRING([--disable-datafiles],
                        [don't install configuration and data files]),
                                        [enable_datafiles=${enableval}])
diff --git a/connman.manifest b/connman.manifest
new file mode 100644 (file)
index 0000000..e8b8e16
--- /dev/null
@@ -0,0 +1,174 @@
+<manifest>
+       <define>
+               <domain name="connman"/>
+               <provide>
+                       <label name="connman::get"/>
+                       <label name="connman::set"/>
+                       <label name="connman::profile"/>
+               </provide>
+               <request>
+                       <smack request="system::use_internet" type="rw"/>
+                       <smack request="dbus" type="rwx"/>
+                       <smack request="net-config" type="rwx"/>
+                       <smack request="wpasupplicant" type="rwx"/>
+                       <smack request="telephony_framework::api_private" type="rwx"/>
+                       <smack request="telephony_framework::api_ps_public" type="rwx"/>
+                       <smack request="telephony_framework::api_ps_private" type="rwx"/>
+                       <smack request="telephony_framework::api_ps_profile" type="rwx"/>
+               </request>
+               <permit>
+                       <smack permit="dbus" type="rwx"/>
+                       <smack permit="system::use_internet" type="rw"/>
+               </permit>
+       </define>
+       <assign>
+               <filesystem path="/usr/lib/systemd/system/connman.service" label="_"/>
+               <filesystem path="/usr/lib/systemd/system/multi-user.target.wants/connman.service" label="_"/>
+               <filesystem path="/usr/share/dbus-1/services/net.connman.service" label="_"/>
+               <filesystem path="/usr/sbin/connman.service" label="_" exec_label="none"/>
+               <filesystem path="/usr/share/license/connman" label="_"/>
+               <dbus name="net.connman" own="connman" bus="system">
+                       <node name="/">
+                       <interface name="net.connman.Manager">
+                               <annotation name="net.connman.smack" value="connman"/>
+                               <method name="GetTechnologies">
+                                       <annotation name="net.connman.smack" value="connman::get"/>
+                               </method>
+                               <method name="GetProperties">
+                                       <annotation name="net.connman.smack" value="connman::get"/>
+                               </method>
+                               <method name="GetServices">
+                                       <annotation name="net.connman.smack" value="connman::get"/>
+                               </method>
+                       </interface>
+                       <interface name="net.connman.Service">
+                               <annotation name="net.connman.smack" value="connman"/>
+                               <method name="Connect">
+                                       <annotation name="net.connman.smack" value="connman::set"/>
+                               </method>
+                               <method name="Disconnect">
+                                       <annotation name="net.connman.smack" value="connman::set"/>
+                               </method>
+                               <method name="SetProperty">
+                                       <annotation name="net.connman.smack" value="connman::profile"/>
+                               </method>
+                               <method name="GetProperties">
+                                       <annotation name="net.connman.smack" value="connman::get"/>
+                               </method>
+                               <method name="Remove">
+                                       <annotation name="net.connman.smack" value="connman::profile"/>
+                               </method>
+                               <signal name="PropertyChanged">
+                                       <annotation name="net.connman.smack" value="connman::get"/>
+                               </signal>
+                       </interface>
+                       <interface name="net.connman.Technology">
+                               <annotation name="net.connman.smack" value="connman"/>
+                               <method name="Scan">
+                                       <annotation name="net.connman.smack" value="connman::set"/>
+                               </method>
+                       </interface>
+                       <interface name="net.connman.Clock">
+                               <annotation name="net.connman.smack" value="connman"/>
+                       </interface>
+                       <interface name="net.connman.Provider">
+                               <annotation name="net.connman.smack" value="connman"/>
+                       </interface>
+                       <interface name="net.connman.Session">
+                               <annotation name="net.connman.smack" value="connman"/>
+                       </interface>
+                       <interface name="net.connman.Agent">
+                               <annotation name="net.connman.smack" value="connman"/>
+                       </interface>
+                       <interface name="net.connman.Error">
+                               <annotation name="net.connman.smack" value="connman"/>
+                       </interface>
+                       <interface name="net.connman.Notification">
+                               <annotation name="net.connman.smack" value="connman"/>
+                       </interface>
+                       <interface name="net.connman.Debug">
+                               <annotation name="net.connman.smack" value="connman"/>
+                       </interface>
+                       <interface name="net.connman.Counter">
+                               <annotation name="net.connman.smack" value="connman"/>
+                       </interface>
+                       <interface name="net.connman.Task">
+                               <annotation name="net.connman.smack" value="connman"/>
+                       </interface>
+                       </node>
+               </dbus>
+               <dbus name="net.connman" own="connman" bus="system">
+                       <node name="/net/connman/*">
+                       <interface name="net.connman.Manager">
+                               <annotation name="net.connman.smack" value="connman"/>
+                               <method name="GetTechnologies">
+                                       <annotation name="net.connman.smack" value="connman::get"/>
+                               </method>
+                               <method name="GetProperties">
+                                       <annotation name="net.connman.smack" value="connman::get"/>
+                               </method>
+                               <method name="GetServices">
+                                       <annotation name="net.connman.smack" value="connman::get"/>
+                               </method>
+                       </interface>
+                       <interface name="net.connman.Service">
+                               <annotation name="net.connman.smack" value="connman"/>
+                               <method name="Connect">
+                                       <annotation name="net.connman.smack" value="connman::set"/>
+                               </method>
+                               <method name="Disconnect">
+                                       <annotation name="net.connman.smack" value="connman::set"/>
+                               </method>
+                               <method name="SetProperty">
+                                       <annotation name="net.connman.smack" value="connman::profile"/>
+                               </method>
+                               <method name="GetProperties">
+                                       <annotation name="net.connman.smack" value="connman::get"/>
+                               </method>
+                               <method name="Remove">
+                                       <annotation name="net.connman.smack" value="connman::profile"/>
+                               </method>
+                               <signal name="PropertyChanged">
+                                       <annotation name="net.connman.smack" value="connman::get"/>
+                               </signal>
+                       </interface>
+                       <interface name="net.connman.Technology">
+                               <annotation name="net.connman.smack" value="connman"/>
+                               <method name="Scan">
+                                       <annotation name="net.connman.smack" value="connman::set"/>
+                               </method>
+                       </interface>
+                       <interface name="net.connman.Clock">
+                               <annotation name="net.connman.smack" value="connman"/>
+                       </interface>
+                       <interface name="net.connman.Provider">
+                               <annotation name="net.connman.smack" value="connman"/>
+                       </interface>
+                       <interface name="net.connman.Session">
+                               <annotation name="net.connman.smack" value="connman"/>
+                       </interface>
+                       <interface name="net.connman.Agent">
+                               <annotation name="net.connman.smack" value="connman"/>
+                       </interface>
+                       <interface name="net.connman.Error">
+                               <annotation name="net.connman.smack" value="connman"/>
+                       </interface>
+                       <interface name="net.connman.Notification">
+                               <annotation name="net.connman.smack" value="connman"/>
+                       </interface>
+                       <interface name="net.connman.Debug">
+                               <annotation name="net.connman.smack" value="connman"/>
+                       </interface>
+                       <interface name="net.connman.Counter">
+                               <annotation name="net.connman.smack" value="connman"/>
+                       </interface>
+                       <interface name="net.connman.Task">
+                               <annotation name="net.connman.smack" value="connman"/>
+                       </interface>
+                       </node>
+               </dbus>
+       </assign>
+       <request>
+               <domain name="connman"/>
+       </request>
+</manifest>
diff --git a/debian/changelog b/debian/changelog
deleted file mode 100644 (file)
index 30ce2fc..0000000
+++ /dev/null
@@ -1,854 +0,0 @@
-connman (0.78.4-0slp2+90) unstable; urgency=low
-
-  * Ethernet : add technology driver to ethernet plugin
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.78.4-0slp2+90
-
- -- Jaehyun Kim <jeik01.kim@samsung.com>  Tue, 21 Aug 2012 13:51:37 +0900
-
-connman (0.78.4-0slp2+89) unstable; urgency=low
-
-  * Fix 'unable to connect' issue and reply pending bug
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.78.4-0slp2+89
-
- -- Jaehyun Kim <jeik01.kim@samsung.com>  Thu, 16 Aug 2012 22:39:54 +0900
-
-connman (0.78.4-0slp2+88) unstable; urgency=low
-
-  * Fix compile warnings and some build environment
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.78.4-0slp2+88
-
- -- Danny Jeongseok Seo <s.seo@samsung.com>  Fri, 10 Aug 2012 15:13:37 +0900
-
-connman (0.78.4-0slp2+87) unstable; urgency=low
-
-  * Skip DHCP discovery if server already known
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.78.4-0slp2+87
-
- -- Danny Jeongseok Seo <s.seo@samsung.com>  Wed, 25 Jul 2012 16:17:12 +0900
-
-connman (0.78.4-0slp2+86) unstable; urgency=low
-
-  * Check the technology type of connected service first and reorder service list
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.78.4-0slp2+86
-
- -- Jaehyun Kim <jeik01.kim@samsung.com>  Fri, 20 Jul 2012 20:26:35 +0900
-
-connman (0.78.4-0slp2+85) unstable; urgency=low
-
-  * Support EAP detail information
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.78.4-0slp2+85
-
- -- Danny Jeongseok Seo <s.seo@samsung.com>  Fri, 15 Jun 2012 18:06:38 +0900
-
-connman (0.78.4-0slp2+84) unstable; urgency=low
-
-  * Remove a code that disables dbus auto-activation when sends a dbus message to net-config
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.78.4-0slp2+84
-
- -- Jaehyun Kim <jeik01.kim@samsung.com>  Fri, 08 Jun 2012 20:37:12 +0900
-
-connman (0.78.4-0slp2+83) unstable; urgency=low
-
-  * Fix a bss updated, when Wi-Fi security updated from PSK to OPEN
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.78.4-0slp2+83
-
- -- Danny Jeongseok Seo <s.seo@samsung.com>  Mon, 04 Jun 2012 11:01:53 +0900
-
-connman (0.78.4-0slp2+82) unstable; urgency=low
-
-  * Fix invalid key retries
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.78.4-0slp2+82
-
- -- Danny Jeongseok Seo <s.seo@samsung.com>  Thu, 24 May 2012 17:03:16 +0900
-
-connman (0.78.4-0slp2+81) unstable; urgency=low
-
-  * Fix wifi_scan
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.78.4-0slp2+81
-
- -- Danny Jeongseok Seo <s.seo@samsung.com>  Tue, 22 May 2012 17:32:09 +0900
-
-connman (0.78.4-0slp2+80) unstable; urgency=low
-
-  * Wi-Fi enterprise SIM and AKA
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.78.4-0slp2+80
-
- -- Danny Jeongseok Seo <s.seo@samsung.com>  Sun, 13 May 2012 17:36:37 +0900
-
-connman (0.78.4-0slp2+79) unstable; urgency=low
-
-  * Revise Wi-Fi enterprise to enable PEAP, TLS, TTLS
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.78.4-0slp2+79
-
- -- Danny Jeongseok Seo <s.seo@samsung.com>  Fri, 11 May 2012 10:28:04 +0900
-
-connman (0.78.4-0slp2+78) unstable; urgency=low
-
-  * Fix telephony technology driver
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.78.4-0slp2+78
-
- -- Danny Jeongseok Seo <s.seo@samsung.com>  Wed, 09 May 2012 17:56:57 +0900
-
-connman (0.78.4-0slp2+77) unstable; urgency=low
-
-  * Upgrade ConnMan-stable 0.78.4
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.78.4-0slp2+77
-
- -- Danny Jeongseok Seo <s.seo@samsung.com>  Tue, 17 Apr 2012 20:51:49 +0900
-
-connman (0.77.2-0slp2+76) unstable; urgency=low
-
-  * Update ConnMan init script for Android suplicant
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.77.2-0slp2+76
-
- -- Danny Jeongseok Seo <s.seo@samsung.com>  Tue, 24 Apr 2012 20:28:32 +0900
-
-connman (0.77.2-0slp2+75) unstable; urgency=low
-
-  * Fix a bug which connman can't update MAC address of Wi-Fi
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.77.2-0slp2+75
-
- -- root <jeik01.kim@samsung.com>  Thu, 12 Apr 2012 18:23:04 +0900
-
-connman (0.77.2-0slp2+74) unstable; urgency=low
-
-  * Fix cellular profile add bugs
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.77.2-0slp2+74
-
- -- Jaehyun Kim <jeik01.kim@samsung.com>  Mon, 09 Apr 2012 17:47:31 +0900
-
-connman (0.77.2-0slp2+73) unstable; urgency=low
-
-  * Fix Wi-Fi tethering state bugs
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.77.2-0slp2+73
-
- -- Danny Jeongseok Seo <s.seo@samsung.com>  Mon, 02 Apr 2012 14:14:38 +0900
-
-connman (0.77.2-0slp2+72) unstable; urgency=low
-
-  * Fix default network info update
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.77.2-0slp2+72
-
- -- Danny Jeongseok Seo <s.seo@samsung.com>  Thu, 29 Mar 2012 21:48:36 +0900
-
-connman (0.77.2-0slp2+71) unstable; urgency=low
-
-  * Fix bugs on event handling(flight mode/data allowed/roaming)
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.77.2-0slp2+71
-
- -- Jaehyun Kim <jeik01.kim@samsung.com>  Fri, 23 Mar 2012 19:27:26 +0900
-
-connman (0.77.2-0slp2+70) unstable; urgency=low
-
-  * Enable ConnMan debug log for developers
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.77.2-0slp2+70
-
- -- Danny Jeongseok Seo <s.seo@samsung.com>  Thu, 22 Mar 2012 20:00:15 +0900
-
-connman (0.77.2-0slp2+69) unstable; urgency=low
-
-  * Revise Wi-Fi device scan
-  * If deivce is connecing, do no make scan
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.77.2-0slp2+69
-
- -- Danny Jeongseok Seo <s.seo@samsung.com>  Fri, 16 Mar 2012 17:09:01 +0900
-
-connman (0.77.2-0slp2+68) unstable; urgency=low
-
-  * Fix ConnMan __connman_service_connect_default
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.77.2-0slp2+68
-
- -- Danny Jeongseok Seo <s.seo@samsung.com>  Fri, 16 Mar 2012 16:46:34 +0900
-
-connman (0.77.2-0slp2+67) unstable; urgency=low
-
-  * Revise New Telephony DBus interface name
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.77.2-0slp2+67
-
- -- Danny Jeongseok Seo <s.seo@samsung.com>  Thu, 15 Mar 2012 13:31:17 +0900
-
-connman (0.77.2-0slp2+66) unstable; urgency=low
-
-  * Rename sonet plug-in to telephony plug-in
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.77.2-0slp2+66
-
- -- Danny Jeongseok Seo <s.seo@samsung.com>  Fri, 09 Mar 2012 17:37:20 +0900
-
-connman (0.77.2-0slp2+65) unstable; urgency=low
-
-  * Write log routine without syslogd
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.77.2-0slp2+65
-
- -- Danny Jeongseok Seo <s.seo@samsung.com>  Fri, 09 Mar 2012 16:35:07 +0900
-
-connman (0.77.2-0slp2+64) unstable; urgency=low
-
-  * Revise connection management of Tizen data network to fit New Telepony
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.77.2-0slp2+64
-
- -- Danny Jeongseok Seo <s.seo@samsung.com>  Fri, 09 Mar 2012 11:33:46 +0900
-
-connman (0.77.2-0slp2+63) unstable; urgency=low
-
-  * Change sonet's dbus service name
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.77.2-0slp2+63
-
- -- Sunkey Lee <yuvjjang.lee@samsung.com>  Thu, 09 Feb 2012 10:36:51 +0900
-
-connman (0.77.2-0slp2+62) unstable; urgency=low
-
-  * Add mixed wifi encryption mode to service interface
-  * Lookup a matched service using essid and update it immediately when Manager.ProvisionService is invoked
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.77.2-0slp2+62
-
- -- Jaehyun Kim <jeik01.kim@samsung.com>  Thu, 02 Feb 2012 20:00:10 +0900
-
-connman (0.77.2-0slp2+61) unstable; urgency=low
-
-  * Add a propertiy(wifi encryption mode) to service interface
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.77.2-0slp2+61
-
- -- Jaehyun Kim <jeik01.kim@samsung.com>  Wed, 01 Feb 2012 16:42:26 +0900
-
-connman (0.77.2-0slp2+60) unstable; urgency=low
-
-  * Fix wifi disconnect callback bug
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.77.2-0slp2+60
-
- -- Sunkey Lee <yuvjjang.lee@samsung.com>  Tue, 31 Jan 2012 16:20:46 +0900
-
-connman (0.77.2-0slp2+59) unstable; urgency=low
-
-  * Enable loopback plugin
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.77.2-0slp2+59
-
- -- Sunkey Lee <yuvjjang.lee@samsung.com>  Wed, 25 Jan 2012 14:29:57 +0900
-
-connman (0.77.2-0slp2+58) unstable; urgency=low
-
-  * Modify the way to set dbus access authority
-  * According to security plicy, change to use ID from string
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.77.2-0slp2+58
-
- -- Sanghoon Cho <sanghoon80.cho@samsung.com>  Tue, 17 Jan 2012 17:13:02 +0900
-
-connman (0.77.2-0slp2+57) unstable; urgency=low
-
-  * Fix a disconnect callback crash
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.77.2-0slp2+57
-
- -- Sunkey Lee <yuvjjang.lee@samsung.com>  Thu, 12 Jan 2012 21:50:24 +0900
-
-connman (0.77.2-0slp2+56) unstable; urgency=low
-
-  * Fix a bug
-  * The WPS flag("UseWPS") is not cleared when cancel the wps connection
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.77.2-0slp2+56
-
- -- Sanghoon Cho <sanghoon80.cho@samsung.com>  Thu, 12 Jan 2012 20:36:07 +0900
-
-connman (0.77.2-0slp2+55) unstable; urgency=low
-
-  * Release connman_0.77.2-0slp2+55
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.77.2-0slp2+55
-
- -- Jaehyun Kim <jeik01.kim@samsung.com>  Thu, 12 Jan 2012 11:20:49 +0900
-
-connman (0.77.2-0slp2+54) unstable; urgency=low
-
-  * Release connman_0.77.2-0slp2+54
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.77.2-0slp2+54
-
- -- Sunkey Lee <yuvjjang.lee@samsung.com>  Thu, 22 Dec 2011 17:07:43 +0900
-
-connman (0.77.2-0slp2+53) unstable; urgency=low
-
-  * Release connman_0.77.2-0slp2+53
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.77.2-0slp2+53
-
- -- DongHoo Park <donghoo.park@samsung.com>  Fri, 09 Dec 2011 20:03:37 +0900
-
-connman (0.77.2-0slp2+52) unstable; urgency=low
-
-  * Clear service->pending though connection is stopped before completed
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.77.2-0slp2+52
-
- -- Misun Kim <ms0123.kim@samsung.com>  Wed, 07 Dec 2011 18:52:24 +0900
-
-connman (0.77.2-0slp2+51) unstable; urgency=low
-
-  * Fix a bug which service's state doesn't recover from associating state
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.77.2-0slp2+51
-
- -- Sunkey Lee <yuvjjang.lee@samsung.com>  Wed, 07 Dec 2011 11:01:32 +0900
-
-connman (0.77.2-0slp2+50) unstable; urgency=low
-
-  * Release connman_0.77.2-0slp2+50
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.77.2-0slp2+50
-
- -- Jeongseok Seo <s.seo@samsung.com>  Mon, 05 Dec 2011 23:22:06 +0900
-
-connman (0.77.2-0slp2+49) unstable; urgency=low
-
-  * Release connman_0.77.2-0slp2+49
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.77.2-0slp2+49
-
- -- Jeongseok Seo <s.seo@samsung.com>  Sat, 03 Dec 2011 12:04:44 +0900
-
-connman (0.77.2-0slp2+48) unstable; urgency=low
-
-  * Delete dev package of connman
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.77.2-0slp2+48
-
- -- Howon Jung <howon.jung@samsung.com>  Tue, 29 Nov 2011 14:54:53 +0900
-
-connman (0.77.2-0slp2+47) unstable; urgency=low
-
-  * Create new key-value file when there is no default one
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.77.2-0slp2+47
-
- -- Jeongseok Seo <s.seo@samsung.com>  Mon, 28 Nov 2011 21:34:28 +0900
-
-connman (0.77.2-0slp2+46) unstable; urgency=low
-
-  * Fix the bug which is not able to create modem instance when sonet plugin receives the modem_added dbus-signal
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.77.2-0slp2+46
-
- -- DongHoo Park <donghoo.park@samsung.com>  Thu, 24 Nov 2011 12:57:35 +0900
-
-connman (0.77.2-0slp2+45) unstable; urgency=low
-
-  * Add reference count and scan decision scheme to control Wi-Fi scan for saving power
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.77.2-0slp2+45
-
- -- Jeongseok Seo <s.seo@samsung.com>  Mon, 21 Nov 2011 14:59:28 +0900
-
-connman (0.77.2-0slp2+44) unstable; urgency=low
-
-  * plugin/alwayson.c: Fix crash, that is loosing related service instance when timer is called back
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.77.2-0slp2+44
-
- -- DongHoo Park <donghoo.park@samsung.com>  Tue, 22 Nov 2011 17:12:02 +0900
-
-connman (0.77.2-0slp2+43) unstable; urgency=low
-
-  * src/device.c: Fix the connman crash when the cellular_service_enabled variable is set before creating device instance
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.77.2-0slp2+43
-
- -- DongHoo Park <donghoo.park@samsung.com>  Tue, 22 Nov 2011 13:48:31 +0900
-
-connman (0.77.2-0slp2+42) unstable; urgency=low
-
-  * src/service.c: Patch a valid DBUS reply of synchronous service_disconnect instead of NULL reply
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.77.2-0slp2+42
-
- -- DongHoo Park <donghoo.park@samsung.com>  Mon, 21 Nov 2011 19:57:04 +0900
-
-connman (0.77.2-0slp2+41) unstable; urgency=low
-
-  * plugin/sonet.c: Change the action of IP method type setting
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.77.2-0slp2+41
-
- -- DongHoo Park <donghoo.park@samsung.com>  Fri, 18 Nov 2011 16:38:06 +0900
-
-connman (0.77.2-0slp2+40) unstable; urgency=low
-
-  * Unnecessary sigaction and backtrace of connmand
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.77.2-0slp2+40
-
- -- Jeongseok Seo <s.seo@samsung.com>  Wed, 16 Nov 2011 21:57:43 +0900
-
-connman (0.77.2-0slp2+39) unstable; urgency=low
-
-  * fix the bug which is wrong conditional statement in sonet plug-in
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.77.2-0slp2+39
-
- -- DongHoo Park <donghoo.park@samsung.com>  Wed, 16 Nov 2011 20:49:33 +0900
-
-connman (0.77.2-0slp2+38) unstable; urgency=low
-
-  * not to create ip_config structure when ipv6 address is not available and remove unnecessary patch
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.77.2-0slp2+38
-
- -- DongHoo Park <donghoo.park@samsung.com>  Wed, 16 Nov 2011 14:28:37 +0900
-
-connman (0.77.2-0slp2+37) unstable; urgency=low
-
-  * fix to set default routing table, not to retey in no-link case, dbus crash
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.77.2-0slp2+37
-
- -- DongHoo Park <donghoo.park@samsung.com>  Mon, 14 Nov 2011 18:18:14 +0900
-
-connman (0.77.2-0slp2+36) unstable; urgency=low
-
-  * change connection retry logic
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.77.2-0slp2+36
-
- -- DongHoo Park <donghoo.park@samsung.com>  Fri, 11 Nov 2011 14:25:52 +0900
-
-connman (0.77.2-0slp2+35) unstable; urgency=low
-
-  * connman_dbus_service_property_changed_with_error_cause can get root causes of service state failure
-    without ConnMan agent
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.77.2-0slp2+35
-
- -- Jeongseok Seo <s.seo@samsung.com>  Fri, 04 Nov 2011 18:16:33 +0900
-
-connman (0.77.2-0slp2+34) unstable; urgency=low
-
-  * Fix minor warnings and prevent bugs
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.77.2-0slp2+34
-
- -- Jeongseok Seo <s.seo@samsung.com>  Fri, 04 Nov 2011 16:53:44 +0900
-
-connman (0.77.2-0slp2+33) unstable; urgency=low
-
-  * fix ther return value of timeout function
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.77.2-0slp2+33
-
- -- DongHoo Park <donghoo.park@samsung.com>  Thu, 03 Nov 2011 14:31:40 +0900
-
-connman (0.77.2-0slp2+32) unstable; urgency=low
-
-  * update sonet plug-in
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.77.2-0slp2+32
-
- -- DongHoo Park <donghoo.park@samsung.com>  Thu, 27 Oct 2011 08:26:59 +0900
-
-connman (0.77.2-0slp2+31) unstable; urgency=low
-
-  * __connman_service_lookup_from_index() returns incorrect service pointer
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.77.2-0slp2+31
-
- -- Misun Kim <ms0123.kim@samsung.com>  Mon, 17 Oct 2011 21:08:37 +0900
-
-connman (0.77.2-0slp2+30) unstable; urgency=low
-
-  * don't try auto connection and scan immediately when the service is changed to IDLE state
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.77.2-0slp2+30
-
- -- Misun Kim <ms0123.kim@samsung.com>  Fri, 14 Oct 2011 15:51:23 +0900
-
-connman (0.77.2-0slp2+29) unstable; urgency=low
-
-  * Fix auto_connect failure after remove_service (forgot Wi-Fi profile)
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.77.2-0slp2+29
-
- -- Jeongseok Seo <s.seo@samsung.com>  Wed, 12 Oct 2011 13:05:55 +0900
-
-connman (0.77.2-0slp2+28) unstable; urgency=low
-
-  * Fix auto_connect problem, when device->connections remains it is not triggered
-  * device: clear device->connections and auto_connect checks device state, not device->connections
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.77.2-0slp2+28
-
- -- Jeongseok Seo <s.seo@samsung.com>  Wed, 05 Oct 2011 13:18:56 +0900
-
-connman (0.77.2-0slp2+27) unstable; urgency=low
-
-  * fix network enable signal handler problem
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.77.2-0slp2+27
-
- -- DongHoo Park <donghoo.park@samsung.com>  Tue, 27 Sep 2011 20:17:09 +0900
-
-connman (0.77.2-0slp2+26) unstable; urgency=low
-
-  * ConnMan doesn't care about cellular for auto connecting
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.77.2-0slp2+26
-
- -- Misun Kim <ms0123.kim@samsung.com>  Tue, 27 Sep 2011 11:59:29 +0900
-
-connman (0.77.2-0slp2+25) unstable; urgency=low
-
-  * Remove libnetfilter-queue-dev dependency
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.77.2-0slp2+25
-
- -- JaeHyun Kim <jeik01.kim@samsung.com>  Tue, 27 Sep 2011 10:32:13 +0900
-
-connman (0.77.2-0slp2+24) unstable; urgency=low
-
-  * fix error code num (ISCONN)
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.77.2-0slp2+24
-
- -- DongHoo Park <donghoo.park@samsung.com>  Mon, 26 Sep 2011 20:50:47 +0900
-
-connman (0.77.2-0slp2+23) unstable; urgency=low
-
-  * fix MMS (3g connection) problem
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.77.2-0slp2+23
-
- -- DongHoo Park <donghoo.park@samsung.com>  Mon, 26 Sep 2011 16:07:00 +0900
-
-connman (0.77.2-0slp2+22) unstable; urgency=low
-
-  * Fix pdp activation failure recovery at the very begining
-  * Fix retry timer (unlimited loop)
-  * Fix MMS, WAP auto join problem
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.77.2-0slp2+22
-
- -- Jeongseok Seo <s.seo@samsung.com>  Thu, 22 Sep 2011 21:58:44 +0900
-
-connman (0.77.2-0slp2+21) unstable; urgency=low
-
-  * check connected service type / reorder service list
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.77.2-0slp2+21
-
- -- Misun Kim <ms0123.kim@samsung.com>  Thu, 22 Sep 2011 14:35:03 +0900
-
-connman (0.77.2-0slp2+20) unstable; urgency=low
-
-  * Init script
-  * Arrange SEC_EXT tag
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.77.2-0slp2+20
-
- -- Jeongseok Seo <s.seo@samsung.com>  Wed, 21 Sep 2011 22:35:32 +0900
-
-connman (0.77.2-0slp2+19) unstable; urgency=low
-
-  * Fix RequestScan with specific bearer type
-  * Fix technology enable/disable, fix net.connman.Error.NoCarrier
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.77.2-0slp2+19
-
- -- Jeongseok Seo <s.seo@samsung.com>  Thu, 22 Sep 2011 11:12:09 +0900
-
-connman (0.77.2-0slp2+18) unstable; urgency=low
-
-  * fix default connection problem (unexpected 3g connection)
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.77.2-0slp2+18
-
- -- DongHoo Park <donghoo.park@samsung.com>  Wed, 21 Sep 2011 11:13:37 +0900
-
-connman (0.77.2-0slp2+17) unstable; urgency=low
-
-  * ConnMan upgrade to 0.77.2 commit 162dce08a37b8e09e098ef682bcafd2a7aa155d0
-  * git://github.com/sameo/connman-stable
-  * git://git.kernel.org/?p=network/connman/connman-stable.git
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.77.2-0slp2+17
-
- -- Jeongseok Seo <s.seo@samsung.com>  Mon, 19 Sep 2011 21:00:12 +0900
-
-connman (0.73-0slp2+17) unstable; urgency=low
-
-  * add connection retry timer
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.73-0slp2+17
-
- -- DongHoo Park <donghoo.park@samsung.com>  Fri, 16 Sep 2011 16:15:16 +0900
-
-connman (0.73-0slp2+16) unstable; urgency=low
-
-  * try auto connecting without reference to celluar connection
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.73-0slp2+16
-
- -- Misun Kim <ms0123.kim@samsung.com>  Fri, 16 Sep 2011 15:16:25 +0900
-
-connman (0.73-0slp2+15) unstable; urgency=low
-
-  * Fix serveral ConnMan crashes by Samuel Ortiz
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.73-0slp2+15
-
- -- Jeongseok Seo <s.seo@samsung.com>  Thu, 15 Sep 2011 23:25:36 +0900
-
-connman (0.73-0slp2+14) unstable; urgency=low
-
-  * fix build warnning - pass invalid pointer
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.73-0slp2+14
-
- -- DongHoo Park <donghoo.park@samsung.com>  Wed, 14 Sep 2011 10:59:36 +0900
-
-connman (0.73-0slp2+13) unstable; urgency=low
-
-  * stop scanning in connected state / bug fix - bg scan interval was increased by scan request
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.73-0slp2+13
-
- -- Misun Kim <ms0123.kim@samsung.com>  Wed, 07 Sep 2011 15:38:48 +0900
-
-connman (0.73-0slp2+12) unstable; urgency=low
-
-  * Add scan complete signal
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.73-0slp2+12
-
- -- Sunkey Lee <yuvjjang.lee@samsung.com>  Wed, 31 Aug 2011 15:05:01 +0900
-
-connman (0.73-0slp2+11) unstable; urgency=low
-
-  * add cellular service enabled notifier
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.73-0slp2+11
-
- -- DongHoo Park <donghoo.park@samsung.com>  Tue, 30 Aug 2011 19:19:51 +0900
-
-connman (0.73-0slp2+10) unstable; urgency=low
-
-  * fix to make multiple pdp, add network option changed signal
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.73-0slp2+10
-
- -- DongHoo Park <donghoo.park@samsung.com>  Mon, 29 Aug 2011 17:04:38 +0900
-
-connman (0.73-0slp2+9) unstable; urgency=low
-
-  * add sim init signal and fix duplicate device, network
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.73-0slp2+9
-
- -- DongHoo Park <donghoo.park@samsung.com>  Wed, 24 Aug 2011 23:06:46 +0900
-
-connman (0.73-0slp2+8) unstable; urgency=low
-
-  * add sonet plug-in , always on plug-in, modify device force scan trigger
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.73-0slp2+8
-
- -- DongHoo Park <donghoo.park@samsung.com>  Tue, 23 Aug 2011 10:44:17 +0900
-
-connman (0.73-0slp2+7) unstable; urgency=low
-
-  * Add more APIs to use user-initiated pdp connection management
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.73-0slp2+6
-
- -- Jeongseok Seo <s.seo@samsung.com>  Tue, 23 Aug 2011 09:45:22 +0900
-
-connman (0.73-0slp2+6) unstable; urgency=low
-
-  * System-wide connection management (so-called "one connection")
-  * Git: slp/pkgs/c/connman
-  * Tag: connman_0.73-0slp2+6
-
- -- Jeongseok Seo <s.seo@samsung.com>  Thu, 18 Aug 2011 16:11:37 +0900
-
-connman (0.73-0slp2+5) unstable; urgency=low
-
-  * apply samsung bg scan & auto power on
-
- -- Misun Kim <ms0123.kim@samsung.com>  Fri, 27 Jul 2011 15:54:27 +0900
-
-connman (0.73-0slp2+4) unstable; urgency=low
-
-  * apply always-on feature in modman plug-in
-
- -- DongHoo Park <donghoo.park@samsung.com>  Thu, 21 Jul 2011 20:54:32 +0900
-
-connman (0.73-0slp2+3) unstable; urgency=low
-
-  * enable log, change modman plug-in, add always on plug-in
-
- -- DongHoo Park <donghoo.park@samsung.com>  Thu, 07 Jul 2011 08:06:24 +0900
-
-connman (0.73-0slp2+2) unstable; urgency=low
-
-  * Delete unnecessary codes
-
- -- Jeongseok Seo <s.seo@samsung.com>  Mon, 27 Jun 2011 15:12:48 +0900
-
-connman (0.73-0slp2+1) unstable; urgency=low
-
-  * Version up
-
- -- DongHoo Park <donghoo.park@samsung.com>  Mon, 13 Jun 2011 21:19:57 +0900
-
-connman (0.64-0slp2+4) unstable; urgency=low
-
-  * change local state directory
-
- -- JaeHyun Kim <jeik01.kim@samsung.com>  Wed, 27 Apr 2011 17:19:59 +0900
-
-connman (0.64-0slp2+3) unstable; urgency=low
-
-  * reorder service list
-
- -- Jongmin Lee <jm105.lee@samsung.com>  Wed, 05 Jan 2011 17:15:33 +0900
-
-connman (0.64-0slp2+2) unstable; urgency=low
-
-  * resolve that inet_ntoa gets 100% CPU usage, sometimes
-
- -- Jongmin Lee <jm105.lee@samsung.com>  Mon, 29 Nov 2010 14:02:54 +0900
-
-connman (0.64-0slp2+1) unstable; urgency=low
-
-  * upgrade to connman-0.64
-  * apply samsung patch
-
- -- Jongmin Lee <jm105.lee@samsung.com>  Mon, 29 Nov 2010 08:40:36 +0900
-
-connman (0.63-0slp2+5) unstable; urgency=low
-
-  * enable auto activation
-
- -- Jongmin Lee <jm105.lee@samsung.com>  Wed, 24 Nov 2010 20:12:19 +0900
-
-connman (0.63-0slp2+4) unstable; urgency=low
-
-  * during idle timeout, check active sessions from proc file system
-
- -- Jongmin Lee <jm105.lee@samsung.com>  Mon, 22 Nov 2010 14:12:32 +0900
-
-connman (0.63-0slp2+3) unstable; urgency=low
-
-  * setup hookif when started
-  * move wifi plugin as builtin
-  * set vconfkey for http_proxy
-
- -- Jongmin Lee <jm105.lee@samsung.com>  Tue, 16 Nov 2010 09:18:10 +0900
-
-connman (0.63-0slp2+2) unstable; urgency=low
-
-  * add autonomous on demand data connection
-
- -- Jongmin Lee <jm105.lee@samsung.com>  Mon, 15 Nov 2010 08:56:13 +0900
-
-connman (0.63-0slp2+1) unstable; urgency=low
-
-  * upgrade to connman-0.63
-  * apply samsung patch
-
- -- Jongmin Lee <jm105.lee@samsung.com>  Fri, 05 Nov 2010 17:06:38 +0900
-
-connman (0.62-0slp2+7) unstable; urgency=low
-
-  * enable on-demand connnection
-
- -- Jongmin Lee <jm105.lee@samsung.com>  Wed, 27 Oct 2010 20:42:13 +0900
-
-connman (0.62-0slp2+6) unstable; urgency=low
-
-  * rollback on-demand connection
-
- -- Jongmin Lee <jm105.lee@samsung.com>  Mon, 25 Oct 2010 13:15:57 +0900
-
-connman (0.62-0slp2+5) unstable; urgency=low
-
-  * remove modman state watch
-
- -- Jongmin Lee <jm105.lee@samsung.com>  Thu, 21 Oct 2010 08:52:44 +0900
-
-connman (0.62-0slp2+4) unstable; urgency=low
-
-  * update roaming property properly
-
- -- Jongmin Lee <jm105.lee@samsung.com>  Wed, 20 Oct 2010 20:03:19 +0900
-
-connman (0.62-0slp2+3) unstable; urgency=low
-
-  * enable on-demand connection (only for dns query)
-
- -- Jongmin Lee <jm105.lee@samsung.com>  Tue, 19 Oct 2010 21:09:27 +0900
-
-connman (0.62-0slp2+2) unstable; urgency=low
-
-  * support ethernet/ipv4/proxy property of
-    service interface in pdp connection
-
- -- Jongmin Lee <jm105.lee@samsung.com>  Mon, 18 Oct 2010 20:14:59 +0900
-
-connman (0.62-0slp2+1) unstable; urgency=low
-
-  * add modman plugin
-
- -- Jongmin Lee <jm105.lee@samsung.com>  Fri, 08 Oct 2010 09:00:00 +0900
-
-connman (0.62-0slp2) unstable; urgency=low
-
-  * Upgrade to connman-0.62
-
- -- Jongmin Lee <jm105.lee@samsung.com>  Mon, 04 Oct 2010 08:06:53 +0900
-
-connman (0.61-0slp2) unstable; urgency=low
-
-  * Upgrade to connman-0.61
-
- -- Jongmin Lee <jm105.lee@samsung.com>  Sat, 11 Sep 2010 18:13:33 +0900
-
-connman (0.60-0slp2) unstable; urgency=low
-
-  * Upgrade to connman-0.60
-
- -- Jongmin Lee <jm105.lee@samsung.com>  Thu, 09 Sep 2010 08:08:38 +0900
-
-connman (0.59-0slp2) unstable; urgency=low
-
-  * debianize
-
- -- Jongmin Lee <jm105.lee@samsung.com>  Thu, 02 Sep 2010 12:11:03 +0900
diff --git a/debian/compat b/debian/compat
deleted file mode 100644 (file)
index 7ed6ff8..0000000
+++ /dev/null
@@ -1 +0,0 @@
-5
diff --git a/debian/connman-dev.install.in b/debian/connman-dev.install.in
deleted file mode 100644 (file)
index e2e41aa..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-@PREFIX@/include/*
-@PREFIX@/lib/pkgconfig/*
diff --git a/debian/connman.install.in b/debian/connman.install.in
deleted file mode 100644 (file)
index ed34c04..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-@PREFIX@/sbin/*
-@PREFIX@/lib/connman/plugins/*.so
-@PREFIX@/share/dbus-1/services/*
-@PREFIX@/etc/dbus-1/system.d/*
-@PREFIX@/etc/connman/main.conf
-/var/lib/connman/settings
-/etc/rc.d/init.d/connman
-/etc/rc.d/rc3.d/S61connman
-/etc/rc.d/rc5.d/S61connman
diff --git a/debian/connman.postinst b/debian/connman.postinst
deleted file mode 100644 (file)
index 111562d..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-#!/bin/sh
-chmod 600 /var/lib/connman/settings
diff --git a/debian/control b/debian/control
deleted file mode 100644 (file)
index a8172b9..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-Source: connman
-Section: net
-Priority: extra
-Maintainer: Danny Jeongseok Seo <s.seo@samsung.com>
-Uploaders: Danny Jeongseok Seo <s.seo@samsung.com>, DongHoo Park <donghoo.park@samsung.com>, Jaehyun Kim <jeik01.kim@samsung.com>, Misun Kim <ms0123.kim@samsung.com>, Sunkey Lee <yuvjjang.lee@samsung.com>, Sanghoon Cho <sanghoon80.cho@samsung.com>
-Build-Depends: debhelper (>= 5), autotools-dev, libglib2.0-dev, libdbus-1-dev, iptables-dev
-Standards-Version: 0.1.0
-
-Package: connman
-Section: net
-Architecture: any
-Depends: ${shlibs:Depends}, ${misc:Depends}
-Description: Connection Manager
- The Linux Connection Manager project provides a daemon for managing
- Internet connections within embedded devices running the Linux
- operating system. The Connection Manager is designed to be slim and to
- use as few resources as possible, so it can be easily integrated in
- other Moblin-based embedded systems. It is fully modular system that
- can be extended through plug-ins, to support all kinds of wired or
- wireless technologies. Also, configuration methods like DHCP and
- domain name resolving are implemented using plug-ins. The plug-in
- approach allows for easy adaption and modification for various use cases.
-
-Package: connman-dbg
-Section: net
-Architecture: any
-Depends: connman (= ${Source-Version}), ${misc:Depends}
-Description: debug symbols for Connection Manager
diff --git a/debian/rules b/debian/rules
deleted file mode 100755 (executable)
index dc4bf65..0000000
+++ /dev/null
@@ -1,151 +0,0 @@
-#!/usr/bin/make -f
-# -*- makefile -*-
-# Sample debian/rules that uses debhelper.
-# This file was originally written by Joey Hess and Craig Small.
-# As a special exception, when this file is copied by dh-make into a
-# dh-make output file, you may use that output file without restriction.
-# This special exception was added by Craig Small in version 0.37 of dh-make.
-
-# Uncomment this to turn on verbose mode.
-#export DH_VERBOSE=1
-
-# These are used for cross-compiling and for saving the configure script
-# from having to guess our platform (since we know it already)
-
-DEB_HOST_GNU_TYPE   ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE)
-DEB_BUILD_GNU_TYPE  ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE)
-
-CFLAGS ?= -Wall -g -O2
-LDFLAGS ?=  -Wl,--rpath=$(PREFIX)/lib -Wl,--as-needed
-PREFIX ?= /usr
-DATADIR ?= /opt
-
-CONFIGURE_ARGS = \
-               --localstatedir=/var \
-               --enable-tizen-ext
-               $(NULL)
-#              --enable-debug \
-#                $(NULL)
-
-configure: configure.ac
-       ./autogen.sh
-
-config.status: configure
-       dh_testdir
-       # Add here commands to configure the package.
-       CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" ./configure --prefix=$(PREFIX) $(CONFIGURE_ARGS)
-
-build: build-stamp
-
-build-stamp:  config.status
-       dh_testdir
-
-       # Add here commands to compile the package.
-       $(MAKE)
-       #docbook-to-man debian/ncurses.sgml > ncurses.1
-
-       for f in `find $(CURDIR)/debian/ -name "*.in"`; do \
-               cat $$f > $${f%.in}; \
-               sed -i -e "s#@PREFIX@#$(PREFIX)#g" $${f%.in}; \
-               sed -i -e "s#@DATADIR@#$(DATADIR)#g" $${f%.in}; \
-       done
-
-       touch $@
-
-clean:
-       dh_testdir
-       dh_testroot
-       rm -f build-stamp
-
-       # Add here commands to clean up after the build process.
-       -$(MAKE) distclean
-
-       for f in `find $(CURDIR)/debian/ -name "*.in"`; do \
-               rm -f $${f%.in}; \
-       done
-
-       rm -f depcomp
-       rm -f compile
-       rm -f missing
-       rm -f ltmain.sh
-       rm -f install-sh
-       rm -f config.guess
-       rm -f configh.h
-       rm -f config.h.in
-       rm -f config.log
-       rm -f config.sub
-       rm -f config.guess
-       rm -f configure
-       rm -f Makefile.in
-       rm -f aclocal.m4
-       rm -f ../connman_*.deb
-       rm -f ../connman-*.deb
-       rm -f ../connman_*.changes
-       rm -f ../connman_*.dsc
-       rm -f ../connman_*.tar.gz
-
-       dh_clean
-
-install: build
-       dh_testdir
-       dh_testroot
-       dh_clean -k
-       dh_installdirs
-
-       # Add here commands to install the package into debian/connman.
-       $(MAKE) DESTDIR=$(CURDIR)/debian/tmp install
-
-       mkdir -p $(CURDIR)/debian/tmp/var/lib/connman
-       cp -f $(CURDIR)/resources/var/lib/connman/settings \
-               $(CURDIR)/debian/tmp/var/lib/connman/settings
-       mkdir -p $(CURDIR)/debian/tmp$(PREFIX)/share/dbus-1/services
-       cp -f $(CURDIR)/resources$(PREFIX)/share/dbus-1/services/net.connman.service \
-               $(CURDIR)/debian/tmp$(PREFIX)/share/dbus-1/services/net.connman.service
-       mkdir -p $(CURDIR)/debian/tmp$(PREFIX)/etc/connman
-       cp -f $(CURDIR)/src/main.conf $(CURDIR)/debian/tmp$(PREFIX)/etc/connman/main.conf
-       mkdir -p $(CURDIR)/debian/tmp/etc/rc.d/init.d
-       cp -f $(CURDIR)/resources/etc/rc.d/init.d/connman \
-               $(CURDIR)/debian/tmp/etc/rc.d/init.d/connman
-       mkdir -p $(CURDIR)/debian/tmp/etc/rc.d/rc3.d
-       ln -s ../init.d/connman $(CURDIR)/debian/tmp/etc/rc.d/rc3.d/S61connman
-       mkdir -p $(CURDIR)/debian/tmp/etc/rc.d/rc5.d
-       ln -s ../init.d/connman $(CURDIR)/debian/tmp/etc/rc.d/rc5.d/S61connman
-
-
-# Build architecture-independent files here.
-binary-indep: build install
-# We have nothing to do by default.
-
-# Build architecture-dependent files here.
-binary-arch: build install
-       dh_testdir
-       dh_testroot
-#      dh_installchangelogs
-#      dh_installdocs
-#      dh_installexamples
-       dh_install --sourcedir=debian/tmp
-#      dh_installmenu
-#      dh_installdebconf
-#      dh_installlogrotate
-#      dh_installemacsen
-#      dh_installpam
-#      dh_installmime
-#      dh_python
-#      dh_installinit
-#      dh_installcron
-#      dh_installinfo
-#      dh_installman
-       dh_link
-       dh_strip --dbg-package=connman-dbg
-       dh_compress
-       dh_fixperms
-#      dh_perl
-       dh_makeshlibs
-       dh_installdeb
-       dh_shlibdeps
-       dh_gencontrol
-       dh_md5sums
-       dh_builddeb
-
-binary: binary-indep binary-arch
-.PHONY: build clean binary-indep binary-arch binary install
index 14b2e50..91371af 100644 (file)
@@ -23,6 +23,17 @@ Methods              void Release()
 
                        Possible Errors: net.connman.Agent.Error.Retry
 
+               void RequestBrowser(object service, string url)
+
+                       This method gets called when it is required
+                       to ask the user to open a website to procceed
+                       with login handling.
+
+                       This can happen if connected to a hotspot portal
+                       page without WISPr support.
+
+                       Possible Errors: net.connman.Agent.Error.Canceled
+
                dict RequestInput(object service, dict fields)
 
                        This method gets called when trying to connect to
@@ -40,7 +51,12 @@ Methods              void Release()
                        The dictionary arguments contains field names with
                        their input parameters.
 
+                       In case of WISPr credentials requests and if the user
+                       prefers to login through the browser by himself, agent
+                       will have to return a LaunchBrowser error (see below).
+
                        Possible Errors: net.connman.Agent.Error.Canceled
+                                        net.connman.Agent.Error.LaunchBrowser
 
                void Cancel()
 
@@ -71,6 +87,15 @@ Fields               string Name
                        key, a PSK passphrase or a passphrase for EAP
                        authentication methods.
 
+               string PreviousPassphrase
+
+                       The previous passphrase successfully saved, i.e.
+                       which lead to a successfull connection. This field is
+                       provided as an informational argument when connecting
+                       with it does not work anymore, for instance when it
+                       has been changed on the AP. Such argument appears when
+                       a RequestInput is raised after a retry.
+
                string WPS
 
                        This field requests the use of WPS to get associated.
@@ -99,7 +124,8 @@ Arguments    string Type
                string Requirement
 
                        Contains the requirement option. Valid values are
-                       "mandatory", "optional" or "alternate".
+                       "mandatory", "optional", "alternate" or
+                       "informational".
 
                        The "alternate" value specifies that this field can be
                        returned as an alternative to another one. An example
@@ -108,20 +134,42 @@ Arguments string Type
                        All "mandatory" fields must be returned, while the
                        "optional" can be returned if available.
 
+                       Nothing needs to be returned for "informational", as it
+                       is here only to provide an information so a value is
+                       attached to it.
+
                array{string} Alternates
 
                        Contains the list of alternate field names this
                        field can be represented by.
 
+               string Value
+
+                       Contains data as a string, relatively to an
+                       "informational" argument.
+
 Examples       Requesting a passphrase for WPA2 network
 
                        RequestInput("/service1",
-                               { "Passphrase" : { "Type" : "psk",
+                               { "Passphrase" : { "Type"        : "psk",
                                                   "Requirement" : "mandatory"
                                                 }
                                }
                        ==> { "Passphrase" : "secret123" }
 
+               Requesting a passphrase after an error on the previous one:
+
+                       RequestInput("/service1",
+                               { "Passphrase" : { "Type"        : "psk",
+                                                  "Requirement" : "mandatory"
+                                                },
+                                 "PreviousPassphrase" :
+                                       { "Type"       : "psk",
+                                         "Requirement : "informational",
+                                         "Value"      : "secret123"
+                                       }
+                               }
+
                Requesting name for hidden network
 
                        RequestInput("/service2",
@@ -129,9 +177,9 @@ Examples    Requesting a passphrase for WPA2 network
                                             "Requirement" : "mandatory",
                                             "Alternates"  : [ "SSID" ]
                                           },
-                                 "SSID" : { "Type" : "ssid",
-                                            "Requirement" : "alternate"
-                                          }
+                                 "SSID" : { "Type"        : "ssid",
+                                            "Requirement" : "alternate"
+                                          }
                                }
                        ==> { "Name" : "My hidden network" }
 
@@ -143,7 +191,7 @@ Examples    Requesting a passphrase for WPA2 network
                                                   "Alternates"  : [ "WPS" ]
                                                 },
                                  "WPS"        : { "Type"        : "wpspin",
-                                                  "Requirement" : "alternate"
+                                                  "Requirement" : "alternate"
                                                 }
                                }
 
@@ -155,7 +203,7 @@ Examples    Requesting a passphrase for WPA2 network
                                { "Identity"   : { "Type"        : "string",
                                                   "Requirement" : "mandatory"
                                                 },
-                                 "Passphrase" : { "Type" : "passphrase",
+                                 "Passphrase" : { "Type"        : "passphrase",
                                                   "Requirement" : "mandatory"
                                                 }
                                }
@@ -168,7 +216,7 @@ Examples    Requesting a passphrase for WPA2 network
                                { "Identity"   : { "Type"        : "string",
                                                   "Requirement" : "mandatory"
                                                 },
-                                 "Passphrase" : { "Type" : "response",
+                                 "Passphrase" : { "Type"        : "response",
                                                   "Requirement" : "mandatory"
                                                 }
                                }
@@ -178,12 +226,12 @@ Examples  Requesting a passphrase for WPA2 network
                Requesting username and password for a WISPr-enabled hotspot:
 
                        RequestInput("/service5",
-                                { "Username"   : { "Type"        : "string",
-                                                   "Requirement" : "mandatory"
-                                                 },
-                                  "Password"   : { "Type"        : "passphrase",
-                                                   "Requirement" : "mandatory"
-                                                 }
-                                }
-
-                        ==> { "Username" : "foo", "Password": "secret" }
+                               { "Username"   : { "Type"        : "string",
+                                                  "Requirement" : "mandatory"
+                                                },
+                                 "Password"   : { "Type"        : "passphrase",
+                                                  "Requirement" : "mandatory"
+                                                }
+                               }
+
+                       ==> { "Username" : "foo", "Password": "secret" }
index 4ec3160..6818f5a 100644 (file)
@@ -5,14 +5,14 @@ Service               net.connman
 Interface      net.connman.Clock
 Object path    /
 
-Methods                dict GetProperties()
+Methods                dict GetProperties()  [experimental]
 
                        Returns all system clock properties. See the
                        properties section for available properties.
 
                        Possible Errors: [service].Error.InvalidArguments
 
-               void SetProperty(string name, variant value)
+               void SetProperty(string name, variant value)  [experimental]
 
                        Changes the value of the specified property. Only
                        properties that are listed as read-write are
@@ -22,13 +22,13 @@ Methods             dict GetProperties()
                        Possible Errors: [service].Error.InvalidArguments
                                         [service].Error.InvalidProperty
 
-Signals                PropertyChanged(string name, variant value)
+Signals                PropertyChanged(string name, variant value)  [experimental]
 
                        This signal indicates a changed value of the given
                        property.
 
 
-Properties     uint64 Time [readonly or readwrite]
+Properties     uint64 Time [readonly or readwrite]  [experimental]
 
                        Current system time in seconds since epoch.
 
@@ -44,7 +44,7 @@ Properties    uint64 Time [readonly or readwrite]
                        time should be using gettimeofday() and related
                        system calls.
 
-               string TimeUpdates [readwrite]
+               string TimeUpdates [readwrite]  [experimental]
 
                        Possible values are "manual" and "auto" to indicate
                        time update policy.
@@ -53,7 +53,7 @@ Properties    uint64 Time [readonly or readwrite]
                        many sources as possible to determine the correct
                        and updated time.
 
-               string Timezone [readonly or readwrite]
+               string Timezone [readonly or readwrite]  [experimental]
 
                        Current system timezone string. Allowed values
                        are from the standard timezone data (tzdata)
@@ -66,7 +66,7 @@ Properties    uint64 Time [readonly or readwrite]
                        When the timezone gets changed a PropertyChanged
                        signal will be send out.
 
-               string TimezoneUpdates [readwrite]
+               string TimezoneUpdates [readwrite]  [experimental]
 
                        Possible values are "manual" and "auto" to indicate
                        timezone update policy.
@@ -75,7 +75,7 @@ Properties    uint64 Time [readonly or readwrite]
                        many sources as possible to determine the correct
                        timezone.
 
-               array{string} Timeservers [readwrite]
+               array{string} Timeservers [readwrite]  [experimental]
 
                        List of global default NTP servers. The list should
                        be sorted in order of preference.
index cf89c09..4f76832 100644 (file)
@@ -8,6 +8,12 @@ characters than letters or numbers and must have a .config suffix.
 Those configuration files are text files with a simple format and we typically
 have one file per provisioned network.
 
+If the config file is removed, then Connman tries to remove the
+provisioned service. If individual service entry inside config is removed,
+then the corresponding provisioned service is removed. If service
+entry is changed, then corresponding service is removed and then
+immediately re-provisioned.
+
 
 Global entry [global]
 =====================
@@ -25,26 +31,27 @@ Service entry [service_*]
 =========================
 
 Each provisioned service must start with the [service_*] tag. Replace * with
-your service identifier.
-The service identifier can be anything and will be used internally by connman
-to store the different services into an hash table.
+an identifier unique to the config file.
 
 Allowed fields:
 - Type: Service type. We currently only support wifi.
-- SSID: An hexadecimal or a string representation of a 802.11 SSID.
+- Name: A string representation of an 802.11 SSID. If the SSID field is
+  present, the Name field is ignored.
+- SSID: A hexadecimal representation of an 802.11 SSID. If the SSID field is
+  omitted, the Name field is used instead.
 - EAP: EAP type. We currently only support tls, ttls or peap.
 - CACertFile: File path to CA certificate file (PEM/DER).
 - ClientCertFile: File path to client certificate file (PEM/DER).
 - PrivateKeyFile: File path to client private key file (PEM/DER/PFX).
 - PrivateKeyPassphrase: Password/passphrase for private key file.
 - PrivateKeyPassphraseType: We only support the fsid passphrase type for now.
-This is for private keys generated by using their own filesystem UUID as the
-passphrase. The PrivateKeyPassphrase field is ignored when this field is set
-to fsid.
+  This is for private keys generated by using their own filesystem UUID as the
+  passphrase. The PrivateKeyPassphrase field is ignored when this field is set
+  to fsid.
 - Identity: Identity string for EAP.
 - Phase2: Phase2 (inner authentication with TLS tunnel) authentication method.
-Prefix the value with "EAP-" to indicate the usage of an EAP-based inner
-authentication method (should only be used with EAP = TTLS).
+  Prefix the value with "EAP-" to indicate the usage of an EAP-based inner
+  authentication method (should only be used with EAP = TTLS).
 - Passphrase: RSN/WPA/WPA2 Passphrase
 
 
index cb6d1fa..3bd201d 100644 (file)
@@ -22,30 +22,10 @@ Methods             dict GetProperties()
                        Possible Errors: [service].Error.InvalidArguments
                                         [service].Error.InvalidProperty
 
-               string GetState()
+               array{object,dict} GetTechnologies()
 
-                       Return global connection state of a system. The
-                       same value is return via the State property.
-
-                       Possible Errors: [service].Error.InvalidArguments
-
-               void RequestScan(string type)
-
-                       Request to trigger a scan for the specified
-                       technology. The empty string "" triggers scanning
-                       on all technologies.
-
-                       Possible Errors: [service].Error.InvalidArguments
-
-               void EnableTechnology(string type)
-
-                       Enable specified type of technologies.
-
-                       Possible Errors: [service].Error.InvalidArguments
-
-               void DisableTechnology(string type)
-
-                       Disable specified type of technologies.
+                       Returns a list of tuples with technology object
+                       path and dictionary of technology properties.
 
                        Possible Errors: [service].Error.InvalidArguments
 
@@ -59,58 +39,6 @@ Methods              dict GetProperties()
 
                        Possible Errors: [service].Error.InvalidArguments
 
-               object LookupService(string pattern)
-
-                       Lookup a service matching the specific pattern.
-
-                       Examples are interface names like "eth0", "wlan0"
-                       etc. or service names like "hotspot" etc.
-
-                       In case of multiple services match the the pattern
-                       an error is returned.
-
-                       Possible Errors: [service].Error.InvalidArguments
-                                        [service].Error.NotUnique
-                                        [service].Error.NotFound
-
-               object ConnectService(dict network)
-
-                       Connect to a network specified by the given
-                       properties.
-
-                       For connecting to a hidden WiFi network for example
-                       it is required that Type = "wifi" and the SSID
-                       properties are provided.
-
-                       When successful this method will return object
-                       path of the service object.
-
-                       This method can also be used to connect to an
-                       already existing service. It works exactly the
-                       same as executing the Connect method from the
-                       service interface.
-
-                       This method call will only return in case of an
-                       error or when the service is fully connected. So
-                       setting a longer D-Bus timeout might be a really
-                       good idea.
-
-                       When 'SessionMode' property is enabled, this method
-                       call is disallowed.
-
-                       Possible Errors: [service].Error.InvalidArguments
-
-               void ProvisionService(string configuration)
-
-                       Provide a configuration for services.
-
-                       Service configuration is provided as a single string
-                       that follows the same format as configuration files,
-                       see config-format.txt. An exception to this format
-                       is that only one service can be provisioned per call.
-
-                       Possible Errors: [service].Error.InvalidArguments
-
                object ConnectProvider(dict provider)
 
                        Connect to a VPN specified by the given provider
@@ -144,7 +72,7 @@ Methods              dict GetProperties()
 
                        Possible Errors: [service].Error.InvalidArguments
 
-               void RegisterCounter(object path, uint32 accuracy, uint32 period)
+               void RegisterCounter(object path, uint32 accuracy, uint32 period)  [experimental]
 
                        Register a new counter for user notifications.
 
@@ -165,13 +93,13 @@ Methods            dict GetProperties()
 
                        Possible Errors: [service].Error.InvalidArguments
 
-               void UnregisterCounter(object path)
+               void UnregisterCounter(object path)  [experimental]
 
                        Unregister an existing counter.
 
                        Possible Errors: [service].Error.InvalidArguments
 
-               object CreateSession(dict settings, object notifier)
+               object CreateSession(dict settings, object notifier)  [experimental]
 
                        Create a new session for the application. Every
                        application can create multiple session with
@@ -187,7 +115,7 @@ Methods             dict GetProperties()
                        Every application should at least create one session
                        to inform about its requirements and it purpose.
 
-               void DestroySession(object session)
+               void DestroySession(object session)  [experimental]
 
                        Remove the previously created session.
 
@@ -213,54 +141,75 @@ Methods           dict GetProperties()
 
                        Possible Errors: [service].Error.InvalidArguments
 
-Signals                PropertyChanged(string name, variant value)
+Signals                TechnologyAdded(object path, dict properties)
 
-                       This signal indicates a changed value of the given
-                       property.
+                       Signal that is sent when a new technology is added.
 
-               StateChanged(string state)
+                       It contains the object path of the technology and
+                       also its properties.
 
-                       This signal is similar to the PropertyChanged signal
-                       for the State property.
+               TechnologyRemoved(object path)
 
-                       It exists for application state only care about the
-                       current state and so can avoid to be woken up when
-                       other details changes.
+                       Signal that is sent when a modem has been removed.
 
-Signals                ScanCompleted(boolean success)
+                       The object path is no longer accessible after this
+                       signal and only emitted for reference.
 
-                       This signal indicates that RequestScan was finished completely and 
-                       networks were updated.
+               ServicesChanged(array{object, dict}, array{object})
 
-Properties     string State [readonly]
+                       Signals a list of services that have been changed
+                       via the first array. And a list of service that
+                       have been removed via the second array.
 
-                       The global connection state of a system. Possible
-                       values are "online" if at least one connection exists
-                       and "offline" if no device is connected.
+                       The list of added services is sorted. The dictionary
+                       with the properties might be empty in case none of
+                       the properties have changed. Or only contains the
+                       properties that have changed.
 
-                       In certain situations the state might change to
-                       the value "connected". This can only be seen if
-                       previously no connection was present.
+                       For newly added services the whole set of properties
+                       will be present.
 
-               array{string} AvailableTechnologies [readonly]
+                       The list of removed services can be empty.
 
-                       The list of available technologies. The strings
-                       are the same as the ones from the service types.
+                       This signal will only be triggered when the sort
+                       order of the service list or the number of services
+                       changes. It will not be emitted if only a property
+                       of the service object changes. For that it is
+                       required to watch the PropertyChanged signal of
+                       the service object.
 
-               array{string} EnabledTechnologies [readonly]
+               PropertyChanged(string name, variant value)
 
-                       The list of enabled technologies. The strings
-                       are the same as the ones from the service types.
+                       This signal indicates a changed value of the given
+                       property.
+
+Properties     string State [readonly]
 
-               array{string} ConnectedTechnologies [readonly]
+                       The global connection state of a system. Possible
+                       values are "offline", "idle", "ready" and "online".
 
-                       The list of connected technologies. The strings
-                       are the same as the ones from the service type.
+                       If the device is in offline mode, the value "offline"
+                       indicates this special global state. It can also be
+                       retrieved via the OfflineMode property, but is kept
+                       here for consistency and to differentiate from "idle".
 
-               string DefaultTechnology [readonly]
+                       However when OfflineMode property is true, the State
+                       property can still be "idle", "ready" or "online"
+                       since it is possible by the end user to re-enable
+                       individual technologies like WiFi and Bluetooth while
+                       in offline mode.
 
-                       The current connected technology which holds the
-                       default route.
+                       The states "idle", "ready" and "online" match to
+                       states from the services. If no service is in
+                       either "ready" or "online" state it will indicate
+                       the "idle" state.
+
+                       If at least one service is in "ready" state and no
+                       service is in "online" state, then it will indicate
+                       the "ready" state.
+
+                       When at least one service is in "online" state,
+                       this property will indicate "online" as well.
 
                boolean OfflineMode [readwrite]
 
@@ -275,31 +224,7 @@ Properties string State [readonly]
                        the limited usage of WiFi or Bluetooth devices might
                        be allowed in some situations.
 
-               object ActiveProfile [readwrite]
-
-                       Object path of the current active profile.
-
-               array{object} Technologies [readonly]
-
-                       List of technology object paths.
-
-               array{object} Services [readonly]
-
-                       List of service object paths. The list is sorted
-                       internally to have the service with the default
-                       route always first and then the favorite services
-                       followed by scan results.
-
-                       This list represents the available services for the
-                       current selected profile. If the profile gets changed
-                       then this list will be updated.
-
-                       The same list is available via the profile object
-                       itself. It is just provided here for convenience of
-                       applications only dealing with the current active
-                       profile.
-
-               boolean SessionMode [readwrite]
+               boolean SessionMode [readwrite]  [experminental]
 
                        This disables the auto connect feature. It should be
                        enabled when the Session API is used. When SessionMode
index 92ce2b9..14fec14 100644 (file)
@@ -11,8 +11,8 @@ preferences. This is the service list and interface.
 
 The basic idea is that Connection Manager maintains a single flat and sorted
 list of all available, preferred or previously used services. A service here
-can be either a Ethernet device, a WiFi network, a WiMAX service provider
-or a remote Bluetooth device (for example a mobile phone).
+can be either a Ethernet device, a WiFi network or a remote Bluetooth device
+(for example a mobile phone).
 
 This list of service is sorted by Connection Manager and there is no need
 for the user interface to implement its own sorting. User decisions will
@@ -28,14 +28,10 @@ the order of services in this list.
        +---------------------------------------+
        | My WiFi AP       (strength 80, rsn)   |
        +---------------------------------------+
-       | Clear WiMAX      (strength 70)        |
-       +---------------------------------------+
        | Other AP         (strength 70, rsn)   |
        +---------------------------------------+
        | Friends AP       (strength 70, wep)   |
        +---------------------------------------+
-       | Other WiMAX      (strength 50)        |
-       +---------------------------------------+
 
 If none of the services has been used before the sorting order will be done
 with these priorities:
@@ -43,8 +39,8 @@ with these priorities:
        1. Ethernet     (lower index numbers first)
        2. Bluetooth    (last used devices first)
        3. GSM/UTMS/3G  (if SIM card is present, activated and not roaming)
-       3. WiFi/WiMAX   (signal strength first, then prefer WiMAX over WiFi,
-                        then more secure network first)
+       3. WiFi         (signal strength first, then more secure network
+                       first)
 
 The Ethernet devices are always sorted first since they are physically built
 into the system and will be always present. In cases they are switched off
@@ -57,16 +53,9 @@ do have a signal strength, it is mostly unknown since background scanning
 in Bluetooth is too expensive. The choice here is to sort the last used
 Bluetooth device before the others.
 
-The WiFi and WiMAX networks are treated equally since both provide signal
-strength information and networks closer in the proximity should be shown
-before others since it is more likely they are selected first. The signal
-strength value is normalized to 0-100 (effectively a percentage) and allows
-an easy sorting.
-
-If the signal strength is identical then the WiMAX network should be shown
-first since it is a licensed spectrum and more reliable. Also the number
-of WiMAX networks will be smaller than the number of WiFi since that operates
-in an unlicensed spectrum.
+WiFi networks closer in the proximity should be shown first since it is more
+likely they are selected. The signal strength value is normalized to 0-100
+(effectively a percentage) and allows an easy sorting.
 
 WiFi networks with the same signal strength are then sorted by their security
 setting. WPA2 encrypted networks should be preferred over WPA/WEP and also
@@ -76,9 +65,9 @@ order.
 In the case the WiFi network uses WPS for setup and it is clearly detectable
 that a network waits for Connection Manager to connect to it (for example via
 a push-to-connect button press on the AP), then this network should be shown
-first before any other WiFi or WiMAX network. The reason here is that the
-user already made a choice via the access point. However this depends on
-technical details if it is possible to detect these situations.
+first before any other WiFi networks. The reason here is that the user already
+made a choice via the access point. However this depends on technical details
+if it is possible to detect these situations.
 
 
 Service order
@@ -94,10 +83,10 @@ a networking point of view.
 Selecting the "My WiFi AP" and successfully connecting to it makes it a
 favorite device and it will become an order number bigger than 0. All
 order numbers are internally. They are given only to service that are marked
-as favorite. For WiFi, WiMAX and Bluetooth a successful connection attempt
-makes these services automatically a favorite. For Ethernet the plugging
-of a cable makes it a favorite. Disconnecting from a network doesn't remove
-the favorite setting. It is a manual operation and is equal to users pressing
+as favorite. For WiFi and Bluetooth a successful connection attempt makes
+these services automatically a favorite. For Ethernet the plugging of a cable
+makes it a favorite. Disconnecting from a network doesn't remove the favorite
+setting. It is a manual operation and is equal to users pressing
 delete/remove button.
 
        +---------------------------------------+
@@ -230,10 +219,6 @@ In case of WiFi this will be the SSID value. The SSID is a binary array and
 will be converted into printable form. Unprintable characters are replaced
 with spaces.
 
-For WiMAX networks the provider name like Clear or X-OHM will be used. This
-name either comes directly from the network itself or from a provisioning
-database of the WiMAX service.
-
 For Bluetooth the device alias is used. The alias is different since it
 can be overwritten by the user via the Bluetooth service. The identification
 is still done based on its address, but the display name might change. In
@@ -353,15 +338,14 @@ are more for advanced features and most applications don't need them at all.
 
 The services are represented as a list of object paths. Every of these object
 paths contains a service interface. A service is a global collection for
-Ethernet devices, WiFi networks, Bluetooth services, WiMAX providers etc. and
-all these different types are treated equally.
+Ethernet devices, WiFi networks, Bluetooth services etc. and all these
+different types are treated equally.
 
 Every local Ethernet card will show up as exactly one service. WiFi networks
 will be grouped by SSID, mode and security setting. Bluetooth PAN and DUN
-service will show up per remote device. For WiMAX the provider name will
-be used for grouping different base stations and access providers. This
-creates a simple list that can be directly displayed to the users since these
-are the exact details users should care about.
+service will show up per remote device. This creates a simple list that can
+be directly displayed to the users since these are the exact details users
+should care about.
 
        properties = manager.GetProperties()
 
index ffd16b0..2be62fb 100644 (file)
@@ -5,11 +5,14 @@ Service               net.connman
 Interface      net.connman.Service
 Object path    [variable prefix]/{service0,service1,...}
 
-Methods                dict GetProperties()
+Methods                dict GetProperties()  [deprecated]
 
                        Returns properties for the service object. See
                        the properties section for available properties.
 
+                       Usage of this method is highly discouraged. Use
+                       the Manager.GetServices() method instead.
+
                        Possible Errors: [service].Error.InvalidArguments
 
                void SetProperty(string name, variant value)
@@ -32,7 +35,7 @@ Methods               dict GetProperties()
                void Connect()
 
                        Connect this service. It will attempt to connect
-                       WiFi, WiMAX or Bluetooth services.
+                       WiFi or Bluetooth services.
 
                        For Ethernet devices this method can only be used
                        if it has previously been disconnected. Otherwise
@@ -104,7 +107,7 @@ Methods             dict GetProperties()
 
                        Possible Errors: [service].Error.InvalidArguments
 
-               void ResetCounters()
+               void ResetCounters()  [experimental]
 
                        Reset the counter statistics.
 
@@ -147,10 +150,11 @@ Properties        string State [readonly]
                        The service name (for example "Wireless" etc.)
 
                        This name can be used for directly displaying it in
-                       the application. It has pure informational purpose.
+                       the application. It has pure informational purpose
+                       and no attempt should be made to translate it.
 
-                       For Ethernet devices and hidden WiFi networks it is
-                       not guaranteed that this property is present.
+                       For Ethernet devices and hidden WiFi networks this
+                       property is not present.
 
                string Type [readonly]
 
@@ -160,6 +164,9 @@ Properties  string State [readonly]
                        advanced properties or showing the correct icon
                        to the user.
 
+                       Together with a missing Name property, this can
+                       be used to identify hidden WiFi networks.
+
                array{string} Security [readonly]
 
                        If the service type is WiFi, then this property is
@@ -167,39 +174,11 @@ Properties        string State [readonly]
                        or key management settings.
 
                        Possible values are "none", "wep", "psk", "ieee8021x"
-                       and also "wps". Alternate values for "psk" can also
-                       be "wpa" and "rsn".
+                       and also "wps".
 
                        This property might be only present for WiFi
                        services.
 
-               boolean LoginRequired [readonly]
-
-                       This property indicates that an additional login
-                       step, like web based authentication, is needed
-                       before the connection establishment can proceed.
-
-               string Passphrase [readwrite]
-
-                       If the service type is WiFi, then this property
-                       can be used to store a passphrase.
-
-                       No PropertyChanged signals will be sent for this
-                       property. The PassphraseRequired property should
-                       be monitored instead.
-
-                       This property might also not always be included
-                       since it is protected by a different security policy.
-
-               boolean PassphraseRequired [readonly]
-
-                       If the service type is WiFi, then this property
-                       indicates if a passphrase is required.
-
-                       If a passphrase has been set already or if no
-                       passphrase is needed, then this property will
-                       be set to false.
-
                string BSSID [readonly]
 
                        If the service type is WiFi, then this property
@@ -289,8 +268,9 @@ Properties  string State [readonly]
                array{string} Nameservers.Configuration [readwrite]
 
                        The list of manually configured domain name
-                       servers. Some 3G networks don't provide correct
-                       name servers and this allows for an override.
+                       servers. Some cellular networks don't provide
+                       correct name servers and this allows for an
+                       override.
 
                        This array is sorted by priority and the first
                        entry in the list represents the nameserver with
@@ -305,9 +285,32 @@ Properties string State [readonly]
                        the service. However there might be small window
                        where name resolution might fail.
 
+               array{string} Timeservers [readonly]
+
+                       The list of currently active timeservers for this
+                       service. If the server is not in READY or ONLINE
+                       state than this list will be empty.
+
+               array{string} Timeservers.Configuration [readwrite]
+
+                       The list of manually configured time servers.
+
+                       The first entry in the list represents the
+                       timeserver with the highest priority.
+
+                       When using manual configuration this setting
+                       is useful to override all the other timeserver
+                       settings. This is service specific, hence only
+                       the values for the default service are used.
+
+                       Changes to this property will result in restart
+                       of NTP query.
+
                array{string} Domains [readonly]
 
-                       The list of currently used search domains.
+                       The list of currently used search domains taken
+                       from Domains.Configurations if set, otherwise a
+                       domain name if provided by DHCP or VPNs.
 
                array{string} Domains.Configuration [readwrite]
 
@@ -459,19 +462,19 @@ Properties        string State [readonly]
 
                        string Host [readonly]
 
-                              VPN host IP.
+                               VPN host IP.
 
                        string Domain [readonly]
 
-                              VPN Domain.
+                               VPN Domain.
 
                        string Name [readonly]
 
-                              VPN provider Name.
+                               VPN provider Name.
 
                        string Type [readonly]
 
-                              VPN provider type.
+                               VPN provider type.
 
                dict Ethernet [readonly]
 
index 3a0c9ca..e19c6bf 100644 (file)
@@ -88,22 +88,45 @@ Settings    string  Bearer [readonly]
 
                        This indicates the current bearer that is used
                        for this session. Or an empty string if no bearer
-                       is available.
-
-               boolean Online [readonly]
-
-                       This indicates if the connection is online or
-                       offline.
-
-                       This maps to the online service state. And it is
-                       only valid for the selected bearer configuration.
-                       Otherwise it will be reported as offline even if
-                       the global state would be online.
-
-                       In addition the Online settings notification might
-                       not happen right away. Notifications of online state
+                       if available.
+
+               string ConnectionType [readwrite]
+
+                       This is used to indicate which connection is requested
+                       from the session. The state of the session will be
+                       updated accordingly. Values can be nothing, 'local' or
+                       'internet'.
+                       'local' means the session requests to be connected,
+                       but does not require specifically to be online.
+                       Therefore State property will be set to 'connected' if
+                       underlying service gets ready and/or online.
+                       'online' means the session requests to be connected,
+                       and online. State property will never get 'connected'
+                       but instead will switch to 'online' if underlying
+                       service gets online.
+                       No value means the session requests any kind of
+                       connection and the state will be updated on all steps,
+                       'connected' and 'online'. This is the default value.
+
+               boolean State [readonly]
+
+                       This indicates if the connection is disconnected,
+                       connected or online. It is updated according to the
+                       selected ConnectionType. The session will not be
+                       in a useful shape (i.e.: providing a network connection
+                       to the owner) until its State gets updated to connected
+                       and/or online.
+
+                       This maps to the useful port of the  service state.
+                       And it is only valid for the selected bearer
+                       configuration. Otherwise it will be reported as
+                       disconnected even if connected services are present.
+
+                       In addition the State settings notification might
+                       not happen right away. Notifications of this state
                        can be delayed based on the speed of the bearer. It
-                       is done to avoid congestion on bearers like 3G etc.
+                       is done to avoid congestion on bearers like cellular
+                       etc.
 
                boolean Priority [readwrite]
 
@@ -157,15 +180,11 @@ Settings  string  Bearer [readonly]
 
                dict IPv4 [readonly]
 
-                       Current IPv4 configuration. This settings is only
-                       valid when online is true as well. Otherwise an
-                       empty dictionary is reported.
+                       Current IPv4 configuration.
 
                dict IPv6 [readonly]
 
-                       Current IPv6 configuration. This setting is only
-                       valid when online is true as well. Otherwise an
-                       empty dictionary is reported.
+                       Current IPv6 configuration.
 
                boolean AvoidHandover [readwrite]
 
index 12b421c..6429776 100644 (file)
@@ -5,23 +5,59 @@ Service               net.connman
 Interface      net.connman.Technology
 Object path    [variable prefix]/{technology0,technology1,...}
 
-Methods                dict GetProperties()
+Methods                dict GetProperties()  [deprecated]
 
                        Returns properties for the technology object. See
                        the properties section for available properties.
 
+                       Usage of this method is highly discouraged. Use
+                       the Manager.GetTechnologies() method instead.
+
+                       Possible Errors: [service].Error.InvalidArguments
+
+               void SetProperty(string name, variant value)
+
+                       Changes the value of the specified property. Only
+                       properties that are listed as read-write are
+                       changeable. On success a PropertyChanged signal
+                       will be emitted.
+
                        Possible Errors: [service].Error.InvalidArguments
+                                        [service].Error.InvalidProperty
+
+               void Scan()
+
+                       Trigger a scan for this specific technology. The
+                       method call will return when a scan has been
+                       finished and results are available. So setting
+                       a longer D-Bus timeout might be a really good
+                       idea.
+
+                       Results will be signaled via the ServicesChanged
+                       signal from the manager interface.
 
 Signals                PropertyChanged(string name, variant value)
 
                        This signal indicates a changed value of the given
                        property.
 
-Properties     string State [readonly]
+Properties     boolean Powered [readwrite]
+
+                       Boolean representing the power state of the
+                       technology. False means that the technology is
+                       off (and is available RF-Killed) while True means
+                       that the technology is enabled.
+
+               boolean Connected [readonly]
+
+                       Boolean representing if a technolgy is connected.
 
-                       The technology state information.
+                       This is just a convience property for allowing the
+                       UI to easily show if this technolgy has an active
+                       connection or not.
 
-                       Valid states are "offline", "enabled" and "connected".
+                       If this property is True it means that at least one
+                       service of this technology is in ready state.
 
                string Name [readonly]
 
@@ -44,16 +80,16 @@ Properties  string State [readonly]
 
                string TetheringIdentifier [readwrite]
 
-                      The tethering broadcasted identifier.
+                       The tethering broadcasted identifier.
 
-                      This property is only valid for the WiFi technology,
-                      and is then mapped to the WiFi AP SSID clients will
-                      have to join in order to gain internet connectivity.
+                       This property is only valid for the WiFi technology,
+                       and is then mapped to the WiFi AP SSID clients will
+                       have to join in order to gain internet connectivity.
 
                string TetheringPassphrase [readwrite]
 
-                      The tethering connection passphrase.
+                       The tethering connection passphrase.
 
-                      This property is only valid for the WiFi technology,
-                      and is then mapped to the WPA pre-shared key clients
-                      will have to use in order to establish a connection.
+                       This property is only valid for the WiFi technology,
+                       and is then mapped to the WPA pre-shared key clients
+                       will have to use in order to establish a connection.
diff --git a/gdbus/client.c b/gdbus/client.c
new file mode 100644 (file)
index 0000000..be8cc29
--- /dev/null
@@ -0,0 +1,1263 @@
+/*
+ *
+ *  D-Bus helper library
+ *
+ *  Copyright (C) 2004-2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <glib.h>
+#include <dbus/dbus.h>
+
+#include "gdbus.h"
+
+#define METHOD_CALL_TIMEOUT (300 * 1000)
+
+#ifndef DBUS_INTERFACE_OBJECT_MANAGER
+#define DBUS_INTERFACE_OBJECT_MANAGER DBUS_INTERFACE_DBUS ".ObjectManager"
+#endif
+
+struct GDBusClient {
+       int ref_count;
+       DBusConnection *dbus_conn;
+       char *service_name;
+       char *base_path;
+       guint watch;
+       guint added_watch;
+       guint removed_watch;
+       GPtrArray *match_rules;
+       DBusPendingCall *pending_call;
+       DBusPendingCall *get_objects_call;
+       GDBusWatchFunction connect_func;
+       void *connect_data;
+       GDBusWatchFunction disconn_func;
+       void *disconn_data;
+       GDBusMessageFunction signal_func;
+       void *signal_data;
+       GDBusProxyFunction proxy_added;
+       GDBusProxyFunction proxy_removed;
+       GDBusPropertyFunction property_changed;
+       void *user_data;
+       GList *proxy_list;
+};
+
+struct GDBusProxy {
+       int ref_count;
+       GDBusClient *client;
+       char *obj_path;
+       char *interface;
+       GHashTable *prop_list;
+       guint watch;
+       GDBusPropertyFunction prop_func;
+       void *prop_data;
+       GDBusProxyFunction removed_func;
+       void *removed_data;
+};
+
+struct prop_entry {
+       char *name;
+       int type;
+       DBusMessage *msg;
+};
+
+static void modify_match_reply(DBusPendingCall *call, void *user_data)
+{
+       DBusMessage *reply = dbus_pending_call_steal_reply(call);
+       DBusError error;
+
+       dbus_error_init(&error);
+
+       if (dbus_set_error_from_message(&error, reply) == TRUE)
+               dbus_error_free(&error);
+
+       dbus_message_unref(reply);
+}
+
+static gboolean modify_match(DBusConnection *conn, const char *member,
+                                                       const char *rule)
+{
+       DBusMessage *msg;
+       DBusPendingCall *call;
+
+       msg = dbus_message_new_method_call(DBUS_SERVICE_DBUS, DBUS_PATH_DBUS,
+                                       DBUS_INTERFACE_DBUS, member);
+       if (msg == NULL)
+               return FALSE;
+
+       dbus_message_append_args(msg, DBUS_TYPE_STRING, &rule,
+                                               DBUS_TYPE_INVALID);
+
+       if (g_dbus_send_message_with_reply(conn, msg, &call, -1) == FALSE) {
+               dbus_message_unref(msg);
+               return FALSE;
+       }
+
+       dbus_pending_call_set_notify(call, modify_match_reply, NULL, NULL);
+       dbus_pending_call_unref(call);
+
+       dbus_message_unref(msg);
+
+       return TRUE;
+}
+
+static void iter_append_iter(DBusMessageIter *base, DBusMessageIter *iter)
+{
+       int type;
+
+       type = dbus_message_iter_get_arg_type(iter);
+
+       if (dbus_type_is_basic(type)) {
+               const void *value;
+
+               dbus_message_iter_get_basic(iter, &value);
+               dbus_message_iter_append_basic(base, type, &value);
+       } else if (dbus_type_is_container(type)) {
+               DBusMessageIter iter_sub, base_sub;
+               char *sig;
+
+               dbus_message_iter_recurse(iter, &iter_sub);
+
+               switch (type) {
+               case DBUS_TYPE_ARRAY:
+               case DBUS_TYPE_VARIANT:
+                       sig = dbus_message_iter_get_signature(&iter_sub);
+                       break;
+               default:
+                       sig = NULL;
+                       break;
+               }
+
+               dbus_message_iter_open_container(base, type, sig, &base_sub);
+
+               if (sig != NULL)
+                       dbus_free(sig);
+
+               while (dbus_message_iter_get_arg_type(&iter_sub) !=
+                                                       DBUS_TYPE_INVALID) {
+                       iter_append_iter(&base_sub, &iter_sub);
+                       dbus_message_iter_next(&iter_sub);
+               }
+
+               dbus_message_iter_close_container(base, &base_sub);
+       }
+}
+
+static void prop_entry_update(struct prop_entry *prop, DBusMessageIter *iter)
+{
+       DBusMessage *msg;
+       DBusMessageIter base;
+
+       msg = dbus_message_new(DBUS_MESSAGE_TYPE_METHOD_RETURN);
+       if (msg == NULL)
+               return;
+
+       dbus_message_iter_init_append(msg, &base);
+       iter_append_iter(&base, iter);
+
+       if (prop->msg != NULL)
+               dbus_message_unref(prop->msg);
+
+       prop->msg = dbus_message_copy(msg);
+       dbus_message_unref(msg);
+}
+
+static struct prop_entry *prop_entry_new(const char *name,
+                                               DBusMessageIter *iter)
+{
+       struct prop_entry *prop;
+
+       prop = g_try_new0(struct prop_entry, 1);
+       if (prop == NULL)
+               return NULL;
+
+       prop->name = g_strdup(name);
+       prop->type = dbus_message_iter_get_arg_type(iter);
+
+       prop_entry_update(prop, iter);
+
+       return prop;
+}
+
+static void prop_entry_free(gpointer data)
+{
+       struct prop_entry *prop = data;
+
+       if (prop->msg != NULL)
+               dbus_message_unref(prop->msg);
+
+       g_free(prop->name);
+
+       g_free(prop);
+}
+
+static void add_property(GDBusProxy *proxy, const char *name,
+                               DBusMessageIter *iter, gboolean send_changed)
+{
+       GDBusClient *client = proxy->client;
+       DBusMessageIter value;
+       struct prop_entry *prop;
+
+       if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_VARIANT)
+               return;
+
+       dbus_message_iter_recurse(iter, &value);
+
+       prop = g_hash_table_lookup(proxy->prop_list, name);
+       if (prop != NULL) {
+               prop_entry_update(prop, &value);
+               goto done;
+       }
+
+       prop = prop_entry_new(name, &value);
+       if (prop == NULL)
+               return;
+
+       g_hash_table_replace(proxy->prop_list, prop->name, prop);
+
+done:
+       if (proxy->prop_func)
+               proxy->prop_func(proxy, name, &value, proxy->prop_data);
+
+       if (client == NULL || send_changed == FALSE)
+               return;
+
+       if (client->property_changed)
+               client->property_changed(proxy, name, &value,
+                                                       client->user_data);
+}
+
+static void update_properties(GDBusProxy *proxy, DBusMessageIter *iter,
+                                                       gboolean send_changed)
+{
+       DBusMessageIter dict;
+
+       if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
+               return;
+
+       dbus_message_iter_recurse(iter, &dict);
+
+       while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+               DBusMessageIter entry;
+               const char *name;
+
+               dbus_message_iter_recurse(&dict, &entry);
+
+               if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
+                       break;
+
+               dbus_message_iter_get_basic(&entry, &name);
+               dbus_message_iter_next(&entry);
+
+               add_property(proxy, name, &entry, send_changed);
+
+               dbus_message_iter_next(&dict);
+       }
+}
+
+static void get_all_properties_reply(DBusPendingCall *call, void *user_data)
+{
+       GDBusProxy *proxy = user_data;
+       GDBusClient *client = proxy->client;
+       DBusMessage *reply = dbus_pending_call_steal_reply(call);
+       DBusMessageIter iter;
+       DBusError error;
+
+       dbus_error_init(&error);
+
+       if (dbus_set_error_from_message(&error, reply) == TRUE) {
+               dbus_error_free(&error);
+               goto done;
+       }
+
+       dbus_message_iter_init(reply, &iter);
+
+       update_properties(proxy, &iter, FALSE);
+
+done:
+       if (g_list_find(client->proxy_list, proxy) == NULL) {
+               if (client->proxy_added)
+                       client->proxy_added(proxy, client->user_data);
+
+               client->proxy_list = g_list_append(client->proxy_list, proxy);
+       }
+
+       dbus_message_unref(reply);
+
+       g_dbus_client_unref(client);
+}
+
+static void get_all_properties(GDBusProxy *proxy)
+{
+       GDBusClient *client = proxy->client;
+       const char *service_name = client->service_name;
+       DBusMessage *msg;
+       DBusPendingCall *call;
+
+       msg = dbus_message_new_method_call(service_name, proxy->obj_path,
+                                       DBUS_INTERFACE_PROPERTIES, "GetAll");
+       if (msg == NULL)
+               return;
+
+       dbus_message_append_args(msg, DBUS_TYPE_STRING, &proxy->interface,
+                                                       DBUS_TYPE_INVALID);
+
+       if (g_dbus_send_message_with_reply(client->dbus_conn, msg,
+                                                       &call, -1) == FALSE) {
+               dbus_message_unref(msg);
+               return;
+       }
+
+       g_dbus_client_ref(client);
+
+       dbus_pending_call_set_notify(call, get_all_properties_reply,
+                                                       proxy, NULL);
+       dbus_pending_call_unref(call);
+
+       dbus_message_unref(msg);
+}
+
+static GDBusProxy *proxy_lookup(GDBusClient *client, const char *path,
+                                               const char *interface)
+{
+       GList *list;
+
+       for (list = g_list_first(client->proxy_list); list;
+                                               list = g_list_next(list)) {
+               GDBusProxy *proxy = list->data;
+
+               if (g_str_equal(proxy->interface, interface) == TRUE &&
+                               g_str_equal(proxy->obj_path, path) == TRUE)
+                       return proxy;
+        }
+
+       return NULL;
+}
+
+static gboolean properties_changed(DBusConnection *conn, DBusMessage *msg,
+                                                       void *user_data)
+{
+       GDBusProxy *proxy = user_data;
+       GDBusClient *client = proxy->client;
+       DBusMessageIter iter, entry;
+       const char *interface;
+
+       if (dbus_message_iter_init(msg, &iter) == FALSE)
+               return TRUE;
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+               return TRUE;
+
+       dbus_message_iter_get_basic(&iter, &interface);
+       dbus_message_iter_next(&iter);
+
+       update_properties(proxy, &iter, TRUE);
+
+       dbus_message_iter_next(&iter);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
+               return TRUE;
+
+       dbus_message_iter_recurse(&iter, &entry);
+
+       while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING) {
+               const char *name;
+
+               dbus_message_iter_get_basic(&entry, &name);
+
+               g_hash_table_remove(proxy->prop_list, name);
+
+               if (proxy->prop_func)
+                       proxy->prop_func(proxy, name, NULL, proxy->prop_data);
+
+               if (client->property_changed)
+                       client->property_changed(proxy, name, NULL,
+                                                       client->user_data);
+
+               dbus_message_iter_next(&entry);
+       }
+
+       return TRUE;
+}
+
+static GDBusProxy *proxy_new(GDBusClient *client, const char *path,
+                                               const char *interface)
+{
+       GDBusProxy *proxy;
+
+       proxy = g_try_new0(GDBusProxy, 1);
+       if (proxy == NULL)
+               return NULL;
+
+       proxy->client = client;
+       proxy->obj_path = g_strdup(path);
+       proxy->interface = g_strdup(interface);
+
+       proxy->prop_list = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                                       NULL, prop_entry_free);
+       proxy->watch = g_dbus_add_properties_watch(client->dbus_conn,
+                                                       client->service_name,
+                                                       proxy->obj_path,
+                                                       proxy->interface,
+                                                       properties_changed,
+                                                       proxy, NULL);
+
+       return g_dbus_proxy_ref(proxy);
+}
+
+static void proxy_free(gpointer data)
+{
+       GDBusProxy *proxy = data;
+
+       if (proxy->client) {
+               GDBusClient *client = proxy->client;
+
+               if (client->proxy_removed)
+                       client->proxy_removed(proxy, client->user_data);
+
+               g_dbus_remove_watch(client->dbus_conn, proxy->watch);
+
+               g_hash_table_remove_all(proxy->prop_list);
+
+               proxy->client = NULL;
+       }
+
+       if (proxy->removed_func)
+               proxy->removed_func(proxy, proxy->removed_data);
+
+       g_dbus_proxy_unref(proxy);
+}
+
+static void proxy_remove(GDBusClient *client, const char *path,
+                                               const char *interface)
+{
+       GList *list;
+
+       for (list = g_list_first(client->proxy_list); list;
+                                               list = g_list_next(list)) {
+               GDBusProxy *proxy = list->data;
+
+               if (g_str_equal(proxy->interface, interface) == TRUE &&
+                               g_str_equal(proxy->obj_path, path) == TRUE) {
+                       client->proxy_list =
+                               g_list_delete_link(client->proxy_list, list);
+                       proxy_free(proxy);
+                       break;
+               }
+       }
+}
+
+GDBusProxy *g_dbus_proxy_new(GDBusClient *client, const char *path,
+                                                       const char *interface)
+{
+       GDBusProxy *proxy;
+
+       if (client == NULL)
+               return NULL;
+
+       proxy = proxy_lookup(client, path, interface);
+       if (proxy)
+               return g_dbus_proxy_ref(proxy);
+
+       proxy = proxy_new(client, path, interface);
+       if (proxy == NULL)
+               return NULL;
+
+       get_all_properties(proxy);
+
+       return g_dbus_proxy_ref(proxy);
+}
+
+GDBusProxy *g_dbus_proxy_ref(GDBusProxy *proxy)
+{
+       if (proxy == NULL)
+               return NULL;
+
+       __sync_fetch_and_add(&proxy->ref_count, 1);
+
+       return proxy;
+}
+
+void g_dbus_proxy_unref(GDBusProxy *proxy)
+{
+       if (proxy == NULL)
+               return;
+
+       if (__sync_sub_and_fetch(&proxy->ref_count, 1) > 0)
+               return;
+
+       g_hash_table_destroy(proxy->prop_list);
+
+       g_free(proxy->obj_path);
+       g_free(proxy->interface);
+
+       g_free(proxy);
+}
+
+const char *g_dbus_proxy_get_path(GDBusProxy *proxy)
+{
+       if (proxy == NULL)
+               return NULL;
+
+       return proxy->obj_path;
+}
+
+const char *g_dbus_proxy_get_interface(GDBusProxy *proxy)
+{
+       if (proxy == NULL)
+               return NULL;
+
+       return proxy->interface;
+}
+
+gboolean g_dbus_proxy_get_property(GDBusProxy *proxy, const char *name,
+                                                        DBusMessageIter *iter)
+{
+       struct prop_entry *prop;
+
+       if (proxy == NULL || name == NULL)
+               return FALSE;
+
+       prop = g_hash_table_lookup(proxy->prop_list, name);
+       if (prop == NULL)
+               return FALSE;
+
+       if (prop->msg == NULL)
+               return FALSE;
+
+       if (dbus_message_iter_init(prop->msg, iter) == FALSE)
+               return FALSE;
+
+       return TRUE;
+}
+
+struct refresh_property_data {
+       GDBusProxy *proxy;
+       char *name;
+};
+
+static void refresh_property_free(gpointer user_data)
+{
+       struct refresh_property_data *data = user_data;
+
+       g_free(data->name);
+       g_free(data);
+}
+
+static void refresh_property_reply(DBusPendingCall *call, void *user_data)
+{
+       struct refresh_property_data *data = user_data;
+       DBusMessage *reply = dbus_pending_call_steal_reply(call);
+       DBusError error;
+
+       dbus_error_init(&error);
+
+       if (dbus_set_error_from_message(&error, reply) == FALSE) {
+               DBusMessageIter iter;
+
+               dbus_message_iter_init(reply, &iter);
+
+               add_property(data->proxy, data->name, &iter, TRUE);
+       } else
+               dbus_error_free(&error);
+
+       dbus_message_unref(reply);
+}
+
+gboolean g_dbus_proxy_refresh_property(GDBusProxy *proxy, const char *name)
+{
+       struct refresh_property_data *data;
+       GDBusClient *client;
+       DBusMessage *msg;
+       DBusMessageIter iter;
+       DBusPendingCall *call;
+
+       if (proxy == NULL || name == NULL)
+               return FALSE;
+
+       client = proxy->client;
+       if (client == NULL)
+               return FALSE;
+
+       data = g_try_new0(struct refresh_property_data, 1);
+       if (data == NULL)
+               return FALSE;
+
+       data->proxy = proxy;
+       data->name = g_strdup(name);
+
+       msg = dbus_message_new_method_call(client->service_name,
+                       proxy->obj_path, DBUS_INTERFACE_PROPERTIES, "Get");
+       if (msg == NULL) {
+               refresh_property_free(data);
+               return FALSE;
+       }
+
+       dbus_message_iter_init_append(msg, &iter);
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING,
+                                                       &proxy->interface);
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &name);
+
+       if (g_dbus_send_message_with_reply(client->dbus_conn, msg,
+                                                       &call, -1) == FALSE) {
+               dbus_message_unref(msg);
+               refresh_property_free(data);
+               return FALSE;
+       }
+
+       dbus_pending_call_set_notify(call, refresh_property_reply,
+                                               data, refresh_property_free);
+       dbus_pending_call_unref(call);
+
+       dbus_message_unref(msg);
+
+       return TRUE;
+}
+
+struct set_property_data {
+       GDBusResultFunction function;
+       void *user_data;
+       GDBusDestroyFunction destroy;
+};
+
+static void set_property_reply(DBusPendingCall *call, void *user_data)
+{
+       struct set_property_data *data = user_data;
+       DBusMessage *reply = dbus_pending_call_steal_reply(call);
+       DBusError error;
+
+       dbus_error_init(&error);
+
+       dbus_set_error_from_message(&error, reply);
+
+       if (data->function)
+               data->function(&error, data->user_data);
+
+       if (data->destroy)
+               data->destroy(data->user_data);
+
+       dbus_error_free(&error);
+
+       dbus_message_unref(reply);
+}
+
+gboolean g_dbus_proxy_set_property_basic(GDBusProxy *proxy,
+                               const char *name, int type, const void *value,
+                               GDBusResultFunction function, void *user_data,
+                               GDBusDestroyFunction destroy)
+{
+       struct set_property_data *data;
+       GDBusClient *client;
+       DBusMessage *msg;
+       DBusMessageIter iter, variant;
+       DBusPendingCall *call;
+       char type_as_str[2];
+
+       if (proxy == NULL || name == NULL || value == NULL)
+               return FALSE;
+
+       if (dbus_type_is_basic(type) == FALSE)
+               return FALSE;
+
+       client = proxy->client;
+       if (client == NULL)
+               return FALSE;
+
+       data = g_try_new0(struct set_property_data, 1);
+       if (data == NULL)
+               return FALSE;
+
+       data->function = function;
+       data->user_data = user_data;
+       data->destroy = destroy;
+
+       msg = dbus_message_new_method_call(client->service_name,
+                       proxy->obj_path, DBUS_INTERFACE_PROPERTIES, "Set");
+       if (msg == NULL) {
+               g_free(data);
+               return FALSE;
+       }
+
+       type_as_str[0] = (char) type;
+       type_as_str[1] = '\0';
+
+       dbus_message_iter_init_append(msg, &iter);
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING,
+                                                       &proxy->interface);
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &name);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
+                                               type_as_str, &variant);
+       dbus_message_iter_append_basic(&variant, type, value);
+       dbus_message_iter_close_container(&iter, &variant);
+
+       if (g_dbus_send_message_with_reply(client->dbus_conn, msg,
+                                                       &call, -1) == FALSE) {
+               dbus_message_unref(msg);
+               g_free(data);
+               return FALSE;
+       }
+
+       dbus_pending_call_set_notify(call, set_property_reply, data, g_free);
+       dbus_pending_call_unref(call);
+
+       dbus_message_unref(msg);
+
+       return TRUE;
+}
+
+struct method_call_data {
+       GDBusReturnFunction function;
+       void *user_data;
+       GDBusDestroyFunction destroy;
+};
+
+static void method_call_reply(DBusPendingCall *call, void *user_data)
+{
+       struct method_call_data *data = user_data;
+       DBusMessage *reply = dbus_pending_call_steal_reply(call);
+
+       if (data->function)
+               data->function(reply, data->user_data);
+
+       if (data->destroy)
+               data->destroy(data->user_data);
+
+       dbus_message_unref(reply);
+}
+
+gboolean g_dbus_proxy_method_call(GDBusProxy *proxy, const char *method,
+                               GDBusSetupFunction setup,
+                               GDBusReturnFunction function, void *user_data,
+                               GDBusDestroyFunction destroy)
+{
+       struct method_call_data *data;
+       GDBusClient *client;
+       DBusMessage *msg;
+       DBusPendingCall *call;
+
+       if (proxy == NULL || method == NULL)
+               return FALSE;
+
+       client = proxy->client;
+       if (client == NULL)
+               return FALSE;
+
+       data = g_try_new0(struct method_call_data, 1);
+       if (data == NULL)
+               return FALSE;
+
+       data->function = function;
+       data->user_data = user_data;
+       data->destroy = destroy;
+
+       msg = dbus_message_new_method_call(client->service_name,
+                               proxy->obj_path, proxy->interface, method);
+       if (msg == NULL) {
+               g_free(data);
+               return FALSE;
+       }
+
+       if (setup) {
+               DBusMessageIter iter;
+
+               dbus_message_iter_init_append(msg, &iter);
+               setup(&iter, data->user_data);
+       }
+
+       if (g_dbus_send_message_with_reply(client->dbus_conn, msg,
+                                       &call, METHOD_CALL_TIMEOUT) == FALSE) {
+               dbus_message_unref(msg);
+               g_free(data);
+               return FALSE;
+       }
+
+       dbus_pending_call_set_notify(call, method_call_reply, data, g_free);
+       dbus_pending_call_unref(call);
+
+       dbus_message_unref(msg);
+
+       return TRUE;
+}
+
+gboolean g_dbus_proxy_set_property_watch(GDBusProxy *proxy,
+                       GDBusPropertyFunction function, void *user_data)
+{
+       if (proxy == NULL)
+               return FALSE;
+
+       proxy->prop_func = function;
+       proxy->prop_data = user_data;
+
+       return TRUE;
+}
+
+gboolean g_dbus_proxy_set_removed_watch(GDBusProxy *proxy,
+                               GDBusProxyFunction function, void *user_data)
+{
+       if (proxy == NULL)
+               return FALSE;
+
+       proxy->removed_func = function;
+       proxy->removed_data = user_data;
+
+       return TRUE;
+}
+
+static void refresh_properties(GDBusClient *client)
+{
+       GList *list;
+
+       for (list = g_list_first(client->proxy_list); list;
+                                               list = g_list_next(list)) {
+               GDBusProxy *proxy = list->data;
+
+               get_all_properties(proxy);
+        }
+}
+
+static void parse_properties(GDBusClient *client, const char *path,
+                               const char *interface, DBusMessageIter *iter)
+{
+       GDBusProxy *proxy;
+
+       if (g_str_equal(interface, DBUS_INTERFACE_INTROSPECTABLE) == TRUE)
+               return;
+
+       if (g_str_equal(interface, DBUS_INTERFACE_PROPERTIES) == TRUE)
+               return;
+
+       proxy = proxy_lookup(client, path, interface);
+       if (proxy) {
+               update_properties(proxy, iter, FALSE);
+               return;
+       }
+
+       proxy = proxy_new(client, path, interface);
+       if (proxy == NULL)
+               return;
+
+       update_properties(proxy, iter, FALSE);
+
+       if (client->proxy_added)
+               client->proxy_added(proxy, client->user_data);
+
+       client->proxy_list = g_list_append(client->proxy_list, proxy);
+}
+
+static void parse_interfaces(GDBusClient *client, const char *path,
+                                               DBusMessageIter *iter)
+{
+       DBusMessageIter dict;
+
+       if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
+               return;
+
+       dbus_message_iter_recurse(iter, &dict);
+
+       while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+               DBusMessageIter entry;
+               const char *interface;
+
+               dbus_message_iter_recurse(&dict, &entry);
+
+               if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
+                       break;
+
+               dbus_message_iter_get_basic(&entry, &interface);
+               dbus_message_iter_next(&entry);
+
+               parse_properties(client, path, interface, &entry);
+
+               dbus_message_iter_next(&dict);
+       }
+}
+
+static gboolean interfaces_added(DBusConnection *conn, DBusMessage *msg,
+                                                       void *user_data)
+{
+       GDBusClient *client = user_data;
+       DBusMessageIter iter;
+       const char *path;
+
+       if (dbus_message_iter_init(msg, &iter) == FALSE)
+               return TRUE;
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_OBJECT_PATH)
+               return TRUE;
+
+       dbus_message_iter_get_basic(&iter, &path);
+       dbus_message_iter_next(&iter);
+
+       g_dbus_client_ref(client);
+
+       parse_interfaces(client, path, &iter);
+
+       g_dbus_client_unref(client);
+
+       return TRUE;
+}
+
+static gboolean interfaces_removed(DBusConnection *conn, DBusMessage *msg,
+                                                       void *user_data)
+{
+       GDBusClient *client = user_data;
+       DBusMessageIter iter, entry;
+       const char *path;
+
+       if (dbus_message_iter_init(msg, &iter) == FALSE)
+               return TRUE;
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_OBJECT_PATH)
+               return TRUE;
+
+       dbus_message_iter_get_basic(&iter, &path);
+       dbus_message_iter_next(&iter);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
+               return TRUE;
+
+       dbus_message_iter_recurse(&iter, &entry);
+
+       g_dbus_client_ref(client);
+
+       while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING) {
+               const char *interface;
+
+               dbus_message_iter_get_basic(&entry, &interface);
+               proxy_remove(client, path, interface);
+               dbus_message_iter_next(&entry);
+       }
+
+       g_dbus_client_unref(client);
+
+       return TRUE;
+}
+
+static void parse_managed_objects(GDBusClient *client, DBusMessage *msg)
+{
+       DBusMessageIter iter, dict;
+
+       if (dbus_message_iter_init(msg, &iter) == FALSE)
+               return;
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
+               return;
+
+       dbus_message_iter_recurse(&iter, &dict);
+
+       while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+               DBusMessageIter entry;
+               const char *path;
+
+               dbus_message_iter_recurse(&dict, &entry);
+
+               if (dbus_message_iter_get_arg_type(&entry) !=
+                                                       DBUS_TYPE_OBJECT_PATH)
+                       break;
+
+               dbus_message_iter_get_basic(&entry, &path);
+               dbus_message_iter_next(&entry);
+
+               parse_interfaces(client, path, &entry);
+
+               dbus_message_iter_next(&dict);
+       }
+}
+
+static void get_managed_objects_reply(DBusPendingCall *call, void *user_data)
+{
+       GDBusClient *client = user_data;
+       DBusMessage *reply = dbus_pending_call_steal_reply(call);
+       DBusError error;
+
+       g_dbus_client_ref(client);
+
+       dbus_error_init(&error);
+
+       if (dbus_set_error_from_message(&error, reply) == TRUE) {
+               dbus_error_free(&error);
+               goto done;
+       }
+
+       parse_managed_objects(client, reply);
+
+done:
+       dbus_message_unref(reply);
+
+       dbus_pending_call_unref(client->get_objects_call);
+       client->get_objects_call = NULL;
+
+       g_dbus_client_unref(client);
+}
+
+static void get_managed_objects(GDBusClient *client)
+{
+       DBusMessage *msg;
+
+       if (!client->proxy_added && !client->proxy_removed) {
+               refresh_properties(client);
+               return;
+       }
+
+       if (client->get_objects_call != NULL)
+               return;
+
+       msg = dbus_message_new_method_call(client->service_name, "/",
+                                       DBUS_INTERFACE_DBUS ".ObjectManager",
+                                                       "GetManagedObjects");
+       if (msg == NULL)
+               return;
+
+       dbus_message_append_args(msg, DBUS_TYPE_INVALID);
+
+       if (g_dbus_send_message_with_reply(client->dbus_conn, msg,
+                               &client->get_objects_call, -1) == FALSE) {
+               dbus_message_unref(msg);
+               return;
+       }
+
+       dbus_pending_call_set_notify(client->get_objects_call,
+                                               get_managed_objects_reply,
+                                               client, NULL);
+
+       dbus_message_unref(msg);
+}
+
+static void service_connect(DBusConnection *conn, void *user_data)
+{
+       GDBusClient *client = user_data;
+
+       g_dbus_client_ref(client);
+
+       if (client->connect_func)
+               client->connect_func(conn, client->connect_data);
+
+       get_managed_objects(client);
+
+       g_dbus_client_unref(client);
+}
+
+static void service_disconnect(DBusConnection *conn, void *user_data)
+{
+       GDBusClient *client = user_data;
+
+       g_list_free_full(client->proxy_list, proxy_free);
+       client->proxy_list = NULL;
+
+       if (client->disconn_func)
+               client->disconn_func(conn, client->disconn_data);
+}
+
+static DBusHandlerResult message_filter(DBusConnection *connection,
+                                       DBusMessage *message, void *user_data)
+{
+       GDBusClient *client = user_data;
+       const char *sender, *path, *interface;
+
+       if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_SIGNAL)
+               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+       sender = dbus_message_get_sender(message);
+       if (sender == NULL)
+               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+       path = dbus_message_get_path(message);
+       interface = dbus_message_get_interface(message);
+
+       if (g_str_has_prefix(path, client->base_path) == FALSE)
+               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+       if (g_str_equal(interface, DBUS_INTERFACE_PROPERTIES) == TRUE)
+               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+       if (client->signal_func)
+               client->signal_func(connection, message, client->signal_data);
+
+       return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+GDBusClient *g_dbus_client_new(DBusConnection *connection,
+                                       const char *service, const char *path)
+{
+       GDBusClient *client;
+       unsigned int i;
+
+       if (connection == NULL)
+               return NULL;
+
+       client = g_try_new0(GDBusClient, 1);
+       if (client == NULL)
+               return NULL;
+
+       if (dbus_connection_add_filter(connection, message_filter,
+                                               client, NULL) == FALSE) {
+               g_free(client);
+               return NULL;
+       }
+
+       client->dbus_conn = dbus_connection_ref(connection);
+       client->service_name = g_strdup(service);
+       client->base_path = g_strdup(path);
+
+       client->match_rules = g_ptr_array_sized_new(1);
+       g_ptr_array_set_free_func(client->match_rules, g_free);
+
+       client->watch = g_dbus_add_service_watch(connection, service,
+                                               service_connect,
+                                               service_disconnect,
+                                               client, NULL);
+       client->added_watch = g_dbus_add_signal_watch(connection, service,
+                                               "/",
+                                               DBUS_INTERFACE_OBJECT_MANAGER,
+                                               "InterfacesAdded",
+                                               interfaces_added,
+                                               client, NULL);
+       client->removed_watch = g_dbus_add_signal_watch(connection, service,
+                                               "/",
+                                               DBUS_INTERFACE_OBJECT_MANAGER,
+                                               "InterfacesRemoved",
+                                               interfaces_removed,
+                                               client, NULL);
+       g_ptr_array_add(client->match_rules, g_strdup_printf("type='signal',"
+                               "sender='%s',path_namespace='%s'",
+                               client->service_name, client->base_path));
+
+       for (i = 0; i < client->match_rules->len; i++) {
+               modify_match(client->dbus_conn, "AddMatch",
+                               g_ptr_array_index(client->match_rules, i));
+       }
+
+       return g_dbus_client_ref(client);
+}
+
+GDBusClient *g_dbus_client_ref(GDBusClient *client)
+{
+       if (client == NULL)
+               return NULL;
+
+       __sync_fetch_and_add(&client->ref_count, 1);
+
+       return client;
+}
+
+void g_dbus_client_unref(GDBusClient *client)
+{
+       unsigned int i;
+
+       if (client == NULL)
+               return;
+
+       if (__sync_sub_and_fetch(&client->ref_count, 1) > 0)
+               return;
+
+       if (client->pending_call != NULL) {
+               dbus_pending_call_cancel(client->pending_call);
+               dbus_pending_call_unref(client->pending_call);
+       }
+
+       if (client->get_objects_call != NULL) {
+               dbus_pending_call_cancel(client->get_objects_call);
+               dbus_pending_call_unref(client->get_objects_call);
+       }
+
+       for (i = 0; i < client->match_rules->len; i++) {
+               modify_match(client->dbus_conn, "RemoveMatch",
+                               g_ptr_array_index(client->match_rules, i));
+       }
+
+       g_ptr_array_free(client->match_rules, TRUE);
+
+       dbus_connection_remove_filter(client->dbus_conn,
+                                               message_filter, client);
+
+       g_list_free_full(client->proxy_list, proxy_free);
+
+       if (client->disconn_func)
+               client->disconn_func(client->dbus_conn, client->disconn_data);
+
+       g_dbus_remove_watch(client->dbus_conn, client->watch);
+       g_dbus_remove_watch(client->dbus_conn, client->added_watch);
+       g_dbus_remove_watch(client->dbus_conn, client->removed_watch);
+
+       dbus_connection_unref(client->dbus_conn);
+
+       g_free(client->service_name);
+       g_free(client->base_path);
+
+       g_free(client);
+}
+
+gboolean g_dbus_client_set_connect_watch(GDBusClient *client,
+                               GDBusWatchFunction function, void *user_data)
+{
+       if (client == NULL)
+               return FALSE;
+
+       client->connect_func = function;
+       client->connect_data = user_data;
+
+       return TRUE;
+}
+
+gboolean g_dbus_client_set_disconnect_watch(GDBusClient *client,
+                               GDBusWatchFunction function, void *user_data)
+{
+       if (client == NULL)
+               return FALSE;
+
+       client->disconn_func = function;
+       client->disconn_data = user_data;
+
+       return TRUE;
+}
+
+gboolean g_dbus_client_set_signal_watch(GDBusClient *client,
+                               GDBusMessageFunction function, void *user_data)
+{
+       if (client == NULL)
+               return FALSE;
+
+       client->signal_func = function;
+       client->signal_data = user_data;
+
+       return TRUE;
+}
+
+gboolean g_dbus_client_set_proxy_handlers(GDBusClient *client,
+                                       GDBusProxyFunction proxy_added,
+                                       GDBusProxyFunction proxy_removed,
+                                       GDBusPropertyFunction property_changed,
+                                       void *user_data)
+{
+       if (client == NULL)
+               return FALSE;
+
+       client->proxy_added = proxy_added;
+       client->proxy_removed = proxy_removed;
+       client->property_changed = property_changed;
+       client->user_data = user_data;
+
+       get_managed_objects(client);
+
+       return TRUE;
+}
index a0583e6..9542109 100644 (file)
@@ -31,9 +31,23 @@ extern "C" {
 #include <dbus/dbus.h>
 #include <glib.h>
 
+typedef enum GDBusMethodFlags GDBusMethodFlags;
+typedef enum GDBusSignalFlags GDBusSignalFlags;
+typedef enum GDBusPropertyFlags GDBusPropertyFlags;
+typedef enum GDBusSecurityFlags GDBusSecurityFlags;
+
+typedef struct GDBusArgInfo GDBusArgInfo;
+typedef struct GDBusMethodTable GDBusMethodTable;
+typedef struct GDBusSignalTable GDBusSignalTable;
+typedef struct GDBusPropertyTable GDBusPropertyTable;
+typedef struct GDBusSecurityTable GDBusSecurityTable;
+
 typedef void (* GDBusWatchFunction) (DBusConnection *connection,
                                                        void *user_data);
 
+typedef void (* GDBusMessageFunction) (DBusConnection *connection,
+                                        DBusMessage *message, void *user_data);
+
 typedef gboolean (* GDBusSignalFunction) (DBusConnection *connection,
                                        DBusMessage *message, void *user_data);
 
@@ -55,6 +69,18 @@ typedef void (* GDBusDestroyFunction) (void *user_data);
 typedef DBusMessage * (* GDBusMethodFunction) (DBusConnection *connection,
                                        DBusMessage *message, void *user_data);
 
+typedef gboolean (*GDBusPropertyGetter)(const GDBusPropertyTable *property,
+                                       DBusMessageIter *iter, void *data);
+
+typedef guint32 GDBusPendingPropertySet;
+
+typedef void (*GDBusPropertySetter)(const GDBusPropertyTable *property,
+                       DBusMessageIter *value, GDBusPendingPropertySet id,
+                       void *data);
+
+typedef gboolean (*GDBusPropertyExists)(const GDBusPropertyTable *property,
+                                                               void *data);
+
 typedef guint32 GDBusPendingReply;
 
 typedef void (* GDBusSecurityFunction) (DBusConnection *connection,
@@ -62,53 +88,134 @@ typedef void (* GDBusSecurityFunction) (DBusConnection *connection,
                                                gboolean interaction,
                                                GDBusPendingReply pending);
 
-typedef enum {
-       G_DBUS_METHOD_FLAG_DEPRECATED = (1 << 0),
-       G_DBUS_METHOD_FLAG_NOREPLY    = (1 << 1),
-       G_DBUS_METHOD_FLAG_ASYNC      = (1 << 2),
-} GDBusMethodFlags;
+enum GDBusFlags {
+       G_DBUS_FLAG_ENABLE_EXPERIMENTAL = (1 << 0),
+};
 
-typedef enum {
-       G_DBUS_SIGNAL_FLAG_DEPRECATED = (1 << 0),
-} GDBusSignalFlags;
+enum GDBusMethodFlags {
+       G_DBUS_METHOD_FLAG_DEPRECATED   = (1 << 0),
+       G_DBUS_METHOD_FLAG_NOREPLY      = (1 << 1),
+       G_DBUS_METHOD_FLAG_ASYNC        = (1 << 2),
+       G_DBUS_METHOD_FLAG_EXPERIMENTAL = (1 << 3),
+};
 
-typedef enum {
-       G_DBUS_PROPERTY_FLAG_DEPRECATED = (1 << 0),
-} GDBusPropertyFlags;
+enum GDBusSignalFlags {
+       G_DBUS_SIGNAL_FLAG_DEPRECATED   = (1 << 0),
+       G_DBUS_SIGNAL_FLAG_EXPERIMENTAL = (1 << 1),
+};
 
-typedef enum {
+enum GDBusPropertyFlags {
+       G_DBUS_PROPERTY_FLAG_DEPRECATED   = (1 << 0),
+       G_DBUS_PROPERTY_FLAG_EXPERIMENTAL = (1 << 1),
+};
+
+enum GDBusSecurityFlags {
        G_DBUS_SECURITY_FLAG_DEPRECATED        = (1 << 0),
        G_DBUS_SECURITY_FLAG_BUILTIN           = (1 << 1),
        G_DBUS_SECURITY_FLAG_ALLOW_INTERACTION = (1 << 2),
-} GDBusSecurityFlags;
+};
 
-typedef struct {
+struct GDBusArgInfo {
        const char *name;
        const char *signature;
-       const char *reply;
+};
+
+struct GDBusMethodTable {
+       const char *name;
        GDBusMethodFunction function;
        GDBusMethodFlags flags;
        unsigned int privilege;
-} GDBusMethodTable;
+       const GDBusArgInfo *in_args;
+       const GDBusArgInfo *out_args;
+};
 
-typedef struct {
+struct GDBusSignalTable {
        const char *name;
-       const char *signature;
        GDBusSignalFlags flags;
-} GDBusSignalTable;
+       const GDBusArgInfo *args;
+};
 
-typedef struct {
+struct GDBusPropertyTable {
        const char *name;
        const char *type;
+       GDBusPropertyGetter get;
+       GDBusPropertySetter set;
+       GDBusPropertyExists exists;
        GDBusPropertyFlags flags;
-} GDBusPropertyTable;
+};
 
-typedef struct {
+struct GDBusSecurityTable {
        unsigned int privilege;
        const char *action;
        GDBusSecurityFlags flags;
        GDBusSecurityFunction function;
-} GDBusSecurityTable;
+};
+
+#define GDBUS_ARGS(args...) (const GDBusArgInfo[]) { args, { } }
+
+#define GDBUS_METHOD(_name, _in_args, _out_args, _function) \
+       .name = _name, \
+       .in_args = _in_args, \
+       .out_args = _out_args, \
+       .function = _function
+
+#define GDBUS_ASYNC_METHOD(_name, _in_args, _out_args, _function) \
+       .name = _name, \
+       .in_args = _in_args, \
+       .out_args = _out_args, \
+       .function = _function, \
+       .flags = G_DBUS_METHOD_FLAG_ASYNC
+
+#define GDBUS_DEPRECATED_METHOD(_name, _in_args, _out_args, _function) \
+       .name = _name, \
+       .in_args = _in_args, \
+       .out_args = _out_args, \
+       .function = _function, \
+       .flags = G_DBUS_METHOD_FLAG_DEPRECATED
+
+#define GDBUS_DEPRECATED_ASYNC_METHOD(_name, _in_args, _out_args, _function) \
+       .name = _name, \
+       .in_args = _in_args, \
+       .out_args = _out_args, \
+       .function = _function, \
+       .flags = G_DBUS_METHOD_FLAG_ASYNC | G_DBUS_METHOD_FLAG_DEPRECATED
+
+#define GDBUS_EXPERIMENTAL_METHOD(_name, _in_args, _out_args, _function) \
+       .name = _name, \
+       .in_args = _in_args, \
+       .out_args = _out_args, \
+       .function = _function, \
+       .flags = G_DBUS_METHOD_FLAG_EXPERIMENTAL
+
+#define GDBUS_EXPERIMENTAL_ASYNC_METHOD(_name, _in_args, _out_args, _function) \
+       .name = _name, \
+       .in_args = _in_args, \
+       .out_args = _out_args, \
+       .function = _function, \
+       .flags = G_DBUS_METHOD_FLAG_ASYNC | G_DBUS_METHOD_FLAG_EXPERIMENTAL
+
+#define GDBUS_NOREPLY_METHOD(_name, _in_args, _out_args, _function) \
+       .name = _name, \
+       .in_args = _in_args, \
+       .out_args = _out_args, \
+       .function = _function, \
+       .flags = G_DBUS_METHOD_FLAG_NOREPLY
+
+#define GDBUS_SIGNAL(_name, _args) \
+       .name = _name, \
+       .args = _args
+
+#define GDBUS_DEPRECATED_SIGNAL(_name, _args) \
+       .name = _name, \
+       .args = _args, \
+       .flags = G_DBUS_SIGNAL_FLAG_DEPRECATED
+
+#define GDBUS_EXPERIMENTAL_SIGNAL(_name, _args) \
+       .name = _name, \
+       .args = _args, \
+       .flags = G_DBUS_SIGNAL_FLAG_EXPERIMENTAL
+
+void g_dbus_set_flags(int flags);
 
 gboolean g_dbus_register_interface(DBusConnection *connection,
                                        const char *path, const char *name,
@@ -143,6 +250,15 @@ DBusMessage *g_dbus_create_reply_valist(DBusMessage *message,
                                                int type, va_list args);
 
 gboolean g_dbus_send_message(DBusConnection *connection, DBusMessage *message);
+gboolean g_dbus_send_message_with_reply(DBusConnection *connection,
+                                       DBusMessage *message,
+                                       DBusPendingCall **call, int timeout);
+gboolean g_dbus_send_error(DBusConnection *connection, DBusMessage *message,
+                               const char *name, const char *format, ...)
+                                        __attribute__((format(printf, 4, 5)));
+gboolean g_dbus_send_error_valist(DBusConnection *connection,
+                                       DBusMessage *message, const char *name,
+                                       const char *format, va_list args);
 gboolean g_dbus_send_reply(DBusConnection *connection,
                                DBusMessage *message, int type, ...);
 gboolean g_dbus_send_reply_valist(DBusConnection *connection,
@@ -167,9 +283,89 @@ guint g_dbus_add_signal_watch(DBusConnection *connection,
                                const char *interface, const char *member,
                                GDBusSignalFunction function, void *user_data,
                                GDBusDestroyFunction destroy);
+guint g_dbus_add_properties_watch(DBusConnection *connection,
+                               const char *sender, const char *path,
+                               const char *interface,
+                               GDBusSignalFunction function, void *user_data,
+                               GDBusDestroyFunction destroy);
 gboolean g_dbus_remove_watch(DBusConnection *connection, guint tag);
 void g_dbus_remove_all_watches(DBusConnection *connection);
 
+void g_dbus_pending_property_success(GDBusPendingPropertySet id);
+void g_dbus_pending_property_error_valist(GDBusPendingReply id,
+                       const char *name, const char *format, va_list args);
+void g_dbus_pending_property_error(GDBusPendingReply id, const char *name,
+                                               const char *format, ...);
+void g_dbus_emit_property_changed(DBusConnection *connection,
+                               const char *path, const char *interface,
+                               const char *name);
+gboolean g_dbus_get_properties(DBusConnection *connection, const char *path,
+                               const char *interface, DBusMessageIter *iter);
+
+gboolean g_dbus_attach_object_manager(DBusConnection *connection);
+gboolean g_dbus_detach_object_manager(DBusConnection *connection);
+
+typedef struct GDBusClient GDBusClient;
+typedef struct GDBusProxy GDBusProxy;
+
+GDBusProxy *g_dbus_proxy_new(GDBusClient *client, const char *path,
+                                                       const char *interface);
+
+GDBusProxy *g_dbus_proxy_ref(GDBusProxy *proxy);
+void g_dbus_proxy_unref(GDBusProxy *proxy);
+
+const char *g_dbus_proxy_get_path(GDBusProxy *proxy);
+const char *g_dbus_proxy_get_interface(GDBusProxy *proxy);
+
+gboolean g_dbus_proxy_get_property(GDBusProxy *proxy, const char *name,
+                                                       DBusMessageIter *iter);
+
+gboolean g_dbus_proxy_refresh_property(GDBusProxy *proxy, const char *name);
+
+typedef void (* GDBusResultFunction) (const DBusError *error, void *user_data);
+
+gboolean g_dbus_proxy_set_property_basic(GDBusProxy *proxy,
+                               const char *name, int type, const void *value,
+                               GDBusResultFunction function, void *user_data,
+                               GDBusDestroyFunction destroy);
+
+typedef void (* GDBusSetupFunction) (DBusMessageIter *iter, void *user_data);
+typedef void (* GDBusReturnFunction) (DBusMessage *message, void *user_data);
+
+gboolean g_dbus_proxy_method_call(GDBusProxy *proxy, const char *method,
+                               GDBusSetupFunction setup,
+                               GDBusReturnFunction function, void *user_data,
+                               GDBusDestroyFunction destroy);
+
+typedef void (* GDBusProxyFunction) (GDBusProxy *proxy, void *user_data);
+typedef void (* GDBusPropertyFunction) (GDBusProxy *proxy, const char *name,
+                                       DBusMessageIter *iter, void *user_data);
+
+gboolean g_dbus_proxy_set_property_watch(GDBusProxy *proxy,
+                       GDBusPropertyFunction function, void *user_data);
+
+gboolean g_dbus_proxy_set_removed_watch(GDBusProxy *proxy,
+                       GDBusProxyFunction destroy, void *user_data);
+
+GDBusClient *g_dbus_client_new(DBusConnection *connection,
+                                       const char *service, const char *path);
+
+GDBusClient *g_dbus_client_ref(GDBusClient *client);
+void g_dbus_client_unref(GDBusClient *client);
+
+gboolean g_dbus_client_set_connect_watch(GDBusClient *client,
+                               GDBusWatchFunction function, void *user_data);
+gboolean g_dbus_client_set_disconnect_watch(GDBusClient *client,
+                               GDBusWatchFunction function, void *user_data);
+gboolean g_dbus_client_set_signal_watch(GDBusClient *client,
+                               GDBusMessageFunction function, void *user_data);
+
+gboolean g_dbus_client_set_proxy_handlers(GDBusClient *client,
+                                       GDBusProxyFunction proxy_added,
+                                       GDBusProxyFunction proxy_removed,
+                                       GDBusPropertyFunction property_changed,
+                                       void *user_data);
+
 #ifdef __cplusplus
 }
 #endif
index 8718da0..099b67f 100644 (file)
 #include <glib.h>
 #include <dbus/dbus.h>
 
-#ifdef NEED_DBUS_WATCH_GET_UNIX_FD
-#define dbus_watch_get_unix_fd dbus_watch_get_fd
-#endif
-
 #include "gdbus.h"
 
 #define DISPATCH_TIMEOUT  0
@@ -96,8 +92,9 @@ static gboolean watch_func(GIOChannel *chan, GIOCondition cond, gpointer data)
        struct watch_info *info = data;
        unsigned int flags = 0;
        DBusDispatchStatus status;
+       DBusConnection *conn;
 
-       dbus_connection_ref(info->conn);
+       conn = dbus_connection_ref(info->conn);
 
        if (cond & G_IO_IN)  flags |= DBUS_WATCH_READABLE;
        if (cond & G_IO_OUT) flags |= DBUS_WATCH_WRITABLE;
@@ -106,10 +103,10 @@ static gboolean watch_func(GIOChannel *chan, GIOCondition cond, gpointer data)
 
        dbus_watch_handle(info->watch, flags);
 
-       status = dbus_connection_get_dispatch_status(info->conn);
-       queue_dispatch(info->conn, status);
+       status = dbus_connection_get_dispatch_status(conn);
+       queue_dispatch(conn, status);
 
-       dbus_connection_unref(info->conn);
+       dbus_connection_unref(conn);
 
        return TRUE;
 }
index d17a101..b248cbb 100644 (file)
 #define error(fmt...)
 #define debug(fmt...)
 
+#define DBUS_INTERFACE_OBJECT_MANAGER "org.freedesktop.DBus.ObjectManager"
+
+#ifndef DBUS_ERROR_UNKNOWN_PROPERTY
+#define DBUS_ERROR_UNKNOWN_PROPERTY "org.freedesktop.DBus.Error.UnknownProperty"
+#endif
+
+#ifndef DBUS_ERROR_PROPERTY_READ_ONLY
+#define DBUS_ERROR_PROPERTY_READ_ONLY "org.freedesktop.DBus.Error.PropertyReadOnly"
+#endif
+
 struct generic_data {
        unsigned int refcount;
+       DBusConnection *conn;
+       char *path;
        GSList *interfaces;
+       GSList *objects;
+       GSList *added;
+       GSList *removed;
+       guint process_id;
+       gboolean pending_prop;
        char *introspect;
+       struct generic_data *parent;
 };
 
 struct interface_data {
@@ -48,6 +66,7 @@ struct interface_data {
        const GDBusMethodTable *methods;
        const GDBusSignalTable *signals;
        const GDBusPropertyTable *properties;
+       GSList *pending_prop;
        void *user_data;
        GDBusDestroyFunction destroy;
 };
@@ -59,94 +78,115 @@ struct security_data {
        void *iface_user_data;
 };
 
-static void print_arguments(GString *gstr, const char *sig,
-                                               const char *direction)
-{
-       int i;
-
-       for (i = 0; sig[i]; i++) {
-               char type[32];
-               int struct_level, dict_level;
-               unsigned int len;
-               gboolean complete;
-
-               complete = FALSE;
-               struct_level = dict_level = 0;
-               memset(type, 0, sizeof(type));
-
-               /* Gather enough data to have a single complete type */
-               for (len = 0; len < (sizeof(type) - 1) && sig[i]; len++, i++) {
-                       switch (sig[i]){
-                       case '(':
-                               struct_level++;
-                               break;
-                       case ')':
-                               struct_level--;
-                               if (struct_level <= 0 && dict_level <= 0)
-                                       complete = TRUE;
-                               break;
-                       case '{':
-                               dict_level++;
-                               break;
-                       case '}':
-                               dict_level--;
-                               if (struct_level <= 0 && dict_level <= 0)
-                                       complete = TRUE;
-                               break;
-                       case 'a':
-                               break;
-                       default:
-                               if (struct_level <= 0 && dict_level <= 0)
-                                       complete = TRUE;
-                               break;
-                       }
+struct property_data {
+       DBusConnection *conn;
+       GDBusPendingPropertySet id;
+       DBusMessage *message;
+};
 
-                       type[len] = sig[i];
+static int global_flags = 0;
+static struct generic_data *root;
+static GSList *pending = NULL;
 
-                       if (complete)
-                               break;
-               }
+static gboolean process_changes(gpointer user_data);
+static void process_properties_from_interface(struct generic_data *data,
+                                               struct interface_data *iface);
+static void process_property_changes(struct generic_data *data);
 
+static void print_arguments(GString *gstr, const GDBusArgInfo *args,
+                                               const char *direction)
+{
+       for (; args && args->name; args++) {
+               g_string_append_printf(gstr,
+                                       "<arg name=\"%s\" type=\"%s\"",
+                                       args->name, args->signature);
 
                if (direction)
                        g_string_append_printf(gstr,
-                                       "\t\t\t<arg type=\"%s\" direction=\"%s\"/>\n",
-                                       type, direction);
+                                       " direction=\"%s\"/>\n", direction);
                else
-                       g_string_append_printf(gstr,
-                                       "\t\t\t<arg type=\"%s\"/>\n",
-                                       type);
+                       g_string_append_printf(gstr, "/>\n");
+
        }
 }
 
+#define G_DBUS_ANNOTATE(name_, value_)                         \
+       "<annotation name=\"org.freedesktop.DBus." name_ "\" "  \
+       "value=\"" value_ "\"/>"
+
+#define G_DBUS_ANNOTATE_DEPRECATED \
+       G_DBUS_ANNOTATE("Deprecated", "true")
+
+#define G_DBUS_ANNOTATE_NOREPLY \
+       G_DBUS_ANNOTATE("Method.NoReply", "true")
+
+static gboolean check_experimental(int flags, int flag)
+{
+       if (!(flags & flag))
+               return FALSE;
+
+       return !(global_flags & G_DBUS_FLAG_ENABLE_EXPERIMENTAL);
+}
+
 static void generate_interface_xml(GString *gstr, struct interface_data *iface)
 {
        const GDBusMethodTable *method;
        const GDBusSignalTable *signal;
+       const GDBusPropertyTable *property;
 
        for (method = iface->methods; method && method->name; method++) {
-               if (!strlen(method->signature) && !strlen(method->reply))
-                       g_string_append_printf(gstr, "\t\t<method name=\"%s\"/>\n",
-                                                               method->name);
-               else {
-                       g_string_append_printf(gstr, "\t\t<method name=\"%s\">\n",
+               if (check_experimental(method->flags,
+                                       G_DBUS_METHOD_FLAG_EXPERIMENTAL))
+                       continue;
+
+               g_string_append_printf(gstr, "<method name=\"%s\">",
                                                                method->name);
-                       print_arguments(gstr, method->signature, "in");
-                       print_arguments(gstr, method->reply, "out");
-                       g_string_append_printf(gstr, "\t\t</method>\n");
-               }
+               print_arguments(gstr, method->in_args, "in");
+               print_arguments(gstr, method->out_args, "out");
+
+               if (method->flags & G_DBUS_METHOD_FLAG_DEPRECATED)
+                       g_string_append_printf(gstr,
+                                               G_DBUS_ANNOTATE_DEPRECATED);
+
+               if (method->flags & G_DBUS_METHOD_FLAG_NOREPLY)
+                       g_string_append_printf(gstr, G_DBUS_ANNOTATE_NOREPLY);
+
+               g_string_append_printf(gstr, "</method>");
        }
 
        for (signal = iface->signals; signal && signal->name; signal++) {
-               if (!strlen(signal->signature))
-                       g_string_append_printf(gstr, "\t\t<signal name=\"%s\"/>\n",
-                                                               signal->name);
-               else {
-                       g_string_append_printf(gstr, "\t\t<signal name=\"%s\">\n",
+               if (check_experimental(signal->flags,
+                                       G_DBUS_SIGNAL_FLAG_EXPERIMENTAL))
+                       continue;
+
+               g_string_append_printf(gstr, "<signal name=\"%s\">",
                                                                signal->name);
-                       print_arguments(gstr, signal->signature, NULL);
-                       g_string_append_printf(gstr, "\t\t</signal>\n");
-               }
+               print_arguments(gstr, signal->args, NULL);
+
+               if (signal->flags & G_DBUS_SIGNAL_FLAG_DEPRECATED)
+                       g_string_append_printf(gstr,
+                                               G_DBUS_ANNOTATE_DEPRECATED);
+
+               g_string_append_printf(gstr, "</signal>\n");
+       }
+
+       for (property = iface->properties; property && property->name;
+                                                               property++) {
+               if (check_experimental(property->flags,
+                                       G_DBUS_PROPERTY_FLAG_EXPERIMENTAL))
+                       continue;
+
+               g_string_append_printf(gstr, "<property name=\"%s\""
+                                       " type=\"%s\" access=\"%s%s\">",
+                                       property->name, property->type,
+                                       property->get ? "read" : "",
+                                       property->set ? "write" : "");
+
+               if (property->flags & G_DBUS_PROPERTY_FLAG_DEPRECATED)
+                       g_string_append_printf(gstr,
+                                               G_DBUS_ANNOTATE_DEPRECATED);
+
+               g_string_append_printf(gstr, "</property>");
        }
 }
 
@@ -162,30 +202,30 @@ static void generate_introspection_xml(DBusConnection *conn,
 
        gstr = g_string_new(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE);
 
-       g_string_append_printf(gstr, "<node>\n");
+       g_string_append_printf(gstr, "<node>");
 
        for (list = data->interfaces; list; list = list->next) {
                struct interface_data *iface = list->data;
 
-               g_string_append_printf(gstr, "\t<interface name=\"%s\">\n",
+               g_string_append_printf(gstr, "<interface name=\"%s\">",
                                                                iface->name);
 
                generate_interface_xml(gstr, iface);
 
-               g_string_append_printf(gstr, "\t</interface>\n");
+               g_string_append_printf(gstr, "</interface>");
        }
 
        if (!dbus_connection_list_registered(conn, path, &children))
                goto done;
 
        for (i = 0; children[i]; i++)
-               g_string_append_printf(gstr, "\t<node name=\"%s\"/>\n",
+               g_string_append_printf(gstr, "<node name=\"%s\"/>",
                                                                children[i]);
 
        dbus_free_string_array(children);
 
 done:
-       g_string_append_printf(gstr, "</node>\n");
+       g_string_append_printf(gstr, "</node>");
 
        data->introspect = g_string_free(gstr, FALSE);
 }
@@ -196,11 +236,6 @@ static DBusMessage *introspect(DBusConnection *connection,
        struct generic_data *data = user_data;
        DBusMessage *reply;
 
-       if (!dbus_message_has_signature(message, DBUS_TYPE_INVALID_AS_STRING)) {
-               error("Unexpected signature to introspect call");
-               return NULL;
-       }
-
        if (data->introspect == NULL)
                generate_introspection_xml(connection, data,
                                                dbus_message_get_path(message));
@@ -237,8 +272,7 @@ static DBusHandlerResult process_message(DBusConnection *connection,
        if (reply == NULL)
                return DBUS_HANDLER_RESULT_NEED_MEMORY;
 
-       dbus_connection_send(connection, reply, NULL);
-       dbus_message_unref(reply);
+       g_dbus_send_message(connection, reply);
 
        return DBUS_HANDLER_RESULT_HANDLED;
 }
@@ -253,7 +287,7 @@ void g_dbus_pending_success(DBusConnection *connection,
 {
        GSList *list;
 
-        for (list = pending_security; list; list = list->next) {
+       for (list = pending_security; list; list = list->next) {
                struct security_data *secdata = list->data;
 
                if (secdata->pending != pending)
@@ -267,7 +301,7 @@ void g_dbus_pending_success(DBusConnection *connection,
                dbus_message_unref(secdata->message);
                g_free(secdata);
                return;
-        }
+       }
 }
 
 void g_dbus_pending_error_valist(DBusConnection *connection,
@@ -276,26 +310,21 @@ void g_dbus_pending_error_valist(DBusConnection *connection,
 {
        GSList *list;
 
-        for (list = pending_security; list; list = list->next) {
+       for (list = pending_security; list; list = list->next) {
                struct security_data *secdata = list->data;
-               DBusMessage *reply;
 
                if (secdata->pending != pending)
                        continue;
 
                pending_security = g_slist_remove(pending_security, secdata);
 
-               reply = g_dbus_create_error_valist(secdata->message,
+               g_dbus_send_error_valist(connection, secdata->message,
                                                        name, format, args);
-               if (reply != NULL) {
-                       dbus_connection_send(connection, reply, NULL);
-                       dbus_message_unref(reply);
-               }
 
                dbus_message_unref(secdata->message);
                g_free(secdata);
                return;
-        }
+       }
 }
 
 void g_dbus_pending_error(DBusConnection *connection,
@@ -391,12 +420,178 @@ static gboolean check_privilege(DBusConnection *conn, DBusMessage *msg,
        return FALSE;
 }
 
-static void generic_unregister(DBusConnection *connection, void *user_data)
+static GDBusPendingPropertySet next_pending_property = 1;
+static GSList *pending_property_set;
+
+static struct property_data *remove_pending_property_data(
+                                               GDBusPendingPropertySet id)
 {
-       struct generic_data *data = user_data;
+       struct property_data *propdata;
+       GSList *l;
 
-       g_free(data->introspect);
-       g_free(data);
+       for (l = pending_property_set; l != NULL; l = l->next) {
+               propdata = l->data;
+               if (propdata->id != id)
+                       continue;
+
+               break;
+       }
+
+       if (l == NULL)
+               return NULL;
+
+       pending_property_set = g_slist_delete_link(pending_property_set, l);
+
+       return propdata;
+}
+
+void g_dbus_pending_property_success(GDBusPendingPropertySet id)
+{
+       struct property_data *propdata;
+
+       propdata = remove_pending_property_data(id);
+       if (propdata == NULL)
+               return;
+
+       g_dbus_send_reply(propdata->conn, propdata->message,
+                                                       DBUS_TYPE_INVALID);
+       dbus_message_unref(propdata->message);
+       g_free(propdata);
+}
+
+void g_dbus_pending_property_error_valist(GDBusPendingReply id,
+                                       const char *name, const char *format,
+                                       va_list args)
+{
+       struct property_data *propdata;
+
+       propdata = remove_pending_property_data(id);
+       if (propdata == NULL)
+               return;
+
+       g_dbus_send_error_valist(propdata->conn, propdata->message, name,
+                                                               format, args);
+
+       dbus_message_unref(propdata->message);
+       g_free(propdata);
+}
+
+void g_dbus_pending_property_error(GDBusPendingReply id, const char *name,
+                                               const char *format, ...)
+{
+       va_list args;
+
+       va_start(args, format);
+
+       g_dbus_pending_property_error_valist(id, name, format, args);
+
+       va_end(args);
+}
+
+static void reset_parent(gpointer data, gpointer user_data)
+{
+       struct generic_data *child = data;
+       struct generic_data *parent = user_data;
+
+       child->parent = parent;
+}
+
+static void append_property(struct interface_data *iface,
+                       const GDBusPropertyTable *p, DBusMessageIter *dict)
+{
+       DBusMessageIter entry, value;
+
+       dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL,
+                                                               &entry);
+       dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &p->name);
+       dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, p->type,
+                                                               &value);
+
+       p->get(p, &value, iface->user_data);
+
+       dbus_message_iter_close_container(&entry, &value);
+       dbus_message_iter_close_container(dict, &entry);
+}
+
+static void append_properties(struct interface_data *data,
+                                                       DBusMessageIter *iter)
+{
+       DBusMessageIter dict;
+       const GDBusPropertyTable *p;
+
+       dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+                               DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                               DBUS_TYPE_STRING_AS_STRING
+                               DBUS_TYPE_VARIANT_AS_STRING
+                               DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+       for (p = data->properties; p && p->name; p++) {
+               if (check_experimental(p->flags,
+                                       G_DBUS_PROPERTY_FLAG_EXPERIMENTAL))
+                       continue;
+
+               if (p->get == NULL)
+                       continue;
+
+               if (p->exists != NULL && !p->exists(p, data->user_data))
+                       continue;
+
+               append_property(data, p, &dict);
+       }
+
+       dbus_message_iter_close_container(iter, &dict);
+}
+
+static void append_interface(gpointer data, gpointer user_data)
+{
+       struct interface_data *iface = data;
+       DBusMessageIter *array = user_data;
+       DBusMessageIter entry;
+
+       dbus_message_iter_open_container(array, DBUS_TYPE_DICT_ENTRY, NULL,
+                                                               &entry);
+       dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &iface->name);
+       append_properties(data, &entry);
+       dbus_message_iter_close_container(array, &entry);
+}
+
+static void emit_interfaces_added(struct generic_data *data)
+{
+       DBusMessage *signal;
+       DBusMessageIter iter, array;
+
+       if (root == NULL || data == root)
+               return;
+
+       signal = dbus_message_new_signal(root->path,
+                                       DBUS_INTERFACE_OBJECT_MANAGER,
+                                       "InterfacesAdded");
+       if (signal == NULL)
+               return;
+
+       dbus_message_iter_init_append(signal, &iter);
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
+                                                               &data->path);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                               DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                               DBUS_TYPE_STRING_AS_STRING
+                               DBUS_TYPE_ARRAY_AS_STRING
+                               DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                               DBUS_TYPE_STRING_AS_STRING
+                               DBUS_TYPE_VARIANT_AS_STRING
+                               DBUS_DICT_ENTRY_END_CHAR_AS_STRING
+                               DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &array);
+
+       g_slist_foreach(data->added, append_interface, &array);
+       g_slist_free(data->added);
+       data->added = NULL;
+
+       dbus_message_iter_close_container(&iter, &array);
+
+       /* Use dbus_connection_send to avoid recursive calls to g_dbus_flush */
+       dbus_connection_send(data->conn, signal, NULL);
+       dbus_message_unref(signal);
 }
 
 static struct interface_data *find_interface(GSList *interfaces,
@@ -416,49 +611,83 @@ static struct interface_data *find_interface(GSList *interfaces,
        return NULL;
 }
 
-static DBusHandlerResult generic_message(DBusConnection *connection,
-                                       DBusMessage *message, void *user_data)
+static gboolean g_dbus_args_have_signature(const GDBusArgInfo *args,
+                                                       DBusMessage *message)
 {
-       struct generic_data *data = user_data;
-       struct interface_data *iface;
-       const GDBusMethodTable *method;
-       const char *interface;
+       const char *sig = dbus_message_get_signature(message);
+       const char *p = NULL;
 
-       interface = dbus_message_get_interface(message);
+       for (; args && args->signature && *sig; args++) {
+               p = args->signature;
 
-       iface = find_interface(data->interfaces, interface);
+               for (; *sig && *p; sig++, p++) {
+                       if (*p != *sig)
+                               return FALSE;
+               }
+       }
+
+       if (*sig || (p && *p) || (args && args->signature))
+               return FALSE;
+
+       return TRUE;
+}
+
+static void add_pending(struct generic_data *data)
+{
+       if (data->process_id > 0)
+               return;
+
+       data->process_id = g_idle_add(process_changes, data);
+
+       pending = g_slist_append(pending, data);
+}
+
+static gboolean remove_interface(struct generic_data *data, const char *name)
+{
+       struct interface_data *iface;
+
+       iface = find_interface(data->interfaces, name);
        if (iface == NULL)
-               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+               return FALSE;
 
-       for (method = iface->methods; method &&
-                       method->name && method->function; method++) {
-               if (dbus_message_is_method_call(message, iface->name,
-                                                       method->name) == FALSE)
-                       continue;
+       process_properties_from_interface(data, iface);
 
-               if (dbus_message_has_signature(message,
-                                               method->signature) == FALSE)
-                       continue;
+       data->interfaces = g_slist_remove(data->interfaces, iface);
 
-               if (check_privilege(connection, message, method,
-                                               iface->user_data) == TRUE)
-                       return DBUS_HANDLER_RESULT_HANDLED;
+       if (iface->destroy) {
+               iface->destroy(iface->user_data);
+               iface->user_data = NULL;
+       }
 
-               return process_message(connection, message, method,
-                                                       iface->user_data);
+       /*
+        * Interface being removed was just added, on the same mainloop
+        * iteration? Don't send any signal
+        */
+       if (g_slist_find(data->added, iface)) {
+               data->added = g_slist_remove(data->added, iface);
+               g_free(iface->name);
+               g_free(iface);
+               return TRUE;
        }
 
-       return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-}
+       if (data->parent == NULL) {
+               g_free(iface->name);
+               g_free(iface);
+               return TRUE;
+       }
 
-static DBusObjectPathVTable generic_table = {
-       .unregister_function    = generic_unregister,
-       .message_function       = generic_message,
-};
+       data->removed = g_slist_prepend(data->removed, iface->name);
+       g_free(iface);
+
+       add_pending(data);
+
+       return TRUE;
+}
 
-static void invalidate_parent_data(DBusConnection *conn, const char *child_path)
+static struct generic_data *invalidate_parent_data(DBusConnection *conn,
+                                               const char *child_path)
 {
-       struct generic_data *data = NULL;
+       struct generic_data *data = NULL, *child = NULL, *parent = NULL;
        char *parent_path, *slash;
 
        parent_path = g_strdup(child_path);
@@ -479,95 +708,564 @@ static void invalidate_parent_data(DBusConnection *conn, const char *child_path)
                goto done;
        }
 
-       invalidate_parent_data(conn, parent_path);
+       parent = invalidate_parent_data(conn, parent_path);
 
-       if (data == NULL)
-               goto done;
+       if (data == NULL) {
+               data = parent;
+               if (data == NULL)
+                       goto done;
+       }
 
        g_free(data->introspect);
        data->introspect = NULL;
 
+       if (!dbus_connection_get_object_path_data(conn, child_path,
+                                                       (void *) &child))
+               goto done;
+
+       if (child == NULL || g_slist_find(data->objects, child) != NULL)
+               goto done;
+
+       data->objects = g_slist_prepend(data->objects, child);
+       child->parent = data;
+
 done:
        g_free(parent_path);
+       return data;
 }
 
-static GDBusMethodTable introspect_methods[] = {
-       { "Introspect", "",     "s", introspect },
-       { }
-};
-
-static void add_interface(struct generic_data *data, const char *name,
-                               const GDBusMethodTable *methods,
-                               const GDBusSignalTable *signals,
-                               const GDBusPropertyTable *properties,
-                               void *user_data,
-                               GDBusDestroyFunction destroy)
+static inline const GDBusPropertyTable *find_property(const GDBusPropertyTable *properties,
+                                                       const char *name)
 {
-       struct interface_data *iface;
-
-       iface = g_new0(struct interface_data, 1);
-       iface->name = g_strdup(name);
-       iface->methods = methods;
-       iface->signals = signals;
-       iface->properties = properties;
-       iface->user_data = user_data;
-       iface->destroy = destroy;
+       const GDBusPropertyTable *p;
 
-       data->interfaces = g_slist_append(data->interfaces, iface);
-}
+       for (p = properties; p && p->name; p++) {
+               if (strcmp(name, p->name) != 0)
+                       continue;
 
-static struct generic_data *object_path_ref(DBusConnection *connection,
-                                                       const char *path)
-{
-       struct generic_data *data;
+               if (check_experimental(p->flags,
+                                       G_DBUS_PROPERTY_FLAG_EXPERIMENTAL))
+                       break;
 
-       if (dbus_connection_get_object_path_data(connection, path,
-                                               (void *) &data) == TRUE) {
-               if (data != NULL) {
-                       data->refcount++;
-                       return data;
-               }
+               return p;
        }
 
-       data = g_new0(struct generic_data, 1);
-       data->refcount = 1;
+       return NULL;
+}
 
-       data->introspect = g_strdup(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE "<node></node>");
+static DBusMessage *properties_get(DBusConnection *connection,
+                                       DBusMessage *message, void *user_data)
+{
+       struct generic_data *data = user_data;
+       struct interface_data *iface;
+       const GDBusPropertyTable *property;
+       const char *interface, *name;
+       DBusMessageIter iter, value;
+       DBusMessage *reply;
 
-       if (!dbus_connection_register_object_path(connection, path,
-                                               &generic_table, data)) {
-               g_free(data->introspect);
-               g_free(data);
+       if (!dbus_message_get_args(message, NULL,
+                                       DBUS_TYPE_STRING, &interface,
+                                       DBUS_TYPE_STRING, &name,
+                                       DBUS_TYPE_INVALID))
                return NULL;
-       }
 
-       invalidate_parent_data(connection, path);
+       iface = find_interface(data->interfaces, interface);
+       if (iface == NULL)
+               return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS,
+                               "No such interface '%s'", interface);
 
-       add_interface(data, DBUS_INTERFACE_INTROSPECTABLE,
-                       introspect_methods, NULL, NULL, data, NULL);
+       property = find_property(iface->properties, name);
+       if (property == NULL)
+               return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS,
+                               "No such property '%s'", name);
 
-       return data;
-}
+       if (property->exists != NULL &&
+                       !property->exists(property, iface->user_data))
+               return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS,
+                                       "No such property '%s'", name);
 
-static gboolean remove_interface(struct generic_data *data, const char *name)
-{
-       struct interface_data *iface;
+       if (property->get == NULL)
+               return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS,
+                               "Property '%s' is not readable", name);
 
-       iface = find_interface(data->interfaces, name);
+       reply = dbus_message_new_method_return(message);
+       if (reply == NULL)
+               return NULL;
+
+       dbus_message_iter_init_append(reply, &iter);
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
+                                               property->type, &value);
+
+       if (!property->get(property, &value, iface->user_data)) {
+               dbus_message_unref(reply);
+               return NULL;
+       }
+
+       dbus_message_iter_close_container(&iter, &value);
+
+       return reply;
+}
+
+static DBusMessage *properties_get_all(DBusConnection *connection,
+                                       DBusMessage *message, void *user_data)
+{
+       struct generic_data *data = user_data;
+       struct interface_data *iface;
+       const char *interface;
+       DBusMessageIter iter;
+       DBusMessage *reply;
+
+       if (!dbus_message_get_args(message, NULL,
+                                       DBUS_TYPE_STRING, &interface,
+                                       DBUS_TYPE_INVALID))
+               return NULL;
+
+       iface = find_interface(data->interfaces, interface);
        if (iface == NULL)
-               return FALSE;
+               return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS,
+                                       "No such interface '%s'", interface);
 
-       data->interfaces = g_slist_remove(data->interfaces, iface);
+       reply = dbus_message_new_method_return(message);
+       if (reply == NULL)
+               return NULL;
 
-       if (iface->destroy)
-               iface->destroy(iface->user_data);
+       dbus_message_iter_init_append(reply, &iter);
 
-       g_free(iface->name);
-       g_free(iface);
+       append_properties(iface, &iter);
+
+       return reply;
+}
+
+static DBusMessage *properties_set(DBusConnection *connection,
+                                       DBusMessage *message, void *user_data)
+{
+       struct generic_data *data = user_data;
+       DBusMessageIter iter, sub;
+       struct interface_data *iface;
+       const GDBusPropertyTable *property;
+       const char *name, *interface;
+       struct property_data *propdata;
+       gboolean valid_signature;
+       char *signature;
+
+       if (!dbus_message_iter_init(message, &iter))
+               return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS,
+                                                       "No arguments given");
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+               return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS,
+                                       "Invalid argument type: '%c'",
+                                       dbus_message_iter_get_arg_type(&iter));
+
+       dbus_message_iter_get_basic(&iter, &interface);
+       dbus_message_iter_next(&iter);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+               return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS,
+                                       "Invalid argument type: '%c'",
+                                       dbus_message_iter_get_arg_type(&iter));
+
+       dbus_message_iter_get_basic(&iter, &name);
+       dbus_message_iter_next(&iter);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
+               return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS,
+                                       "Invalid argument type: '%c'",
+                                       dbus_message_iter_get_arg_type(&iter));
+
+       dbus_message_iter_recurse(&iter, &sub);
+
+       iface = find_interface(data->interfaces, interface);
+       if (iface == NULL)
+               return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS,
+                                       "No such interface '%s'", interface);
+
+       property = find_property(iface->properties, name);
+       if (property == NULL)
+               return g_dbus_create_error(message,
+                                               DBUS_ERROR_UNKNOWN_PROPERTY,
+                                               "No such property '%s'", name);
+
+       if (property->set == NULL)
+               return g_dbus_create_error(message,
+                                       DBUS_ERROR_PROPERTY_READ_ONLY,
+                                       "Property '%s' is not writable", name);
+
+       if (property->exists != NULL &&
+                       !property->exists(property, iface->user_data))
+               return g_dbus_create_error(message,
+                                               DBUS_ERROR_UNKNOWN_PROPERTY,
+                                               "No such property '%s'", name);
+
+       signature = dbus_message_iter_get_signature(&sub);
+       valid_signature = strcmp(signature, property->type) ? FALSE : TRUE;
+       dbus_free(signature);
+       if (!valid_signature)
+               return g_dbus_create_error(message,
+                                       DBUS_ERROR_INVALID_SIGNATURE,
+                                       "Invalid signature for '%s'", name);
+
+       propdata = g_new(struct property_data, 1);
+       propdata->id = next_pending_property++;
+       propdata->message = dbus_message_ref(message);
+       propdata->conn = connection;
+       pending_property_set = g_slist_prepend(pending_property_set, propdata);
+
+       property->set(property, &sub, propdata->id, iface->user_data);
+
+       return NULL;
+}
+
+static const GDBusMethodTable properties_methods[] = {
+       { GDBUS_METHOD("Get",
+                       GDBUS_ARGS({ "interface", "s" }, { "name", "s" }),
+                       GDBUS_ARGS({ "value", "v" }),
+                       properties_get) },
+       { GDBUS_ASYNC_METHOD("Set",
+                       GDBUS_ARGS({ "interface", "s" }, { "name", "s" },
+                                                       { "value", "v" }),
+                       NULL,
+                       properties_set) },
+       { GDBUS_METHOD("GetAll",
+                       GDBUS_ARGS({ "interface", "s" }),
+                       GDBUS_ARGS({ "properties", "a{sv}" }),
+                       properties_get_all) },
+       { }
+};
+
+static const GDBusSignalTable properties_signals[] = {
+       { GDBUS_SIGNAL("PropertiesChanged",
+                       GDBUS_ARGS({ "interface", "s" },
+                                       { "changed_properties", "a{sv}" },
+                                       { "invalidated_properties", "as"})) },
+       { }
+};
+
+static void append_name(gpointer data, gpointer user_data)
+{
+       char *name = data;
+       DBusMessageIter *iter = user_data;
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &name);
+}
+
+static void emit_interfaces_removed(struct generic_data *data)
+{
+       DBusMessage *signal;
+       DBusMessageIter iter, array;
+
+       if (root == NULL || data == root)
+               return;
+
+       signal = dbus_message_new_signal(root->path,
+                                       DBUS_INTERFACE_OBJECT_MANAGER,
+                                       "InterfacesRemoved");
+       if (signal == NULL)
+               return;
+
+       dbus_message_iter_init_append(signal, &iter);
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
+                                                               &data->path);
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                                       DBUS_TYPE_STRING_AS_STRING, &array);
+
+       g_slist_foreach(data->removed, append_name, &array);
+       g_slist_free_full(data->removed, g_free);
+       data->removed = NULL;
+
+       dbus_message_iter_close_container(&iter, &array);
+
+       /* Use dbus_connection_send to avoid recursive calls to g_dbus_flush */
+       dbus_connection_send(data->conn, signal, NULL);
+       dbus_message_unref(signal);
+}
+
+static void remove_pending(struct generic_data *data)
+{
+       if (data->process_id > 0) {
+               g_source_remove(data->process_id);
+               data->process_id = 0;
+       }
+
+       pending = g_slist_remove(pending, data);
+}
+
+static gboolean process_changes(gpointer user_data)
+{
+       struct generic_data *data = user_data;
+
+       remove_pending(data);
+
+       if (data->added != NULL)
+               emit_interfaces_added(data);
+
+       /* Flush pending properties */
+       if (data->pending_prop == TRUE)
+               process_property_changes(data);
+
+       if (data->removed != NULL)
+               emit_interfaces_removed(data);
+
+       data->process_id = 0;
+
+       return FALSE;
+}
+
+static void generic_unregister(DBusConnection *connection, void *user_data)
+{
+       struct generic_data *data = user_data;
+       struct generic_data *parent = data->parent;
+
+       if (parent != NULL)
+               parent->objects = g_slist_remove(parent->objects, data);
+
+       if (data->process_id > 0) {
+               g_source_remove(data->process_id);
+               data->process_id = 0;
+               process_changes(data);
+       }
+
+       g_slist_foreach(data->objects, reset_parent, data->parent);
+       g_slist_free(data->objects);
+
+       dbus_connection_unref(data->conn);
+       g_free(data->introspect);
+       g_free(data->path);
+       g_free(data);
+}
+
+static DBusHandlerResult generic_message(DBusConnection *connection,
+                                       DBusMessage *message, void *user_data)
+{
+       struct generic_data *data = user_data;
+       struct interface_data *iface;
+       const GDBusMethodTable *method;
+       const char *interface;
+
+       interface = dbus_message_get_interface(message);
+
+       iface = find_interface(data->interfaces, interface);
+       if (iface == NULL)
+               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+       for (method = iface->methods; method &&
+                       method->name && method->function; method++) {
+
+               if (dbus_message_is_method_call(message, iface->name,
+                                                       method->name) == FALSE)
+                       continue;
+
+               if (check_experimental(method->flags,
+                                       G_DBUS_METHOD_FLAG_EXPERIMENTAL))
+                       return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+               if (g_dbus_args_have_signature(method->in_args,
+                                                       message) == FALSE)
+                       continue;
+
+               if (check_privilege(connection, message, method,
+                                               iface->user_data) == TRUE)
+                       return DBUS_HANDLER_RESULT_HANDLED;
+
+               return process_message(connection, message, method,
+                                                       iface->user_data);
+       }
+
+       return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static DBusObjectPathVTable generic_table = {
+       .unregister_function    = generic_unregister,
+       .message_function       = generic_message,
+};
+
+static const GDBusMethodTable introspect_methods[] = {
+       { GDBUS_METHOD("Introspect", NULL,
+                       GDBUS_ARGS({ "xml", "s" }), introspect) },
+       { }
+};
+
+static void append_interfaces(struct generic_data *data, DBusMessageIter *iter)
+{
+       DBusMessageIter array;
+       GSList *l;
+
+       dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+                               DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                               DBUS_TYPE_STRING_AS_STRING
+                               DBUS_TYPE_ARRAY_AS_STRING
+                               DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                               DBUS_TYPE_STRING_AS_STRING
+                               DBUS_TYPE_VARIANT_AS_STRING
+                               DBUS_DICT_ENTRY_END_CHAR_AS_STRING
+                               DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &array);
+
+       for (l = data->interfaces; l != NULL; l = l->next) {
+               if (g_slist_find(data->added, l->data))
+                       continue;
+
+               append_interface(l->data, &array);
+       }
+
+       dbus_message_iter_close_container(iter, &array);
+}
+
+static void append_object(gpointer data, gpointer user_data)
+{
+       struct generic_data *child = data;
+       DBusMessageIter *array = user_data;
+       DBusMessageIter entry;
+
+       dbus_message_iter_open_container(array, DBUS_TYPE_DICT_ENTRY, NULL,
+                                                               &entry);
+       dbus_message_iter_append_basic(&entry, DBUS_TYPE_OBJECT_PATH,
+                                                               &child->path);
+       append_interfaces(child, &entry);
+       dbus_message_iter_close_container(array, &entry);
+
+       g_slist_foreach(child->objects, append_object, user_data);
+}
+
+static DBusMessage *get_objects(DBusConnection *connection,
+                               DBusMessage *message, void *user_data)
+{
+       struct generic_data *data = user_data;
+       DBusMessage *reply;
+       DBusMessageIter iter;
+       DBusMessageIter array;
+
+       reply = dbus_message_new_method_return(message);
+       if (reply == NULL)
+               return NULL;
+
+       dbus_message_iter_init_append(reply, &iter);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                                       DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                                       DBUS_TYPE_OBJECT_PATH_AS_STRING
+                                       DBUS_TYPE_ARRAY_AS_STRING
+                                       DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                                       DBUS_TYPE_STRING_AS_STRING
+                                       DBUS_TYPE_ARRAY_AS_STRING
+                                       DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                                       DBUS_TYPE_STRING_AS_STRING
+                                       DBUS_TYPE_VARIANT_AS_STRING
+                                       DBUS_DICT_ENTRY_END_CHAR_AS_STRING
+                                       DBUS_DICT_ENTRY_END_CHAR_AS_STRING
+                                       DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+                                       &array);
+
+       g_slist_foreach(data->objects, append_object, &array);
+
+       dbus_message_iter_close_container(&iter, &array);
+
+       return reply;
+}
+
+static const GDBusMethodTable manager_methods[] = {
+       { GDBUS_METHOD("GetManagedObjects", NULL,
+               GDBUS_ARGS({ "objects", "a{oa{sa{sv}}}" }), get_objects) },
+       { }
+};
+
+static const GDBusSignalTable manager_signals[] = {
+       { GDBUS_SIGNAL("InterfacesAdded",
+               GDBUS_ARGS({ "object", "o" },
+                               { "interfaces", "a{sa{sv}}" })) },
+       { GDBUS_SIGNAL("InterfacesRemoved",
+               GDBUS_ARGS({ "object", "o" }, { "interfaces", "as" })) },
+       { }
+};
+
+static gboolean add_interface(struct generic_data *data,
+                               const char *name,
+                               const GDBusMethodTable *methods,
+                               const GDBusSignalTable *signals,
+                               const GDBusPropertyTable *properties,
+                               void *user_data,
+                               GDBusDestroyFunction destroy)
+{
+       struct interface_data *iface;
+       const GDBusMethodTable *method;
+       const GDBusSignalTable *signal;
+       const GDBusPropertyTable *property;
+
+       for (method = methods; method && method->name; method++) {
+               if (!check_experimental(method->flags,
+                                       G_DBUS_METHOD_FLAG_EXPERIMENTAL))
+                       goto done;
+       }
+
+       for (signal = signals; signal && signal->name; signal++) {
+               if (!check_experimental(signal->flags,
+                                       G_DBUS_SIGNAL_FLAG_EXPERIMENTAL))
+                       goto done;
+       }
+
+       for (property = properties; property && property->name; property++) {
+               if (!check_experimental(property->flags,
+                                       G_DBUS_PROPERTY_FLAG_EXPERIMENTAL))
+                       goto done;
+       }
+
+       /* Nothing to register */
+       return FALSE;
+
+done:
+       iface = g_new0(struct interface_data, 1);
+       iface->name = g_strdup(name);
+       iface->methods = methods;
+       iface->signals = signals;
+       iface->properties = properties;
+       iface->user_data = user_data;
+       iface->destroy = destroy;
+
+       data->interfaces = g_slist_append(data->interfaces, iface);
+       if (data->parent == NULL)
+               return TRUE;
+
+       data->added = g_slist_append(data->added, iface);
+
+       add_pending(data);
 
        return TRUE;
 }
 
+static struct generic_data *object_path_ref(DBusConnection *connection,
+                                                       const char *path)
+{
+       struct generic_data *data;
+
+       if (dbus_connection_get_object_path_data(connection, path,
+                                               (void *) &data) == TRUE) {
+               if (data != NULL) {
+                       data->refcount++;
+                       return data;
+               }
+       }
+
+       data = g_new0(struct generic_data, 1);
+       data->conn = dbus_connection_ref(connection);
+       data->path = g_strdup(path);
+       data->refcount = 1;
+
+       data->introspect = g_strdup(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE "<node></node>");
+
+       if (!dbus_connection_register_object_path(connection, path,
+                                               &generic_table, data)) {
+               g_free(data->introspect);
+               g_free(data);
+               return NULL;
+       }
+
+       invalidate_parent_data(connection, path);
+
+       add_interface(data, DBUS_INTERFACE_INTROSPECTABLE, introspect_methods,
+                                               NULL, NULL, data, NULL);
+
+       return data;
+}
+
 static void object_path_unref(DBusConnection *connection, const char *path)
 {
        struct generic_data *data = NULL;
@@ -585,15 +1283,16 @@ static void object_path_unref(DBusConnection *connection, const char *path)
                return;
 
        remove_interface(data, DBUS_INTERFACE_INTROSPECTABLE);
+       remove_interface(data, DBUS_INTERFACE_PROPERTIES);
 
-       invalidate_parent_data(connection, path);
+       invalidate_parent_data(data->conn, data->path);
 
-       dbus_connection_unregister_object_path(connection, path);
+       dbus_connection_unregister_object_path(data->conn, data->path);
 }
 
 static gboolean check_signal(DBusConnection *conn, const char *path,
                                const char *interface, const char *name,
-                               const char **args)
+                               const GDBusArgInfo **args)
 {
        struct generic_data *data = NULL;
        struct interface_data *iface;
@@ -615,58 +1314,21 @@ static gboolean check_signal(DBusConnection *conn, const char *path,
        }
 
        for (signal = iface->signals; signal && signal->name; signal++) {
-               if (!strcmp(signal->name, name)) {
-                       *args = signal->signature;
-                       break;
-               }
-       }
-
-       if (*args == NULL) {
-               error("No signal named %s on interface %s", name, interface);
-               return FALSE;
-       }
-
-       return TRUE;
-}
-
-static dbus_bool_t emit_signal_valist(DBusConnection *conn,
-                                               const char *path,
-                                               const char *interface,
-                                               const char *name,
-                                               int first,
-                                               va_list var_args)
-{
-       DBusMessage *signal;
-       dbus_bool_t ret;
-       const char *signature, *args;
-
-       if (!check_signal(conn, path, interface, name, &args))
-               return FALSE;
-
-       signal = dbus_message_new_signal(path, interface, name);
-       if (signal == NULL) {
-               error("Unable to allocate new %s.%s signal", interface,  name);
-               return FALSE;
-       }
+               if (strcmp(signal->name, name) != 0)
+                       continue;
 
-       ret = dbus_message_append_args_valist(signal, first, var_args);
-       if (!ret)
-               goto fail;
+               if (signal->flags & G_DBUS_SIGNAL_FLAG_EXPERIMENTAL) {
+                       const char *env = g_getenv("GDBUS_EXPERIMENTAL");
+                       if (g_strcmp0(env, "1") != 0)
+                               break;
+               }
 
-       signature = dbus_message_get_signature(signal);
-       if (strcmp(args, signature) != 0) {
-               error("%s.%s: expected signature'%s' but got '%s'",
-                               interface, name, args, signature);
-               ret = FALSE;
-               goto fail;
+               *args = signal->args;
+               return TRUE;
        }
 
-       ret = dbus_connection_send(conn, signal, NULL);
-
-fail:
-       dbus_message_unref(signal);
-
-       return ret;
+       error("No signal named %s on interface %s", name, interface);
+       return FALSE;
 }
 
 gboolean g_dbus_register_interface(DBusConnection *connection,
@@ -688,8 +1350,17 @@ gboolean g_dbus_register_interface(DBusConnection *connection,
                return FALSE;
        }
 
-       add_interface(data, name, methods, signals,
-                       properties, user_data, destroy);
+       if (!add_interface(data, name, methods, signals, properties, user_data,
+                                                               destroy)) {
+               object_path_unref(connection, path);
+               return FALSE;
+       }
+
+       if (properties != NULL && !find_interface(data->interfaces,
+                                               DBUS_INTERFACE_PROPERTIES))
+               add_interface(data, DBUS_INTERFACE_PROPERTIES,
+                               properties_methods, properties_signals, NULL,
+                               data, NULL);
 
        g_free(data->introspect);
        data->introspect = NULL;
@@ -718,7 +1389,7 @@ gboolean g_dbus_unregister_interface(DBusConnection *connection,
        g_free(data->introspect);
        data->introspect = NULL;
 
-       object_path_unref(connection, path);
+       object_path_unref(connection, data->path);
 
        return TRUE;
 }
@@ -796,20 +1467,100 @@ DBusMessage *g_dbus_create_reply(DBusMessage *message, int type, ...)
        return reply;
 }
 
+static void g_dbus_flush(DBusConnection *connection)
+{
+       GSList *l;
+
+       for (l = pending; l;) {
+               struct generic_data *data = l->data;
+
+               l = l->next;
+               if (data->conn != connection)
+                       continue;
+
+               process_changes(data);
+       }
+}
+
 gboolean g_dbus_send_message(DBusConnection *connection, DBusMessage *message)
 {
-       dbus_bool_t result;
+       dbus_bool_t result = FALSE;
 
        if (dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_METHOD_CALL)
                dbus_message_set_no_reply(message, TRUE);
+       else if (dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_SIGNAL) {
+               const char *path = dbus_message_get_path(message);
+               const char *interface = dbus_message_get_interface(message);
+               const char *name = dbus_message_get_member(message);
+               const GDBusArgInfo *args;
+
+               if (!check_signal(connection, path, interface, name, &args))
+                       goto out;
+       }
+
+       /* Flush pending signal to guarantee message order */
+       g_dbus_flush(connection);
 
        result = dbus_connection_send(connection, message, NULL);
 
+out:
        dbus_message_unref(message);
 
        return result;
 }
 
+gboolean g_dbus_send_message_with_reply(DBusConnection *connection,
+                                       DBusMessage *message,
+                                       DBusPendingCall **call, int timeout)
+{
+       dbus_bool_t ret;
+
+       /* Flush pending signal to guarantee message order */
+       g_dbus_flush(connection);
+
+       ret = dbus_connection_send_with_reply(connection, message, call,
+                                                               timeout);
+
+       if (ret == TRUE && call != NULL && *call == NULL) {
+               error("Unable to send message (passing fd blocked?)");
+               return FALSE;
+       }
+
+       return ret;
+}
+
+gboolean g_dbus_send_error_valist(DBusConnection *connection,
+                                       DBusMessage *message, const char *name,
+                                       const char *format, va_list args)
+{
+       DBusMessage *error;
+       char str[1024];
+
+       vsnprintf(str, sizeof(str), format, args);
+
+       error = dbus_message_new_error(message, name, str);
+       if (error == NULL)
+               return FALSE;
+
+       return g_dbus_send_message(connection, error);
+}
+
+gboolean g_dbus_send_error(DBusConnection *connection, DBusMessage *message,
+                               const char *name, const char *format, ...)
+{
+       va_list args;
+       gboolean result;
+
+       va_start(args, format);
+
+       result = g_dbus_send_error_valist(connection, message, name,
+                                                       format, args);
+
+       va_end(args);
+
+       return result;
+}
+
 gboolean g_dbus_send_reply_valist(DBusConnection *connection,
                                DBusMessage *message, int type, va_list args)
 {
@@ -851,7 +1602,7 @@ gboolean g_dbus_emit_signal(DBusConnection *connection,
 
        va_start(args, type);
 
-       result = emit_signal_valist(connection, path, interface,
+       result = g_dbus_emit_signal_valist(connection, path, interface,
                                                        name, type, args);
 
        va_end(args);
@@ -863,6 +1614,209 @@ gboolean g_dbus_emit_signal_valist(DBusConnection *connection,
                                const char *path, const char *interface,
                                const char *name, int type, va_list args)
 {
-       return emit_signal_valist(connection, path, interface,
-                                                       name, type, args);
+       DBusMessage *signal;
+       dbus_bool_t ret;
+       const GDBusArgInfo *args_info;
+
+       if (!check_signal(connection, path, interface, name, &args_info))
+               return FALSE;
+
+       signal = dbus_message_new_signal(path, interface, name);
+       if (signal == NULL) {
+               error("Unable to allocate new %s.%s signal", interface,  name);
+               return FALSE;
+       }
+
+       ret = dbus_message_append_args_valist(signal, type, args);
+       if (!ret)
+               goto fail;
+
+       if (g_dbus_args_have_signature(args_info, signal) == FALSE) {
+               error("%s.%s: got unexpected signature '%s'", interface, name,
+                                       dbus_message_get_signature(signal));
+               ret = FALSE;
+               goto fail;
+       }
+
+       return g_dbus_send_message(connection, signal);
+
+fail:
+       dbus_message_unref(signal);
+
+       return ret;
+}
+
+static void process_properties_from_interface(struct generic_data *data,
+                                               struct interface_data *iface)
+{
+       GSList *l;
+       DBusMessage *signal;
+       DBusMessageIter iter, dict, array;
+       GSList *invalidated;
+
+       data->pending_prop = FALSE;
+
+       if (iface->pending_prop == NULL)
+               return;
+
+       signal = dbus_message_new_signal(data->path,
+                       DBUS_INTERFACE_PROPERTIES, "PropertiesChanged");
+       if (signal == NULL) {
+               error("Unable to allocate new " DBUS_INTERFACE_PROPERTIES
+                                               ".PropertiesChanged signal");
+               return;
+       }
+
+       iface->pending_prop = g_slist_reverse(iface->pending_prop);
+
+       dbus_message_iter_init_append(signal, &iter);
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &iface->name);
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                       DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                       DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+                       DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+       invalidated = NULL;
+
+       for (l = iface->pending_prop; l != NULL; l = l->next) {
+               GDBusPropertyTable *p = l->data;
+
+               if (p->get == NULL)
+                       continue;
+
+               if (p->exists != NULL && !p->exists(p, iface->user_data)) {
+                       invalidated = g_slist_prepend(invalidated, p);
+                       continue;
+               }
+
+               append_property(iface, p, &dict);
+       }
+
+       dbus_message_iter_close_container(&iter, &dict);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                               DBUS_TYPE_STRING_AS_STRING, &array);
+       for (l = invalidated; l != NULL; l = g_slist_next(l)) {
+               GDBusPropertyTable *p = l->data;
+
+               dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING,
+                                                               &p->name);
+       }
+       g_slist_free(invalidated);
+       dbus_message_iter_close_container(&iter, &array);
+
+       g_slist_free(iface->pending_prop);
+       iface->pending_prop = NULL;
+
+       /* Use dbus_connection_send to avoid recursive calls to g_dbus_flush */
+       dbus_connection_send(data->conn, signal, NULL);
+       dbus_message_unref(signal);
+}
+
+static void process_property_changes(struct generic_data *data)
+{
+       GSList *l;
+
+       for (l = data->interfaces; l != NULL; l = l->next) {
+               struct interface_data *iface = l->data;
+
+               process_properties_from_interface(data, iface);
+       }
+}
+
+void g_dbus_emit_property_changed(DBusConnection *connection,
+                               const char *path, const char *interface,
+                               const char *name)
+{
+       const GDBusPropertyTable *property;
+       struct generic_data *data;
+       struct interface_data *iface;
+
+       if (path == NULL)
+               return;
+
+       if (!dbus_connection_get_object_path_data(connection, path,
+                                       (void **) &data) || data == NULL)
+               return;
+
+       iface = find_interface(data->interfaces, interface);
+       if (iface == NULL)
+               return;
+
+       /*
+        * If ObjectManager is attached, don't emit property changed if
+        * interface is not yet published
+        */
+       if (root && g_slist_find(data->added, iface))
+               return;
+
+       property = find_property(iface->properties, name);
+       if (property == NULL) {
+               error("Could not find property %s in %p", name,
+                                                       iface->properties);
+               return;
+       }
+
+       if (g_slist_find(iface->pending_prop, (void *) property) != NULL)
+               return;
+
+       data->pending_prop = TRUE;
+       iface->pending_prop = g_slist_prepend(iface->pending_prop,
+                                               (void *) property);
+
+       add_pending(data);
+}
+
+gboolean g_dbus_get_properties(DBusConnection *connection, const char *path,
+                               const char *interface, DBusMessageIter *iter)
+{
+       struct generic_data *data;
+       struct interface_data *iface;
+
+       if (path == NULL)
+               return FALSE;
+
+       if (!dbus_connection_get_object_path_data(connection, path,
+                                       (void **) &data) || data == NULL)
+               return FALSE;
+
+       iface = find_interface(data->interfaces, interface);
+       if (iface == NULL)
+               return FALSE;
+
+       append_properties(iface, iter);
+
+       return TRUE;
+}
+
+gboolean g_dbus_attach_object_manager(DBusConnection *connection)
+{
+       struct generic_data *data;
+
+       data = object_path_ref(connection, "/");
+       if (data == NULL)
+               return FALSE;
+
+       add_interface(data, DBUS_INTERFACE_OBJECT_MANAGER,
+                                       manager_methods, manager_signals,
+                                       NULL, data, NULL);
+       root = data;
+
+       return TRUE;
+}
+
+gboolean g_dbus_detach_object_manager(DBusConnection *connection)
+{
+       if (!g_dbus_unregister_interface(connection, "/",
+                                       DBUS_INTERFACE_OBJECT_MANAGER))
+               return FALSE;
+
+       root = NULL;
+
+       return TRUE;
+}
+
+void g_dbus_set_flags(int flags)
+{
+       global_flags = flags;
 }
index fba58c3..0f99f4f 100644 (file)
@@ -78,7 +78,7 @@ struct filter_data {
        gboolean registered;
 };
 
-static struct filter_data *filter_data_find(DBusConnection *connection,
+static struct filter_data *filter_data_find_match(DBusConnection *connection,
                                                        const char *name,
                                                        const char *owner,
                                                        const char *path,
@@ -95,28 +95,39 @@ static struct filter_data *filter_data_find(DBusConnection *connection,
                if (connection != data->connection)
                        continue;
 
-               if (name && data->name &&
-                               g_str_equal(name, data->name) == FALSE)
+               if (g_strcmp0(name, data->name) != 0)
                        continue;
 
-               if (owner && data->owner &&
-                               g_str_equal(owner, data->owner) == FALSE)
+               if (g_strcmp0(owner, data->owner) != 0)
                        continue;
 
-               if (path && data->path &&
-                               g_str_equal(path, data->path) == FALSE)
+               if (g_strcmp0(path, data->path) != 0)
                        continue;
 
-               if (interface && data->interface &&
-                               g_str_equal(interface, data->interface) == FALSE)
+               if (g_strcmp0(interface, data->interface) != 0)
                        continue;
 
-               if (member && data->member &&
-                               g_str_equal(member, data->member) == FALSE)
+               if (g_strcmp0(member, data->member) != 0)
                        continue;
 
-               if (argument && data->argument &&
-                               g_str_equal(argument, data->argument) == FALSE)
+               if (g_strcmp0(argument, data->argument) != 0)
+                       continue;
+
+               return data;
+       }
+
+       return NULL;
+}
+
+static struct filter_data *filter_data_find(DBusConnection *connection)
+{
+       GSList *current;
+
+       for (current = listeners;
+                       current != NULL; current = current->next) {
+               struct filter_data *data = current->data;
+
+               if (connection != data->connection)
                        continue;
 
                return data;
@@ -204,7 +215,7 @@ static struct filter_data *filter_data_get(DBusConnection *connection,
        struct filter_data *data;
        const char *name = NULL, *owner = NULL;
 
-       if (filter_data_find(connection, NULL, NULL, NULL, NULL, NULL, NULL) == NULL) {
+       if (filter_data_find(connection) == NULL) {
                if (!dbus_connection_add_filter(connection,
                                        message_filter, NULL, NULL)) {
                        error("dbus_connection_add_filter() failed");
@@ -221,16 +232,16 @@ static struct filter_data *filter_data_get(DBusConnection *connection,
                name = sender;
 
 proceed:
-       data = filter_data_find(connection, name, owner, path, interface,
-                                       member, argument);
+       data = filter_data_find_match(connection, name, owner, path,
+                                               interface, member, argument);
        if (data)
                return data;
 
        data = g_new0(struct filter_data, 1);
 
        data->connection = dbus_connection_ref(connection);
-       data->name = name ? g_strdup(name) : NULL;
-       data->owner = owner ? g_strdup(owner) : NULL;
+       data->name = g_strdup(name);
+       data->owner = g_strdup(owner);
        data->path = g_strdup(path);
        data->interface = g_strdup(interface);
        data->member = g_strdup(member);
@@ -270,6 +281,11 @@ static void filter_data_free(struct filter_data *data)
 {
        GSList *l;
 
+       /* Remove filter if there are no listeners left for the connection */
+       if (filter_data_find(data->connection) == NULL)
+               dbus_connection_remove_filter(data->connection, message_filter,
+                                                                       NULL);
+
        for (l = data->callbacks; l != NULL; l = l->next)
                g_free(l->data);
 
@@ -349,8 +365,6 @@ static void service_data_free(struct service_data *data)
 static gboolean filter_data_remove_callback(struct filter_data *data,
                                                struct filter_callback *cb)
 {
-       DBusConnection *connection;
-
        data->callbacks = g_slist_remove(data->callbacks, cb);
        data->processed = g_slist_remove(data->processed, cb);
 
@@ -374,19 +388,9 @@ static gboolean filter_data_remove_callback(struct filter_data *data,
        if (data->registered && !remove_match(data))
                return FALSE;
 
-       connection = dbus_connection_ref(data->connection);
        listeners = g_slist_remove(listeners, data);
        filter_data_free(data);
 
-       /* Remove filter if there are no listeners left for the connection */
-       data = filter_data_find(connection, NULL, NULL, NULL, NULL, NULL,
-                                       NULL);
-       if (data == NULL)
-               dbus_connection_remove_filter(connection, message_filter,
-                                               NULL);
-
-       dbus_connection_unref(connection);
-
        return TRUE;
 }
 
@@ -502,6 +506,7 @@ static DBusHandlerResult message_filter(DBusConnection *connection,
 {
        struct filter_data *data;
        const char *sender, *path, *iface, *member, *arg = NULL;
+       GSList *current, *delete_listener = NULL;
 
        /* Only filter signals */
        if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_SIGNAL)
@@ -513,38 +518,68 @@ static DBusHandlerResult message_filter(DBusConnection *connection,
        member = dbus_message_get_member(message);
        dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &arg, DBUS_TYPE_INVALID);
 
-       /* Sender is always bus name */
-       data = filter_data_find(connection, NULL, sender, path, iface, member,
-                                       arg);
-       if (data == NULL) {
-               error("Got %s.%s signal which has no listeners", iface, member);
+       /* Sender is always the owner */
+       if (sender == NULL)
                return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-       }
 
-       if (data->handle_func) {
-               data->lock = TRUE;
+       for (current = listeners; current != NULL; current = current->next) {
+               data = current->data;
+
+               if (connection != data->connection)
+                       continue;
+
+               if (data->owner && g_str_equal(sender, data->owner) == FALSE)
+                       continue;
+
+               if (data->path && g_str_equal(path, data->path) == FALSE)
+                       continue;
+
+               if (data->interface && g_str_equal(iface,
+                                               data->interface) == FALSE)
+                       continue;
+
+               if (data->member && g_str_equal(member, data->member) == FALSE)
+                       continue;
+
+               if (data->argument && g_str_equal(arg,
+                                               data->argument) == FALSE)
+                       continue;
 
-               data->handle_func(connection, message, data);
+               if (data->handle_func) {
+                       data->lock = TRUE;
 
-               data->callbacks = data->processed;
-               data->processed = NULL;
-               data->lock = FALSE;
+                       data->handle_func(connection, message, data);
+
+                       data->callbacks = data->processed;
+                       data->processed = NULL;
+                       data->lock = FALSE;
+               }
+
+               if (!data->callbacks)
+                       delete_listener = g_slist_prepend(delete_listener,
+                                                               current);
        }
 
-       if (data->callbacks)
+       if (delete_listener == NULL)
                return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 
-       remove_match(data);
+       for (current = delete_listener; current != NULL;
+                                       current = delete_listener->next) {
+               GSList *l = current->data;
 
-       listeners = g_slist_remove(listeners, data);
-       filter_data_free(data);
+               data = l->data;
 
-       /* Remove filter if there no listener left for the connection */
-       data = filter_data_find(connection, NULL, NULL, NULL, NULL, NULL,
-                                       NULL);
-       if (data == NULL)
-               dbus_connection_remove_filter(connection, message_filter,
-                                               NULL);
+               /* Has any other callback added callbacks back to this data? */
+               if (data->callbacks != NULL)
+                       continue;
+
+               remove_match(data);
+               listeners = g_slist_delete_link(listeners, l);
+
+               filter_data_free(data);
+       }
+
+       g_slist_free(delete_listener);
 
        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 }
@@ -553,12 +588,16 @@ static gboolean update_service(void *user_data)
 {
        struct service_data *data = user_data;
        struct filter_callback *cb = data->callback;
+       DBusConnection *conn;
 
        update_name_cache(data->name, data->owner);
+       conn = dbus_connection_ref(data->conn);
+       service_data_free(data);
+
        if (cb->conn_func)
-               cb->conn_func(data->conn, cb->user_data);
+               cb->conn_func(conn, cb->user_data);
 
-       service_data_free(data);
+       dbus_connection_unref(conn);
 
        return FALSE;
 }
@@ -666,7 +705,7 @@ guint g_dbus_add_service_watch(DBusConnection *connection, const char *name,
        if (data == NULL)
                return 0;
 
-       cb = filter_data_add_callback(data, connect, disconnect, NULL, NULL,
+       cb = filter_data_add_callback(data, connect, disconnect, NULL, destroy,
                                        user_data);
        if (cb == NULL)
                return 0;
@@ -712,6 +751,34 @@ guint g_dbus_add_signal_watch(DBusConnection *connection,
        return cb->id;
 }
 
+guint g_dbus_add_properties_watch(DBusConnection *connection,
+                               const char *sender, const char *path,
+                               const char *interface,
+                               GDBusSignalFunction function, void *user_data,
+                               GDBusDestroyFunction destroy)
+{
+       struct filter_data *data;
+       struct filter_callback *cb;
+
+       data = filter_data_get(connection, signal_filter, sender, path,
+                               DBUS_INTERFACE_PROPERTIES, "PropertiesChanged",
+                               interface);
+       if (data == NULL)
+               return 0;
+
+       cb = filter_data_add_callback(data, NULL, NULL, function, destroy,
+                                       user_data);
+       if (cb == NULL)
+               return 0;
+
+       if (data->name != NULL && data->name_watch == 0)
+               data->name_watch = g_dbus_add_service_watch(connection,
+                                                       data->name, NULL,
+                                                       NULL, NULL, NULL);
+
+       return cb->id;
+}
+
 gboolean g_dbus_remove_watch(DBusConnection *connection, guint id)
 {
        struct filter_data *data;
@@ -738,11 +805,8 @@ void g_dbus_remove_all_watches(DBusConnection *connection)
 {
        struct filter_data *data;
 
-       while ((data = filter_data_find(connection, NULL, NULL, NULL, NULL,
-                                       NULL, NULL))) {
+       while ((data = filter_data_find(connection))) {
                listeners = g_slist_remove(listeners, data);
                filter_data_call_and_free(data);
        }
-
-       dbus_connection_remove_filter(connection, message_filter, NULL);
 }
old mode 100644 (file)
new mode 100755 (executable)
index 29c30fa..8d79f39
@@ -2,7 +2,7 @@
  *
  *  DHCP client library with GLib integration
  *
- *  Copyright (C) 2009-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2009-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
@@ -31,6 +31,7 @@
 #include <string.h>
 #include <sys/ioctl.h>
 #include <arpa/inet.h>
+#include <sys/time.h>
 
 #include <netpacket/packet.h>
 #include <netinet/if_ether.h>
 #include <linux/filter.h>
 
 #include <glib.h>
+#if defined TIZEN_EXT
+#include <connman/setting.h>
+#endif
 
 #include "gdhcp.h"
 #include "common.h"
 #include "ipv4ll.h"
+#if defined TIZEN_EXT && defined TIZEN_RTC_TIMER
+#include "tizen/rtctimer.h"
+#endif
 
 #define DISCOVER_TIMEOUT 3
 #define DISCOVER_RETRIES 10
@@ -69,6 +76,13 @@ typedef enum _dhcp_client_state {
        IPV4LL_ANNOUNCE,
        IPV4LL_MONITOR,
        IPV4LL_DEFEND,
+       INFORMATION_REQ,
+       SOLICITATION,
+       REQUEST,
+       CONFIRM,
+       RENEW,
+       REBIND,
+       RELEASE,
 } ClientState;
 
 struct _GDHCPClient {
@@ -82,6 +96,7 @@ struct _GDHCPClient {
        uint32_t server_ip;
        uint32_t requested_ip;
        char *assigned_ip;
+       time_t start;
        uint32_t lease_seconds;
        ListenMode listen_mode;
        int listener_sockfd;
@@ -89,6 +104,9 @@ struct _GDHCPClient {
        uint8_t ack_retry_times;
        uint8_t conflicts;
        guint timeout;
+#if defined TIZEN_EXT && defined TIZEN_RTC_TIMER
+       int rtc_timeout;
+#endif
        guint listener_watch;
        GIOChannel *listener_channel;
        GList *require_list;
@@ -109,7 +127,37 @@ struct _GDHCPClient {
        gpointer address_conflict_data;
        GDHCPDebugFunc debug_func;
        gpointer debug_data;
+       GDHCPClientEventFunc information_req_cb;
+       gpointer information_req_data;
+       GDHCPClientEventFunc solicitation_cb;
+       gpointer solicitation_data;
+       GDHCPClientEventFunc advertise_cb;
+       gpointer advertise_data;
+       GDHCPClientEventFunc request_cb;
+       gpointer request_data;
+       GDHCPClientEventFunc renew_cb;
+       gpointer renew_data;
+       GDHCPClientEventFunc rebind_cb;
+       gpointer rebind_data;
+       GDHCPClientEventFunc release_cb;
+       gpointer release_data;
+       GDHCPClientEventFunc confirm_cb;
+       gpointer confirm_data;
        char *last_address;
+       unsigned char *duid;
+       int duid_len;
+       unsigned char *server_duid;
+       int server_duid_len;
+       uint16_t status_code;
+       uint32_t iaid;
+       uint32_t T1, T2;
+       struct in6_addr ia_na;
+       struct in6_addr ia_ta;
+       time_t last_renew;
+       time_t last_rebind;
+       time_t expire;
+       gboolean retransmit;
+       struct timeval start_time;
 #if defined TIZEN_EXT
        gboolean init_reboot;
 #endif
@@ -132,12 +180,16 @@ static inline void debug(GDHCPClient *client, const char *format, ...)
 }
 
 /* Initialize the packet with the proper defaults */
-static void init_packet(GDHCPClient *dhcp_client,
-               struct dhcp_packet *packet, char type)
+static void init_packet(GDHCPClient *dhcp_client, gpointer pkt, char type)
 {
-       dhcp_init_header(packet, type);
+       if (dhcp_client->type == G_DHCP_IPV6)
+               dhcpv6_init_header(pkt, type);
+       else {
+               struct dhcp_packet *packet = pkt;
 
-       memcpy(packet->chaddr, dhcp_client->mac_address, 6);
+               dhcp_init_header(packet, type);
+               memcpy(packet->chaddr, dhcp_client->mac_address, 6);
+       }
 }
 
 static void add_request_options(GDHCPClient *dhcp_client,
@@ -162,6 +214,182 @@ static void add_request_options(GDHCPClient *dhcp_client,
        }
 }
 
+struct hash_params {
+       unsigned char *buf;
+       int max_buf;
+       unsigned char **ptr_buf;
+};
+
+static void add_dhcpv6_binary_option(gpointer key, gpointer value,
+                                       gpointer user_data)
+{
+       uint8_t *option = value;
+       uint16_t len;
+       struct hash_params *params = user_data;
+
+       /* option[0][1] contains option code */
+       len = option[2] << 8 | option[3];
+
+       if ((*params->ptr_buf + len + 2 + 2) > (params->buf + params->max_buf))
+               return;
+
+       memcpy(*params->ptr_buf, option, len + 2 + 2);
+       (*params->ptr_buf) += len + 2 + 2;
+}
+
+static void add_dhcpv6_send_options(GDHCPClient *dhcp_client,
+                               unsigned char *buf, int max_buf,
+                               unsigned char **ptr_buf)
+{
+       struct hash_params params = {
+               .buf = buf,
+               .max_buf = max_buf,
+               .ptr_buf = ptr_buf
+       };
+
+       if (dhcp_client->type == G_DHCP_IPV4)
+               return;
+
+       g_hash_table_foreach(dhcp_client->send_value_hash,
+                               add_dhcpv6_binary_option, &params);
+
+       *ptr_buf = *params.ptr_buf;
+}
+
+static void copy_option(uint8_t *buf, uint16_t code, uint16_t len,
+                       uint8_t *msg)
+{
+       buf[0] = code >> 8;
+       buf[1] = code & 0xff;
+       buf[2] = len >> 8;
+       buf[3] = len & 0xff;
+       if (len > 0 && msg != NULL)
+               memcpy(&buf[4], msg, len);
+}
+
+static int32_t get_time_diff(struct timeval *tv)
+{
+       struct timeval now;
+       int32_t hsec;
+
+       gettimeofday(&now, NULL);
+
+       hsec = (now.tv_sec - tv->tv_sec) * 100;
+       hsec += (now.tv_usec - tv->tv_usec) / 10000;
+
+       return hsec;
+}
+
+static void remove_timeouts(GDHCPClient *dhcp_client)
+{
+       if (dhcp_client->timeout > 0)
+               g_source_remove(dhcp_client->timeout);
+
+       dhcp_client->timeout = 0;
+}
+
+static void add_dhcpv6_request_options(GDHCPClient *dhcp_client,
+                               struct dhcpv6_packet *packet,
+                               unsigned char *buf, int max_buf,
+                               unsigned char **ptr_buf)
+{
+       GList *list;
+       uint16_t code, value;
+       int32_t diff;
+       int len;
+
+       if (dhcp_client->type == G_DHCP_IPV4)
+               return;
+
+       for (list = dhcp_client->request_list; list; list = list->next) {
+               code = (uint16_t) GPOINTER_TO_INT(list->data);
+
+               switch (code) {
+               case G_DHCPV6_CLIENTID:
+                       if (dhcp_client->duid == NULL)
+                               return;
+
+                       len = 2 + 2 + dhcp_client->duid_len;
+                       if ((*ptr_buf + len) > (buf + max_buf)) {
+                               debug(dhcp_client, "Too long dhcpv6 message "
+                                       "when writing client id option");
+                               return;
+                       }
+
+                       copy_option(*ptr_buf, G_DHCPV6_CLIENTID,
+                               dhcp_client->duid_len, dhcp_client->duid);
+                       (*ptr_buf) += len;
+                       break;
+
+               case G_DHCPV6_SERVERID:
+                       if (dhcp_client->server_duid == NULL)
+                               return;
+
+                       len = 2 + 2 + dhcp_client->server_duid_len;
+                       if ((*ptr_buf + len) > (buf + max_buf)) {
+                               debug(dhcp_client, "Too long dhcpv6 message "
+                                       "when writing server id option");
+                               return;
+                       }
+
+                       copy_option(*ptr_buf, G_DHCPV6_SERVERID,
+                               dhcp_client->server_duid_len,
+                               dhcp_client->server_duid);
+                       (*ptr_buf) += len;
+                       break;
+
+               case G_DHCPV6_RAPID_COMMIT:
+                       len = 2 + 2;
+                       if ((*ptr_buf + len) > (buf + max_buf)) {
+                               debug(dhcp_client, "Too long dhcpv6 message "
+                                       "when writing rapid commit option");
+                               return;
+                       }
+
+                       copy_option(*ptr_buf, G_DHCPV6_RAPID_COMMIT, 0, 0);
+                       (*ptr_buf) += len;
+                       break;
+
+               case G_DHCPV6_ORO:
+                       break;
+
+               case G_DHCPV6_ELAPSED_TIME:
+                       if (dhcp_client->retransmit == FALSE) {
+                               /*
+                                * Initial message, elapsed time is 0.
+                                */
+                               diff = 0;
+                       } else {
+                               diff = get_time_diff(&dhcp_client->start_time);
+                               if (diff < 0 || diff > 0xffff)
+                                       diff = 0xffff;
+                       }
+
+                       len = 2 + 2 + 2;
+                       if ((*ptr_buf + len) > (buf + max_buf)) {
+                               debug(dhcp_client, "Too long dhcpv6 message "
+                                       "when writing elapsed time option");
+                               return;
+                       }
+
+                       value = htons((uint16_t)diff);
+                       copy_option(*ptr_buf, G_DHCPV6_ELAPSED_TIME,
+                               2, (uint8_t *)&value);
+                       (*ptr_buf) += len;
+                       break;
+
+               case G_DHCPV6_DNS_SERVERS:
+                       break;
+
+               case G_DHCPV6_SNTP_SERVERS:
+                       break;
+
+               default:
+                       break;
+               }
+       }
+}
+
 static void add_binary_option(gpointer key, gpointer value, gpointer user_data)
 {
        uint8_t *option = value;
@@ -177,6 +405,17 @@ static void add_send_options(GDHCPClient *dhcp_client,
                                add_binary_option, packet);
 }
 
+/*
+ * Return an RFC 951- and 2131-complaint BOOTP 'secs' value that
+ * represents the number of seconds elapsed from the start of
+ * attempting DHCP to satisfy some DHCP servers that allow for an
+ * "authoritative" reply before responding.
+ */
+static uint16_t dhcp_attempt_secs(GDHCPClient *dhcp_client)
+{
+       return htons(MIN(time(NULL) - dhcp_client->start, UINT16_MAX));
+}
+
 static int send_discover(GDHCPClient *dhcp_client, uint32_t requested)
 {
        struct dhcp_packet packet;
@@ -186,13 +425,14 @@ static int send_discover(GDHCPClient *dhcp_client, uint32_t requested)
        init_packet(dhcp_client, &packet, DHCPDISCOVER);
 
        packet.xid = dhcp_client->xid;
+       packet.secs = dhcp_attempt_secs(dhcp_client);
 
        if (requested)
-               dhcp_add_simple_option(&packet, DHCP_REQUESTED_IP, requested);
+               dhcp_add_option_uint32(&packet, DHCP_REQUESTED_IP, requested);
 
        /* Explicitly saying that we want RFC-compliant packets helps
         * some buggy DHCP servers to NOT send bigger packets */
-       dhcp_add_simple_option(&packet, DHCP_MAX_SIZE, htons(576));
+       dhcp_add_option_uint16(&packet, DHCP_MAX_SIZE, 576);
 
        add_request_options(dhcp_client, &packet);
 
@@ -212,13 +452,18 @@ static int send_select(GDHCPClient *dhcp_client)
        init_packet(dhcp_client, &packet, DHCPREQUEST);
 
        packet.xid = dhcp_client->xid;
+#if defined TIZEN_EXT
+       if (dhcp_client->init_reboot != TRUE)
+#endif
+       packet.secs = dhcp_attempt_secs(dhcp_client);
 
-       dhcp_add_simple_option(&packet, DHCP_REQUESTED_IP,
-                                       dhcp_client->requested_ip);
+       dhcp_add_option_uint32(&packet, DHCP_REQUESTED_IP,
+                                               dhcp_client->requested_ip);
 #if defined TIZEN_EXT
        if (dhcp_client->init_reboot != TRUE)
 #endif
-       dhcp_add_simple_option(&packet, DHCP_SERVER_ID, dhcp_client->server_ip);
+       dhcp_add_option_uint32(&packet, DHCP_SERVER_ID,
+                                               dhcp_client->server_ip);
 
        add_request_options(dhcp_client, &packet);
 
@@ -237,7 +482,7 @@ static int send_renew(GDHCPClient *dhcp_client)
 
        init_packet(dhcp_client , &packet, DHCPREQUEST);
        packet.xid = dhcp_client->xid;
-       packet.ciaddr = dhcp_client->requested_ip;
+       packet.ciaddr = htonl(dhcp_client->requested_ip);
 
        add_request_options(dhcp_client, &packet);
 
@@ -256,7 +501,7 @@ static int send_rebound(GDHCPClient *dhcp_client)
 
        init_packet(dhcp_client , &packet, DHCPREQUEST);
        packet.xid = dhcp_client->xid;
-       packet.ciaddr = dhcp_client->requested_ip;
+       packet.ciaddr = htonl(dhcp_client->requested_ip);
 
        add_request_options(dhcp_client, &packet);
 
@@ -272,13 +517,17 @@ static int send_release(GDHCPClient *dhcp_client,
 {
        struct dhcp_packet packet;
 
+#if defined TIZEN_EXT
+       if (connman_setting_get_bool("WiFiDHCPRelease") != TRUE)
+               return 0;
+#endif
        debug(dhcp_client, "sending DHCP release request");
 
        init_packet(dhcp_client, &packet, DHCPRELEASE);
        packet.xid = rand();
-       packet.ciaddr = ciaddr;
+       packet.ciaddr = htonl(ciaddr);
 
-       dhcp_add_simple_option(&packet, DHCP_SERVER_ID, server);
+       dhcp_add_option_uint32(&packet, DHCP_SERVER_ID, server);
 
        return dhcp_send_kernel_packet(&packet, ciaddr, CLIENT_PORT,
                                                server, SERVER_PORT);
@@ -340,9 +589,7 @@ static gboolean send_announce_packet(gpointer dhcp_data)
                                dhcp_client->requested_ip,
                                dhcp_client->ifindex);
 
-       if (dhcp_client->timeout > 0)
-               g_source_remove(dhcp_client->timeout);
-       dhcp_client->timeout = 0;
+       remove_timeouts(dhcp_client);
 
        if (dhcp_client->state == IPV4LL_DEFEND) {
                dhcp_client->timeout =
@@ -394,6 +641,333 @@ done:
        close(sk);
 }
 
+void g_dhcpv6_client_set_retransmit(GDHCPClient *dhcp_client)
+{
+       if (dhcp_client == NULL)
+               return;
+
+       dhcp_client->retransmit = TRUE;
+}
+
+void g_dhcpv6_client_clear_retransmit(GDHCPClient *dhcp_client)
+{
+       if (dhcp_client == NULL)
+               return;
+
+       dhcp_client->retransmit = FALSE;
+}
+
+int g_dhcpv6_create_duid(GDHCPDuidType duid_type, int index, int type,
+                       unsigned char **duid, int *duid_len)
+{
+       time_t duid_time;
+
+       switch (duid_type) {
+       case G_DHCPV6_DUID_LLT:
+               *duid_len = 2 + 2 + 4 + ETH_ALEN;
+               *duid = g_try_malloc(*duid_len);
+               if (*duid == NULL)
+                       return -ENOMEM;
+
+               (*duid)[0] = 0;
+               (*duid)[1] = 1;
+               get_interface_mac_address(index, &(*duid)[2 + 2 + 4]);
+               (*duid)[2] = 0;
+               (*duid)[3] = type;
+               duid_time = time(NULL) - DUID_TIME_EPOCH;
+               (*duid)[4] = duid_time >> 24;
+               (*duid)[5] = duid_time >> 16;
+               (*duid)[6] = duid_time >> 8;
+               (*duid)[7] = duid_time & 0xff;
+               break;
+       case G_DHCPV6_DUID_EN:
+               return -EINVAL;
+       case G_DHCPV6_DUID_LL:
+               *duid_len = 2 + 2 + ETH_ALEN;
+               *duid = g_try_malloc(*duid_len);
+               if (*duid == NULL)
+                       return -ENOMEM;
+
+               (*duid)[0] = 0;
+               (*duid)[1] = 3;
+               get_interface_mac_address(index, &(*duid)[2 + 2]);
+               (*duid)[2] = 0;
+               (*duid)[3] = type;
+               break;
+       }
+
+       return 0;
+}
+
+int g_dhcpv6_client_set_duid(GDHCPClient *dhcp_client, unsigned char *duid,
+                       int duid_len)
+{
+       if (dhcp_client == NULL || dhcp_client->type == G_DHCP_IPV4)
+               return -EINVAL;
+
+       g_free(dhcp_client->duid);
+
+       dhcp_client->duid = duid;
+       dhcp_client->duid_len = duid_len;
+
+       return 0;
+}
+
+uint32_t g_dhcpv6_client_get_iaid(GDHCPClient *dhcp_client)
+{
+       if (dhcp_client == NULL || dhcp_client->type == G_DHCP_IPV4)
+               return 0;
+
+       return dhcp_client->iaid;
+}
+
+void g_dhcpv6_client_create_iaid(GDHCPClient *dhcp_client, int index,
+                               unsigned char *iaid)
+{
+       uint8_t buf[6];
+
+       get_interface_mac_address(index, buf);
+
+       memcpy(iaid, &buf[2], 4);
+       dhcp_client->iaid = iaid[0] << 24 |
+                       iaid[1] << 16 | iaid[2] << 8 | iaid[3];
+}
+
+int g_dhcpv6_client_get_timeouts(GDHCPClient *dhcp_client,
+                               uint32_t *T1, uint32_t *T2,
+                               time_t *last_renew, time_t *last_rebind,
+                               time_t *expire)
+{
+       if (dhcp_client == NULL || dhcp_client->type == G_DHCP_IPV4)
+               return -EINVAL;
+
+       if (T1 != NULL)
+               *T1 = dhcp_client->T1;
+
+       if (T2 != NULL)
+               *T2 = dhcp_client->T2;
+
+       if (last_renew != NULL)
+               *last_renew = dhcp_client->last_renew;
+
+       if (last_rebind != NULL)
+               *last_rebind = dhcp_client->last_rebind;
+
+       if (expire != NULL)
+               *expire = dhcp_client->expire;
+
+       return 0;
+}
+
+static uint8_t *create_iaaddr(GDHCPClient *dhcp_client, uint8_t *buf,
+                               uint16_t len)
+{
+       buf[0] = 0;
+       buf[1] = G_DHCPV6_IAADDR;
+       buf[2] = 0;
+       buf[3] = len;
+       memcpy(&buf[4], &dhcp_client->ia_na, 16);
+       memset(&buf[20], 0, 4); /* preferred */
+       memset(&buf[24], 0, 4); /* valid */
+       return buf;
+}
+
+static void put_iaid(GDHCPClient *dhcp_client, int index, uint8_t *buf)
+{
+       uint32_t iaid;
+
+       iaid = g_dhcpv6_client_get_iaid(dhcp_client);
+       if (iaid == 0) {
+               g_dhcpv6_client_create_iaid(dhcp_client, index, buf);
+               return;
+       }
+
+       buf[0] = iaid >> 24;
+       buf[1] = iaid >> 16;
+       buf[2] = iaid >> 8;
+       buf[3] = iaid;
+}
+
+int g_dhcpv6_client_set_ia(GDHCPClient *dhcp_client, int index,
+                       int code, uint32_t *T1, uint32_t *T2,
+                       gboolean add_iaaddr, const char *ia_na)
+{
+       if (code == G_DHCPV6_IA_TA) {
+               uint8_t ia_options[4];
+
+               put_iaid(dhcp_client, index, ia_options);
+
+               g_dhcp_client_set_request(dhcp_client, G_DHCPV6_IA_TA);
+               g_dhcpv6_client_set_send(dhcp_client, G_DHCPV6_IA_TA,
+                                       ia_options, sizeof(ia_options));
+
+       } else if (code == G_DHCPV6_IA_NA) {
+               struct in6_addr addr;
+
+               g_dhcp_client_set_request(dhcp_client, G_DHCPV6_IA_NA);
+
+               /*
+                * If caller has specified the IPv6 address it wishes to
+                * to use (ia_na != NULL and address is valid), then send
+                * the address to server.
+                * If caller did not specify the address (ia_na == NULL) and
+                * if the current address is not set, then we should not send
+                * the address sub-option.
+                */
+               if (add_iaaddr == TRUE && ((ia_na == NULL &&
+                       IN6_IS_ADDR_UNSPECIFIED(&dhcp_client->ia_na) == FALSE)
+                       || (ia_na != NULL &&
+                               inet_pton(AF_INET6, ia_na, &addr) == 1))) {
+#define IAADDR_LEN (16+4+4)
+                       uint8_t ia_options[4+4+4+2+2+IAADDR_LEN];
+
+                       if (ia_na != NULL)
+                               memcpy(&dhcp_client->ia_na, &addr,
+                                               sizeof(struct in6_addr));
+
+                       put_iaid(dhcp_client, index, ia_options);
+
+                       if (T1 != NULL) {
+                               ia_options[4] = *T1 >> 24;
+                               ia_options[5] = *T1 >> 16;
+                               ia_options[6] = *T1 >> 8;
+                               ia_options[7] = *T1;
+                       } else
+                               memset(&ia_options[4], 0x00, 4);
+
+                       if (T2 != NULL) {
+                               ia_options[8] = *T2 >> 24;
+                               ia_options[9] = *T2 >> 16;
+                               ia_options[10] = *T2 >> 8;
+                               ia_options[11] = *T2;
+                       } else
+                               memset(&ia_options[8], 0x00, 4);
+
+                       create_iaaddr(dhcp_client, &ia_options[12],
+                                       IAADDR_LEN);
+
+                       g_dhcpv6_client_set_send(dhcp_client, G_DHCPV6_IA_NA,
+                                       ia_options, sizeof(ia_options));
+               } else {
+                       uint8_t ia_options[4+4+4];
+
+                       put_iaid(dhcp_client, index, ia_options);
+
+                       memset(&ia_options[4], 0x00, 4); /* T1 (4 bytes) */
+                       memset(&ia_options[8], 0x00, 4); /* T2 (4 bytes) */
+
+                       g_dhcpv6_client_set_send(dhcp_client, G_DHCPV6_IA_NA,
+                                       ia_options, sizeof(ia_options));
+               }
+
+       } else
+               return -EINVAL;
+
+       return 0;
+}
+
+int g_dhcpv6_client_set_oro(GDHCPClient *dhcp_client, int args, ...)
+{
+       va_list va;
+       int i, j, len = sizeof(uint16_t) * args;
+       uint8_t *values;
+
+       values = g_try_malloc(len);
+       if (values == NULL)
+               return -ENOMEM;
+
+       va_start(va, args);
+       for (i = 0, j = 0; i < args; i++) {
+               uint16_t value = va_arg(va, int);
+               values[j++] = value >> 8;
+               values[j++] = value & 0xff;
+       }
+       va_end(va);
+
+       g_dhcpv6_client_set_send(dhcp_client, G_DHCPV6_ORO, values, len);
+
+       g_free(values);
+
+       return 0;
+}
+
+static int send_dhcpv6_msg(GDHCPClient *dhcp_client, int type, char *msg)
+{
+       struct dhcpv6_packet *packet;
+       uint8_t buf[MAX_DHCPV6_PKT_SIZE];
+       unsigned char *ptr;
+       int ret, max_buf;
+
+       memset(buf, 0, sizeof(buf));
+       packet = (struct dhcpv6_packet *)&buf[0];
+       ptr = buf + sizeof(struct dhcpv6_packet);
+
+       init_packet(dhcp_client, packet, type);
+
+       if (dhcp_client->retransmit == FALSE) {
+               dhcp_client->xid = packet->transaction_id[0] << 16 |
+                               packet->transaction_id[1] << 8 |
+                               packet->transaction_id[2];
+               gettimeofday(&dhcp_client->start_time, NULL);
+       } else {
+               packet->transaction_id[0] = dhcp_client->xid >> 16;
+               packet->transaction_id[1] = dhcp_client->xid >> 8 ;
+               packet->transaction_id[2] = dhcp_client->xid;
+       }
+
+       g_dhcp_client_set_request(dhcp_client, G_DHCPV6_ELAPSED_TIME);
+
+       debug(dhcp_client, "sending DHCPv6 %s message xid 0x%04x", msg,
+                                                       dhcp_client->xid);
+
+       max_buf = MAX_DHCPV6_PKT_SIZE - sizeof(struct dhcpv6_packet);
+
+       add_dhcpv6_request_options(dhcp_client, packet, buf, max_buf, &ptr);
+
+       add_dhcpv6_send_options(dhcp_client, buf, max_buf, &ptr);
+
+       ret = dhcpv6_send_packet(dhcp_client->ifindex, packet, ptr - buf);
+
+       debug(dhcp_client, "sent %d pkt %p len %d", ret, packet, ptr - buf);
+       return ret;
+}
+
+static int send_solicitation(GDHCPClient *dhcp_client)
+{
+       return send_dhcpv6_msg(dhcp_client, DHCPV6_SOLICIT, "solicit");
+}
+
+static int send_dhcpv6_request(GDHCPClient *dhcp_client)
+{
+       return send_dhcpv6_msg(dhcp_client, DHCPV6_REQUEST, "request");
+}
+
+static int send_dhcpv6_confirm(GDHCPClient *dhcp_client)
+{
+       return send_dhcpv6_msg(dhcp_client, DHCPV6_CONFIRM, "confirm");
+}
+
+static int send_dhcpv6_renew(GDHCPClient *dhcp_client)
+{
+       return send_dhcpv6_msg(dhcp_client, DHCPV6_RENEW, "renew");
+}
+
+static int send_dhcpv6_rebind(GDHCPClient *dhcp_client)
+{
+       return send_dhcpv6_msg(dhcp_client, DHCPV6_REBIND, "rebind");
+}
+
+static int send_dhcpv6_release(GDHCPClient *dhcp_client)
+{
+       return send_dhcpv6_msg(dhcp_client, DHCPV6_RELEASE, "release");
+}
+
+static int send_information_req(GDHCPClient *dhcp_client)
+{
+       return send_dhcpv6_msg(dhcp_client, DHCPV6_INFORMATION_REQ,
+                               "information-req");
+}
+
 static void remove_value(gpointer data, gpointer user_data)
 {
        char *value = data;
@@ -457,6 +1031,10 @@ GDHCPClient *g_dhcp_client_new(GDHCPType type,
                                g_direct_equal, NULL, g_free);
        dhcp_client->request_list = NULL;
        dhcp_client->require_list = NULL;
+       dhcp_client->duid = NULL;
+       dhcp_client->duid_len = 0;
+       dhcp_client->last_renew = dhcp_client->last_rebind = time(NULL);
+       dhcp_client->expire = 0;
 
        *error = G_DHCP_CLIENT_ERROR_NONE;
 
@@ -523,7 +1101,7 @@ static int dhcp_l2_socket(int ifindex)
 
        fd = socket(PF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC, htons(ETH_P_IP));
        if (fd < 0)
-               return fd;
+               return -errno;
 
        if (SERVER_PORT == 67 && CLIENT_PORT == 68)
                /* Use only if standard ports are in use */
@@ -536,8 +1114,9 @@ static int dhcp_l2_socket(int ifindex)
        sock.sll_ifindex = ifindex;
 
        if (bind(fd, (struct sockaddr *) &sock, sizeof(sock)) != 0) {
+               int err = -errno;
                close(fd);
-               return -errno;
+               return err;
        }
 
        return fd;
@@ -616,10 +1195,7 @@ static void ipv4ll_start(GDHCPClient *dhcp_client)
        guint timeout;
        int seed;
 
-       if (dhcp_client->timeout > 0) {
-               g_source_remove(dhcp_client->timeout);
-               dhcp_client->timeout = 0;
-       }
+       remove_timeouts(dhcp_client);
 
        switch_listening_mode(dhcp_client, L_NONE);
        dhcp_client->type = G_DHCP_IPV4LL;
@@ -646,8 +1222,7 @@ static void ipv4ll_stop(GDHCPClient *dhcp_client)
 
        switch_listening_mode(dhcp_client, L_NONE);
 
-       if (dhcp_client->timeout > 0)
-               g_source_remove(dhcp_client->timeout);
+       remove_timeouts(dhcp_client);
 
        if (dhcp_client->listener_watch > 0) {
                g_source_remove(dhcp_client->listener_watch);
@@ -671,7 +1246,6 @@ static int ipv4ll_recv_arp_packet(GDHCPClient *dhcp_client)
        int target_conflict;
 
        memset(&arp, 0, sizeof(arp));
-       bytes = 0;
        bytes = read(dhcp_client->listener_sockfd, &arp, sizeof(arp));
        if (bytes < 0)
                return bytes;
@@ -680,7 +1254,7 @@ static int ipv4ll_recv_arp_packet(GDHCPClient *dhcp_client)
                        arp.arp_op != htons(ARPOP_REQUEST))
                return -EINVAL;
 
-       ip_requested = ntohl(dhcp_client->requested_ip);
+       ip_requested = htonl(dhcp_client->requested_ip);
        source_conflict = !memcmp(arp.arp_spa, &ip_requested,
                                                sizeof(ip_requested));
 
@@ -736,17 +1310,33 @@ static int ipv4ll_recv_arp_packet(GDHCPClient *dhcp_client)
        return 0;
 }
 
-static gboolean check_package_owner(GDHCPClient *dhcp_client,
-                                       struct dhcp_packet *packet)
+static gboolean check_package_owner(GDHCPClient *dhcp_client, gpointer pkt)
 {
-       if (packet->xid != dhcp_client->xid)
-               return FALSE;
+       if (dhcp_client->type == G_DHCP_IPV6) {
+               struct dhcpv6_packet *packet6 = pkt;
+               uint32_t xid;
 
-       if (packet->hlen != 6)
-               return FALSE;
+               if (packet6 == NULL)
+                       return FALSE;
 
-       if (memcmp(packet->chaddr, dhcp_client->mac_address, 6))
-               return FALSE;
+               xid = packet6->transaction_id[0] << 16 |
+                       packet6->transaction_id[1] << 8 |
+                       packet6->transaction_id[2];
+
+               if (xid != dhcp_client->xid)
+                       return FALSE;
+       } else {
+               struct dhcp_packet *packet = pkt;
+
+               if (packet->xid != dhcp_client->xid)
+                       return FALSE;
+
+               if (packet->hlen != 6)
+                       return FALSE;
+
+               if (memcmp(packet->chaddr, dhcp_client->mac_address, 6))
+                       return FALSE;
+       }
 
        return TRUE;
 }
@@ -757,6 +1347,21 @@ static gboolean request_timeout(gpointer user_data)
 {
        GDHCPClient *dhcp_client = user_data;
 
+#if defined TIZEN_EXT
+       if (dhcp_client->init_reboot == TRUE) {
+               debug(dhcp_client, "DHCPREQUEST of INIT-REBOOT has failed");
+
+               /* Start DHCPDISCOVERY when DHCPREQUEST of INIT-REBOOT has failed */
+               g_dhcp_client_set_address_known(dhcp_client, FALSE);
+
+               dhcp_client->retry_times = 0;
+               dhcp_client->requested_ip = 0;
+
+               g_dhcp_client_start(dhcp_client, dhcp_client->last_address);
+
+               return FALSE;
+       }
+#endif
        debug(dhcp_client, "request timeout (retries %d)",
                                        dhcp_client->retry_times);
 
@@ -776,14 +1381,15 @@ static int switch_listening_mode(GDHCPClient *dhcp_client,
        GIOChannel *listener_channel;
        int listener_sockfd;
 
-       debug(dhcp_client, "switch listening mode (%d ==> %d)",
-                               dhcp_client->listen_mode, listen_mode);
-
        if (dhcp_client->listen_mode == listen_mode)
                return 0;
 
+       debug(dhcp_client, "switch listening mode (%d ==> %d)",
+                               dhcp_client->listen_mode, listen_mode);
+
        if (dhcp_client->listen_mode != L_NONE) {
-               g_source_remove(dhcp_client->listener_watch);
+               if (dhcp_client->listener_watch > 0)
+                       g_source_remove(dhcp_client->listener_watch);
                dhcp_client->listener_channel = NULL;
                dhcp_client->listen_mode = L_NONE;
                dhcp_client->listener_sockfd = -1;
@@ -795,10 +1401,16 @@ static int switch_listening_mode(GDHCPClient *dhcp_client,
 
        if (listen_mode == L2)
                listener_sockfd = dhcp_l2_socket(dhcp_client->ifindex);
-       else if (listen_mode == L3)
-               listener_sockfd = dhcp_l3_socket(CLIENT_PORT,
-                                               dhcp_client->interface);
-       else if (listen_mode == L_ARP)
+       else if (listen_mode == L3) {
+               if (dhcp_client->type == G_DHCP_IPV6)
+                       listener_sockfd = dhcp_l3_socket(DHCPV6_CLIENT_PORT,
+                                                       dhcp_client->interface,
+                                                       AF_INET6);
+               else
+                       listener_sockfd = dhcp_l3_socket(CLIENT_PORT,
+                                                       dhcp_client->interface,
+                                                       AF_INET);
+       } else if (listen_mode == L_ARP)
                listener_sockfd = ipv4ll_arp_socket(dhcp_client->ifindex);
        else
                return -EIO;
@@ -856,15 +1468,14 @@ static void start_request(GDHCPClient *dhcp_client)
 
 static uint32_t get_lease(struct dhcp_packet *packet)
 {
-       uint8_t *option_u8;
+       uint8_t *option;
        uint32_t lease_seconds;
 
-       option_u8 = dhcp_get_option(packet, DHCP_LEASE_TIME);
-       if (option_u8 == NULL)
+       option = dhcp_get_option(packet, DHCP_LEASE_TIME);
+       if (option == NULL)
                return 3600;
 
-       lease_seconds = dhcp_get_unaligned((uint32_t *) option_u8);
-       lease_seconds = ntohl(lease_seconds);
+       lease_seconds = get_be32(option);
        /* paranoia: must not be prone to overflows */
        lease_seconds &= 0x0fffffff;
        if (lease_seconds < 10)
@@ -877,13 +1488,17 @@ static void restart_dhcp(GDHCPClient *dhcp_client, int retry_times)
 {
        debug(dhcp_client, "restart DHCP (retries %d)", retry_times);
 
-       if (dhcp_client->timeout > 0) {
-               g_source_remove(dhcp_client->timeout);
-               dhcp_client->timeout = 0;
+       remove_timeouts(dhcp_client);
+#if defined TIZEN_EXT && defined TIZEN_RTC_TIMER
+       if (dhcp_client->rtc_timeout > 0) {
+               rtc_timeout_remove(dhcp_client->rtc_timeout);
+               dhcp_client->rtc_timeout = 0;
        }
+#endif
 
        dhcp_client->retry_times = retry_times;
        dhcp_client->requested_ip = 0;
+       dhcp_client->state = INIT_SELECTING;
        switch_listening_mode(dhcp_client, L2);
 
        g_dhcp_client_start(dhcp_client, dhcp_client->last_address);
@@ -911,12 +1526,20 @@ static gboolean start_rebound_timeout(gpointer user_data)
        } else {
                send_rebound(dhcp_client);
 
+#if defined TIZEN_EXT && defined TIZEN_RTC_TIMER
+               dhcp_client->rtc_timeout = rtc_timeout_add_seconds(
+                               dhcp_client->lease_seconds >> 1,
+                               start_rebound_timeout,
+                               dhcp_client,
+                               NULL);
+#else
                dhcp_client->timeout =
                                g_timeout_add_seconds_full(G_PRIORITY_HIGH,
                                                dhcp_client->lease_seconds >> 1,
                                                        start_rebound_timeout,
                                                                dhcp_client,
                                                                NULL);
+#endif
        }
 
        return FALSE;
@@ -928,11 +1551,19 @@ static void start_rebound(GDHCPClient *dhcp_client)
 
        dhcp_client->state = REBINDING;
 
+#if defined TIZEN_EXT && defined TIZEN_RTC_TIMER
+       dhcp_client->rtc_timeout = rtc_timeout_add_seconds(
+                       dhcp_client->lease_seconds >> 1,
+                       start_rebound_timeout,
+                       dhcp_client,
+                       NULL);
+#else
        dhcp_client->timeout = g_timeout_add_seconds_full(G_PRIORITY_HIGH,
                                                dhcp_client->lease_seconds >> 1,
                                                        start_rebound_timeout,
                                                                dhcp_client,
                                                                NULL);
+#endif
 }
 
 static gboolean start_renew_timeout(gpointer user_data)
@@ -953,13 +1584,28 @@ static gboolean start_renew_timeout(gpointer user_data)
 
                if (dhcp_client->timeout > 0)
                        g_source_remove(dhcp_client->timeout);
+#if defined TIZEN_EXT && defined TIZEN_RTC_TIMER
+               dhcp_client->timeout = 0;
 
+               if (dhcp_client->rtc_timeout > 0)
+                       rtc_timeout_remove(dhcp_client->rtc_timeout);
+               dhcp_client->rtc_timeout = 0;
+#endif
+
+#if defined TIZEN_EXT && defined TIZEN_RTC_TIMER
+               dhcp_client->rtc_timeout = rtc_timeout_add_seconds(
+                               dhcp_client->lease_seconds >> 1,
+                               start_renew_timeout,
+                               dhcp_client,
+                               NULL);
+#else
                dhcp_client->timeout =
                                g_timeout_add_seconds_full(G_PRIORITY_HIGH,
                                                dhcp_client->lease_seconds >> 1,
                                                        start_renew_timeout,
                                                                dhcp_client,
                                                                NULL);
+#endif
        }
 
        return FALSE;
@@ -973,11 +1619,26 @@ static void start_bound(GDHCPClient *dhcp_client)
 
        if (dhcp_client->timeout > 0)
                g_source_remove(dhcp_client->timeout);
+#if defined TIZEN_EXT && defined TIZEN_RTC_TIMER
+       dhcp_client->timeout = 0;
+
+       if (dhcp_client->rtc_timeout > 0)
+               rtc_timeout_remove(dhcp_client->rtc_timeout);
+       dhcp_client->rtc_timeout = 0;
+#endif
 
+#if defined TIZEN_EXT && defined TIZEN_RTC_TIMER
+       dhcp_client->rtc_timeout = rtc_timeout_add_seconds(
+                       dhcp_client->lease_seconds >> 1,
+                       start_renew_timeout,
+                       dhcp_client,
+                       NULL);
+#else
        dhcp_client->timeout = g_timeout_add_seconds_full(G_PRIORITY_HIGH,
                                        dhcp_client->lease_seconds >> 1,
                                        start_renew_timeout, dhcp_client,
                                                        NULL);
+#endif
 }
 
 static gboolean restart_dhcp_timeout(gpointer user_data)
@@ -1030,7 +1691,7 @@ static char *malloc_option_value_string(uint8_t *option, GDHCPOptionType type)
                return NULL;
        upper_length = len_of_option_as_string[type] *
                        ((unsigned)len / (unsigned)optlen);
-       dest = ret = malloc(upper_length + 1);
+       dest = ret = g_malloc(upper_length + 1);
        if (ret == NULL)
                return NULL;
 
@@ -1040,16 +1701,13 @@ static char *malloc_option_value_string(uint8_t *option, GDHCPOptionType type)
                        dest += sprint_nip(dest, "", option);
                        break;
                case OPTION_U16: {
-                       uint16_t val_u16 = dhcp_get_unaligned(
-                                               (uint16_t *) option);
-                       dest += sprintf(dest, "%u", ntohs(val_u16));
+                       uint16_t val_u16 = get_be16(option);
+                       dest += sprintf(dest, "%u", val_u16);
                        break;
                }
                case OPTION_U32: {
-                       uint32_t val_u32 = dhcp_get_unaligned(
-                                               (uint32_t *) option);
-                       dest += sprintf(dest, type == OPTION_U32 ? "%lu" :
-                                       "%ld", (unsigned long) ntohl(val_u32));
+                       uint32_t val_u32 = get_be32(option);
+                       dest += sprintf(dest, "%u", val_u32);
                        break;
                }
                case OPTION_STRING:
@@ -1094,6 +1752,211 @@ static GList *get_option_value_list(char *value, GDHCPOptionType type)
        return list;
 }
 
+static inline uint32_t get_uint32(unsigned char *value)
+{
+       return value[0] << 24 | value[1] << 16 |
+               value[2] << 8 | value[3];
+}
+
+static inline uint16_t get_uint16(unsigned char *value)
+{
+       return value[0] << 8 | value[1];
+}
+
+static GList *get_addresses(GDHCPClient *dhcp_client,
+                               int code, int len,
+                               unsigned char *value,
+                               uint16_t *status)
+{
+       GList *list = NULL;
+       struct in6_addr addr;
+       uint32_t iaid, T1 = 0, T2 = 0, preferred = 0, valid = 0;
+       uint16_t option_len, option_code, st = 0, max_len;
+       int addr_count = 0, i, pos;
+       uint8_t *option;
+       char *str;
+
+       if (value == NULL || len < 4)
+               return NULL;
+
+       iaid = get_uint32(&value[0]);
+       if (dhcp_client->iaid != iaid)
+               return NULL;
+
+       if (code == G_DHCPV6_IA_NA) {
+               T1 = get_uint32(&value[4]);
+               T2 = get_uint32(&value[8]);
+
+               if (T1 > T2)
+                       /* RFC 3315, 22.4 */
+                       return NULL;
+
+               pos = 12;
+       } else
+               pos = 4;
+
+       if (len <= pos)
+               return NULL;
+
+       max_len = len - pos;
+
+       /* We have more sub-options in this packet. */
+       do {
+               option = dhcpv6_get_sub_option(&value[pos], max_len,
+                                       &option_code, &option_len);
+
+               debug(dhcp_client, "pos %d option %p code %d len %d",
+                       pos, option, option_code, option_len);
+
+               if (option == NULL)
+                       break;
+
+               if (pos >= max_len)
+                       break;
+
+               switch (option_code) {
+               case G_DHCPV6_IAADDR:
+                       i = 0;
+                       memcpy(&addr, &option[0], sizeof(addr));
+                       i += sizeof(addr);
+                       preferred = get_uint32(&option[i]);
+                       i += 4;
+                       valid = get_uint32(&option[i]);
+
+                       addr_count++;
+                       break;
+
+               case G_DHCPV6_STATUS_CODE:
+                       st = get_uint16(&option[0]);
+                       debug(dhcp_client, "error code %d", st);
+                       if (option_len > 2) {
+                               str = g_strndup((gchar *)&option[2],
+                                               option_len - 2);
+                               debug(dhcp_client, "error text: %s", str);
+                               g_free(str);
+                       }
+
+                       *status = st;
+                       break;
+               }
+
+               pos += 2 + 2 + option_len;
+
+       } while (option != NULL);
+
+       if (addr_count > 0 && st == 0) {
+               /* We only support one address atm */
+               char addr_str[INET6_ADDRSTRLEN + 1];
+
+               if (preferred > valid)
+                       /* RFC 3315, 22.6 */
+                       return NULL;
+
+               dhcp_client->T1 = T1;
+               dhcp_client->T2 = T2;
+
+               inet_ntop(AF_INET6, &addr, addr_str, INET6_ADDRSTRLEN);
+               debug(dhcp_client, "count %d addr %s T1 %u T2 %u",
+                       addr_count, addr_str, T1, T2);
+
+               list = g_list_append(list, g_strdup(addr_str));
+
+               if (code == G_DHCPV6_IA_NA)
+                       memcpy(&dhcp_client->ia_na, &addr,
+                                               sizeof(struct in6_addr));
+               else
+                       memcpy(&dhcp_client->ia_ta, &addr,
+                                               sizeof(struct in6_addr));
+
+               g_dhcpv6_client_set_expire(dhcp_client, valid);
+       }
+
+       return list;
+}
+
+static GList *get_dhcpv6_option_value_list(GDHCPClient *dhcp_client,
+                                       int code, int len,
+                                       unsigned char *value,
+                                       uint16_t *status)
+{
+       GList *list = NULL;
+       char *str;
+       int i;
+
+       if (value == NULL)
+               return NULL;
+
+       switch (code) {
+       case G_DHCPV6_DNS_SERVERS:      /* RFC 3646, chapter 3 */
+       case G_DHCPV6_SNTP_SERVERS:     /* RFC 4075, chapter 4 */
+               if (len % 16) {
+                       debug(dhcp_client,
+                               "%s server list length (%d) is invalid",
+                               code == G_DHCPV6_DNS_SERVERS ? "DNS" : "SNTP",
+                               len);
+                       return NULL;
+               }
+               for (i = 0; i < len; i += 16) {
+
+                       str = g_try_malloc0(INET6_ADDRSTRLEN+1);
+                       if (str == NULL)
+                               return list;
+
+                       if (inet_ntop(AF_INET6, &value[i], str,
+                                       INET6_ADDRSTRLEN) == NULL)
+                               g_free(str);
+                       else
+                               list = g_list_append(list, str);
+               }
+               break;
+
+       case G_DHCPV6_IA_NA:            /* RFC 3315, chapter 22.4 */
+       case G_DHCPV6_IA_TA:            /* RFC 3315, chapter 22.5 */
+               list = get_addresses(dhcp_client, code, len, value, status);
+               break;
+
+       default:
+               break;
+       }
+
+       return list;
+}
+
+static void get_dhcpv6_request(GDHCPClient *dhcp_client,
+                               struct dhcpv6_packet *packet,
+                               uint16_t pkt_len, uint16_t *status)
+{
+       GList *list, *value_list;
+       uint8_t *option;
+       uint16_t code;
+       uint16_t option_len;
+
+       for (list = dhcp_client->request_list; list; list = list->next) {
+               code = (uint16_t) GPOINTER_TO_INT(list->data);
+
+               option = dhcpv6_get_option(packet, pkt_len, code, &option_len,
+                                               NULL);
+               if (option == NULL) {
+                       g_hash_table_remove(dhcp_client->code_value_hash,
+                                               GINT_TO_POINTER((int) code));
+                       continue;
+               }
+
+               value_list = get_dhcpv6_option_value_list(dhcp_client, code,
+                                               option_len, option, status);
+
+               debug(dhcp_client, "code %d %p len %d list %p", code, option,
+                       option_len, value_list);
+
+               if (value_list == NULL)
+                       g_hash_table_remove(dhcp_client->code_value_hash,
+                                               GINT_TO_POINTER((int) code));
+               else
+                       g_hash_table_insert(dhcp_client->code_value_hash,
+                               GINT_TO_POINTER((int) code), value_list);
+       }
+}
+
 static void get_request(GDHCPClient *dhcp_client, struct dhcp_packet *packet)
 {
        GDHCPOptionType type;
@@ -1137,7 +2000,15 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
 {
        GDHCPClient *dhcp_client = user_data;
        struct dhcp_packet packet;
-       uint8_t *message_type, *option_u8;
+       struct dhcpv6_packet *packet6 = NULL;
+       uint8_t *message_type = NULL, *client_id = NULL, *option,
+               *server_id = NULL;
+       uint16_t option_len = 0, status = 0;
+       uint32_t xid = 0;
+       gpointer pkt;
+       unsigned char buf[MAX_DHCPV6_PKT_SIZE];
+       uint16_t pkt_len = 0;
+       int count;
        int re;
 
        if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
@@ -1148,12 +2019,29 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
        if (dhcp_client->listen_mode == L_NONE)
                return FALSE;
 
+       pkt = &packet;
+
+       dhcp_client->status_code = 0;
+
        if (dhcp_client->listen_mode == L2)
-               re = dhcp_recv_l2_packet(&packet, dhcp_client->listener_sockfd);
-       else if (dhcp_client->listen_mode == L3)
-               re = dhcp_recv_l3_packet(&packet, dhcp_client->listener_sockfd);
-       else if (dhcp_client->listen_mode == L_ARP) {
-               re = ipv4ll_recv_arp_packet(dhcp_client);
+               re = dhcp_recv_l2_packet(&packet,
+                                       dhcp_client->listener_sockfd);
+       else if (dhcp_client->listen_mode == L3) {
+               if (dhcp_client->type == G_DHCP_IPV6) {
+                       re = dhcpv6_recv_l3_packet(&packet6, buf, sizeof(buf),
+                                               dhcp_client->listener_sockfd);
+                       pkt_len = re;
+                       pkt = packet6;
+                       xid = packet6->transaction_id[0] << 16 |
+                               packet6->transaction_id[1] << 8 |
+                               packet6->transaction_id[2];
+               } else {
+                       re = dhcp_recv_l3_packet(&packet,
+                                               dhcp_client->listener_sockfd);
+                       xid = packet.xid;
+               }
+       } else if (dhcp_client->listen_mode == L_ARP) {
+               ipv4ll_recv_arp_packet(dhcp_client);
                return TRUE;
        }
        else
@@ -1162,30 +2050,68 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
        if (re < 0)
                return TRUE;
 
-       if (check_package_owner(dhcp_client, &packet) == FALSE)
+       if (check_package_owner(dhcp_client, pkt) == FALSE)
                return TRUE;
 
-       message_type = dhcp_get_option(&packet, DHCP_MESSAGE_TYPE);
-       if (message_type == NULL)
-               /* No message type option, ignore package */
+       if (dhcp_client->type == G_DHCP_IPV6) {
+               if (packet6 == NULL)
+                       return TRUE;
+
+               count = 0;
+               client_id = dhcpv6_get_option(packet6, pkt_len,
+                               G_DHCPV6_CLIENTID, &option_len, &count);
+
+               if (client_id == NULL || count == 0 || option_len == 0 ||
+                               memcmp(dhcp_client->duid, client_id,
+                                       dhcp_client->duid_len) != 0) {
+                       debug(dhcp_client,
+                               "client duid error, discarding msg %p/%d/%d",
+                               client_id, option_len, count);
+                       return TRUE;
+               }
+
+               option = dhcpv6_get_option(packet6, pkt_len,
+                               G_DHCPV6_STATUS_CODE, &option_len, NULL);
+               if (option != 0 && option_len > 0) {
+                       status = option[0]<<8 | option[1];
+                       if (status != 0) {
+                               debug(dhcp_client, "error code %d", status);
+                               if (option_len > 2) {
+                                       gchar *txt = g_strndup(
+                                               (gchar *)&option[2],
+                                               option_len - 2);
+                                       debug(dhcp_client, "error text: %s",
+                                               txt);
+                                       g_free(txt);
+                               }
+                       }
+                       dhcp_client->status_code = status;
+               }
+       } else {
+               message_type = dhcp_get_option(&packet, DHCP_MESSAGE_TYPE);
+               if (message_type == NULL)
+                       return TRUE;
+       }
+
+       if (message_type == NULL && client_id == NULL)
+               /* No message type / client id option, ignore package */
                return TRUE;
 
-       debug(dhcp_client, "received DHCP packet (current state %d)",
-                                                       dhcp_client->state);
+       debug(dhcp_client, "received DHCP packet xid 0x%04x "
+                       "(current state %d)", xid, dhcp_client->state);
 
        switch (dhcp_client->state) {
        case INIT_SELECTING:
                if (*message_type != DHCPOFFER)
                        return TRUE;
 
-               g_source_remove(dhcp_client->timeout);
+               remove_timeouts(dhcp_client);
                dhcp_client->timeout = 0;
                dhcp_client->retry_times = 0;
 
-               option_u8 = dhcp_get_option(&packet, DHCP_SERVER_ID);
-               dhcp_client->server_ip =
-                               dhcp_get_unaligned((uint32_t *) option_u8);
-               dhcp_client->requested_ip = packet.yiaddr;
+               option = dhcp_get_option(&packet, DHCP_SERVER_ID);
+               dhcp_client->server_ip = get_be32(option);
+               dhcp_client->requested_ip = ntohl(packet.yiaddr);
 
                dhcp_client->state = REQUESTING;
 
@@ -1198,15 +2124,18 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
                if (*message_type == DHCPACK) {
                        dhcp_client->retry_times = 0;
 
-                       if (dhcp_client->timeout > 0)
-                               g_source_remove(dhcp_client->timeout);
-                       dhcp_client->timeout = 0;
+                       remove_timeouts(dhcp_client);
+#if defined TIZEN_EXT && defined TIZEN_RTC_TIMER
+                       if (dhcp_client->rtc_timeout > 0)
+                               rtc_timeout_remove(dhcp_client->rtc_timeout);
+                       dhcp_client->rtc_timeout = 0;
+#endif
 
                        dhcp_client->lease_seconds = get_lease(&packet);
+
 #if defined TIZEN_EXT
                        debug(dhcp_client, "lease %d secs", dhcp_client->lease_seconds);
 #endif
-
                        get_request(dhcp_client, &packet);
 
                        switch_listening_mode(dhcp_client, L_NONE);
@@ -1223,11 +2152,19 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
                } else if (*message_type == DHCPNAK) {
                        dhcp_client->retry_times = 0;
 
-                       if (dhcp_client->timeout > 0)
-                               g_source_remove(dhcp_client->timeout);
+                       remove_timeouts(dhcp_client);
 
 #if defined TIZEN_EXT
-                       g_dhcp_client_set_address_known(dhcp_client, FALSE);
+                       if (dhcp_client->init_reboot == TRUE) {
+                               g_dhcp_client_set_address_known(dhcp_client, FALSE);
+                               dhcp_client->timeout = g_idle_add_full(
+                                                               G_PRIORITY_HIGH,
+                                                               restart_dhcp_timeout,
+                                                               dhcp_client,
+                                                               NULL);
+
+                               break;
+                       }
 #endif
                        dhcp_client->timeout = g_timeout_add_seconds_full(
                                                        G_PRIORITY_HIGH, 3,
@@ -1237,6 +2174,152 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
                }
 
                break;
+       case SOLICITATION:
+               if (dhcp_client->type != G_DHCP_IPV6)
+                       return TRUE;
+
+               if (packet6->message != DHCPV6_REPLY &&
+                               packet6->message != DHCPV6_ADVERTISE)
+                       return TRUE;
+
+               count = 0;
+               server_id = dhcpv6_get_option(packet6, pkt_len,
+                               G_DHCPV6_SERVERID, &option_len, &count);
+               if (server_id == NULL || count != 1 || option_len == 0) {
+                       /* RFC 3315, 15.10 */
+                       debug(dhcp_client,
+                               "server duid error, discarding msg %p/%d/%d",
+                               server_id, option_len, count);
+                       return TRUE;
+               }
+               dhcp_client->server_duid = g_try_malloc(option_len);
+               if (dhcp_client->server_duid == NULL)
+                       return TRUE;
+               memcpy(dhcp_client->server_duid, server_id, option_len);
+               dhcp_client->server_duid_len = option_len;
+
+               if (packet6->message == DHCPV6_REPLY) {
+                       uint8_t *rapid_commit;
+                       count = 0;
+                       option_len = 0;
+                       rapid_commit = dhcpv6_get_option(packet6, pkt_len,
+                                                       G_DHCPV6_RAPID_COMMIT,
+                                                       &option_len, &count);
+                       if (rapid_commit == NULL || option_len == 0 ||
+                                                               count != 1)
+                               /* RFC 3315, 17.1.4 */
+                               return TRUE;
+               }
+
+               switch_listening_mode(dhcp_client, L_NONE);
+
+               if (dhcp_client->status_code == 0)
+                       get_dhcpv6_request(dhcp_client, packet6, pkt_len,
+                                       &dhcp_client->status_code);
+
+               if (packet6->message == DHCPV6_ADVERTISE) {
+                       if (dhcp_client->advertise_cb != NULL)
+                               dhcp_client->advertise_cb(dhcp_client,
+                                               dhcp_client->advertise_data);
+                       return TRUE;
+               }
+
+               if (dhcp_client->solicitation_cb != NULL) {
+                       /*
+                        * The dhcp_client might not be valid after the
+                        * callback call so just return immediately.
+                        */
+                       dhcp_client->solicitation_cb(dhcp_client,
+                                       dhcp_client->solicitation_data);
+                       return TRUE;
+               }
+               break;
+       case INFORMATION_REQ:
+       case REQUEST:
+       case RENEW:
+       case REBIND:
+       case RELEASE:
+       case CONFIRM:
+               if (dhcp_client->type != G_DHCP_IPV6)
+                       return TRUE;
+
+               if (packet6->message != DHCPV6_REPLY)
+                       return TRUE;
+
+               count = 0;
+               option_len = 0;
+               server_id = dhcpv6_get_option(packet6, pkt_len,
+                               G_DHCPV6_SERVERID, &option_len, &count);
+               if (server_id == NULL || count != 1 || option_len == 0 ||
+                               (dhcp_client->server_duid_len > 0 &&
+                               memcmp(dhcp_client->server_duid, server_id,
+                                       dhcp_client->server_duid_len) != 0)) {
+                       /* RFC 3315, 15.10 */
+                       debug(dhcp_client,
+                               "server duid error, discarding msg %p/%d/%d",
+                               server_id, option_len, count);
+                       return TRUE;
+               }
+
+               switch_listening_mode(dhcp_client, L_NONE);
+
+               get_dhcpv6_request(dhcp_client, packet6, pkt_len,
+                                               &dhcp_client->status_code);
+
+               if (dhcp_client->information_req_cb != NULL) {
+                       /*
+                        * The dhcp_client might not be valid after the
+                        * callback call so just return immediately.
+                        */
+                       dhcp_client->information_req_cb(dhcp_client,
+                                       dhcp_client->information_req_data);
+                       return TRUE;
+               }
+               if (dhcp_client->request_cb != NULL) {
+                       dhcp_client->request_cb(dhcp_client,
+                                       dhcp_client->request_data);
+                       return TRUE;
+               }
+               if (dhcp_client->renew_cb != NULL) {
+                       dhcp_client->renew_cb(dhcp_client,
+                                       dhcp_client->renew_data);
+                       return TRUE;
+               }
+               if (dhcp_client->rebind_cb != NULL) {
+                       dhcp_client->rebind_cb(dhcp_client,
+                                       dhcp_client->rebind_data);
+                       return TRUE;
+               }
+               if (dhcp_client->release_cb != NULL) {
+                       dhcp_client->release_cb(dhcp_client,
+                                       dhcp_client->release_data);
+                       return TRUE;
+               }
+               if (dhcp_client->confirm_cb != NULL) {
+                       count = 0;
+                       server_id = dhcpv6_get_option(packet6, pkt_len,
+                                               G_DHCPV6_SERVERID, &option_len,
+                                               &count);
+                       if (server_id == NULL || count != 1 ||
+                                                       option_len == 0) {
+                               /* RFC 3315, 15.10 */
+                               debug(dhcp_client,
+                                       "confirm server duid error, "
+                                       "discarding msg %p/%d/%d",
+                                       server_id, option_len, count);
+                               return TRUE;
+                       }
+                       dhcp_client->server_duid = g_try_malloc(option_len);
+                       if (dhcp_client->server_duid == NULL)
+                               return TRUE;
+                       memcpy(dhcp_client->server_duid, server_id, option_len);
+                       dhcp_client->server_duid_len = option_len;
+
+                       dhcp_client->confirm_cb(dhcp_client,
+                                               dhcp_client->confirm_data);
+                       return TRUE;
+               }
+               break;
        default:
                break;
        }
@@ -1280,6 +2363,11 @@ static gboolean ipv4ll_announce_timeout(gpointer dhcp_data)
        GDHCPClient *dhcp_client = dhcp_data;
        uint32_t ip;
 
+#if defined TIZEN_EXT
+       if (!dhcp_client)
+               return FALSE;
+#endif
+
        debug(dhcp_client, "request timeout (retries %d)",
               dhcp_client->retry_times);
 
@@ -1329,6 +2417,81 @@ int g_dhcp_client_start(GDHCPClient *dhcp_client, const char *last_address)
        int re;
        uint32_t addr;
 
+       if (dhcp_client->type == G_DHCP_IPV6) {
+               if (dhcp_client->information_req_cb) {
+                       dhcp_client->state = INFORMATION_REQ;
+                       re = switch_listening_mode(dhcp_client, L3);
+                       if (re != 0) {
+                               switch_listening_mode(dhcp_client, L_NONE);
+                               dhcp_client->state = 0;
+                               return re;
+                       }
+                       send_information_req(dhcp_client);
+
+               } else if (dhcp_client->solicitation_cb) {
+                       dhcp_client->state = SOLICITATION;
+                       re = switch_listening_mode(dhcp_client, L3);
+                       if (re != 0) {
+                               switch_listening_mode(dhcp_client, L_NONE);
+                               dhcp_client->state = 0;
+                               return re;
+                       }
+                       send_solicitation(dhcp_client);
+
+               } else if (dhcp_client->request_cb) {
+                       dhcp_client->state = REQUEST;
+                       re = switch_listening_mode(dhcp_client, L3);
+                       if (re != 0) {
+                               switch_listening_mode(dhcp_client, L_NONE);
+                               dhcp_client->state = 0;
+                               return re;
+                       }
+                       send_dhcpv6_request(dhcp_client);
+
+               } else if (dhcp_client->confirm_cb) {
+                       dhcp_client->state = CONFIRM;
+                       re = switch_listening_mode(dhcp_client, L3);
+                       if (re != 0) {
+                               switch_listening_mode(dhcp_client, L_NONE);
+                               dhcp_client->state = 0;
+                               return re;
+                       }
+                       send_dhcpv6_confirm(dhcp_client);
+
+               } else if (dhcp_client->renew_cb) {
+                       dhcp_client->state = RENEW;
+                       re = switch_listening_mode(dhcp_client, L3);
+                       if (re != 0) {
+                               switch_listening_mode(dhcp_client, L_NONE);
+                               dhcp_client->state = 0;
+                               return re;
+                       }
+                       send_dhcpv6_renew(dhcp_client);
+
+               } else if (dhcp_client->rebind_cb) {
+                       dhcp_client->state = REBIND;
+                       re = switch_listening_mode(dhcp_client, L3);
+                       if (re != 0) {
+                               switch_listening_mode(dhcp_client, L_NONE);
+                               dhcp_client->state = 0;
+                               return re;
+                       }
+                       send_dhcpv6_rebind(dhcp_client);
+
+               } else if (dhcp_client->release_cb) {
+                       dhcp_client->state = RENEW;
+                       re = switch_listening_mode(dhcp_client, L3);
+                       if (re != 0) {
+                               switch_listening_mode(dhcp_client, L_NONE);
+                               dhcp_client->state = 0;
+                               return re;
+                       }
+                       send_dhcpv6_release(dhcp_client);
+               }
+
+               return 0;
+       }
+
        if (dhcp_client->retry_times == DISCOVER_RETRIES) {
                ipv4ll_start(dhcp_client);
                return 0;
@@ -1344,15 +2507,20 @@ int g_dhcp_client_start(GDHCPClient *dhcp_client, const char *last_address)
                        return re;
 
                dhcp_client->xid = rand();
+               dhcp_client->start = time(NULL);
        }
 
        if (last_address == NULL) {
                addr = 0;
        } else {
-               addr = inet_addr(last_address);
+               addr = ntohl(inet_addr(last_address));
                if (addr == 0xFFFFFFFF) {
                        addr = 0;
+#if defined TIZEN_EXT
+               } else if (dhcp_client->last_address != last_address) {
+#else
                } else {
+#endif
                        g_free(dhcp_client->last_address);
                        dhcp_client->last_address = g_strdup(last_address);
                }
@@ -1386,10 +2554,13 @@ void g_dhcp_client_stop(GDHCPClient *dhcp_client)
                send_release(dhcp_client, dhcp_client->server_ip,
                                        dhcp_client->requested_ip);
 
-       if (dhcp_client->timeout > 0) {
-               g_source_remove(dhcp_client->timeout);
-               dhcp_client->timeout = 0;
+       remove_timeouts(dhcp_client);
+#if defined TIZEN_EXT && defined TIZEN_RTC_TIMER
+       if (dhcp_client->rtc_timeout > 0) {
+               rtc_timeout_remove(dhcp_client->rtc_timeout);
+               dhcp_client->rtc_timeout = 0;
        }
+#endif
 
        if (dhcp_client->listener_watch > 0) {
                g_source_remove(dhcp_client->listener_watch);
@@ -1424,6 +2595,8 @@ void g_dhcp_client_register_event(GDHCPClient *dhcp_client,
                dhcp_client->lease_available_data = data;
                return;
        case G_DHCP_CLIENT_EVENT_IPV4LL_AVAILABLE:
+               if (dhcp_client->type == G_DHCP_IPV6)
+                       return;
                dhcp_client->ipv4ll_available_cb = func;
                dhcp_client->ipv4ll_available_data = data;
                return;
@@ -1436,6 +2609,8 @@ void g_dhcp_client_register_event(GDHCPClient *dhcp_client,
                dhcp_client->lease_lost_data = data;
                return;
        case G_DHCP_CLIENT_EVENT_IPV4LL_LOST:
+               if (dhcp_client->type == G_DHCP_IPV6)
+                       return;
                dhcp_client->ipv4ll_lost_cb = func;
                dhcp_client->ipv4ll_lost_data = data;
                return;
@@ -1443,6 +2618,54 @@ void g_dhcp_client_register_event(GDHCPClient *dhcp_client,
                dhcp_client->address_conflict_cb = func;
                dhcp_client->address_conflict_data = data;
                return;
+       case G_DHCP_CLIENT_EVENT_INFORMATION_REQ:
+               if (dhcp_client->type == G_DHCP_IPV4)
+                       return;
+               dhcp_client->information_req_cb = func;
+               dhcp_client->information_req_data = data;
+               return;
+       case G_DHCP_CLIENT_EVENT_SOLICITATION:
+               if (dhcp_client->type == G_DHCP_IPV4)
+                       return;
+               dhcp_client->solicitation_cb = func;
+               dhcp_client->solicitation_data = data;
+               return;
+       case G_DHCP_CLIENT_EVENT_ADVERTISE:
+               if (dhcp_client->type == G_DHCP_IPV4)
+                       return;
+               dhcp_client->advertise_cb = func;
+               dhcp_client->advertise_data = data;
+               return;
+       case G_DHCP_CLIENT_EVENT_REQUEST:
+               if (dhcp_client->type == G_DHCP_IPV4)
+                       return;
+               dhcp_client->request_cb = func;
+               dhcp_client->request_data = data;
+               return;
+       case G_DHCP_CLIENT_EVENT_RENEW:
+               if (dhcp_client->type == G_DHCP_IPV4)
+                       return;
+               dhcp_client->renew_cb = func;
+               dhcp_client->renew_data = data;
+               return;
+       case G_DHCP_CLIENT_EVENT_REBIND:
+               if (dhcp_client->type == G_DHCP_IPV4)
+                       return;
+               dhcp_client->rebind_cb = func;
+               dhcp_client->rebind_data = data;
+               return;
+       case G_DHCP_CLIENT_EVENT_RELEASE:
+               if (dhcp_client->type == G_DHCP_IPV4)
+                       return;
+               dhcp_client->release_cb = func;
+               dhcp_client->release_data = data;
+               return;
+       case G_DHCP_CLIENT_EVENT_CONFIRM:
+               if (dhcp_client->type == G_DHCP_IPV4)
+                       return;
+               dhcp_client->confirm_cb = func;
+               dhcp_client->confirm_data = data;
+               return;
        }
 }
 
@@ -1460,6 +2683,9 @@ char *g_dhcp_client_get_netmask(GDHCPClient *dhcp_client)
 {
        GList *option = NULL;
 
+       if (dhcp_client->type == G_DHCP_IPV6)
+               return NULL;
+
        switch (dhcp_client->state) {
        case IPV4LL_DEFEND:
        case IPV4LL_MONITOR:
@@ -1475,13 +2701,20 @@ char *g_dhcp_client_get_netmask(GDHCPClient *dhcp_client)
        case RELEASED:
        case IPV4LL_PROBE:
        case IPV4LL_ANNOUNCE:
+       case INFORMATION_REQ:
+       case SOLICITATION:
+       case REQUEST:
+       case CONFIRM:
+       case RENEW:
+       case REBIND:
+       case RELEASE:
                break;
        }
        return NULL;
 }
 
 GDHCPClientError g_dhcp_client_set_request(GDHCPClient *dhcp_client,
-                                               unsigned char option_code)
+                                               unsigned int option_code)
 {
        if (g_list_find(dhcp_client->request_list,
                        GINT_TO_POINTER((int) option_code)) == NULL)
@@ -1492,19 +2725,64 @@ GDHCPClientError g_dhcp_client_set_request(GDHCPClient *dhcp_client,
        return G_DHCP_CLIENT_ERROR_NONE;
 }
 
-static uint8_t *alloc_dhcp_option(int code, const char *str, int extra)
+void g_dhcp_client_clear_requests(GDHCPClient *dhcp_client)
+{
+       g_list_free(dhcp_client->request_list);
+       dhcp_client->request_list = NULL;
+}
+
+void g_dhcp_client_clear_values(GDHCPClient *dhcp_client)
+{
+       g_hash_table_remove_all(dhcp_client->send_value_hash);
+}
+
+static uint8_t *alloc_dhcp_option(int code, const uint8_t *data, unsigned size)
 {
        uint8_t *storage;
-       int len = strnlen(str, 255);
 
-       storage = malloc(len + extra + OPT_DATA);
+       storage = g_try_malloc(size + OPT_DATA);
+       if (storage == NULL)
+               return NULL;
+
        storage[OPT_CODE] = code;
-       storage[OPT_LEN] = len + extra;
-       memcpy(storage + extra + OPT_DATA, str, len);
+       storage[OPT_LEN] = size;
+       memcpy(&storage[OPT_DATA], data, size);
 
        return storage;
 }
 
+static uint8_t *alloc_dhcp_data_option(int code, const uint8_t *data, unsigned size)
+{
+       return alloc_dhcp_option(code, data, MIN(size, 255));
+}
+
+static uint8_t *alloc_dhcp_string_option(int code, const char *str)
+{
+       return alloc_dhcp_data_option(code, (const uint8_t *)str, strlen(str));
+}
+
+GDHCPClientError g_dhcp_client_set_id(GDHCPClient *dhcp_client)
+{
+       const unsigned maclen = 6;
+       const unsigned idlen = maclen + 1;
+       const uint8_t option_code = G_DHCP_CLIENT_ID;
+       uint8_t idbuf[idlen];
+       uint8_t *data_option;
+
+       idbuf[0] = ARPHRD_ETHER;
+
+       memcpy(&idbuf[1], dhcp_client->mac_address, maclen);
+
+       data_option = alloc_dhcp_data_option(option_code, idbuf, idlen);
+       if (data_option == NULL)
+               return G_DHCP_CLIENT_ERROR_NOMEM;
+
+       g_hash_table_insert(dhcp_client->send_value_hash,
+               GINT_TO_POINTER((int) option_code), data_option);
+
+       return G_DHCP_CLIENT_ERROR_NONE;
+}
+
 /* Now only support send hostname */
 GDHCPClientError g_dhcp_client_set_send(GDHCPClient *dhcp_client,
                unsigned char option_code, const char *option_value)
@@ -1512,8 +2790,10 @@ GDHCPClientError g_dhcp_client_set_send(GDHCPClient *dhcp_client,
        uint8_t *binary_option;
 
        if (option_code == G_DHCP_HOST_NAME && option_value != NULL) {
-               binary_option = alloc_dhcp_option(option_code,
-                                                       option_value, 0);
+               binary_option = alloc_dhcp_string_option(option_code,
+                                                       option_value);
+               if (binary_option == NULL)
+                       return G_DHCP_CLIENT_ERROR_NOMEM;
 
                g_hash_table_insert(dhcp_client->send_value_hash,
                        GINT_TO_POINTER((int) option_code), binary_option);
@@ -1522,6 +2802,76 @@ GDHCPClientError g_dhcp_client_set_send(GDHCPClient *dhcp_client,
        return G_DHCP_CLIENT_ERROR_NONE;
 }
 
+static uint8_t *alloc_dhcpv6_option(uint16_t code, uint8_t *option,
+                               uint16_t len)
+{
+       uint8_t *storage;
+
+       storage = g_malloc(2 + 2 + len);
+       if (storage == NULL)
+               return NULL;
+
+       storage[0] = code >> 8;
+       storage[1] = code & 0xff;
+       storage[2] = len >> 8;
+       storage[3] = len & 0xff;
+       memcpy(storage + 2 + 2, option, len);
+
+       return storage;
+}
+
+void g_dhcpv6_client_set_send(GDHCPClient *dhcp_client,
+                                       uint16_t option_code,
+                                       uint8_t *option_value,
+                                       uint16_t option_len)
+{
+       if (option_value != NULL) {
+               uint8_t *binary_option;
+
+               debug(dhcp_client, "setting option %d to %p len %d",
+                       option_code, option_value, option_len);
+
+               binary_option = alloc_dhcpv6_option(option_code, option_value,
+                                               option_len);
+               if (binary_option != NULL)
+                       g_hash_table_insert(dhcp_client->send_value_hash,
+                                       GINT_TO_POINTER((int) option_code),
+                                       binary_option);
+       }
+}
+
+void g_dhcpv6_client_reset_renew(GDHCPClient *dhcp_client)
+{
+       if (dhcp_client == NULL || dhcp_client->type == G_DHCP_IPV4)
+               return;
+
+       dhcp_client->last_renew = time(NULL);
+}
+
+void g_dhcpv6_client_reset_rebind(GDHCPClient *dhcp_client)
+{
+       if (dhcp_client == NULL || dhcp_client->type == G_DHCP_IPV4)
+               return;
+
+       dhcp_client->last_rebind = time(NULL);
+}
+
+void g_dhcpv6_client_set_expire(GDHCPClient *dhcp_client, uint32_t timeout)
+{
+       if (dhcp_client == NULL || dhcp_client->type == G_DHCP_IPV4)
+               return;
+
+       dhcp_client->expire = time(NULL) + timeout;
+}
+
+uint16_t g_dhcpv6_client_get_status(GDHCPClient *dhcp_client)
+{
+       if (dhcp_client == NULL || dhcp_client->type == G_DHCP_IPV4)
+               return 0;
+
+       return dhcp_client->status_code;
+}
+
 GDHCPClient *g_dhcp_client_ref(GDHCPClient *dhcp_client)
 {
        if (dhcp_client == NULL)
@@ -1545,6 +2895,8 @@ void g_dhcp_client_unref(GDHCPClient *dhcp_client)
        g_free(dhcp_client->interface);
        g_free(dhcp_client->assigned_ip);
        g_free(dhcp_client->last_address);
+       g_free(dhcp_client->duid);
+       g_free(dhcp_client->server_duid);
 
        g_list_free(dhcp_client->request_list);
        g_list_free(dhcp_client->require_list);
@@ -1553,6 +2905,9 @@ void g_dhcp_client_unref(GDHCPClient *dhcp_client)
        g_hash_table_destroy(dhcp_client->send_value_hash);
 
        g_free(dhcp_client);
+#if defined TIZEN_EXT
+       dhcp_client = NULL;
+#endif
 }
 
 void g_dhcp_client_set_debug(GDHCPClient *dhcp_client,
index 0a2b51b..3eb5624 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *  DHCP library with GLib integration
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
@@ -23,6 +23,7 @@
 #endif
 
 #include <stdio.h>
+#include <stdlib.h>
 #include <errno.h>
 #include <unistd.h>
 #include <sys/ioctl.h>
@@ -33,6 +34,7 @@
 #include <linux/if.h>
 #include <netpacket/packet.h>
 #include <net/ethernet.h>
+#include <arpa/inet.h>
 
 #include "gdhcp.h"
 #include "common.h"
@@ -142,6 +144,82 @@ int dhcp_end_option(uint8_t *optionptr)
        return i;
 }
 
+uint8_t *dhcpv6_get_option(struct dhcpv6_packet *packet, uint16_t pkt_len,
+                       int code, uint16_t *option_len, int *option_count)
+{
+       int rem, count = 0;
+       uint8_t *optionptr, *found = NULL;
+       uint16_t opt_code, opt_len, len;
+
+       optionptr = packet->options;
+       rem = pkt_len - 1 - 3;
+
+       if (rem <= 0)
+               goto bad_packet;
+
+       while (1) {
+               opt_code = optionptr[0] << 8 | optionptr[1];
+               opt_len = len = optionptr[2] << 8 | optionptr[3];
+               len += 2 + 2; /* skip code and len */
+
+               if (len < 4)
+                       goto bad_packet;
+
+               rem -= len;
+               if (rem < 0)
+                       break;
+
+               if (opt_code == code) {
+                       if (option_len != NULL)
+                               *option_len = opt_len;
+                       found = optionptr + 2 + 2;
+                       count++;
+               }
+
+               if (rem == 0)
+                       break;
+
+               optionptr += len;
+       }
+
+       if (option_count != NULL)
+               *option_count = count;
+
+       return found;
+
+bad_packet:
+       if (option_len != NULL)
+               *option_len = 0;
+       if (option_count != NULL)
+               *option_count = 0;
+       return NULL;
+}
+
+uint8_t *dhcpv6_get_sub_option(unsigned char *option, uint16_t max_len,
+                       uint16_t *option_code, uint16_t *option_len)
+{
+       int rem;
+       uint16_t code, len;
+
+       rem = max_len - 2 - 2;
+
+       if (rem <= 0)
+               /* Bad option */
+               return NULL;
+
+       code = option[0] << 8 | option[1];
+       len = option[2] << 8 | option[3];
+
+       rem -= len;
+       if (rem < 0)
+               return NULL;
+
+       *option_code = code;
+       *option_len = len;
+
+       return &option[4];
+}
+
 /*
  * Add an option (supplied in binary form) to the options.
  * Option format: [code][len][data1][data2]..[dataLEN]
@@ -164,25 +242,91 @@ void dhcp_add_binary_option(struct dhcp_packet *packet, uint8_t *addopt)
        optionptr[end + len] = DHCP_END;
 }
 
-void dhcp_add_simple_option(struct dhcp_packet *packet, uint8_t code,
-                                                       uint32_t data)
+/*
+ * Add an option (supplied in binary form) to the options.
+ * Option format: [code][len][data1][data2]..[dataLEN]
+ */
+void dhcpv6_add_binary_option(struct dhcpv6_packet *packet, uint16_t max_len,
+                               uint16_t *pkt_len, uint8_t *addopt)
+{
+       unsigned len;
+       uint8_t *optionptr = packet->options;
+
+       len = 2 + 2 + (addopt[2] << 8 | addopt[3]);
+
+       /* end position + (option code/length + addopt length) */
+       if (*pkt_len + len >= max_len)
+               /* option did not fit into the packet */
+               return;
+
+       memcpy(optionptr + *pkt_len, addopt, len);
+       *pkt_len += len;
+}
+
+static GDHCPOptionType check_option(uint8_t code, uint8_t data_len)
 {
-       uint8_t option[6], len;
        GDHCPOptionType type = dhcp_get_code_type(code);
+       uint8_t len;
 
        if (type == OPTION_UNKNOWN)
+               return type;
+
+       len = dhcp_option_lengths[type & OPTION_TYPE_MASK];
+       if (len != data_len) {
+               printf("Invalid option len %d (expecting %d) for code 0x%x\n",
+                       data_len, len, code);
+               return OPTION_UNKNOWN;
+       }
+
+       return type;
+}
+
+void dhcp_add_option_uint32(struct dhcp_packet *packet, uint8_t code,
+                                                       uint32_t data)
+{
+       uint8_t option[6];
+
+       if (check_option(code, sizeof(data)) == OPTION_UNKNOWN)
                return;
 
        option[OPT_CODE] = code;
+       option[OPT_LEN] = sizeof(data);
+       put_be32(data, option + OPT_DATA);
 
-       len = dhcp_option_lengths[type & OPTION_TYPE_MASK];
-       option[OPT_LEN] = len;
+       dhcp_add_binary_option(packet, option);
 
-#if __BYTE_ORDER == __BIG_ENDIAN
-       data <<= 8 * (4 - len);
-#endif
+       return;
+}
+
+void dhcp_add_option_uint16(struct dhcp_packet *packet, uint8_t code,
+                                                       uint16_t data)
+{
+       uint8_t option[6];
+
+       if (check_option(code, sizeof(data)) == OPTION_UNKNOWN)
+               return;
+
+       option[OPT_CODE] = code;
+       option[OPT_LEN] = sizeof(data);
+       put_be16(data, option + OPT_DATA);
+
+       dhcp_add_binary_option(packet, option);
+
+       return;
+}
+
+void dhcp_add_option_uint8(struct dhcp_packet *packet, uint8_t code,
+                                                       uint8_t data)
+{
+       uint8_t option[6];
+
+       if (check_option(code, sizeof(data)) == OPTION_UNKNOWN)
+               return;
+
+       option[OPT_CODE] = code;
+       option[OPT_LEN] = sizeof(data);
+       option[OPT_DATA] = data;
 
-       dhcp_put_unaligned(data, (uint32_t *) &option[OPT_DATA]);
        dhcp_add_binary_option(packet, option);
 
        return;
@@ -206,7 +350,22 @@ void dhcp_init_header(struct dhcp_packet *packet, char type)
        packet->cookie = htonl(DHCP_MAGIC);
        packet->options[0] = DHCP_END;
 
-       dhcp_add_simple_option(packet, DHCP_MESSAGE_TYPE, type);
+       dhcp_add_option_uint8(packet, DHCP_MESSAGE_TYPE, type);
+}
+
+void dhcpv6_init_header(struct dhcpv6_packet *packet, uint8_t type)
+{
+       int id;
+
+       memset(packet, 0, sizeof(*packet));
+
+       packet->message = type;
+
+       id = random();
+
+       packet->transaction_id[0] = (id >> 16) & 0xff;
+       packet->transaction_id[1] = (id >> 8) & 0xff;
+       packet->transaction_id[2] = id & 0xff;
 }
 
 static gboolean check_vendor(uint8_t  *option_vendor, const char *vendor)
@@ -255,6 +414,20 @@ int dhcp_recv_l3_packet(struct dhcp_packet *packet, int fd)
        return n;
 }
 
+int dhcpv6_recv_l3_packet(struct dhcpv6_packet **packet, unsigned char *buf,
+                       int buf_len, int fd)
+{
+       int n;
+
+       n = read(fd, buf, buf_len);
+       if (n < 0)
+               return -errno;
+
+       *packet = (struct dhcpv6_packet *)buf;
+
+       return n;
+}
+
 /* TODO: Use glib checksum */
 uint16_t dhcp_checksum(void *addr, int count)
 {
@@ -286,6 +459,78 @@ uint16_t dhcp_checksum(void *addr, int count)
        return ~sum;
 }
 
+#define IN6ADDR_ALL_DHCP_RELAY_AGENTS_AND_SERVERS_MC_INIT \
+       { { { 0xff,0x02,0,0,0,0,0,0,0,0,0,0,0,0x1,0,0x2 } } } /* ff02::1:2 */
+static const struct in6_addr in6addr_all_dhcp_relay_agents_and_servers_mc =
+       IN6ADDR_ALL_DHCP_RELAY_AGENTS_AND_SERVERS_MC_INIT;
+
+/* from netinet/in.h */
+struct in6_pktinfo {
+       struct in6_addr ipi6_addr;  /* src/dst IPv6 address */
+       unsigned int ipi6_ifindex;  /* send/recv interface index */
+};
+
+int dhcpv6_send_packet(int index, struct dhcpv6_packet *dhcp_pkt, int len)
+{
+       struct msghdr m;
+       struct iovec v;
+       struct in6_pktinfo *pktinfo;
+       struct cmsghdr *cmsg;
+       int fd, ret;
+       struct sockaddr_in6 dst;
+       void *control_buf;
+       size_t control_buf_len;
+
+       fd = socket(PF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, IPPROTO_UDP);
+       if (fd < 0)
+               return -errno;
+
+       memset(&dst, 0, sizeof(dst));
+       dst.sin6_family = AF_INET6;
+       dst.sin6_port = htons(DHCPV6_SERVER_PORT);
+
+       dst.sin6_addr = in6addr_all_dhcp_relay_agents_and_servers_mc;
+
+       control_buf_len = CMSG_SPACE(sizeof(struct in6_pktinfo));
+       control_buf = g_try_malloc0(control_buf_len);
+       if (control_buf == NULL) {
+               close(fd);
+               return -ENOMEM;
+       }
+
+       memset(&m, 0, sizeof(m));
+       memset(&v, 0, sizeof(v));
+
+       m.msg_name = &dst;
+       m.msg_namelen = sizeof(dst);
+
+       v.iov_base = (char *)dhcp_pkt;
+       v.iov_len = len;
+       m.msg_iov = &v;
+       m.msg_iovlen = 1;
+
+       m.msg_control = control_buf;
+       m.msg_controllen = control_buf_len;
+       cmsg = CMSG_FIRSTHDR(&m);
+       cmsg->cmsg_level = IPPROTO_IPV6;
+       cmsg->cmsg_type = IPV6_PKTINFO;
+       cmsg->cmsg_len = CMSG_LEN(sizeof(*pktinfo));
+
+       pktinfo = (struct in6_pktinfo *)CMSG_DATA(cmsg);
+       memset(pktinfo, 0, sizeof(*pktinfo));
+       pktinfo->ipi6_ifindex = index;
+       m.msg_controllen = cmsg->cmsg_len;
+
+       ret = sendmsg(fd, &m, 0);
+       if (ret < 0)
+               perror("DHCPv6 msg send failed");
+
+       g_free(control_buf);
+       close(fd);
+
+       return ret;
+}
+
 int dhcp_send_raw_packet(struct dhcp_packet *dhcp_pkt,
                uint32_t source_ip, int source_port, uint32_t dest_ip,
                        int dest_port, const uint8_t *dest_arp, int ifindex)
@@ -372,7 +617,7 @@ int dhcp_send_kernel_packet(struct dhcp_packet *dhcp_pkt,
        memset(&client, 0, sizeof(client));
        client.sin_family = AF_INET;
        client.sin_port = htons(source_port);
-       client.sin_addr.s_addr = source_ip;
+       client.sin_addr.s_addr = htonl(source_ip);
        if (bind(fd, (struct sockaddr *) &client, sizeof(client)) < 0) {
                close(fd);
                return -errno;
@@ -381,7 +626,7 @@ int dhcp_send_kernel_packet(struct dhcp_packet *dhcp_pkt,
        memset(&client, 0, sizeof(client));
        client.sin_family = AF_INET;
        client.sin_port = htons(dest_port);
-       client.sin_addr.s_addr = dest_ip;
+       client.sin_addr.s_addr = htonl(dest_ip);
        if (connect(fd, (struct sockaddr *) &client, sizeof(client)) < 0) {
                close(fd);
                return -errno;
@@ -397,12 +642,16 @@ int dhcp_send_kernel_packet(struct dhcp_packet *dhcp_pkt,
        return n;
 }
 
-int dhcp_l3_socket(int port, const char *interface)
+int dhcp_l3_socket(int port, const char *interface, int family)
 {
-       int fd, opt = 1;
-       struct sockaddr_in addr;
+       int fd, opt = 1, len;
+       struct sockaddr_in addr4;
+       struct sockaddr_in6 addr6;
+       struct sockaddr *addr;
 
-       fd = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, IPPROTO_UDP);
+       fd = socket(family, SOCK_DGRAM | SOCK_CLOEXEC, IPPROTO_UDP);
+       if (fd < 0)
+               return -errno;
 
        setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
 
@@ -412,10 +661,24 @@ int dhcp_l3_socket(int port, const char *interface)
                return -1;
        }
 
-       memset(&addr, 0, sizeof(addr));
-       addr.sin_family = AF_INET;
-       addr.sin_port = htons(port);
-       if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
+       if (family == AF_INET) {
+               memset(&addr4, 0, sizeof(addr4));
+               addr4.sin_family = family;
+               addr4.sin_port = htons(port);
+               addr = (struct sockaddr *)&addr4;
+               len = sizeof(addr4);
+       } else if (family == AF_INET6) {
+               memset(&addr6, 0, sizeof(addr6));
+               addr6.sin6_family = family;
+               addr6.sin6_port = htons(port);
+               addr = (struct sockaddr *)&addr6;
+               len = sizeof(addr6);
+       } else {
+               close(fd);
+               return -EINVAL;
+       }
+
+       if (bind(fd, addr, len) != 0) {
                close(fd);
                return -1;
        }
index 3aab3d2..740eb9c 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  DHCP client library with GLib integration
  *
- *  Copyright (C) 2009-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2009-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
 
 #include <glib.h>
 
+#include "unaligned.h"
 #include "gdhcp.h"
 
-#define dhcp_get_unaligned(ptr)                        \
-({                                             \
-       struct __attribute__((packed)) {        \
-               typeof(*(ptr)) __v;             \
-       } *__p = (void *) (ptr);                \
-       __p->__v;                               \
-})
-
-#define dhcp_put_unaligned(val, ptr)           \
-do {                                           \
-       struct __attribute__((packed)) {        \
-               typeof(*(ptr)) __v;             \
-       } *__p = (void *) (ptr);                \
-       __p->__v = (val);                       \
-} while (0)
-
 #define CLIENT_PORT 68
 #define SERVER_PORT 67
 
+#define DHCPV6_CLIENT_PORT 546
+#define DHCPV6_SERVER_PORT 547
+#define MAX_DHCPV6_PKT_SIZE 1500
+
 #define EXTEND_FOR_BUGGY_SERVERS 80
 
 static const uint8_t MAC_BCAST_ADDR[ETH_ALEN] __attribute__((aligned(2))) = {
@@ -89,6 +78,14 @@ struct ip_udp_dhcp_packet {
        struct dhcp_packet data;
 } __attribute__((packed));
 
+/* See RFC 3315 */
+struct dhcpv6_packet {
+       uint8_t message;
+       uint8_t transaction_id[3];
+       uint8_t options[];
+} __attribute__((packed));
+
+
 /* See RFC 2132 */
 #define DHCP_PADDING           0x00
 #define DHCP_SUBNET            0x01
@@ -130,6 +127,24 @@ struct ip_udp_dhcp_packet {
 #define DHCP_MINTYPE DHCPDISCOVER
 #define DHCP_MAXTYPE DHCPINFORM
 
+/* Message types for DHCPv6, RFC 3315 sec 5.3 */
+#define DHCPV6_SOLICIT         1
+#define DHCPV6_ADVERTISE       2
+#define DHCPV6_REQUEST         3
+#define DHCPV6_CONFIRM         4
+#define DHCPV6_RENEW           5
+#define DHCPV6_REBIND          6
+#define DHCPV6_REPLY           7
+#define DHCPV6_RELEASE         8
+#define DHCPV6_DECLINE         9
+#define DHCPV6_RECONFIGURE     10
+#define DHCPV6_INFORMATION_REQ 11
+
+/*
+ * DUID time starts 2000-01-01.
+ */
+#define DUID_TIME_EPOCH 946684800
+
 typedef enum {
        OPTION_UNKNOWN,
        OPTION_IP,
@@ -156,24 +171,41 @@ static const uint8_t dhcp_option_lengths[] = {
 };
 
 uint8_t *dhcp_get_option(struct dhcp_packet *packet, int code);
+uint8_t *dhcpv6_get_option(struct dhcpv6_packet *packet, uint16_t pkt_len,
+                       int code, uint16_t *option_len, int *option_count);
+uint8_t *dhcpv6_get_sub_option(unsigned char *option, uint16_t max_len,
+                       uint16_t *code, uint16_t *option_len);
 int dhcp_end_option(uint8_t *optionptr);
 void dhcp_add_binary_option(struct dhcp_packet *packet, uint8_t *addopt);
-void dhcp_add_simple_option(struct dhcp_packet *packet,
+void dhcpv6_add_binary_option(struct dhcpv6_packet *packet, uint16_t max_len,
+                               uint16_t *pkt_len, uint8_t *addopt);
+void dhcp_add_option_uint8(struct dhcp_packet *packet,
+                               uint8_t code, uint8_t data);
+void dhcp_add_option_uint16(struct dhcp_packet *packet,
+                               uint8_t code, uint16_t data);
+void dhcp_add_option_uint32(struct dhcp_packet *packet,
                                uint8_t code, uint32_t data);
 GDHCPOptionType dhcp_get_code_type(uint8_t code);
+GDHCPOptionType dhcpv6_get_code_type(uint16_t code);
 
 uint16_t dhcp_checksum(void *addr, int count);
 
 void dhcp_init_header(struct dhcp_packet *packet, char type);
+void dhcpv6_init_header(struct dhcpv6_packet *packet, uint8_t type);
 
 int dhcp_send_raw_packet(struct dhcp_packet *dhcp_pkt,
                        uint32_t source_ip, int source_port,
                        uint32_t dest_ip, int dest_port,
                        const uint8_t *dest_arp, int ifindex);
+int dhcpv6_send_packet(int index, struct dhcpv6_packet *dhcp_pkt, int len);
 int dhcp_send_kernel_packet(struct dhcp_packet *dhcp_pkt,
                        uint32_t source_ip, int source_port,
                        uint32_t dest_ip, int dest_port);
-int dhcp_l3_socket(int port, const char *interface);
+int dhcp_l3_socket(int port, const char *interface, int family);
 int dhcp_recv_l3_packet(struct dhcp_packet *packet, int fd);
+int dhcpv6_recv_l3_packet(struct dhcpv6_packet **packet, unsigned char *buf,
+                       int buf_len, int fd);
+int dhcp_l3_socket_send(int index, int port, int family);
+
 char *get_interface_name(int index);
 gboolean interface_is_up(int index);
index b983c23..71431f5 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  DHCP library with GLib integration
  *
- *  Copyright (C) 2009-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2009-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
@@ -22,6 +22,8 @@
 #ifndef __G_DHCP_H
 #define __G_DHCP_H
 
+#include <stdint.h>
+
 #include <glib.h>
 
 #ifdef __cplusplus
@@ -50,6 +52,14 @@ typedef enum {
        G_DHCP_CLIENT_EVENT_LEASE_LOST,
        G_DHCP_CLIENT_EVENT_IPV4LL_LOST,
        G_DHCP_CLIENT_EVENT_ADDRESS_CONFLICT,
+       G_DHCP_CLIENT_EVENT_INFORMATION_REQ,
+       G_DHCP_CLIENT_EVENT_SOLICITATION,
+       G_DHCP_CLIENT_EVENT_ADVERTISE,
+       G_DHCP_CLIENT_EVENT_REQUEST,
+       G_DHCP_CLIENT_EVENT_RENEW,
+       G_DHCP_CLIENT_EVENT_REBIND,
+       G_DHCP_CLIENT_EVENT_RELEASE,
+       G_DHCP_CLIENT_EVENT_CONFIRM,
 } GDHCPClientEvent;
 
 typedef enum {
@@ -65,6 +75,32 @@ typedef enum {
 #define G_DHCP_DOMAIN_NAME     0x0f
 #define G_DHCP_HOST_NAME       0x0c
 #define G_DHCP_NTP_SERVER      0x2a
+#define G_DHCP_CLIENT_ID       0x3d
+
+#define G_DHCPV6_CLIENTID      1
+#define G_DHCPV6_SERVERID      2
+#define G_DHCPV6_IA_NA         3
+#define G_DHCPV6_IA_TA         4
+#define G_DHCPV6_IAADDR                5
+#define G_DHCPV6_ORO           6
+#define G_DHCPV6_ELAPSED_TIME   8
+#define G_DHCPV6_STATUS_CODE   13
+#define G_DHCPV6_RAPID_COMMIT  14
+#define G_DHCPV6_DNS_SERVERS   23
+#define G_DHCPV6_SNTP_SERVERS  31
+
+#define G_DHCPV6_ERROR_SUCCESS 0
+#define G_DHCPV6_ERROR_FAILURE 1
+#define G_DHCPV6_ERROR_NO_ADDR 2
+#define G_DHCPV6_ERROR_BINDING 3
+#define G_DHCPV6_ERROR_LINK    4
+#define G_DHCPV6_ERROR_MCAST   5
+
+typedef enum {
+       G_DHCPV6_DUID_LLT = 1,
+       G_DHCPV6_DUID_EN  = 2,
+       G_DHCPV6_DUID_LL  = 3,
+} GDHCPDuidType;
 
 typedef void (*GDHCPClientEventFunc) (GDHCPClient *client, gpointer user_data);
 
@@ -85,7 +121,10 @@ void g_dhcp_client_register_event(GDHCPClient *client,
                                        gpointer user_data);
 
 GDHCPClientError g_dhcp_client_set_request(GDHCPClient *client,
-                                               unsigned char option_code);
+                                               unsigned int option_code);
+void g_dhcp_client_clear_requests(GDHCPClient *dhcp_client);
+void g_dhcp_client_clear_values(GDHCPClient *dhcp_client);
+GDHCPClientError g_dhcp_client_set_id(GDHCPClient *client);
 GDHCPClientError g_dhcp_client_set_send(GDHCPClient *client,
                                                unsigned char option_code,
                                                const char *option_value);
@@ -98,9 +137,29 @@ int g_dhcp_client_get_index(GDHCPClient *client);
 
 void g_dhcp_client_set_debug(GDHCPClient *client,
                                GDHCPDebugFunc func, gpointer user_data);
-#if defined TIZEN_EXT
-void g_dhcp_client_set_address_known(GDHCPClient *client, gboolean known);
-#endif
+int g_dhcpv6_create_duid(GDHCPDuidType duid_type, int index, int type,
+                       unsigned char **duid, int *duid_len);
+int g_dhcpv6_client_set_duid(GDHCPClient *dhcp_client, unsigned char *duid,
+                       int duid_len);
+void g_dhcpv6_client_set_send(GDHCPClient *dhcp_client, uint16_t option_code,
+                       uint8_t *option_value, uint16_t option_len);
+uint16_t g_dhcpv6_client_get_status(GDHCPClient *dhcp_client);
+int g_dhcpv6_client_set_oro(GDHCPClient *dhcp_client, int args, ...);
+void g_dhcpv6_client_create_iaid(GDHCPClient *dhcp_client, int index,
+                               unsigned char *iaid);
+int g_dhcpv6_client_get_timeouts(GDHCPClient *dhcp_client,
+                               uint32_t *T1, uint32_t *T2,
+                               time_t *last_renew, time_t *last_rebind,
+                               time_t *expire);
+uint32_t g_dhcpv6_client_get_iaid(GDHCPClient *dhcp_client);
+int g_dhcpv6_client_set_ia(GDHCPClient *dhcp_client, int index,
+                       int code, uint32_t *T1, uint32_t *T2,
+                       gboolean add_addresses, const char *address);
+void g_dhcpv6_client_reset_renew(GDHCPClient *dhcp_client);
+void g_dhcpv6_client_reset_rebind(GDHCPClient *dhcp_client);
+void g_dhcpv6_client_set_expire(GDHCPClient *dhcp_client, uint32_t timeout);
+void g_dhcpv6_client_set_retransmit(GDHCPClient *dhcp_client);
+void g_dhcpv6_client_clear_retransmit(GDHCPClient *dhcp_client);
 
 /* DHCP Server */
 typedef enum {
@@ -132,14 +191,16 @@ int g_dhcp_server_set_option(GDHCPServer *server,
                unsigned char option_code, const char *option_value);
 int g_dhcp_server_set_ip_range(GDHCPServer *server,
                const char *start_ip, const char *end_ip);
-void g_dhcp_server_load_lease(GDHCPServer *dhcp_server, unsigned int expire,
-                               unsigned char *mac, unsigned int lease_ip);
 void g_dhcp_server_set_debug(GDHCPServer *server,
                                GDHCPDebugFunc func, gpointer user_data);
 void g_dhcp_server_set_lease_time(GDHCPServer *dhcp_server,
                                                unsigned int lease_time);
 void g_dhcp_server_set_save_lease(GDHCPServer *dhcp_server,
                                GDHCPSaveLeaseFunc func, gpointer user_data);
+#if defined TIZEN_EXT
+void g_dhcp_client_set_address_known(GDHCPClient *client, gboolean known);
+#endif
+
 #ifdef __cplusplus
 }
 #endif
index 4c09572..9bf52b0 100644 (file)
@@ -36,7 +36,7 @@
 #include "ipv4ll.h"
 
 /**
- * Return a random link local IP
+ * Return a random link local IP (in host byte order)
  */
 uint32_t ipv4ll_random_ip(int seed)
 {
@@ -111,7 +111,7 @@ int ipv4ll_send_arp_packet(uint8_t* source_eth, uint32_t source_ip,
        n = sendto(fd, &p, sizeof(p), 0,
               (struct sockaddr*) &dest, sizeof(dest));
        if (n < 0)
-               return -errno;
+               n = -errno;
 
        close(fd);
 
@@ -122,10 +122,13 @@ int ipv4ll_arp_socket(int ifindex)
 {
        int fd;
        struct sockaddr_ll sock;
+
        fd = socket(PF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC, htons(ETH_P_ARP));
        if (fd < 0)
                return fd;
 
+       memset(&sock, 0, sizeof(sock));
+
        sock.sll_family = AF_PACKET;
        sock.sll_protocol = htons(ETH_P_ARP);
        sock.sll_ifindex = ifindex;
index 4f0b5b7..0c5f295 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  DHCP Server library with GLib integration
  *
- *  Copyright (C) 2009-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2009-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
@@ -140,7 +140,7 @@ static int get_lease(GDHCPServer *dhcp_server, uint32_t yiaddr,
        lease_mac = find_lease_by_mac(dhcp_server, mac);
 
        lease_nip = g_hash_table_lookup(dhcp_server->nip_lease_hash,
-                                               GINT_TO_POINTER((int) yiaddr));
+                                       GINT_TO_POINTER((int) ntohl(yiaddr)));
        debug(dhcp_server, "lease_mac %p lease_nip %p", lease_mac, lease_nip);
 
        if (lease_nip != NULL) {
@@ -148,7 +148,7 @@ static int get_lease(GDHCPServer *dhcp_server, uint32_t yiaddr,
                                g_list_remove(dhcp_server->lease_list,
                                                                lease_nip);
                g_hash_table_remove(dhcp_server->nip_lease_hash,
-                                       GINT_TO_POINTER((int) yiaddr));
+                               GINT_TO_POINTER((int) ntohl(yiaddr)));
 
                if (lease_mac == NULL)
                        *lease = lease_nip;
@@ -200,7 +200,7 @@ static struct dhcp_lease *add_lease(GDHCPServer *dhcp_server, uint32_t expire,
        memset(lease, 0, sizeof(*lease));
 
        memcpy(lease->lease_mac, chaddr, ETH_ALEN);
-       lease->lease_nip = yiaddr;
+       lease->lease_nip = ntohl(yiaddr);
 
        if (expire == 0)
                lease->expire = time(NULL) + dhcp_server->lease_seconds;
@@ -254,13 +254,12 @@ static uint32_t find_free_or_expired_nip(GDHCPServer *dhcp_server,
                if ((ip_addr & 0xff) == 0xff)
                        continue;
 
-               lease = find_lease_by_nip(dhcp_server,
-                               (uint32_t) htonl(ip_addr));
+               lease = find_lease_by_nip(dhcp_server, ip_addr);
                if (lease != NULL)
                        continue;
 
                if (arp_check(htonl(ip_addr), safe_mac) == TRUE)
-                       return htonl(ip_addr);
+                       return ip_addr;
        }
 
        /* The last lease is the oldest one */
@@ -450,7 +449,8 @@ static void init_packet(GDHCPServer *dhcp_server, struct dhcp_packet *packet,
        packet->flags = client_packet->flags;
        packet->gateway_nip = client_packet->gateway_nip;
        packet->ciaddr = client_packet->ciaddr;
-       dhcp_add_simple_option(packet, DHCP_SERVER_ID, dhcp_server->server_nip);
+       dhcp_add_option_uint32(packet, DHCP_SERVER_ID,
+                                               dhcp_server->server_nip);
 }
 
 static void add_option(gpointer key, gpointer value, gpointer user_data)
@@ -470,8 +470,8 @@ static void add_option(gpointer key, gpointer value, gpointer user_data)
                if (inet_aton(option_value, &nip) == 0)
                        return;
 
-               dhcp_add_simple_option(packet, (uint8_t) option_code,
-                                                               nip.s_addr);
+               dhcp_add_option_uint32(packet, (uint8_t) option_code,
+                                                       ntohl(nip.s_addr));
                break;
        default:
                return;
@@ -493,10 +493,10 @@ static gboolean check_requested_nip(GDHCPServer *dhcp_server,
        if (requested_nip == 0)
                return FALSE;
 
-       if (ntohl(requested_nip) < dhcp_server->start_ip)
+       if (requested_nip < dhcp_server->start_ip)
                return FALSE;
 
-       if (ntohl(requested_nip) > dhcp_server->end_ip)
+       if (requested_nip > dhcp_server->end_ip)
                return FALSE;
 
        lease = find_lease_by_nip(dhcp_server, requested_nip);
@@ -543,12 +543,12 @@ static void send_offer(GDHCPServer *dhcp_server,
        init_packet(dhcp_server, &packet, client_packet, DHCPOFFER);
 
        if (lease)
-               packet.yiaddr = lease->lease_nip;
+               packet.yiaddr = htonl(lease->lease_nip);
        else if (check_requested_nip(dhcp_server, requested_nip) == TRUE)
-               packet.yiaddr = requested_nip;
+               packet.yiaddr = htonl(requested_nip);
        else
-               packet.yiaddr = find_free_or_expired_nip(
-                               dhcp_server, client_packet->chaddr);
+               packet.yiaddr = htonl(find_free_or_expired_nip(
+                                       dhcp_server, client_packet->chaddr));
 
        debug(dhcp_server, "find yiaddr %u", packet.yiaddr);
 
@@ -565,8 +565,8 @@ static void send_offer(GDHCPServer *dhcp_server,
                return;
        }
 
-       dhcp_add_simple_option(&packet, DHCP_LEASE_TIME,
-                               htonl(dhcp_server->lease_seconds));
+       dhcp_add_option_uint32(&packet, DHCP_LEASE_TIME,
+                                               dhcp_server->lease_seconds);
        add_server_options(dhcp_server, &packet);
 
        addr.s_addr = packet.yiaddr;
@@ -590,22 +590,22 @@ static void save_lease(GDHCPServer *dhcp_server)
 }
 
 static void send_ACK(GDHCPServer *dhcp_server,
-               struct dhcp_packet *client_packet, uint32_t yiaddr)
+               struct dhcp_packet *client_packet, uint32_t dest)
 {
        struct dhcp_packet packet;
        uint32_t lease_time_sec;
        struct in_addr addr;
 
        init_packet(dhcp_server, &packet, client_packet, DHCPACK);
-       packet.yiaddr = yiaddr;
+       packet.yiaddr = htonl(dest);
 
        lease_time_sec = dhcp_server->lease_seconds;
 
-       dhcp_add_simple_option(&packet, DHCP_LEASE_TIME, htonl(lease_time_sec));
+       dhcp_add_option_uint32(&packet, DHCP_LEASE_TIME, lease_time_sec);
 
        add_server_options(dhcp_server, &packet);
 
-       addr.s_addr = yiaddr;
+       addr.s_addr = htonl(dest);
 
        debug(dhcp_server, "Sending ACK to %s", inet_ntoa(addr));
 
@@ -664,8 +664,7 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
 
        server_id_option = dhcp_get_option(&packet, DHCP_SERVER_ID);
        if (server_id_option) {
-               uint32_t server_nid = dhcp_get_unaligned(
-                                       (uint32_t *) server_id_option);
+               uint32_t server_nid = get_be32(server_id_option);
 
                if (server_nid != dhcp_server->server_nip)
                        return TRUE;
@@ -673,8 +672,7 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
 
        request_ip_option = dhcp_get_option(&packet, DHCP_REQUESTED_IP);
        if (request_ip_option)
-               requested_nip = dhcp_get_unaligned(
-                                       (uint32_t *) request_ip_option);
+               requested_nip = get_be32(request_ip_option);
 
        lease = find_lease_by_mac(dhcp_server, packet.chaddr);
 
@@ -754,7 +752,7 @@ int g_dhcp_server_start(GDHCPServer *dhcp_server)
                return 0;
 
        listener_sockfd = dhcp_l3_socket(SERVER_PORT,
-                               dhcp_server->interface);
+                                       dhcp_server->interface, AF_INET);
        if (listener_sockfd < 0)
                return -EIO;
 
@@ -860,12 +858,6 @@ void g_dhcp_server_unref(GDHCPServer *dhcp_server)
        g_free(dhcp_server);
 }
 
-void g_dhcp_server_load_lease(GDHCPServer *dhcp_server, unsigned int expire,
-                               unsigned char *mac, unsigned int lease_ip)
-{
-       add_lease(dhcp_server, expire, mac, lease_ip);
-}
-
 int g_dhcp_server_set_ip_range(GDHCPServer *dhcp_server,
                const char *start_ip, const char *end_ip)
 {
diff --git a/gdhcp/unaligned.h b/gdhcp/unaligned.h
new file mode 100644 (file)
index 0000000..ffdaae2
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2012  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <endian.h>
+#include <byteswap.h>
+
+#define get_unaligned(ptr)                     \
+({                                             \
+       struct __attribute__((packed)) {        \
+               typeof(*(ptr)) __v;             \
+       } *__p = (typeof(__p)) (ptr);           \
+       __p->__v;                               \
+})
+
+#define put_unaligned(val, ptr)                        \
+do {                                           \
+       struct __attribute__((packed)) {        \
+               typeof(*(ptr)) __v;             \
+       } *__p = (typeof(__p)) (ptr);           \
+       __p->__v = (val);                       \
+} while(0)
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+static inline uint64_t get_le64(const void *ptr)
+{
+       return get_unaligned((const uint64_t *) ptr);
+}
+
+static inline uint64_t get_be64(const void *ptr)
+{
+       return bswap_64(get_unaligned((const uint64_t *) ptr));
+}
+
+static inline uint32_t get_le32(const void *ptr)
+{
+       return get_unaligned((const uint32_t *) ptr);
+}
+
+static inline uint32_t get_be32(const void *ptr)
+{
+       return bswap_32(get_unaligned((const uint32_t *) ptr));
+}
+
+static inline uint16_t get_le16(const void *ptr)
+{
+       return get_unaligned((const uint16_t *) ptr);
+}
+
+static inline uint16_t get_be16(const void *ptr)
+{
+       return bswap_16(get_unaligned((const uint16_t *) ptr));
+}
+
+static inline void put_be16(uint16_t val, void *ptr)
+{
+       put_unaligned(bswap_16(val), (uint16_t *) ptr);
+}
+
+static inline void put_be32(uint32_t val, void *ptr)
+{
+       put_unaligned(bswap_32(val), (uint32_t *) ptr);
+}
+
+static inline void put_le16(uint16_t val, void *ptr)
+{
+       put_unaligned(val, (uint16_t *) ptr);
+}
+
+static inline void put_le32(uint32_t val, void *ptr)
+{
+       put_unaligned(val, (uint32_t *) ptr);
+}
+
+static inline void put_be64(uint64_t val, void *ptr)
+{
+       put_unaligned(bswap_64(val), (uint64_t *) ptr);
+}
+
+static inline void put_le64(uint64_t val, void *ptr)
+{
+       put_unaligned(val, (uint64_t *) ptr);
+}
+#elif __BYTE_ORDER == __BIG_ENDIAN
+static inline uint64_t get_le64(const void *ptr)
+{
+       return bswap_64(get_unaligned((const uint64_t *) ptr));
+}
+
+static inline uint64_t get_be64(const void *ptr)
+{
+       return get_unaligned((const uint64_t *) ptr);
+}
+
+static inline uint32_t get_le32(const void *ptr)
+{
+       return bswap_32(get_unaligned((const uint32_t *) ptr));
+}
+
+static inline uint32_t get_be32(const void *ptr)
+{
+       return get_unaligned((const uint32_t *) ptr);
+}
+
+static inline uint16_t get_le16(const void *ptr)
+{
+       return bswap_16(get_unaligned((const uint16_t *) ptr));
+}
+
+static inline uint16_t get_be16(const void *ptr)
+{
+       return get_unaligned((const uint16_t *) ptr);
+}
+
+static inline void put_be16(uint16_t val, void *ptr)
+{
+       put_unaligned(val, (uint16_t *) ptr);
+}
+
+static inline void put_be32(uint32_t val, void *ptr)
+{
+       put_unaligned(val, (uint32_t *) ptr);
+}
+
+static inline void put_le16(uint16_t val, void *ptr)
+{
+       put_unaligned(bswap_16(val), (uint16_t *) ptr);
+}
+
+static inline void put_le32(uint32_t val, void *ptr)
+{
+       put_unaligned(bswap_32(val), (uint32_t *) ptr);
+}
+
+static inline void put_be64(uint64_t val, void *ptr)
+{
+       put_unaligned(val, (uint64_t *) ptr);
+}
+
+static inline void put_le64(uint64_t val, void *ptr)
+{
+       put_unaligned(bswap_64(val), (uint64_t *) ptr);
+}
+#else
+#error "Unknown byte order"
+#endif
index 7d427be..89776c5 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  WPA supplicant library with GLib integration
  *
- *  Copyright (C) 2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
@@ -30,7 +30,7 @@
 
 #include "dbus.h"
 
-#define TIMEOUT 5000
+#define TIMEOUT 30000
 
 static DBusConnection *connection;
 
index fbada07..dfe77fe 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  WPA supplicant library with GLib integration
  *
- *  Copyright (C) 2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
index 60b4dc3..09fdd3b 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  WPA supplicant library with GLib integration
  *
- *  Copyright (C) 2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
@@ -73,7 +73,10 @@ extern "C" {
 #define G_SUPPLICANT_PAIRWISE_TKIP     (1 << 1)
 #define G_SUPPLICANT_PAIRWISE_CCMP     (1 << 2)
 
-#define G_SUPPLICANT_MAX_FAST_SCAN     4
+#define G_SUPPLICANT_WPS_CONFIGURED     (1 << 0)
+#define G_SUPPLICANT_WPS_PBC            (1 << 1)
+#define G_SUPPLICANT_WPS_PIN            (1 << 2)
+#define G_SUPPLICANT_WPS_REGISTRAR      (1 << 3)
 
 typedef enum {
        G_SUPPLICANT_MODE_UNKNOWN,
@@ -92,6 +95,7 @@ typedef enum {
 
 typedef enum {
        G_SUPPLICANT_STATE_UNKNOWN,
+       G_SUPPLICANT_STATE_DISABLED,
        G_SUPPLICANT_STATE_DISCONNECTED,
        G_SUPPLICANT_STATE_INACTIVE,
        G_SUPPLICANT_STATE_SCANNING,
@@ -129,19 +133,34 @@ struct _GSupplicantSSID {
        const char *phase2_auth;
        dbus_bool_t use_wps;
        const char *pin_wps;
+       const char *bgscan;
+#if defined TIZEN_EXT
+       unsigned char *bssid;
+#endif
 };
 
 typedef struct _GSupplicantSSID GSupplicantSSID;
 
+/*
+ * Max number of SSIDs that can be scanned.
+ * In wpa_s 0.7x the limit is 4.
+ * In wps_s 0.8 or later it is 16.
+ * The value is only used if wpa_supplicant does not return any max limit
+ * for number of scannable SSIDs.
+ */
+#define WPAS_MAX_SCAN_SSIDS 4
+
+struct scan_ssid {
+       unsigned char ssid[32];
+       uint8_t ssid_len;
+};
+
 struct _GSupplicantScanParams {
-       struct scan_ssid {
-               unsigned char ssid[32];
-               uint8_t ssid_len;
-       } ssids[G_SUPPLICANT_MAX_FAST_SCAN];
+       GSList *ssids;
 
        uint8_t num_ssids;
 
-       uint16_t freqs[G_SUPPLICANT_MAX_FAST_SCAN];
+       uint16_t *freqs;
 };
 
 typedef struct _GSupplicantScanParams GSupplicantScanParams;
@@ -220,6 +239,9 @@ const char *g_supplicant_network_get_security(GSupplicantNetwork *network);
 dbus_int16_t g_supplicant_network_get_signal(GSupplicantNetwork *network);
 dbus_uint16_t g_supplicant_network_get_frequency(GSupplicantNetwork *network);
 dbus_bool_t g_supplicant_network_get_wps(GSupplicantNetwork *network);
+dbus_bool_t g_supplicant_network_is_wps_active(GSupplicantNetwork *network);
+dbus_bool_t g_supplicant_network_is_wps_pbc(GSupplicantNetwork *network);
+dbus_bool_t g_supplicant_network_is_wps_advertizing(GSupplicantNetwork *network);
 
 #if defined TIZEN_EXT
 /*
@@ -228,6 +250,10 @@ dbus_bool_t g_supplicant_network_get_wps(GSupplicantNetwork *network);
 const unsigned char *g_supplicant_network_get_bssid(GSupplicantNetwork *network);
 unsigned int g_supplicant_network_get_maxrate(GSupplicantNetwork *network);
 const char *g_supplicant_network_get_enc_mode(GSupplicantNetwork *network);
+unsigned int g_supplicant_network_is_hs20AP(GSupplicantNetwork *network);
+const char *g_supplicant_network_get_eap(GSupplicantNetwork *network);
+const char *g_supplicant_network_get_identity(GSupplicantNetwork *network);
+const char *g_supplicant_network_get_phase2(GSupplicantNetwork *network);
 #endif
 
 struct _GSupplicantCallbacks {
@@ -240,8 +266,14 @@ struct _GSupplicantCallbacks {
        void (*scan_finished) (GSupplicantInterface *interface);
        void (*network_added) (GSupplicantNetwork *network);
        void (*network_removed) (GSupplicantNetwork *network);
+#if defined TIZEN_EXT
+       void (*network_merged) (GSupplicantNetwork *network);
+#endif
        void (*network_changed) (GSupplicantNetwork *network,
                                        const char *property);
+#if defined TIZEN_EXT
+       void (*system_power_off) (void);
+#endif
        void (*debug) (const char *str);
 };
 
@@ -250,6 +282,14 @@ typedef struct _GSupplicantCallbacks GSupplicantCallbacks;
 int g_supplicant_register(const GSupplicantCallbacks *callbacks);
 void g_supplicant_unregister(const GSupplicantCallbacks *callbacks);
 
+static inline
+void g_supplicant_free_scan_params(GSupplicantScanParams *scan_params)
+{
+       g_slist_free_full(scan_params->ssids, g_free);
+       g_free(scan_params->freqs);
+       g_free(scan_params);
+}
+
 #ifdef __cplusplus
 }
 #endif
index 8293c49..a9671c7 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  WPA supplicant library with GLib integration
  *
- *  Copyright (C) 2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
@@ -37,8 +37,6 @@
 #include "dbus.h"
 #include "gsupplicant.h"
 
-#define TIMEOUT 5000
-
 #define IEEE80211_CAP_ESS      0x0001
 #define IEEE80211_CAP_IBSS     0x0002
 #define IEEE80211_CAP_PRIVACY  0x0010
@@ -182,12 +180,18 @@ struct g_supplicant_bss {
        dbus_int16_t signal;
        GSupplicantMode mode;
        GSupplicantSecurity security;
+       dbus_bool_t rsn_selected;
+       unsigned int wpa_keymgmt;
+       unsigned int wpa_pairwise;
+       unsigned int wpa_group;
+       unsigned int rsn_keymgmt;
+       unsigned int rsn_pairwise;
+       unsigned int rsn_group;
        unsigned int keymgmt;
-       unsigned int pairwise;
-       unsigned int group;
        dbus_bool_t privacy;
        dbus_bool_t psk;
        dbus_bool_t ieee8021x;
+       unsigned int wps_capabilities;
 };
 
 struct _GSupplicantNetwork {
@@ -203,8 +207,15 @@ struct _GSupplicantNetwork {
        GSupplicantMode mode;
        GSupplicantSecurity security;
        dbus_bool_t wps;
+       unsigned int wps_capabilities;
        GHashTable *bss_table;
        GHashTable *config_table;
+#if defined TIZEN_EXT
+       unsigned int isHS20AP;
+       char *eap;
+       char *identity;
+       char *phase2;
+#endif
 };
 
 static inline void debug(const char *format, ...)
@@ -280,6 +291,8 @@ static GSupplicantState string2state(const char *state)
 
        if (g_str_equal(state, "unknown") == TRUE)
                return G_SUPPLICANT_STATE_UNKNOWN;
+       else if (g_str_equal(state, "interface_disabled") == TRUE)
+               return G_SUPPLICANT_STATE_DISABLED;
        else if (g_str_equal(state, "disconnected") == TRUE)
                return G_SUPPLICANT_STATE_DISCONNECTED;
        else if (g_str_equal(state, "inactive") == TRUE)
@@ -410,6 +423,19 @@ static void callback_network_removed(GSupplicantNetwork *network)
        callbacks_pointer->network_removed(network);
 }
 
+#if defined TIZEN_EXT
+static void callback_network_merged(GSupplicantNetwork *network)
+{
+       if (callbacks_pointer == NULL)
+               return;
+
+       if (callbacks_pointer->network_merged == NULL)
+               return;
+
+       callbacks_pointer->network_merged(network);
+}
+#endif
+
 static void callback_network_changed(GSupplicantNetwork *network,
                                        const char *property)
 {
@@ -430,11 +456,29 @@ static void remove_interface(gpointer data)
        g_hash_table_destroy(interface->net_mapping);
        g_hash_table_destroy(interface->network_table);
 
+       if (interface->scan_callback != NULL) {
+               SUPPLICANT_DBG("call interface %p callback %p scanning %d",
+                               interface, interface->scan_callback,
+                               interface->scanning);
+
+               interface->scan_callback(-EIO, interface, interface->scan_data);
+                interface->scan_callback = NULL;
+                interface->scan_data = NULL;
+
+               if (interface->scanning == TRUE) {
+                       interface->scanning = FALSE;
+                       callback_scan_finished(interface);
+               }
+       }
+
        callback_interface_removed(interface);
 
        g_free(interface->wps_cred.key);
        g_free(interface->path);
        g_free(interface->network_path);
+#if defined TIZEN_EXT
+       interface->network_path = NULL;
+#endif
        g_free(interface->ifname);
        g_free(interface->driver);
        g_free(interface->bridge);
@@ -454,6 +498,11 @@ static void remove_network(gpointer data)
        g_free(network->path);
        g_free(network->group);
        g_free(network->name);
+#if defined TIZEN_EXT
+       g_free(network->eap);
+       g_free(network->identity);
+       g_free(network->phase2);
+#endif
        g_free(network);
 }
 
@@ -659,6 +708,9 @@ void g_supplicant_interface_set_data(GSupplicantInterface *interface,
                return;
 
        interface->data = data;
+
+       if (!data)
+               interface->scan_callback = NULL;
 }
 
 void *g_supplicant_interface_get_data(GSupplicantInterface *interface)
@@ -708,7 +760,7 @@ const void *g_supplicant_interface_get_wps_ssid(GSupplicantInterface *interface,
        if (ssid_len == NULL)
                return NULL;
 
-       if (interface == NULL || interface->wps_cred.ssid == NULL) {
+       if (interface == NULL || interface->wps_cred.ssid_len == 0) {
                *ssid_len = 0;
                return NULL;
        }
@@ -740,6 +792,9 @@ unsigned int g_supplicant_interface_get_max_scan_ssids(
        if (interface == NULL)
                return 0;
 
+       if (interface->max_scan_ssids == 0)
+               return WPAS_MAX_SCAN_SSIDS;
+
        return interface->max_scan_ssids;
 }
 
@@ -826,7 +881,7 @@ const char *g_supplicant_network_get_security(GSupplicantNetwork *network)
 const void *g_supplicant_network_get_ssid(GSupplicantNetwork *network,
                                                unsigned int *ssid_len)
 {
-       if (network == NULL || network->ssid == NULL) {
+       if (network == NULL || network->ssid_len == 0) {
                *ssid_len = 0;
                return NULL;
        }
@@ -859,6 +914,39 @@ dbus_bool_t g_supplicant_network_get_wps(GSupplicantNetwork *network)
        return network->wps;
 }
 
+dbus_bool_t g_supplicant_network_is_wps_active(GSupplicantNetwork *network)
+{
+       if (network == NULL)
+               return FALSE;
+
+       if (network->wps_capabilities & G_SUPPLICANT_WPS_CONFIGURED)
+               return TRUE;
+
+       return FALSE;
+}
+
+dbus_bool_t g_supplicant_network_is_wps_pbc(GSupplicantNetwork *network)
+{
+       if (network == NULL)
+               return FALSE;
+
+       if (network->wps_capabilities & G_SUPPLICANT_WPS_PBC)
+               return TRUE;
+
+       return FALSE;
+}
+
+dbus_bool_t g_supplicant_network_is_wps_advertizing(GSupplicantNetwork *network)
+{
+       if (network == NULL)
+               return FALSE;
+
+       if (network->wps_capabilities & G_SUPPLICANT_WPS_REGISTRAR)
+               return TRUE;
+
+       return FALSE;
+}
+
 #if defined TIZEN_EXT
 /*
  * Description: Network client requires additional wifi specific info
@@ -884,30 +972,81 @@ const char *g_supplicant_network_get_enc_mode(GSupplicantNetwork *network)
        if (network == NULL || network->best_bss == NULL)
                return NULL;
 
-       if ((network->best_bss->pairwise & G_SUPPLICANT_PAIRWISE_CCMP) &&
-           (network->best_bss->pairwise & G_SUPPLICANT_PAIRWISE_TKIP))
+       unsigned int pairwise;
+
+       if (network->best_bss->rsn_selected)
+               pairwise = network->best_bss->rsn_pairwise;
+       else
+               pairwise = network->best_bss->wpa_pairwise;
+
+       if ((pairwise & G_SUPPLICANT_PAIRWISE_CCMP) &&
+           (pairwise & G_SUPPLICANT_PAIRWISE_TKIP))
                return "mixed";
-       else if (network->best_bss->pairwise & G_SUPPLICANT_PAIRWISE_CCMP)
+       else if (pairwise & G_SUPPLICANT_PAIRWISE_CCMP)
                return "aes";
-       else if (network->best_bss->pairwise & G_SUPPLICANT_PAIRWISE_TKIP)
+       else if (pairwise & G_SUPPLICANT_PAIRWISE_TKIP)
                return "tkip";
        else if (network->best_bss->security == G_SUPPLICANT_SECURITY_WEP)
                return "wep";
 
        return "none";
 }
+
+unsigned int g_supplicant_network_is_hs20AP(GSupplicantNetwork *network)
+{
+       if (network == NULL)
+               return 0;
+
+       return network->isHS20AP;
+}
+
+const char *g_supplicant_network_get_eap(GSupplicantNetwork *network)
+{
+       if (network == NULL || network->eap == NULL)
+               return NULL;
+
+       return network->eap;
+}
+
+const char *g_supplicant_network_get_identity(GSupplicantNetwork *network)
+{
+       if (network == NULL || network->identity == NULL)
+               return NULL;
+
+       return network->identity;
+}
+
+const char *g_supplicant_network_get_phase2(GSupplicantNetwork *network)
+{
+       if (network == NULL || network->phase2 == NULL)
+               return NULL;
+
+       return network->phase2;
+}
 #endif
 
 static void merge_network(GSupplicantNetwork *network)
 {
        GString *str;
        const char *ssid, *mode, *key_mgmt;
+#if defined TIZEN_EXT
+       GSupplicantInterface *interface;
+       const char *isHS20AP;
+       const char *eap, *identity, *phase2;
+#endif
        unsigned int i, ssid_len;
        char *group;
 
        ssid = g_hash_table_lookup(network->config_table, "ssid");
        mode = g_hash_table_lookup(network->config_table, "mode");
        key_mgmt = g_hash_table_lookup(network->config_table, "key_mgmt");
+#if defined TIZEN_EXT
+       isHS20AP = g_hash_table_lookup(network->config_table, "isHS20AP");
+       eap = g_hash_table_lookup(network->config_table, "eap");
+       identity = g_hash_table_lookup(network->config_table, "identity");
+       phase2 = g_hash_table_lookup(network->config_table, "phase2");
+       interface = network->interface;
+#endif
 
        SUPPLICANT_DBG("ssid %s mode %s", ssid, mode);
 
@@ -921,7 +1060,14 @@ static void merge_network(GSupplicantNetwork *network)
                return;
 
        for (i = 0; i < ssid_len; i++)
+#if defined TIZEN_EXT
+       {
+               if (ssid[i] != '"')
+#endif
                g_string_append_printf(str, "%02x", ssid[i]);
+#if defined TIZEN_EXT
+       }
+#endif
 
        if (g_strcmp0(mode, "0") == 0)
                g_string_append_printf(str, "_managed");
@@ -930,11 +1076,41 @@ static void merge_network(GSupplicantNetwork *network)
 
        if (g_strcmp0(key_mgmt, "WPA-PSK") == 0)
                g_string_append_printf(str, "_psk");
+#if defined TIZEN_EXT
+       else if (g_strcmp0(key_mgmt, "WPA-EAP") == 0)
+               g_string_append_printf(str, "_ieee8021x");
+       else
+               g_string_append_printf(str, "_none");
+#endif
 
        group = g_string_free(str, FALSE);
 
        SUPPLICANT_DBG("%s", group);
 
+#if defined TIZEN_EXT
+       if (g_strcmp0(isHS20AP, "1") == 0) {
+               network->isHS20AP = 1;
+               if (network->eap)
+                       g_free(network->eap);
+               network->eap = g_strdup(eap);
+
+               if (network->identity)
+                       g_free(network->identity);
+               network->identity = g_strdup(identity);
+
+               if (network->phase2)
+                       g_free(network->phase2);
+               network->phase2 = g_strdup(phase2);
+       } else
+               network->isHS20AP = 0;
+
+       if (interface)
+               interface->network_path = g_strdup(network->path);
+
+       network->group = g_strdup(group);
+       callback_network_merged(network);
+#endif
+
        g_free(group);
 
        g_hash_table_destroy(network->config_table);
@@ -1034,25 +1210,43 @@ static void interface_network_removed(DBusMessageIter *iter, void *user_data)
 
 static char *create_name(unsigned char *ssid, int ssid_len)
 {
-       char *name;
-       int i;
+       GString *string;
+       const gchar *remainder, *invalid;
+       int valid_bytes, remaining_bytes;
 
        if (ssid_len < 1 || ssid[0] == '\0')
-               name = NULL;
-       else
-               name = g_try_malloc0(ssid_len + 1);
-
-       if (name == NULL)
                return g_strdup("");
 
-       for (i = 0; i < ssid_len; i++) {
-               if (g_ascii_isprint(ssid[i]))
-                       name[i] = ssid[i];
-               else
-                       name[i] = ' ';
+       string = NULL;
+       remainder = (const gchar *)ssid;
+       remaining_bytes = ssid_len;
+
+       while (remaining_bytes != 0) {
+               if (g_utf8_validate(remainder, remaining_bytes,
+                                       &invalid) == TRUE) {
+                       break;
+               }
+
+               valid_bytes = invalid - remainder;
+
+               if (string == NULL)
+                       string = g_string_sized_new(remaining_bytes);
+
+               g_string_append_len(string, remainder, valid_bytes);
+
+               /* append U+FFFD REPLACEMENT CHARACTER */
+               g_string_append(string, "\357\277\275");
+
+               remaining_bytes -= valid_bytes + 1;
+               remainder = invalid + 1;
        }
 
-       return name;
+       if (string == NULL)
+               return g_strndup((const gchar *)ssid, ssid_len + 1);
+
+       g_string_append(string, remainder);
+
+       return g_string_free(string, FALSE);
 }
 
 static char *create_group(struct g_supplicant_bss *bss)
@@ -1082,19 +1276,23 @@ static char *create_group(struct g_supplicant_bss *bss)
        return g_string_free(str, FALSE);
 }
 
-static void add_bss_to_network(struct g_supplicant_bss *bss)
+static void add_or_replace_bss_to_network(struct g_supplicant_bss *bss)
 {
        GSupplicantInterface *interface = bss->interface;
        GSupplicantNetwork *network;
        char *group;
 
        group = create_group(bss);
+       SUPPLICANT_DBG("New group created: %s", group);
+
        if (group == NULL)
                return;
 
        network = g_hash_table_lookup(interface->network_table, group);
        if (network != NULL) {
                g_free(group);
+               SUPPLICANT_DBG("Network %s already exist", network->name);
+
                goto done;
        }
 
@@ -1117,9 +1315,13 @@ static void add_bss_to_network(struct g_supplicant_bss *bss)
        network->frequency = bss->frequency;
        network->best_bss = bss;
 
-       network->wps = FALSE;
-       if ((bss->keymgmt & G_SUPPLICANT_KEYMGMT_WPS) != 0)
-               network->wps = TRUE;
+#if defined TIZEN_EXT
+       network->eap = NULL;
+       network->identity = NULL;
+       network->phase2 = NULL;
+#endif
+
+       SUPPLICANT_DBG("New network %s created", network->name);
 
        network->bss_table = g_hash_table_new_full(g_str_hash, g_str_equal,
                                                        NULL, remove_bss);
@@ -1133,6 +1335,12 @@ static void add_bss_to_network(struct g_supplicant_bss *bss)
        callback_network_added(network);
 
 done:
+       /* We update network's WPS properties if only bss provides WPS. */
+       if ((bss->keymgmt & G_SUPPLICANT_KEYMGMT_WPS) != 0) {
+               network->wps = TRUE;
+               network->wps_capabilities |= bss->wps_capabilities;
+       }
+
        if (bss->signal > network->signal) {
                network->signal = bss->signal;
                network->best_bss = bss;
@@ -1160,7 +1368,7 @@ static void bss_rates(DBusMessageIter *iter, void *user_data)
 
 static void bss_keymgmt(DBusMessageIter *iter, void *user_data)
 {
-       struct g_supplicant_bss *bss = user_data;
+       unsigned int *keymgmt = user_data;
        const char *str = NULL;
        int i;
 
@@ -1170,14 +1378,15 @@ static void bss_keymgmt(DBusMessageIter *iter, void *user_data)
 
        for (i = 0; keymgmt_map[i].str != NULL; i++)
                if (strcmp(str, keymgmt_map[i].str) == 0) {
-                       bss->keymgmt |= keymgmt_map[i].val;
+                       SUPPLICANT_DBG("Keymgmt: %s", str);
+                       *keymgmt |= keymgmt_map[i].val;
                        break;
                }
 }
 
 static void bss_group(DBusMessageIter *iter, void *user_data)
 {
-       struct g_supplicant_bss *bss = user_data;
+       unsigned int *group = user_data;
        const char *str = NULL;
        int i;
 
@@ -1187,14 +1396,15 @@ static void bss_group(DBusMessageIter *iter, void *user_data)
 
        for (i = 0; group_map[i].str != NULL; i++)
                if (strcmp(str, group_map[i].str) == 0) {
-                       bss->group |= group_map[i].val;
+                       SUPPLICANT_DBG("Group: %s", str);
+                       *group |= group_map[i].val;
                        break;
                }
 }
 
 static void bss_pairwise(DBusMessageIter *iter, void *user_data)
 {
-       struct g_supplicant_bss *bss = user_data;
+       unsigned int *pairwise = user_data;
        const char *str = NULL;
        int i;
 
@@ -1204,7 +1414,8 @@ static void bss_pairwise(DBusMessageIter *iter, void *user_data)
 
        for (i = 0; pairwise_map[i].str != NULL; i++)
                if (strcmp(str, pairwise_map[i].str) == 0) {
-                       bss->pairwise |= pairwise_map[i].val;
+                       SUPPLICANT_DBG("Pairwise: %s", str);
+                       *pairwise |= pairwise_map[i].val;
                        break;
                }
 }
@@ -1212,12 +1423,33 @@ static void bss_pairwise(DBusMessageIter *iter, void *user_data)
 static void bss_wpa(const char *key, DBusMessageIter *iter,
                        void *user_data)
 {
-       if (g_strcmp0(key, "KeyMgmt") == 0)
-               supplicant_dbus_array_foreach(iter, bss_keymgmt, user_data);
-       else if (g_strcmp0(key, "Group") == 0)
-               supplicant_dbus_array_foreach(iter, bss_group, user_data);
-       else if (g_strcmp0(key, "Pairwise") == 0)
-               supplicant_dbus_array_foreach(iter, bss_pairwise, user_data);
+       struct g_supplicant_bss *bss = user_data;
+       unsigned int value = 0;
+
+       SUPPLICANT_DBG("Key: %s", key);
+
+       if (g_strcmp0(key, "KeyMgmt") == 0) {
+               supplicant_dbus_array_foreach(iter, bss_keymgmt, &value);
+
+               if (bss->rsn_selected == TRUE)
+                       bss->rsn_keymgmt = value;
+               else
+                       bss->wpa_keymgmt = value;
+       } else if (g_strcmp0(key, "Group") == 0) {
+               supplicant_dbus_array_foreach(iter, bss_group, &value);
+
+               if (bss->rsn_selected == TRUE)
+                       bss->rsn_group = value;
+               else
+                       bss->wpa_group = value;
+       } else if (g_strcmp0(key, "Pairwise") == 0) {
+               supplicant_dbus_array_foreach(iter, bss_pairwise, &value);
+
+               if (bss->rsn_selected == TRUE)
+                       bss->rsn_pairwise = value;
+               else
+                       bss->wpa_pairwise = value;
+       }
 }
 
 static unsigned int get_tlv(unsigned char *ie, unsigned int ie_size,
@@ -1268,13 +1500,19 @@ static void bss_process_ies(DBusMessageIter *iter, void *user_data)
        const unsigned char WPS_OUI[] = { 0x00, 0x50, 0xf2, 0x04 };
        unsigned char *ie, *ie_end;
        DBusMessageIter array;
+       unsigned int value;
        int ie_len;
 
 #define WMM_WPA1_WPS_INFO 221
 #define WPS_INFO_MIN_LEN  6
 #define WPS_VERSION_TLV   0x104A
 #define WPS_STATE_TLV     0x1044
+#define WPS_METHODS_TLV   0x1012
+#define WPS_REGISTRAR_TLV 0x1041
 #define WPS_VERSION       0x10
+#define WPS_PBC           0x04
+#define WPS_PIN           0x00
+#define WPS_CONFIGURED    0x02
 
        dbus_message_iter_recurse(iter, &array);
        dbus_message_iter_get_fixed_array(&array, &ie, &ie_len);
@@ -1282,25 +1520,69 @@ static void bss_process_ies(DBusMessageIter *iter, void *user_data)
        if (ie == NULL || ie_len < 2)
                return;
 
+       bss->wps_capabilities = 0;
+       bss->keymgmt = 0;
+
        for (ie_end = ie + ie_len; ie < ie_end && ie + ie[1] + 1 <= ie_end;
                                                        ie += ie[1] + 2) {
-
                if (ie[0] != WMM_WPA1_WPS_INFO || ie[1] < WPS_INFO_MIN_LEN ||
                        memcmp(ie+2, WPS_OUI, sizeof(WPS_OUI)) != 0)
                        continue;
 
                SUPPLICANT_DBG("IE: match WPS_OUI");
 
-               if (get_tlv(&ie[6], ie[1],
-                               WPS_VERSION_TLV) == WPS_VERSION &&
-                       get_tlv(&ie[6], ie[1],
-                               WPS_STATE_TLV) != 0)
+               value = get_tlv(&ie[6], ie[1], WPS_STATE_TLV);
+               if (get_tlv(&ie[6], ie[1], WPS_VERSION_TLV) == WPS_VERSION &&
+                                                               value != 0) {
                        bss->keymgmt |= G_SUPPLICANT_KEYMGMT_WPS;
+
+                       if (value == WPS_CONFIGURED)
+                               bss->wps_capabilities |=
+                                       G_SUPPLICANT_WPS_CONFIGURED;
+               }
+
+               value = get_tlv(&ie[6], ie[1], WPS_METHODS_TLV);
+               if (value != 0) {
+                       if (GUINT16_FROM_BE(value) == WPS_PBC)
+                               bss->wps_capabilities |= G_SUPPLICANT_WPS_PBC;
+                       if (GUINT16_FROM_BE(value) == WPS_PIN)
+                               bss->wps_capabilities |= G_SUPPLICANT_WPS_PIN;
+               } else
+                       bss->wps_capabilities |=
+                               G_SUPPLICANT_WPS_PBC | G_SUPPLICANT_WPS_PIN;
+
+               /* If the AP sends this it means it's advertizing
+                * as a registrar and the WPS process is launched
+                * on its side */
+               if (get_tlv(&ie[6], ie[1], WPS_REGISTRAR_TLV) != 0)
+                       bss->wps_capabilities |= G_SUPPLICANT_WPS_REGISTRAR;
+
+               SUPPLICANT_DBG("WPS Methods 0x%x", bss->wps_capabilities);
        }
 }
 
 static void bss_compute_security(struct g_supplicant_bss *bss)
 {
+       /*
+        * Combining RSN and WPA keymgmt
+        * We combine it since parsing IEs might have set something for WPS. */
+       bss->keymgmt |= bss->rsn_keymgmt | bss->wpa_keymgmt;
+
+       bss->ieee8021x = FALSE;
+       bss->psk = FALSE;
+
+       if (bss->keymgmt &
+                       (G_SUPPLICANT_KEYMGMT_WPA_EAP |
+                               G_SUPPLICANT_KEYMGMT_WPA_FT_EAP |
+                               G_SUPPLICANT_KEYMGMT_WPA_EAP_256))
+               bss->ieee8021x = TRUE;
+
+       if (bss->keymgmt &
+                       (G_SUPPLICANT_KEYMGMT_WPA_PSK |
+                               G_SUPPLICANT_KEYMGMT_WPA_FT_PSK |
+                               G_SUPPLICANT_KEYMGMT_WPA_PSK_256))
+               bss->psk = TRUE;
+
        if (bss->ieee8021x == TRUE)
                bss->security = G_SUPPLICANT_SECURITY_IEEE8021X;
        else if (bss->psk == TRUE)
@@ -1324,22 +1606,6 @@ static void bss_property(const char *key, DBusMessageIter *iter,
        if (key == NULL)
                return;
 
-       if (g_strcmp0(key, "WPA") == 0) {
-
-               /* WPA/RSN property can be updated:
-                * - When WPS signal arrived at first with signal_bss_changed,
-                * WPA/RSN related properties should be cleared.
-                * - Consecutive WPA and RSN signals can be update properties
-                * correctly.
-                * - When bss added, it's not affect anything. */
-
-               bss->keymgmt = 0;
-               bss->group = 0;
-               bss->pairwise = 0;
-               bss->ieee8021x = FALSE;
-               bss->psk = FALSE;
-       }
-
        if (g_strcmp0(key, "BSSID") == 0) {
                DBusMessageIter array;
                unsigned char *addr;
@@ -1410,21 +1676,14 @@ static void bss_property(const char *key, DBusMessageIter *iter,
 
                dbus_message_iter_get_basic(iter, &privacy);
                bss->privacy = privacy;
-       } else if ((g_strcmp0(key, "RSN") == 0) ||
-                       (g_strcmp0(key, "WPA") == 0)) {
-               supplicant_dbus_property_foreach(iter, bss_wpa, bss);
+       } else if (g_strcmp0(key, "RSN") == 0) {
+               bss->rsn_selected = TRUE;
 
-               if (bss->keymgmt &
-                       (G_SUPPLICANT_KEYMGMT_WPA_EAP |
-                               G_SUPPLICANT_KEYMGMT_WPA_FT_EAP |
-                               G_SUPPLICANT_KEYMGMT_WPA_EAP_256))
-                       bss->ieee8021x = TRUE;
+               supplicant_dbus_property_foreach(iter, bss_wpa, bss);
+       } else if (g_strcmp0(key, "WPA") == 0) {
+               bss->rsn_selected = FALSE;
 
-               if (bss->keymgmt &
-                       (G_SUPPLICANT_KEYMGMT_WPA_PSK |
-                               G_SUPPLICANT_KEYMGMT_WPA_FT_PSK |
-                               G_SUPPLICANT_KEYMGMT_WPA_PSK_256))
-                       bss->psk = TRUE;
+               supplicant_dbus_property_foreach(iter, bss_wpa, bss);
        } else if (g_strcmp0(key, "IEs") == 0)
                bss_process_ies(iter, bss);
        else
@@ -1487,7 +1746,7 @@ static void interface_bss_added_with_keys(DBusMessageIter *iter,
        supplicant_dbus_property_foreach(iter, bss_property, bss);
 
        bss_compute_security(bss);
-       add_bss_to_network(bss);
+       add_or_replace_bss_to_network(bss);
 }
 
 static void interface_bss_added_without_keys(DBusMessageIter *iter,
@@ -1506,7 +1765,7 @@ static void interface_bss_added_without_keys(DBusMessageIter *iter,
                                                        bss_property, bss);
 
        bss_compute_security(bss);
-       add_bss_to_network(bss);
+       add_or_replace_bss_to_network(bss);
 }
 
 static void update_signal(gpointer key, gpointer value,
@@ -1537,6 +1796,9 @@ static void interface_bss_removed(DBusMessageIter *iter, void *user_data)
        GSupplicantInterface *interface = user_data;
        GSupplicantNetwork *network;
        const char *path = NULL;
+#if defined TIZEN_EXT
+       struct g_supplicant_bss *bss;
+#endif
 
        dbus_message_iter_get_basic(iter, &path);
        if (path == NULL)
@@ -1549,7 +1811,17 @@ static void interface_bss_removed(DBusMessageIter *iter, void *user_data)
        g_hash_table_remove(bss_mapping, path);
 
        g_hash_table_remove(interface->bss_mapping, path);
+#if defined TIZEN_EXT
+       bss = g_hash_table_lookup(network->bss_table, path);
+       if (bss != NULL) {
+               if (bss == network->best_bss)
+                       network->best_bss = NULL;
+
+               g_hash_table_remove(network->bss_table, path);
+       }
+#else
        g_hash_table_remove(network->bss_table, path);
+#endif
 
        update_network_signal(network);
 
@@ -1583,7 +1855,6 @@ static void interface_property(const char *key, DBusMessageIter *iter,
                debug_strvalmap("Mode capability", mode_capa_map,
                                                interface->mode_capa);
 
-               interface->ready = TRUE;
                callback_interface_added(interface);
                return;
        }
@@ -1600,6 +1871,10 @@ static void interface_property(const char *key, DBusMessageIter *iter,
                                interface->state = string2state(str);
                                callback_interface_state(interface);
                        }
+               if (interface->state == G_SUPPLICANT_STATE_DISABLED)
+                       interface->ready = FALSE;
+               else
+                       interface->ready = TRUE;
 
                SUPPLICANT_DBG("state %s (%d)", str, interface->state);
        } else if (g_strcmp0(key, "Scanning") == 0) {
@@ -1687,16 +1962,29 @@ static void scan_bss_data(const char *key, DBusMessageIter *iter,
                                void *user_data)
 {
        GSupplicantInterface *interface = user_data;
+#if defined TIZEN_EXT
+       GSupplicantInterfaceCallback scan_callback;
+#endif
 
        if (iter)
                supplicant_dbus_array_foreach(iter, scan_network_update,
                                                interface);
 
+#if defined TIZEN_EXT
+       scan_callback = interface->scan_callback;
+#endif
+
        if (interface->scan_callback != NULL)
                interface->scan_callback(0, interface, interface->scan_data);
 
+#if defined TIZEN_EXT
+       if (interface->scan_callback == scan_callback) {
+#endif
        interface->scan_callback = NULL;
        interface->scan_data = NULL;
+#if defined TIZEN_EXT
+       }
+#endif
 }
 
 static GSupplicantInterface *interface_alloc(const char *path)
@@ -1915,6 +2203,11 @@ static void signal_scan_done(const char *path, DBusMessageIter *iter)
 
        dbus_message_iter_get_basic(iter, &success);
 
+       if (interface->scanning) {
+               callback_scan_finished(interface);
+               interface->scanning = FALSE;
+       }
+
        /*
         * If scan is unsuccessful return -EIO else get the scanned BSSs
         * and update the network details accordingly
@@ -1990,6 +2283,7 @@ static void signal_bss_changed(const char *path, DBusMessageIter *iter)
 {
        GSupplicantInterface *interface;
        GSupplicantNetwork *network;
+       GSupplicantSecurity old_security;
        struct g_supplicant_bss *bss;
 
        SUPPLICANT_DBG("");
@@ -2008,9 +2302,10 @@ static void signal_bss_changed(const char *path, DBusMessageIter *iter)
 
        supplicant_dbus_property_foreach(iter, bss_property, bss);
 
+       old_security = network->security;
        bss_compute_security(bss);
 
-       if (bss->security != network->security) {
+       if (old_security != bss->security) {
                struct g_supplicant_bss *new_bss;
 
                SUPPLICANT_DBG("New network security for %s", bss->ssid);
@@ -2035,7 +2330,7 @@ static void signal_bss_changed(const char *path, DBusMessageIter *iter)
 
                g_hash_table_remove(interface->network_table, network->group);
 
-               add_bss_to_network(new_bss);
+               add_or_replace_bss_to_network(new_bss);
 
                return;
        }
@@ -2074,20 +2369,21 @@ static void wps_credentials(const char *key, DBusMessageIter *iter,
 
        if (g_strcmp0(key, "Key") == 0) {
                DBusMessageIter array;
-               unsigned char *key;
+               unsigned char *key_val;
                int key_len;
 
                dbus_message_iter_recurse(iter, &array);
-               dbus_message_iter_get_fixed_array(&array, &key, &key_len);
+               dbus_message_iter_get_fixed_array(&array, &key_val, &key_len);
 
                g_free(interface->wps_cred.key);
                interface->wps_cred.key = g_try_malloc0(
-                                               sizeof(char) * key_len+1);
+                                               sizeof(char) * key_len + 1);
 
                if (interface->wps_cred.key == NULL)
                        return;
 
-               memcpy(interface->wps_cred.key, key, sizeof(char) * key_len);
+               memcpy(interface->wps_cred.key, key_val,
+                                               sizeof(char) * key_len);
 
                SUPPLICANT_DBG("WPS key present");
        } else if (g_strcmp0(key, "SSID") == 0) {
@@ -2162,6 +2458,29 @@ static void signal_wps_event(const char *path, DBusMessageIter *iter)
        supplicant_dbus_property_foreach(iter, wps_event_args, interface);
 }
 
+#if defined TIZEN_EXT
+static void signal_power_off(const char *path, DBusMessageIter *iter)
+{
+       int poweroff_state = 0;
+
+       dbus_message_iter_get_basic(iter, &poweroff_state);
+
+       SUPPLICANT_DBG("poweroff_state(%d)", poweroff_state);
+
+       /* POWER_OFF_DIRECT 2 && POWER_OFF_RESTART 3 */
+       if (poweroff_state != 2 && poweroff_state != 3)
+               return;
+
+       if (callbacks_pointer == NULL)
+               return;
+
+       if (callbacks_pointer->system_power_off == NULL)
+               return;
+
+       callbacks_pointer->system_power_off();
+}
+#endif
+
 static struct {
        const char *interface;
        const char *member;
@@ -2185,6 +2504,9 @@ static struct {
 
        { SUPPLICANT_INTERFACE ".Interface.WPS", "Credentials", signal_wps_credentials },
        { SUPPLICANT_INTERFACE ".Interface.WPS", "Event",       signal_wps_event       },
+#if defined TIZEN_EXT
+       { "org.tizen.system.deviced.PowerOff", "ChangeState", signal_power_off },
+#endif
 
        { }
 };
@@ -2337,7 +2659,7 @@ static void interface_create_result(const char *error,
        SUPPLICANT_DBG("");
 
        if (error != NULL) {
-               g_critical("error %s", error);
+               g_warning("error %s", error);
                err = -EIO;
                goto done;
        }
@@ -2410,7 +2732,6 @@ static void interface_get_result(const char *error,
 
        if (error != NULL) {
                SUPPLICANT_DBG("Interface not created yet");
-               err = -EIO;
                goto create;
        }
 
@@ -2563,23 +2884,54 @@ int g_supplicant_interface_remove(GSupplicantInterface *interface,
                                                interface_remove_result, data);
 }
 
+#if defined TIZEN_EXT
+gboolean find_valid_interface(gpointer key, gpointer value,
+               gpointer user_data)
+{
+       GSupplicantInterface *interface = value;
+       GSupplicantInterface *interface_check = user_data;
+
+       if (interface == interface_check)
+               return TRUE;
+
+       return FALSE;
+}
+#endif
+
 static void interface_scan_result(const char *error,
                                DBusMessageIter *iter, void *user_data)
 {
        struct interface_scan_data *data = user_data;
+       int err = 0;
 
        if (error != NULL) {
                SUPPLICANT_DBG("error %s", error);
+               err = -EIO;
+       }
 
+#if defined TIZEN_EXT
+       GSupplicantInterface *temp_interface = NULL;
+
+       temp_interface = g_hash_table_find(interface_table, find_valid_interface,
+                               data->interface);
+
+       if (temp_interface == NULL || data->interface->ready == FALSE)
+#else
+       /* A non ready interface cannot send/receive anything */
+       if (data->interface->ready == FALSE)
+#endif
+               err = -ENOLINK;
+
+       if (err != 0) {
                if (data->callback != NULL)
-                       data->callback(-EIO, data->interface, data->user_data);
+                       data->callback(err, data->interface, data->user_data);
        } else {
                data->interface->scan_callback = data->callback;
                data->interface->scan_data = data->user_data;
        }
 
-       if (data != NULL && data->scan_params != NULL)
-               g_free(data->scan_params);
+       if (data->scan_params != NULL)
+               g_supplicant_free_scan_params(data->scan_params);
 
        dbus_free(data);
 }
@@ -2604,7 +2956,7 @@ static void add_scan_frequencies(DBusMessageIter *iter,
        unsigned int freq;
        int i;
 
-       for (i = 0; i < G_SUPPLICANT_MAX_FAST_SCAN; i++) {
+       for (i = 0; i < scan_data->num_ssids; i++) {
                freq = scan_data->freqs[i];
                if (!freq)
                        break;
@@ -2629,11 +2981,13 @@ static void append_ssid(DBusMessageIter *iter,
 static void append_ssids(DBusMessageIter *iter, void *user_data)
 {
        GSupplicantScanParams *scan_data = user_data;
-       int i;
+       GSList *list;
 
-       for (i = 0; i < scan_data->num_ssids; i++)
-               append_ssid(iter, scan_data->ssids[i].ssid,
-                                       scan_data->ssids[i].ssid_len);
+       for (list = scan_data->ssids; list; list = list->next) {
+               struct scan_ssid *scan_ssid = list->data;
+
+               append_ssid(iter, scan_ssid->ssid, scan_ssid->ssid_len);
+       }
 }
 
 static void supplicant_add_scan_frequency(DBusMessageIter *dict,
@@ -2644,7 +2998,7 @@ static void supplicant_add_scan_frequency(DBusMessageIter *dict,
        DBusMessageIter entry, value, array;
        const char *key = "Channels";
 
-       if (scan_params->freqs[0] != 0) {
+       if (scan_params->freqs && scan_params->freqs[0] != 0) {
                dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
                                                NULL, &entry);
 
@@ -2727,6 +3081,7 @@ int g_supplicant_interface_scan(GSupplicantInterface *interface,
        case G_SUPPLICANT_STATE_GROUP_HANDSHAKE:
                return -EBUSY;
        case G_SUPPLICANT_STATE_UNKNOWN:
+       case G_SUPPLICANT_STATE_DISABLED:
        case G_SUPPLICANT_STATE_DISCONNECTED:
        case G_SUPPLICANT_STATE_INACTIVE:
        case G_SUPPLICANT_STATE_SCANNING:
@@ -2739,8 +3094,13 @@ int g_supplicant_interface_scan(GSupplicantInterface *interface,
                return -ENOMEM;
 
        data->interface = interface;
+#if defined TIZEN_EXT
+       data->interface->scan_callback = data->callback = callback;
+       data->interface->scan_data = data->user_data = user_data;
+#else
        data->callback = callback;
        data->user_data = user_data;
+#endif
        data->scan_params = scan_data;
 
        ret = supplicant_dbus_method_call(interface->path,
@@ -2758,10 +3118,15 @@ static int parse_supplicant_error(DBusMessageIter *iter)
        int err = -ECANCELED;
        char *key;
 
+       /* If the given passphrase is malformed wpa_s returns
+        * "invalid message format" but this error should be interpreted as
+        * invalid-key.
+        */
        while (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_STRING) {
                dbus_message_iter_get_basic(iter, &key);
-               if (strncmp(key, "psk", 4) == 0 ||
-                       strncmp(key, "wep_key", 7) == 0) {
+               if (strncmp(key, "psk", 3) == 0 ||
+                               strncmp(key, "wep_key", 7) == 0 ||
+                               strcmp(key, "invalid message format") == 0) {
                        err = -ENOKEY;
                        break;
                }
@@ -2835,8 +3200,20 @@ error:
        if (data->callback != NULL)
                data->callback(err, data->interface, data->user_data);
 
+#if defined TIZEN_EXT
+       GSupplicantInterface *temp_interface = NULL;
+
+       temp_interface = g_hash_table_find(interface_table, find_valid_interface,
+                               interface);
+
+       if (temp_interface != NULL) {
+               g_free(interface->network_path);
+               interface->network_path = NULL;
+       }
+#else
        g_free(interface->network_path);
        interface->network_path = NULL;
+#endif
        g_free(data->ssid);
        g_free(data);
 }
@@ -2913,18 +3290,49 @@ static dbus_bool_t is_psk_raw_key(const char *psk)
        return TRUE;
 }
 
+static unsigned char hexchar2bin(char c)
+{
+       if ((c >= '0') && (c <= '9'))
+               return c - '0';
+       else if ((c >= 'A') && (c <= 'F'))
+               return c - 'A' + 10;
+       else if ((c >= 'a') && (c <= 'f'))
+               return c - 'a' + 10;
+       else
+               return c;
+}
+
+static void hexstring2bin(const char *string, unsigned char *data, size_t data_len)
+{
+       size_t i;
+
+       for (i = 0; i < data_len; i++)
+               data[i] = (hexchar2bin(string[i * 2 + 0]) << 4 |
+                          hexchar2bin(string[i * 2 + 1]) << 0);
+}
+
 static void add_network_security_psk(DBusMessageIter *dict,
                                        GSupplicantSSID *ssid)
 {
        if (ssid->passphrase && strlen(ssid->passphrase) > 0) {
+               const char *key = "psk";
 
-               if (is_psk_raw_key(ssid->passphrase) == TRUE)
-                       supplicant_dbus_property_append_fixed_array(dict,
-                                                       "psk", DBUS_TYPE_BYTE,
-                                                       &ssid->passphrase, 64);
-               else
-                       supplicant_dbus_dict_append_basic(dict, "psk",
-                                                       DBUS_TYPE_STRING,
+               if (is_psk_raw_key(ssid->passphrase) == TRUE) {
+                       unsigned char data[32];
+                       unsigned char *datap = data;
+
+                       /* The above pointer alias is required by D-Bus because
+                        * with D-Bus and GCC, non-heap-allocated arrays cannot
+                        * be passed directly by their base pointer. */
+
+                       hexstring2bin(ssid->passphrase, datap, sizeof(data));
+
+                       supplicant_dbus_dict_append_fixed_array(dict,
+                                                       key, DBUS_TYPE_BYTE,
+                                                       &datap, sizeof(data));
+               } else
+                       supplicant_dbus_dict_append_basic(dict,
+                                                       key, DBUS_TYPE_STRING,
                                                        &ssid->passphrase);
        }
 }
@@ -2946,8 +3354,10 @@ static void add_network_security_tls(DBusMessageIter *dict,
        if (ssid->private_key_path == NULL)
                return;
 
+#if !defined TIZEN_EXT
        if (ssid->private_key_passphrase == NULL)
                return;
+#endif
 
        if (ssid->ca_cert_path)
                supplicant_dbus_dict_append_basic(dict, "ca_cert",
@@ -2956,9 +3366,11 @@ static void add_network_security_tls(DBusMessageIter *dict,
        supplicant_dbus_dict_append_basic(dict, "private_key",
                                                DBUS_TYPE_STRING,
                                                &ssid->private_key_path);
+#if !defined TIZEN_EXT
        supplicant_dbus_dict_append_basic(dict, "private_key_passwd",
                                                DBUS_TYPE_STRING,
                                                &ssid->private_key_passphrase);
+#endif
        supplicant_dbus_dict_append_basic(dict, "client_cert",
                                                DBUS_TYPE_STRING,
                                                &ssid->client_cert_path);
@@ -2983,15 +3395,19 @@ static void add_network_security_peap(DBusMessageIter *dict,
        if (ssid->passphrase == NULL)
                return;
 
+#if !defined TIZEN_EXT
        if (ssid->phase2_auth == NULL)
                return;
+#endif
 
        if (ssid->client_cert_path) {
                if (ssid->private_key_path == NULL)
                        return;
 
+#if !defined TIZEN_EXT
                if (ssid->private_key_passphrase == NULL)
                        return;
+#endif
 
                supplicant_dbus_dict_append_basic(dict, "client_cert",
                                                DBUS_TYPE_STRING,
@@ -3001,9 +3417,11 @@ static void add_network_security_peap(DBusMessageIter *dict,
                                                DBUS_TYPE_STRING,
                                                &ssid->private_key_path);
 
+#if !defined TIZEN_EXT
                supplicant_dbus_dict_append_basic(dict, "private_key_passwd",
                                                DBUS_TYPE_STRING,
                                                &ssid->private_key_passphrase);
+#endif
 
        }
 
@@ -3064,14 +3482,10 @@ static void add_network_security_eap(DBusMessageIter *dict,
                                                &eap_value);
 #if defined TIZEN_EXT
        if (ssid->identity != NULL)
-               supplicant_dbus_dict_append_basic(dict, "identity",
-                                                       DBUS_TYPE_STRING,
-                                                       &ssid->identity);
-#else
+#endif
        supplicant_dbus_dict_append_basic(dict, "identity",
                                                DBUS_TYPE_STRING,
                                                &ssid->identity);
-#endif
 
        g_free(eap_value);
 }
@@ -3168,6 +3582,17 @@ static void add_network_security_proto(DBusMessageIter *dict,
        g_free(proto);
 }
 
+#if defined TIZEN_EXT
+static void add_network_security_none(DBusMessageIter *dict,
+                                       GSupplicantSSID *ssid)
+{
+       const char *auth_alg = "OPEN";
+
+       supplicant_dbus_dict_append_basic(dict, "auth_alg",
+                                       DBUS_TYPE_STRING, &auth_alg);
+}
+#endif
+
 static void add_network_security(DBusMessageIter *dict, GSupplicantSSID *ssid)
 {
        char *key_mgmt;
@@ -3175,6 +3600,12 @@ static void add_network_security(DBusMessageIter *dict, GSupplicantSSID *ssid)
        switch (ssid->security) {
        case G_SUPPLICANT_SECURITY_UNKNOWN:
        case G_SUPPLICANT_SECURITY_NONE:
+#if defined TIZEN_EXT
+               key_mgmt = "NONE";
+               add_network_security_none(dict, ssid);
+               add_network_security_ciphers(dict, ssid);
+               break;
+#endif
        case G_SUPPLICANT_SECURITY_WEP:
                key_mgmt = "NONE";
                add_network_security_wep(dict, ssid);
@@ -3235,6 +3666,10 @@ static void interface_add_network_params(DBusMessageIter *iter, void *user_data)
                supplicant_dbus_dict_append_basic(&dict, "frequency",
                                         DBUS_TYPE_UINT32, &ssid->freq);
 
+       if (ssid->bgscan != NULL)
+               supplicant_dbus_dict_append_basic(&dict, "bgscan",
+                                       DBUS_TYPE_STRING, &ssid->bgscan);
+
        add_network_mode(&dict, ssid);
 
        add_network_security(&dict, ssid);
@@ -3278,6 +3713,10 @@ static void interface_add_wps_params(DBusMessageIter *iter, void *user_data)
                type = "pin";
                supplicant_dbus_dict_append_basic(&dict, "Pin",
                                        DBUS_TYPE_STRING, &ssid->pin_wps);
+#if defined TIZEN_EXT
+               supplicant_dbus_dict_append_fixed_array(&dict, "Bssid",
+                                               DBUS_TYPE_BYTE, &ssid->bssid, 6);
+#endif
        }
 
        supplicant_dbus_dict_append_basic(&dict, "Type",
@@ -3369,8 +3808,12 @@ static void network_remove_result(const char *error,
 
        SUPPLICANT_DBG("");
 
-       if (error != NULL)
+       if (error != NULL) {
                result = -EIO;
+               if (g_strcmp0("org.freedesktop.DBus.Error.UnknownMethod",
+                                               error) == 0)
+                       result = -ECONNABORTED;
+       }
 
        if (data->callback != NULL)
                data->callback(result, data->interface, data->user_data);
@@ -3394,6 +3837,14 @@ static int network_remove(struct interface_data *data)
 
        SUPPLICANT_DBG("");
 
+#if defined TIZEN_EXT
+       GSupplicantInterface *temp_interface = NULL;
+
+       temp_interface = g_hash_table_lookup(interface_table, interface->path);
+       if (temp_interface == NULL)
+               return -EINVAL;
+#endif
+
        return supplicant_dbus_method_call(interface->path,
                        SUPPLICANT_INTERFACE ".Interface", "RemoveNetwork",
                        network_remove_params, network_remove_result, data);
@@ -3403,26 +3854,37 @@ static void interface_disconnect_result(const char *error,
                                DBusMessageIter *iter, void *user_data)
 {
        struct interface_data *data = user_data;
+       int result = 0;
 
        SUPPLICANT_DBG("");
 
+       if (error != NULL) {
+               result = -EIO;
+               if (g_strcmp0("org.freedesktop.DBus.Error.UnknownMethod",
+                                               error) == 0)
+                       result = -ECONNABORTED;
+       }
+
+       if (result < 0 && data->callback != NULL) {
+               data->callback(result, data->interface, data->user_data);
+               data->callback = NULL;
 #if defined TIZEN_EXT
-       if (error != NULL && data->callback != NULL) {
-               data->callback(-EIO, data->interface, data->user_data);
                data->user_data = NULL;
-       }
-#else
-       if (error != NULL && data->callback != NULL)
-               data->callback(-EIO, data->interface, data->user_data);
 #endif
+       }
 
        /* If we are disconnecting from previous WPS successful
         * association. i.e.: it did not went through AddNetwork,
         * and interface->network_path was never set. */
-       if (data->interface->network_path == NULL)
+       if (data->interface->network_path == NULL) {
+               dbus_free(data);
                return;
+       }
 
-       network_remove(data);
+       if (result != -ECONNABORTED)
+               network_remove(data);
+       else
+               dbus_free(data);
 }
 
 int g_supplicant_interface_disconnect(GSupplicantInterface *interface,
@@ -3517,6 +3979,11 @@ int g_supplicant_register(const GSupplicantCallbacks *callbacks)
        dbus_bus_add_match(connection, g_supplicant_rule3, NULL);
        dbus_bus_add_match(connection, g_supplicant_rule4, NULL);
        dbus_bus_add_match(connection, g_supplicant_rule5, NULL);
+#if defined TIZEN_EXT
+       dbus_bus_add_match(connection,
+                       "type=signal,interface=org.tizen.system.deviced.PowerOff,"
+                       "member=ChangeState", NULL);
+#endif
        dbus_connection_flush(connection);
 
        if (dbus_bus_name_has_owner(connection,
@@ -3579,7 +4046,7 @@ void g_supplicant_unregister(const GSupplicantCallbacks *callbacks)
                callback_system_killed();
 
        if (interface_table != NULL) {
-               g_hash_table_foreach(interface_table,   
+               g_hash_table_foreach(interface_table,
                                        unregister_remove_interface, NULL);
                g_hash_table_destroy(interface_table);
                interface_table = NULL;
index d92ae95..47ff8c4 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Web service library with GLib integration
  *
- *  Copyright (C) 2009-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2009-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
@@ -42,7 +42,7 @@ struct _GIOGnuTLSChannel {
        GIOChannel channel;
        gint fd;
        gnutls_certificate_credentials_t cred;
-       gnutls_session session;
+       gnutls_session_t session;
        gboolean established;
        gboolean again;
 };
@@ -408,6 +408,11 @@ static ssize_t g_io_gnutls_pull_func(gnutls_transport_ptr_t transport_data,
        return result;
 }
 
+gboolean g_io_channel_supports_tls(void)
+{
+       return TRUE;
+}
+
 GIOChannel *g_io_channel_gnutls_new(int fd)
 {
        GIOGnuTLSChannel *gnutls_channel;
index cbc098e..f16c75d 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Web service library with GLib integration
  *
- *  Copyright (C) 2009-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2009-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
@@ -21,4 +21,6 @@
 
 #include <glib.h>
 
+gboolean g_io_channel_supports_tls(void);
+
 GIOChannel *g_io_channel_gnutls_new(int fd);
index 14b9825..88db3e4 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Web service library with GLib integration
  *
- *  Copyright (C) 2009-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2009-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
 
 #include "giognutls.h"
 
+gboolean g_io_channel_supports_tls(void)
+{
+       return FALSE;
+}
+
 GIOChannel *g_io_channel_gnutls_new(int fd)
 {
        return NULL;
index 7c4c0bc..721e9d2 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Resolver library with GLib integration
  *
- *  Copyright (C) 2009-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2009-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
@@ -79,6 +79,7 @@ struct resolv_lookup {
 struct resolv_query {
        GResolv *resolv;
 
+       int nr_ns;
        guint timeout;
 
        uint16_t msgid;
@@ -115,24 +116,34 @@ struct _GResolv {
        gpointer debug_data;
 };
 
-static inline void debug(GResolv *resolv, const char *format, ...)
+#define debug(resolv, format, arg...)                          \
+       _debug(resolv, __FILE__, __func__, format, ## arg)
+
+static void _debug(GResolv *resolv, const char *file, const char *caller,
+                                               const char *format, ...)
 {
        char str[256];
        va_list ap;
+       int len;
 
        if (resolv->debug_func == NULL)
                return;
 
        va_start(ap, format);
 
-       if (vsnprintf(str, sizeof(str), format, ap) > 0)
-               resolv->debug_func(str, resolv->debug_data);
+       if ((len = snprintf(str, sizeof(str), "%s:%s() resolv %p ",
+                                               file, caller, resolv)) > 0) {
+               if (vsnprintf(str + len, sizeof(str) - len, format, ap) > 0)
+                       resolv->debug_func(str, resolv->debug_data);
+       }
 
        va_end(ap);
 }
 
 static void destroy_query(struct resolv_query *query)
 {
+       debug(query->resolv, "query %p timeout %d", query, query->timeout);
+
        if (query->timeout > 0)
                g_source_remove(query->timeout);
 
@@ -141,6 +152,9 @@ static void destroy_query(struct resolv_query *query)
 
 static void destroy_lookup(struct resolv_lookup *lookup)
 {
+       debug(lookup->resolv, "lookup %p id %d ipv4 %p ipv6 %p",
+               lookup, lookup->id, lookup->ipv4_query, lookup->ipv6_query);
+
        if (lookup->ipv4_query != NULL) {
                g_queue_remove(lookup->resolv->query_queue,
                                                lookup->ipv4_query);
@@ -458,6 +472,8 @@ static void sort_and_return_results(struct resolv_lookup *lookup)
        char buf[INET6_ADDRSTRLEN + 1];
        GResolvResultStatus status;
        char **results = g_try_new0(char *, lookup->nr_results + 1);
+       GResolvResultFunc result_func = lookup->result_func;
+       void *result_data = lookup->result_data;
        int i, n = 0;
 
        if (!results)
@@ -481,21 +497,30 @@ static void sort_and_return_results(struct resolv_lookup *lookup)
                } else
                        continue;
 
-               results[n++] = strdup(buf);
+               results[n++] = g_strdup(buf);
        }
 
        results[n++] = NULL;
 
-       status = lookup->ipv4_status;
-
-       if (status == G_RESOLV_RESULT_STATUS_SUCCESS)
+       if (lookup->resolv->result_family == AF_INET)
+               status = lookup->ipv4_status;
+       else if (lookup->resolv->result_family == AF_INET6)
                status = lookup->ipv6_status;
+       else {
+               if (lookup->ipv6_status == G_RESOLV_RESULT_STATUS_SUCCESS)
+                       status = lookup->ipv6_status;
+               else
+                       status = lookup->ipv4_status;
+       }
 
-       lookup->result_func(status, results, lookup->result_data);
+       debug(lookup->resolv, "lookup %p received %d results", lookup, n);
 
-       g_strfreev(results);
        g_queue_remove(lookup->resolv->lookup_queue, lookup);
        destroy_lookup(lookup);
+
+       result_func(status, results, result_data);
+
+       g_strfreev(results);
 }
 
 static gboolean query_timeout(gpointer user_data)
@@ -514,11 +539,11 @@ static gboolean query_timeout(gpointer user_data)
                lookup->ipv6_query = NULL;
        }
 
-       if (lookup->ipv4_query == NULL && lookup->ipv4_query == NULL)
-               sort_and_return_results(lookup);
-
-       destroy_query(query);
        g_queue_remove(resolv->query_queue, query);
+       destroy_query(query);
+
+       if (lookup->ipv4_query == NULL && lookup->ipv6_query == NULL)
+               sort_and_return_results(lookup);
 
        return FALSE;
 }
@@ -531,8 +556,10 @@ static void free_nameserver(struct resolv_nameserver *nameserver)
        if (nameserver->udp_watch > 0)
                g_source_remove(nameserver->udp_watch);
 
-       if (nameserver->udp_channel != NULL)
+       if (nameserver->udp_channel != NULL) {
+               g_io_channel_shutdown(nameserver->udp_channel, TRUE, NULL);
                g_io_channel_unref(nameserver->udp_channel);
+       }
 
        g_free(nameserver->address);
        g_free(nameserver);
@@ -553,12 +580,13 @@ static void flush_nameservers(GResolv *resolv)
 static int send_query(GResolv *resolv, const unsigned char *buf, int len)
 {
        GList *list;
+       int nr_ns;
 
        if (resolv->nameserver_list == NULL)
                return -ENOENT;
 
-       for (list = g_list_first(resolv->nameserver_list);
-                                       list; list = g_list_next(list)) {
+       for (list = g_list_first(resolv->nameserver_list), nr_ns = 0;
+                               list; list = g_list_next(list), nr_ns++) {
                struct resolv_nameserver *nameserver = list->data;
                int sk, sent;
 
@@ -572,7 +600,7 @@ static int send_query(GResolv *resolv, const unsigned char *buf, int len)
                        continue;
        }
 
-       return 0;
+       return nr_ns;
 }
 
 static gint compare_lookup_id(gconstpointer a, gconstpointer b)
@@ -607,8 +635,10 @@ static void add_result(struct resolv_lookup *lookup, int family,
                                                        const void *data)
 {
        int n = lookup->nr_results++;
-       lookup->results = g_realloc(lookup->results,
+       lookup->results = g_try_realloc(lookup->results,
                                        sizeof(struct sort_result) * (n + 1));
+       if (lookup->results == NULL)
+               return;
 
        memset(&lookup->results[n], 0, sizeof(struct sort_result));
 
@@ -635,7 +665,17 @@ static void parse_response(struct resolv_nameserver *nameserver,
 
        debug(resolv, "response from %s", nameserver->address);
 
+#if defined TIZEN_EXT
+       if (-1 == ns_initparse(buf, len, &msg))
+               return;
+#else
        ns_initparse(buf, len, &msg);
+#endif
+
+       list = g_queue_find_custom(resolv->query_queue,
+                       GUINT_TO_POINTER(ns_msg_id(msg)), compare_query_msgid);
+       if (!list)
+               return;
 
        rcode = ns_msg_getflag(msg, ns_f_rcode);
        count = ns_msg_count(msg, ns_s_an);
@@ -644,22 +684,22 @@ static void parse_response(struct resolv_nameserver *nameserver,
                                        ns_msg_id(msg), rcode, count);
 
        switch (rcode) {
-       case 0:
+       case ns_r_noerror:
                status = G_RESOLV_RESULT_STATUS_SUCCESS;
                break;
-       case 1:
+       case ns_r_formerr:
                status = G_RESOLV_RESULT_STATUS_FORMAT_ERROR;
                break;
-       case 2:
+       case ns_r_servfail:
                status = G_RESOLV_RESULT_STATUS_SERVER_FAILURE;
                break;
-       case 3:
+       case ns_r_nxdomain:
                status = G_RESOLV_RESULT_STATUS_NAME_ERROR;
                break;
-       case 4:
+       case ns_r_notimpl:
                status = G_RESOLV_RESULT_STATUS_NOT_IMPLEMENTED;
                break;
-       case 5:
+       case ns_r_refused:
                status = G_RESOLV_RESULT_STATUS_REFUSED;
                break;
        default:
@@ -667,24 +707,23 @@ static void parse_response(struct resolv_nameserver *nameserver,
                break;
        }
 
-       list = g_queue_find_custom(resolv->query_queue,
-                       GUINT_TO_POINTER(ns_msg_id(msg)), compare_query_msgid);
-       if (!list)
-               return;
-
        query = list->data;
+       query->nr_ns--;
+
        lookup = query->lookup;
 
-       if (query == lookup->ipv6_query) {
+       if (query == lookup->ipv6_query)
                lookup->ipv6_status = status;
-               lookup->ipv6_query = NULL;
-       } else if (query == lookup->ipv4_query) {
+       else if (query == lookup->ipv4_query)
                lookup->ipv4_status = status;
-               lookup->ipv4_query = NULL;
-       }
 
        for (i = 0; i < count; i++) {
+#if defined TIZEN_EXT
+               if (-1 == ns_parserr(&msg, ns_s_an, i, &rr))
+                       continue;
+#else
                ns_parserr(&msg, ns_s_an, i, &rr);
+#endif
 
                if (ns_rr_class(rr) != ns_c_in)
                        continue;
@@ -701,11 +740,19 @@ static void parse_response(struct resolv_nameserver *nameserver,
                }
        }
 
-       if (lookup->ipv4_query == NULL && lookup->ipv6_query == NULL)
-               sort_and_return_results(lookup);
+       if (status != G_RESOLV_RESULT_STATUS_SUCCESS && query->nr_ns > 0)
+               return;
+
+       if (query == lookup->ipv6_query)
+               lookup->ipv6_query = NULL;
+       else
+               lookup->ipv4_query = NULL;
 
-       destroy_query(query);
        g_queue_remove(resolv->query_queue, query);
+       destroy_query(query);
+
+       if (lookup->ipv4_query == NULL && lookup->ipv6_query == NULL)
+               sort_and_return_results(lookup);
 }
 
 static gboolean received_udp_data(GIOChannel *channel, GIOCondition cond,
@@ -855,6 +902,7 @@ GResolv *g_resolv_ref(GResolv *resolv)
 void g_resolv_unref(GResolv *resolv)
 {
        struct resolv_query *query;
+       struct resolv_lookup *lookup;
 
        if (resolv == NULL)
                return;
@@ -862,8 +910,15 @@ void g_resolv_unref(GResolv *resolv)
        if (__sync_fetch_and_sub(&resolv->ref_count, 1) != 1)
                return;
 
-       while ((query = g_queue_pop_head(resolv->query_queue)))
+       while ((lookup = g_queue_pop_head(resolv->lookup_queue))) {
+               debug(resolv, "lookup %p id %d", lookup, lookup->id);
+               destroy_lookup(lookup);
+       }
+
+       while ((query = g_queue_pop_head(resolv->query_queue))) {
+               debug(resolv, "query %p", query);
                destroy_query(query);
+       }
 
        g_queue_free(resolv->query_queue);
        g_queue_free(resolv->lookup_queue);
@@ -937,7 +992,10 @@ static gint add_query(struct resolv_lookup *lookup, const char *hostname, int ty
 
        query->msgid = buf[0] << 8 | buf[1];
 
-       if (send_query(lookup->resolv, buf, len) < 0) {
+       debug(lookup->resolv, "sending %d bytes", len);
+
+       query->nr_ns = send_query(lookup->resolv, buf, len);
+       if (query->nr_ns <= 0) {
                g_free(query);
                return -EIO;
        }
@@ -947,6 +1005,9 @@ static gint add_query(struct resolv_lookup *lookup, const char *hostname, int ty
 
        g_queue_push_tail(lookup->resolv->query_queue, query);
 
+       debug(lookup->resolv, "lookup %p id %d query %p", lookup, lookup->id,
+                                                                       query);
+
        query->timeout = g_timeout_add_seconds(5, query_timeout, query);
 
        if (type == ns_t_aaaa)
@@ -962,11 +1023,11 @@ guint g_resolv_lookup_hostname(GResolv *resolv, const char *hostname,
 {
        struct resolv_lookup *lookup;
 
-       debug(resolv, "lookup hostname %s", hostname);
-
        if (resolv == NULL)
                return 0;
 
+       debug(resolv, "hostname %s", hostname);
+
        if (resolv->nameserver_list == NULL) {
                int i;
 
@@ -1011,9 +1072,9 @@ guint g_resolv_lookup_hostname(GResolv *resolv, const char *hostname,
        if (resolv->result_family != AF_INET) {
                if (add_query(lookup, hostname, ns_t_aaaa)) {
                        if (resolv->result_family != AF_INET6) {
-                               destroy_query(lookup->ipv4_query);
                                g_queue_remove(resolv->query_queue,
                                                lookup->ipv4_query);
+                               destroy_query(lookup->ipv4_query);
                        }
 
                        g_free(lookup);
@@ -1022,21 +1083,31 @@ guint g_resolv_lookup_hostname(GResolv *resolv, const char *hostname,
        }
 
        g_queue_push_tail(resolv->lookup_queue, lookup);
+
+       debug(resolv, "lookup %p id %d", lookup, lookup->id);
+
        return lookup->id;
 }
 
 gboolean g_resolv_cancel_lookup(GResolv *resolv, guint id)
 {
+       struct resolv_lookup *lookup;
        GList *list;
 
+       debug(resolv, "lookup id %d", id);
+
        list = g_queue_find_custom(resolv->lookup_queue,
                                GUINT_TO_POINTER(id), compare_lookup_id);
 
        if (list == NULL)
                return FALSE;
 
-       destroy_lookup(list->data);
-       g_queue_remove(resolv->query_queue, list->data);
+       lookup = list->data;
+
+       debug(resolv, "lookup %p", lookup);
+
+       g_queue_remove(resolv->lookup_queue, lookup);
+       destroy_lookup(lookup);
 
        return TRUE;
 }
index fa6a55e..3402df9 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Resolver library with GLib integration
  *
- *  Copyright (C) 2009-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2009-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
index 1461346..830977f 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Web service library with GLib integration
  *
- *  Copyright (C) 2009-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2009-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
 
 #include <stdio.h>
 #include <errno.h>
+#include <fcntl.h>
 #include <unistd.h>
 #include <stdlib.h>
 #include <stdarg.h>
 #include <string.h>
 #include <sys/socket.h>
+#include <sys/sendfile.h>
+#include <sys/stat.h>
 #include <arpa/inet.h>
 #include <netdb.h>
 #include <net/if.h>
+#include <netinet/tcp.h>
+#include <ifaddrs.h>
 
 #include "giognutls.h"
 #include "gresolv.h"
@@ -93,7 +98,11 @@ struct web_session {
        GWebResult result;
 
        GWebResultFunc result_func;
+       GWebRouteFunc route_func;
        GWebInputFunc input_func;
+       int fd;
+       gsize length;
+       gsize offset;
        gpointer user_data;
 };
 
@@ -119,31 +128,40 @@ struct _GWeb {
        gpointer debug_data;
 };
 
-static inline void debug(GWeb *web, const char *format, ...)
+#define debug(web, format, arg...)                             \
+       _debug(web, __FILE__, __func__, format, ## arg)
+
+static void _debug(GWeb *web, const char *file, const char *caller,
+                                               const char *format, ...)
 {
        char str[256];
        va_list ap;
+       int len;
 
        if (web->debug_func == NULL)
                return;
 
        va_start(ap, format);
 
-       if (vsnprintf(str, sizeof(str), format, ap) > 0)
-               web->debug_func(str, web->debug_data);
+       if ((len = snprintf(str, sizeof(str), "%s:%s() web %p ",
+                                               file, caller, web)) > 0) {
+               if (vsnprintf(str + len, sizeof(str) - len, format, ap) > 0)
+                       web->debug_func(str, web->debug_data);
+       }
 
        va_end(ap);
 }
 
 static void free_session(struct web_session *session)
 {
-       GWeb *web = session->web;
+       GWeb *web;
 
        if (session == NULL)
                return;
 
        g_free(session->request);
 
+       web = session->web;
        if (session->resolv_action > 0)
                g_resolv_cancel_lookup(web->resolv, session->resolv_action);
 
@@ -256,6 +274,11 @@ void g_web_unref(GWeb *web)
        g_free(web);
 }
 
+gboolean g_web_supports_tls(void)
+{
+       return g_io_channel_supports_tls();
+}
+
 void g_web_set_debug(GWeb *web, GWebDebugFunc func, gpointer user_data)
 {
        if (web == NULL)
@@ -419,7 +442,6 @@ gboolean g_web_get_close_connection(GWeb *web)
 
 static inline void call_result_func(struct web_session *session, guint16 status)
 {
-       gboolean result;
 
        if (session->result_func == NULL)
                return;
@@ -427,23 +449,33 @@ static inline void call_result_func(struct web_session *session, guint16 status)
        if (status != 0)
                session->result.status = status;
 
-       result = session->result_func(&session->result, session->user_data);
+       session->result_func(&session->result, session->user_data);
+
+}
 
-       debug(session->web, "[result function] %s",
-                                       result == TRUE ? "continue" : "stop");
+static inline void call_route_func(struct web_session *session)
+{
+       if (session->route_func != NULL)
+               session->route_func(session->address, session->addr->ai_family,
+                               session->web->index, session->user_data);
 }
 
 static gboolean process_send_buffer(struct web_session *session)
 {
-       GString *buf = session->send_buffer;
+       GString *buf;
        gsize count, bytes_written;
        GIOStatus status;
 
+       if (session == NULL)
+               return FALSE;
+
+       buf = session->send_buffer;
        count = buf->len;
 
        if (count == 0) {
                if (session->request_started == TRUE &&
-                                       session->more_data == FALSE)
+                                       session->more_data == FALSE &&
+                                       session->fd == -1)
                        session->body_done = TRUE;
 
                return FALSE;
@@ -463,6 +495,43 @@ static gboolean process_send_buffer(struct web_session *session)
        return TRUE;
 }
 
+static gboolean process_send_file(struct web_session *session)
+{
+       int sk;
+       off_t offset;
+       ssize_t bytes_sent;
+
+       if (session->fd == -1)
+               return FALSE;
+
+       if (session->request_started == FALSE || session->more_data == TRUE)
+               return FALSE;
+
+       sk = g_io_channel_unix_get_fd(session->transport_channel);
+       if (sk < 0)
+               return FALSE;
+
+       offset = session->offset;
+
+       bytes_sent = sendfile(sk, session->fd, &offset, session->length);
+
+       debug(session->web, "errno: %d, bytes to send %zu / bytes sent %zu",
+                       errno, session->length, bytes_sent);
+
+       if (bytes_sent < 0 && errno != EAGAIN)
+               return FALSE;
+
+       session->offset = offset;
+       session->length -= bytes_sent;
+
+       if (session->length == 0) {
+               session->body_done = TRUE;
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
 static void process_next_chunk(struct web_session *session)
 {
        GString *buf = session->send_buffer;
@@ -491,7 +560,11 @@ static void start_request(struct web_session *session)
 {
        GString *buf = session->send_buffer;
        const char *version;
+#if defined TIZEN_EXT
+       const guint8 *body = NULL;
+#else
        const guint8 *body;
+#endif
        gsize length;
 
        debug(session->web, "request %s from %s",
@@ -531,7 +604,7 @@ static void start_request(struct web_session *session)
                                                        session->content_type);
                if (session->input_func == NULL) {
                        session->more_data = FALSE;
-                       length = 0;
+                       length = session->length;
                } else
                        session->more_data = session->input_func(&body, &length,
                                                        session->user_data);
@@ -552,7 +625,7 @@ static void start_request(struct web_session *session)
                        g_string_append_printf(buf, "%zx\r\n", length);
                        g_string_append_len(buf, (char *) body, length);
                        g_string_append(buf, "\r\n");
-               } else
+               } else if (session->fd == -1)
                        g_string_append_len(buf, (char *) body, length);
        }
 }
@@ -570,6 +643,9 @@ static gboolean send_data(GIOChannel *channel, GIOCondition cond,
        if (process_send_buffer(session) == TRUE)
                return TRUE;
 
+       if (process_send_file(session) == TRUE)
+               return TRUE;
+
        if (session->request_started == FALSE) {
                session->request_started = TRUE;
                start_request(session);
@@ -729,6 +805,10 @@ static void handle_multi_line(struct web_session *session)
                g_string_insert_c(session->current_header, 0, ' ');
        }
 
+#if defined TIZEN_EXT
+       if (session->result.last_key == NULL)
+               return;
+#endif
        value = g_hash_table_lookup(session->result.headers,
                                        session->result.last_key);
        if (value != NULL) {
@@ -901,6 +981,57 @@ static gboolean received_data(GIOChannel *channel, GIOCondition cond,
        return TRUE;
 }
 
+static int bind_to_address(int sk, const char *interface, int family)
+{
+       struct ifaddrs *ifaddr_list, *ifaddr;
+       int size, err = -1;
+
+       if (getifaddrs(&ifaddr_list) < 0)
+               return err;
+
+       for (ifaddr = ifaddr_list; ifaddr != NULL; ifaddr = ifaddr->ifa_next) {
+               if (g_strcmp0(ifaddr->ifa_name, interface) != 0)
+                       continue;
+
+               if (ifaddr->ifa_addr == NULL ||
+                               ifaddr->ifa_addr->sa_family != family)
+                       continue;
+
+               switch (family) {
+               case AF_INET:
+                       size = sizeof(struct sockaddr_in);
+                       break;
+               case AF_INET6:
+                       size = sizeof(struct sockaddr_in6);
+                       break;
+               default:
+                       continue;
+               }
+
+               err = bind(sk, (struct sockaddr *) ifaddr->ifa_addr, size);
+               break;
+       }
+
+       freeifaddrs(ifaddr_list);
+       return err;
+}
+
+static inline int bind_socket(int sk, int index, int family)
+{
+       char interface[IF_NAMESIZE];
+       int err;
+
+       if (if_indextoname(index, interface) == NULL)
+               return -1;
+
+       err = setsockopt(sk, SOL_SOCKET, SO_BINDTODEVICE,
+                                       interface, IF_NAMESIZE);
+       if (err < 0)
+               err = bind_to_address(sk, interface, family);
+
+       return err;
+}
+
 static int connect_session_transport(struct web_session *session)
 {
        GIOFlags flags;
@@ -912,18 +1043,11 @@ static int connect_session_transport(struct web_session *session)
                return -EIO;
 
        if (session->web->index > 0) {
-               char interface[IF_NAMESIZE];
-
-               memset(interface, 0, IF_NAMESIZE);
-
-               if (if_indextoname(session->web->index, interface) != NULL) {
-                       if (setsockopt(sk, SOL_SOCKET, SO_BINDTODEVICE,
-                                               interface, IF_NAMESIZE) < 0) {
-                               close(sk);
-                               return -EIO;
-                       }
-
-                       debug(session->web, "Use interface %s", interface);
+               if (bind_socket(sk, session->web->index,
+                                       session->addr->ai_family) < 0) {
+                       debug(session->web, "bind() %s", strerror(errno));
+                       close(sk);
+                       return -EIO;
                }
        }
 
@@ -936,6 +1060,7 @@ static int connect_session_transport(struct web_session *session)
        }
 
        if (session->transport_channel == NULL) {
+               debug(session->web, "channel missing");
                close(sk);
                return -ENOMEM;
        }
@@ -952,6 +1077,7 @@ static int connect_session_transport(struct web_session *session)
        if (connect(sk, session->addr->ai_addr,
                        session->addr->ai_addrlen) < 0) {
                if (errno != EINPROGRESS) {
+                       debug(session->web, "connect() %s", strerror(errno));
                        close(sk);
                        return -EIO;
                }
@@ -1111,7 +1237,9 @@ static void resolv_result(GResolvResultStatus status,
                return;
        }
 
+       g_free(session->address);
        session->address = g_strdup(results[0]);
+       call_route_func(session);
 
        if (create_transport(session) < 0) {
                call_result_func(session, 409);
@@ -1121,7 +1249,8 @@ static void resolv_result(GResolvResultStatus status,
 
 static guint do_request(GWeb *web, const char *url,
                                const char *type, GWebInputFunc input,
-                               GWebResultFunc func, gpointer user_data)
+                               int fd, gsize length, GWebResultFunc func,
+                               GWebRouteFunc route, gpointer user_data)
 {
        struct web_session *session;
 
@@ -1154,7 +1283,11 @@ static guint do_request(GWeb *web, const char *url,
        session->web = web;
 
        session->result_func = func;
+       session->route_func = route;
        session->input_func = input;
+       session->fd = fd;
+       session->length = length;
+       session->offset = 0;
        session->user_data = user_data;
 
        session->receive_buffer = g_try_malloc(DEFAULT_BUFFER_SIZE);
@@ -1184,32 +1317,12 @@ static guint do_request(GWeb *web, const char *url,
                        return 0;
                }
        } else {
-               struct addrinfo hints;
-               char *port;
-               int ret;
-
                if (session->address == NULL)
                        session->address = g_strdup(session->host);
 
-               memset(&hints, 0, sizeof(struct addrinfo));
-               hints.ai_flags = AI_NUMERICHOST;
-               hints.ai_family = session->web->family;
-
-               if (session->addr != NULL) {
-                       freeaddrinfo(session->addr);
-                       session->addr = NULL;
-               }
-
-               port = g_strdup_printf("%u", session->port);
-               ret = getaddrinfo(session->address, port, &hints,
-                                                       &session->addr);
-               g_free(port);
-               if (ret != 0 || session->addr == NULL) {
-                       free_session(session);
-                       return 0;
-               }
-
-               if (create_transport(session) < 0) {
+               session->resolv_action = g_resolv_lookup_hostname(web->resolv,
+                                       session->address, resolv_result, session);
+               if (session->resolv_action == 0) {
                        free_session(session);
                        return 0;
                }
@@ -1220,17 +1333,40 @@ static guint do_request(GWeb *web, const char *url,
        return web->next_query_id++;
 }
 
-guint g_web_request_get(GWeb *web, const char *url,
-                               GWebResultFunc func, gpointer user_data)
+guint g_web_request_get(GWeb *web, const char *url, GWebResultFunc func,
+               GWebRouteFunc route, gpointer user_data)
 {
-       return do_request(web, url, NULL, NULL, func, user_data);
+       return do_request(web, url, NULL, NULL, -1, 0, func, route, user_data);
 }
 
 guint g_web_request_post(GWeb *web, const char *url,
                                const char *type, GWebInputFunc input,
                                GWebResultFunc func, gpointer user_data)
 {
-       return do_request(web, url, type, input, func, user_data);
+       return do_request(web, url, type, input, -1, 0, func, NULL, user_data);
+}
+
+guint g_web_request_post_file(GWeb *web, const char *url,
+                               const char *type, const char *file,
+                               GWebResultFunc func, gpointer user_data)
+{
+       struct stat st;
+       int fd;
+       guint ret;
+
+       if (stat(file, &st) < 0)
+               return 0;
+
+       fd = open(file, O_RDONLY);
+       if (fd < 0)
+               return 0;
+
+       ret = do_request(web, url, type, NULL, fd, st.st_size, func, NULL,
+                       user_data);
+       if (ret == 0)
+               close(fd);
+
+       return ret;
 }
 
 gboolean g_web_cancel_request(GWeb *web, guint id)
index cfeceb6..104345e 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Web service library with GLib integration
  *
- *  Copyright (C) 2009-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2009-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
@@ -40,6 +40,9 @@ typedef struct _GWebParser GWebParser;
 
 typedef gboolean (*GWebResultFunc)(GWebResult *result, gpointer user_data);
 
+typedef gboolean (*GWebRouteFunc)(const char *addr, int ai_family,
+               int if_index, gpointer user_data);
+
 typedef gboolean (*GWebInputFunc)(const guint8 **data, gsize *length,
                                                        gpointer user_data);
 
@@ -52,6 +55,8 @@ void g_web_unref(GWeb *web);
 
 void g_web_set_debug(GWeb *web, GWebDebugFunc func, gpointer user_data);
 
+gboolean g_web_supports_tls(void);
+
 gboolean g_web_set_proxy(GWeb *web, const char *proxy);
 
 gboolean g_web_set_address_family(GWeb *web, int family);
@@ -70,10 +75,14 @@ void g_web_set_close_connection(GWeb *web, gboolean enabled);
 gboolean g_web_get_close_connection(GWeb *web);
 
 guint g_web_request_get(GWeb *web, const char *url,
-                               GWebResultFunc func, gpointer user_data);
+                               GWebResultFunc func, GWebRouteFunc route,
+                               gpointer user_data);
 guint g_web_request_post(GWeb *web, const char *url,
                                const char *type, GWebInputFunc input,
                                GWebResultFunc func, gpointer user_data);
+guint g_web_request_post_file(GWeb *web, const char *url,
+                               const char *type, const char *file,
+                               GWebResultFunc func, gpointer user_data);
 
 gboolean g_web_cancel_request(GWeb *web, guint id);
 
index 8d17bff..fa6851d 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
index 1fd68ff..8c97f4a 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
@@ -39,11 +39,10 @@ enum connman_device_type {
        CONNMAN_DEVICE_TYPE_UNKNOWN   = 0,
        CONNMAN_DEVICE_TYPE_ETHERNET  = 1,
        CONNMAN_DEVICE_TYPE_WIFI      = 2,
-       CONNMAN_DEVICE_TYPE_WIMAX     = 3,
-       CONNMAN_DEVICE_TYPE_BLUETOOTH = 4,
-       CONNMAN_DEVICE_TYPE_CELLULAR  = 5,
-       CONNMAN_DEVICE_TYPE_GPS       = 6,
-       CONNMAN_DEVICE_TYPE_GADGET    = 7,
+       CONNMAN_DEVICE_TYPE_BLUETOOTH = 3,
+       CONNMAN_DEVICE_TYPE_CELLULAR  = 4,
+       CONNMAN_DEVICE_TYPE_GPS       = 5,
+       CONNMAN_DEVICE_TYPE_GADGET    = 6,
        CONNMAN_DEVICE_TYPE_VENDOR    = 10000,
 };
 
@@ -55,8 +54,18 @@ struct connman_device;
 
 struct connman_device *connman_device_create(const char *node,
                                                enum connman_device_type type);
-struct connman_device *connman_device_ref(struct connman_device *device);
-void connman_device_unref(struct connman_device *device);
+
+#define connman_device_ref(device) \
+       connman_device_ref_debug(device, __FILE__, __LINE__, __func__)
+
+#define connman_device_unref(device) \
+       connman_device_unref_debug(device, __FILE__, __LINE__, __func__)
+
+struct connman_device *
+connman_device_ref_debug(struct connman_device *device,
+                       const char *file, int line, const char *caller);
+void connman_device_unref_debug(struct connman_device *device,
+                       const char *file, int line, const char *caller);
 
 enum connman_device_type connman_device_get_type(struct connman_device *device);
 void connman_device_set_index(struct connman_device *device, int index);
@@ -70,18 +79,22 @@ const char *connman_device_get_ident(struct connman_device *device);
 
 int connman_device_set_powered(struct connman_device *device,
                                                connman_bool_t powered);
+connman_bool_t connman_device_get_powered(struct connman_device *device);
 #if defined TIZEN_EXT
 /*
- * Description: It checks significant and effective Wi-Fi profiles which can make an auto-connection.
- *             It saves power consumption not to scan if there is no valid profile to make an auto-connection.
+ * Description: It checks significant and effective Wi-Fi profiles which
+ * can make an auto-connection. It saves power consumption not to scan if
+ * there is no valid profile to make an auto-connection.
  */
-void connman_device_significant_wifi_profile_ref(struct connman_device *device);
-connman_bool_t connman_device_significant_wifi_profile_unref_and_test(struct connman_device *device);
-
-connman_bool_t connman_device_load_significant_wifi_profile_refcount_from_storage(struct connman_device *device);
-connman_bool_t connman_device_save_significant_wifi_profile_refcount_to_storage(struct connman_device *device);
+void connman_device_sig_wifi_profile_ref(struct connman_device *device);
+connman_bool_t connman_device_sig_wifi_profile_unref_and_test(
+                                       struct connman_device *device);
+
+connman_bool_t connman_device_load_sig_wifi_profile_refcount_from_storage(
+                                               struct connman_device *device);
+connman_bool_t connman_device_save_sig_wifi_profile_refcount2storage(
+                                       struct connman_device *device);
 #endif
-
 int connman_device_set_scanning(struct connman_device *device,
                                                connman_bool_t scanning);
 connman_bool_t connman_device_get_scanning(struct connman_device *device);
@@ -104,8 +117,6 @@ int connman_device_remove_network(struct connman_device *device,
                                        struct connman_network *network);
 void connman_device_remove_all_networks(struct connman_device *device);
 
-void connman_device_schedule_scan(struct connman_device *device);
-
 int connman_device_register(struct connman_device *device);
 void connman_device_unregister(struct connman_device *device);
 
@@ -122,6 +133,10 @@ struct connman_device_driver {
        int (*disable) (struct connman_device *device);
        int (*scan) (struct connman_device *device);
        int (*scan_fast) (struct connman_device *device);
+       int (*scan_hidden)(struct connman_device *device,
+                       const char *ssid, unsigned int ssid_len,
+                       const char *identity, const char* passphrase,
+                       void *user_data);
 };
 
 int connman_device_driver_register(struct connman_device_driver *driver);
index 1aa23ea..1f6583f 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
@@ -42,6 +42,9 @@ int connman_inet_ifup(int index);
 int connman_inet_ifdown(int index);
 
 struct connman_device *connman_inet_create_device(int index);
+#if defined TIZEN_EXT
+void connman_inet_update_device_ident(struct connman_device *device);
+#endif
 connman_bool_t connman_inet_is_cfg80211(int index);
 
 int connman_inet_set_address(int index, struct connman_ipaddress *ipaddress);
@@ -78,6 +81,11 @@ int connman_inet_remove_from_bridge(int index, const char *bridge);
 int connman_inet_set_mtu(int index, int mtu);
 int connman_inet_setup_tunnel(char *tunnel, int mtu);
 int connman_inet_create_tunnel(char **iface);
+int connman_inet_get_dest_addr(int index, char **dest);
+int connman_inet_ipv6_get_dest_addr(int index, char **dest);
+int connman_inet_check_ipaddress(const char *host);
+connman_bool_t connman_inet_check_hostname(const char *ptr, size_t len);
+connman_bool_t connman_inet_is_ipv6_supported();
 
 #ifdef __cplusplus
 }
index 34463eb..3612755 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
@@ -32,14 +32,7 @@ extern "C" {
  * @short_description: Functions for IP configuration handling
  */
 
-struct connman_ipaddress {
-       int family;
-       unsigned char prefixlen;
-       char *local;
-       char *peer;
-       char *broadcast;
-       char *gateway;
-};
+struct connman_ipaddress;
 
 struct connman_ipaddress *connman_ipaddress_alloc(int family);
 void connman_ipaddress_free(struct connman_ipaddress *ipaddress);
@@ -74,33 +67,6 @@ enum connman_ipconfig_method {
 
 struct connman_ipconfig;
 
-struct connman_ipconfig_ops {
-       void (*up) (struct connman_ipconfig *ipconfig);
-       void (*down) (struct connman_ipconfig *ipconfig);
-       void (*lower_up) (struct connman_ipconfig *ipconfig);
-       void (*lower_down) (struct connman_ipconfig *ipconfig);
-       void (*ip_bound) (struct connman_ipconfig *ipconfig);
-       void (*ip_release) (struct connman_ipconfig *ipconfig);
-};
-
-struct connman_ipconfig *connman_ipconfig_create(int index,
-                                       enum connman_ipconfig_type type);
-struct connman_ipconfig *connman_ipconfig_ref(struct connman_ipconfig *ipconfig);
-void connman_ipconfig_unref(struct connman_ipconfig *ipconfig);
-
-void *connman_ipconfig_get_data(struct connman_ipconfig *ipconfig);
-void connman_ipconfig_set_data(struct connman_ipconfig *ipconfig, void *data);
-
-int connman_ipconfig_get_index(struct connman_ipconfig *ipconfig);
-const char *connman_ipconfig_get_ifname(struct connman_ipconfig *ipconfig);
-
-void connman_ipconfig_set_ops(struct connman_ipconfig *ipconfig,
-                               const struct connman_ipconfig_ops *ops);
-int connman_ipconfig_set_method(struct connman_ipconfig *ipconfig,
-                                       enum connman_ipconfig_method method);
-void __connman_ipconfig_disable_ipv6(struct connman_ipconfig *ipconfig);
-void __connman_ipconfig_enable_ipv6(struct connman_ipconfig *ipconfig);
-
 #ifdef __cplusplus
 }
 #endif
index bab77a7..284cc7a 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
@@ -68,6 +68,16 @@ struct connman_debug_desc {
  * Simple macro around connman_debug() which also include the function
  * name it is called in.
  */
+#if defined TIZEN_EXT
+#define DBG(fmt, arg...) do { \
+       static struct connman_debug_desc __connman_debug_desc \
+       __attribute__((used, section("__debug"), aligned(8))) = { \
+               .file = __FILE__, .flags = CONNMAN_DEBUG_FLAG_DEFAULT, \
+       }; \
+       if (__connman_debug_desc.flags & CONNMAN_DEBUG_FLAG_PRINT) \
+               connman_debug("%s " fmt, __FUNCTION__ , ## arg); \
+} while (0)
+#else
 #define DBG(fmt, arg...) do { \
        static struct connman_debug_desc __connman_debug_desc \
        __attribute__((used, section("__debug"), aligned(8))) = { \
@@ -77,6 +87,7 @@ struct connman_debug_desc {
                connman_debug("%s:%s() " fmt, \
                                        __FILE__, __FUNCTION__ , ## arg); \
 } while (0)
+#endif
 
 #ifdef __cplusplus
 }
index a69745a..b361c08 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
@@ -40,7 +40,6 @@ enum connman_network_type {
        CONNMAN_NETWORK_TYPE_UNKNOWN       = 0,
        CONNMAN_NETWORK_TYPE_ETHERNET      = 1,
        CONNMAN_NETWORK_TYPE_WIFI          = 2,
-       CONNMAN_NETWORK_TYPE_WIMAX         = 3,
        CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN = 8,
        CONNMAN_NETWORK_TYPE_BLUETOOTH_DUN = 9,
        CONNMAN_NETWORK_TYPE_CELLULAR      = 10,
@@ -53,6 +52,9 @@ enum connman_network_error {
        CONNMAN_NETWORK_ERROR_CONFIGURE_FAIL  = 2,
        CONNMAN_NETWORK_ERROR_INVALID_KEY     = 3,
        CONNMAN_NETWORK_ERROR_CONNECT_FAIL    = 4,
+#if defined TIZEN_EXT
+       CONNMAN_NETWORK_ERROR_DHCP_FAIL       = 5,
+#endif
 };
 
 #define CONNMAN_NETWORK_PRIORITY_LOW      -100
@@ -63,8 +65,18 @@ struct connman_network;
 
 struct connman_network *connman_network_create(const char *identifier,
                                        enum connman_network_type type);
-struct connman_network *connman_network_ref(struct connman_network *network);
-void connman_network_unref(struct connman_network *network);
+
+#define connman_network_ref(network) \
+       connman_network_ref_debug(network, __FILE__, __LINE__, __func__)
+
+#define connman_network_unref(network) \
+       connman_network_unref_debug(network, __FILE__, __LINE__, __func__)
+
+struct connman_network *
+connman_network_ref_debug(struct connman_network *network,
+                       const char *file, int line, const char *caller);
+void connman_network_unref_debug(struct connman_network *network,
+                       const char *file, int line, const char *caller);
 
 enum connman_network_type connman_network_get_type(struct connman_network *network);
 const char *connman_network_get_identifier(struct connman_network *network);
@@ -84,6 +96,9 @@ int connman_network_set_associating(struct connman_network *network,
                                                connman_bool_t associating);
 void connman_network_set_error(struct connman_network *network,
                                        enum connman_network_error error);
+#if defined TIZEN_EXT
+void connman_network_clear_associating(struct connman_network *network);
+#endif
 void connman_network_clear_error(struct connman_network *network);
 int connman_network_set_connected(struct connman_network *network,
                                                connman_bool_t connected);
@@ -91,6 +106,10 @@ connman_bool_t connman_network_get_connected(struct connman_network *network);
 
 connman_bool_t connman_network_get_associating(struct connman_network *network);
 
+void connman_network_clear_hidden(void *user_data);
+int connman_network_connect_hidden(struct connman_network *network,
+                       char *identity, char* passphrase, void *user_data);
+
 void connman_network_set_ipv4_method(struct connman_network *network,
                                        enum connman_ipconfig_method method);
 void connman_network_set_ipv6_method(struct connman_network *network,
@@ -103,11 +122,6 @@ int connman_network_set_domain(struct connman_network *network,
                                     const char *domain);
 #if defined TIZEN_EXT
 /*
- * Description: Telephony plug-in requires manual PROXY setting function
- */
-int connman_network_set_proxy(struct connman_network *network,
-                               const char *proxies);
-/*
  * Description: Network client requires additional wifi specific info
  */
 int connman_network_set_bssid(struct connman_network *network,
@@ -122,7 +136,9 @@ int connman_network_set_enc_mode(struct connman_network *network,
                                const char *encryption_mode);
 const char *connman_network_get_enc_mode(struct connman_network *network);
 
-const char *connman_network_get_ifname(struct connman_network *network);
+int connman_network_set_is_hs20AP(struct connman_network *network,
+                               unsigned int isHS20AP);
+unsigned int connman_network_get_is_hs20AP(struct connman_network *network);
 #endif
 
 int connman_network_set_name(struct connman_network *network,
@@ -130,8 +146,6 @@ int connman_network_set_name(struct connman_network *network,
 int connman_network_set_strength(struct connman_network *network,
                                                connman_uint8_t strength);
 connman_uint8_t connman_network_get_strength(struct connman_network *network);
-int connman_network_set_roaming(struct connman_network *network,
-                                               connman_bool_t roaming);
 int connman_network_set_frequency(struct connman_network *network,
                                        connman_uint16_t frequency);
 connman_uint16_t connman_network_get_frequency(struct connman_network *network);
@@ -167,6 +181,9 @@ struct connman_network_driver {
        void (*remove) (struct connman_network *network);
        int (*connect) (struct connman_network *network);
        int (*disconnect) (struct connman_network *network);
+#if defined TIZEN_EXT
+       int (*merge) (struct connman_network *network);
+#endif
 };
 
 int connman_network_driver_register(struct connman_network_driver *driver);
index 74c3d6b..4412717 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
index 66f5bfb..5e97ed4 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
index 692a4e5..8d2bb08 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
index 8e9f859..cd059a8 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
@@ -59,8 +59,19 @@ enum connman_provider_error {
 struct connman_provider;
 struct connman_ipaddress;
 
-struct connman_provider *connman_provider_ref(struct connman_provider *provider);
-void connman_provider_unref(struct connman_provider *provider);
+#define connman_provider_ref(provider) \
+       connman_provider_ref_debug(provider, __FILE__, __LINE__, __func__)
+
+#define connman_provider_unref(provider) \
+       connman_provider_unref_debug(provider, __FILE__, __LINE__, __func__)
+
+struct connman_provider *
+connman_provider_ref_debug(struct connman_provider *provider,
+                       const char *file, int line, const char *caller);
+void connman_provider_unref_debug(struct connman_provider *provider,
+                       const char *file, int line, const char *caller);
+
+int connman_provider_disconnect(struct connman_provider *provider);
 
 int connman_provider_set_string(struct connman_provider *provider,
                                        const char *key, const char *value);
index d4b2148..7842f65 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
index c7c6b53..57cb287 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
@@ -32,16 +32,13 @@ extern "C" {
  * @short_description: Functions for registering resolver modules
  */
 
-int connman_resolver_append(const char *interface, const char *domain,
+int connman_resolver_append(int index, const char *domain,
                                                        const char *server);
-int connman_resolver_append_lifetime(const char *interface, const char *domain,
+int connman_resolver_append_lifetime(int index, const char *domain,
                                     const char *server, unsigned int lifetime);
-int connman_resolver_remove(const char *interface, const char *domain,
+int connman_resolver_remove(int index, const char *domain,
                                                        const char *server);
-int connman_resolver_remove_all(const char *interface);
-
-int connman_resolver_append_public_server(const char *server);
-int connman_resolver_remove_public_server(const char *server);
+int connman_resolver_remove_all(int index);
 
 void connman_resolver_flush(void);
 
index a3789c9..aa70f4d 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
index 08d362a..37de6b8 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
@@ -39,12 +39,11 @@ enum connman_service_type {
        CONNMAN_SERVICE_TYPE_SYSTEM    = 1,
        CONNMAN_SERVICE_TYPE_ETHERNET  = 2,
        CONNMAN_SERVICE_TYPE_WIFI      = 3,
-       CONNMAN_SERVICE_TYPE_WIMAX     = 4,
-       CONNMAN_SERVICE_TYPE_BLUETOOTH = 5,
-       CONNMAN_SERVICE_TYPE_CELLULAR  = 6,
-       CONNMAN_SERVICE_TYPE_GPS       = 7,
-       CONNMAN_SERVICE_TYPE_VPN       = 8,
-       CONNMAN_SERVICE_TYPE_GADGET    = 9,
+       CONNMAN_SERVICE_TYPE_BLUETOOTH = 4,
+       CONNMAN_SERVICE_TYPE_CELLULAR  = 5,
+       CONNMAN_SERVICE_TYPE_GPS       = 6,
+       CONNMAN_SERVICE_TYPE_VPN       = 7,
+       CONNMAN_SERVICE_TYPE_GADGET    = 8,
 };
 
 enum connman_service_security {
@@ -87,22 +86,38 @@ enum connman_service_proxy_method {
 };
 
 struct connman_service;
+struct connman_network;
 
 struct connman_service *connman_service_create(void);
-struct connman_service *connman_service_ref(struct connman_service *service);
-void connman_service_unref(struct connman_service *service);
+
+#define connman_service_ref(service) \
+       connman_service_ref_debug(service, __FILE__, __LINE__, __func__)
+
+#define connman_service_unref(service) \
+       connman_service_unref_debug(service, __FILE__, __LINE__, __func__)
+
+struct connman_service *
+connman_service_ref_debug(struct connman_service *service,
+                       const char *file, int line, const char *caller);
+void connman_service_unref_debug(struct connman_service *service,
+                       const char *file, int line, const char *caller);
 
 enum connman_service_type connman_service_get_type(struct connman_service *service);
 char *connman_service_get_interface(struct connman_service *service);
 
 const char *connman_service_get_domainname(struct connman_service *service);
 char **connman_service_get_nameservers(struct connman_service *service);
+char **connman_service_get_timeservers_config(struct connman_service *service);
+char **connman_service_get_timeservers(struct connman_service *service);
 void connman_service_set_proxy_method(struct connman_service *service, enum connman_service_proxy_method method);
 enum connman_service_proxy_method connman_service_get_proxy_method(struct connman_service *service);
 char **connman_service_get_proxy_servers(struct connman_service *service);
 char **connman_service_get_proxy_excludes(struct connman_service *service);
 const char *connman_service_get_proxy_url(struct connman_service *service);
 const char *connman_service_get_proxy_autoconfig(struct connman_service *service);
+connman_bool_t connman_service_get_favorite(struct connman_service *service);
+
+struct connman_service *connman_service_lookup_from_network(struct connman_network *network);
 
 #if defined TIZEN_EXT
 /*
@@ -114,18 +129,31 @@ const char *connman_service_get_proxy_autoconfig(struct connman_service *service
 /*
  * Increase reference count of user-initiated packet data network connection
  */
-void connman_service_user_initiated_pdn_connection_ref(struct connman_service *service);
+void connman_service_user_pdn_connection_ref(struct connman_service *service);
+
+/*
+ * Decrease reference count of user initiated packet data network connection
+ * and return TRUE if counter is zero.
+ */
+connman_bool_t connman_service_user_pdn_connection_unref_and_test(
+                                       struct connman_service *service);
 
 /*
- * Decrease reference count of user initiated packet data network connection and return TRUE if counter is zero.
+ * Test reference count of user initiated packet data network connection
+ * and return TRUE if counter is zero. No impact to reference count
  */
-connman_bool_t connman_service_user_initiated_pdn_connection_unref_and_test(struct connman_service *service);
+connman_bool_t connman_service_is_no_ref_user_pdn_connection(
+                                       struct connman_service *service);
+#endif
+
+#if defined TIZEN_EXT
+struct connman_service *connman_service_get_default_connection(void);
 
 /*
- * Test reference count of user initiated packet data network connection and return TRUE if counter is zero.
- * No impact to reference count
+ * Description: telephony plug-in requires manual PROXY setting
  */
-connman_bool_t connman_service_is_no_ref_user_initiated_pdn_connection(struct connman_service *service);
+int connman_service_set_proxy(struct connman_service *service,
+                                       const char *proxy, gboolean active);
 #endif
 
 #ifdef __cplusplus
index 78adad7..e867a78 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2011  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
 #ifndef __CONNMAN_SETTING_H
 #define __CONNMAN_SETTING_H
 
+#include <connman/types.h>
+
 #ifdef __cplusplus
 extern "C" {
 #endif
 
 connman_bool_t connman_setting_get_bool(const char *key);
+char **connman_setting_get_string_list(const char *key);
+unsigned int *connman_setting_get_uint_list(const char *key);
+
+unsigned int connman_timeout_input_request(void);
+unsigned int connman_timeout_browser_launch(void);
 
 #ifdef __cplusplus
 }
index c108511..4c23a14 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2011  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
index d93df2d..9977d63 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
index 39fcbbb..f3fab20 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
index c5019ed..48ea194 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
 extern "C" {
 #endif
 
-#define CONNMAN_TIMESERVER_PRIORITY_LOW      -100
-#define CONNMAN_TIMESERVER_PRIORITY_DEFAULT     0
-#define CONNMAN_TIMESERVER_PRIORITY_HIGH      100
-
-/**
- * SECTION:timeserver
- * @title: timeserver premitives
- * @short_description: Functions for handling time servers (including NTP)
- */
-
-int connman_timeserver_append(const char *server);
-int connman_timeserver_remove(const char *server);
-void connman_timeserver_sync(void);
-
-struct connman_timeserver_driver {
-       const char *name;
-       int priority;
-       int (*append) (const char *server);
-       int (*remove) (const char *server);
-       void (*sync) (void);
-};
-
-int connman_timeserver_driver_register(struct connman_timeserver_driver *driver);
-void connman_timeserver_driver_unregister(struct connman_timeserver_driver *driver);
+int __connman_timeserver_system_set(char **server);
 
 #ifdef __cplusplus
 }
index 70aff9e..0f671ec 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
index a358997..b8165f6 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
index 38d8748..d3d6dd3 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
diff --git a/packaging/connman.changes b/packaging/connman.changes
deleted file mode 100644 (file)
index 24dd444..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-* Fri Oct 05 2012 prajwal.karur.mohan@intel.com <prajwal.karur.mohan@intel.com> tizen_2.0_build@5ed7094
-- Adding connman-test subpackage
-
index cfe210b..4a3bb7f 100644 (file)
-#sbs-git:pkgs/c/connman connman 0.78.4
-
-Name:       connman
-Summary:    Connection Manager
-Version:    0.78.4_90
-Release:    1
-Group:      System/Network
-License:    GNU General Public License version 2
-URL:        http://connman.net
-Source0:    %{name}-%{version}.tar.gz
-BuildRequires:  pkgconfig(glib-2.0)
-BuildRequires:  pkgconfig(dbus-1)
-BuildRequires:  pkgconfig(xtables)
-BuildRequires:  pkgconfig(libiptc)
+Name:          connman
+Summary:       Connection Manager
+Version:       1.3.313
+Release:       1
+Group:         System/Network
+License:       GPL-2.0+
+URL:           http://connman.net
+Source0:       %{name}-%{version}.tar.gz
+BuildRequires: pkgconfig(dbus-1)
+BuildRequires: pkgconfig(glib-2.0)
+BuildRequires: pkgconfig(xtables)
+BuildRequires: pkgconfig(libsmack)
+BuildRequires: model-build-features
+Requires:              systemd
+Requires(post):                systemd
+Requires(preun):       systemd
+Requires(postun):      systemd
 
 %description
 Connection Manager provides a daemon for managing Internet connections
 within embedded devices running the Linux operating system.
 
-%package test
-Summary:        Test Scripts for Connection Manager
-Group:          Development/Tools
-Requires:       %{name} = %{version}
-Requires:       dbus-python
-Requires:       pygobject
-Requires:       python-xml
-
-%description test
-Scripts for testing Connman and its functionality
-
 %prep
 %setup -q
 
 
 %build
+CFLAGS+=" -Wall -Werror -O2 -D_FORTIFY_SOURCE=2"
 
-./autogen.sh
+./bootstrap
 
-./configure --prefix=/usr \
+%configure --prefix=/usr \
+            --sysconfdir=/etc \
             --localstatedir=/var \
             --enable-tizen-ext \
-            --enable-test \
+            --enable-tizen-rtc-timer \
+            --enable-threads \
+            --enable-ethernet \
+            --enable-wifi=builtin \
+            --enable-bluetooth \
+            --enable-telephony=builtin \
+            --enable-loopback \
+            --disable-client \
+            --disable-ofono \
+            --disable-tools \
+            --disable-wispr \
+            --disable-linklocaladdr \
+            --with-systemdunitdir=%{_libdir}/systemd/system \
+            --enable-pie
+
+make %{?_smp_mflags}
 
 
-make %{?jobs:-j%jobs}
-
 %install
-rm -rf %{buildroot}
 %make_install
 
-mkdir -p %{buildroot}/var/lib/connman
-cp resources/var/lib/connman/settings %{buildroot}/var/lib/connman/settings
-mkdir -p %{buildroot}/usr/share/dbus-1/services
-cp resources/usr/share/dbus-1/services/net.connman.service %{buildroot}/usr/share/dbus-1/services/net.connman.service
-mkdir -p %{buildroot}/usr/etc/connman
-cp src/main.conf %{buildroot}/usr/etc/connman/main.conf
-mkdir -p %{buildroot}/etc/rc.d/init.d
-cp resources/etc/rc.d/init.d/connman %{buildroot}/etc/rc.d/init.d/connman
-mkdir -p %{buildroot}/etc/rc.d/rc3.d
-ln -s ../init.d/connman %{buildroot}/etc/rc.d/rc3.d/S61connman
-mkdir -p %{buildroot}/etc/rc.d/rc5.d
-ln -s ../init.d/connman %{buildroot}/etc/rc.d/rc5.d/S61connman
+#Systemd service file
+mkdir -p %{buildroot}%{_libdir}/systemd/system/
+cp src/connman.service %{buildroot}%{_libdir}/systemd/system/connman.service
+mkdir -p %{buildroot}%{_libdir}/systemd/system/multi-user.target.wants/
+ln -s ../connman.service %{buildroot}%{_libdir}/systemd/system/multi-user.target.wants/connman.service
+
+mkdir -p %{buildroot}%{_localstatedir}/lib/connman
+cp resources/var/lib/connman/settings %{buildroot}%{_localstatedir}/lib/connman/settings
+mkdir -p %{buildroot}%{_datadir}/dbus-1/services
+cp resources/usr/share/dbus-1/services/net.connman.service %{buildroot}%{_datadir}/dbus-1/services/net.connman.service
+mkdir -p %{buildroot}%{_sysconfdir}/connman
+cp src/main.conf %{buildroot}%{_sysconfdir}/connman/main.conf
 
-rm -rf %{buildroot}/usr/include/
-rm -rf %{buildroot}/usr/lib/pkgconfig/
-rm %{buildroot}/etc/dbus-1/system.d/*.conf
+rm -rf %{buildroot}%{_includedir}
+rm -rf %{buildroot}%{_libdir}/pkgconfig/*.pc
+rm %{buildroot}%{_sysconfdir}/dbus-1/system.d/*.conf
 
-mkdir -p %{buildroot}/usr/etc/dbus-1/system.d/
-cp src/connman.conf %{buildroot}/usr/etc/dbus-1/system.d/
+mkdir -p %{buildroot}%{_sbindir}/
+cp resources/usr/sbin/connman.service %{buildroot}%{_sbindir}/connman.service
 
+#DBus DAC (manifest enables DBus SMACK)
+#mkdir -p %{buildroot}%{_sysconfdir}/dbus-1/system.d/
+#cp src/connman.conf %{buildroot}%{_sysconfdir}/dbus-1/system.d/
+
+#License
+mkdir -p %{buildroot}%{_datadir}/license
+cp COPYING %{buildroot}%{_datadir}/license/connman
 
 %post
-#Resource
-chmod 600 /var/lib/connman/settings
+#systemctl daemon-reload
+#systemctl restart connman.service
 
+%preun
+#systemctl stop connman.service
 
-%files
-%defattr(-,root,root,-)
-#%doc AUTHORS COPYING INSTALL ChangeLog NEWS README
-%{_sbindir}/*
-%{_var}/lib/connman/settings
-%{_libdir}/connman/plugins/*.so
-%{_datadir}/dbus-1/services/*
-%{_prefix}/etc/dbus-1/system.d/*
-%{_prefix}/etc/connman/main.conf
-%{_sysconfdir}/rc.d/init.d/connman
-%{_sysconfdir}/rc.d/rc3.d/S61connman
-%{_sysconfdir}/rc.d/rc5.d/S61connman
-
-%files test
-%{_libdir}/%{name}/test/*
+%postun
+#systemctl daemon-reload
 
+
+%files
+%manifest connman.manifest
+%attr(500,root,root) %{_sbindir}/*
+%attr(600,root,root) %{_localstatedir}/lib/connman/settings
+#All of builtin plugins
+#%{_libdir}/connman/plugins/*.so
+%attr(644,root,root) %{_datadir}/dbus-1/services/*
+#DBus DAC
+#%attr(644,root,root) %{_sysconfdir}/dbus-1/system.d/*
+%attr(644,root,root) %{_sysconfdir}/connman/main.conf
+%attr(644,root,root) %{_libdir}/systemd/system/connman.service
+%attr(644,root,root) %{_libdir}/systemd/system/multi-user.target.wants/connman.service
+%{_datadir}/license/connman
index 1c8d3a0..a9b6530 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2013  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
 #include <config.h>
 #endif
 
-#include <stdio.h>
 #include <errno.h>
-#include <stdlib.h>
 #include <string.h>
-#include <netinet/ether.h>
 
-#include <gdbus.h>
+#if defined TIZEN_EXT
+#include <glib.h>
+#include <stdbool.h>
+#endif
 
 #define CONNMAN_API_SUBJECT_TO_CHANGE
 #include <connman/plugin.h>
+#include <connman/dbus.h>
 #include <connman/technology.h>
 #include <connman/device.h>
 #include <connman/inet.h>
-#include <connman/dbus.h>
-#include <connman/log.h>
-
-#define BLUEZ_SERVICE                  "org.bluez"
-#define BLUEZ_MANAGER_INTERFACE                BLUEZ_SERVICE ".Manager"
-#define BLUEZ_ADAPTER_INTERFACE                BLUEZ_SERVICE ".Adapter"
-#define BLUEZ_DEVICE_INTERFACE         BLUEZ_SERVICE ".Device"
-#define BLUEZ_NETWORK_INTERFACE                BLUEZ_SERVICE ".Network"
-#define BLUEZ_NETWORK_SERVER           BLUEZ_SERVICE ".NetworkServer"
-
-#define LIST_ADAPTERS                  "ListAdapters"
-#define ADAPTER_ADDED                  "AdapterAdded"
-#define ADAPTER_REMOVED                        "AdapterRemoved"
-#define DEVICE_REMOVED                 "DeviceRemoved"
-
-#define PROPERTY_CHANGED               "PropertyChanged"
-#define GET_PROPERTIES                 "GetProperties"
-#define SET_PROPERTY                   "SetProperty"
+#include <gdbus.h>
 
-#define CONNECT                                "Connect"
-#define DISCONNECT                     "Disconnect"
+#define BLUEZ_SERVICE                   "org.bluez"
+#define BLUEZ_PATH                      "/org/bluez"
+#define BLUETOOTH_PAN_NAP               "00001116-0000-1000-8000-00805f9b34fb"
 
-#define REGISTER                       "Register"
-#define UNREGISTER                     "Unregister"
+#define BLUETOOTH_ADDR_LEN              6
 
-#define UUID_NAP       "00001116-0000-1000-8000-00805f9b34fb"
+static DBusConnection *connection;
+static GDBusClient *client;
+static GHashTable *devices;
+static GHashTable *networks;
+static bool bluetooth_tethering;
 
-#define TIMEOUT 5000
+struct bluetooth_pan {
+       struct connman_network *network;
+       GDBusProxy *btdevice_proxy;
+       GDBusProxy *btnetwork_proxy;
+};
 
-static DBusConnection *connection;
+static void address2ident(const char *address, char *ident)
+{
+       int i;
 
-static GHashTable *bluetooth_devices = NULL;
-static GHashTable *bluetooth_networks = NULL;
+       for (i = 0; i < BLUETOOTH_ADDR_LEN; i++) {
+               ident[i * 2] = address[i * 3];
+               ident[i * 2 + 1] = address[i * 3 + 1];
+       }
+       ident[BLUETOOTH_ADDR_LEN * 2] = '\0';
+}
 
-static int pan_probe(struct connman_network *network)
+static const char *proxy_get_string(GDBusProxy *proxy, const char *property)
 {
-       DBG("network %p", network);
+       DBusMessageIter iter;
+       const char *str;
 
-       return 0;
+       if (!g_dbus_proxy_get_property(proxy, property, &iter))
+               return NULL;
+       dbus_message_iter_get_basic(&iter, &str);
+       return str;
 }
 
-static void pan_remove(struct connman_network *network)
+static bool proxy_get_bool(GDBusProxy *proxy, const char *property)
 {
-       DBG("network %p", network);
+       DBusMessageIter iter;
+       dbus_bool_t value;
+
+       if (!g_dbus_proxy_get_property(proxy, property, &iter))
+               return false;
+       dbus_message_iter_get_basic(&iter, &value);
+       return value;
 }
 
-static void connect_reply(DBusPendingCall *call, void *user_data)
+static bool proxy_get_nap(GDBusProxy *proxy)
 {
-       struct connman_network *network = user_data;
-       DBusMessage *reply;
-       DBusError error;
-       const char *interface = NULL;
-       int index;
+        DBusMessageIter iter, value;
 
-       DBG("network %p", network);
+       if (!proxy)
+               return false;
 
-       reply = dbus_pending_call_steal_reply(call);
+        if (!g_dbus_proxy_get_property(proxy, "UUIDs", &iter))
+                return false;
 
-       dbus_error_init(&error);
+        dbus_message_iter_recurse(&iter, &value);
+        while (dbus_message_iter_get_arg_type(&value) == DBUS_TYPE_STRING) {
+                const char *uuid;
 
-       if (dbus_set_error_from_message(&error, reply) == TRUE) {
-               connman_error("%s", error.message);
-               dbus_error_free(&error);
+                dbus_message_iter_get_basic(&value, &uuid);
+                if (strcmp(uuid, BLUETOOTH_PAN_NAP) == 0)
+                        return true;
 
-               goto err;
-       }
+                dbus_message_iter_next(&value);
+        }
 
-       if (dbus_message_get_args(reply, &error,
-                                       DBUS_TYPE_STRING, &interface,
-                                               DBUS_TYPE_INVALID) == FALSE) {
-               if (dbus_error_is_set(&error) == TRUE) {
-                       connman_error("%s", error.message);
-                       dbus_error_free(&error);
-               } else
-                       connman_error("Wrong arguments for connect");
-               goto err;
-       }
+        return false;
+}
 
-       if (interface == NULL)
-               goto err;
+static int bluetooth_pan_probe(struct connman_network *network)
+{
+       GHashTableIter iter;
+       gpointer key, value;
+
+       DBG("network %p", network);
 
-       DBG("interface %s", interface);
+       g_hash_table_iter_init(&iter, networks);
 
-       index = connman_inet_ifindex(interface);
+       while (g_hash_table_iter_next(&iter, &key, &value)) {
+               struct bluetooth_pan *pan = value;
 
-       connman_network_set_index(network, index);
+               if (network == pan->network)
+                       return 0;
+       }
 
-       connman_network_set_connected(network, TRUE);
+       return -EOPNOTSUPP;
+}
 
-       dbus_message_unref(reply);
+static void pan_remove_nap(struct bluetooth_pan *pan)
+{
+       struct connman_device *device;
+       struct connman_network *network = pan->network;
 
-       dbus_pending_call_unref(call);
+       DBG("network %p pan %p", pan->network, pan);
 
-       return;
-err:
+       if (!network)
+               return;
 
-       connman_network_set_connected(network, FALSE);
+       pan->network = NULL;
+       connman_network_set_data(network, NULL);
 
-       dbus_message_unref(reply);
+       device = connman_network_get_device(network);
+       if (device)
+               connman_device_remove_network(device, network);
 
-       dbus_pending_call_unref(call);
+       connman_network_unref(network);
 }
 
-static int pan_connect(struct connman_network *network)
+static void bluetooth_pan_remove(struct connman_network *network)
 {
-       const char *path = connman_network_get_string(network, "Path");
-       const char *uuid = "nap";
-       DBusMessage *message;
-       DBusPendingCall *call;
+       struct bluetooth_pan *pan = connman_network_get_data(network);
 
-       DBG("network %p", network);
+       DBG("network %p pan %p", network, pan);
 
-       if (path == NULL)
-               return -EINVAL;
+       connman_network_set_data(network, NULL);
 
-       message = dbus_message_new_method_call(BLUEZ_SERVICE, path,
-                                       BLUEZ_NETWORK_INTERFACE, CONNECT);
-       if (message == NULL)
-               return -ENOMEM;
-
-       dbus_message_set_auto_start(message, FALSE);
+       if (pan)
+               pan_remove_nap(pan);
+}
 
-       dbus_message_append_args(message, DBUS_TYPE_STRING, &uuid,
-                                                       DBUS_TYPE_INVALID);
+static bool pan_connect(struct bluetooth_pan *pan,
+               const char *iface)
+{
+       int index;
 
-       if (dbus_connection_send_with_reply(connection, message,
-                                       &call, TIMEOUT * 10) == FALSE) {
-               connman_error("Failed to connect service");
-               dbus_message_unref(message);
-               return -EINVAL;
+       if (!iface) {
+               if (!proxy_get_bool(pan->btnetwork_proxy, "Connected"))
+                       return false;
+               iface = proxy_get_string(pan->btnetwork_proxy, "Interface");
        }
 
-       if (call == NULL) {
-               connman_error("D-Bus connection not available");
-               dbus_message_unref(message);
-               return -EINVAL;
-       }
+       if (!iface)
+               return false;
 
-       dbus_pending_call_set_notify(call, connect_reply, network, NULL);
+       index = connman_inet_ifindex(iface);
+       if (index < 0) {
+               DBG("network %p invalid index %d", pan->network, index);
+               return false;
+       }
 
-       dbus_message_unref(message);
+#if defined TIZEN_EXT
+       if (pan->network) {
+#endif
+       connman_network_set_index(pan->network, index);
+       connman_network_set_connected(pan->network, true);
+#if defined TIZEN_EXT
+       }
+#endif
 
-       return -EINPROGRESS;
+       return true;
 }
 
-static void disconnect_reply(DBusPendingCall *call, void *user_data)
+static void pan_connect_cb(DBusMessage *message, void *user_data)
 {
-       struct connman_network *network = user_data;
-       DBusMessage *reply;
-       DBusError error;
-
-       DBG("network %p", network);
+       const char *path = user_data;
+       const char *iface = NULL;
+       struct bluetooth_pan *pan;
+       DBusMessageIter iter;
 
-       reply = dbus_pending_call_steal_reply(call);
+       pan = g_hash_table_lookup(networks, path);
+       if (!pan) {
+               DBG("network already removed");
+               return;
+       }
 
-       dbus_error_init(&error);
+       if (dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_ERROR) {
+               const char *dbus_error = dbus_message_get_error_name(message);
 
-       if (dbus_set_error_from_message(&error, reply) == TRUE) {
-               connman_error("%s", error.message);
-               dbus_error_free(&error);
-               goto done;
-       }
+               DBG("network %p %s", pan->network, dbus_error);
 
-       if (dbus_message_get_args(reply, &error, DBUS_TYPE_INVALID) == FALSE) {
-               if (dbus_error_is_set(&error) == TRUE) {
-                       connman_error("%s", error.message);
-                       dbus_error_free(&error);
-               } else
-                       connman_error("Wrong arguments for disconnect");
-               goto done;
+               if (strcmp(dbus_error,
+                               "org.bluez.Error.AlreadyConnected") != 0) {
+                       connman_network_set_error(pan->network,
+                               CONNMAN_NETWORK_ERROR_ASSOCIATE_FAIL);
+                       return;
+               }
+       } else {
+               if (dbus_message_iter_init(message, &iter) &&
+                               dbus_message_iter_get_arg_type(&iter) ==
+                               DBUS_TYPE_STRING)
+                       dbus_message_iter_get_basic(&iter, &iface);
        }
 
-       connman_network_set_connected(network, FALSE);
+       DBG("network %p interface %s", pan->network, iface);
 
-done:
-       dbus_message_unref(reply);
+       pan_connect(pan, iface);
+}
 
-       dbus_pending_call_unref(call);
+static void pan_connect_append(DBusMessageIter *iter,
+               void *user_data)
+{
+       const char *role = BLUETOOTH_PAN_NAP;
 
-       connman_network_unref(network);
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &role);
 }
 
-static int pan_disconnect(struct connman_network *network)
+static int bluetooth_pan_connect(struct connman_network *network)
 {
-       const char *path = connman_network_get_string(network, "Path");
-       DBusMessage *message;
-       DBusPendingCall *call;
+       struct bluetooth_pan *pan = connman_network_get_data(network);
+       const char *path;
 
        DBG("network %p", network);
 
-       if (path == NULL)
+       if (!pan)
                return -EINVAL;
 
-       message = dbus_message_new_method_call(BLUEZ_SERVICE, path,
-                                       BLUEZ_NETWORK_INTERFACE, DISCONNECT);
-       if (message == NULL)
-               return -ENOMEM;
+       path = g_dbus_proxy_get_path(pan->btnetwork_proxy);
 
-       dbus_message_set_auto_start(message, FALSE);
+       if (!g_dbus_proxy_method_call(pan->btnetwork_proxy, "Connect",
+                       pan_connect_append, pan_connect_cb,
+                       g_strdup(path), g_free))
+               return -EIO;
 
-       dbus_message_append_args(message, DBUS_TYPE_INVALID);
+#if defined TIZEN_EXT
+       if (pan->network)
+#endif
+       connman_network_set_associating(pan->network, true);
 
-       if (dbus_connection_send_with_reply(connection, message,
-                                               &call, TIMEOUT) == FALSE) {
-               connman_error("Failed to disconnect service");
-               dbus_message_unref(message);
-               return -EINVAL;
-       }
+       return -EINPROGRESS;
+}
 
-       if (call == NULL) {
-               connman_error("D-Bus connection not available");
-               dbus_message_unref(message);
-               return -EINVAL;
-       }
+static void pan_disconnect_cb(DBusMessage *message, void *user_data)
+{
+       const char *path = user_data;
+       struct bluetooth_pan *pan;
 
-       connman_network_ref(network);
+       pan = g_hash_table_lookup(networks, path);
+       if (!pan) {
+               DBG("network already removed");
+               return;
+       }
 
-       connman_network_set_associating(network, FALSE);
+       if (dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_ERROR) {
+               const char *dbus_error = dbus_message_get_error_name(message);
 
-       dbus_pending_call_set_notify(call, disconnect_reply, network, NULL);
+               DBG("network %p %s", pan->network, dbus_error);
+       }
 
-       dbus_message_unref(message);
+       DBG("network %p", pan->network);
 
-       return 0;
+#if defined TIZEN_EXT
+       if (pan->network)
+#endif
+       connman_network_set_connected(pan->network, false);
 }
 
-static struct connman_network_driver pan_driver = {
-       .name           = "bluetooth-pan",
-       .type           = CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN,
-       .probe          = pan_probe,
-       .remove         = pan_remove,
-       .connect        = pan_connect,
-       .disconnect     = pan_disconnect,
-};
-
-static gboolean network_changed(DBusConnection *connection,
-                               DBusMessage *message, void *user_data)
+static int bluetooth_pan_disconnect(struct connman_network *network)
 {
-       const char *path = dbus_message_get_path(message);
-       struct connman_network *network;
-       DBusMessageIter iter, value;
-       const char *key;
-
-       DBG("path %s", path);
-
-       network = g_hash_table_lookup(bluetooth_networks, path);
-       if (network == NULL)
-               return TRUE;
-
-       if (dbus_message_iter_init(message, &iter) == FALSE)
-               return TRUE;
-
-       dbus_message_iter_get_basic(&iter, &key);
+       struct bluetooth_pan *pan = connman_network_get_data(network);
+       const char *path;
 
-       dbus_message_iter_next(&iter);
-       dbus_message_iter_recurse(&iter, &value);
+       DBG("network %p", network);
 
-       if (g_str_equal(key, "Connected") == TRUE) {
-               dbus_bool_t connected;
+       if (!pan)
+               return -EINVAL;
 
-               dbus_message_iter_get_basic(&value, &connected);
+#if defined TIZEN_EXT
+       if (connman_network_get_associating(network) == TRUE)
+               connman_network_clear_associating(network);
+#endif
 
-               if (connected == TRUE)
-                       return TRUE;
+       path = g_dbus_proxy_get_path(pan->btnetwork_proxy);
 
-               connman_network_set_associating(network, FALSE);
-               connman_network_set_connected(network, FALSE);
-       }
+       if (!g_dbus_proxy_method_call(pan->btnetwork_proxy, "Disconnect",
+                       NULL, pan_disconnect_cb, g_strdup(path), g_free))
+               return -EIO;
 
-       return TRUE;
+       return -EINPROGRESS;
 }
 
-static void extract_properties(DBusMessage *reply, const char **parent,
-                                               const char **address,
-                                               const char **name,
-                                               const char **alias,
-                                               dbus_bool_t *powered,
-                                               dbus_bool_t *scanning,
-                                               DBusMessageIter *uuids,
-                                               DBusMessageIter *networks)
+static void btnetwork_property_change(GDBusProxy *proxy, const char *name,
+               DBusMessageIter *iter, void *user_data)
 {
-       DBusMessageIter array, dict;
+       struct bluetooth_pan *pan;
+       dbus_bool_t connected;
+       bool proxy_connected, network_connected;
 
-       if (dbus_message_iter_init(reply, &array) == FALSE)
+       if (strcmp(name, "Connected") != 0)
                return;
 
-       if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_ARRAY)
+       pan = g_hash_table_lookup(networks, g_dbus_proxy_get_path(proxy));
+       if (!pan || !pan->network)
                return;
 
-       dbus_message_iter_recurse(&array, &dict);
-
-       while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
-               DBusMessageIter entry, value;
-               const char *key;
-
-               dbus_message_iter_recurse(&dict, &entry);
-               dbus_message_iter_get_basic(&entry, &key);
-
-               dbus_message_iter_next(&entry);
-               dbus_message_iter_recurse(&entry, &value);
-
-               if (g_str_equal(key, "Adapter") == TRUE) {
-                       if (parent != NULL)
-                               dbus_message_iter_get_basic(&value, parent);
-               } else if (g_str_equal(key, "Address") == TRUE) {
-                       if (address != NULL)
-                               dbus_message_iter_get_basic(&value, address);
-               } else if (g_str_equal(key, "Name") == TRUE) {
-                       if (name != NULL)
-                               dbus_message_iter_get_basic(&value, name);
-               } else if (g_str_equal(key, "Alias") == TRUE) {
-                       if (alias != NULL)
-                               dbus_message_iter_get_basic(&value, alias);
-               } else if (g_str_equal(key, "Powered") == TRUE) {
-                       if (powered != NULL)
-                               dbus_message_iter_get_basic(&value, powered);
-               } else if (g_str_equal(key, "Discovering") == TRUE) {
-                       if (scanning != NULL)
-                               dbus_message_iter_get_basic(&value, scanning);
-               } else if (g_str_equal(key, "Devices") == TRUE) {
-                       if (networks != NULL)
-                               memcpy(networks, &value, sizeof(value));
-               } else if (g_str_equal(key, "UUIDs") == TRUE) {
-                       if (uuids != NULL)
-                               memcpy(uuids, &value, sizeof(value));
-               }
+       dbus_message_iter_get_basic(iter, &connected);
+       proxy_connected = connected;
 
-               dbus_message_iter_next(&dict);
-       }
-}
-
-static dbus_bool_t has_pan(DBusMessageIter *array)
-{
-       DBusMessageIter value;
-
-       if (dbus_message_iter_get_arg_type(array) != DBUS_TYPE_ARRAY)
-               return FALSE;
+       network_connected = connman_network_get_connected(pan->network);
 
-       dbus_message_iter_recurse(array, &value);
-
-       while (dbus_message_iter_get_arg_type(&value) == DBUS_TYPE_STRING) {
-               const char *uuid;
-
-               dbus_message_iter_get_basic(&value, &uuid);
-
-               if (g_strcmp0(uuid, UUID_NAP) == 0)
-                       return TRUE;
-
-               dbus_message_iter_next(&value);
-       }
+       DBG("network %p network connected %d proxy connected %d",
+                       pan->network, network_connected, proxy_connected);
 
-       return FALSE;
+       if (network_connected != proxy_connected)
+               connman_network_set_connected(pan->network, proxy_connected);
 }
 
-static void network_properties_reply(DBusPendingCall *call, void *user_data)
+static void pan_create_nap(struct bluetooth_pan *pan)
 {
-       char *path = user_data;
        struct connman_device *device;
-       struct connman_network *network;
-       DBusMessage *reply;
-       DBusMessageIter uuids;
-       const char *parent = NULL, *address = NULL, *name = NULL;
-       struct ether_addr addr;
-       char ident[13];
-
-       reply = dbus_pending_call_steal_reply(call);
 
-       extract_properties(reply, &parent, &address, NULL, &name,
-                                               NULL, NULL, &uuids, NULL);
+       if (!proxy_get_nap(pan->btdevice_proxy)) {
+               pan_remove_nap(pan);
+               return;
+       }
 
-       if (parent == NULL)
-               goto done;
+       device = g_hash_table_lookup(devices,
+                       proxy_get_string(pan->btdevice_proxy, "Adapter"));
 
-       device = g_hash_table_lookup(bluetooth_devices, parent);
-       if (device == NULL)
-               goto done;
+       if (!device || !connman_device_get_powered(device))
+               return;
 
-       if (address == NULL)
-               goto done;
+       if (!pan->network) {
+               const char *address;
+               char ident[BLUETOOTH_ADDR_LEN * 2 + 1];
+               const char *name, *path;
 
-       ether_aton_r(address, &addr);
+               address = proxy_get_string(pan->btdevice_proxy, "Address");
+               if (!address) {
+                       connman_warn("Bluetooth device address missing");
+                       return;
+               }
 
-       snprintf(ident, 13, "%02x%02x%02x%02x%02x%02x",
-                                               addr.ether_addr_octet[0],
-                                               addr.ether_addr_octet[1],
-                                               addr.ether_addr_octet[2],
-                                               addr.ether_addr_octet[3],
-                                               addr.ether_addr_octet[4],
-                                               addr.ether_addr_octet[5]);
+               address2ident(address, ident);
 
-       if (has_pan(&uuids) == FALSE)
-               goto done;
+               pan->network = connman_network_create(ident,
+                               CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN);
 
-       network = connman_device_get_network(device, ident);
-       if (network != NULL)
-               goto done;
+               name = proxy_get_string(pan->btdevice_proxy, "Alias");
+               path = g_dbus_proxy_get_path(pan->btnetwork_proxy);
 
-       network = connman_network_create(ident,
-                                       CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN);
-       if (network == NULL)
-               goto done;
+               DBG("network %p %s %s", pan->network, path, name);
 
-       connman_network_set_string(network, "Path", path);
+               if (!pan->network) {
+                       connman_warn("Bluetooth network %s creation failed",
+                                       path);
+                       return;
+               }
 
-       connman_network_set_name(network, name);
+               connman_network_set_data(pan->network, pan);
+               connman_network_set_name(pan->network, name);
+               connman_network_set_group(pan->network, ident);
+       }
 
-       connman_device_add_network(device, network);
+       connman_device_add_network(device, pan->network);
 
-       connman_network_set_group(network, ident);
+       if (pan_connect(pan, NULL))
+               DBG("network %p already connected", pan->network);
+}
 
-       g_hash_table_insert(bluetooth_networks, g_strdup(path), network);
+static void btdevice_property_change(GDBusProxy *proxy, const char *name,
+               DBusMessageIter *iter, void *user_data)
+{
+       struct bluetooth_pan *pan;
+       bool pan_nap = false;
 
-done:
-       dbus_message_unref(reply);
+       if (strcmp(name, "UUIDs") != 0)
+               return;
 
-       dbus_pending_call_unref(call);
-}
+       pan = g_hash_table_lookup(networks, g_dbus_proxy_get_path(proxy));
+       if (!pan)
+               return;
 
-static void add_network(struct connman_device *device, const char *path)
-{
-       DBusMessage *message;
-       DBusPendingCall *call;
+       if (pan->network &&
+                       connman_network_get_device(pan->network))
+               pan_nap = true;
 
-       DBG("path %s", path);
+       DBG("network %p network nap %d proxy nap %d", pan->network, pan_nap,
+                       proxy_get_nap(pan->btdevice_proxy));
 
-       message = dbus_message_new_method_call(BLUEZ_SERVICE, path,
-                               BLUEZ_DEVICE_INTERFACE, GET_PROPERTIES);
-       if (message == NULL)
+       if (proxy_get_nap(pan->btdevice_proxy) == pan_nap)
                return;
 
-       dbus_message_set_auto_start(message, FALSE);
+       pan_create_nap(pan);
+}
+
+static void pan_free(gpointer data)
+{
+       struct bluetooth_pan *pan = data;
 
-       if (dbus_connection_send_with_reply(connection, message,
-                                               &call, TIMEOUT) == FALSE) {
-               connman_error("Failed to get network properties for %s", path);
-               goto done;
+       if (pan->btnetwork_proxy) {
+               g_dbus_proxy_unref(pan->btnetwork_proxy);
+               pan->btnetwork_proxy = NULL;
        }
 
-       if (call == NULL) {
-               connman_error("D-Bus connection not available");
-               goto done;
+       if (pan->btdevice_proxy) {
+               g_dbus_proxy_unref(pan->btdevice_proxy);
+               pan->btdevice_proxy = NULL;
        }
 
-       dbus_pending_call_set_notify(call, network_properties_reply,
-                                               g_strdup(path), g_free);
+       pan_remove_nap(pan);
 
-done:
-       dbus_message_unref(message);
+       g_free(pan);
 }
 
-static void check_networks(struct connman_device *device,
-                                               DBusMessageIter *array)
+static void pan_create(GDBusProxy *network_proxy)
 {
-       DBusMessageIter value;
-
-       if (dbus_message_iter_get_arg_type(array) != DBUS_TYPE_ARRAY)
-               return;
+       const char *path = g_dbus_proxy_get_path(network_proxy);
+       struct bluetooth_pan *pan;
 
-       dbus_message_iter_recurse(array, &value);
+       pan = g_try_new0(struct bluetooth_pan, 1);
 
-       while (dbus_message_iter_get_arg_type(&value) == DBUS_TYPE_OBJECT_PATH) {
-               const char *path;
+       if (!pan) {
+               connman_error("Out of memory creating PAN NAP");
+               return;
+       }
 
-               dbus_message_iter_get_basic(&value, &path);
+       g_hash_table_replace(networks, g_strdup(path), pan);
 
-               add_network(device, path);
+       pan->btnetwork_proxy = g_dbus_proxy_ref(network_proxy);
+       pan->btdevice_proxy = g_dbus_proxy_new(client, path,
+                       "org.bluez.Device1");
 
-               dbus_message_iter_next(&value);
+       if (!pan->btdevice_proxy) {
+               connman_error("Cannot create BT PAN watcher %s", path);
+               g_hash_table_remove(networks, path);
+               return;
        }
-}
 
-static gboolean adapter_changed(DBusConnection *connection,
-                               DBusMessage *message, void *user_data)
-{
-       const char *path = dbus_message_get_path(message);
-       struct connman_device *device;
-       DBusMessageIter iter, value;
-       const char *key;
+       g_dbus_proxy_set_property_watch(pan->btnetwork_proxy,
+                       btnetwork_property_change, NULL);
+
+       g_dbus_proxy_set_property_watch(pan->btdevice_proxy,
+                       btdevice_property_change, NULL);
 
-       DBG("path %s", path);
+       DBG("pan %p %s nap %d", pan, path, proxy_get_nap(pan->btdevice_proxy));
 
-       device = g_hash_table_lookup(bluetooth_devices, path);
-       if (device == NULL)
-               return TRUE;
+       pan_create_nap(pan);
+}
 
-       if (dbus_message_iter_init(message, &iter) == FALSE)
-               return TRUE;
+static struct connman_network_driver network_driver = {
+       .name           = "bluetooth",
+       .type           = CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN,
+       .probe          = bluetooth_pan_probe,
+       .remove         = bluetooth_pan_remove,
+       .connect        = bluetooth_pan_connect,
+       .disconnect     = bluetooth_pan_disconnect,
+};
 
-       dbus_message_iter_get_basic(&iter, &key);
+static void enable_device(struct connman_device *device, const char *path)
+{
+       GHashTableIter iter;
+       gpointer key, value;
 
-       dbus_message_iter_next(&iter);
-       dbus_message_iter_recurse(&iter, &value);
+       DBG("device %p %s", device, path);
+       connman_device_set_powered(device, true);
 
-       if (g_str_equal(key, "Powered") == TRUE) {
-               dbus_bool_t val;
+       g_hash_table_iter_init(&iter, networks);
+       while (g_hash_table_iter_next(&iter, &key, &value)) {
+               struct bluetooth_pan *pan = value;
 
-               dbus_message_iter_get_basic(&value, &val);
-               connman_device_set_powered(device, val);
-       } else if (g_str_equal(key, "Discovering") == TRUE) {
-               dbus_bool_t val;
+               if (g_strcmp0(proxy_get_string(pan->btdevice_proxy, "Adapter"),
+                                               path) == 0) {
 
-               dbus_message_iter_get_basic(&value, &val);
-               connman_device_set_scanning(device, val);
-       } else if (g_str_equal(key, "Devices") == TRUE) {
-               check_networks(device, &value);
+                       DBG("enable network %p", pan->network);
+                       pan_create_nap(pan);
+               }
        }
-
-       return TRUE;
 }
 
-static gboolean device_removed(DBusConnection *connection,
-                               DBusMessage *message, void *user_data)
+static void device_enable_cb(const DBusError *error, void *user_data)
 {
-       const char *network_path;
-       struct connman_network *network;
+       char *path = user_data;
        struct connman_device *device;
-       DBusMessageIter iter;
-
-       DBG("");
-
-       if (dbus_message_iter_init(message, &iter) == FALSE)
-               return TRUE;
 
-       dbus_message_iter_get_basic(&iter, &network_path);
-
-       network = g_hash_table_lookup(bluetooth_networks, network_path);
-       if (network == NULL)
-               return TRUE;
-
-       device = connman_network_get_device(network);
-       if (device == NULL)
-               return TRUE;
+       device = g_hash_table_lookup(devices, path);
+       if (!device) {
+               DBG("device already removed");
+               goto out;
+       }
 
-       g_hash_table_remove(bluetooth_networks, network_path);
+       if (dbus_error_is_set(error)) {
+               connman_warn("Bluetooth device %s not enabled %s",
+                               path, error->message);
+               goto out;
+       }
 
-       return TRUE;
+       enable_device(device, path);
+out:
+       g_free(path);
 }
 
-static gboolean device_changed(DBusConnection *connection,
-                               DBusMessage *message, void *user_data)
+static int bluetooth_device_enable(struct connman_device *device)
 {
-       const char *path = dbus_message_get_path(message);
-       DBusMessageIter iter, value;
-       const char *key;
-
-       DBG("path %s", path);
+       GDBusProxy *proxy = connman_device_get_data(device);
+       dbus_bool_t device_powered = TRUE;
+       const char *path;
 
-       if (dbus_message_iter_init(message, &iter) == FALSE)
-               return TRUE;
+       if (!proxy)
+               return 0;
 
-       dbus_message_iter_get_basic(&iter, &key);
+       path = g_dbus_proxy_get_path(proxy);
 
-       dbus_message_iter_next(&iter);
-       dbus_message_iter_recurse(&iter, &value);
+       if (proxy_get_bool(proxy, "Powered")) {
+               DBG("already enabled %p %s", device, path);
+               return -EALREADY;
+       }
 
-       DBG("key %s", key);
+       DBG("device %p %s", device, path);
 
-       if (g_str_equal(key, "UUIDs") == TRUE)
-               add_network(NULL, path);
+       g_dbus_proxy_set_property_basic(proxy, "Powered",
+                       DBUS_TYPE_BOOLEAN, &device_powered,
+                       device_enable_cb, g_strdup(path), NULL);
 
-       return TRUE;
+       return -EINPROGRESS;
 }
 
-static void remove_device_networks(struct connman_device *device)
+static void disable_device(struct connman_device *device, const char *path)
 {
        GHashTableIter iter;
        gpointer key, value;
-       GSList *key_list = NULL;
-       GSList *list;
-
-       if (bluetooth_networks == NULL)
-               return;
-
-       g_hash_table_iter_init(&iter, bluetooth_networks);
-
-       while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
-               struct connman_network *network = value;
 
-               if (connman_network_get_device(network) != device)
-                       continue;
-
-               key_list = g_slist_append(key_list, key);
-       }
+       DBG("device %p %s", device, path);
+       connman_device_set_powered(device, false);
 
-       for (list = key_list; list != NULL; list = list->next) {
-               const char *network_path = list->data;
+       g_hash_table_iter_init(&iter, networks);
+       while (g_hash_table_iter_next(&iter, &key, &value)) {
+               struct bluetooth_pan *pan = value;
 
-               g_hash_table_remove(bluetooth_networks, network_path);
+               if (pan->network && connman_network_get_device(pan->network)
+                               == device) {
+                       DBG("disable network %p", pan->network);
+                       connman_device_remove_network(device, pan->network);
+               }
        }
-
-       g_slist_free(key_list);
 }
 
-static void adapter_properties_reply(DBusPendingCall *call, void *user_data)
+static void device_disable_cb(const DBusError *error, void *user_data)
 {
        char *path = user_data;
        struct connman_device *device;
-       DBusMessage *reply;
-       DBusMessageIter networks;
-       const char *address = NULL, *name = NULL;
-       dbus_bool_t powered = FALSE, scanning = FALSE;
-       struct ether_addr addr;
-       char ident[13];
-
-       DBG("path %s", path);
-
-       reply = dbus_pending_call_steal_reply(call);
-
-       if (path == NULL)
-               goto done;
 
-       extract_properties(reply, NULL, &address, &name, NULL,
-                                       &powered, &scanning, NULL, &networks);
-
-       if (address == NULL)
-               goto done;
-
-       if (g_strcmp0(address, "00:00:00:00:00:00") == 0)
-               goto done;
-
-       device = g_hash_table_lookup(bluetooth_devices, path);
-       if (device != NULL)
-               goto update;
-
-       ether_aton_r(address, &addr);
-
-       snprintf(ident, 13, "%02x%02x%02x%02x%02x%02x",
-                                               addr.ether_addr_octet[0],
-                                               addr.ether_addr_octet[1],
-                                               addr.ether_addr_octet[2],
-                                               addr.ether_addr_octet[3],
-                                               addr.ether_addr_octet[4],
-                                               addr.ether_addr_octet[5]);
-
-       device = connman_device_create(ident, CONNMAN_DEVICE_TYPE_BLUETOOTH);
-       if (device == NULL)
-               goto done;
-
-       connman_device_set_ident(device, ident);
-
-       connman_device_set_string(device, "Path", path);
-
-       if (connman_device_register(device) < 0) {
-               connman_device_unref(device);
-               goto done;
+       device = g_hash_table_lookup(devices, path);
+       if (!device) {
+               DBG("device already removed");
+               goto out;
        }
 
-       g_hash_table_insert(bluetooth_devices, g_strdup(path), device);
-
-update:
-       connman_device_set_string(device, "Address", address);
-       connman_device_set_string(device, "Name", name);
-       connman_device_set_string(device, "Path", path);
-
-       connman_device_set_powered(device, powered);
-       connman_device_set_scanning(device, scanning);
-
-       if (powered == TRUE)
-               check_networks(device, &networks);
-       else
-               remove_device_networks(device);
+       if (dbus_error_is_set(error)) {
+               connman_warn("Bluetooth device %s not disabled: %s",
+                               path, error->message);
+               goto out;
+       }
 
-done:
-       dbus_message_unref(reply);
+       disable_device(device, path);
 
-       dbus_pending_call_unref(call);
+out:
+       g_free(path);
 }
 
-static void add_adapter(DBusConnection *connection, const char *path)
+static int bluetooth_device_disable(struct connman_device *device)
 {
-       DBusMessage *message;
-       DBusPendingCall *call;
+       GDBusProxy *proxy = connman_device_get_data(device);
+       dbus_bool_t device_powered = FALSE;
+       const char *path;
 
-       DBG("path %s", path);
+       if (!proxy)
+               return 0;
 
-       message = dbus_message_new_method_call(BLUEZ_SERVICE, path,
-                               BLUEZ_ADAPTER_INTERFACE, GET_PROPERTIES);
-       if (message == NULL)
-               return;
+       path = g_dbus_proxy_get_path(proxy);
 
-       dbus_message_set_auto_start(message, FALSE);
-
-       if (dbus_connection_send_with_reply(connection, message,
-                                               &call, TIMEOUT) == FALSE) {
-               connman_error("Failed to get adapter properties for %s", path);
-               goto done;
+       if (!proxy_get_bool(proxy, "Powered")) {
+               DBG("already disabled %p %s", device, path);
+               return -EALREADY;
        }
 
-       if (call == NULL) {
-               connman_error("D-Bus connection not available");
-               goto done;
-       }
+       DBG("device %p %s", device, path);
 
-       dbus_pending_call_set_notify(call, adapter_properties_reply,
-                                               g_strdup(path), g_free);
+       g_dbus_proxy_set_property_basic(proxy, "Powered",
+                       DBUS_TYPE_BOOLEAN, &device_powered,
+                       device_disable_cb, g_strdup(path), NULL);
 
-done:
-       dbus_message_unref(message);
-}
-
-static gboolean adapter_added(DBusConnection *connection, DBusMessage *message,
-                               void *user_data)
-{
-       const char *path;
-
-       dbus_message_get_args(message, NULL, DBUS_TYPE_OBJECT_PATH, &path,
-                               DBUS_TYPE_INVALID);
-       add_adapter(connection, path);
-       return TRUE;
-}
-
-static void remove_adapter(DBusConnection *connection, const char *path)
-{
-       DBG("path %s", path);
-
-       g_hash_table_remove(bluetooth_devices, path);
+       return -EINPROGRESS;
 }
 
-static gboolean adapter_removed(DBusConnection *connection, DBusMessage *message,
-                               void *user_data)
+static void adapter_property_change(GDBusProxy *proxy, const char *name,
+               DBusMessageIter *iter, void *user_data)
 {
+       struct connman_device *device;
        const char *path;
+       bool adapter_powered, device_powered;
 
-       dbus_message_get_args(message, NULL, DBUS_TYPE_OBJECT_PATH, &path,
-                               DBUS_TYPE_INVALID);
-       remove_adapter(connection, path);
-       return TRUE;
-}
-
-static void list_adapters_reply(DBusPendingCall *call, void *user_data)
-{
-       DBusMessage *reply;
-       DBusError error;
-       char **adapters;
-       int i, num_adapters;
-
-       DBG("");
+       if (strcmp(name, "Powered") != 0)
+               return;
 
-       reply = dbus_pending_call_steal_reply(call);
+       path = g_dbus_proxy_get_path(proxy);
+       device = g_hash_table_lookup(devices, path);
 
-       dbus_error_init(&error);
+       adapter_powered = proxy_get_bool(proxy, "Powered");
+       device_powered = connman_device_get_powered(device);
 
-       if (dbus_set_error_from_message(&error, reply) == TRUE) {
-               connman_error("%s", error.message);
-               dbus_error_free(&error);
-               goto done;
-       }
+       DBG("device %p %s device powered %d adapter powered %d", device, path,
+                       device_powered, adapter_powered);
 
-       if (dbus_message_get_args(reply, &error,
-                               DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH,
-                                               &adapters, &num_adapters,
-                                               DBUS_TYPE_INVALID) == FALSE) {
-               if (dbus_error_is_set(&error) == TRUE) {
-                       connman_error("%s", error.message);
-                       dbus_error_free(&error);
-               } else
-                       connman_error("Wrong arguments for adapter list");
-               goto done;
+       if (device_powered != adapter_powered) {
+               if (adapter_powered)
+                       enable_device(device, path);
+               else
+                       disable_device(device, path);
        }
-
-       for (i = 0; i < num_adapters; i++)
-               add_adapter(connection, adapters[i]);
-
-       g_strfreev(adapters);
-
-done:
-       dbus_message_unref(reply);
-
-       dbus_pending_call_unref(call);
 }
 
-static void unregister_device(gpointer data)
+static void device_free(gpointer data)
 {
        struct connman_device *device = data;
+       GDBusProxy *proxy = connman_device_get_data(device);
 
-       DBG("");
-
-       remove_device_networks(device);
+       connman_device_set_data(device, NULL);
+       if (proxy)
+               g_dbus_proxy_unref(proxy);
 
        connman_device_unregister(device);
        connman_device_unref(device);
 }
 
-static void remove_network(gpointer data)
-{
-       struct connman_network *network = data;
-       struct connman_device *device;
-
-       DBG("network %p", network);
+struct tethering_info {
+       struct connman_technology *technology;
+       char *bridge;
+       bool enable;
+};
 
-       device = connman_network_get_device(network);
-       if (device != NULL)
-               connman_device_remove_network(device, network);
+static void tethering_free(void *user_data)
+{
+       struct tethering_info *tethering = user_data;
 
-       connman_network_unref(network);
+       g_free(tethering->bridge);
+       g_free(tethering);
 }
 
-static void bluetooth_connect(DBusConnection *connection, void *user_data)
+static void tethering_create_cb(DBusMessage *message, void *user_data)
 {
-       DBusMessage *message;
-       DBusPendingCall *call;
-
-       DBG("connection %p", connection);
-
-       bluetooth_devices = g_hash_table_new_full(g_str_hash, g_str_equal,
-                                               g_free, unregister_device);
+       struct tethering_info *tethering = user_data;
 
-       bluetooth_networks = g_hash_table_new_full(g_str_hash, g_str_equal,
-                                               g_free, remove_network);
+       if (dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_ERROR) {
+               const char *dbus_error = dbus_message_get_error_name(message);
 
-       message = dbus_message_new_method_call(BLUEZ_SERVICE, "/",
-                               BLUEZ_MANAGER_INTERFACE, LIST_ADAPTERS);
-       if (message == NULL)
+               DBG("%s tethering failed: %s",
+                               tethering->enable ? "enable" : "disable",
+                               dbus_error);
                return;
-
-       dbus_message_set_auto_start(message, FALSE);
-
-       if (dbus_connection_send_with_reply(connection, message,
-                                               &call, TIMEOUT) == FALSE) {
-               connman_error("Failed to get Bluetooth adapters");
-               goto done;
        }
 
-       if (call == NULL) {
-               connman_error("D-Bus connection not available");
-               goto done;
-       }
+       DBG("bridge %s %s", tethering->bridge, tethering->enable ?
+                       "enabled": "disabled");
 
-       dbus_pending_call_set_notify(call, list_adapters_reply, NULL, NULL);
-
-done:
-       dbus_message_unref(message);
+       if (tethering->technology)
+               connman_technology_tethering_notify(tethering->technology,
+                               tethering->enable);
 }
 
-static void bluetooth_disconnect(DBusConnection *connection, void *user_data)
+static void tethering_append(DBusMessageIter *iter, void *user_data)
 {
-       DBG("connection %p", connection);
-
-       if (bluetooth_devices == NULL)
-               return;
+       struct tethering_info *tethering = user_data;
+       const char *nap = "nap";
 
-       g_hash_table_destroy(bluetooth_networks);
-       bluetooth_networks = NULL;
-       g_hash_table_destroy(bluetooth_devices);
-       bluetooth_devices = NULL;
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &nap);
+       if (tethering->enable)
+               dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
+                               &tethering->bridge);
 }
 
-static int bluetooth_probe(struct connman_device *device)
+static bool tethering_create(const char *path,
+               struct connman_technology *technology, const char *bridge,
+               bool enabled)
 {
-       DBG("device %p", device);
+       struct tethering_info *tethering = g_new0(struct tethering_info, 1);
+       GDBusProxy *proxy;
+       const char *method;
+       bool result;
 
-       return 0;
-}
-
-static void bluetooth_remove(struct connman_device *device)
-{
-       DBG("device %p", device);
-}
+       DBG("path %s bridge %s", path, bridge);
 
-static void powered_reply(DBusPendingCall *call, void *user_data)
-{
-       DBusError error;
-       DBusMessage *reply;
+       if (!bridge)
+               return -EINVAL;
 
-       DBG("");
+       proxy = g_dbus_proxy_new(client, path, "org.bluez.NetworkServer1");
+       if (!proxy)
+               return false;
 
-       reply = dbus_pending_call_steal_reply(call);
+       tethering->technology = technology;
+       tethering->bridge = g_strdup(bridge);
+       tethering->enable = enabled;
 
-       dbus_error_init(&error);
+       if (tethering->enable)
+               method = "Register";
+       else
+               method = "Unregister";
 
-       if (dbus_set_error_from_message(&error, reply) == TRUE) {
-               connman_error("%s", error.message);
-               dbus_error_free(&error);
-               dbus_message_unref(reply);
-               dbus_pending_call_unref(call);
-               return;
-       }
+       result = g_dbus_proxy_method_call(proxy, method, tethering_append,
+                       tethering_create_cb, tethering, tethering_free);
 
-       dbus_message_unref(reply);
-       dbus_pending_call_unref(call);
+       g_dbus_proxy_unref(proxy);
 
-       add_adapter(connection, user_data);
+       return result;
 }
 
-static int change_powered(DBusConnection *connection, const char *path,
-                                                       dbus_bool_t powered)
+static void device_create(GDBusProxy *proxy)
 {
-       DBusMessage *message;
-       DBusMessageIter iter;
-       DBusPendingCall *call;
+       struct connman_device *device = NULL;
+       const char *path = g_dbus_proxy_get_path(proxy);
+       const char *address;
+       char ident[BLUETOOTH_ADDR_LEN * 2 + 1];
+       bool powered;
 
-       DBG("");
+       address = proxy_get_string(proxy, "Address");
+       if (!address)
+               return;
 
-       if (path == NULL)
-               return -EINVAL;
+       address2ident(address, ident);
 
-       message = dbus_message_new_method_call(BLUEZ_SERVICE, path,
-                                       BLUEZ_ADAPTER_INTERFACE, SET_PROPERTY);
-       if (message == NULL)
-               return -ENOMEM;
+       device = connman_device_create("bluetooth",
+                       CONNMAN_DEVICE_TYPE_BLUETOOTH);
+       if (!device)
+               return;
 
-       dbus_message_set_auto_start(message, FALSE);
+       connman_device_set_data(device, g_dbus_proxy_ref(proxy));
+       connman_device_set_ident(device, ident);
 
-       dbus_message_iter_init_append(message, &iter);
-       connman_dbus_property_append_basic(&iter, "Powered",
-                                               DBUS_TYPE_BOOLEAN, &powered);
+       g_hash_table_replace(devices, g_strdup(path), device);
 
-       if (dbus_connection_send_with_reply(connection, message,
-                                               &call, TIMEOUT) == FALSE) {
-               connman_error("Failed to change Powered property");
-               dbus_message_unref(message);
-               return -EINVAL;
-       }
+       DBG("device %p %s device powered %d adapter powered %d", device,
+                       path, connman_device_get_powered(device),
+                       proxy_get_bool(proxy, "Powered"));
 
-       if (call == NULL) {
-               connman_error("D-Bus connection not available");
-               dbus_message_unref(message);
-               return -EINVAL;
+       if (connman_device_register(device) < 0) {
+               g_hash_table_remove(devices, device);
+               return;
        }
 
-       dbus_pending_call_set_notify(call, powered_reply,
-                                       g_strdup(path), g_free);
-
-       dbus_message_unref(message);
-
-       return -EINPROGRESS;
-}
+       g_dbus_proxy_set_property_watch(proxy, adapter_property_change, NULL);
 
-static int bluetooth_enable(struct connman_device *device)
-{
-       const char *path = connman_device_get_string(device, "Path");
-
-       DBG("device %p", device);
+       powered = proxy_get_bool(proxy, "Powered");
+       connman_device_set_powered(device, powered);
 
-       return change_powered(connection, path, TRUE);
+       if (proxy_get_nap(proxy) && !bluetooth_tethering)
+               tethering_create(path, NULL, NULL, false);
 }
 
-static int bluetooth_disable(struct connman_device *device)
+static void object_added(GDBusProxy *proxy, void *user_data)
 {
-       const char *path = connman_device_get_string(device, "Path");
-
-       DBG("device %p", device);
-
-       return change_powered(connection, path, FALSE);
-}
+       const char *interface;
 
-static struct connman_device_driver bluetooth_driver = {
-       .name           = "bluetooth",
-       .type           = CONNMAN_DEVICE_TYPE_BLUETOOTH,
-       .probe          = bluetooth_probe,
-       .remove         = bluetooth_remove,
-       .enable         = bluetooth_enable,
-       .disable        = bluetooth_disable,
-};
+       interface = g_dbus_proxy_get_interface(proxy);
+       if (!interface) {
+               connman_warn("Interface or proxy missing when adding "
+                                                       "bluetooth object");
+               return;
+       }
 
-static int tech_probe(struct connman_technology *technology)
-{
-       return 0;
-}
+       if (strcmp(interface, "org.bluez.Adapter1") == 0) {
+               DBG("%s %s", interface, g_dbus_proxy_get_path(proxy));
+               device_create(proxy);
+               return;
+       }
 
-static void tech_remove(struct connman_technology *technology)
-{
+       if (strcmp(interface, "org.bluez.Network1") == 0) {
+               DBG("%s %s", interface, g_dbus_proxy_get_path(proxy));
+               pan_create(proxy);
+               return;
+       }
 }
 
-static void server_register_reply(DBusPendingCall *call, void *user_data)
+static void object_removed(GDBusProxy *proxy, void *user_data)
 {
-       struct connman_technology *technology = user_data;
-       DBusError error;
-       DBusMessage *reply;
-
-       DBG("");
-
-       reply = dbus_pending_call_steal_reply(call);
+       const char *interface, *path;
 
-       dbus_error_init(&error);
-
-       if (dbus_set_error_from_message(&error, reply) == TRUE) {
-               connman_error("%s", error.message);
-               dbus_error_free(&error);
-               dbus_message_unref(reply);
-               dbus_pending_call_unref(call);
+       interface = g_dbus_proxy_get_interface(proxy);
+       if (!interface) {
+               connman_warn("Interface or proxy missing when removing "
+                                                       "bluetooth object");
                return;
        }
 
-       dbus_message_unref(reply);
-       dbus_pending_call_unref(call);
-
-       connman_technology_tethering_notify(technology, TRUE);
-}
-
-static void server_unregister_reply(DBusPendingCall *call, void *user_data)
-{
-       struct connman_technology *technology = user_data;
-       DBusError error;
-       DBusMessage *reply;
-
-       DBG("");
+       if (strcmp(interface, "org.bluez.Adapter1") == 0) {
+               path = g_dbus_proxy_get_path(proxy);
+               DBG("%s %s", interface, path);
 
-       reply = dbus_pending_call_steal_reply(call);
+               g_hash_table_remove(devices, path);
+       }
 
-       dbus_error_init(&error);
+       if (strcmp(interface, "org.bluez.Network1") == 0) {
+               path = g_dbus_proxy_get_path(proxy);
+               DBG("%s %s", interface, path);
 
-       if (dbus_set_error_from_message(&error, reply) == TRUE) {
-               connman_error("%s", error.message);
-               dbus_error_free(&error);
-               dbus_message_unref(reply);
-               dbus_pending_call_unref(call);
-               return;
+               g_hash_table_remove(networks, path);
        }
 
-       dbus_message_unref(reply);
-       dbus_pending_call_unref(call);
-
-       connman_technology_tethering_notify(technology, FALSE);
 }
 
-
-static void server_register(const char *path, const char *uuid,
-                               struct connman_technology *technology,
-                               const char *bridge, connman_bool_t enabled)
+static int bluetooth_device_probe(struct connman_device *device)
 {
-       DBusMessage *message;
-       DBusPendingCall *call;
-       char *command;
-
-       DBG("path %s enabled %d", path, enabled);
-
-       command = enabled ? REGISTER : UNREGISTER;
-
-       message = dbus_message_new_method_call(BLUEZ_SERVICE, path,
-                                       BLUEZ_NETWORK_SERVER, command);
-       if (message == NULL)
-               return;
-
-       dbus_message_set_auto_start(message, FALSE);
-
-       dbus_message_append_args(message, DBUS_TYPE_STRING, &uuid,
-                                                       DBUS_TYPE_INVALID);
+       GHashTableIter iter;
+       gpointer key, value;
 
-       if (enabled == TRUE)
-               dbus_message_append_args(message, DBUS_TYPE_STRING, &bridge,
-                                                       DBUS_TYPE_INVALID);
+       g_hash_table_iter_init(&iter, devices);
 
-       if (dbus_connection_send_with_reply(connection, message,
-                                               &call, TIMEOUT) == FALSE) {
-               connman_error("Failed to enable PAN server");
-               dbus_message_unref(message);
-               return;
-       }
+       while (g_hash_table_iter_next(&iter, &key, &value)) {
+               struct connman_device *known = value;
 
-       if (call == NULL) {
-               connman_error("D-Bus connection not available");
-               dbus_message_unref(message);
-               return;
+               if (device == known)
+                       return 0;
        }
 
-       if (enabled == TRUE)
-               dbus_pending_call_set_notify(call, server_register_reply,
-                                               technology, NULL);
-       else
-               dbus_pending_call_set_notify(call, server_unregister_reply,
-                                               technology, NULL);
+       return -EOPNOTSUPP;
+}
 
-       dbus_message_unref(message);
+static void bluetooth_device_remove(struct connman_device *device)
+{
+       DBG("%p", device);
 }
 
-struct tethering_info {
-       struct connman_technology *technology;
-       const char *bridge;
+static struct connman_device_driver device_driver = {
+       .name           = "bluetooth",
+       .type           = CONNMAN_DEVICE_TYPE_BLUETOOTH,
+       .probe          = bluetooth_device_probe,
+       .remove         = bluetooth_device_remove,
+       .enable         = bluetooth_device_enable,
+       .disable        = bluetooth_device_disable,
 };
 
-static void enable_nap(gpointer key, gpointer value, gpointer user_data)
+static int bluetooth_tech_probe(struct connman_technology *technology)
 {
-       struct tethering_info *info = user_data;
-       struct connman_device *device = value;
-       const char *path;
-
-       DBG("");
+       return 0;
+}
 
-       path = connman_device_get_string(device, "Path");
+static void bluetooth_tech_remove(struct connman_technology *technology)
+{
 
-       server_register(path, "nap", info->technology, info->bridge, TRUE);
 }
 
-static void disable_nap(gpointer key, gpointer value, gpointer user_data)
+#if defined TIZEN_EXT
+static int bluetooth_tech_set_tethering(struct connman_technology *technology,
+               const char *identifier, const char *passphrase,
+               const char *bridge, connman_bool_t enabled)
+#else
+static int bluetooth_tech_set_tethering(struct connman_technology *technology,
+               const char *identifier, const char *passphrase,
+               const char *bridge, bool enabled)
+#endif
 {
-       struct tethering_info *info = user_data;
-       struct connman_device *device = value;
-       const char *path;
+       GHashTableIter hash_iter;
+       gpointer key, value;
+       int i = 0;
 
-       DBG("");
+       bluetooth_tethering = enabled;
 
-       path = connman_device_get_string(device, "Path");
+       g_hash_table_iter_init(&hash_iter, devices);
 
-       server_register(path, "nap", info->technology, info->bridge, FALSE);
-}
+       while (g_hash_table_iter_next(&hash_iter, &key, &value)) {
+               const char *path = key;
+               struct connman_device *device = value;
 
-static int tech_set_tethering(struct connman_technology *technology,
-                               const char *identifier, const char *passphrase,
-                               const char *bridge, connman_bool_t enabled)
-{
-       struct tethering_info info = {
-               .technology     = technology,
-               .bridge         = bridge,
-       };
+               DBG("device %p", device);
 
-       DBG("bridge %s", bridge);
+               if (tethering_create(path, technology, bridge, enabled)
+                               )
+                       i++;
+       }
 
-       if (enabled)
-               g_hash_table_foreach(bluetooth_devices, enable_nap, &info);
-       else
-               g_hash_table_foreach(bluetooth_devices, disable_nap, &info);
+       DBG("%s %d device(s)", enabled ? "enabled" : "disabled", i);
 
-       return 0;
+       if (i == 0)
+               return -ENODEV;
+
+       return -EINPROGRESS;
 }
 
 static struct connman_technology_driver tech_driver = {
        .name           = "bluetooth",
        .type           = CONNMAN_SERVICE_TYPE_BLUETOOTH,
-       .probe          = tech_probe,
-       .remove         = tech_remove,
-       .set_tethering  = tech_set_tethering,
+       .probe          = bluetooth_tech_probe,
+       .remove         = bluetooth_tech_remove,
+       .set_tethering  = bluetooth_tech_set_tethering,
 };
 
-static guint watch;
-static guint added_watch;
-static guint removed_watch;
-static guint adapter_watch;
-static guint device_watch;
-static guint device_removed_watch;
-static guint network_watch;
-
 static int bluetooth_init(void)
 {
-       int err;
-
        connection = connman_dbus_get_connection();
-       if (connection == NULL)
-               return -EIO;
+       if (!connection)
+               goto out;
 
-       watch = g_dbus_add_service_watch(connection, BLUEZ_SERVICE,
-                       bluetooth_connect, bluetooth_disconnect, NULL, NULL);
-
-       added_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
-                                               BLUEZ_MANAGER_INTERFACE,
-                                               ADAPTER_ADDED, adapter_added,
-                                               NULL, NULL);
-
-       removed_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
-                                               BLUEZ_MANAGER_INTERFACE,
-                                               ADAPTER_REMOVED, adapter_removed,
-                                               NULL, NULL);
-
-       adapter_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
-                                               BLUEZ_ADAPTER_INTERFACE,
-                                               PROPERTY_CHANGED, adapter_changed,
-                                               NULL, NULL);
-
-       device_removed_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
-                                               BLUEZ_ADAPTER_INTERFACE,
-                                               DEVICE_REMOVED, device_removed,
-                                               NULL, NULL);
-
-       device_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
-                                               BLUEZ_DEVICE_INTERFACE,
-                                               PROPERTY_CHANGED, device_changed,
-                                               NULL, NULL);
-
-       network_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
-                                               BLUEZ_NETWORK_INTERFACE,
-                                               PROPERTY_CHANGED, network_changed,
-                                               NULL, NULL);
-
-       if (watch == 0 || added_watch == 0 || removed_watch == 0
-                       || adapter_watch == 0 || network_watch == 0
-                               || device_watch == 0
-                                       || device_removed_watch == 0) {
-               err = -EIO;
-               goto remove;
+       if (connman_technology_driver_register(&tech_driver) < 0) {
+               connman_warn("Failed to initialize technology for Bluez 5");
+               goto out;
        }
 
-       err = connman_network_driver_register(&pan_driver);
-       if (err < 0)
-               goto remove;
+       devices = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
+                       device_free);
 
-       err = connman_device_driver_register(&bluetooth_driver);
-       if (err < 0) {
-               connman_network_driver_unregister(&pan_driver);
-               goto remove;
+       if (connman_device_driver_register(&device_driver) < 0) {
+               connman_warn("Failed to initialize device driver for "
+                               BLUEZ_SERVICE);
+               connman_technology_driver_unregister(&tech_driver);
+               goto out;
        }
 
-       err = connman_technology_driver_register(&tech_driver);
-       if (err < 0) {
-               connman_device_driver_unregister(&bluetooth_driver);
-               connman_network_driver_unregister(&pan_driver);
-               goto remove;
+       if (connman_network_driver_register(&network_driver) < 0) {
+               connman_technology_driver_unregister(&tech_driver);
+               connman_device_driver_unregister(&device_driver);
+               goto out;
        }
 
+       networks = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
+                       pan_free);
+
+       client = g_dbus_client_new(connection, BLUEZ_SERVICE, BLUEZ_PATH);
+       if (!client) {
+               connman_warn("Failed to initialize D-Bus client for "
+                               BLUEZ_SERVICE);
+               goto out;
+       }
+
+       g_dbus_client_set_proxy_handlers(client, object_added, object_removed,
+                       NULL, NULL);
+
        return 0;
 
-remove:
-       g_dbus_remove_watch(connection, watch);
-       g_dbus_remove_watch(connection, added_watch);
-       g_dbus_remove_watch(connection, removed_watch);
-       g_dbus_remove_watch(connection, adapter_watch);
-       g_dbus_remove_watch(connection, device_removed_watch);
-       g_dbus_remove_watch(connection, device_watch);
-       g_dbus_remove_watch(connection, network_watch);
+out:
+       if (networks)
+               g_hash_table_destroy(networks);
 
-       dbus_connection_unref(connection);
+       if (devices)
+               g_hash_table_destroy(devices);
 
-       return err;
+       if (client)
+               g_dbus_client_unref(client);
+
+       if (connection)
+               dbus_connection_unref(connection);
+
+       return -EIO;
 }
 
 static void bluetooth_exit(void)
 {
-       g_dbus_remove_watch(connection, watch);
-       g_dbus_remove_watch(connection, added_watch);
-       g_dbus_remove_watch(connection, removed_watch);
-       g_dbus_remove_watch(connection, adapter_watch);
-       g_dbus_remove_watch(connection, device_removed_watch);
-       g_dbus_remove_watch(connection, device_watch);
-       g_dbus_remove_watch(connection, network_watch);
+       /*
+        * We unset the disabling of the Bluetooth device when shutting down
+        * so that non-PAN BT connections are not affected.
+        */
+       device_driver.disable = NULL;
 
-       bluetooth_disconnect(connection, NULL);
+       connman_network_driver_unregister(&network_driver);
+       g_hash_table_destroy(networks);
 
-       connman_technology_driver_unregister(&tech_driver);
-
-       connman_device_driver_unregister(&bluetooth_driver);
-       connman_network_driver_unregister(&pan_driver);
+       connman_device_driver_unregister(&device_driver);
+       g_hash_table_destroy(devices);
 
+       connman_technology_driver_unregister(&tech_driver);
        dbus_connection_unref(connection);
 }
 
 CONNMAN_PLUGIN_DEFINE(bluetooth, "Bluetooth technology plugin", VERSION,
-               CONNMAN_PLUGIN_PRIORITY_DEFAULT, bluetooth_init, bluetooth_exit)
+                CONNMAN_PLUGIN_PRIORITY_DEFAULT, bluetooth_init, bluetooth_exit)
diff --git a/plugins/bluetooth_legacy.c b/plugins/bluetooth_legacy.c
new file mode 100644 (file)
index 0000000..4f5a550
--- /dev/null
@@ -0,0 +1,1295 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netinet/ether.h>
+
+#include <gdbus.h>
+
+#define CONNMAN_API_SUBJECT_TO_CHANGE
+#include <connman/plugin.h>
+#include <connman/technology.h>
+#include <connman/device.h>
+#include <connman/inet.h>
+#include <connman/dbus.h>
+#include <connman/log.h>
+
+#define BLUEZ_SERVICE                  "org.bluez"
+#define BLUEZ_MANAGER_INTERFACE                BLUEZ_SERVICE ".Manager"
+#define BLUEZ_ADAPTER_INTERFACE                BLUEZ_SERVICE ".Adapter"
+#define BLUEZ_DEVICE_INTERFACE         BLUEZ_SERVICE ".Device"
+#define BLUEZ_NETWORK_INTERFACE                BLUEZ_SERVICE ".Network"
+#define BLUEZ_NETWORK_SERVER           BLUEZ_SERVICE ".NetworkServer"
+
+#define LIST_ADAPTERS                  "ListAdapters"
+#define ADAPTER_ADDED                  "AdapterAdded"
+#define ADAPTER_REMOVED                        "AdapterRemoved"
+#define DEVICE_REMOVED                 "DeviceRemoved"
+
+#define PROPERTY_CHANGED               "PropertyChanged"
+#define GET_PROPERTIES                 "GetProperties"
+#define SET_PROPERTY                   "SetProperty"
+
+#define CONNECT                                "Connect"
+#define DISCONNECT                     "Disconnect"
+
+#define REGISTER                       "Register"
+#define UNREGISTER                     "Unregister"
+
+#define UUID_NAP       "00001116-0000-1000-8000-00805f9b34fb"
+
+#define TIMEOUT 5000
+
+static DBusConnection *connection;
+
+static GHashTable *bluetooth_devices = NULL;
+static GHashTable *bluetooth_networks = NULL;
+
+static int pan_probe(struct connman_network *network)
+{
+       DBG("network %p", network);
+
+       return 0;
+}
+
+static void pan_remove(struct connman_network *network)
+{
+       DBG("network %p", network);
+}
+
+static void connect_reply(DBusPendingCall *call, void *user_data)
+{
+       struct connman_network *network = user_data;
+       DBusMessage *reply;
+       DBusError error;
+       const char *interface = NULL;
+       int index;
+
+       DBG("network %p", network);
+
+       reply = dbus_pending_call_steal_reply(call);
+
+       dbus_error_init(&error);
+
+       if (dbus_set_error_from_message(&error, reply) == TRUE) {
+               connman_error("%s", error.message);
+               dbus_error_free(&error);
+
+               goto err;
+       }
+
+       if (dbus_message_get_args(reply, &error,
+                                       DBUS_TYPE_STRING, &interface,
+                                               DBUS_TYPE_INVALID) == FALSE) {
+               if (dbus_error_is_set(&error) == TRUE) {
+                       connman_error("%s", error.message);
+                       dbus_error_free(&error);
+               } else
+                       connman_error("Wrong arguments for connect");
+               goto err;
+       }
+
+       if (interface == NULL)
+               goto err;
+
+       DBG("interface %s", interface);
+
+       index = connman_inet_ifindex(interface);
+
+       connman_network_set_index(network, index);
+
+       connman_network_set_connected(network, TRUE);
+
+       dbus_message_unref(reply);
+
+       dbus_pending_call_unref(call);
+
+       return;
+err:
+
+       connman_network_set_connected(network, FALSE);
+
+       dbus_message_unref(reply);
+
+       dbus_pending_call_unref(call);
+}
+
+static int pan_connect(struct connman_network *network)
+{
+       const char *path = connman_network_get_string(network, "Path");
+       const char *uuid = "nap";
+       DBusMessage *message;
+       DBusPendingCall *call;
+
+       DBG("network %p", network);
+
+       if (path == NULL)
+               return -EINVAL;
+
+       message = dbus_message_new_method_call(BLUEZ_SERVICE, path,
+                                       BLUEZ_NETWORK_INTERFACE, CONNECT);
+       if (message == NULL)
+               return -ENOMEM;
+
+       dbus_message_set_auto_start(message, FALSE);
+
+       dbus_message_append_args(message, DBUS_TYPE_STRING, &uuid,
+                                                       DBUS_TYPE_INVALID);
+
+       if (dbus_connection_send_with_reply(connection, message,
+                                       &call, TIMEOUT * 10) == FALSE) {
+               connman_error("Failed to connect service");
+               dbus_message_unref(message);
+               return -EINVAL;
+       }
+
+       if (call == NULL) {
+               connman_error("D-Bus connection not available");
+               dbus_message_unref(message);
+               return -EINVAL;
+       }
+
+       dbus_pending_call_set_notify(call, connect_reply, network, NULL);
+
+       dbus_message_unref(message);
+
+       return -EINPROGRESS;
+}
+
+static void disconnect_reply(DBusPendingCall *call, void *user_data)
+{
+       struct connman_network *network = user_data;
+       DBusMessage *reply;
+       DBusError error;
+
+       DBG("network %p", network);
+
+       reply = dbus_pending_call_steal_reply(call);
+
+       dbus_error_init(&error);
+
+       if (dbus_set_error_from_message(&error, reply) == TRUE) {
+               connman_error("%s", error.message);
+               dbus_error_free(&error);
+               goto done;
+       }
+
+       if (dbus_message_get_args(reply, &error, DBUS_TYPE_INVALID) == FALSE) {
+               if (dbus_error_is_set(&error) == TRUE) {
+                       connman_error("%s", error.message);
+                       dbus_error_free(&error);
+               } else
+                       connman_error("Wrong arguments for disconnect");
+               goto done;
+       }
+
+       connman_network_set_connected(network, FALSE);
+
+done:
+       dbus_message_unref(reply);
+
+       dbus_pending_call_unref(call);
+
+       connman_network_unref(network);
+}
+
+static int pan_disconnect(struct connman_network *network)
+{
+       const char *path = connman_network_get_string(network, "Path");
+       DBusMessage *message;
+       DBusPendingCall *call;
+
+       DBG("network %p", network);
+
+       if (path == NULL)
+               return -EINVAL;
+
+#if defined TIZEN_EXT
+       if (connman_network_get_associating(network) == TRUE)
+               connman_network_clear_associating(network);
+#endif
+
+       message = dbus_message_new_method_call(BLUEZ_SERVICE, path,
+                                       BLUEZ_NETWORK_INTERFACE, DISCONNECT);
+       if (message == NULL)
+               return -ENOMEM;
+
+       dbus_message_set_auto_start(message, FALSE);
+
+       dbus_message_append_args(message, DBUS_TYPE_INVALID);
+
+       if (dbus_connection_send_with_reply(connection, message,
+                                               &call, TIMEOUT) == FALSE) {
+               connman_error("Failed to disconnect service");
+               dbus_message_unref(message);
+               return -EINVAL;
+       }
+
+       if (call == NULL) {
+               connman_error("D-Bus connection not available");
+               dbus_message_unref(message);
+               return -EINVAL;
+       }
+
+       connman_network_ref(network);
+
+       connman_network_set_associating(network, FALSE);
+
+       dbus_pending_call_set_notify(call, disconnect_reply, network, NULL);
+
+       dbus_message_unref(message);
+
+       return 0;
+}
+
+static struct connman_network_driver pan_driver = {
+       .name           = "bluetooth_legacy-pan",
+       .type           = CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN,
+       .probe          = pan_probe,
+       .remove         = pan_remove,
+       .connect        = pan_connect,
+       .disconnect     = pan_disconnect,
+};
+
+static gboolean network_changed(DBusConnection *connection,
+                               DBusMessage *message, void *user_data)
+{
+       const char *path = dbus_message_get_path(message);
+       struct connman_network *network;
+       DBusMessageIter iter, value;
+       const char *key;
+
+       DBG("path %s", path);
+
+       network = g_hash_table_lookup(bluetooth_networks, path);
+       if (network == NULL)
+               return TRUE;
+
+       if (dbus_message_iter_init(message, &iter) == FALSE)
+               return TRUE;
+
+       dbus_message_iter_get_basic(&iter, &key);
+
+       dbus_message_iter_next(&iter);
+       dbus_message_iter_recurse(&iter, &value);
+
+       if (g_str_equal(key, "Connected") == TRUE) {
+               dbus_bool_t connected;
+
+               dbus_message_iter_get_basic(&value, &connected);
+
+               if (connected == TRUE)
+                       return TRUE;
+
+               connman_network_set_associating(network, FALSE);
+               connman_network_set_connected(network, FALSE);
+       }
+
+       return TRUE;
+}
+
+static void extract_properties(DBusMessage *reply, const char **parent,
+                                               const char **address,
+                                               const char **name,
+                                               const char **alias,
+                                               dbus_bool_t *powered,
+                                               dbus_bool_t *scanning,
+                                               DBusMessageIter *uuids,
+                                               DBusMessageIter *networks)
+{
+       DBusMessageIter array, dict;
+
+       if (dbus_message_iter_init(reply, &array) == FALSE)
+               return;
+
+       if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_ARRAY)
+               return;
+
+       dbus_message_iter_recurse(&array, &dict);
+
+       while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+               DBusMessageIter entry, value;
+               const char *key;
+
+               dbus_message_iter_recurse(&dict, &entry);
+               dbus_message_iter_get_basic(&entry, &key);
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_recurse(&entry, &value);
+
+               if (g_str_equal(key, "Adapter") == TRUE) {
+                       if (parent != NULL)
+                               dbus_message_iter_get_basic(&value, parent);
+               } else if (g_str_equal(key, "Address") == TRUE) {
+                       if (address != NULL)
+                               dbus_message_iter_get_basic(&value, address);
+               } else if (g_str_equal(key, "Name") == TRUE) {
+                       if (name != NULL)
+                               dbus_message_iter_get_basic(&value, name);
+               } else if (g_str_equal(key, "Alias") == TRUE) {
+                       if (alias != NULL)
+                               dbus_message_iter_get_basic(&value, alias);
+               } else if (g_str_equal(key, "Powered") == TRUE) {
+                       if (powered != NULL)
+                               dbus_message_iter_get_basic(&value, powered);
+               } else if (g_str_equal(key, "Discovering") == TRUE) {
+                       if (scanning != NULL)
+                               dbus_message_iter_get_basic(&value, scanning);
+               } else if (g_str_equal(key, "Devices") == TRUE) {
+                       if (networks != NULL)
+                               memcpy(networks, &value, sizeof(value));
+               } else if (g_str_equal(key, "UUIDs") == TRUE) {
+                       if (uuids != NULL)
+                               memcpy(uuids, &value, sizeof(value));
+               }
+
+               dbus_message_iter_next(&dict);
+       }
+}
+
+static dbus_bool_t has_pan(DBusMessageIter *array)
+{
+       DBusMessageIter value;
+
+       if (dbus_message_iter_get_arg_type(array) != DBUS_TYPE_ARRAY)
+               return FALSE;
+
+       dbus_message_iter_recurse(array, &value);
+
+       while (dbus_message_iter_get_arg_type(&value) == DBUS_TYPE_STRING) {
+               const char *uuid;
+
+               dbus_message_iter_get_basic(&value, &uuid);
+
+               if (g_strcmp0(uuid, UUID_NAP) == 0)
+                       return TRUE;
+
+               dbus_message_iter_next(&value);
+       }
+
+       return FALSE;
+}
+
+static void network_properties_reply(DBusPendingCall *call, void *user_data)
+{
+       char *path = user_data;
+       struct connman_device *device;
+       struct connman_network *network;
+       DBusMessage *reply;
+       DBusMessageIter uuids;
+       const char *parent = NULL, *address = NULL, *name = NULL;
+       struct ether_addr addr;
+       char ident[13];
+
+       reply = dbus_pending_call_steal_reply(call);
+
+       extract_properties(reply, &parent, &address, NULL, &name,
+                                               NULL, NULL, &uuids, NULL);
+
+       if (parent == NULL)
+               goto done;
+
+       device = g_hash_table_lookup(bluetooth_devices, parent);
+       if (device == NULL)
+               goto done;
+
+       if (address == NULL)
+               goto done;
+
+       ether_aton_r(address, &addr);
+
+       snprintf(ident, 13, "%02x%02x%02x%02x%02x%02x",
+                                               addr.ether_addr_octet[0],
+                                               addr.ether_addr_octet[1],
+                                               addr.ether_addr_octet[2],
+                                               addr.ether_addr_octet[3],
+                                               addr.ether_addr_octet[4],
+                                               addr.ether_addr_octet[5]);
+
+       if (has_pan(&uuids) == FALSE)
+               goto done;
+
+       network = connman_device_get_network(device, ident);
+       if (network != NULL)
+               goto done;
+
+       network = connman_network_create(ident,
+                                       CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN);
+       if (network == NULL)
+               goto done;
+
+       connman_network_set_string(network, "Path", path);
+
+       connman_network_set_name(network, name);
+
+       connman_device_add_network(device, network);
+
+       connman_network_set_group(network, ident);
+
+       g_hash_table_replace(bluetooth_networks, g_strdup(path), network);
+
+done:
+       dbus_message_unref(reply);
+
+       dbus_pending_call_unref(call);
+}
+
+static void add_network(struct connman_device *device, const char *path)
+{
+       DBusMessage *message;
+       DBusPendingCall *call;
+
+       DBG("path %s", path);
+
+       message = dbus_message_new_method_call(BLUEZ_SERVICE, path,
+                               BLUEZ_DEVICE_INTERFACE, GET_PROPERTIES);
+       if (message == NULL)
+               return;
+
+       dbus_message_set_auto_start(message, FALSE);
+
+       if (dbus_connection_send_with_reply(connection, message,
+                                               &call, TIMEOUT) == FALSE) {
+               connman_error("Failed to get network properties for %s", path);
+               goto done;
+       }
+
+       if (call == NULL) {
+               connman_error("D-Bus connection not available");
+               goto done;
+       }
+
+       dbus_pending_call_set_notify(call, network_properties_reply,
+                                               g_strdup(path), g_free);
+
+done:
+       dbus_message_unref(message);
+}
+
+static void check_networks(struct connman_device *device,
+                                               DBusMessageIter *array)
+{
+       DBusMessageIter value;
+
+       if (dbus_message_iter_get_arg_type(array) != DBUS_TYPE_ARRAY)
+               return;
+
+       dbus_message_iter_recurse(array, &value);
+
+       while (dbus_message_iter_get_arg_type(&value) == DBUS_TYPE_OBJECT_PATH) {
+               const char *path;
+
+               dbus_message_iter_get_basic(&value, &path);
+
+               add_network(device, path);
+
+               dbus_message_iter_next(&value);
+       }
+}
+
+static gboolean adapter_changed(DBusConnection *connection,
+                               DBusMessage *message, void *user_data)
+{
+       const char *path = dbus_message_get_path(message);
+       struct connman_device *device;
+       DBusMessageIter iter, value;
+       const char *key;
+
+       DBG("path %s", path);
+
+       device = g_hash_table_lookup(bluetooth_devices, path);
+       if (device == NULL)
+               return TRUE;
+
+       if (dbus_message_iter_init(message, &iter) == FALSE)
+               return TRUE;
+
+       dbus_message_iter_get_basic(&iter, &key);
+
+       dbus_message_iter_next(&iter);
+       dbus_message_iter_recurse(&iter, &value);
+
+       if (g_str_equal(key, "Powered") == TRUE) {
+               dbus_bool_t val;
+
+               dbus_message_iter_get_basic(&value, &val);
+               connman_device_set_powered(device, val);
+       } else if (g_str_equal(key, "Discovering") == TRUE) {
+               dbus_bool_t val;
+
+               dbus_message_iter_get_basic(&value, &val);
+               connman_device_set_scanning(device, val);
+       } else if (g_str_equal(key, "Devices") == TRUE) {
+               check_networks(device, &value);
+       }
+
+       return TRUE;
+}
+
+static gboolean device_removed(DBusConnection *connection,
+                               DBusMessage *message, void *user_data)
+{
+       const char *network_path;
+       struct connman_network *network;
+       struct connman_device *device;
+       DBusMessageIter iter;
+
+       DBG("");
+
+       if (dbus_message_iter_init(message, &iter) == FALSE)
+               return TRUE;
+
+       dbus_message_iter_get_basic(&iter, &network_path);
+
+       network = g_hash_table_lookup(bluetooth_networks, network_path);
+       if (network == NULL)
+               return TRUE;
+
+       device = connman_network_get_device(network);
+       if (device == NULL)
+               return TRUE;
+
+       g_hash_table_remove(bluetooth_networks, network_path);
+
+       return TRUE;
+}
+
+static gboolean device_changed(DBusConnection *connection,
+                               DBusMessage *message, void *user_data)
+{
+       const char *path = dbus_message_get_path(message);
+       DBusMessageIter iter, value;
+       const char *key;
+
+       DBG("path %s", path);
+
+       if (dbus_message_iter_init(message, &iter) == FALSE)
+               return TRUE;
+
+       dbus_message_iter_get_basic(&iter, &key);
+
+       dbus_message_iter_next(&iter);
+       dbus_message_iter_recurse(&iter, &value);
+
+       DBG("key %s", key);
+
+       if (g_str_equal(key, "UUIDs") == TRUE)
+               add_network(NULL, path);
+
+       return TRUE;
+}
+
+static void remove_device_networks(struct connman_device *device)
+{
+       GHashTableIter iter;
+       gpointer key, value;
+       GSList *key_list = NULL;
+       GSList *list;
+
+       if (bluetooth_networks == NULL)
+               return;
+
+       g_hash_table_iter_init(&iter, bluetooth_networks);
+
+       while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
+               struct connman_network *network = value;
+
+               if (connman_network_get_device(network) != device)
+                       continue;
+
+               key_list = g_slist_prepend(key_list, key);
+       }
+
+       for (list = key_list; list != NULL; list = list->next) {
+               const char *network_path = list->data;
+
+               g_hash_table_remove(bluetooth_networks, network_path);
+       }
+
+       g_slist_free(key_list);
+}
+
+static void adapter_properties_reply(DBusPendingCall *call, void *user_data)
+{
+       char *path = user_data;
+       struct connman_device *device;
+       DBusMessage *reply;
+       DBusMessageIter networks;
+       const char *address = NULL, *name = NULL;
+       dbus_bool_t powered = FALSE, scanning = FALSE;
+       struct ether_addr addr;
+       char ident[13];
+
+       DBG("path %s", path);
+
+       reply = dbus_pending_call_steal_reply(call);
+
+       if (path == NULL)
+               goto done;
+
+       extract_properties(reply, NULL, &address, &name, NULL,
+                                       &powered, &scanning, NULL, &networks);
+
+       if (address == NULL)
+               goto done;
+
+       if (g_strcmp0(address, "00:00:00:00:00:00") == 0)
+               goto done;
+
+       device = g_hash_table_lookup(bluetooth_devices, path);
+       if (device != NULL)
+               goto update;
+
+       ether_aton_r(address, &addr);
+
+       snprintf(ident, 13, "%02x%02x%02x%02x%02x%02x",
+                                               addr.ether_addr_octet[0],
+                                               addr.ether_addr_octet[1],
+                                               addr.ether_addr_octet[2],
+                                               addr.ether_addr_octet[3],
+                                               addr.ether_addr_octet[4],
+                                               addr.ether_addr_octet[5]);
+
+       device = connman_device_create("bluetooth_legacy", CONNMAN_DEVICE_TYPE_BLUETOOTH);
+       if (device == NULL)
+               goto done;
+
+       connman_device_set_ident(device, ident);
+
+       connman_device_set_string(device, "Path", path);
+
+       if (connman_device_register(device) < 0) {
+               connman_device_unref(device);
+               goto done;
+       }
+
+       g_hash_table_insert(bluetooth_devices, g_strdup(path), device);
+
+update:
+       connman_device_set_string(device, "Address", address);
+       connman_device_set_string(device, "Name", name);
+       connman_device_set_string(device, "Path", path);
+
+       connman_device_set_powered(device, powered);
+       connman_device_set_scanning(device, scanning);
+
+       if (powered == TRUE)
+               check_networks(device, &networks);
+       else
+               remove_device_networks(device);
+
+done:
+       dbus_message_unref(reply);
+
+       dbus_pending_call_unref(call);
+}
+
+static void add_adapter(DBusConnection *connection, const char *path)
+{
+       DBusMessage *message;
+       DBusPendingCall *call;
+
+       DBG("path %s", path);
+
+       message = dbus_message_new_method_call(BLUEZ_SERVICE, path,
+                               BLUEZ_ADAPTER_INTERFACE, GET_PROPERTIES);
+       if (message == NULL)
+               return;
+
+       dbus_message_set_auto_start(message, FALSE);
+
+       if (dbus_connection_send_with_reply(connection, message,
+                                               &call, TIMEOUT) == FALSE) {
+               connman_error("Failed to get adapter properties for %s", path);
+               goto done;
+       }
+
+       if (call == NULL) {
+               connman_error("D-Bus connection not available");
+               goto done;
+       }
+
+       dbus_pending_call_set_notify(call, adapter_properties_reply,
+                                               g_strdup(path), g_free);
+
+done:
+       dbus_message_unref(message);
+}
+
+static gboolean adapter_added(DBusConnection *connection, DBusMessage *message,
+                               void *user_data)
+{
+       const char *path;
+
+       dbus_message_get_args(message, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+                               DBUS_TYPE_INVALID);
+       add_adapter(connection, path);
+       return TRUE;
+}
+
+static void remove_adapter(DBusConnection *connection, const char *path)
+{
+       DBG("path %s", path);
+
+       g_hash_table_remove(bluetooth_devices, path);
+}
+
+static gboolean adapter_removed(DBusConnection *connection, DBusMessage *message,
+                               void *user_data)
+{
+       const char *path;
+
+       dbus_message_get_args(message, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+                               DBUS_TYPE_INVALID);
+       remove_adapter(connection, path);
+       return TRUE;
+}
+
+static void list_adapters_reply(DBusPendingCall *call, void *user_data)
+{
+       DBusMessage *reply;
+       DBusError error;
+       char **adapters;
+       int i, num_adapters;
+
+       DBG("");
+
+       reply = dbus_pending_call_steal_reply(call);
+
+       dbus_error_init(&error);
+
+       if (dbus_set_error_from_message(&error, reply) == TRUE) {
+               connman_error("%s", error.message);
+               dbus_error_free(&error);
+               goto done;
+       }
+
+       if (dbus_message_get_args(reply, &error,
+                               DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH,
+                                               &adapters, &num_adapters,
+                                               DBUS_TYPE_INVALID) == FALSE) {
+               if (dbus_error_is_set(&error) == TRUE) {
+                       connman_error("%s", error.message);
+                       dbus_error_free(&error);
+               } else
+                       connman_error("Wrong arguments for adapter list");
+               goto done;
+       }
+
+       for (i = 0; i < num_adapters; i++)
+               add_adapter(connection, adapters[i]);
+
+       g_strfreev(adapters);
+
+done:
+       dbus_message_unref(reply);
+
+       dbus_pending_call_unref(call);
+}
+
+static void unregister_device(gpointer data)
+{
+       struct connman_device *device = data;
+
+       DBG("");
+
+       remove_device_networks(device);
+
+       connman_device_unregister(device);
+       connman_device_unref(device);
+}
+
+static void remove_network(gpointer data)
+{
+       struct connman_network *network = data;
+       struct connman_device *device;
+
+       DBG("network %p", network);
+
+       device = connman_network_get_device(network);
+       if (device != NULL)
+               connman_device_remove_network(device, network);
+
+       connman_network_unref(network);
+}
+
+static void bluetooth_connect(DBusConnection *connection, void *user_data)
+{
+       DBusMessage *message;
+       DBusPendingCall *call;
+
+       DBG("connection %p", connection);
+
+       bluetooth_devices = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                               g_free, unregister_device);
+
+       bluetooth_networks = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                               g_free, remove_network);
+
+       message = dbus_message_new_method_call(BLUEZ_SERVICE, "/",
+                               BLUEZ_MANAGER_INTERFACE, LIST_ADAPTERS);
+       if (message == NULL)
+               return;
+
+       dbus_message_set_auto_start(message, FALSE);
+
+       if (dbus_connection_send_with_reply(connection, message,
+                                               &call, TIMEOUT) == FALSE) {
+               connman_error("Failed to get Bluetooth adapters");
+               goto done;
+       }
+
+       if (call == NULL) {
+               connman_error("D-Bus connection not available");
+               goto done;
+       }
+
+       dbus_pending_call_set_notify(call, list_adapters_reply, NULL, NULL);
+
+done:
+       dbus_message_unref(message);
+}
+
+static void bluetooth_disconnect(DBusConnection *connection, void *user_data)
+{
+       DBG("connection %p", connection);
+
+       if (bluetooth_devices == NULL)
+               return;
+
+       g_hash_table_destroy(bluetooth_networks);
+       bluetooth_networks = NULL;
+       g_hash_table_destroy(bluetooth_devices);
+       bluetooth_devices = NULL;
+}
+
+static int bluetooth_probe(struct connman_device *device)
+{
+       GHashTableIter iter;
+       gpointer key, value;
+
+       DBG("device %p", device);
+
+       if (bluetooth_devices == NULL)
+               return -ENOTSUP;
+
+       g_hash_table_iter_init(&iter, bluetooth_devices);
+
+       while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
+               struct connman_device *device_pan = value;
+
+               if (device == device_pan)
+                       return 0;
+       }
+
+       return -ENOTSUP;
+}
+
+static void bluetooth_remove(struct connman_device *device)
+{
+       DBG("device %p", device);
+}
+
+static void powered_reply(DBusPendingCall *call, void *user_data)
+{
+       DBusError error;
+       DBusMessage *reply;
+
+       DBG("");
+
+       reply = dbus_pending_call_steal_reply(call);
+
+       dbus_error_init(&error);
+
+       if (dbus_set_error_from_message(&error, reply) == TRUE) {
+               connman_error("%s", error.message);
+               dbus_error_free(&error);
+               dbus_message_unref(reply);
+               dbus_pending_call_unref(call);
+               return;
+       }
+
+       dbus_message_unref(reply);
+       dbus_pending_call_unref(call);
+
+       add_adapter(connection, user_data);
+}
+
+static int change_powered(DBusConnection *connection, const char *path,
+                                                       dbus_bool_t powered)
+{
+       DBusMessage *message;
+       DBusMessageIter iter;
+       DBusPendingCall *call;
+
+       DBG("");
+
+       if (path == NULL)
+               return -EINVAL;
+
+       message = dbus_message_new_method_call(BLUEZ_SERVICE, path,
+                                       BLUEZ_ADAPTER_INTERFACE, SET_PROPERTY);
+       if (message == NULL)
+               return -ENOMEM;
+
+       dbus_message_set_auto_start(message, FALSE);
+
+       dbus_message_iter_init_append(message, &iter);
+       connman_dbus_property_append_basic(&iter, "Powered",
+                                               DBUS_TYPE_BOOLEAN, &powered);
+
+       if (dbus_connection_send_with_reply(connection, message,
+                                               &call, TIMEOUT) == FALSE) {
+               connman_error("Failed to change Powered property");
+               dbus_message_unref(message);
+               return -EINVAL;
+       }
+
+       if (call == NULL) {
+               connman_error("D-Bus connection not available");
+               dbus_message_unref(message);
+               return -EINVAL;
+       }
+
+       dbus_pending_call_set_notify(call, powered_reply,
+                                       g_strdup(path), g_free);
+
+       dbus_message_unref(message);
+
+       return -EINPROGRESS;
+}
+
+static int bluetooth_enable(struct connman_device *device)
+{
+       const char *path = connman_device_get_string(device, "Path");
+
+       DBG("device %p", device);
+
+       return change_powered(connection, path, TRUE);
+}
+
+static int bluetooth_disable(struct connman_device *device)
+{
+       const char *path = connman_device_get_string(device, "Path");
+
+       DBG("device %p", device);
+
+       return change_powered(connection, path, FALSE);
+}
+
+static struct connman_device_driver bluetooth_driver = {
+       .name           = "bluetooth_legacy",
+       .type           = CONNMAN_DEVICE_TYPE_BLUETOOTH,
+       .probe          = bluetooth_probe,
+       .remove         = bluetooth_remove,
+       .enable         = bluetooth_enable,
+       .disable        = bluetooth_disable,
+};
+
+static int tech_probe(struct connman_technology *technology)
+{
+       return 0;
+}
+
+static void tech_remove(struct connman_technology *technology)
+{
+}
+
+static void server_register_reply(DBusPendingCall *call, void *user_data)
+{
+       struct connman_technology *technology = user_data;
+       DBusError error;
+       DBusMessage *reply;
+
+       DBG("");
+
+       reply = dbus_pending_call_steal_reply(call);
+
+       dbus_error_init(&error);
+
+       if (dbus_set_error_from_message(&error, reply) == TRUE) {
+               connman_error("%s", error.message);
+               dbus_error_free(&error);
+               dbus_message_unref(reply);
+               dbus_pending_call_unref(call);
+               return;
+       }
+
+       dbus_message_unref(reply);
+       dbus_pending_call_unref(call);
+
+       connman_technology_tethering_notify(technology, TRUE);
+}
+
+static void server_unregister_reply(DBusPendingCall *call, void *user_data)
+{
+       struct connman_technology *technology = user_data;
+       DBusError error;
+       DBusMessage *reply;
+
+       DBG("");
+
+       reply = dbus_pending_call_steal_reply(call);
+
+       dbus_error_init(&error);
+
+       if (dbus_set_error_from_message(&error, reply) == TRUE) {
+               connman_error("%s", error.message);
+               dbus_error_free(&error);
+               dbus_message_unref(reply);
+               dbus_pending_call_unref(call);
+               return;
+       }
+
+       dbus_message_unref(reply);
+       dbus_pending_call_unref(call);
+
+       connman_technology_tethering_notify(technology, FALSE);
+}
+
+
+static void server_register(const char *path, const char *uuid,
+                               struct connman_technology *technology,
+                               const char *bridge, connman_bool_t enabled)
+{
+       DBusMessage *message;
+       DBusPendingCall *call;
+       char *command;
+
+       DBG("path %s enabled %d", path, enabled);
+
+       command = enabled ? REGISTER : UNREGISTER;
+
+       message = dbus_message_new_method_call(BLUEZ_SERVICE, path,
+                                       BLUEZ_NETWORK_SERVER, command);
+       if (message == NULL)
+               return;
+
+       dbus_message_set_auto_start(message, FALSE);
+
+       dbus_message_append_args(message, DBUS_TYPE_STRING, &uuid,
+                                                       DBUS_TYPE_INVALID);
+
+       if (enabled == TRUE)
+               dbus_message_append_args(message, DBUS_TYPE_STRING, &bridge,
+                                                       DBUS_TYPE_INVALID);
+
+       if (dbus_connection_send_with_reply(connection, message,
+                                               &call, TIMEOUT) == FALSE) {
+               connman_error("Failed to enable PAN server");
+               dbus_message_unref(message);
+               return;
+       }
+
+       if (call == NULL) {
+               connman_error("D-Bus connection not available");
+               dbus_message_unref(message);
+               return;
+       }
+
+       if (enabled == TRUE)
+               dbus_pending_call_set_notify(call, server_register_reply,
+                                               technology, NULL);
+       else
+               dbus_pending_call_set_notify(call, server_unregister_reply,
+                                               technology, NULL);
+
+       dbus_message_unref(message);
+}
+
+struct tethering_info {
+       struct connman_technology *technology;
+       const char *bridge;
+};
+
+static void enable_nap(gpointer key, gpointer value, gpointer user_data)
+{
+       struct tethering_info *info = user_data;
+       struct connman_device *device = value;
+       const char *path;
+
+       DBG("");
+
+       path = connman_device_get_string(device, "Path");
+
+       server_register(path, "nap", info->technology, info->bridge, TRUE);
+}
+
+static void disable_nap(gpointer key, gpointer value, gpointer user_data)
+{
+       struct tethering_info *info = user_data;
+       struct connman_device *device = value;
+       const char *path;
+
+       DBG("");
+
+       path = connman_device_get_string(device, "Path");
+
+       server_register(path, "nap", info->technology, info->bridge, FALSE);
+}
+
+static int tech_set_tethering(struct connman_technology *technology,
+                               const char *identifier, const char *passphrase,
+                               const char *bridge, connman_bool_t enabled)
+{
+       struct tethering_info info = {
+               .technology     = technology,
+               .bridge         = bridge,
+       };
+
+       DBG("bridge %s", bridge);
+
+       if (enabled)
+               g_hash_table_foreach(bluetooth_devices, enable_nap, &info);
+       else
+               g_hash_table_foreach(bluetooth_devices, disable_nap, &info);
+
+       return 0;
+}
+
+static struct connman_technology_driver tech_driver = {
+       .name           = "bluetooth_legacy",
+       .type           = CONNMAN_SERVICE_TYPE_BLUETOOTH,
+       .probe          = tech_probe,
+       .remove         = tech_remove,
+       .set_tethering  = tech_set_tethering,
+};
+
+static guint watch;
+static guint added_watch;
+static guint removed_watch;
+static guint adapter_watch;
+static guint device_watch;
+static guint device_removed_watch;
+static guint network_watch;
+
+static int bluetooth_init(void)
+{
+       int err;
+
+       connection = connman_dbus_get_connection();
+       if (connection == NULL)
+               return -EIO;
+
+       watch = g_dbus_add_service_watch(connection, BLUEZ_SERVICE,
+                       bluetooth_connect, bluetooth_disconnect, NULL, NULL);
+
+       added_watch = g_dbus_add_signal_watch(connection, BLUEZ_SERVICE, NULL,
+                                               BLUEZ_MANAGER_INTERFACE,
+                                               ADAPTER_ADDED, adapter_added,
+                                               NULL, NULL);
+
+       removed_watch = g_dbus_add_signal_watch(connection, BLUEZ_SERVICE, NULL,
+                                               BLUEZ_MANAGER_INTERFACE,
+                                               ADAPTER_REMOVED, adapter_removed,
+                                               NULL, NULL);
+
+       adapter_watch = g_dbus_add_signal_watch(connection, BLUEZ_SERVICE,
+                                               NULL, BLUEZ_ADAPTER_INTERFACE,
+                                               PROPERTY_CHANGED, adapter_changed,
+                                               NULL, NULL);
+
+       device_removed_watch = g_dbus_add_signal_watch(connection,
+                                               BLUEZ_SERVICE, NULL,
+                                               BLUEZ_ADAPTER_INTERFACE,
+                                               DEVICE_REMOVED, device_removed,
+                                               NULL, NULL);
+
+       device_watch = g_dbus_add_signal_watch(connection, BLUEZ_SERVICE, NULL,
+                                               BLUEZ_DEVICE_INTERFACE,
+                                               PROPERTY_CHANGED, device_changed,
+                                               NULL, NULL);
+
+       network_watch = g_dbus_add_signal_watch(connection, BLUEZ_SERVICE,
+                                               NULL, BLUEZ_NETWORK_INTERFACE,
+                                               PROPERTY_CHANGED, network_changed,
+                                               NULL, NULL);
+
+       if (watch == 0 || added_watch == 0 || removed_watch == 0
+                       || adapter_watch == 0 || network_watch == 0
+                               || device_watch == 0
+                                       || device_removed_watch == 0) {
+               err = -EIO;
+               goto remove;
+       }
+
+       err = connman_network_driver_register(&pan_driver);
+       if (err < 0)
+               goto remove;
+
+       err = connman_device_driver_register(&bluetooth_driver);
+       if (err < 0) {
+               connman_network_driver_unregister(&pan_driver);
+               goto remove;
+       }
+
+       err = connman_technology_driver_register(&tech_driver);
+       if (err < 0) {
+               connman_device_driver_unregister(&bluetooth_driver);
+               connman_network_driver_unregister(&pan_driver);
+               goto remove;
+       }
+
+       return 0;
+
+remove:
+       g_dbus_remove_watch(connection, watch);
+       g_dbus_remove_watch(connection, added_watch);
+       g_dbus_remove_watch(connection, removed_watch);
+       g_dbus_remove_watch(connection, adapter_watch);
+       g_dbus_remove_watch(connection, device_removed_watch);
+       g_dbus_remove_watch(connection, device_watch);
+       g_dbus_remove_watch(connection, network_watch);
+
+       dbus_connection_unref(connection);
+
+       return err;
+}
+
+static void bluetooth_exit(void)
+{
+       g_dbus_remove_watch(connection, watch);
+       g_dbus_remove_watch(connection, added_watch);
+       g_dbus_remove_watch(connection, removed_watch);
+       g_dbus_remove_watch(connection, adapter_watch);
+       g_dbus_remove_watch(connection, device_removed_watch);
+       g_dbus_remove_watch(connection, device_watch);
+       g_dbus_remove_watch(connection, network_watch);
+
+       bluetooth_disconnect(connection, NULL);
+
+       connman_technology_driver_unregister(&tech_driver);
+
+       connman_device_driver_unregister(&bluetooth_driver);
+       connman_network_driver_unregister(&pan_driver);
+
+       dbus_connection_unref(connection);
+}
+
+CONNMAN_PLUGIN_DEFINE(bluetooth, "Bluetooth technology plugin", VERSION,
+               CONNMAN_PLUGIN_PRIORITY_DEFAULT, bluetooth_init, bluetooth_exit)
diff --git a/plugins/dundee.c b/plugins/dundee.c
new file mode 100644 (file)
index 0000000..d0ecdef
--- /dev/null
@@ -0,0 +1,852 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2012  BMW Car IT GmbH. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <errno.h>
+
+#include <gdbus.h>
+
+#define CONNMAN_API_SUBJECT_TO_CHANGE
+#include <connman/plugin.h>
+#include <connman/device.h>
+#include <connman/network.h>
+#include <connman/inet.h>
+#include <connman/dbus.h>
+
+#define DUNDEE_SERVICE                 "org.ofono.dundee"
+#define DUNDEE_MANAGER_INTERFACE       DUNDEE_SERVICE ".Manager"
+#define DUNDEE_DEVICE_INTERFACE                DUNDEE_SERVICE ".Device"
+
+#define DEVICE_ADDED                   "DeviceAdded"
+#define DEVICE_REMOVED                 "DeviceRemoved"
+#define PROPERTY_CHANGED               "PropertyChanged"
+
+#define GET_PROPERTIES                 "GetProperties"
+#define SET_PROPERTY                   "SetProperty"
+#define GET_DEVICES                    "GetDevices"
+
+#define TIMEOUT 40000
+
+static DBusConnection *connection;
+
+static GHashTable *dundee_devices = NULL;
+
+struct dundee_data {
+       char *path;
+       char *name;
+
+       struct connman_device *device;
+       struct connman_network *network;
+
+       connman_bool_t active;
+
+       int index;
+
+       /* IPv4 Settings */
+       enum connman_ipconfig_method method;
+       struct connman_ipaddress *address;
+       char *nameservers;
+
+       DBusPendingCall *call;
+};
+
+static char *get_ident(const char *path)
+{
+       char *pos;
+
+       if (*path != '/')
+               return NULL;
+
+       pos = strrchr(path, '/');
+       if (pos == NULL)
+               return NULL;
+
+       return pos + 1;
+}
+
+static void create_device(struct dundee_data *info)
+{
+       struct connman_device *device;
+       char *ident;
+
+       DBG("%s", info->path);
+
+       ident = g_strdup(get_ident(info->path));
+       device = connman_device_create(ident, CONNMAN_DEVICE_TYPE_BLUETOOTH);
+       if (device == NULL)
+               goto out;
+
+       DBG("device %p", device);
+
+       connman_device_set_ident(device, ident);
+
+       connman_device_set_string(device, "Path", info->path);
+
+       connman_device_set_data(device, info);
+
+       if (connman_device_register(device) < 0) {
+               connman_error("Failed to register DUN device");
+               connman_device_unref(device);
+               goto out;
+       }
+
+       info->device = device;
+
+out:
+       g_free(ident);
+}
+
+static void destroy_device(struct dundee_data *info)
+{
+       connman_device_set_powered(info->device, FALSE);
+
+       if (info->call != NULL)
+               dbus_pending_call_cancel(info->call);
+
+       if (info->network != NULL) {
+               connman_device_remove_network(info->device, info->network);
+               connman_network_unref(info->network);
+               info->network = NULL;
+       }
+
+       connman_device_unregister(info->device);
+       connman_device_unref(info->device);
+
+       info->device = NULL;
+}
+
+static void device_destroy(gpointer data)
+{
+       struct dundee_data *info = data;
+
+       if (info->device != NULL)
+               destroy_device(info);
+
+       g_free(info->path);
+       g_free(info->name);
+
+       g_free(info);
+}
+
+static void create_network(struct dundee_data *info)
+{
+       struct connman_network *network;
+       const char *group;
+
+       DBG("%s", info->path);
+
+       network = connman_network_create(info->path,
+                               CONNMAN_NETWORK_TYPE_BLUETOOTH_DUN);
+       if (network == NULL)
+               return;
+
+       DBG("network %p", network);
+
+       connman_network_set_data(network, info);
+
+       connman_network_set_string(network, "Path",
+                               info->path);
+
+       connman_network_set_name(network, info->name);
+
+       group = get_ident(info->path);
+       connman_network_set_group(network, group);
+
+       connman_network_set_available(network, TRUE);
+
+       if (connman_device_add_network(info->device, network) < 0) {
+               connman_network_unref(network);
+               return;
+       }
+
+       info->network = network;
+}
+
+static void set_connected(struct dundee_data *info)
+{
+       DBG("%s", info->path);
+
+       connman_inet_ifup(info->index);
+
+       connman_network_set_index(info->network, info->index);
+       connman_network_set_ipv4_method(info->network,
+                                       CONNMAN_IPCONFIG_METHOD_FIXED);
+       connman_network_set_ipaddress(info->network, info->address);
+       connman_network_set_nameservers(info->network, info->nameservers);
+
+       connman_network_set_connected(info->network, TRUE);
+}
+
+static void set_disconnected(struct dundee_data *info)
+{
+       DBG("%s", info->path);
+
+       connman_network_set_connected(info->network, FALSE);
+       connman_inet_ifdown(info->index);
+}
+
+static void set_property_reply(DBusPendingCall *call, void *user_data)
+{
+       struct dundee_data *info = user_data;
+       DBusMessage *reply;
+       DBusError error;
+
+       DBG("%s", info->path);
+
+       info->call = NULL;
+
+       dbus_error_init(&error);
+
+       reply = dbus_pending_call_steal_reply(call);
+
+       if (dbus_set_error_from_message(&error, reply)) {
+               connman_error("Failed to change property: %s %s %s",
+                               info->path, error.name, error.message);
+               dbus_error_free(&error);
+
+               connman_network_set_error(info->network,
+                                       CONNMAN_NETWORK_ERROR_ASSOCIATE_FAIL);
+       }
+
+       dbus_message_unref(reply);
+
+       dbus_pending_call_unref(call);
+}
+
+static int set_property(struct dundee_data *info,
+                       const char *property, int type, void *value)
+{
+       DBusMessage *message;
+       DBusMessageIter iter;
+
+       DBG("%s %s", info->path, property);
+
+       message = dbus_message_new_method_call(DUNDEE_SERVICE, info->path,
+                                       DUNDEE_DEVICE_INTERFACE, SET_PROPERTY);
+       if (message == NULL)
+               return -ENOMEM;
+
+       dbus_message_iter_init_append(message, &iter);
+       connman_dbus_property_append_basic(&iter, property, type, value);
+
+       if (dbus_connection_send_with_reply(connection, message,
+                       &info->call, TIMEOUT) == FALSE) {
+               connman_error("Failed to change property: %s %s",
+                               info->path, property);
+               dbus_message_unref(message);
+               return -EINVAL;
+       }
+
+       if (info->call == NULL) {
+               connman_error("D-Bus connection not available");
+               dbus_message_unref(message);
+               return -EINVAL;
+       }
+
+       dbus_pending_call_set_notify(info->call, set_property_reply,
+                                       info, NULL);
+
+       dbus_message_unref(message);
+
+       return -EINPROGRESS;
+}
+
+static int device_set_active(struct dundee_data *info)
+{
+       dbus_bool_t active = TRUE;
+
+       DBG("%s", info->path);
+
+       return set_property(info, "Active", DBUS_TYPE_BOOLEAN,
+                               &active);
+}
+
+static int device_set_inactive(struct dundee_data *info)
+{
+       dbus_bool_t active = FALSE;
+       int err;
+
+       DBG("%s", info->path);
+
+       err = set_property(info, "Active", DBUS_TYPE_BOOLEAN,
+                               &active);
+       if (err == -EINPROGRESS)
+               return 0;
+
+       return err;
+}
+
+static int network_probe(struct connman_network *network)
+{
+       DBG("network %p", network);
+
+       return 0;
+}
+
+static void network_remove(struct connman_network *network)
+{
+       DBG("network %p", network);
+}
+
+static int network_connect(struct connman_network *network)
+{
+       struct dundee_data *info = connman_network_get_data(network);
+
+       DBG("network %p", network);
+
+       return device_set_active(info);
+}
+
+static int network_disconnect(struct connman_network *network)
+{
+       struct dundee_data *info = connman_network_get_data(network);
+
+       DBG("network %p", network);
+
+       return device_set_inactive(info);
+}
+
+static struct connman_network_driver network_driver = {
+       .name           = "network",
+       .type           = CONNMAN_NETWORK_TYPE_BLUETOOTH_DUN,
+       .probe          = network_probe,
+       .remove         = network_remove,
+       .connect        = network_connect,
+       .disconnect     = network_disconnect,
+};
+
+static int dundee_probe(struct connman_device *device)
+{
+       GHashTableIter iter;
+       gpointer key, value;
+
+       DBG("device %p", device);
+
+       if (dundee_devices == NULL)
+               return -ENOTSUP;
+
+       g_hash_table_iter_init(&iter, dundee_devices);
+
+       while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
+               struct dundee_data *info = value;
+
+               if (device == info->device)
+                       return 0;
+       }
+
+       return -ENOTSUP;
+}
+
+static void dundee_remove(struct connman_device *device)
+{
+       DBG("device %p", device);
+}
+
+static int dundee_enable(struct connman_device *device)
+{
+       DBG("device %p", device);
+
+       return 0;
+}
+
+static int dundee_disable(struct connman_device *device)
+{
+       DBG("device %p", device);
+
+       return 0;
+}
+
+static struct connman_device_driver dundee_driver = {
+       .name           = "dundee",
+       .type           = CONNMAN_DEVICE_TYPE_BLUETOOTH,
+       .probe          = dundee_probe,
+       .remove         = dundee_remove,
+       .enable         = dundee_enable,
+       .disable        = dundee_disable,
+};
+
+static char *extract_nameservers(DBusMessageIter *array)
+{
+       DBusMessageIter entry;
+       char *nameservers = NULL;
+       char *tmp;
+
+       dbus_message_iter_recurse(array, &entry);
+
+       while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING) {
+               const char *nameserver;
+
+               dbus_message_iter_get_basic(&entry, &nameserver);
+
+               if (nameservers == NULL) {
+                       nameservers = g_strdup(nameserver);
+               } else {
+                       tmp = nameservers;
+                       nameservers = g_strdup_printf("%s %s", tmp, nameserver);
+                       g_free(tmp);
+               }
+
+               dbus_message_iter_next(&entry);
+       }
+
+       return nameservers;
+}
+
+static void extract_settings(DBusMessageIter *array,
+                               struct dundee_data *info)
+{
+       DBusMessageIter dict;
+       char *address = NULL, *gateway = NULL;
+       char *nameservers = NULL;
+       const char *interface = NULL;
+       int index = -1;
+
+       if (dbus_message_iter_get_arg_type(array) != DBUS_TYPE_ARRAY)
+               return;
+
+       dbus_message_iter_recurse(array, &dict);
+
+       while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+               DBusMessageIter entry, value;
+               const char *key, *val;
+
+               dbus_message_iter_recurse(&dict, &entry);
+               dbus_message_iter_get_basic(&entry, &key);
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_recurse(&entry, &value);
+
+               if (g_str_equal(key, "Interface") == TRUE) {
+                       dbus_message_iter_get_basic(&value, &interface);
+
+                       DBG("Interface %s", interface);
+
+                       index = connman_inet_ifindex(interface);
+
+                       DBG("index %d", index);
+
+                       if (index < 0)
+                               break;
+               } else if (g_str_equal(key, "Address") == TRUE) {
+                       dbus_message_iter_get_basic(&value, &val);
+
+                       address = g_strdup(val);
+
+                       DBG("Address %s", address);
+               } else if (g_str_equal(key, "DomainNameServers") == TRUE) {
+                       nameservers = extract_nameservers(&value);
+
+                       DBG("Nameservers %s", nameservers);
+               } else if (g_str_equal(key, "Gateway") == TRUE) {
+                       dbus_message_iter_get_basic(&value, &val);
+
+                       gateway = g_strdup(val);
+
+                       DBG("Gateway %s", gateway);
+               }
+
+               dbus_message_iter_next(&dict);
+       }
+
+       if (index < 0)
+               goto out;
+
+       info->address = connman_ipaddress_alloc(CONNMAN_IPCONFIG_TYPE_IPV4);
+       if (info->address == NULL)
+               goto out;
+
+       info->index = index;
+       connman_ipaddress_set_ipv4(info->address, address, NULL, gateway);
+
+       info->nameservers = nameservers;
+
+out:
+       if (info->nameservers != nameservers)
+               g_free(nameservers);
+
+       g_free(address);
+       g_free(gateway);
+}
+
+static gboolean device_changed(DBusConnection *connection,
+                               DBusMessage *message,
+                               void *user_data)
+{
+       const char *path = dbus_message_get_path(message);
+       struct dundee_data *info = NULL;
+       DBusMessageIter iter, value;
+       const char *key;
+       const char *signature = DBUS_TYPE_STRING_AS_STRING
+               DBUS_TYPE_VARIANT_AS_STRING;
+
+       if (dbus_message_has_signature(message, signature) == FALSE) {
+               connman_error("dundee signature does not match");
+               return TRUE;
+       }
+
+       info = g_hash_table_lookup(dundee_devices, path);
+       if (info == NULL)
+               return TRUE;
+
+       if (dbus_message_iter_init(message, &iter) == FALSE)
+               return TRUE;
+
+       dbus_message_iter_get_basic(&iter, &key);
+
+       dbus_message_iter_next(&iter);
+       dbus_message_iter_recurse(&iter, &value);
+
+       /*
+        * Dundee guarantees the ordering of Settings and
+        * Active. Settings will always be send before Active = True.
+        * That means we don't have to order here.
+        */
+       if (g_str_equal(key, "Active") == TRUE) {
+               dbus_message_iter_get_basic(&value, &info->active);
+
+               DBG("%s Active %d", info->path, info->active);
+
+               if (info->active == TRUE)
+                       set_connected(info);
+               else
+                       set_disconnected(info);
+       } else if (g_str_equal(key, "Settings") == TRUE) {
+               DBG("%s Settings", info->path);
+
+               extract_settings(&value, info);
+       } else if (g_str_equal(key, "Name") == TRUE) {
+               char *name;
+
+               dbus_message_iter_get_basic(&value, &name);
+
+               g_free(info->name);
+               info->name = g_strdup(name);
+
+               DBG("%s Name %s", info->path, info->name);
+
+               connman_network_set_name(info->network, info->name);
+               connman_network_update(info->network);
+       }
+
+       return TRUE;
+}
+
+static void add_device(const char *path, DBusMessageIter *properties)
+{
+       struct dundee_data *info;
+
+       info = g_hash_table_lookup(dundee_devices, path);
+       if (info != NULL)
+               return;
+
+       info = g_try_new0(struct dundee_data, 1);
+       if (info == NULL)
+               return;
+
+       info->path = g_strdup(path);
+
+       while (dbus_message_iter_get_arg_type(properties) ==
+                       DBUS_TYPE_DICT_ENTRY) {
+               DBusMessageIter entry, value;
+               const char *key;
+
+               dbus_message_iter_recurse(properties, &entry);
+               dbus_message_iter_get_basic(&entry, &key);
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_recurse(&entry, &value);
+
+               if (g_str_equal(key, "Active") == TRUE) {
+                       dbus_message_iter_get_basic(&value, &info->active);
+
+                       DBG("%s Active %d", info->path, info->active);
+               } else if (g_str_equal(key, "Settings") == TRUE) {
+                       DBG("%s Settings", info->path);
+
+                       extract_settings(&value, info);
+               } else if (g_str_equal(key, "Name") == TRUE) {
+                       char *name;
+
+                       dbus_message_iter_get_basic(&value, &name);
+
+                       info->name = g_strdup(name);
+
+                       DBG("%s Name %s", info->path, info->name);
+               }
+
+               dbus_message_iter_next(properties);
+       }
+
+       g_hash_table_insert(dundee_devices, g_strdup(path), info);
+
+       create_device(info);
+       create_network(info);
+
+       if (info->active == TRUE)
+               set_connected(info);
+}
+
+static gboolean device_added(DBusConnection *connection, DBusMessage *message,
+                               void *user_data)
+{
+       DBusMessageIter iter, properties;
+       const char *path;
+       const char *signature = DBUS_TYPE_OBJECT_PATH_AS_STRING
+               DBUS_TYPE_ARRAY_AS_STRING
+               DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+               DBUS_TYPE_STRING_AS_STRING
+               DBUS_TYPE_VARIANT_AS_STRING
+               DBUS_DICT_ENTRY_END_CHAR_AS_STRING;
+
+       if (dbus_message_has_signature(message, signature) == FALSE) {
+               connman_error("dundee signature does not match");
+               return TRUE;
+       }
+
+       DBG("");
+
+       if (dbus_message_iter_init(message, &iter) == FALSE)
+               return TRUE;
+
+       dbus_message_iter_get_basic(&iter, &path);
+
+       dbus_message_iter_next(&iter);
+       dbus_message_iter_recurse(&iter, &properties);
+
+       add_device(path, &properties);
+
+       return TRUE;
+}
+
+static void remove_device(DBusConnection *connection, const char *path)
+{
+       DBG("path %s", path);
+
+       g_hash_table_remove(dundee_devices, path);
+}
+
+static gboolean device_removed(DBusConnection *connection, DBusMessage *message,
+                               void *user_data)
+{
+       const char *path;
+       const char *signature = DBUS_TYPE_OBJECT_PATH_AS_STRING;
+
+       if (dbus_message_has_signature(message, signature) == FALSE) {
+               connman_error("dundee signature does not match");
+               return TRUE;
+       }
+
+       dbus_message_get_args(message, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+                               DBUS_TYPE_INVALID);
+       remove_device(connection, path);
+       return TRUE;
+}
+
+static void manager_get_devices_reply(DBusPendingCall *call, void *user_data)
+{
+       DBusMessage *reply;
+       DBusError error;
+       DBusMessageIter array, dict;
+       const char *signature = DBUS_TYPE_ARRAY_AS_STRING
+               DBUS_STRUCT_BEGIN_CHAR_AS_STRING
+               DBUS_TYPE_OBJECT_PATH_AS_STRING
+               DBUS_TYPE_ARRAY_AS_STRING
+               DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+               DBUS_TYPE_STRING_AS_STRING
+               DBUS_TYPE_VARIANT_AS_STRING
+               DBUS_DICT_ENTRY_END_CHAR_AS_STRING
+               DBUS_STRUCT_END_CHAR_AS_STRING;
+
+       DBG("");
+
+       reply = dbus_pending_call_steal_reply(call);
+
+       if (dbus_message_has_signature(reply, signature) == FALSE) {
+               connman_error("dundee signature does not match");
+               goto done;
+       }
+
+       dbus_error_init(&error);
+
+       if (dbus_set_error_from_message(&error, reply) == TRUE) {
+               connman_error("%s", error.message);
+               dbus_error_free(&error);
+               goto done;
+       }
+
+       if (dbus_message_iter_init(reply, &array) == FALSE)
+               goto done;
+
+       dbus_message_iter_recurse(&array, &dict);
+
+       while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_STRUCT) {
+               DBusMessageIter value, properties;
+               const char *path;
+
+               dbus_message_iter_recurse(&dict, &value);
+               dbus_message_iter_get_basic(&value, &path);
+
+               dbus_message_iter_next(&value);
+               dbus_message_iter_recurse(&value, &properties);
+
+               add_device(path, &properties);
+
+               dbus_message_iter_next(&dict);
+       }
+
+done:
+       dbus_message_unref(reply);
+
+       dbus_pending_call_unref(call);
+}
+
+static int manager_get_devices(void)
+{
+       DBusMessage *message;
+       DBusPendingCall *call;
+
+       DBG("");
+
+       message = dbus_message_new_method_call(DUNDEE_SERVICE, "/",
+                                       DUNDEE_MANAGER_INTERFACE, GET_DEVICES);
+       if (message == NULL)
+               return -ENOMEM;
+
+       if (dbus_connection_send_with_reply(connection, message,
+                                               &call, TIMEOUT) == FALSE) {
+               connman_error("Failed to call GetDevices()");
+               dbus_message_unref(message);
+               return -EINVAL;
+       }
+
+       if (call == NULL) {
+               connman_error("D-Bus connection not available");
+               dbus_message_unref(message);
+               return -EINVAL;
+       }
+
+       dbus_pending_call_set_notify(call, manager_get_devices_reply,
+                                       NULL, NULL);
+
+       dbus_message_unref(message);
+
+       return -EINPROGRESS;
+}
+
+static void dundee_connect(DBusConnection *connection, void *user_data)
+{
+       DBG("connection %p", connection);
+
+       dundee_devices = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                       g_free, device_destroy);
+
+       manager_get_devices();
+}
+
+static void dundee_disconnect(DBusConnection *connection, void *user_data)
+{
+       DBG("connection %p", connection);
+
+       g_hash_table_destroy(dundee_devices);
+       dundee_devices = NULL;
+}
+
+static guint watch;
+static guint added_watch;
+static guint removed_watch;
+static guint device_watch;
+
+static int dundee_init(void)
+{
+       int err;
+
+       connection = connman_dbus_get_connection();
+       if (connection == NULL)
+               return -EIO;
+
+       watch = g_dbus_add_service_watch(connection, DUNDEE_SERVICE,
+                       dundee_connect, dundee_disconnect, NULL, NULL);
+
+       added_watch = g_dbus_add_signal_watch(connection, DUNDEE_SERVICE, NULL,
+                                               DUNDEE_MANAGER_INTERFACE,
+                                               DEVICE_ADDED, device_added,
+                                               NULL, NULL);
+
+       removed_watch = g_dbus_add_signal_watch(connection, DUNDEE_SERVICE,
+                                               NULL, DUNDEE_MANAGER_INTERFACE,
+                                               DEVICE_REMOVED, device_removed,
+                                               NULL, NULL);
+
+       device_watch = g_dbus_add_signal_watch(connection, DUNDEE_SERVICE,
+                                               NULL, DUNDEE_DEVICE_INTERFACE,
+                                               PROPERTY_CHANGED,
+                                               device_changed,
+                                               NULL, NULL);
+
+
+       if (watch == 0 || added_watch == 0 || removed_watch == 0 ||
+                       device_watch == 0) {
+               err = -EIO;
+               goto remove;
+       }
+
+       err = connman_network_driver_register(&network_driver);
+       if (err < 0)
+               goto remove;
+
+       err = connman_device_driver_register(&dundee_driver);
+       if (err < 0) {
+               connman_network_driver_unregister(&network_driver);
+               goto remove;
+       }
+
+       return 0;
+
+remove:
+       g_dbus_remove_watch(connection, watch);
+       g_dbus_remove_watch(connection, added_watch);
+       g_dbus_remove_watch(connection, removed_watch);
+       g_dbus_remove_watch(connection, device_watch);
+
+       dbus_connection_unref(connection);
+
+       return err;
+}
+
+static void dundee_exit(void)
+{
+       g_dbus_remove_watch(connection, watch);
+       g_dbus_remove_watch(connection, added_watch);
+       g_dbus_remove_watch(connection, removed_watch);
+       g_dbus_remove_watch(connection, device_watch);
+
+       connman_device_driver_unregister(&dundee_driver);
+       connman_network_driver_unregister(&network_driver);
+
+       dbus_connection_unref(connection);
+}
+
+CONNMAN_PLUGIN_DEFINE(dundee, "Dundee plugin", VERSION,
+               CONNMAN_PLUGIN_PRIORITY_DEFAULT, dundee_init, dundee_exit)
index 68a721b..fc0fdc7 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
 #endif
 
 #include <glib.h>
+#if defined TIZEN_EXT
+#include <stdlib.h>
+#include <string.h>
+#endif
 
 #define CONNMAN_API_SUBJECT_TO_CHANGE
 #include <connman/technology.h>
@@ -63,6 +67,8 @@ static int cable_connect(struct connman_network *network)
 {
        DBG("network %p", network);
 
+       connman_network_set_connected(network, TRUE);
+
        return 0;
 }
 
@@ -70,6 +76,8 @@ static int cable_disconnect(struct connman_network *network)
 {
        DBG("network %p", network);
 
+       connman_network_set_connected(network, FALSE);
+
        return 0;
 }
 
@@ -85,14 +93,18 @@ static struct connman_network_driver cable_driver = {
 #if defined TIZEN_EXT
 static void set_proxy(struct connman_network *network)
 {
-       const char http_proxy[] = "http_proxy";
+       struct connman_service *service;
+       const char *http_proxy = "http_proxy";
        char *proxy = NULL;
 
+       service = connman_service_lookup_from_network(network);
+       if (service == NULL)
+               return;
+
        proxy = getenv(http_proxy);
-       DBG("Get system proxy: %s", proxy);
 
-       if(proxy != NULL && strlen(proxy) > 8)
-               connman_network_set_proxy(network, proxy);
+       if (proxy != NULL && strlen(proxy) > 8)
+               connman_service_set_proxy(service, proxy, FALSE);
 }
 #endif
 
@@ -121,12 +133,9 @@ static void add_network(struct connman_device *device,
 
        connman_network_set_group(network, "cable");
 
-       connman_network_set_connected(network, TRUE);
-
 #if defined TIZEN_EXT
        set_proxy(network);
 #endif
-
        ethernet->network = network;
 }
 
@@ -327,7 +336,6 @@ static struct connman_technology_driver tech_driver = {
        .set_tethering          = tech_set_tethering,
 };
 
-#if defined TIZEN_EXT
 static int eth_probe(struct connman_technology *technology)
 {
        return 0;
@@ -344,17 +352,14 @@ static struct connman_technology_driver eth_driver = {
        .probe                  = eth_probe,
        .remove                 = eth_remove,
 };
-#endif
 
 static int ethernet_init(void)
 {
        int err;
 
-#if defined TIZEN_EXT
        err = connman_technology_driver_register(&eth_driver);
        if (err < 0)
                return err;
-#endif
 
        err = connman_network_driver_register(&cable_driver);
        if (err < 0)
@@ -378,9 +383,7 @@ static int ethernet_init(void)
 
 static void ethernet_exit(void)
 {
-#if defined TIZEN_EXT
        connman_technology_driver_unregister(&eth_driver);
-#endif
 
        connman_technology_driver_unregister(&tech_driver);
 
diff --git a/plugins/fake.c b/plugins/fake.c
deleted file mode 100644 (file)
index 6ec4ba5..0000000
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- *
- *  Connection Manager
- *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License version 2 as
- *  published by the Free Software Foundation.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-
-#define CONNMAN_API_SUBJECT_TO_CHANGE
-#include <connman/plugin.h>
-#include <connman/device.h>
-#include <connman/log.h>
-
-static void create_network(struct connman_device *device, const char *name)
-{
-       struct connman_network *network;
-
-       network = connman_network_create(name, CONNMAN_NETWORK_TYPE_VENDOR);
-       if (network == NULL)
-               return;
-
-       connman_network_register(network);
-
-       connman_device_add_network(device, network);
-       connman_network_unref(network);
-}
-
-static int device_probe(struct connman_device *device)
-{
-       DBG("");
-
-       return 0;
-}
-
-static void device_remove(struct connman_device *device)
-{
-       DBG("");
-}
-
-static int device_enable(struct connman_device *device)
-{
-       DBG("");
-
-       create_network(device, "network_one");
-       create_network(device, "network_two");
-
-       return 0;
-}
-
-static int device_disable(struct connman_device *device)
-{
-       DBG("");
-
-       return 0;
-}
-
-static struct connman_device_driver device_driver = {
-       .name           = "fake",
-       .type           = CONNMAN_DEVICE_TYPE_VENDOR,
-       .probe          = device_probe,
-       .remove         = device_remove,
-       .enable         = device_enable,
-       .disable        = device_disable,
-};
-
-static void create_device(const char *name)
-{
-       struct connman_device *device;
-
-       device = connman_device_create(name, CONNMAN_DEVICE_TYPE_VENDOR);
-       if (device == NULL)
-               return;
-
-       connman_device_register(device);
-       connman_device_unref(device);
-}
-
-static int fake_init(void)
-{
-       create_device("fake");
-
-       return connman_device_driver_register(&device_driver);
-}
-
-static void fake_exit(void)
-{
-       connman_device_driver_unregister(&device_driver);
-}
-
-CONNMAN_PLUGIN_DEFINE(fake, "Tesing plugin", VERSION,
-               CONNMAN_PLUGIN_PRIORITY_DEFAULT, fake_init, fake_exit)
diff --git a/plugins/google.c b/plugins/google.c
deleted file mode 100644 (file)
index 0afc31c..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- *
- *  Connection Manager
- *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License version 2 as
- *  published by the Free Software Foundation.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#define CONNMAN_API_SUBJECT_TO_CHANGE
-#include <connman/plugin.h>
-#include <connman/resolver.h>
-
-#define GOOGLE_DNS1    "8.8.8.8"
-#define GOOGLE_DNS2    "8.8.4.4"
-
-static int google_init(void)
-{
-       connman_resolver_append_public_server(GOOGLE_DNS1);
-       connman_resolver_append_public_server(GOOGLE_DNS2);
-
-       return 0;
-}
-
-static void google_exit(void)
-{
-       connman_resolver_remove_public_server(GOOGLE_DNS2);
-       connman_resolver_remove_public_server(GOOGLE_DNS1);
-}
-
-CONNMAN_PLUGIN_DEFINE(google, "Google Public DNS plugin", VERSION,
-                       CONNMAN_PLUGIN_PRIORITY_LOW, google_init, google_exit)
index 911fa35..20521b0 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  hh2serial GPS
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
index a36e4aa..3b014d3 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
@@ -69,7 +69,6 @@ static void iospm_service_enabled(enum connman_service_type type,
        case CONNMAN_SERVICE_TYPE_SYSTEM:
        case CONNMAN_SERVICE_TYPE_ETHERNET:
        case CONNMAN_SERVICE_TYPE_WIFI:
-       case CONNMAN_SERVICE_TYPE_WIMAX:
        case CONNMAN_SERVICE_TYPE_CELLULAR:
        case CONNMAN_SERVICE_TYPE_GPS:
        case CONNMAN_SERVICE_TYPE_VPN:
diff --git a/plugins/iwmx.c b/plugins/iwmx.c
deleted file mode 100644 (file)
index e79c1af..0000000
+++ /dev/null
@@ -1,588 +0,0 @@
-/*
- *
- *  Connection Manager
- *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License version 2 as
- *  published by the Free Software Foundation.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <errno.h>
-#include <string.h>
-
-#include <glib.h>
-
-#define CONNMAN_API_SUBJECT_TO_CHANGE
-#include <connman/plugin.h>
-#include <connman/device.h>
-#include <connman/inet.h>
-#include <connman/log.h>
-
-#include <WiMaxAPI.h>
-#include <WiMaxAPIEx.h>
-
-#include "iwmx.h"
-
-/*
- * Connman plugin interface
- *
- * This part deals with the connman internals
- */
-
-/* WiMAX network driver probe/remove, nops */
-static int iwmx_cm_network_probe(struct connman_network *nw)
-{
-       return 0;
-}
-
-static void iwmx_cm_network_remove(struct connman_network *nw)
-{
-}
-
-/*
- * Called by connman when it wants us to tell the device to connect to
- * the network @network_el; the device is @network_el->parent.
- *
- * We do a synchronous call to start the connection; the logic
- * attached to the status change callback will update the connman
- * internals once the change happens.
- */
-static int iwmx_cm_network_connect(struct connman_network *nw)
-{
-       int result;
-       struct wmxsdk *wmxsdk;
-       const char *station_name = connman_network_get_identifier(nw);
-
-       wmxsdk = connman_device_get_data(connman_network_get_device(nw));
-       result = iwmx_sdk_connect(wmxsdk, nw);
-       DBG("(nw %p [%s] wmxsdk %p) = %d\n", nw, station_name, wmxsdk, result);
-       return result;
-}
-
-/*
- * Called by connman to have the device @nw->parent
- * disconnected from @nw.
- *
- * We do a synchronous call to start the disconnection; the logic
- * attached to the status change callback will update the connman
- * internals once the change happens.
- */
-static int iwmx_cm_network_disconnect(struct connman_network *nw)
-{
-       int result;
-       struct wmxsdk *wmxsdk;
-       const char *station_name = connman_network_get_identifier(nw);
-
-       wmxsdk = connman_device_get_data(connman_network_get_device(nw));
-       result = iwmx_sdk_disconnect(wmxsdk);
-       DBG("(nw %p [%s] wmxsdk %p) = %d\n", nw, station_name, wmxsdk, result);
-       return 0;
-}
-
-/*
- * "Driver" for the networks detected by a device.
- */
-static struct connman_network_driver iwmx_cm_network_driver = {
-       .name           = "iwmx",
-       .type           = CONNMAN_NETWORK_TYPE_WIMAX,
-       .probe          = iwmx_cm_network_probe,
-       .remove         = iwmx_cm_network_remove,
-       .connect        = iwmx_cm_network_connect,
-       .disconnect     = iwmx_cm_network_disconnect,
-};
-
-/*
- * A (maybe) new network is available, create/update its data
- *
- * If the network is new, we create and register a new element; if it
- * is not, we reuse the one in the list.
- *
- * NOTE:
- *   wmxsdk->network_mutex has to be locked
- */
-struct connman_network *__iwmx_cm_network_available(
-                       struct wmxsdk *wmxsdk, const char *station_name,
-                       const void *sdk_nspname, size_t sdk_nspname_size,
-                                                               int strength)
-{
-       struct connman_network *nw = NULL;
-       struct connman_device *dev = wmxsdk->dev;
-       char group[3 * strlen(station_name) + 1];
-       unsigned cnt;
-
-       nw = connman_device_get_network(dev, station_name);
-       if (nw == NULL) {
-               DBG("new network %s", station_name);
-               nw = connman_network_create(station_name,
-                                           CONNMAN_NETWORK_TYPE_WIMAX);
-               connman_network_register(nw);
-               connman_network_set_index(nw, connman_device_get_index(dev));
-               connman_network_set_name(nw, station_name);
-               connman_network_set_blob(nw, "WiMAX.NSP.name",
-                                        sdk_nspname, sdk_nspname_size);
-               /* FIXME: add roaming info? */
-               /* Set the group name -- this has to be a unique
-                * [a-zA-Z0-9_] string common to all the networks that
-                * are actually the same provider. In WiMAX each
-                * network from the CAPI is a single provider, so we
-                * just set this as the network name, encoded in
-                * hex. */
-               for (cnt = 0; station_name[cnt] != 0; cnt++)
-                       sprintf(group + 3 * cnt, "%02x", station_name[cnt]);
-               group[3 * cnt + 1] = 0;
-               connman_network_set_group(nw, station_name);
-               if (connman_device_add_network(dev, nw) < 0) {
-                       connman_network_unregister(nw);
-                       connman_network_unref(nw);
-                       goto error_add;
-               }
-       } else
-               DBG("updating network %s nw %p\n", station_name, nw);
-       connman_network_set_available(nw, TRUE);
-       connman_network_set_strength(nw, strength);
-error_add:
-       return nw;
-}
-
-/*
- * A new network is available [locking version]
- *
- * See __iwmx_cm_network_available() for docs
- */
-struct connman_network *iwmx_cm_network_available(
-                       struct wmxsdk *wmxsdk, const char *station_name,
-                       const void *sdk_nspname, size_t sdk_nspname_size,
-                                                               int strength)
-{
-       struct connman_network *nw;
-
-       g_static_mutex_lock(&wmxsdk->network_mutex);
-       nw = __iwmx_cm_network_available(wmxsdk, station_name,
-                                       sdk_nspname, sdk_nspname_size,
-                                       strength);
-       g_static_mutex_unlock(&wmxsdk->network_mutex);
-       return nw;
-}
-
-/*
- * The device has been enabled, make sure connman knows
- */
-static void iwmx_cm_dev_enabled(struct wmxsdk *wmxsdk)
-{
-       struct connman_device *dev = wmxsdk->dev;
-       connman_inet_ifup(connman_device_get_index(dev));
-       connman_device_set_powered(dev, TRUE);
-}
-
-/*
- * The device has been disabled, make sure connman is aware of it.
- */
-static void iwmx_cm_dev_disabled(struct wmxsdk *wmxsdk)
-{
-       struct connman_device *dev = wmxsdk->dev;
-       connman_inet_ifdown(connman_device_get_index(dev));
-       connman_device_set_powered(dev, FALSE);
-}
-
-/*
- * The device has been (externally to connman) connnected to a
- * network, make sure connman knows.
- *
- * When the device is connected to a network, this function is called
- * to change connman's internal state to reflect the fact.
- *
- * If the change came from an external entity, that means that our
- * connect code wasn't called. Our connect code sets
- * @wmxsdk->connecting_nw to the network we were connecting
- * to. If it is unset, it means an external entity forced the device
- * to connect. In that case, we need to find out which network it was
- * connected to, and create/lookup a @nw for it.
- *
- * Once the nw is set, then we are done.
- */
-static void iwmx_cm_dev_connected(struct wmxsdk *wmxsdk)
-{
-       struct connman_network *nw;
-
-       g_mutex_lock(wmxsdk->connect_mutex);
-       nw = wmxsdk->connecting_nw;
-       if (nw == NULL) {
-               nw = __iwmx_sdk_get_connected_network(wmxsdk);
-               if (nw == NULL) {
-                       connman_error("wmxsdk: can't find connected network\n");
-                       goto error_nw_find;
-               }
-       }
-       wmxsdk->nw = connman_network_ref(nw);
-       wmxsdk->connecting_nw = NULL;
-       connman_network_set_ipv4_method(nw, CONNMAN_IPCONFIG_METHOD_DHCP);
-       connman_network_set_connected(nw, TRUE);
-       DBG("connected to network %s\n",
-           connman_network_get_identifier(nw));
-error_nw_find:
-       g_mutex_unlock(wmxsdk->connect_mutex);
-}
-
-/*
- * The device has been (externally to connman) disconnnected, make
- * sure connman knows
- *
- * We need to reverse the steps done in iwmx_cm_dev_connected().
- * If the event was caused by an external entity and we had no record
- * of being connected to a network...well, bad luck. We'll just
- * pretend it happened ok.
- */
-static void __iwmx_cm_dev_disconnected(struct wmxsdk *wmxsdk)
-{
-       struct connman_network *nw = wmxsdk->nw;
-
-       if (nw != NULL) {
-               DBG("disconnected from network %s\n",
-                                       connman_network_get_identifier(nw));
-               connman_network_set_connected(nw, FALSE);
-               connman_network_unregister(nw);
-               connman_network_unref(nw);
-               wmxsdk->nw = NULL;
-       } else
-               DBG("disconnected from unknown network\n");
-}
-
-/*
- * The device has been disconnnected, make sure connman knows
- *
- * See __iwmx_cm_dev_disconnect() for more information.
- */
-static void iwmx_cm_dev_disconnected(struct wmxsdk *wmxsdk)
-{
-       g_mutex_lock(wmxsdk->connect_mutex);
-       __iwmx_cm_dev_disconnected(wmxsdk);
-       g_mutex_unlock(wmxsdk->connect_mutex);
-}
-
-/*
- * Handle a change in state
- *
- * This is were most of the action happens. When the device changes
- * state, this will catch it (through the state change callback or an
- * explicit call) and call iwmx_cm_dev_*ed() to indicate to connman what
- * happened.
- *
- * Finally, cache the new device status.
- */
-void __iwmx_cm_state_change(struct wmxsdk *wmxsdk,
-                                       WIMAX_API_DEVICE_STATUS __new_status)
-{
-       WIMAX_API_DEVICE_STATUS __old_status = wmxsdk->status;
-       WIMAX_API_DEVICE_STATUS old_status;
-       WIMAX_API_DEVICE_STATUS new_status;
-
-       /*
-        * Simplify state transition computations.
-        *
-        * For practical effects, some states are the same
-        */
-
-#if HAVE_IWMXSDK_STATUS_IDLE
-       /* Conection_Idle is the same as Data_Connected */
-       if (__old_status == WIMAX_API_DEVICE_STATUS_Connection_Idle)
-               old_status = WIMAX_API_DEVICE_STATUS_Data_Connected;
-       else
-               old_status = __old_status;
-       if (__new_status == WIMAX_API_DEVICE_STATUS_Connection_Idle)
-               new_status = WIMAX_API_DEVICE_STATUS_Data_Connected;
-       else
-               new_status = __new_status;
-#endif /* #if HAVE_IWMXSDK_STATUS_IDLE */
-       /* Radio off: all are just RF_OFF_SW (the highest) */
-       switch (__old_status) {
-       case WIMAX_API_DEVICE_STATUS_RF_OFF_HW_SW:
-       case WIMAX_API_DEVICE_STATUS_RF_OFF_HW:
-       case WIMAX_API_DEVICE_STATUS_RF_OFF_SW:
-               old_status = WIMAX_API_DEVICE_STATUS_RF_OFF_SW;
-               break;
-       default:
-               old_status = __old_status;
-               break;
-       }
-
-       switch (__new_status) {
-       case WIMAX_API_DEVICE_STATUS_RF_OFF_HW_SW:
-       case WIMAX_API_DEVICE_STATUS_RF_OFF_HW:
-       case WIMAX_API_DEVICE_STATUS_RF_OFF_SW:
-               new_status = WIMAX_API_DEVICE_STATUS_RF_OFF_SW;
-               break;
-       default:
-               new_status = __new_status;
-               break;
-       }
-
-       /* If no real state change, do nothing */
-       if (old_status == new_status) {
-               DBG("no state changed\n");
-               return;
-       } else
-               DBG("state change from %d (%d: %s) to %d (%d: %s)\n",
-                   old_status, __old_status,
-                   iwmx_sdk_dev_status_to_str(__old_status),
-                   new_status, __new_status,
-                   iwmx_sdk_dev_status_to_str(__new_status));
-
-       /* Cleanup old state */
-       switch (old_status) {
-       case WIMAX_API_DEVICE_STATUS_UnInitialized:
-               /* This means the plugin is starting but the device is
-                * in some state already, so we need to update our
-                * internal knowledge of it. */
-               if (new_status > WIMAX_API_DEVICE_STATUS_RF_OFF_SW)
-                       iwmx_cm_dev_enabled(wmxsdk);
-               break;
-       case WIMAX_API_DEVICE_STATUS_RF_OFF_SW:
-               /* This means the radio is being turned on, so enable
-                * the device ( unless going to uninitialized). */
-               if (new_status != WIMAX_API_DEVICE_STATUS_RF_OFF_SW)
-                       iwmx_cm_dev_enabled(wmxsdk);
-               break;
-       case WIMAX_API_DEVICE_STATUS_Ready:
-               break;
-       case WIMAX_API_DEVICE_STATUS_Scanning:
-               break;
-       case WIMAX_API_DEVICE_STATUS_Connecting:
-               break;
-       case WIMAX_API_DEVICE_STATUS_Data_Connected:
-               iwmx_cm_dev_disconnected(wmxsdk);
-               break;
-       default:
-               connman_error("wmxsdk: unknown old status %d\n", old_status);
-               return;
-       };
-
-       /* Implement new state */
-       switch (new_status) {
-       case WIMAX_API_DEVICE_STATUS_UnInitialized:
-               break;
-       case WIMAX_API_DEVICE_STATUS_RF_OFF_SW:
-               /* This means the radio is being turned off, so
-                * disable the device unless coming from uninitialized. */
-               if (old_status != WIMAX_API_DEVICE_STATUS_UnInitialized)
-                       iwmx_cm_dev_disabled(wmxsdk);
-               break;
-       case WIMAX_API_DEVICE_STATUS_Ready:
-               break;
-       case WIMAX_API_DEVICE_STATUS_Scanning:
-               break;
-       case WIMAX_API_DEVICE_STATUS_Connecting:
-               break;
-       case WIMAX_API_DEVICE_STATUS_Data_Connected:
-               iwmx_cm_dev_connected(wmxsdk);
-               break;
-       default:
-               connman_error("wmxsdk: unknown new status %d\n", old_status);
-               return;
-       };
-       wmxsdk->status = __new_status;
-}
-
-/*
- * Implement a device state transition [locking version]
- *
- * See __iwmx_cm_state_change()
- */
-void iwmx_cm_state_change(struct wmxsdk *wmxsdk,
-                                WIMAX_API_DEVICE_STATUS __new_status)
-{
-       g_mutex_lock(wmxsdk->status_mutex);
-       __iwmx_cm_state_change(wmxsdk, __new_status);
-       g_mutex_unlock(wmxsdk->status_mutex);
-}
-
-/*
- * Read the cached device status
- */
-WIMAX_API_DEVICE_STATUS iwmx_cm_status_get(struct wmxsdk *wmxsdk)
-{
-       WIMAX_API_DEVICE_STATUS status;
-
-       g_mutex_lock(wmxsdk->status_mutex);
-       status = wmxsdk->status;
-       g_mutex_unlock(wmxsdk->status_mutex);
-       return status;
-}
-
-/*
- * Called by connman when a device is enabled by the user
- *
- * We need to turn the radio on; the state change function will poke
- * the internals.
- */
-static int iwmx_cm_enable(struct connman_device *dev)
-{
-       int result;
-       struct wmxsdk *wmxsdk = connman_device_get_data(dev);
-
-       connman_inet_ifup(connman_device_get_index(dev));
-       result = iwmx_sdk_rf_state_set(wmxsdk, WIMAX_API_RF_ON);
-       return result;
-}
-
-/*
- * Called by connman when a device is disabled by the user
- *
- * Simple: just make sure the radio is off; the state change function
- * will poke the internals.
- */
-static int iwmx_cm_disable(struct connman_device *dev)
-{
-       int result;
-       struct wmxsdk *wmxsdk = connman_device_get_data(dev);
-
-       result = iwmx_sdk_rf_state_set(wmxsdk, WIMAX_API_RF_OFF);
-       connman_inet_ifdown(connman_device_get_index(dev));
-       return 0;
-}
-
-/*
- * Probe deferred call from when the mainloop is idle
- *
- * probe() schedules this to be called from the mainloop when idle to
- * do a device status evaluation. Needed because of an internal race
- * condition in connman. FIXME: deploy into _probe() when fixed.
- */
-static gboolean __iwmx_cm_probe_dpc(gpointer _wmxsdk)
-{
-       int result;
-       struct wmxsdk *wmxsdk = _wmxsdk;
-       result = iwmx_sdk_get_device_status(wmxsdk);
-       if (result < 0)
-               connman_error("wmxsdk: can't get status: %d\n", result);
-       else
-               iwmx_cm_state_change(wmxsdk, result);
-       return FALSE;
-}
-
-/*
- * Called by connman when a new device pops in
- *
- * We allocate our private structure, register with the WiMAX API,
- * open their device, subscribe to all the callbacks.
- *
- * At the end, we launch a deferred call (to work around current
- * connman issues that need to be fixed in the future) and update the
- * device's status. This allows us to pick up the current status and
- * adapt connman's idea of the device to it.
- */
-static int iwmx_cm_probe(struct connman_device *dev)
-{
-       int result;
-       struct wmxsdk *wmxsdk = NULL;
-
-       wmxsdk = connman_device_get_data(dev);
-       if (wmxsdk == NULL)
-               /* not called from a discovery done by the WiMAX
-                * Network Service, ignore */
-               return -ENODEV;
-
-       result = iwmx_sdk_setup(wmxsdk);
-       if (result < 0)
-               goto error_setup;
-
-       /* There is a race condition in the connman core that doesn't
-        * allow us to call this directly and things to work properly
-        * FIXME FIXME FIXME: merge _dpc call in here when connman is fixed */
-       g_idle_add(__iwmx_cm_probe_dpc, wmxsdk);
-       return 0;
-
-       iwmx_sdk_remove(wmxsdk);
-error_setup:
-       return result;
-}
-
-/*
- * Called when a device is removed from connman
- *
- * Cleanup all that is done in _probe. Remove callbacks, unregister
- * from the WiMAX API.
- */
-static void iwmx_cm_remove(struct connman_device *dev)
-{
-       struct wmxsdk *wmxsdk = connman_device_get_data(dev);
-       iwmx_sdk_remove(wmxsdk);
-}
-
-/*
- * Called by connman to ask the device to scan for networks
- *
- * We have set in the WiMAX API the scan result callbacks, so we just
- * start a simple scan (not a wide one).
- *
- * First we obtain the current list of networks and pass it to the
- * callback processor. Then we start an scan cycle.
- */
-static int iwmx_cm_scan(struct connman_device *dev)
-{
-       struct wmxsdk *wmxsdk = connman_device_get_data(dev);
-       return iwmx_sdk_scan(wmxsdk);
-}
-
-/*
- * Driver for a WiMAX API based device.
- */
-static struct connman_device_driver iwmx_cm_device_driver = {
-       .name           = "iwmx",
-       .type           = CONNMAN_DEVICE_TYPE_WIMAX,
-       .probe          = iwmx_cm_probe,
-       .remove         = iwmx_cm_remove,
-       .enable         = iwmx_cm_enable,
-       .disable        = iwmx_cm_disable,
-       .scan           = iwmx_cm_scan,
-};
-
-static int iwmx_cm_init(void)
-{
-       int result;
-
-       result = connman_device_driver_register(&iwmx_cm_device_driver);
-       if (result < 0)
-               goto error_driver_register;
-       result = connman_network_driver_register(&iwmx_cm_network_driver);
-       if (result < 0)
-               goto error_network_driver_register;
-       result = iwmx_sdk_api_init();
-       if (result < 0)
-               goto error_iwmx_sdk_init;
-       return 0;
-
-error_iwmx_sdk_init:
-       connman_network_driver_unregister(&iwmx_cm_network_driver);
-error_network_driver_register:
-       connman_device_driver_unregister(&iwmx_cm_device_driver);
-error_driver_register:
-       return result;
-}
-
-static void iwmx_cm_exit(void)
-{
-       iwmx_sdk_api_exit();
-       connman_network_driver_unregister(&iwmx_cm_network_driver);
-       connman_device_driver_unregister(&iwmx_cm_device_driver);
-}
-
-CONNMAN_PLUGIN_DEFINE(iwmx, "Intel WiMAX SDK / Common API plugin",
-                       CONNMAN_VERSION, CONNMAN_PLUGIN_PRIORITY_LOW,
-                                               iwmx_cm_init, iwmx_cm_exit);
diff --git a/plugins/iwmx.h b/plugins/iwmx.h
deleted file mode 100644 (file)
index f3bc07b..0000000
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
- *
- *  Connection Manager
- *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License version 2 as
- *  published by the Free Software Foundation.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- *
- */
-
-/* Fix source compat brakage from 1.4 to 1.5...*/
-#ifndef HAVE_WIMAX_API_DEVICE_ID
-typedef struct WIMAX_API_DEVICE_ID WIMAX_API_DEVICE_ID;
-#endif
-
-#ifndef HAVE_WIMAX_API_CONNECTED_NSP_INFO
-typedef struct WIMAX_API_CONNECTED_NSP_INFO WIMAX_API_CONNECTED_NSP_INFO;
-#endif
-
-#ifndef HAVE_WIMAX_API_NSP_INFO_EX
-typedef struct WIMAX_API_NSP_INFO_EX WIMAX_API_NSP_INFO_EX;
-#endif
-
-#ifndef HAVE_WIMAX_API_HW_DEVICE_ID
-typedef struct WIMAX_API_HW_DEVICE_ID WIMAX_API_HW_DEVICE_ID;
-#endif
-
-
-/*
- *
- * The plugin is broken in two main parts: the glue to connman
- * (iwmx_cm_*() functions) and the glue to the libiWmxSdk (iwmx_sdk_*()
- * functions). They connect using a well defined interface.
- *
- * The plugin is state based and operates reactively to state
- * transtitions on the WiMAX device or to user requests, even from
- * external control tools that are not aware of connman.
- *
- * When the user requests connman to do something, it goes into a call
- * implemented by the 'struct connman_driver iwmx_cm_driver' (or
- * iwmx_cm_network_driver) that will instruct libiWmxSDK to change the
- * device's state.
- *
- * When the device changes state, a state change callback is sent back
- * by libiWmxSDK, which gets fed to iwmx_cm_state_change(), which
- * evaluates the state change and updates connman's internal state in
- * response.
- *
- * This allows the device to be also controlled by external tools
- * without driving connman out of state.
- *
- * Device's state changes can be caused through:
- *
- *  - connman (by user request)
- *
- *  - any other external utility (eg: WmxSDK's wimaxcu)
- *
- *  - external stimuli: network connection broken when going out of
- *    range
- *
- * Functions named __*() normally indicate that require locking. See
- * their doc header.
- *
- * ENUMERATION
- *
- * When we receive a normal probe request [iwmx_cm_probe()] from
- * connman, we ignore it (we can tell based on the connman device
- * having NULL data).
- *
- * The plugin has registered with the WiMAX Network Service and it
- * will listen to its device add/rm messages [iwmx_sdk_addremove_cb()]
- * and use that to create a  device [iwmx_sdk_dev_add()] which will be
- * registered with connman. [iwmx_cm_dev_add()]. Then connman will
- * enumerate the device, call again iwmx_cm_probe() and at this time,
- * we'll recognize it, pass through iwmx_sdk_setup() and complete the
- * probe process.
- *
- * If the daemon dies, in theory the plugin will realize and remove
- * the WiMAX device.
- */
-
-struct wmxsdk {
-       WIMAX_API_DEVICE_ID device_id;
-       struct connman_device *dev;
-
-       GStaticMutex network_mutex;
-
-       WIMAX_API_DEVICE_STATUS status;
-       GMutex *status_mutex;
-
-       /*
-        * nw points to the network we are connected to. connecting_nw
-        * points to the network we have requested to connect.
-        */
-       GMutex *connect_mutex;
-       struct connman_network *connecting_nw, *nw;
-
-       char name[100];
-       char ifname[16];
-};
-
-/* Initialize a [zeroed] struct wmxsdk */
-static inline void wmxsdk_init(struct wmxsdk *wmxsdk)
-{
-       g_static_mutex_init(&wmxsdk->network_mutex);
-
-       wmxsdk->status = WIMAX_API_DEVICE_STATUS_UnInitialized;
-       wmxsdk->status_mutex = g_mutex_new();
-       g_assert(wmxsdk->status_mutex);
-
-       wmxsdk->connect_mutex = g_mutex_new();
-       g_assert(wmxsdk->connect_mutex);
-}
-
-/* Misc utilities */
-#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
-#define container_of(pointer, type, member)                            \
-({                                                                     \
-       type *object = NULL;                                            \
-       size_t offset = (void *) &object->member - (void *) object;     \
-       (type *) ((void *) pointer - offset);                           \
-})
-
-/* Misc values */
-enum {
-       /**
-        * Time we wait for callbacks: 5s
-        *
-        * I know, it is huge, but L4 and the device sometimes take
-        * some time, especially when there is crypto involved.
-        */
-       IWMX_SDK_L4_TIMEOUT_US = 5 * 1000 * 1000,
-
-       /*
-        * WARNING!!!!!
-        *
-        * ONLY ONE DEVICE SUPPORTED
-        *
-        * - on removal, there is no way to know which device was
-        *   removed (the removed device is removed from the list and
-        *   the callback doesn't have any more information than the
-        *   index in the list that getlistdevice would return -- racy
-        *   as hell).
-        *
-        * - on insertion, there is not enough information provided.
-        */
-       IWMX_SDK_DEV_MAX = 1,
-};
-
-struct connman_network *__iwmx_cm_network_available(
-                       struct wmxsdk *wmxsdk, const char *station_name,
-                       const void *sdk_nspname, size_t sdk_nspname_size,
-                                                               int strength);
-
-struct connman_network *iwmx_cm_network_available(
-                       struct wmxsdk *wmxsdk, const char *station_name,
-                       const void *sdk_nspname, size_t sdk_nspname_size,
-                                                               int strength);
-
-WIMAX_API_DEVICE_STATUS iwmx_cm_status_get(struct wmxsdk *wmxsdk);
-void __iwmx_cm_state_change(struct wmxsdk *wmxsdk,
-                                       WIMAX_API_DEVICE_STATUS __new_status);
-void iwmx_cm_state_change(struct wmxsdk *wmxsdk,
-                                       WIMAX_API_DEVICE_STATUS __new_status);
-
-int iwmx_sdk_connect(struct wmxsdk *wmxsdk, struct connman_network *nw);
-int iwmx_sdk_disconnect(struct wmxsdk *wmxsdk);
-struct connman_network *__iwmx_sdk_get_connected_network(struct wmxsdk *wmxsdk);
-const char *iwmx_sdk_dev_status_to_str(WIMAX_API_DEVICE_STATUS status);
-int iwmx_sdk_rf_state_set(struct wmxsdk *wmxsdk, WIMAX_API_RF_STATE rf_state);
-WIMAX_API_DEVICE_STATUS iwmx_sdk_get_device_status(struct wmxsdk *wmxsdk);
-int iwmx_sdk_setup(struct wmxsdk *wmxsdk);
-void iwmx_sdk_remove(struct wmxsdk *wmxsdk);
-int iwmx_sdk_scan(struct wmxsdk *wmxsdk);
-int iwmx_sdk_api_init(void);
-void iwmx_sdk_api_exit(void);
diff --git a/plugins/iwmxsdk.c b/plugins/iwmxsdk.c
deleted file mode 100644 (file)
index 33ea3c3..0000000
+++ /dev/null
@@ -1,1044 +0,0 @@
-/*
- *
- *  Connection Manager
- *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License version 2 as
- *  published by the Free Software Foundation.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <string.h>
-#include <net/if.h>
-
-#include <glib.h>
-
-#define CONNMAN_API_SUBJECT_TO_CHANGE
-#include <connman/device.h>
-#include <connman/inet.h>
-#include <connman/log.h>
-
-#include <WiMaxType.h>
-#include <WiMaxAPI.h>
-#include <WiMaxAPIEx.h>
-
-#include "iwmx.h"
-
-/* Yes, this is dirty; see above on IWMX_SDK_DEV_MAX*/
-static struct wmxsdk *g_iwmx_sdk_devs[IWMX_SDK_DEV_MAX];
-
-static struct wmxsdk *deviceid_to_wmxsdk(WIMAX_API_DEVICE_ID *device_id)
-{
-       unsigned cnt;
-       for (cnt = 0; cnt < IWMX_SDK_DEV_MAX; cnt++) {
-               struct wmxsdk *wmxsdk = g_iwmx_sdk_devs[cnt];
-               if (wmxsdk &&
-                   wmxsdk->device_id.deviceIndex == device_id->deviceIndex)
-                       return wmxsdk;
-       }
-       return NULL;
-}
-
-static WIMAX_API_DEVICE_ID g_api;
-
-
-/*
- * FIXME: pulled it it out of some hole
- *
- * the cinr to percentage computation comes from the L3/L4 doc
- *
- * But some other places (L4 code) have a more complex, seemingly
- * logarithmical computation.
- *
- * Oh well...
- *
- */
-static int cinr_to_percentage(int cinr)
-{
-       int strength;
-       if (cinr <= -5)
-               strength = 0;
-       else if (cinr >= 25)
-               strength = 100;
-       else    /* Calc percentage on the value from -5 to 25 */
-               strength = ((100UL * (cinr - -5)) / (25 - -5));
-       return strength;
-}
-
-/*
- * Convert a WiMAX API status to an string.
- */
-const char *iwmx_sdk_dev_status_to_str(WIMAX_API_DEVICE_STATUS status)
-{
-       switch (status) {
-       case WIMAX_API_DEVICE_STATUS_UnInitialized:
-               return "Uninitialized";
-               break;
-       case WIMAX_API_DEVICE_STATUS_RF_OFF_HW_SW:
-               return "Device RF Off(both H/W and S/W)";
-               break;
-       case WIMAX_API_DEVICE_STATUS_RF_OFF_HW:
-               return "Device RF Off(via H/W switch)";
-       case WIMAX_API_DEVICE_STATUS_RF_OFF_SW:
-               return "Device RF Off(via S/W switch)";
-       case WIMAX_API_DEVICE_STATUS_Ready:
-               return "Device is ready";
-       case WIMAX_API_DEVICE_STATUS_Scanning:
-               return "Device is scanning";
-       case WIMAX_API_DEVICE_STATUS_Connecting:
-               return "Connection in progress";
-       case WIMAX_API_DEVICE_STATUS_Data_Connected:
-               return "Layer 2 connected";
-#if HAVE_IWMXSDK_STATUS_IDLE
-       case WIMAX_API_DEVICE_STATUS_Connection_Idle:
-               return "Idle connection";
-#endif /* #if HAVE_IWMXSDK_STATUS_IDLE */
-       default:
-               return "unknown state";
-       }
-}
-
-/*
- * Get the device's status from the device
- *
- * Does NOT cache the result
- * Does NOT trigger a state change in connman
- *
- * Returns < 0 errno code on error, status code if ok.
- */
-WIMAX_API_DEVICE_STATUS iwmx_sdk_get_device_status(struct wmxsdk *wmxsdk)
-{
-       WIMAX_API_RET r;
-       char errstr[512];
-       UINT32 errstr_size = sizeof(errstr);
-
-       WIMAX_API_DEVICE_STATUS dev_status;
-       WIMAX_API_CONNECTION_PROGRESS_INFO pi;
-
-       r = GetDeviceStatus(&wmxsdk->device_id, &dev_status, &pi);
-       if (r != WIMAX_API_RET_SUCCESS) {
-               GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
-               connman_error("wmxsdk: Cannot read device state: %d (%s)\n",
-                       r, errstr);
-               dev_status = -EIO;
-       }
-       return dev_status;
-}
-
-/*
- * Get the device's status from the device but return a string describing it
- *
- * Same conditions as iwmx_sdk_get_device_status().
- */
-static const char *iwmx_sdk_get_device_status_str(struct wmxsdk *wmxsdk)
-{
-       const char *result;
-       WIMAX_API_DEVICE_STATUS dev_status;
-
-       dev_status = iwmx_sdk_get_device_status(wmxsdk);
-       if ((int) dev_status < 0)
-               result = "cannot read device state";
-       else
-               result = iwmx_sdk_dev_status_to_str(dev_status);
-       return result;
-}
-
-/*
- * Translate a WiMAX network type to a readable name.
- */
-static const char *iwmx_sdk_network_type_name(enum _WIMAX_API_NETWORK_TYPE network_type)
-{
-       static char *network_type_name[] = {
-               [WIMAX_API_HOME] = "",
-               [WIMAX_API_PARTNER] = " (partner network)",
-               [WIMAX_API_ROAMING_PARTNER] = " (roaming partner network)",
-               [WIMAX_API_UNKNOWN] = " (unknown network)",
-       };
-       if (network_type > WIMAX_API_UNKNOWN)
-               return "(BUG! UNKNOWN NETWORK_TYPE MODE)";
-       else
-               return network_type_name[network_type];
-}
-
-/*
- * If the device is connected but we don't know about the network,
- * create the knowledge of it.
- *
- * Asks the WiMAX API to report which NSP we are connected to and we
- * create/update a network_el in the device's network list. Then
- * return it.
- *
- * Returns NULL on error.
- *
- * NOTE: wmxsdk->network_mutex has to be taken
- */
-struct connman_network *__iwmx_sdk_get_connected_network(struct wmxsdk *wmxsdk)
-{
-       struct connman_network *nw;
-
-       WIMAX_API_CONNECTED_NSP_INFO nsp_info;
-       WIMAX_API_RET r;
-       char errstr[512];
-       UINT32 errstr_size = sizeof(errstr);
-
-       /* The device is getting connected due to an external (to
-        * connman) event; find which is the nw we are getting
-        * connected to. if we don't have it, add it */
-       r = GetConnectedNSP(&wmxsdk->device_id, &nsp_info);
-       if (r != WIMAX_API_RET_SUCCESS) {
-               GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
-               connman_error(
-                       "wmxsdk: Cannot get connected NSP info: %d (%s)\n",
-                       r, errstr);
-               strcpy((char *) nsp_info.NSPName, "unknown");
-               nw = iwmx_cm_network_available(
-                       wmxsdk, "unknown",
-                       nsp_info.NSPName, strlen((char *) nsp_info.NSPName) + 1,
-                       cinr_to_percentage(nsp_info.CINR - 10));
-       } else {
-               nw = iwmx_cm_network_available(
-                       wmxsdk, (char *) nsp_info.NSPName,
-                       nsp_info.NSPName, strlen((char *) nsp_info.NSPName) + 1,
-                       cinr_to_percentage(nsp_info.CINR - 10));
-       }
-       return nw;
-}
-
-/*
- * Callback for a RF State command
- *
- * Called by the WiMAX API when a command sent to change the RF state
- * is completed. This is just a confirmation of what happened with the
- * command.
- *
- * We don't do anything, as when the device changes state, the state
- * change callback is called and that will fiddle with the connman
- * internals.
- */
-static void __iwmx_sdk_rf_state_cb(WIMAX_API_DEVICE_ID *device_id,
-                                  WIMAX_API_RF_STATE rf_state)
-{
-       DBG("rf_state changed to %d\n", rf_state);
-}
-
-/*
- * Turn the radio on or off
- *
- * First it checks that we are in the right state before doing
- * anything; there might be no need to do anything.
- *
- * Issue a command to the WiMAX API, wait for a callback confirming it
- * is done. Sometimes the callback is missed -- in that case, do force
- * a state change evaluation.
- *
- * Frustration note:
- *
- *      Geezoos efing Xist, they make difficult even the most simple
- *      of the operations
- *
- *      This thing is definitely a pain. If the radio is ON already
- *      and you switch it on again...well, there is no way to tell
- *      because you don't get a callback saying it basically
- *      suceeded. But on the other hand, if the thing was in a
- *      different state and action needs to be taken, you have to wait
- *      for a callback to confirm it's done. However, there is also an
- *      state change callback, which is almost the same, so now you
- *      have to handle things in two "unrelated" threads of execution.
- *
- *      How the shpx are you expected to tell the difference? Check
- *      status first? On timeout? Nice gap (eighteen wheeler size) for
- *      race conditions.
- */
-int iwmx_sdk_rf_state_set(struct wmxsdk *wmxsdk, WIMAX_API_RF_STATE rf_state)
-{
-       int result;
-
-       WIMAX_API_RET r;
-       char errstr[512];
-       UINT32 errstr_size = sizeof(errstr);
-       WIMAX_API_DEVICE_STATUS dev_status;
-
-       g_assert(rf_state == WIMAX_API_RF_ON || rf_state == WIMAX_API_RF_OFF);
-
-       /* Guess what the current radio state is; if it is ON
-        * already, don't redo it. */
-       dev_status = iwmx_sdk_get_device_status(wmxsdk);
-       if ((int) dev_status < 0) {
-               result = dev_status;
-               goto error_get_status;
-       }
-       switch (dev_status) {
-       case WIMAX_API_DEVICE_STATUS_UnInitialized:
-               result = -EINVAL;
-               goto error_cant_do;
-       case WIMAX_API_DEVICE_STATUS_RF_OFF_HW_SW:
-       case WIMAX_API_DEVICE_STATUS_RF_OFF_HW:
-               connman_error(
-                       "wmxsdk: cannot turn on radio: hw switch is off\n");
-               result = -EPERM;
-               goto error_cant_do;
-               break;
-       case WIMAX_API_DEVICE_STATUS_RF_OFF_SW:
-               if (rf_state == WIMAX_API_RF_OFF) {
-                       result = 0;
-                       DBG("radio is already off\n");
-                       goto out_done;
-               }
-               break;
-       case WIMAX_API_DEVICE_STATUS_Ready:
-       case WIMAX_API_DEVICE_STATUS_Scanning:
-       case WIMAX_API_DEVICE_STATUS_Connecting:
-       case WIMAX_API_DEVICE_STATUS_Data_Connected:
-#if HAVE_IWMXSDK_STATUS_IDLE
-       case WIMAX_API_DEVICE_STATUS_Connection_Idle:
-#endif
-               if (rf_state == WIMAX_API_RF_ON) {
-                       result = 0;
-                       DBG("radio is already on\n");
-                       goto out_done;
-               }
-               break;
-       default:
-               g_assert(1);
-       }
-       /* Ok, flip the radio */
-       r = CmdControlPowerManagement(&wmxsdk->device_id, rf_state);
-       if (r != WIMAX_API_RET_SUCCESS) {
-               GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
-               connman_error("wmxsdk: Cannot flip radio to %d: %d (%s) "
-                             "[device is in state %s]\n",
-                             rf_state, r, errstr,
-                             iwmx_sdk_get_device_status_str(wmxsdk));
-               result = -EIO;
-       } else
-               result = -EINPROGRESS;
-out_done:
-error_cant_do:
-error_get_status:
-       return result;
-}
-
-/*
- * Callback for a Connect command
- *
- * Called by the WiMAX API when a command sent to connect is
- * completed. This is just a confirmation of what happened with the
- * command.
- *
- * WE DON'T DO MUCH HERE -- the real meat happens when a state change
- * callback is sent, where we detect we move to connected state (or
- * from disconnecting to something else); the state change callback is
- * called and that will fiddle with the connman internals.
- */
-static void __iwmx_sdk_connect_cb(WIMAX_API_DEVICE_ID *device_id,
-                                 WIMAX_API_NETWORK_CONNECTION_RESP resp)
-{
-       WIMAX_API_DEVICE_STATUS status;
-       struct wmxsdk *wmxsdk = deviceid_to_wmxsdk(device_id);
-
-       status = iwmx_cm_status_get(wmxsdk);
-       if (resp == WIMAX_API_CONNECTION_SUCCESS) {
-               if (status != WIMAX_API_DEVICE_STATUS_Data_Connected
-#if HAVE_IWMXSDK_STATUS_IDLE
-                   && status != WIMAX_API_DEVICE_STATUS_Connection_Idle
-#endif
-                       )
-                       connman_error("wmxsdk: error: connect worked, but state"
-                                     " didn't change (now it is %d [%s])\n",
-                                     status,
-                                     iwmx_sdk_dev_status_to_str(status));
-       } else
-               connman_error("wmxsdk: failed to connect (status %d: %s)\n",
-                             status, iwmx_sdk_dev_status_to_str(status));
-}
-
-/*
- * Connect to a network
- *
- * This function starts the connection process to a given network;
- * when the device changes status, the status change callback will
- * tell connman if the network is finally connected or not.
- *
- * One of the reasons it is done like that is to allow external tools
- * to control the device and the plugin just passing the status so
- * connman displays the right info.
- */
-int iwmx_sdk_connect(struct wmxsdk *wmxsdk, struct connman_network *nw)
-{
-       int result;
-
-       WIMAX_API_RET r;
-       char errstr[512];
-       UINT32 errstr_size = sizeof(errstr);
-       WIMAX_API_DEVICE_STATUS dev_status;
-       const char *station_name = connman_network_get_identifier(nw);
-       const void *sdk_nspname;
-       unsigned int sdk_nspname_size;
-
-       g_mutex_lock(wmxsdk->connect_mutex);
-       /* Guess what the current radio state is; if it is ON
-        * already, don't redo it. */
-       dev_status = iwmx_cm_status_get(wmxsdk);
-       if ((int) dev_status < 0) {
-               result = dev_status;
-               goto error_get_status;
-       }
-       switch (dev_status) {
-       case WIMAX_API_DEVICE_STATUS_UnInitialized:
-               connman_error("wmxsdk: SW BUG? HW is uninitialized\n");
-               result = -EINVAL;
-               goto error_cant_do;
-       case WIMAX_API_DEVICE_STATUS_RF_OFF_HW_SW:
-       case WIMAX_API_DEVICE_STATUS_RF_OFF_HW:
-       case WIMAX_API_DEVICE_STATUS_RF_OFF_SW:
-               connman_error("wmxsdk: Cannot connect: radio is off\n");
-               result = -EPERM;
-               goto error_cant_do;
-       case WIMAX_API_DEVICE_STATUS_Ready:
-       case WIMAX_API_DEVICE_STATUS_Scanning:
-               break;
-       case WIMAX_API_DEVICE_STATUS_Connecting:
-               DBG("Connect already pending, waiting for it\n");
-               result = -EINPROGRESS;
-               goto error_cant_do;
-       case WIMAX_API_DEVICE_STATUS_Data_Connected:
-#if HAVE_IWMXSDK_STATUS_IDLE
-       case WIMAX_API_DEVICE_STATUS_Connection_Idle:
-#endif
-               connman_error("wmxsdk: BUG? need to disconnect?\n");
-               result = -EINVAL;
-               goto error_cant_do;
-       default:
-               g_assert(1);
-       }
-
-       /* Ok, do the connection, wait for a callback */
-       wmxsdk->connecting_nw = connman_network_ref(nw);
-       sdk_nspname = connman_network_get_blob(nw, "WiMAX.NSP.name",
-                                                       &sdk_nspname_size);
-       g_assert(sdk_nspname != NULL);
-       r = CmdConnectToNetwork(&wmxsdk->device_id, (void *) sdk_nspname, 0, 0);
-       if (r != WIMAX_API_RET_SUCCESS) {
-               GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
-               connman_error("wmxsdk: Cannot connect to network %s: %d (%s)"
-                             " - device is in state '%s'\n",
-                             station_name, r, errstr,
-                             iwmx_sdk_get_device_status_str(wmxsdk));
-               result = -EIO;
-               connman_network_unref(nw);
-               wmxsdk->connecting_nw = NULL;
-       } else
-               result = -EINPROGRESS;
-error_cant_do:
-error_get_status:
-       g_mutex_unlock(wmxsdk->connect_mutex);
-       return result;
-}
-
-/*
- * Callback for a Disconnect command
- *
- * Called by the WiMAX API when a command sent to connect is
- * completed. This is just a confirmation of what happened with the
- * command.
- *
- * When the device changes state, the state change callback is called
- * and that will fiddle with the connman internals.
- *
- * We just update the result of the command and wake up anybody who is
- * waiting for this conditional variable.
- */
-static void __iwmx_sdk_disconnect_cb(WIMAX_API_DEVICE_ID *device_id,
-                                    WIMAX_API_NETWORK_CONNECTION_RESP resp)
-{
-       struct wmxsdk *wmxsdk = deviceid_to_wmxsdk(device_id);
-       WIMAX_API_DEVICE_STATUS status;
-
-       status = iwmx_cm_status_get(wmxsdk);
-       if (resp == WIMAX_API_CONNECTION_SUCCESS) {
-               if (status == WIMAX_API_DEVICE_STATUS_Data_Connected
-#if HAVE_IWMXSDK_STATUS_IDLE
-                   || status == WIMAX_API_DEVICE_STATUS_Connection_Idle
-#endif
-                       )
-                       connman_error("wmxsdk: error: disconnect worked, "
-                                     "but state didn't change (now it is "
-                                     "%d [%s])\n", status,
-                                     iwmx_sdk_dev_status_to_str(status));
-       } else
-               connman_error("wmxsdk: failed to disconnect (status %d: %s)\n",
-                             status, iwmx_sdk_dev_status_to_str(status));
-}
-
-/*
- * Disconnect from a network
- *
- * This function tells the device to disconnect; the state change
- * callback will take care of inform connman's internals.
- */
-int iwmx_sdk_disconnect(struct wmxsdk *wmxsdk)
-{
-       int result;
-
-       WIMAX_API_RET r;
-       char errstr[512];
-       UINT32 errstr_size = sizeof(errstr);
-       WIMAX_API_DEVICE_STATUS dev_status;
-
-       g_mutex_lock(wmxsdk->connect_mutex);
-       /* Guess what the current radio state is; if it is ON
-        * already, don't redo it. */
-       dev_status = iwmx_sdk_get_device_status(wmxsdk);
-       if ((int) dev_status < 0) {
-               result = dev_status;
-               goto error_get_status;
-       }
-       switch (dev_status) {
-       case WIMAX_API_DEVICE_STATUS_UnInitialized:
-               connman_error("wmxsdk: SW BUG? HW is uninitialized\n");
-               result = -EINVAL;
-               goto error_cant_do;
-       case WIMAX_API_DEVICE_STATUS_RF_OFF_HW_SW:
-       case WIMAX_API_DEVICE_STATUS_RF_OFF_HW:
-       case WIMAX_API_DEVICE_STATUS_RF_OFF_SW:
-               DBG("Cannot disconnect, radio is off; ignoring\n");
-               result = 0;
-               goto error_cant_do;
-       case WIMAX_API_DEVICE_STATUS_Ready:
-       case WIMAX_API_DEVICE_STATUS_Scanning:
-               DBG("Cannot disconnect, already disconnected; ignoring\n");
-               result = 0;
-               goto error_cant_do;
-       case WIMAX_API_DEVICE_STATUS_Connecting:
-       case WIMAX_API_DEVICE_STATUS_Data_Connected:
-#if HAVE_IWMXSDK_STATUS_IDLE
-       case WIMAX_API_DEVICE_STATUS_Connection_Idle:
-#endif
-               break;
-       default:
-               g_assert(1);
-       }
-       /* Ok, flip the radio */
-       r = CmdDisconnectFromNetwork(&wmxsdk->device_id);
-       if (r != WIMAX_API_RET_SUCCESS) {
-               GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
-               connman_error("wmxsdk: Cannot disconnect from network: "
-                             "%d (%s)\n", r, errstr);
-               result = -EIO;
-       } else
-               result = -EINPROGRESS;
-error_cant_do:
-error_get_status:
-       g_mutex_unlock(wmxsdk->connect_mutex);
-       return result;
-}
-
-/*
- * Callback for state change messages
- *
- * Just pass them to the state transition handler
- */
-static void __iwmx_sdk_state_change_cb(WIMAX_API_DEVICE_ID *device_id,
-                                       WIMAX_API_DEVICE_STATUS status,
-                                       WIMAX_API_STATUS_REASON reason,
-                                       WIMAX_API_CONNECTION_PROGRESS_INFO pi)
-{
-       struct wmxsdk *wmxsdk = deviceid_to_wmxsdk(device_id);
-       iwmx_cm_state_change(wmxsdk, status);
-}
-
-/*
- * Called by _iwmx_sdk_*scan_cb() when [wide or preferred] scan results
- * are available.
- *
- * From here we update the connman core idea of which networks are
- * available.
- */
-static void __iwmx_sdk_scan_common_cb(WIMAX_API_DEVICE_ID *device_id,
-                                     WIMAX_API_NSP_INFO_EX *nsp_list,
-                                     UINT32 nsp_list_size)
-{
-       struct wmxsdk *wmxsdk = deviceid_to_wmxsdk(device_id);
-       unsigned itr;
-       char station_name[256];
-
-       g_static_mutex_lock(&wmxsdk->network_mutex);
-       for (itr = 0; itr < nsp_list_size; itr++) {
-               int strength;
-               WIMAX_API_NSP_INFO_EX *nsp_info = &nsp_list[itr];
-               snprintf(station_name, sizeof(station_name),
-                        "%s", (char *)nsp_info->NSPName);
-               /* CAPI is reporing link quality as zero -- if it is
-                * zero, check if it is a bug by computing it based on
-                * CINR. If it is different, use the computed one. */
-               strength = nsp_info->linkQuality;
-               if (strength == 0) {    /* huh */
-                       int linkq_expected =
-                               cinr_to_percentage(nsp_info->CINR - 10);
-                       if (linkq_expected != strength)
-                               strength = linkq_expected;
-               }
-
-               __iwmx_cm_network_available(
-                       wmxsdk, station_name,
-                       nsp_info->NSPName,
-                       strlen((char *) nsp_info->NSPName) + 1,
-                       strength);
-       }
-       g_static_mutex_unlock(&wmxsdk->network_mutex);
-}
-
-/*
- * Called by the WiMAX API when we get a wide scan result
- *
- * We treat them same as wide, so we just call that.
- */
-static void __iwmx_sdk_wide_scan_cb(WIMAX_API_DEVICE_ID *device_id,
-                                   WIMAX_API_NSP_INFO_EX *nsp_list,
-                                   UINT32 nsp_list_size)
-{
-       __iwmx_sdk_scan_common_cb(device_id, nsp_list, nsp_list_size);
-}
-
-/*
- * Called by the WiMAX API when we get a normal (non wide) scan result
- *
- * We treat them same as wide, so we just call that.
- */
-static void __iwmx_sdk_scan_cb(WIMAX_API_DEVICE_ID *device_id,
-                               WIMAX_API_NSP_INFO_EX *nsp_list,
-                               UINT32 nsp_list_size, UINT32 searchProgress)
-{
-       __iwmx_sdk_scan_common_cb(device_id, nsp_list, nsp_list_size);
-}
-
-/*
- * Called to ask the device to scan for networks
- *
- * We don't really scan as the WiMAX SDK daemon scans in the
- * background for us. We just get the results. See iwmx_sdk_setup().
- */
-int iwmx_sdk_scan(struct wmxsdk *wmxsdk)
-{
-       int result;
-
-       UINT32 nsp_list_length = 10;
-       WIMAX_API_NSP_INFO_EX nsp_list[10];     /* FIXME: up to 32? */
-
-       WIMAX_API_RET r;
-       char errstr[512];
-       UINT32 errstr_size = sizeof(errstr);
-
-       r = GetNetworkListEx(&wmxsdk->device_id, nsp_list, &nsp_list_length);
-       if (r != WIMAX_API_RET_SUCCESS) {
-               GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
-               connman_error("wmxsdk: Cannot get network list: %d (%s)\n",
-                             r, errstr);
-               result = -EIO;
-               goto error_scan;
-       }
-
-       if (nsp_list_length == 0)
-               DBG("no networks\n");
-       else
-               __iwmx_sdk_scan_common_cb(&wmxsdk->device_id, nsp_list,
-                                       nsp_list_length);
-       result = 0;
-error_scan:
-       return result;
-}
-
-/*
- * Initialize the WiMAX API, register with it, setup callbacks
- *
- * Called through
- *
- * iwmx_sdk_dev_add
- *   connman_inet_create_device
- *      connman_register
- *         iwmx_cm_probe()
- */
-int iwmx_sdk_setup(struct wmxsdk *wmxsdk)
-{
-       int result;
-
-       WIMAX_API_RET r;
-
-       char errstr[512];
-       UINT32 errstr_size = sizeof(errstr);
-
-       result = -ENFILE;
-
-       /* device_id initialized by iwmx_sdk_dev_add */
-
-       r = WiMaxDeviceOpen(&wmxsdk->device_id);
-       if (r != WIMAX_API_RET_SUCCESS) {
-               GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
-               connman_error("wmxsdk: Cannot open device: %d (%s)\n",
-                             r, errstr);
-               goto error_wimaxdeviceopen;
-       }
-
-       /*
-        * We scan in auto mode (in the background)
-        *
-        * Otherwise is messy -- if we have connman triggering a scan
-        * when we call iwmx_cm_scan() -> iwmx_sdk_scan(), most of the
-        * times that causes a race condition when the UI asks for a
-        * scan right before displaying the network menu. As there is
-        * no way to cancel an ongoing scan before connecting, we are
-        * stuck. So we do auto bg and have iwmx_sdk_scan() just return
-        * the current network list.
-        */
-       r = SetConnectionMode(&wmxsdk->device_id,
-                             WIMAX_API_CONNECTION_AUTO_SCAN_MANUAL_CONNECT);
-       if (r != WIMAX_API_RET_SUCCESS) {
-               GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
-               connman_error("wmxsdk: Cannot set connectin mode to manual: "
-                             "%d (%s)\n", r, errstr);
-               goto error_connection_mode;
-       }
-
-       r = SubscribeControlPowerManagement(&wmxsdk->device_id,
-                                           __iwmx_sdk_rf_state_cb);
-       if (r != WIMAX_API_RET_SUCCESS) {
-               GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
-               connman_error("wmxsdk: Cannot subscribe to radio change "
-                             "events: %u (%s)\n", r, errstr);
-               result = -EIO;
-               goto error_subscribe_rf_state;
-       }
-
-       r = SubscribeDeviceStatusChange(&wmxsdk->device_id,
-                                       __iwmx_sdk_state_change_cb);
-       if (r != WIMAX_API_RET_SUCCESS) {
-               GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
-               connman_error("wmxsdk: Cannot subscribe to state chaneg events:"
-                             "%d (%s)\n", r, errstr);
-               goto error_subscribe_state_change;
-       }
-
-       r = SubscribeNetworkSearchWideScanEx(&wmxsdk->device_id,
-                                            __iwmx_sdk_wide_scan_cb);
-       if (r != WIMAX_API_RET_SUCCESS) {
-               GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
-               connman_error("wmxsdk: Cannot subscribe to wide scan events: "
-                             "%d (%s)\n", r, errstr);
-               goto error_subscribe_wide_scan;
-       }
-       r = SubscribeNetworkSearchEx(&wmxsdk->device_id, __iwmx_sdk_scan_cb);
-       if (r != WIMAX_API_RET_SUCCESS) {
-               GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
-               connman_error("wmxsdk: Cannot subscribe to scan events: "
-                             "%d (%s)\n", r, errstr);
-               goto error_subscribe_scan;
-       }
-
-       r = SubscribeConnectToNetwork(&wmxsdk->device_id,
-                                     __iwmx_sdk_connect_cb);
-       if (r != WIMAX_API_RET_SUCCESS) {
-               GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
-               connman_error("wmxsdk: Cannot subscribe to connect events: "
-                             "%d (%s)\n", r, errstr);
-               goto error_subscribe_connect;
-       }
-
-       r = SubscribeDisconnectToNetwork(&wmxsdk->device_id,
-                                        __iwmx_sdk_disconnect_cb);
-       if (r != WIMAX_API_RET_SUCCESS) {
-               GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
-               connman_error("wmxsdk: Cannot subscribe to disconnect events: "
-                             "%d (%s)\n", r, errstr);
-               goto error_subscribe_disconnect;
-       }
-       result = 0;
-out:
-       return result;
-
-       UnsubscribeDisconnectToNetwork(&wmxsdk->device_id);
-error_subscribe_disconnect:
-       UnsubscribeConnectToNetwork(&wmxsdk->device_id);
-error_subscribe_connect:
-       UnsubscribeNetworkSearchEx(&wmxsdk->device_id);
-error_subscribe_scan:
-       UnsubscribeNetworkSearchWideScanEx(&wmxsdk->device_id);
-error_subscribe_wide_scan:
-       UnsubscribeDeviceStatusChange(&wmxsdk->device_id);
-error_subscribe_state_change:
-       UnsubscribeControlPowerManagement(&wmxsdk->device_id);
-error_subscribe_rf_state:
-error_connection_mode:
-       WiMaxDeviceClose(&wmxsdk->device_id);
-error_wimaxdeviceopen:
-       goto out;
-}
-
-/*
- * Called when a device is removed from connman
- *
- * Cleanup all that is done in iwmx_sdk_setup(). Remove callbacks,
- * unregister from the WiMAX API.
- */
-void iwmx_sdk_remove(struct wmxsdk *wmxsdk)
-{
-       UnsubscribeDisconnectToNetwork(&wmxsdk->device_id);
-       UnsubscribeConnectToNetwork(&wmxsdk->device_id);
-       UnsubscribeNetworkSearchEx(&wmxsdk->device_id);
-       UnsubscribeNetworkSearchWideScanEx(&wmxsdk->device_id);
-       UnsubscribeDeviceStatusChange(&wmxsdk->device_id);
-       UnsubscribeControlPowerManagement(&wmxsdk->device_id);
-       WiMaxDeviceClose(&wmxsdk->device_id);
-}
-
-static void iwmx_sdk_dev_add(unsigned idx, unsigned api_idx, const char *name)
-{
-       int result, ifindex;
-       struct wmxsdk *wmxsdk;
-       const char *s;
-
-       if (idx >= IWMX_SDK_DEV_MAX) {
-               connman_error("BUG! idx (%u) >= IWMX_SDK_DEV_MAX (%u)\n",
-                             idx, IWMX_SDK_DEV_MAX);
-               goto error_bug;
-       }
-       if (g_iwmx_sdk_devs[idx] != NULL) {
-               connman_error("BUG! device index %u already enumerated?\n",
-                             idx);
-               goto error_bug;
-       }
-
-       wmxsdk = malloc(sizeof(*wmxsdk));
-       if (wmxsdk == NULL) {
-               connman_error("Can't allocate %zu bytes\n",
-                             sizeof(*wmxsdk));
-               goto error_bug;
-       }
-
-       memset(wmxsdk, 0, sizeof(*wmxsdk));
-       wmxsdk_init(wmxsdk);
-       /*
-        * This depends on a hack in the WiMAX Network Service; it has
-        * to return, as part of the device name, a string "if:IFNAME"
-        * where the OS's device name is stored.
-        */
-       s = strstr(name, "if:");
-       if (s == NULL
-           || sscanf(s, "if:%15[^ \f\n\r\t\v]", wmxsdk->ifname) != 1) {
-               connman_error("Cannot extract network interface name off '%s'",
-                             name);
-               goto error_noifname;
-       }
-       DBG("network interface name: '%s'", wmxsdk->ifname);
-
-       ifindex = if_nametoindex(wmxsdk->ifname);
-       if (ifindex <= 0) {
-               result = -ENFILE;
-               connman_error("wxmsdk: %s: cannot find interface index\n",
-                             wmxsdk->ifname);
-               goto error_noifname;
-       }
-
-       wmxsdk->dev = connman_inet_create_device(ifindex);
-       if (wmxsdk->dev == NULL) {
-               connman_error("wmxsdk: %s: failed to create connman_device\n",
-                             name);
-               goto error_create;
-       }
-       strncpy(wmxsdk->name, name, sizeof(wmxsdk->name));
-       connman_device_set_data(wmxsdk->dev, wmxsdk);
-
-       wmxsdk->device_id.privilege = WIMAX_API_PRIVILEGE_READ_WRITE;
-       wmxsdk->device_id.deviceIndex = api_idx;
-
-       result = connman_device_register(wmxsdk->dev);
-       if (result < 0) {
-               connman_error("wmxsdk: %s: failed to register: %d\n",
-                             wmxsdk->ifname, result);
-               goto error_dev_add;
-       }
-       g_iwmx_sdk_devs[idx] = wmxsdk;
-       return;
-
-error_dev_add:
-       wmxsdk->name[0] = 0;
-       connman_device_unref(wmxsdk->dev);
-       wmxsdk->dev = NULL;
-error_noifname:
-error_create:
-error_bug:
-       return;
-}
-
-static void iwmx_sdk_dev_rm(unsigned idx)
-{
-       struct wmxsdk *wmxsdk;
-
-       if (idx >= IWMX_SDK_DEV_MAX) {
-               connman_error("BUG! idx (%u) >= IWMX_SDK_DEV_MAX (%u)\n",
-                             idx, IWMX_SDK_DEV_MAX);
-               goto error_bug;
-       }
-       wmxsdk = g_iwmx_sdk_devs[idx];
-       if (wmxsdk->dev == NULL) {
-               DBG("device index %u not enumerated? ignoring\n", idx);
-               goto error_bug;
-       }
-
-       connman_device_unregister(wmxsdk->dev);
-       wmxsdk->name[0] = 0;
-       connman_device_unref(wmxsdk->dev);
-       memset(wmxsdk, 0, sizeof(*wmxsdk));
-       g_iwmx_sdk_devs[idx] = NULL;
-       free(wmxsdk);
-error_bug:
-       return;
-}
-
-static void iwmx_sdk_addremove_cb(WIMAX_API_DEVICE_ID *devid,
-                                 BOOL presence)
-{
-       unsigned int cnt;
-       WIMAX_API_RET r;
-       WIMAX_API_HW_DEVICE_ID device_id_list[5];
-       UINT32 device_id_list_size = ARRAY_SIZE(device_id_list);
-
-       char errstr[512];
-       UINT32 errstr_size = sizeof(errstr);
-
-       DBG("cb: handle %u index #%u is %d\n", devid->sdkHandle,
-           devid->deviceIndex, presence);
-
-       r = GetListDevice(devid, device_id_list, &device_id_list_size);
-       if (r != WIMAX_API_RET_SUCCESS) {
-               GetErrorString(devid, r, errstr, &errstr_size);
-               connman_error("wmxsdk: Cannot obtain list "
-                             "of devices: %d (%s)\n", r, errstr);
-               return;
-       }
-
-       if (device_id_list_size == 0)
-               DBG("No WiMAX devices reported\n");
-       else
-               for (cnt = 0; cnt < device_id_list_size; cnt++) {
-                       WIMAX_API_HW_DEVICE_ID *dev =
-                               device_id_list + cnt;
-                       DBG("#%u index #%u device %s\n",
-                           cnt, dev->deviceIndex, dev->deviceName);
-               }
-       if (device_id_list_size < devid->deviceIndex) {
-               connman_error("wmxsdk: changed device (%u) not in the list? "
-                             "(%u items)\n",
-                             devid->deviceIndex, device_id_list_size);
-               return;
-       }
-
-       if (presence) {
-               WIMAX_API_HW_DEVICE_ID *dev =
-                       device_id_list + devid->deviceIndex;
-               iwmx_sdk_dev_add(devid->deviceIndex, dev->deviceIndex,
-                              dev->deviceName);
-       } else {
-               iwmx_sdk_dev_rm(devid->deviceIndex);
-       }
-}
-
-/*
- * Initialize the WiMAX API, register with it, setup callbacks for
- * device coming up / dissapearing
- */
-int iwmx_sdk_api_init(void)
-{
-       int result;
-       unsigned int cnt;
-       WIMAX_API_RET r;
-       char errstr[512];
-       UINT32 errstr_size = sizeof(errstr);
-
-       WIMAX_API_HW_DEVICE_ID device_id_list[5];
-       UINT32 device_id_list_size = ARRAY_SIZE(device_id_list);
-
-       memset(&g_api, 0, sizeof(g_api));
-       g_api.privilege = WIMAX_API_PRIVILEGE_READ_WRITE;
-
-       result = -EIO;
-       r = WiMaxAPIOpen(&g_api);
-       if (r != WIMAX_API_RET_SUCCESS) {
-               GetErrorString(&g_api, r, errstr, &errstr_size);
-               connman_error("wmxsdk: WiMaxAPIOpen failed with %d (%s)\n",
-                             r, errstr);
-               goto error_wimaxapiopen;
-       }
-
-       r = SubscribeDeviceInsertRemove(&g_api, iwmx_sdk_addremove_cb);
-       if (r != WIMAX_API_RET_SUCCESS) {
-               GetErrorString(&g_api, r, errstr, &errstr_size);
-               connman_error("wmxsdk: insert/remove subscribe failed with "
-                             "%d (%s)\n", r, errstr);
-               goto error_close;
-       }
-
-       r = GetListDevice(&g_api, device_id_list, &device_id_list_size);
-       if (r != WIMAX_API_RET_SUCCESS) {
-               GetErrorString(&g_api, r, errstr, &errstr_size);
-               connman_error("wmxsdk: Cannot obtain list "
-                             "of devices: %d (%s)\n", r, errstr);
-               goto error_close;
-       }
-       if (device_id_list_size < g_api.deviceIndex) {
-               connman_error("wmxsdk: changed device (%u) not in the list? "
-                             "(%u items)\n",
-                             g_api.deviceIndex, device_id_list_size);
-       }
-
-       if (device_id_list_size == 0)
-               DBG("No WiMAX devices reported\n");
-       else
-               for (cnt = 0; cnt < device_id_list_size; cnt++) {
-                       WIMAX_API_HW_DEVICE_ID *dev =
-                               device_id_list + cnt;
-                       DBG("#%u index #%u device %s\n",
-                           cnt, dev->deviceIndex, dev->deviceName);
-                       iwmx_sdk_dev_add(cnt, dev->deviceIndex,
-                                        dev->deviceName);
-               }
-       return 0;
-
-error_close:
-       WiMaxAPIClose(&g_api);
-error_wimaxapiopen:
-       return result;
-}
-
-void iwmx_sdk_api_exit(void)
-{
-       WIMAX_API_RET r;
-
-       char errstr[512];
-       UINT32 errstr_size = sizeof(errstr);
-
-       r = WiMaxAPIClose(&g_api);
-       if (r != WIMAX_API_RET_SUCCESS) {
-               GetErrorString(&g_api, r, errstr, &errstr_size);
-               connman_error("wmxsdk: WiMaxAPIClose failed with %d (%s)\n",
-                             r, errstr);
-       }
-       return;
-}
index efbcd3d..5a655e6 100644 (file)
@@ -3,7 +3,7 @@
  *  Connection Manager
  *
  *  Copyright (C) 2010  BMW Car IT GmbH. All rights reserved.
- *  Copyright (C) 2011  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
index fa3b273..d5002cf 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
@@ -39,6 +39,7 @@
 #include <connman/plugin.h>
 #include <connman/utsname.h>
 #include <connman/log.h>
+#include <connman/inet.h>
 
 static in_addr_t loopback_address;
 static in_addr_t loopback_netmask;
@@ -152,7 +153,7 @@ static int setup_loopback(void)
                        goto done;
                }
 
-               connman_warn("Correcting wrong lookback settings");
+               connman_warn("Correcting wrong loopback settings");
        }
 
        memset(&addr, 0, sizeof(addr));
@@ -206,12 +207,21 @@ static const char *loopback_get_hostname(void)
 
 static int loopback_set_hostname(const char *hostname)
 {
-       int err;
+       const char *ptr;
+       int err, len;
 
        if (g_strcmp0(hostname, "<hostname>") == 0)
                return 0;
 
-       if (sethostname(hostname, strlen(hostname)) < 0) {
+       len = strlen(hostname);
+
+       if (connman_inet_check_hostname(hostname, len) == FALSE)
+               return -EINVAL;
+
+       if ((ptr = strstr(hostname, ".")) != NULL)
+               len = ptr - hostname;
+
+       if (sethostname(hostname, len) < 0) {
                err = -errno;
                connman_error("Failed to set hostname to %s", hostname);
                return err;
@@ -224,9 +234,14 @@ static int loopback_set_hostname(const char *hostname)
 
 static int loopback_set_domainname(const char *domainname)
 {
-       int err;
+       int err, len;
+
+       len = strlen(domainname);
+
+       if (connman_inet_check_hostname(domainname, len) == FALSE)
+               return -EINVAL;
 
-       if (setdomainname(domainname, strlen(domainname)) < 0) {
+       if (setdomainname(domainname, len) < 0) {
                err = -errno;
                connman_error("Failed to set domainname to %s", domainname);
                return err;
index 9c66d97..400389b 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
@@ -42,8 +42,6 @@ enum {
        NM_STATE_CONNECTED_GLOBAL = 70
 };
 
-#define NM_STATE_CONNECTED NM_STATE_CONNECTED_GLOBAL
-
 #define NM_SERVICE    "org.freedesktop.NetworkManager"
 #define NM_PATH       "/org/freedesktop/NetworkManager"
 #define NM_INTERFACE  NM_SERVICE
@@ -51,19 +49,22 @@ enum {
 #define DBUS_PROPERTIES_INTERFACE      "org.freedesktop.DBus.Properties"
 
 static DBusConnection *connection = NULL;
-static dbus_uint32_t state = NM_STATE_UNKNOWN;
+static struct connman_service *current_service = NULL;
+static dbus_uint32_t nm_state = NM_STATE_UNKNOWN;
 
 static void state_changed(dbus_uint32_t state)
 {
        DBusMessage *signal;
 
+       DBG("state %d", state);
+
        signal = dbus_message_new_signal(NM_PATH, NM_INTERFACE,
                                                "StateChanged");
        if (signal == NULL)
                return;
 
        dbus_message_append_args(signal, DBUS_TYPE_UINT32, &state,
-                               DBUS_TYPE_INVALID);
+                                               DBUS_TYPE_INVALID);
 
        g_dbus_send_message(connection, signal);
 }
@@ -74,6 +75,8 @@ static void properties_changed(dbus_uint32_t state)
        DBusMessageIter iter, dict, dict_entry, dict_val;
        DBusMessage *signal;
 
+       DBG("state %d", state);
+
        signal = dbus_message_new_signal(NM_PATH, NM_INTERFACE,
                                                "PropertiesChanged");
        if (signal == NULL)
@@ -107,22 +110,75 @@ static void properties_changed(dbus_uint32_t state)
 
 static void default_changed(struct connman_service *service)
 {
-       if (service != NULL)
-               state = NM_STATE_CONNECTED;
+       DBG("service %p", service);
+
+       if (service == NULL)
+               nm_state = NM_STATE_DISCONNECTED;
        else
-               state = NM_STATE_DISCONNECTED;
+               nm_state = NM_STATE_CONNECTED_LOCAL;
+
+       state_changed(nm_state);
+       properties_changed(nm_state);
 
-       DBG("%p %d", service, state);
+       current_service = service;
+}
 
-       state_changed(state);
+static void service_state_changed(struct connman_service *service,
+                                       enum connman_service_state state)
+{
+       DBG("service %p state %d", service, state);
+
+       if (current_service == NULL || current_service != service)
+               return;
+
+       switch (state) {
+       case CONNMAN_SERVICE_STATE_UNKNOWN:
+               nm_state = NM_STATE_UNKNOWN;
+               break;
+       case CONNMAN_SERVICE_STATE_FAILURE:
+       case CONNMAN_SERVICE_STATE_IDLE:
+               nm_state = NM_STATE_DISCONNECTED;
+               break;
+       case CONNMAN_SERVICE_STATE_ASSOCIATION:
+       case CONNMAN_SERVICE_STATE_CONFIGURATION:
+               nm_state = NM_STATE_CONNECTING;
+               break;
+       case CONNMAN_SERVICE_STATE_READY:
+               nm_state = NM_STATE_CONNECTED_LOCAL;
+               break;
+       case CONNMAN_SERVICE_STATE_ONLINE:
+               nm_state = NM_STATE_CONNECTED_GLOBAL;
+               break;
+       case CONNMAN_SERVICE_STATE_DISCONNECT:
+               nm_state = NM_STATE_DISCONNECTING;
+               break;
+       }
 
-       properties_changed(state);
+       state_changed(nm_state);
+       properties_changed(nm_state);
+}
+
+static void offline_mode(connman_bool_t enabled)
+{
+       DBG("enabled %d", enabled);
+
+       if (enabled == TRUE)
+               nm_state = NM_STATE_ASLEEP;
+       else
+               nm_state = NM_STATE_DISCONNECTED;
+
+       state_changed(nm_state);
+       properties_changed(nm_state);
+
+       current_service = NULL;
 }
 
 static struct connman_notifier notifier = {
-       .name           = "nmcompat",
-       .priority       = CONNMAN_NOTIFIER_PRIORITY_DEFAULT,
-       .default_changed= default_changed,
+       .name                   = "nmcompat",
+       .priority               = CONNMAN_NOTIFIER_PRIORITY_DEFAULT,
+       .default_changed        = default_changed,
+       .service_state_changed  = service_state_changed,
+       .offline_mode           = offline_mode,
 };
 
 static DBusMessage *property_get(DBusConnection *conn,
@@ -130,19 +186,21 @@ static DBusMessage *property_get(DBusConnection *conn,
 {
        const char *interface, *key;
 
-       DBG("conn %p", conn);
-
-       dbus_message_get_args(msg, NULL,
-                               DBUS_TYPE_STRING, &interface,
-                               DBUS_TYPE_STRING, &key,
-                               DBUS_TYPE_INVALID);
+       dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &interface,
+                               DBUS_TYPE_STRING, &key, DBUS_TYPE_INVALID);
 
        DBG("interface %s property %s", interface, key);
 
-       if (g_strcmp0(key, "State") == 0) {
+       if (g_str_equal(interface, NM_INTERFACE) == FALSE)
+               return dbus_message_new_error(msg, DBUS_ERROR_FAILED,
+                                               "Unsupported interface");
+
+       if (g_str_equal(key, "State") == TRUE) {
                DBusMessage *reply;
                DBusMessageIter iter, value;
 
+               DBG("state %d", nm_state);
+
                reply = dbus_message_new_method_return(msg);
                if (reply == NULL)
                        return NULL;
@@ -150,10 +208,9 @@ static DBusMessage *property_get(DBusConnection *conn,
                dbus_message_iter_init_append(reply, &iter);
 
                dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
-                                               DBUS_TYPE_UINT32_AS_STRING,
-                                               &value);
-               dbus_message_iter_append_basic(&value, DBUS_TYPE_UINT32,
-                                               &state);
+                                       DBUS_TYPE_UINT32_AS_STRING, &value);
+               dbus_message_iter_append_basic(&value,
+                                               DBUS_TYPE_UINT32, &nm_state);
                dbus_message_iter_close_container(&iter, &value);
 
                return reply;
@@ -163,14 +220,18 @@ static DBusMessage *property_get(DBusConnection *conn,
                                                "Unsupported property");
 }
 
-static GDBusMethodTable methods[] = {
-       { "Get", "ss",  "v",   property_get     },
+static const GDBusMethodTable methods[] = {
+       { GDBUS_METHOD("Get",
+                       GDBUS_ARGS({ "interface", "s" }, { "key", "s" }),
+                       GDBUS_ARGS({ "property", "v" }), property_get) },
        { },
 };
 
-static GDBusSignalTable signals[] = {
-       { "PropertiesChanged",  "a{sv}" },
-       { "StateChanged",       "u"     },
+static const GDBusSignalTable signals[] = {
+       { GDBUS_SIGNAL("PropertiesChanged",
+                       GDBUS_ARGS({ "properties", "a{sv}" })) },
+       { GDBUS_SIGNAL("StateChanged",
+                       GDBUS_ARGS({ "state", "u" })) },
        { },
 };
 
@@ -183,7 +244,7 @@ static int nmcompat_init(void)
                return -1;
 
        if (g_dbus_request_name(connection, NM_SERVICE, NULL) == FALSE) {
-               connman_error("nmcompat: failed register service\n");
+               connman_error("nmcompat: failed to register service");
                return -1;
        }
 
diff --git a/plugins/ntpd.c b/plugins/ntpd.c
deleted file mode 100644 (file)
index 19e9828..0000000
+++ /dev/null
@@ -1,325 +0,0 @@
-/*
- *
- *  Connection Manager
- *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License version 2 as
- *  published by the Free Software Foundation.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <errno.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <string.h>
-#include <arpa/inet.h>
-
-#include <gdbus.h>
-
-#define CONNMAN_API_SUBJECT_TO_CHANGE
-#include <connman/types.h>
-#include <connman/plugin.h>
-#include <connman/task.h>
-#include <connman/timeserver.h>
-#include <connman/log.h>
-
-/*
- * The peers list are the peers currently added to a running ntpd,
- * while pending_peers are the one appended but not used by ntpd yet.
- */
-static GList *peers = NULL;
-static GList *pending_peers = NULL;
-
-#define NTPD_PORT 123
-
-struct ntpd_peer {
-       char *server;
-       int refcount;
-};
-
-struct ntpdate_task {
-       struct connman_task *task;
-       gint conf_fd;
-       char *conf_path;
-};
-
-static struct ntpd_peer *find_peer(GList *peer_list, const char* server)
-{
-       GList *list;
-       struct ntpd_peer *peer;
-
-       for (list = peer_list; list; list = list->next) {
-               peer = list->data;
-
-               if (g_str_equal(peer->server, server))
-                       return peer;
-       }
-
-       return NULL;
-}
-
-static void remove_peer(GList *peer_list, struct ntpd_peer *peer)
-{
-       if (__sync_fetch_and_sub(&peer->refcount, 1) != 1)
-               return;
-
-       g_free(peer->server);
-       g_free(peer);
-       peer_list = g_list_remove(peer_list, peer);
-}
-
-static connman_bool_t ntpd_running(void)
-{
-       int sock;
-       connman_bool_t ret;
-       struct sockaddr_in server_addr;
-
-       if ((sock = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0)) == -1)
-               return FALSE;
-
-       server_addr.sin_family = AF_INET;
-        server_addr.sin_port = htons(NTPD_PORT);
-        server_addr.sin_addr.s_addr = INADDR_ANY;
-        memset(&(server_addr.sin_zero), 0, 8);
-
-       if (bind(sock, (struct sockaddr *)&server_addr,
-                       sizeof(struct sockaddr)) == -1) {
-               if (errno == EADDRINUSE)
-                       ret = TRUE;
-               else
-                       ret = FALSE;
-        } else
-               ret = FALSE;
-
-       close(sock);
-
-       return ret;
-}
-
-static void ntpdate_died(struct connman_task *task,
-                               int exit_code, void *user_data)
-{
-       struct ntpdate_task *ntpdate = user_data;
-
-       DBG("");
-
-       unlink(ntpdate->conf_path);
-       g_free(ntpdate->conf_path);
-       connman_task_destroy(ntpdate->task);
-}
-
-static void ntpdate_add_peer(struct ntpdate_task *ntpdate, char *peer)
-{
-       FILE *conf_file;
-
-       DBG("%s", peer);
-
-       conf_file = fdopen(ntpdate->conf_fd, "a+");
-       if (conf_file == NULL) {
-               connman_error("fdopen failed");
-               return;
-       }
-
-       fprintf(conf_file, "server %s iburst\n", peer);
-
-       fclose(conf_file);
-}
-
-static int ntpdate(void)
-{
-       int err;
-       GError *g_err;
-       GList *list;
-       struct ntpd_peer *peer;
-       struct ntpdate_task *ntpdate;
-
-       DBG("");
-
-       ntpdate = g_try_new0(struct ntpdate_task, 1);
-       if (ntpdate == NULL)
-               return -ENOMEM;
-
-       /* ntpdate is deprecated, we use ntpd -q instead */
-       ntpdate->task = connman_task_create(NTPD);
-       if (ntpdate->task == NULL) {
-               err = -ENOMEM;
-               goto error_task;
-       }
-
-       connman_task_add_argument(ntpdate->task, "-g", NULL);
-       connman_task_add_argument(ntpdate->task, "-q", NULL);
-
-       /* The servers are added through a temp configuration file */
-       ntpdate->conf_fd = g_file_open_tmp("connman.ntp.conf_XXXXXX",
-                                               &ntpdate->conf_path, &g_err);
-       if  (ntpdate->conf_fd == -1) {
-               err = g_err->code;
-               g_free(g_err);
-               goto error_open;
-       }
-
-       connman_task_add_argument(ntpdate->task, "-c", ntpdate->conf_path);
-
-       DBG("conf path %s", ntpdate->conf_path);
-
-       for (list = pending_peers; list; list = list->next) {
-               peer = list->data;
-
-               ntpdate_add_peer(ntpdate, peer->server);
-       }
-
-       for (list = peers; list; list = list->next) {
-               peer = list->data;
-
-               ntpdate_add_peer(ntpdate, peer->server);
-       }
-
-       close(ntpdate->conf_fd);
-
-       return connman_task_run(ntpdate->task, ntpdate_died, ntpdate,
-                                               NULL, NULL, NULL);
-error_open:
-       connman_task_destroy(ntpdate->task);
-
-error_task:
-       g_free(ntpdate);
-
-       return err;
-}
-
-static int ntpd_add_peer(char *peer)
-{
-       DBG("%s", peer);
-
-       return 0;
-}
-
-static void ntpd_sync(void)
-{
-       int err;
-       GList *list;
-
-       DBG("");
-
-       if (g_list_length(pending_peers) == 0 &&
-                       g_list_length(peers) == 0)
-               return;
-
-       if (!ntpd_running()) {
-               ntpdate();
-               return;
-       }
-
-       /* TODO Grab ntp keys path */
-
-       list = g_list_first(pending_peers);
-       while(list) {
-               struct ntpd_peer *peer = list->data;
-
-               err = ntpd_add_peer(peer->server);
-               if (err)
-                       continue;
-
-               peers = g_list_prepend(peers, peer);
-
-               list = g_list_next(list);
-
-               pending_peers = g_list_remove(pending_peers, peer);
-       };
-}
-
-static int ntpd_append(const char *server)
-{
-       struct ntpd_peer *peer;
-
-       DBG("");
-
-       if (server == NULL)
-               return 0;
-
-       if ((peer = find_peer(pending_peers, server)) ||
-                       (peer = find_peer(peers, server))) {
-               __sync_fetch_and_add(&peer->refcount, 1);
-               return 0;
-       }
-
-       peer = g_try_new0(struct ntpd_peer, 1);
-       if (peer == NULL)
-               return -ENOMEM;
-
-       peer->server = g_strdup(server);
-       if (peer->server == NULL) {
-               g_free(peer);
-               return -ENOMEM;
-       }
-
-       peer->refcount = 1;
-
-       pending_peers = g_list_prepend(pending_peers, peer);
-
-       return 0;
-}
-
-static int ntpd_remove(const char *server)
-{
-       struct ntpd_peer *peer;
-
-       DBG("");
-
-       if (server == NULL)
-               return 0;
-
-       peer = find_peer(peers, server);
-       if (peer == NULL)
-               goto remove;
-
-       remove_peer(peers, peer);
-
-remove:
-       /* TODO: send ntpd remove command */
-
-       peer = find_peer(pending_peers, server);
-       if (peer == NULL)
-               return 0;
-
-       remove_peer(pending_peers, peer);
-
-       return 0;
-}
-
-static struct connman_timeserver_driver ntpd_driver = {
-       .name           = "ntpd",
-       .priority       = CONNMAN_TIMESERVER_PRIORITY_DEFAULT,
-       .append         = ntpd_append,
-       .remove         = ntpd_remove,
-       .sync           = ntpd_sync,
-};
-
-static int ntpd_init(void)
-{
-       return connman_timeserver_driver_register(&ntpd_driver);
-}
-
-static void ntpd_exit(void)
-{
-       connman_timeserver_driver_unregister(&ntpd_driver);
-}
-
-CONNMAN_PLUGIN_DEFINE(ntpd, "ntpd plugin", VERSION,
-               CONNMAN_PLUGIN_PRIORITY_DEFAULT, ntpd_init, ntpd_exit)
index 3318cac..791047a 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *  Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
  *  Copyright (C) 2011  BWM Car IT GmbH. All rights reserved.
  *
@@ -43,8 +43,6 @@
 
 #include "mcc.h"
 
-#define uninitialized_var(x) x = x
-
 #define OFONO_SERVICE                  "org.ofono"
 
 #define OFONO_MANAGER_INTERFACE                OFONO_SERVICE ".Manager"
@@ -83,13 +81,48 @@ enum ofono_api {
  *   powered -> SubscriberIdentity or Online = True -> gprs, context ->
  *     attached -> netreg -> ready
  *
- * Enabling and disabling modems are steered through the rfkill
- * interface. That means when ConnMan toggles the rfkill bit oFono
- * will add or remove the modems.
+ * Depending on the modem type, this plugin will behave differently.
+ *
+ * GSM working flow:
+ *
+ * When a new modem appears, the plugin always powers it up. This
+ * allows the plugin to create a connman_device. The core will call
+ * modem_enable() if the technology is enabled. modem_enable() will
+ * then set the modem online. If the technology is disabled then
+ * modem_disable() will just set the modem offline. The modem is
+ * always kept powered all the time.
+ *
+ * After setting the modem online the plugin waits for the
+ * ConnectionManager and ConnectionContext to appear. When the context
+ * signals that it is attached and the NetworkRegistration interface
+ * appears, a new Service will be created and registered at the core.
+ *
+ * When asked to connect to the network (network_connect()) the plugin
+ * will set the Active property on the context. If this operation is
+ * successful the modem is connected to the network. oFono will inform
+ * the plugin about IP configuration through the updating the context's
+ * properties.
  *
- * ConnMan will always power up (set Powered and Online) the
- * modems. No need to power them down because this will be done
- * through the rfkill inteface.
+ * CDMA working flow:
+ *
+ * When a new modem appears, the plugin always powers it up. This
+ * allows the plugin to create connman_device either using IMSI either
+ * using modem Serial if the modem got a SIM interface or not.
+ *
+ * As for GSM, the core will call modem_enable() if the technology
+ * is enabled. modem_enable() will then set the modem online.
+ * If the technology is disabled then modem_disable() will just set the
+ * modem offline. The modem is always kept powered all the time.
+ *
+ * After setting the modem online the plugin waits for CdmaConnectionManager
+ * interface to appear. Then, once CdmaNetworkRegistration appears, a new
+ * Service will be created and registered at the core.
+ *
+ * When asked to connect to the network (network_connect()) the plugin
+ * will power up the CdmaConnectionManager interface.
+ * If the operation is successful the modem is connected to the network.
+ * oFono will inform the plugin about IP configuration through the
+ * updating CdmaConnectionManager settings properties.
  */
 
 static DBusConnection *connection;
@@ -136,7 +169,7 @@ struct modem_data {
 
        /* ConnectionContext Interface */
        connman_bool_t active;
-       connman_bool_t set_active;
+       connman_bool_t valid_apn; /* APN is 'valid' if length > 0 */
 
        /* SimManager Interface */
        char *imsi;
@@ -226,6 +259,12 @@ static void set_connected(struct modem_data *modem)
 
        DBG("%s", modem->path);
 
+       if (modem->context->index < 0 ||
+                       modem->context->ipv4_address == NULL) {
+               connman_error("Invalid index and/or address");
+               return;
+       }
+
        connman_network_set_index(modem->network, modem->context->index);
 
        switch (modem->context->ipv4_method) {
@@ -798,7 +837,7 @@ static void extract_ipv6_settings(DBusMessageIter *array,
 {
        DBusMessageIter dict;
        char *address = NULL, *gateway = NULL;
-       unsigned char prefix_length;
+       unsigned char prefix_length = 0;
        char *nameservers = NULL;
        const char *interface = NULL;
        int index = -1;
@@ -901,7 +940,7 @@ static connman_bool_t ready_to_create_device(struct modem_data *modem)
 static void create_device(struct modem_data *modem)
 {
        struct connman_device *device;
-       char *uninitialized_var(ident);
+       char *ident = NULL;
 
        DBG("%s", modem->path);
 
@@ -1026,7 +1065,7 @@ static void remove_network(struct modem_data *modem)
 static int add_cm_context(struct modem_data *modem, const char *context_path,
                                DBusMessageIter *dict)
 {
-       const char *context_type;
+       const char *context_type = NULL;
        struct network_context *context = NULL;
        connman_bool_t active = FALSE;
 
@@ -1071,8 +1110,17 @@ static int add_cm_context(struct modem_data *modem, const char *context_path,
                        dbus_message_iter_get_basic(&value, &active);
 
                        DBG("%s Active %d", modem->path, active);
-               }
+               } else if (g_str_equal(key, "AccessPointName") == TRUE) {
+                       const char *apn;
 
+                       dbus_message_iter_get_basic(&value, &apn);
+                       if (apn != NULL && strlen(apn) > 0)
+                               modem->valid_apn = TRUE;
+                       else
+                               modem->valid_apn = FALSE;
+
+                       DBG("%s AccessPointName '%s'", modem->path, apn);
+               }
                dbus_message_iter_next(dict);
        }
 
@@ -1086,6 +1134,12 @@ static int add_cm_context(struct modem_data *modem, const char *context_path,
 
        g_hash_table_replace(context_hash, g_strdup(context_path), modem);
 
+       if (modem->valid_apn == TRUE && modem->attached == TRUE &&
+                       has_interface(modem->interfaces,
+                               OFONO_API_NETREG) == TRUE) {
+               add_network(modem);
+       }
+
        return 0;
 }
 
@@ -1102,9 +1156,14 @@ static void remove_cm_context(struct modem_data *modem,
 
        network_context_free(modem->context);
        modem->context = NULL;
+
+       modem->valid_apn = FALSE;
+
+       if (modem->network != NULL)
+               remove_network(modem);
 }
 
-static gboolean context_changed(DBusConnection *connection,
+static gboolean context_changed(DBusConnection *conn,
                                DBusMessage *message,
                                void *user_data)
 {
@@ -1149,6 +1208,39 @@ static gboolean context_changed(DBusConnection *connection,
                        set_connected(modem);
                else
                        set_disconnected(modem);
+       } else if (g_str_equal(key, "AccessPointName") == TRUE) {
+               const char *apn;
+
+               dbus_message_iter_get_basic(&value, &apn);
+
+               DBG("%s AccessPointName %s", modem->path, apn);
+
+               if (apn != NULL && strlen(apn) > 0) {
+                       modem->valid_apn = TRUE;
+
+                       if (modem->network != NULL)
+                               return TRUE;
+
+                       if (modem->attached == FALSE)
+                               return TRUE;
+
+                       if (has_interface(modem->interfaces,
+                                       OFONO_API_NETREG) == FALSE) {
+                               return TRUE;
+                       }
+
+                       add_network(modem);
+
+                       if (modem->active == TRUE)
+                               set_connected(modem);
+               } else {
+                       modem->valid_apn = FALSE;
+
+                       if (modem->network == NULL)
+                               return TRUE;
+
+                       remove_network(modem);
+               }
        }
 
        return TRUE;
@@ -1240,7 +1332,7 @@ static int cm_get_contexts(struct modem_data *modem)
        return -EINPROGRESS;
 }
 
-static gboolean cm_context_added(DBusConnection *connection,
+static gboolean cm_context_added(DBusConnection *conn,
                                        DBusMessage *message,
                                        void *user_data)
 {
@@ -1269,7 +1361,7 @@ static gboolean cm_context_added(DBusConnection *connection,
        return TRUE;
 }
 
-static gboolean cm_context_removed(DBusConnection *connection,
+static gboolean cm_context_removed(DBusConnection *conn,
                                        DBusMessage *message,
                                        void *user_data)
 {
@@ -1413,7 +1505,7 @@ static void netreg_update_regdom(struct modem_data *modem,
                connman_technology_set_regdom(alpha2);
 }
 
-static gboolean netreg_changed(DBusConnection *connection, DBusMessage *message,
+static gboolean netreg_changed(DBusConnection *conn, DBusMessage *message,
                                void *user_data)
 {
        const char *path = dbus_message_get_path(message);
@@ -1487,7 +1579,8 @@ static void netreg_properties_reply(struct modem_data *modem,
                return;
        }
 
-       add_network(modem);
+       if (modem->valid_apn == TRUE)
+               add_network(modem);
 
        if (modem->active == TRUE)
                set_connected(modem);
@@ -1521,7 +1614,7 @@ static void add_cdma_network(struct modem_data *modem)
                set_connected(modem);
 }
 
-static gboolean cdma_netreg_changed(DBusConnection *connection,
+static gboolean cdma_netreg_changed(DBusConnection *conn,
                                        DBusMessage *message,
                                        void *user_data)
 {
@@ -1604,8 +1697,10 @@ static void cm_update_attached(struct modem_data *modem,
 
        DBG("%s Attached %d", modem->path, modem->attached);
 
-       if (modem->attached == FALSE)
+       if (modem->attached == FALSE) {
+               remove_network(modem);
                return;
+       }
 
        if (has_interface(modem->interfaces,
                                OFONO_API_NETREG) == FALSE) {
@@ -1629,7 +1724,7 @@ static void cm_update_powered(struct modem_data *modem,
        cm_set_powered(modem, TRUE);
 }
 
-static gboolean cm_changed(DBusConnection *connection, DBusMessage *message,
+static gboolean cm_changed(DBusConnection *conn, DBusMessage *message,
                                void *user_data)
 {
        const char *path = dbus_message_get_path(message);
@@ -1684,7 +1779,7 @@ static void cdma_cm_update_settings(struct modem_data *modem,
        extract_ipv4_settings(value, modem->context);
 }
 
-static gboolean cdma_cm_changed(DBusConnection *connection,
+static gboolean cdma_cm_changed(DBusConnection *conn,
                                DBusMessage *message, void *user_data)
 {
        const char *path = dbus_message_get_path(message);
@@ -1790,7 +1885,7 @@ static void sim_update_imsi(struct modem_data *modem,
        modem->imsi = g_strdup(imsi);
 }
 
-static gboolean sim_changed(DBusConnection *connection, DBusMessage *message,
+static gboolean sim_changed(DBusConnection *conn, DBusMessage *message,
                                void *user_data)
 {
        const char *path = dbus_message_get_path(message);
@@ -1968,7 +2063,7 @@ static void modem_update_interfaces(struct modem_data *modem,
        }
 }
 
-static gboolean modem_changed(DBusConnection *connection, DBusMessage *message,
+static gboolean modem_changed(DBusConnection *conn, DBusMessage *message,
                                void *user_data)
 {
        const char *path = dbus_message_get_path(message);
@@ -2161,7 +2256,7 @@ static void remove_modem(gpointer data)
        g_free(modem);
 }
 
-static gboolean modem_added(DBusConnection *connection,
+static gboolean modem_added(DBusConnection *conn,
                                DBusMessage *message, void *user_data)
 {
        DBusMessageIter iter, properties;
@@ -2182,7 +2277,7 @@ static gboolean modem_added(DBusConnection *connection,
        return TRUE;
 }
 
-static gboolean modem_removed(DBusConnection *connection,
+static gboolean modem_removed(DBusConnection *conn,
                                DBusMessage *message, void *user_data)
 {
        DBusMessageIter iter;
@@ -2359,7 +2454,7 @@ static int network_disconnect(struct connman_network *network)
 }
 
 static struct connman_network_driver network_driver = {
-       .name           = "network",
+       .name           = "cellular",
        .type           = CONNMAN_NETWORK_TYPE_CELLULAR,
        .probe          = network_probe,
        .remove         = network_remove,
@@ -2459,68 +2554,71 @@ static int ofono_init(void)
                                        OFONO_SERVICE, ofono_connect,
                                        ofono_disconnect, NULL, NULL);
 
-       modem_added_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
-                                               OFONO_MANAGER_INTERFACE,
+       modem_added_watch = g_dbus_add_signal_watch(connection, OFONO_SERVICE,
+                                               NULL, OFONO_MANAGER_INTERFACE,
                                                MODEM_ADDED,
                                                modem_added,
                                                NULL, NULL);
 
-       modem_removed_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
+       modem_removed_watch = g_dbus_add_signal_watch(connection,
+                                               OFONO_SERVICE, NULL,
                                                OFONO_MANAGER_INTERFACE,
                                                MODEM_REMOVED,
                                                modem_removed,
                                                NULL, NULL);
 
-       modem_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
+       modem_watch = g_dbus_add_signal_watch(connection, OFONO_SERVICE, NULL,
                                                OFONO_MODEM_INTERFACE,
                                                PROPERTY_CHANGED,
                                                modem_changed,
                                                NULL, NULL);
 
-       cm_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
+       cm_watch = g_dbus_add_signal_watch(connection, OFONO_SERVICE, NULL,
                                                OFONO_CM_INTERFACE,
                                                PROPERTY_CHANGED,
                                                cm_changed,
                                                NULL, NULL);
 
-       sim_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
+       sim_watch = g_dbus_add_signal_watch(connection, OFONO_SERVICE, NULL,
                                                OFONO_SIM_INTERFACE,
                                                PROPERTY_CHANGED,
                                                sim_changed,
                                                NULL, NULL);
 
-       context_added_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
+       context_added_watch = g_dbus_add_signal_watch(connection,
+                                               OFONO_SERVICE, NULL,
                                                OFONO_CM_INTERFACE,
                                                CONTEXT_ADDED,
                                                cm_context_added,
                                                NULL, NULL);
 
-       context_removed_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
+       context_removed_watch = g_dbus_add_signal_watch(connection,
+                                               OFONO_SERVICE, NULL,
                                                OFONO_CM_INTERFACE,
                                                CONTEXT_REMOVED,
                                                cm_context_removed,
                                                NULL, NULL);
 
-       context_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
-                                               OFONO_CONTEXT_INTERFACE,
+       context_watch = g_dbus_add_signal_watch(connection, OFONO_SERVICE,
+                                               NULL, OFONO_CONTEXT_INTERFACE,
                                                PROPERTY_CHANGED,
                                                context_changed,
                                                NULL, NULL);
 
-       netreg_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
+       netreg_watch = g_dbus_add_signal_watch(connection, OFONO_SERVICE, NULL,
                                                OFONO_NETREG_INTERFACE,
                                                PROPERTY_CHANGED,
                                                netreg_changed,
                                                NULL, NULL);
 
-       cdma_cm_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
-                                               OFONO_CDMA_CM_INTERFACE,
+       cdma_cm_watch = g_dbus_add_signal_watch(connection, OFONO_SERVICE,
+                                               NULL, OFONO_CDMA_CM_INTERFACE,
                                                PROPERTY_CHANGED,
                                                cdma_cm_changed,
                                                NULL, NULL);
 
-       cdma_netreg_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
-                                               OFONO_CDMA_NETREG_INTERFACE,
+       cdma_netreg_watch = g_dbus_add_signal_watch(connection, OFONO_SERVICE,
+                                               NULL, OFONO_CDMA_NETREG_INTERFACE,
                                                PROPERTY_CHANGED,
                                                cdma_netreg_changed,
                                                NULL, NULL);
index cf3bcd9..70be7ae 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
index 2f7decf..8df8a3f 100644 (file)
@@ -48,20 +48,21 @@ static DBusConnection *connection;
 struct {
        const char *cm_opt;
        const char *ov_opt;
+       char       has_value;
 } ov_options[] = {
-       { "Host", "--remote" },
-       { "OpenVPN.CACert", "--ca" },
-       { "OpenVPN.Cert", "--cert" },
-       { "OpenVPN.Key", "--key" },
-       { "OpenVPN.MTU", "--mtu" },
-       { "OpenVPN.Proto", "--proto" },
-       { "OpenVPN.Port", "--port" },
-       { "OpenVPN.AuthUserPass", "--auth-user-pass" },
-       { "OpenVPN.TLSRemote", "--tls-remote" },
-       { "OpenVPN.Cipher", "--cipher" },
-       { "OpenVPN.Auth", "--auth" },
-       { "OpenVPN.CompLZO", "--comp-lzo" },
-       { "OpenVPN.RemoteCertTls", "--remote-cert-tls" },
+       { "Host", "--remote", 1 },
+       { "OpenVPN.CACert", "--ca", 1 },
+       { "OpenVPN.Cert", "--cert", 1 },
+       { "OpenVPN.Key", "--key", 1 },
+       { "OpenVPN.MTU", "--mtu", 1 },
+       { "OpenVPN.Proto", "--proto", 1 },
+       { "OpenVPN.Port", "--port", 1 },
+       { "OpenVPN.AuthUserPass", "--auth-user-pass", 1 },
+       { "OpenVPN.TLSRemote", "--tls-remote", 1 },
+       { "OpenVPN.Cipher", "--cipher", 1 },
+       { "OpenVPN.Auth", "--auth", 1 },
+       { "OpenVPN.CompLZO", "--comp-lzo", 0 },
+       { "OpenVPN.RemoteCertTls", "--remote-cert-tls", 1 },
 };
 
 static void ov_append_dns_entries(const char *key, const char *value,
@@ -208,7 +209,8 @@ static int task_append_config_data(struct connman_provider *provider,
                        continue;
 
                if (connman_task_add_argument(task,
-                                       ov_options[i].ov_opt, option) < 0) {
+                                       ov_options[i].ov_opt,
+                                       ov_options[i].has_value ? option : NULL) < 0) {
                        return -EIO;
                }
        }
index 84c22ab..418dce2 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
@@ -121,6 +121,7 @@ static void create_proxy_configuration(void)
 
        switch(connman_service_get_proxy_method(default_service)) {
        case CONNMAN_SERVICE_PROXY_METHOD_UNKNOWN:
+               connman_dbus_dict_close(&iter, &dict);
                goto done;
        case CONNMAN_SERVICE_PROXY_METHOD_DIRECT:
                method= "direct";
@@ -129,8 +130,10 @@ static void create_proxy_configuration(void)
                method = "manual";
 
                str_list = connman_service_get_proxy_servers(default_service);
-               if (str_list == NULL)
+               if (str_list == NULL) {
+                       connman_dbus_dict_close(&iter, &dict);
                        goto done;
+               }
 
                connman_dbus_dict_append_array(&dict, "Servers",
                                        DBUS_TYPE_STRING, append_string_list,
@@ -154,8 +157,10 @@ static void create_proxy_configuration(void)
                if (str == NULL) {
                        str = connman_service_get_proxy_autoconfig(
                                                        default_service);
-                       if (str == NULL)
+                       if (str == NULL) {
+                               connman_dbus_dict_close(&iter, &dict);
                                goto done;
+                       }
                }
 
                connman_dbus_dict_append_basic(&dict, "URL",
@@ -183,6 +188,7 @@ static void create_proxy_configuration(void)
                connman_dbus_dict_append_array(&dict, "Nameservers",
                                        DBUS_TYPE_STRING, append_string_list,
                                        str_list);
+       g_strfreev(str_list);
 
        connman_dbus_dict_close(&iter, &dict);
 
index 0b232d9..ae38364 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
index 40f95ed..9db1b89 100644 (file)
@@ -3,7 +3,7 @@
  *  Connection Manager
  *
  *  Copyright (C) 2010  BMW Car IT GmbH. All rights reserved.
- *  Copyright (C) 2011  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
index 91de525..50e27c3 100644 (file)
@@ -1,31 +1,51 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
 #ifdef HAVE_CONFIG_H
 #include <config.h>
 #endif
 
 #include <errno.h>
-#include <stdlib.h>
-
 #include <gdbus.h>
+#include <stdlib.h>
 #include <string.h>
 
 #define CONNMAN_API_SUBJECT_TO_CHANGE
-#include <connman/plugin.h>
-#include <connman/device.h>
-#include <connman/network.h>
-#include <connman/ipconfig.h>
 #include <connman/dbus.h>
 #include <connman/inet.h>
+#include <connman/plugin.h>
+#include <connman/network.h>
+#include <connman/setting.h>
 #include <connman/technology.h>
-#include <connman/log.h>
+
+#include <connman.h>
 
 #define PS_DBUS_SERVICE                                "com.tcore.ps"
 
-#define PS_MASTER_INTERFACE            PS_DBUS_SERVICE ".master"
-#define PS_MODEM_INTERFACE             PS_DBUS_SERVICE ".modem"
+#define PS_MASTER_INTERFACE                    PS_DBUS_SERVICE ".master"
+#define PS_MODEM_INTERFACE                     PS_DBUS_SERVICE ".modem"
 #define PS_SERVICE_INTERFACE           PS_DBUS_SERVICE ".service"
 #define PS_CONTEXT_INTERFACE           PS_DBUS_SERVICE ".context"
 
-//methods
+/* methods */
 #define GET_MODEMS                             "GetModems"
 #define GET_SERVICES                   "GetServices"
 #define GET_CONTEXTS                   "GetContexts"
@@ -34,7 +54,7 @@
 #define GET_PROPERTIES                 "GetProperties"
 #define SET_PROPERTY                   "SetProperties"
 
-//signals
+/* signals */
 #define MODEM_ADDED                            "ModemAdded"
 #define MODEM_REMOVED                  "ModemRemoved"
 #define SERVICE_ADDED                  "ServiceAdded"
@@ -43,9 +63,9 @@
 #define CONTEXT_REMOVED                        "ContextRemoved"
 #define PROPERTY_CHANGED               "PropertyChanged"
 
-#define TIMEOUT 40000
+#define TIMEOUT 130000
 
-#define STRING2BOOL(a) ((g_str_equal(a, "TRUE")) ?  (TRUE):(FALSE))
+#define STRING2BOOL(a) ((g_str_equal(a, "TRUE")) ? (TRUE):(FALSE))
 
 static DBusConnection *connection;
 static GHashTable      *modem_hash;
@@ -53,16 +73,16 @@ static GHashTable   *service_hash;
 static GHashTable      *network_hash;
 
 struct telephony_service {
-       charpath;
+       char *path;
 
        gpointer p_modem;
        char *act;
-       gboolean roaming; //global roaming state
-       gboolean ps_attached; //packet service is available
+       gboolean roaming; /* global roaming state */
+       gboolean ps_attached; /* packet service is available */
 };
 
 struct telephony_modem {
-       charpath;
+       char *path;
 
        char *operator;
        gboolean powered;
@@ -80,13 +100,13 @@ struct telephony_network {
        struct connman_network *network;
 
        enum connman_ipconfig_method ipv4_method;
-       struct connman_ipaddress ipv4_address;
+       struct connman_ipaddress *ipv4_address;
 
        enum connman_ipconfig_method ipv6_method;
-       struct connman_ipaddress ipv6_address;
+       struct connman_ipaddress *ipv6_address;
 };
 
-// function prototype
+/* function prototype */
 static void telephony_connect(DBusConnection *connection, void *user_data);
 static void telephony_disconnect(DBusConnection *connection, void *user_data);
 static void __remove_modem(gpointer data);
@@ -104,48 +124,53 @@ static int __network_connect(struct connman_network *network);
 static int __network_disconnect(struct connman_network *network);
 
 
-// dbus request and reply
-static int __dbus_request(const char *path, const char *interface, const char *method,
+/* dbus request and reply */
+static int __dbus_request(const char *path, const char *interface,
+                       const char *method,
                        DBusPendingCallNotifyFunction notify, void *user_data,
                        DBusFreeFunction free_function, int type, ...);
 
 static int __request_get_modems(void);
 static void __response_get_modems(DBusPendingCall *call, void *user_data);
-static int __request_get_services(const charpath);
+static int __request_get_services(const char *path);
 static void __response_get_services(DBusPendingCall *call, void *user_data);
-static int __request_get_contexts(struct telephony_modemmodem);
+static int __request_get_contexts(struct telephony_modem *modem);
 static void __response_get_contexts(DBusPendingCall *call, void *user_data);
 static int __request_network_activate(struct connman_network *network);
 static void __response_network_activate(DBusPendingCall *call, void *user_data);
 static int __request_network_deactivate(struct connman_network *network);
 
-// telephony internal function
+/* telephony internal function */
 static void __add_modem(const char *path, DBusMessageIter *prop);
-static void __add_service(struct telephony_modem* modem, const char *service_path, DBusMessageIter *prop);
-static void __add_connman_device(const char* modem_path, const char* operator);
+static void __add_service(struct telephony_modem *modem,
+                       const char *service_path, DBusMessageIter *prop);
+static void __add_connman_device(const char *modem_path, const char *operator);
 static void __remove_connman_device(struct telephony_modem *modem);
 static void __remove_connman_networks(struct connman_device *device);
-static void __set_device_powered(struct telephony_modem *modem, gboolean powered);
-static int __check_device_powered(const char *path, gboolean online);
-static gboolean __check_network_available(struct connman_network *network);
-static void __create_service(struct connman_network *network);
-static int __add_context(struct connman_device *device, const char *path, DBusMessageIter *prop);
-static gboolean __set_network_ipconfig(struct telephony_network *network, DBusMessageIter *dict);
-static void __set_network_connected(struct telephony_network *network, gboolean connected);
-static char *__get_ident(const char *path);
-
-// signal handler
-static gboolean __changed_modem(DBusConnection *connection, DBusMessage *message, void *user_data);
-static gboolean __added_modem(DBusConnection *connection, DBusMessage *message, void *user_data);
-static gboolean __removed_modem(DBusConnection *connection, DBusMessage *message, void *user_data);
-static gboolean __changed_service(DBusConnection *connection, DBusMessage *message, void *user_data);
-static gboolean __added_service(DBusConnection *connection, DBusMessage *message, void *user_data);
-static gboolean __removed_service(DBusConnection *connection, DBusMessage *message, void *user_data);
-static gboolean __changed_context(DBusConnection *connection, DBusMessage *message, void *user_data);
-static gboolean __added_context(DBusConnection *connection, DBusMessage *message, void *user_data);
-static gboolean __removed_context(DBusConnection *connection, DBusMessage *message, void *user_data);
-
-// device driver
+static int __add_context(struct connman_device *device, const char *path,
+                                                       DBusMessageIter *prop);
+
+/* signal handler */
+static gboolean __changed_modem(DBusConnection *connection,
+                               DBusMessage *message, void *user_data);
+static gboolean __added_modem(DBusConnection *connection,
+                               DBusMessage *message, void *user_data);
+static gboolean __removed_modem(DBusConnection *connection,
+                               DBusMessage *message, void *user_data);
+static gboolean __changed_service(DBusConnection *connection,
+                               DBusMessage *message, void *user_data);
+static gboolean __added_service(DBusConnection *connection,
+                               DBusMessage *message, void *user_data);
+static gboolean __removed_service(DBusConnection *connection,
+                               DBusMessage *message, void *user_data);
+static gboolean __changed_context(DBusConnection *connection,
+                               DBusMessage *message, void *user_data);
+static gboolean __added_context(DBusConnection *connection,
+                               DBusMessage *message, void *user_data);
+static gboolean __removed_context(DBusConnection *connection,
+                               DBusMessage *message, void *user_data);
+
+/* device driver */
 static struct connman_device_driver modem_driver = {
        .name           = "device",
        .type           = CONNMAN_DEVICE_TYPE_CELLULAR,
@@ -155,7 +180,7 @@ static struct connman_device_driver modem_driver = {
        .disable        = __modem_disable,
 };
 
-// network driver
+/* network driver */
 static struct connman_network_driver network_driver = {
        .name           = "network",
        .type           = CONNMAN_NETWORK_TYPE_CELLULAR,
@@ -172,6 +197,7 @@ static int tech_probe(struct connman_technology *technology)
 
 static void tech_remove(struct connman_technology *technology)
 {
+       return;
 }
 
 static struct connman_technology_driver tech_driver = {
@@ -181,13 +207,16 @@ static struct connman_technology_driver tech_driver = {
        .remove         = tech_remove,
 };
 
-// local function
+/* local function */
 static void telephony_connect(DBusConnection *connection, void *user_data)
 {
        DBG("connection %p", connection);
-       modem_hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, __remove_modem);
-       service_hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, __remove_service);
-       network_hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, __remove_network);
+       modem_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                               g_free, __remove_modem);
+       service_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                               g_free, __remove_service);
+       network_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                               g_free, __remove_network);
        __request_get_modems();
        return;
 }
@@ -241,9 +270,37 @@ static void __remove_network(gpointer data)
        connman_network_unref(info->network);
 
        g_free(info->path);
+
+       connman_ipaddress_free(info->ipv4_address);
+       connman_ipaddress_free(info->ipv6_address);
+
        g_free(info);
 }
 
+static void __set_device_powered(struct telephony_modem *modem,
+                                       gboolean powered)
+{
+       DBG("set modem(%s) powered(%d)", modem->path, powered);
+
+       if (modem->device)
+               connman_device_set_powered(modem->device, powered);
+}
+
+static int __check_device_powered(const char *path, gboolean powered)
+{
+       struct telephony_modem *modem = g_hash_table_lookup(modem_hash, path);
+
+       if (modem == NULL)
+               return -ENODEV;
+
+       DBG("check modem (%s) powered (%d)", modem->path, modem->powered);
+
+       if (modem->powered == powered)
+               return -EALREADY;
+
+       return 0;
+}
+
 static int __modem_probe(struct connman_device *device)
 {
        DBG("device %p", device);
@@ -279,28 +336,8 @@ static int __network_probe(struct connman_network *network)
 
 static int __network_connect(struct connman_network *network)
 {
-       struct connman_device *device;
-       struct telephony_modem *modem;
-
        DBG("network %p", network);
 
-       device = connman_network_get_device(network);
-       if (device == NULL)
-               return -ENODEV;
-
-       modem = connman_device_get_data(device);
-       if (modem == NULL)
-               return -ENODEV;
-
-       if (modem->powered == FALSE)
-               return -ENOLINK;
-
-/*     if (modem->online == FALSE)
-               return -ENOLINK;
-
-       if (modem->roaming_allowed == FALSE && modem->roaming == TRUE)
-               return -ENOLINK;*/
-
        return __request_network_activate(network);
 }
 
@@ -308,8 +345,8 @@ static int __network_disconnect(struct connman_network *network)
 {
        DBG("network %p", network);
 
-       if (connman_network_get_index(network) < 0)
-               return -ENOTCONN;
+       if (connman_network_get_associating(network) == TRUE)
+               connman_network_clear_associating(network);
 
        connman_network_set_associating(network, FALSE);
 
@@ -325,7 +362,8 @@ static void __network_remove(struct connman_network *network)
        return;
 }
 
-static int __dbus_request(const char *path, const char *interface, const char *method,
+static int __dbus_request(const char *path, const char *interface,
+                       const char *method,
                        DBusPendingCallNotifyFunction notify, void *user_data,
                        DBusFreeFunction free_function, int type, ...)
 {
@@ -341,7 +379,8 @@ static int __dbus_request(const char *path, const char *interface, const char *m
        if (path == NULL)
                return -EINVAL;
 
-       message = dbus_message_new_method_call(PS_DBUS_SERVICE, path, interface, method);
+       message = dbus_message_new_method_call(PS_DBUS_SERVICE, path,
+                                               interface, method);
        if (message == NULL)
                return -ENOMEM;
 
@@ -354,7 +393,8 @@ static int __dbus_request(const char *path, const char *interface, const char *m
        if (!ok)
                return -ENOMEM;
 
-       if (dbus_connection_send_with_reply(connection, message, &call, TIMEOUT) == FALSE) {
+       if (dbus_connection_send_with_reply(connection, message,
+                                               &call, TIMEOUT) == FALSE) {
                connman_error("Failed to call %s.%s", interface, method);
                dbus_message_unref(message);
                return -EINVAL;
@@ -376,7 +416,7 @@ static int __dbus_request(const char *path, const char *interface, const char *m
 static int __request_get_modems(void)
 {
        DBG("request get modem");
-       //call connect master
+       /* call connect master */
        return __dbus_request("/", PS_MASTER_INTERFACE, GET_MODEMS,
                        __response_get_modems, NULL, NULL, DBUS_TYPE_INVALID);
 }
@@ -394,7 +434,7 @@ static void __response_get_modems(DBusPendingCall *call, void *user_data)
        dbus_error_init(&error);
 
        if (dbus_set_error_from_message(&error, reply)) {
-               connman_error("GetModems() %s %s",error.name, error.message);
+               connman_error("GetModems() %s %s", error.name, error.message);
                dbus_error_free(&error);
                goto done;
        }
@@ -406,7 +446,9 @@ static void __response_get_modems(DBusPendingCall *call, void *user_data)
 
        dbus_message_iter_recurse(&args, &dict);
 
-       //DBG("message type (%d) dic(%d)", dbus_message_iter_get_arg_type(&dict), DBUS_TYPE_DICT_ENTRY);
+       /* DBG("message type (%d) dic(%d)",
+        *      dbus_message_iter_get_arg_type(&dict), DBUS_TYPE_DICT_ENTRY);
+        */
 
        while (dbus_message_iter_get_arg_type(&dict) != DBUS_TYPE_INVALID) {
                DBusMessageIter entry, property;
@@ -430,11 +472,12 @@ done:
        return;
 }
 
-static int __request_get_services(const charpath)
+static int __request_get_services(const char *path)
 {
        DBG("request get service");
-       return __dbus_request(path,PS_MODEM_INTERFACE,GET_SERVICES,
-                       __response_get_services, g_strdup(path), g_free, DBUS_TYPE_INVALID);
+       return __dbus_request(path, PS_MODEM_INTERFACE, GET_SERVICES,
+                               __response_get_services, g_strdup(path),
+                               g_free, DBUS_TYPE_INVALID);
 }
 
 static void __response_get_services(DBusPendingCall *call, void *user_data)
@@ -459,7 +502,7 @@ static void __response_get_services(DBusPendingCall *call, void *user_data)
        dbus_error_init(&error);
 
        if (dbus_set_error_from_message(&error, reply)) {
-               connman_error("GetServices() %s %s",error.name, error.message);
+               connman_error("GetServices() %s %s", error.name, error.message);
                dbus_error_free(&error);
                goto done;
        }
@@ -471,7 +514,9 @@ static void __response_get_services(DBusPendingCall *call, void *user_data)
 
        dbus_message_iter_recurse(&args, &dict);
 
-       //DBG("message type (%d) dic(%d)", dbus_message_iter_get_arg_type(&dict), DBUS_TYPE_DICT_ENTRY);
+       /* DBG("message type (%d) dic(%d)",
+        *       dbus_message_iter_get_arg_type(&dict), DBUS_TYPE_DICT_ENTRY);
+        */
 
        while (dbus_message_iter_get_arg_type(&dict) != DBUS_TYPE_INVALID) {
                DBusMessageIter entry, property;
@@ -484,7 +529,7 @@ static void __response_get_services(DBusPendingCall *call, void *user_data)
                dbus_message_iter_next(&entry);
                dbus_message_iter_recurse(&entry, &property);
 
-               __add_service(modem, service_path,&property);
+               __add_service(modem, service_path, &property);
 
                dbus_message_iter_next(&dict);
        }
@@ -495,11 +540,13 @@ done:
        return;
 }
 
-static int __request_get_contexts(struct telephony_modemmodem)
+static int __request_get_contexts(struct telephony_modem *modem)
 {
        DBG("request get contexts");
-       return __dbus_request(modem->s_service->path,PS_SERVICE_INTERFACE,GET_CONTEXTS,
-               __response_get_contexts, g_strdup(modem->path), g_free, DBUS_TYPE_INVALID);
+       return __dbus_request(modem->s_service->path,
+                               PS_SERVICE_INTERFACE, GET_CONTEXTS,
+                               __response_get_contexts, g_strdup(modem->path),
+                               g_free, DBUS_TYPE_INVALID);
 }
 
 static void __response_get_contexts(DBusPendingCall *call, void *user_data)
@@ -526,7 +573,7 @@ static void __response_get_contexts(DBusPendingCall *call, void *user_data)
        dbus_error_init(&error);
 
        if (dbus_set_error_from_message(&error, reply)) {
-               connman_error("GetContexts() %s %s",error.name, error.message);
+               connman_error("GetContexts() %s %s", error.name, error.message);
                dbus_error_free(&error);
                goto done;
        }
@@ -557,7 +604,6 @@ static void __response_get_contexts(DBusPendingCall *call, void *user_data)
 done:
        dbus_message_unref(reply);
        dbus_pending_call_unref(call);
-       return;
 }
 
 static int __request_network_activate(struct connman_network *network)
@@ -567,8 +613,17 @@ static int __request_network_activate(struct connman_network *network)
        const char *path = connman_network_get_string(network, "Path");
        DBG("network %p, path %s", network, path);
 
-       return __dbus_request(path,PS_CONTEXT_INTERFACE,ACTIVATE_CONTEXT,
-               __response_network_activate, g_strdup(path), NULL, DBUS_TYPE_INVALID);
+       return __dbus_request(path, PS_CONTEXT_INTERFACE, ACTIVATE_CONTEXT,
+                       __response_network_activate,
+                       g_strdup(path), NULL, DBUS_TYPE_INVALID);
+}
+
+static gboolean __check_network_available(struct connman_network *network)
+{
+       if (network == NULL || connman_network_get_device(network) == NULL)
+               return FALSE;
+
+       return TRUE;
 }
 
 static void __response_network_activate(DBusPendingCall *call, void *user_data)
@@ -579,7 +634,7 @@ static void __response_network_activate(DBusPendingCall *call, void *user_data)
        DBusMessage *reply;
 
        struct telephony_network *info;
-       const charpath = user_data;
+       const char *path = user_data;
 
        info = g_hash_table_lookup(network_hash, path);
        reply = dbus_pending_call_steal_reply(call);
@@ -587,17 +642,27 @@ static void __response_network_activate(DBusPendingCall *call, void *user_data)
        if (info == NULL)
                goto done;
 
-       if (!__check_network_available(info->network)) {
+       if (__check_network_available(info->network) == FALSE) {
                g_hash_table_remove(network_hash, path);
                goto done;
        }
 
        dbus_error_init(&error);
        if (dbus_set_error_from_message(&error, reply)) {
-               connman_error("connection activate() %s %s",error.name, error.message);
+               connman_error("connection activate() %s %s",
+                                       error.name, error.message);
+
+               if (connman_network_get_associating(info->network) == TRUE)
+                       connman_network_set_error(info->network,
+                                       CONNMAN_NETWORK_ERROR_ASSOCIATE_FAIL);
+
+               if (connman_network_get_connecting(info->network) == TRUE)
+                       connman_network_set_error(info->network,
+                                       CONNMAN_NETWORK_ERROR_CONNECT_FAIL);
 
                if (connman_network_get_index(info->network) < 0)
-                       connman_network_set_error(info->network, CONNMAN_NETWORK_ERROR_ASSOCIATE_FAIL);
+                       connman_network_set_error(info->network,
+                                       CONNMAN_NETWORK_ERROR_ASSOCIATE_FAIL);
 
                dbus_error_free(&error);
                goto done;
@@ -616,7 +681,7 @@ static int __request_network_deactivate(struct connman_network *network)
        const char *path = connman_network_get_string(network, "Path");
        DBG("network %p, path %s", network, path);
 
-       return __dbus_request(path,PS_CONTEXT_INTERFACE,DEACTIVATE_CONTEXT,
+       return __dbus_request(path, PS_CONTEXT_INTERFACE, DEACTIVATE_CONTEXT,
                NULL, NULL, NULL, DBUS_TYPE_INVALID);
 }
 
@@ -628,8 +693,9 @@ static void __add_modem(const char *path, DBusMessageIter *prop)
        if (modem != NULL)
                return;
 
-       modem = (struct telephony_modem *)malloc( sizeof(struct telephony_modem));
-       memset(modem, 0, sizeof(struct telephony_modem));
+       modem = g_try_new0(struct telephony_modem, 1);
+       if (modem == NULL)
+               return;
 
        modem->path = g_strdup(path);
        modem->device = NULL;
@@ -678,15 +744,17 @@ static void __add_modem(const char *path, DBusMessageIter *prop)
        return;
 }
 
-static void __add_service(struct telephony_modem* modem, const char *service_path, DBusMessageIter *prop)
+static void __add_service(struct telephony_modem *modem,
+                               const char *service_path, DBusMessageIter *prop)
 {
        struct telephony_service *service;
 
        if (modem->s_service != NULL)
                return;
 
-       service = (struct telephony_service *)malloc( sizeof(struct telephony_service));
-       memset(service, 0, sizeof(struct telephony_service));
+       service = g_try_new0(struct telephony_service, 1);
+       if (service == NULL)
+               return;
 
        service->path = g_strdup(service_path);
        service->p_modem = modem;
@@ -721,7 +789,7 @@ static void __add_service(struct telephony_modem* modem, const char *service_pat
        return;
 }
 
-static void __add_connman_device(const char* modem_path, const char* operator)
+static void __add_connman_device(const char *modem_path, const char *operator)
 {
        struct telephony_modem *modem;
        struct connman_device *device;
@@ -739,7 +807,8 @@ static void __add_connman_device(const char* modem_path, const char* operator)
                return;
 
        if (modem->device) {
-               if (!g_strcmp0(operator, connman_device_get_ident(modem->device)))
+               if (!g_strcmp0(operator,
+                               connman_device_get_ident(modem->device)))
                        return;
 
                __remove_connman_device(modem);
@@ -813,295 +882,389 @@ static void __remove_connman_networks(struct connman_device *device)
        g_slist_free(info_list);
 }
 
-static void __set_device_powered(struct telephony_modem *modem, gboolean powered)
-{
-       DBG("set modem(%s) powered(%d)", modem->path, powered);
-
-       if (modem->device) {
-               connman_device_set_powered(modem->device, powered);
-       }
-
-       return;
-}
-
-static int __check_device_powered(const char *path, gboolean powered)
+static char *__get_ident(const char *path)
 {
-       struct telephony_modem *modem = g_hash_table_lookup(modem_hash, path);
-
-       if (modem == NULL)
-               return -ENODEV;
+       char *pos;
 
-       DBG("check modem (%s) powered (%d)", modem->path, modem->powered);
+       if (*path != '/')
+               return NULL;
 
-       if (modem->powered == powered)
-               return -EALREADY;
+       pos = strrchr(path, '/');
+       if (pos == NULL)
+               return NULL;
 
-       return 0;
+       return pos + 1;
 }
 
-static gboolean __check_network_available(struct connman_network *network)
+static gboolean connman_ipaddress_updated(struct connman_ipaddress *ipaddress,
+                                       const char *address, const char *gateway)
 {
-       if (network == NULL || connman_network_get_device(network) == NULL) {
-               DBG("Modem or network was removed");
+       if (ipaddress == NULL)
                return FALSE;
-       }
-
-       return TRUE;
-}
-
-static int __add_context(struct connman_device *device, const char *path, DBusMessageIter *prop)
-{
-       char *ident;
-       gboolean active = FALSE;
-
-       struct telephony_modem *modem = connman_device_get_data(device);
-       struct connman_network *network;
-       struct telephony_network *info;
 
-       DBG("modem %p device %p path %s", modem, device, path);
+       if (g_strcmp0(ipaddress->local, address) != 0)
+               return TRUE;
 
-       ident = __get_ident(path);
+       if (g_strcmp0(ipaddress->gateway, gateway) != 0)
+               return TRUE;
 
-       network = connman_device_get_network(device, ident);
-       if (network != NULL)
-               return -EALREADY;
+       return FALSE;
+}
 
-       info = g_hash_table_lookup(network_hash, path);
-       if (info != NULL) {
-               DBG("path %p already exists with device %p", path, connman_network_get_device(info->network));
+static void __set_network_connected(struct telephony_network *network,
+                                       connman_bool_t connected)
+{
+       gboolean setip = FALSE;
 
-               if (connman_network_get_device(info->network))
-                       return -EALREADY;
+       DBG("network %p connected %d", network, connected);
 
-               g_hash_table_remove(network_hash, path);
-       }
+       if (connman_network_get_connected(network->network) == connected)
+               return;
 
-       network = connman_network_create(ident, CONNMAN_NETWORK_TYPE_CELLULAR);
-       if (network == NULL)
-               return -ENOMEM;
+       switch (network->ipv4_method) {
+       case CONNMAN_IPCONFIG_METHOD_UNKNOWN:
+       case CONNMAN_IPCONFIG_METHOD_OFF:
+       case CONNMAN_IPCONFIG_METHOD_MANUAL:
+       case CONNMAN_IPCONFIG_METHOD_AUTO:
+               setip = TRUE;
+               break;
 
-       info = (struct telephony_network *)malloc( sizeof(struct telephony_network));
-       memset(info, 0, sizeof(struct telephony_network));
+       case CONNMAN_IPCONFIG_METHOD_FIXED:
+               connman_network_set_ipv4_method(network->network,
+                                                       network->ipv4_method);
+               connman_network_set_ipaddress(network->network,
+                                                       network->ipv4_address);
+               setip = TRUE;
+               break;
 
-       if (info == NULL) {
-               connman_network_unref(network);
-               return -ENOMEM;
+       case CONNMAN_IPCONFIG_METHOD_DHCP:
+               connman_network_set_ipv4_method(network->network,
+                                                       network->ipv4_method);
+               setip = TRUE;
+               break;
        }
 
-       info->path = g_strdup(path);
-
-       connman_ipaddress_clear(&info->ipv4_address);
-       connman_ipaddress_clear(&info->ipv6_address);
-       info->network = network;
-
-       connman_network_set_string(network, "Path", path);
-       connman_network_set_name(network, path);
-       
-       __create_service(network);
-
-       g_hash_table_insert(network_hash, g_strdup(path), info);
+       switch (network->ipv6_method) {
+       case CONNMAN_IPCONFIG_METHOD_UNKNOWN:
+       case CONNMAN_IPCONFIG_METHOD_OFF:
+               setip = TRUE;
+               break;
 
-       connman_network_set_available(network, TRUE);
-       connman_network_set_index(network, -1);
-       connman_network_set_roaming(network, modem->s_service->roaming);
+       case CONNMAN_IPCONFIG_METHOD_MANUAL:
+       case CONNMAN_IPCONFIG_METHOD_DHCP:
+               break;
+       case CONNMAN_IPCONFIG_METHOD_AUTO:
+               connman_network_set_ipv6_method(network->network,
+                                                       network->ipv6_method);
+               setip = TRUE;
+               break;
 
-       if (connman_device_add_network(device, network) != 0) {
-               g_hash_table_remove(network_hash, path);
-               return -EIO;
+       case CONNMAN_IPCONFIG_METHOD_FIXED:
+               connman_network_set_ipv6_method(network->network,
+                                                       network->ipv6_method);
+               connman_network_set_ipaddress(network->network,
+                                                       network->ipv6_address);
+               setip = TRUE;
+               break;
        }
 
-       active = __set_network_ipconfig(info, prop);
-
-       if (active && (connman_network_get_connecting(network) || connman_network_get_associating(network)))
-               __set_network_connected(info, active);
-
-       return 0;
-}
-
-static void __create_service(struct connman_network *network)
-{
-       const char *path;
-       char *group;
-
-       DBG("");
-
-       path = connman_network_get_string(network, "Path");
-
-       group = __get_ident(path);
-
-       connman_network_set_group(network, group);
+       if (setip == TRUE)
+               connman_network_set_connected(network->network, connected);
 }
 
-static gboolean __set_network_ipconfig(struct telephony_network *network, DBusMessageIter *dict)
+static connman_bool_t __set_network_context(
+                                                       struct telephony_network *network,
+                                                       DBusMessageIter *dict)
 {
-       DBG("set network info");
-
-       gboolean active = FALSE;
-       char *dev_name=NULL, *proxy_addr=NULL;
-       char *ipv4_addr=NULL, *ipv4_gw=NULL, *ipv4_netmask=NULL, *ipv4_dns1=NULL, *ipv4_dns2=NULL;
-       char *ipv6_addr=NULL, *ipv6_gw=NULL, *ipv6_netmask=NULL, *ipv6_dns1=NULL, *ipv6_dns2=NULL;
-       int index;
+       int index = 0;
+       connman_bool_t active = FALSE;
+       gboolean ipv4_updated = FALSE;
+       gboolean ipv6_updated = FALSE;
+       gboolean default_internet = FALSE;
+       gboolean active_proxy = FALSE;
+       char **proxies = NULL;
+       const char *dev_name = NULL;
+       const char *proxy_addr = NULL;
+       char *ipv4_addr = NULL, *ipv4_gw = NULL, *ipv4_netmask = NULL,
+                                       *ipv4_dns1 = NULL, *ipv4_dns2 = NULL;
+       char *ipv6_addr = NULL, *ipv6_gw = NULL, *ipv6_netmask = NULL,
+                                       *ipv6_dns1 = NULL, *ipv6_dns2 = NULL;
+       struct connman_service *service;
 
        while (dbus_message_iter_get_arg_type(dict) != DBUS_TYPE_INVALID) {
                DBusMessageIter entry;
-               const char* key, *tmp;
+               const char *key, *value;
 
                dbus_message_iter_recurse(dict, &entry);
                dbus_message_iter_get_basic(&entry, &key);
 
                dbus_message_iter_next(&entry);
 
-               DBG("key (%s)", key);
-
                if (g_str_equal(key, "dev_name") == TRUE) {
                        dbus_message_iter_get_basic(&entry, &dev_name);
                        DBG("dev_name (%s)", dev_name);
-               } else if(g_str_equal(key, "proxy") == TRUE) {
+               } else if (g_str_equal(key, "proxy") == TRUE) {
                        dbus_message_iter_get_basic(&entry, &proxy_addr);
+                       DBG("proxy_addr (%s)", proxy_addr);
                } else if (g_str_equal(key, "ipv4_address") == TRUE) {
                        dbus_message_iter_get_basic(&entry, &ipv4_addr);
-                       DBG("ipv4 address (%s)", ipv4_addr);
+                       DBG("ipv4_addr (%s)", ipv4_addr);
                } else if (g_str_equal(key, "ipv4_gateway") == TRUE) {
                        dbus_message_iter_get_basic(&entry, &ipv4_gw);
+                       DBG("ipv4_gw (%s)", ipv4_gw);
                } else if (g_str_equal(key, "ipv4_netmask") == TRUE) {
                        dbus_message_iter_get_basic(&entry, &ipv4_netmask);
+                       DBG("ipv4_netmask (%s)", ipv4_netmask);
                } else if (g_str_equal(key, "ipv4_dns1") == TRUE) {
                        dbus_message_iter_get_basic(&entry, &ipv4_dns1);
+                       DBG("ipv4_dns1 (%s)", ipv4_dns1);
                } else if (g_str_equal(key, "ipv4_dns2") == TRUE) {
                        dbus_message_iter_get_basic(&entry, &ipv4_dns2);
+                       DBG("ipv4_dns2 (%s)", ipv4_dns2);
                } else if (g_str_equal(key, "ipv6_address") == TRUE) {
                        dbus_message_iter_get_basic(&entry, &ipv6_addr);
                        DBG("ipv6 address (%s)", ipv6_addr);
                } else if (g_str_equal(key, "ipv6_gateway") == TRUE) {
                        dbus_message_iter_get_basic(&entry, &ipv6_gw);
+                       DBG("ipv6_gw (%s)", ipv6_gw);
                } else if (g_str_equal(key, "ipv6_netmask") == TRUE) {
                        dbus_message_iter_get_basic(&entry, &ipv6_netmask);
+                       DBG("ipv6_netmask (%s)", ipv6_netmask);
                } else if (g_str_equal(key, "ipv6_dns1") == TRUE) {
                        dbus_message_iter_get_basic(&entry, &ipv6_dns1);
+                       DBG("ipv6_dns1 (%s)", ipv6_dns1);
                } else if (g_str_equal(key, "ipv6_dns2") == TRUE) {
                        dbus_message_iter_get_basic(&entry, &ipv6_dns2);
+                       DBG("ipv6_dns2 (%s)", ipv6_dns2);
                } else if (g_str_equal(key, "active") == TRUE) {
-                       dbus_message_iter_get_basic(&entry, &tmp);
-                       DBG("active (%s)", tmp);
-                       active = STRING2BOOL(tmp);
+                       dbus_message_iter_get_basic(&entry, &value);
+                       DBG("active (%s)", value);
+                       active = STRING2BOOL(value);
+               } else if (g_str_equal(key, "default_internet_conn") == TRUE) {
+                       dbus_message_iter_get_basic(&entry, &value);
+                       DBG("default_internet (%s)", value);
+                       default_internet = STRING2BOOL(value);
                }
 
                dbus_message_iter_next(dict);
        }
 
-       // interface index set
-       if (g_str_equal(dev_name ,"")) {
-               const char *ifname = NULL;
-               ifname = connman_network_get_ifname(network->network);
-               index = connman_inet_ifindex(ifname);
-       } else
+       if (g_strcmp0(proxy_addr, ":") == 0)
+               proxy_addr = NULL;
+       if (g_strcmp0(ipv4_addr, "0.0.0.0") == 0)
+               ipv4_addr = NULL;
+       if (g_strcmp0(ipv4_gw, "0.0.0.0") == 0)
+               ipv4_gw = NULL;
+       if (g_strcmp0(ipv4_netmask, "0.0.0.0") == 0)
+               ipv4_netmask = NULL;
+       if (g_strcmp0(ipv4_dns1, "0.0.0.0") == 0)
+               ipv4_dns1 = NULL;
+       if (g_strcmp0(ipv4_dns2, "0.0.0.0") == 0)
+               ipv4_dns2 = NULL;
+       if (g_strcmp0(ipv6_addr, "::") == 0)
+               ipv6_addr = NULL;
+       if (g_strcmp0(ipv6_gw, "::") == 0)
+               ipv6_gw = NULL;
+       if (g_strcmp0(ipv6_netmask, "::") == 0)
+               ipv6_netmask = NULL;
+       if (g_strcmp0(ipv6_dns1, "::") == 0)
+               ipv6_dns1 = NULL;
+       if (g_strcmp0(ipv6_dns2, "::") == 0)
+               ipv6_dns2 = NULL;
+
+       connman_network_set_bool(network->network, "DefaultInternet",
+                                                               default_internet);
+
+       service = connman_service_lookup_from_network(network->network);
+       if (service == NULL)
+               return FALSE;
+
+       if (connman_setting_get_bool("SingleConnectedTechnology") == TRUE) {
+               /* Wi-Fi technology is always a top priority */
+               if (active == TRUE &&
+                               connman_service_is_no_ref_user_pdn_connection(service) == TRUE &&
+                               connman_service_get_type(connman_service_get_default_connection())
+                                       == CONNMAN_SERVICE_TYPE_WIFI) {
+                       __request_network_deactivate(network->network);
+
+                       return FALSE;
+               }
+       }
+
+       /* interface index set */
+       if (dev_name != NULL) {
                index = connman_inet_ifindex(dev_name);
+               connman_network_set_index(network->network, index);
+               DBG("interface index %d", index);
+       }
+
+       /* proxy set */
+       if (active == TRUE &&
+                       connman_network_get_connected(network->network) == TRUE)
+               active_proxy = TRUE;
+
+       proxies = connman_service_get_proxy_servers(service);
+       if (proxies != NULL) {
+               if (proxy_addr == NULL)
+                       connman_service_set_proxy(service, proxy_addr, active_proxy);
+               else if (g_strcmp0(proxy_addr, proxies[0]) != 0)
+                       connman_service_set_proxy(service, proxy_addr, active_proxy);
+       } else if (proxy_addr != NULL)
+               connman_service_set_proxy(service, proxy_addr, active_proxy);
 
-       DBG("interface %s, index %d", dev_name, index);
-       connman_network_set_index(network->network, index);
+       if (proxies != NULL)
+               g_strfreev(proxies);
 
-       // proxy set
-       DBG("proxy (%s) is set", proxy_addr);
-       connman_network_set_proxy(network->network, proxy_addr);
+       __connman_service_nameserver_clear(service);
 
-       // ipv4 set
-       if (g_str_equal(ipv4_addr, "0.0.0.0")) {
+       /* ipv4 set */
+       if (network->ipv4_address == NULL)
+               network->ipv4_address =
+                               connman_ipaddress_alloc(CONNMAN_IPCONFIG_TYPE_IPV4);
+
+       if (network->ipv4_address == NULL)
+               return FALSE;
+
+       if (ipv4_addr == NULL)
                network->ipv4_method = CONNMAN_IPCONFIG_METHOD_OFF;
-       } else {
+       else
                network->ipv4_method = CONNMAN_IPCONFIG_METHOD_FIXED;
-               connman_ipaddress_set_ipv4(&network->ipv4_address, ipv4_addr, ipv4_netmask, ipv4_gw);
-               gchar *nameservers = g_strdup_printf("%s %s", ipv4_dns1, ipv4_dns2);
-               connman_network_set_nameservers(network->network, nameservers);
-       }
 
-       // ipv6 set
-       if (g_str_equal(ipv6_addr, "::")) {
+       ipv4_updated = connman_ipaddress_updated(network->ipv4_address,
+                                               ipv4_addr, ipv4_gw);
+       if (ipv4_updated == TRUE)
+               connman_ipaddress_set_ipv4(network->ipv4_address, ipv4_addr,
+                                               ipv4_netmask, ipv4_gw);
+
+       if (ipv4_dns1)
+               __connman_service_nameserver_append(service, ipv4_dns1, FALSE);
+       //if (ipv4_dns2)
+       if (ipv4_dns2 && !ipv4_dns1)
+               __connman_service_nameserver_append(service, ipv4_dns2, FALSE);
+
+       /* ipv6 set */
+       if (network->ipv6_address == NULL)
+               network->ipv6_address =
+                               connman_ipaddress_alloc(CONNMAN_IPCONFIG_TYPE_IPV6);
+
+       if (network->ipv6_address == NULL)
+               return FALSE;
+
+       if (ipv6_addr == NULL)
                network->ipv6_method = CONNMAN_IPCONFIG_METHOD_OFF;
-       } else {
+       else
                network->ipv6_method = CONNMAN_IPCONFIG_METHOD_FIXED;
-               unsigned char prefix_length = 64;
-               connman_ipaddress_set_ipv6(&network->ipv6_address, ipv6_addr, prefix_length, ipv6_gw);
-               gchar *nameservers = g_strdup_printf("%s %s", ipv6_dns1, ipv6_dns2);
-               connman_network_set_nameservers(network->network, nameservers);
+
+       ipv6_updated = connman_ipaddress_updated(network->ipv6_address,
+                                               ipv6_addr, ipv6_gw);
+       if (ipv6_updated == TRUE)
+               connman_ipaddress_set_ipv6(network->ipv6_address, ipv6_addr,
+                                               64, ipv6_gw);
+
+       if (ipv6_dns1)
+               __connman_service_nameserver_append(service, ipv6_dns1, FALSE);
+       //if (ipv6_dns2)
+       if (ipv6_dns2 && !ipv6_dns1)
+               __connman_service_nameserver_append(service, ipv6_dns2, FALSE);
+
+       if (active == TRUE &&
+                       connman_network_get_connected(network->network) == TRUE) {
+               if (ipv4_updated == TRUE || ipv6_updated == TRUE) {
+                       DBG("IPv4 updated %d, IPv6 updated %d", ipv4_updated, ipv6_updated);
+
+                       __set_network_connected(network, FALSE);
+               } else {
+                       DBG("Already connected");
+
+                       return active;
+               }
        }
 
-       if(active)
+       if (active == TRUE)
                connman_network_set_associating(network->network, TRUE);
 
        return active;
 }
 
-static void __set_network_connected(struct telephony_network *network, gboolean connected)
+static int __add_context(struct connman_device *device, const char *path,
+                               DBusMessageIter *prop)
 {
-       gboolean setip = FALSE;
+       char *ident;
+       connman_bool_t active = FALSE;
 
-       DBG("network %p connected %d", network, connected);
+       struct telephony_modem *modem = connman_device_get_data(device);
+       struct connman_network *network;
+       struct telephony_network *info;
 
-       switch (network->ipv4_method) {
-       case CONNMAN_IPCONFIG_METHOD_UNKNOWN:
-       case CONNMAN_IPCONFIG_METHOD_OFF:
-       case CONNMAN_IPCONFIG_METHOD_MANUAL:
-       case CONNMAN_IPCONFIG_METHOD_AUTO:
-               setip = TRUE;
-               break;
+       DBG("modem %p device %p path %s", modem, device, path);
 
-       case CONNMAN_IPCONFIG_METHOD_FIXED:
-               connman_network_set_ipv4_method(network->network,network->ipv4_method);
-               connman_network_set_ipaddress(network->network, &network->ipv4_address);
-               setip = TRUE;
-               break;
+       ident = __get_ident(path);
 
-       case CONNMAN_IPCONFIG_METHOD_DHCP:
-               connman_network_set_ipv4_method(network->network, network->ipv4_method);
-               setip = TRUE;
-               break;
+       network = connman_device_get_network(device, ident);
+       if (network != NULL)
+               return -EALREADY;
+
+       info = g_hash_table_lookup(network_hash, path);
+       if (info != NULL) {
+               DBG("path %p already exists with device %p", path,
+                       connman_network_get_device(info->network));
+
+               if (connman_network_get_device(info->network))
+                       return -EALREADY;
+
+               g_hash_table_remove(network_hash, path);
        }
 
-       switch (network->ipv6_method) {
-       case CONNMAN_IPCONFIG_METHOD_UNKNOWN:
-       case CONNMAN_IPCONFIG_METHOD_OFF:
-       case CONNMAN_IPCONFIG_METHOD_MANUAL:
-       case CONNMAN_IPCONFIG_METHOD_DHCP:
-       case CONNMAN_IPCONFIG_METHOD_AUTO:
-               DBG("ipv6 not supported");
-               break;;
+       network = connman_network_create(ident, CONNMAN_NETWORK_TYPE_CELLULAR);
+       if (network == NULL)
+               return -ENOMEM;
 
-       case CONNMAN_IPCONFIG_METHOD_FIXED:
-               connman_network_set_ipv6_method(network->network, network->ipv6_method);
-               connman_network_set_ipaddress(network->network, &network->ipv6_address);
-               setip = TRUE;
-               break;
+       info = g_try_new0(struct telephony_network, 1);
+       if (info == NULL) {
+               connman_network_unref(network);
+               return -ENOMEM;
        }
 
-       if (setip == TRUE)
-               connman_network_set_connected(network->network, connected);
+       info->path = g_strdup(path);
 
-       return;
-}
+       connman_ipaddress_clear(info->ipv4_address);
+       connman_ipaddress_clear(info->ipv6_address);
 
-static char *__get_ident(const char *path)
-{
-       char *pos;
+       info->network = network;
 
-       if (*path != '/')
-               return NULL;
+       connman_network_set_string(network, "Path", path);
+       connman_network_set_name(network, path);
 
-       pos = strrchr(path, '/');
-       if (pos == NULL)
-               return NULL;
+       connman_network_set_group(network, ident);
 
-       return pos + 1;
+       g_hash_table_insert(network_hash, g_strdup(path), info);
+
+       connman_network_set_available(network, TRUE);
+       connman_network_set_bool(network, "Roaming", modem->s_service->roaming);
+
+       if (connman_device_add_network(device, network) != 0) {
+               g_hash_table_remove(network_hash, path);
+               return -EIO;
+       }
+
+       active = __set_network_context(info, prop);
+
+       if (active == TRUE && (connman_network_get_associating(network) == TRUE ||
+                       connman_network_get_connecting(network) == TRUE))
+               __set_network_connected(info, active);
+
+       return 0;
 }
 
-static gboolean __changed_modem(DBusConnection *connection, DBusMessage *message, void *user_data)
+static gboolean __changed_modem(DBusConnection *connection,
+                               DBusMessage *message, void *user_data)
 {
-       DBG("modem changed signal");
-
+       gboolean old_powered;
        DBusMessageIter args, dict;
-       const char *path = dbus_message_get_path(message);
        struct telephony_modem *modem;
+       const char *path = dbus_message_get_path(message);
 
-       DBG("modem path %s", path);
+       DBG("modem changed signal %s", path);
 
        modem = g_hash_table_lookup(modem_hash, path);
        if (modem == NULL) {
@@ -1109,6 +1272,8 @@ static gboolean __changed_modem(DBusConnection *connection, DBusMessage *message
                return TRUE;
        }
 
+       old_powered = modem->powered;
+
        DBG("message signature (%s)", dbus_message_get_signature(message));
 
        if (dbus_message_iter_init(message, &args) == FALSE) {
@@ -1120,7 +1285,7 @@ static gboolean __changed_modem(DBusConnection *connection, DBusMessage *message
 
        while (dbus_message_iter_get_arg_type(&dict) != DBUS_TYPE_INVALID) {
                DBusMessageIter entry;
-               const charkey, *tmp;
+               const char *key, *tmp;
 
                dbus_message_iter_recurse(&dict, &entry);
                dbus_message_iter_get_basic(&entry, &key);
@@ -1150,34 +1315,33 @@ static gboolean __changed_modem(DBusConnection *connection, DBusMessage *message
        if (modem->device == NULL)
                __add_connman_device(path, modem->operator);
 
-       __set_device_powered(modem, modem->powered);
+       if (old_powered != modem->powered)
+               __set_device_powered(modem, modem->powered);
 
        if (modem->powered != TRUE) {
                DBG("modem is not powered");
                return TRUE;
        }
 
-       if(!modem->s_service){
+       if (modem->s_service == NULL) {
                __request_get_services(modem->path);
                return TRUE;
        }
 
-       if(modem->flight_mode || !modem->data_allowed){
-               DBG("modem(%s) flight mode(%d) data allowed(%d)", modem->path, modem->flight_mode, modem->data_allowed);
-               return TRUE;
-       }
+       DBG("modem(%s) flight mode(%d) data allowed(%d)",
+                       modem->path, modem->flight_mode, modem->data_allowed);
 
        return TRUE;
 }
 
-static gboolean __added_modem(DBusConnection *connection, DBusMessage *message, void *user_data)
+static gboolean __added_modem(DBusConnection *connection,
+                               DBusMessage *message, void *user_data)
 {
-       DBG("modem added signal");
-
        const char *modem_path = NULL;
        DBusMessageIter args, dict, tmp;
 
-       DBG("message signature (%s)", dbus_message_get_signature(message));
+       DBG("modem added signal (%s)", dbus_message_get_signature(message));
+
        if (dbus_message_iter_init(message, &args) == FALSE) {
                DBG("error to read message");
                return TRUE;
@@ -1198,27 +1362,26 @@ static gboolean __added_modem(DBusConnection *connection, DBusMessage *message,
 
                DBG("key (%s) value(%s)", key, value);
 
-               if (g_str_equal(key, "path") == TRUE) {
+               if (g_str_equal(key, "path") == TRUE)
                        modem_path = g_strdup(value);
-               }
 
                dbus_message_iter_next(&tmp);
        }
 
-       if (modem_path != NULL) {
+       if (modem_path != NULL)
                __add_modem(modem_path, &dict);
-       }
 
        return TRUE;
 }
 
-static gboolean __removed_modem(DBusConnection *connection, DBusMessage *message, void *user_data)
+static gboolean __removed_modem(DBusConnection *connection,
+                                       DBusMessage *message, void *user_data)
 {
-       DBG("modem removed signal");
-
        DBusMessageIter iter;
        const char *modem_path;
 
+       DBG("modem removed signal");
+
        if (dbus_message_iter_init(message, &iter) == FALSE) {
                DBG("error to read message");
                return TRUE;
@@ -1230,17 +1393,16 @@ static gboolean __removed_modem(DBusConnection *connection, DBusMessage *message
        return TRUE;
 }
 
-static gboolean __changed_service(DBusConnection *connection, DBusMessage *message, void *user_data)
+static gboolean __changed_service(DBusConnection *connection,
+                                       DBusMessage *message, void *user_data)
 {
-       DBG("service changed signal");
-
        DBusMessageIter args, dict;
-       const char *service_path = dbus_message_get_path(message);
        struct telephony_modem *modem;
-       struct telephony_service *s_service;
        gboolean roaming_option = TRUE;
+       struct telephony_service *s_service;
+       const char *service_path = dbus_message_get_path(message);
 
-       DBG("service path %s", service_path);
+       DBG("service changed signal %s", service_path);
 
        s_service = g_hash_table_lookup(service_hash, service_path);
        if (s_service == NULL) {
@@ -1265,7 +1427,7 @@ static gboolean __changed_service(DBusConnection *connection, DBusMessage *messa
 
        while (dbus_message_iter_get_arg_type(&dict) != DBUS_TYPE_INVALID) {
                DBusMessageIter entry;
-               const charkey, *tmp;
+               const char *key, *tmp;
 
                dbus_message_iter_recurse(&dict, &entry);
                dbus_message_iter_get_basic(&entry, &key);
@@ -1286,20 +1448,21 @@ static gboolean __changed_service(DBusConnection *connection, DBusMessage *messa
                dbus_message_iter_next(&dict);
        }
 
-       roaming_option &= (!s_service->roaming && !modem->roaming_allowed) || modem->roaming_allowed;
+       roaming_option &= (!s_service->roaming && !modem->roaming_allowed)
+                               || modem->roaming_allowed;
 
        return TRUE;
 }
 
-
-static gboolean __added_service(DBusConnection *connection, DBusMessage *message, void *user_data)
+static gboolean __added_service(DBusConnection *connection,
+                               DBusMessage *message, void *user_data)
 {
-       DBG("service added signal");
-
-       const char *path = dbus_message_get_path(message);
+       struct telephony_modem *modem;
        const char *service_path = NULL;
        DBusMessageIter args, dict, tmp;
-       struct telephony_modem *modem;
+       const char *path = dbus_message_get_path(message);
+
+       DBG("service added signal %s", path);
 
        modem = g_hash_table_lookup(modem_hash, path);
        if (modem == NULL || modem->device == NULL)
@@ -1326,27 +1489,26 @@ static gboolean __added_service(DBusConnection *connection, DBusMessage *message
 
                DBG("key (%s) value(%s)", key, value);
 
-               if (g_str_equal(key, "path") == TRUE) {
-                       service_path = g_strdup(value);
-               }
+               if (g_str_equal(key, "path") == TRUE)
+                       service_path = value;
 
                dbus_message_iter_next(&tmp);
        }
 
-       if (service_path != NULL) {
+       if (service_path != NULL)
                __add_service(modem, service_path, &dict);
-       }
 
        return TRUE;
 }
 
-static gboolean __removed_service(DBusConnection *connection, DBusMessage *message, void *user_data)
+static gboolean __removed_service(DBusConnection *connection,
+                                       DBusMessage *message, void *user_data)
 {
-       DBG("service removed signal");
-
        DBusMessageIter iter;
        const char *service_path;
 
+       DBG("service removed signal");
+
        if (dbus_message_iter_init(message, &iter) == FALSE) {
                DBG("error to read message");
                return TRUE;
@@ -1358,21 +1520,21 @@ static gboolean __removed_service(DBusConnection *connection, DBusMessage *messa
        return TRUE;
 }
 
-static gboolean __changed_context(DBusConnection *connection, DBusMessage *message, void *user_data)
+static gboolean __changed_context(DBusConnection *connection,
+                                       DBusMessage *message, void *user_data)
 {
-       DBG("network changed signal");
-
-       gboolean active = FALSE;
-       const char *path = dbus_message_get_path(message);
-       struct telephony_network *info;
+       connman_bool_t active = FALSE;
        DBusMessageIter args, dict;
+       struct telephony_network *info;
+       const char *path = dbus_message_get_path(message);
+
+       DBG("network changed signal %s", path);
 
-       DBG("path %s", path);
        info = g_hash_table_lookup(network_hash, path);
        if (info == NULL)
                return TRUE;
 
-       if (!__check_network_available(info->network)) {
+       if (__check_network_available(info->network) == FALSE) {
                g_hash_table_remove(network_hash, path);
                return TRUE;
        }
@@ -1384,33 +1546,35 @@ static gboolean __changed_context(DBusConnection *connection, DBusMessage *messa
 
        dbus_message_iter_recurse(&args, &dict);
 
-       active = __set_network_ipconfig(info, &dict);
+       active = __set_network_context(info, &dict);
 
-       if (active == FALSE)
-               __set_network_connected(info, active);
-       else if ( (connman_network_get_connecting(info->network) ||
-                       connman_network_get_associating(info->network)) )
-               __set_network_connected(info, active);
+       __set_network_connected(info, active);
+
+       if (active == FALSE &&
+                       connman_network_get_associating(info->network) == TRUE)
+               connman_network_set_error(info->network,
+                                       CONNMAN_NETWORK_ERROR_ASSOCIATE_FAIL);
 
        return TRUE;
 }
 
-static gboolean __added_context(DBusConnection *connection, DBusMessage *message, void *user_data)
+static gboolean __added_context(DBusConnection *connection,
+                                       DBusMessage *message, void *user_data)
 {
-       DBG("network added signal");
-
-       DBusMessageIter args, dict, tmp;
-       const char *path = dbus_message_get_path(message);
        const char *network_path = NULL;
-       struct telephony_service *service = NULL;
+       DBusMessageIter args, dict, tmp;
        struct telephony_modem *modem = NULL;
+       struct telephony_service *service = NULL;
+       const char *path = dbus_message_get_path(message);
+
+       DBG("network added signal %s", path);
 
        service = g_hash_table_lookup(service_hash, path);
        if (service == NULL || service->p_modem == NULL)
                return TRUE;
 
        modem = service->p_modem;
-       if(modem == NULL || modem->device == NULL)
+       if (modem == NULL || modem->device == NULL)
                return TRUE;
 
        DBG("message signature (%s)", dbus_message_get_signature(message));
@@ -1434,28 +1598,27 @@ static gboolean __added_context(DBusConnection *connection, DBusMessage *message
 
                DBG("key (%s) value(%s)", key, value);
 
-               if (g_str_equal(key, "path") == TRUE) {
+               if (g_str_equal(key, "path") == TRUE)
                        network_path = g_strdup(value);
-               }
 
                dbus_message_iter_next(&tmp);
        }
 
-       if (network_path != NULL) {
+       if (network_path != NULL)
                __add_context(modem->device, network_path, &dict);
-       }
 
        return TRUE;
 }
 
-static gboolean __removed_context(DBusConnection *connection, DBusMessage *message, void *user_data)
+static gboolean __removed_context(DBusConnection *connection,
+                                       DBusMessage *message, void *user_data)
 {
-       DBG("network removed signal");
-
        DBusMessageIter iter;
-       const char *path = dbus_message_get_path(message);
        const char *network_path = NULL;
        struct telephony_service *service = NULL;
+       const char *path = dbus_message_get_path(message);
+
+       DBG("network removed signal %s", path);
 
        service = g_hash_table_lookup(service_hash, path);
        if (service == NULL || service->p_modem == NULL)
@@ -1472,7 +1635,7 @@ static gboolean __removed_context(DBusConnection *connection, DBusMessage *messa
        return TRUE;
 }
 
-// telephony initialization
+/* telephony initialization */
 static guint watch;
 static guint modem_watch;
 static guint modem_added_watch;
@@ -1486,46 +1649,74 @@ static guint context_removed_watch;
 
 static int telephony_init(void)
 {
-       DBG("telephony plugin");
        int err;
 
+       DBG("telephony plugin");
+
        connection = connman_dbus_get_connection();
        if (connection == NULL)
                return -EIO;
 
-       // telephony watch
-       watch = g_dbus_add_service_watch(connection, PS_DBUS_SERVICE,   telephony_connect, telephony_disconnect, NULL, NULL);
-
-       modem_watch = g_dbus_add_signal_watch(connection, NULL, NULL, PS_MODEM_INTERFACE,
-                                               PROPERTY_CHANGED, __changed_modem, NULL, NULL);
-
-       modem_added_watch = g_dbus_add_signal_watch(connection, NULL, NULL, PS_MASTER_INTERFACE,
-                                               MODEM_ADDED, __added_modem, NULL, NULL);
-
-       modem_removed_watch = g_dbus_add_signal_watch(connection, NULL, NULL, PS_MASTER_INTERFACE,
-                                               MODEM_REMOVED, __removed_modem, NULL, NULL);
-
-       service_watch = g_dbus_add_signal_watch(connection, NULL, NULL, PS_SERVICE_INTERFACE,
-                                               PROPERTY_CHANGED, __changed_service, NULL, NULL);
-
-       service_added_watch = g_dbus_add_signal_watch(connection, NULL, NULL, PS_MODEM_INTERFACE,
-                                               SERVICE_ADDED, __added_service, NULL, NULL);
-
-       service_removed_watch = g_dbus_add_signal_watch(connection, NULL, NULL, PS_MODEM_INTERFACE,
-                                               SERVICE_REMOVED, __removed_service, NULL, NULL);
-
-       context_watch = g_dbus_add_signal_watch(connection, NULL, NULL, PS_CONTEXT_INTERFACE,
-                                               PROPERTY_CHANGED, __changed_context, NULL, NULL);
-
-       context_added_watch = g_dbus_add_signal_watch(connection, NULL, NULL, PS_SERVICE_INTERFACE,
-                                               CONTEXT_ADDED, __added_context, NULL, NULL);
-
-       context_removed_watch = g_dbus_add_signal_watch(connection, NULL, NULL, PS_SERVICE_INTERFACE,
-                                               CONTEXT_REMOVED, __removed_context, NULL, NULL);
-
-       if (watch == 0 || modem_watch == 0 || modem_added_watch == 0 || modem_removed_watch == 0
-                       || service_watch == 0 || service_added_watch == 0 || service_removed_watch == 0
-                       || context_watch == 0 || context_added_watch == 0 || context_removed_watch == 0) {
+       /* telephony watch */
+       watch = g_dbus_add_service_watch(connection, PS_DBUS_SERVICE,
+                                       telephony_connect, telephony_disconnect,
+                                       NULL, NULL);
+
+       modem_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
+                                               PS_MODEM_INTERFACE,
+                                               PROPERTY_CHANGED,
+                                               __changed_modem, NULL, NULL);
+
+       modem_added_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
+                                               PS_MASTER_INTERFACE,
+                                               MODEM_ADDED, __added_modem,
+                                               NULL, NULL);
+
+       modem_removed_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
+                                               PS_MASTER_INTERFACE,
+                                               MODEM_REMOVED, __removed_modem,
+                                               NULL, NULL);
+
+       service_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
+                                               PS_SERVICE_INTERFACE,
+                                               PROPERTY_CHANGED,
+                                               __changed_service,
+                                               NULL, NULL);
+
+       service_added_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
+                                               PS_MODEM_INTERFACE,
+                                               SERVICE_ADDED, __added_service,
+                                               NULL, NULL);
+
+       service_removed_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
+                                               PS_MODEM_INTERFACE,
+                                               SERVICE_REMOVED,
+                                               __removed_service,
+                                               NULL, NULL);
+
+       context_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
+                                               PS_CONTEXT_INTERFACE,
+                                               PROPERTY_CHANGED,
+                                               __changed_context,
+                                               NULL, NULL);
+
+       context_added_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
+                                               PS_SERVICE_INTERFACE,
+                                               CONTEXT_ADDED, __added_context,
+                                               NULL, NULL);
+
+       context_removed_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
+                                               PS_SERVICE_INTERFACE,
+                                               CONTEXT_REMOVED,
+                                               __removed_context,
+                                               NULL, NULL);
+
+       if (watch == 0 || modem_watch == 0 || modem_added_watch == 0
+                       || modem_removed_watch == 0 || service_watch == 0
+                       || service_added_watch == 0 || context_watch == 0
+                       || service_removed_watch == 0
+                       || context_added_watch == 0
+                       || context_removed_watch == 0) {
                err = -EIO;
                goto remove;
        }
index 8a30d8f..4d3561e 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2011  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
index 9f49f32..165c325 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
@@ -37,7 +37,6 @@
 
 #include <dbus/dbus.h>
 
-#include <glib/ghash.h>
 #include <glib/gprintf.h>
 
 #include <connman/provider.h>
@@ -135,7 +134,12 @@ void vpn_died(struct connman_task *task, int exit_code, void *user_data)
 
        stop_vpn(provider);
        connman_provider_set_data(provider, NULL);
-       connman_rtnl_remove_watch(data->watch);
+
+       if (data->watch != 0) {
+               connman_provider_unref(provider);
+               connman_rtnl_remove_watch(data->watch);
+               data->watch = 0;
+       }
 
 vpn_exit:
        if (state != VPN_STATE_READY && state != VPN_STATE_DISCONNECT) {
@@ -158,10 +162,12 @@ vpn_exit:
                                                CONNMAN_PROVIDER_STATE_IDLE);
 
        connman_provider_set_index(provider, -1);
-       connman_provider_unref(data->provider);
 
-       g_free(data->if_name);
-       g_free(data);
+       if (data != NULL) {
+               connman_provider_unref(data->provider);
+               g_free(data->if_name);
+               g_free(data);
+       }
 
        connman_task_destroy(task);
 }
@@ -226,6 +232,7 @@ static DBusMessage *vpn_notify(struct connman_task *task,
        case VPN_STATE_CONNECT:
        case VPN_STATE_READY:
                index = connman_provider_get_index(provider);
+               connman_provider_ref(provider);
                data->watch = connman_rtnl_add_newlink_watch(index,
                                                     vpn_newlink, provider);
                connman_inet_ifup(index);
@@ -346,9 +353,12 @@ static int vpn_connect(struct connman_provider *provider)
 
        vpn_driver_data = g_hash_table_lookup(driver_hash, name);
 
-       if (vpn_driver_data != NULL && vpn_driver_data->vpn_driver != NULL &&
-               vpn_driver_data->vpn_driver->flags != VPN_FLAG_NO_TUN) {
+       if (vpn_driver_data == NULL || vpn_driver_data->vpn_driver == NULL) {
+               ret = -EINVAL;
+               goto exist_err;
+       }
 
+       if (vpn_driver_data->vpn_driver->flags != VPN_FLAG_NO_TUN) {
                ret = vpn_create_tun(provider);
                if (ret < 0)
                        goto exist_err;
@@ -421,10 +431,12 @@ static int vpn_disconnect(struct connman_provider *provider)
        if (vpn_driver_data->vpn_driver->disconnect)
                vpn_driver_data->vpn_driver->disconnect();
 
-       if (data->watch != 0)
+       if (data->watch != 0) {
+               connman_provider_unref(provider);
                connman_rtnl_remove_watch(data->watch);
+               data->watch = 0;
+       }
 
-       data->watch = 0;
        data->state = VPN_STATE_DISCONNECT;
        connman_task_stop(data->task);
 
@@ -439,9 +451,12 @@ static int vpn_remove(struct connman_provider *provider)
        if (data == NULL)
                return 0;
 
-       if (data->watch != 0)
+       if (data->watch != 0) {
+               connman_provider_unref(provider);
                connman_rtnl_remove_watch(data->watch);
-       data->watch = 0;
+               data->watch = 0;
+       }
+
        connman_task_stop(data->task);
 
        g_usleep(G_USEC_PER_SEC);
@@ -484,13 +499,18 @@ int vpn_register(const char *name, struct vpn_driver *vpn_driver,
        data->provider_driver.remove = vpn_remove;
        data->provider_driver.save = vpn_save;
 
-       if (driver_hash == NULL) {
+       if (driver_hash == NULL)
                driver_hash = g_hash_table_new_full(g_str_hash,
                                                        g_str_equal,
                                                        NULL, g_free);
+
+       if (driver_hash == NULL) {
+               connman_error("driver_hash not initialized for %s", name);
+               g_free(data);
+               return -ENOMEM;
        }
 
-       g_hash_table_insert(driver_hash, (char *)name, data);
+       g_hash_table_replace(driver_hash, (char *)name, data);
 
        connman_provider_driver_register(&data->provider_driver);
 
index cd707dc..fea1c15 100644 (file)
@@ -55,24 +55,29 @@ struct {
        const char *vpnc_opt;
        const char *vpnc_default;
        int type;
+       connman_bool_t cm_save;
 } vpnc_options[] = {
-       { "Host", "IPSec gateway", NULL, OPT_STRING },
-       { "VPNC.IPSec.ID", "IPSec ID", NULL, OPT_STRING },
-       { "VPNC.IPSec.Secret", "IPSec secret", NULL, OPT_STRING },
-       { "VPNC.Xauth.Username", "Xauth username", NULL, OPT_STRING },
-       { "VPNC.Xauth.Password", "Xauth password", NULL, OPT_STRING },
-       { "VPNC.IKE.Authmode", "IKE Authmode", NULL, OPT_STRING },
-       { "VPNC.IKE.DHGroup", "IKE DH Group", NULL, OPT_STRING },
-       { "VPNC.PFS", "Perfect Forward Secrecy", NULL, OPT_STRING },
-       { "VPNC.Domain", "Domain", NULL, OPT_STRING },
-       { "VPNC.Vendor", "Vendor", NULL, OPT_STRING },
-       { "VPNC.LocalPort", "Local Port", "0", OPT_STRING },
-       { "VPNC.CiscoPort","Cisco UDP Encapsulation Port", "0", OPT_STRING },
-       { "VPNC.AppVersion", "Application Version", NULL, OPT_STRING },
-       { "VPNC.NATTMode", "NAT Traversal Mode", "cisco-udp", OPT_STRING },
-       { "VPNC.DPDTimeout", "DPD idle timeout (our side)", NULL, OPT_STRING },
-       { "VPNC.SingleDES", "Enable Single DES", NULL, OPT_BOOLEAN },
-       { "VPNC.NoEncryption", "Enable no encryption", NULL, OPT_BOOLEAN },
+       { "Host", "IPSec gateway", NULL, OPT_STRING, TRUE },
+       { "VPNC.IPSec.ID", "IPSec ID", NULL, OPT_STRING, TRUE },
+       { "VPNC.IPSec.Secret", "IPSec secret", NULL, OPT_STRING, FALSE },
+       { "VPNC.Xauth.Username", "Xauth username", NULL, OPT_STRING, FALSE },
+       { "VPNC.Xauth.Password", "Xauth password", NULL, OPT_STRING, FALSE },
+       { "VPNC.IKE.Authmode", "IKE Authmode", NULL, OPT_STRING, TRUE },
+       { "VPNC.IKE.DHGroup", "IKE DH Group", NULL, OPT_STRING, TRUE },
+       { "VPNC.PFS", "Perfect Forward Secrecy", NULL, OPT_STRING, TRUE },
+       { "VPNC.Domain", "Domain", NULL, OPT_STRING, TRUE },
+       { "VPNC.Vendor", "Vendor", NULL, OPT_STRING, TRUE },
+       { "VPNC.LocalPort", "Local Port", "0", OPT_STRING, TRUE, },
+       { "VPNC.CiscoPort","Cisco UDP Encapsulation Port", "0", OPT_STRING,
+         TRUE },
+       { "VPNC.AppVersion", "Application Version", NULL, OPT_STRING, TRUE },
+       { "VPNC.NATTMode", "NAT Traversal Mode", "cisco-udp", OPT_STRING,
+         TRUE },
+       { "VPNC.DPDTimeout", "DPD idle timeout (our side)", NULL, OPT_STRING,
+         TRUE },
+       { "VPNC.SingleDES", "Enable Single DES", NULL, OPT_BOOLEAN, TRUE },
+       { "VPNC.NoEncryption", "Enable no encryption", NULL, OPT_BOOLEAN,
+         TRUE },
 };
 
 static int vc_notify(DBusMessage *msg, struct connman_provider *provider)
@@ -231,11 +236,15 @@ static int vc_write_config_data(struct connman_provider *provider, int fd)
 
 static int vc_save(struct connman_provider *provider, GKeyFile *keyfile)
 {
-       char *option;
+       const char *option;
        int i;
 
        for (i = 0; i < (int)ARRAY_SIZE(vpnc_options); i++) {
                if (strncmp(vpnc_options[i].cm_opt, "VPNC.", 5) == 0) {
+
+                       if (vpnc_options[i].cm_save == FALSE)
+                               continue;
+
                        option = connman_provider_get_string(provider,
                                                        vpnc_options[i].cm_opt);
                        if (option == NULL)
index bd2f507..8eec2e1 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
 #include <connman/log.h>
 #include <connman/option.h>
 #include <connman/storage.h>
+#include <include/setting.h>
 
 #include <gsupplicant/gsupplicant.h>
 
 #define CLEANUP_TIMEOUT   8    /* in seconds */
 #define INACTIVE_TIMEOUT  12   /* in seconds */
+#define FAVORITE_MAXIMUM_RETRIES 2
+
+#define BGSCAN_DEFAULT "simple:30:-45:300"
+#define AUTOSCAN_DEFAULT "exponential:3:300"
+
+static struct connman_technology *wifi_technology = NULL;
+
+struct hidden_params {
+       char ssid[32];
+       unsigned int ssid_len;
+       char *identity;
+       char *passphrase;
 #if defined TIZEN_EXT
-/*
- * Jan, 9th 2012. TIZEN
- * Remove reconnect procedure after getting disassoc event
- */
-#define MAXIMUM_RETRIES   1
-#else
-#define MAXIMUM_RETRIES   4
+       GSupplicantScanParams *scan_params;
 #endif
+       gpointer user_data;
+};
 
-struct connman_technology *wifi_technology = NULL;
+/**
+ * Used for autoscan "emulation".
+ * Should be removed when wpa_s autoscan support will be by default.
+ */
+struct autoscan_params {
+       int base;
+       int limit;
+       int interval;
+       unsigned int timeout;
+};
 
 struct wifi_data {
        char *identifier;
@@ -84,17 +102,37 @@ struct wifi_data {
        unsigned flags;
        unsigned int watch;
        int retries;
+       struct hidden_params *hidden;
+       /**
+        * autoscan "emulation".
+        */
+       struct autoscan_params *autoscan;
+#if defined TIZEN_EXT
+       int assoc_retry_count;
+       struct connman_network *scan_pending_network;
+       struct connman_network *interface_disconnected_network;
+       guint timer_wifi_enable;
+       gboolean postpone_hidden;
+#endif
 };
 
 #if defined TIZEN_EXT
-struct callback_data {
-       struct wifi_data *wifi_inf_data;
-       void *data;
-};
+/**
+ * Scanning after very first auto_connect to show Wi-Fi lists up.
+ */
+#include "connman.h"
+
+#define TIZEN_ASSOC_RETRY_COUNT                3
+
+static gboolean wifi_first_scan = FALSE;
+static gboolean found_with_first_scan = FALSE;
+static gboolean is_wifi_notifier_registered = FALSE;
 #endif
 
 static GList *iface_list = NULL;
 
+static void start_autoscan(struct connman_device *device);
+
 static void handle_tethering(struct wifi_data *wifi)
 {
        if (wifi->tethering == FALSE)
@@ -119,11 +157,11 @@ static void wifi_newlink(unsigned flags, unsigned change, void *user_data)
        struct connman_device *device = user_data;
        struct wifi_data *wifi = connman_device_get_data(device);
 
-       DBG("index %d flags %d change %d", wifi->index, flags, change);
-
-       if (!change)
+       if (wifi == NULL)
                return;
 
+       DBG("index %d flags %d change %d", wifi->index, flags, change);
+
        if ((wifi->flags & IFF_UP) != (flags & IFF_UP)) {
                if (flags & IFF_UP)
                        DBG("interface up");
@@ -153,11 +191,6 @@ static int wifi_probe(struct connman_device *device)
        if (wifi == NULL)
                return -ENOMEM;
 
-       wifi->connected = FALSE;
-       wifi->disconnecting = FALSE;
-       wifi->tethering = FALSE;
-       wifi->bridged = FALSE;
-       wifi->bridge = NULL;
        wifi->state = G_SUPPLICANT_STATE_INACTIVE;
 
        connman_device_set_data(device, wifi);
@@ -174,44 +207,558 @@ static int wifi_probe(struct connman_device *device)
        return 0;
 }
 
-static void remove_networks(struct connman_device *device,
-                               struct wifi_data *wifi)
+static void remove_networks(struct connman_device *device,
+                               struct wifi_data *wifi)
+{
+       GSList *list;
+
+       for (list = wifi->networks; list != NULL; list = list->next) {
+               struct connman_network *network = list->data;
+
+               connman_device_remove_network(device, network);
+               connman_network_unref(network);
+       }
+
+       g_slist_free(wifi->networks);
+       wifi->networks = NULL;
+}
+
+static void reset_autoscan(struct connman_device *device)
+{
+       struct wifi_data *wifi = connman_device_get_data(device);
+       struct autoscan_params *autoscan;
+
+       DBG("");
+
+       if (wifi == NULL || wifi->autoscan == NULL)
+               return;
+
+       autoscan = wifi->autoscan;
+
+       if (autoscan->timeout == 0 && autoscan->interval == 0)
+               return;
+
+       g_source_remove(autoscan->timeout);
+
+       autoscan->timeout = 0;
+       autoscan->interval = 0;
+
+       connman_device_unref(device);
+}
+
+static void stop_autoscan(struct connman_device *device)
+{
+       const struct wifi_data *wifi = connman_device_get_data(device);
+
+       if (wifi == NULL || wifi->autoscan == NULL)
+               return;
+
+       reset_autoscan(device);
+
+       connman_device_set_scanning(device, FALSE);
+}
+
+static void wifi_remove(struct connman_device *device)
+{
+       struct wifi_data *wifi = connman_device_get_data(device);
+
+       DBG("device %p wifi %p", device, wifi);
+
+       if (wifi == NULL)
+               return;
+
+#if defined TIZEN_EXT
+       if (wifi->timer_wifi_enable > 0) {
+               g_source_remove(wifi->timer_wifi_enable);
+
+               wifi->timer_wifi_enable = 0;
+       }
+#endif
+
+       iface_list = g_list_remove(iface_list, wifi);
+
+       remove_networks(device, wifi);
+
+       connman_device_set_powered(device, FALSE);
+       connman_device_set_data(device, NULL);
+       connman_device_unref(wifi->device);
+       connman_rtnl_remove_watch(wifi->watch);
+
+       g_supplicant_interface_set_data(wifi->interface, NULL);
+
+       g_free(wifi->autoscan);
+       g_free(wifi->identifier);
+       g_free(wifi);
+}
+
+static int add_scan_param(gchar *hex_ssid, int freq,
+                       GSupplicantScanParams *scan_data,
+                       int driver_max_scan_ssids)
+{
+       unsigned int i;
+       struct scan_ssid *scan_ssid;
+
+       if (driver_max_scan_ssids > scan_data->num_ssids && hex_ssid != NULL) {
+               gchar *ssid;
+               unsigned int j = 0, hex;
+               size_t hex_ssid_len = strlen(hex_ssid);
+
+               ssid = g_try_malloc0(hex_ssid_len / 2);
+               if (ssid == NULL)
+                       return -ENOMEM;
+
+               for (i = 0; i < hex_ssid_len; i += 2) {
+                       sscanf(hex_ssid + i, "%02x", &hex);
+                       ssid[j++] = hex;
+               }
+
+               scan_ssid = g_try_new(struct scan_ssid, 1);
+               if (scan_ssid == NULL) {
+                       g_free(ssid);
+                       return -ENOMEM;
+               }
+
+               memcpy(scan_ssid->ssid, ssid, j);
+               scan_ssid->ssid_len = j;
+               scan_data->ssids = g_slist_prepend(scan_data->ssids,
+                                                               scan_ssid);
+
+               scan_data->num_ssids++;
+
+               g_free(ssid);
+       } else
+               return -EINVAL;
+
+       scan_data->ssids = g_slist_reverse(scan_data->ssids);
+
+       if (scan_data->freqs == NULL) {
+               scan_data->freqs = g_try_malloc0(sizeof(uint16_t) *
+                                               scan_data->num_ssids);
+               if (scan_data->freqs == NULL) {
+                       g_slist_free_full(scan_data->ssids, g_free);
+                       return -ENOMEM;
+               }
+       } else {
+               scan_data->freqs = g_try_realloc(scan_data->freqs,
+                               sizeof(uint16_t) * scan_data->num_ssids);
+               if (scan_data->freqs == NULL) {
+                       g_slist_free_full(scan_data->ssids, g_free);
+                       return -ENOMEM;
+               }
+               scan_data->freqs[scan_data->num_ssids - 1] = 0;
+       }
+
+       /* Don't add duplicate entries */
+       for (i = 0; i < scan_data->num_ssids; i++) {
+               if (scan_data->freqs[i] == 0) {
+                       scan_data->freqs[i] = freq;
+                       break;
+               } else if (scan_data->freqs[i] == freq)
+                       break;
+       }
+
+       return 0;
+}
+
+static int get_hidden_connections(int max_ssids,
+                               GSupplicantScanParams *scan_data)
+{
+       GKeyFile *keyfile;
+       gchar **services;
+       char *ssid;
+       gchar *str;
+       int i, freq;
+       gboolean value;
+       int num_ssids = 0, add_param_failed = 0;
+
+       services = connman_storage_get_services();
+       for (i = 0; services && services[i]; i++) {
+               if (strncmp(services[i], "wifi_", 5) != 0)
+                       continue;
+
+               keyfile = connman_storage_load_service(services[i]);
+               if (keyfile == NULL)
+                       continue;
+
+               value = g_key_file_get_boolean(keyfile,
+                                       services[i], "Hidden", NULL);
+               if (value == FALSE) {
+                       g_key_file_free(keyfile);
+                       continue;
+               }
+
+               value = g_key_file_get_boolean(keyfile,
+                                       services[i], "Favorite", NULL);
+               if (value == FALSE) {
+                       g_key_file_free(keyfile);
+                       continue;
+               }
+
+               value = g_key_file_get_boolean(keyfile,
+                                       services[i], "AutoConnect", NULL);
+               if (value == FALSE) {
+                       g_key_file_free(keyfile);
+                       continue;
+               }
+
+               ssid = g_key_file_get_string(keyfile,
+                                       services[i], "SSID", NULL);
+
+               freq = g_key_file_get_integer(keyfile, services[i],
+                                       "Frequency", NULL);
+
+               if (add_scan_param(ssid, freq, scan_data, max_ssids) < 0) {
+                       str = g_key_file_get_string(keyfile,
+                                       services[i], "Name", NULL);
+                       DBG("Cannot scan %s (%s)", ssid, str);
+                       g_free(str);
+                       add_param_failed++;
+               }
+
+               num_ssids++;
+
+               g_key_file_free(keyfile);
+       }
+
+       if (add_param_failed > 0)
+               DBG("Unable to scan %d out of %d SSIDs (max is %d)",
+                       add_param_failed, num_ssids, max_ssids);
+
+       g_strfreev(services);
+
+       return num_ssids > max_ssids ? max_ssids : num_ssids;
+}
+
+static int throw_wifi_scan(struct connman_device *device,
+                       GSupplicantInterfaceCallback callback)
+{
+       struct wifi_data *wifi = connman_device_get_data(device);
+       int ret;
+
+       if (wifi == NULL)
+               return -ENODEV;
+
+       DBG("device %p %p", device, wifi->interface);
+
+       if (wifi->tethering == TRUE)
+               return -EBUSY;
+
+       if (connman_device_get_scanning(device) == TRUE)
+               return -EALREADY;
+
+       connman_device_ref(device);
+
+       ret = g_supplicant_interface_scan(wifi->interface, NULL,
+                                               callback, device);
+       if (ret == 0)
+               connman_device_set_scanning(device, TRUE);
+       else
+               connman_device_unref(device);
+
+       return ret;
+}
+
+static void hidden_free(struct hidden_params *hidden)
+{
+       if (hidden == NULL)
+               return;
+
+#if defined TIZEN_EXT
+       if (hidden->scan_params != NULL)
+               g_supplicant_free_scan_params(hidden->scan_params);
+#endif
+       g_free(hidden->identity);
+       g_free(hidden->passphrase);
+       g_free(hidden);
+}
+
+#if defined TIZEN_EXT
+static void service_state_changed(struct connman_service *service,
+                                       enum connman_service_state state);
+
+static int network_connect(struct connman_network *network);
+
+static struct connman_notifier notifier = {
+       .name                   = "wifi",
+       .priority               = CONNMAN_NOTIFIER_PRIORITY_DEFAULT,
+       .service_state_changed  = service_state_changed,
+};
+
+static void service_state_changed(struct connman_service *service,
+                                       enum connman_service_state state)
+{
+       enum connman_service_type type;
+
+       type = connman_service_get_type(service);
+       if (type != CONNMAN_SERVICE_TYPE_WIFI)
+               return;
+
+       DBG("service %p state %d", service, state);
+
+       switch (state) {
+       case CONNMAN_SERVICE_STATE_READY:
+       case CONNMAN_SERVICE_STATE_ONLINE:
+       case CONNMAN_SERVICE_STATE_FAILURE:
+               connman_notifier_unregister(&notifier);
+               is_wifi_notifier_registered = FALSE;
+
+               __connman_device_request_scan(type);
+               break;
+
+       default:
+               break;
+       }
+}
+#endif
+
+static void scan_callback(int result, GSupplicantInterface *interface,
+                                               void *user_data)
+{
+       struct connman_device *device = user_data;
+       struct wifi_data *wifi = connman_device_get_data(device);
+       connman_bool_t scanning;
+
+       DBG("result %d wifi %p", result, wifi);
+
+#if defined TIZEN_EXT
+       if (wifi != NULL && wifi->hidden != NULL && wifi->postpone_hidden != TRUE) {
+#else
+       if (wifi != NULL && wifi->hidden != NULL) {
+#endif
+               connman_network_clear_hidden(wifi->hidden->user_data);
+               hidden_free(wifi->hidden);
+               wifi->hidden = NULL;
+       }
+
+       if (result < 0)
+               connman_device_reset_scanning(device);
+
+#if defined TIZEN_EXT
+       /* User is connecting to a hidden AP, let's wait for finished event */
+       if (wifi != NULL && wifi->hidden != NULL && wifi->postpone_hidden == TRUE) {
+               GSupplicantScanParams *scan_params;
+               int ret;
+
+               wifi->postpone_hidden = FALSE;
+               scan_params = wifi->hidden->scan_params;
+               wifi->hidden->scan_params = NULL;
+
+               reset_autoscan(device);
+
+               ret = g_supplicant_interface_scan(wifi->interface, scan_params,
+                                                       scan_callback, device);
+               if (ret == 0)
+                       return;
+
+               /* On error, let's recall scan_callback, which will cleanup */
+               return scan_callback(ret, interface, user_data);
+       }
+#endif
+
+       scanning = connman_device_get_scanning(device);
+
+       if (scanning == TRUE)
+               connman_device_set_scanning(device, FALSE);
+
+       if (result != -ENOLINK)
+#if defined TIZEN_EXT
+       if (result != -EIO)
+#endif
+               start_autoscan(device);
+
+       /*
+        * If we are here then we were scanning; however, if we are
+        * also mid-flight disabling the interface, then wifi_disable
+        * has already cleared the device scanning state and
+        * unreferenced the device, obviating the need to do it here.
+        */
+
+       if (scanning == TRUE)
+               connman_device_unref(device);
+
+#if defined TIZEN_EXT
+       if (wifi && wifi->scan_pending_network) {
+               network_connect(wifi->scan_pending_network);
+               wifi->scan_pending_network = NULL;
+       }
+
+       if (is_wifi_notifier_registered != TRUE &&
+                       wifi_first_scan == TRUE && found_with_first_scan == TRUE) {
+               wifi_first_scan = FALSE;
+               found_with_first_scan = FALSE;
+
+               connman_notifier_register(&notifier);
+               is_wifi_notifier_registered = TRUE;
+       }
+#endif
+}
+
+static void scan_callback_hidden(int result,
+                       GSupplicantInterface *interface, void *user_data)
+{
+       struct connman_device *device = user_data;
+       struct wifi_data *wifi = connman_device_get_data(device);
+       int driver_max_ssids;
+
+       DBG("result %d wifi %p", result, wifi);
+
+       if (wifi == NULL)
+               goto out;
+
+#if defined TIZEN_EXT
+       /* User is trying to connect to a hidden AP */
+       if (wifi->hidden != NULL && wifi->postpone_hidden == TRUE)
+               goto out;
+#endif
+
+       /*
+        * Scan hidden networks so that we can autoconnect to them.
+        */
+       driver_max_ssids = g_supplicant_interface_get_max_scan_ssids(
+                                                       wifi->interface);
+       DBG("max ssids %d", driver_max_ssids);
+
+       if (driver_max_ssids > 0) {
+               GSupplicantScanParams *scan_params;
+               int ret;
+
+               scan_params = g_try_malloc0(sizeof(GSupplicantScanParams));
+               if (scan_params == NULL)
+                       goto out;
+
+               if (get_hidden_connections(driver_max_ssids,
+                                               scan_params) > 0) {
+                       ret = g_supplicant_interface_scan(wifi->interface,
+                                                       scan_params,
+                                                       scan_callback,
+                                                       device);
+                       if (ret == 0)
+                               return;
+               }
+
+               g_supplicant_free_scan_params(scan_params);
+       }
+
+out:
+       scan_callback(result, interface, user_data);
+}
+
+static gboolean autoscan_timeout(gpointer data)
+{
+       struct connman_device *device = data;
+       struct wifi_data *wifi = connman_device_get_data(device);
+       struct autoscan_params *autoscan;
+       int interval;
+
+       if (wifi == NULL)
+               return FALSE;
+
+       autoscan = wifi->autoscan;
+
+       if (autoscan->interval <= 0) {
+               interval = autoscan->base;
+               goto set_interval;
+       } else
+               interval = autoscan->interval * autoscan->base;
+
+       if (autoscan->interval >= autoscan->limit)
+               interval = autoscan->limit;
+
+       throw_wifi_scan(wifi->device, scan_callback_hidden);
+
+set_interval:
+       DBG("interval %d", interval);
+
+       autoscan->interval = interval;
+
+       autoscan->timeout = g_timeout_add_seconds(interval,
+                                               autoscan_timeout, device);
+
+       return FALSE;
+}
+
+static void start_autoscan(struct connman_device *device)
+{
+       struct wifi_data *wifi = connman_device_get_data(device);
+       struct autoscan_params *autoscan;
+
+       DBG("");
+
+       if (wifi == NULL)
+               return;
+
+       autoscan = wifi->autoscan;
+       if (autoscan == NULL)
+               return;
+
+       if (autoscan->timeout > 0 || autoscan->interval > 0)
+               return;
+
+       connman_device_ref(device);
+
+       autoscan_timeout(device);
+}
+
+static struct autoscan_params *parse_autoscan_params(const char *params)
 {
-       GSList *list;
+       struct autoscan_params *autoscan;
+       char **list_params;
+       int limit;
+       int base;
 
-       for (list = wifi->networks; list != NULL; list = list->next) {
-               struct connman_network *network = list->data;
+       DBG("Emulating autoscan");
 
-               connman_device_remove_network(device, network);
-               connman_network_unref(network);
+       list_params = g_strsplit(params, ":", 0);
+       if (list_params == 0)
+               return NULL;
+
+       if (g_strv_length(list_params) < 3) {
+               g_strfreev(list_params);
+               return NULL;
        }
 
-       g_slist_free(wifi->networks);
-       wifi->networks = NULL;
+       base = atoi(list_params[1]);
+       limit = atoi(list_params[2]);
+
+       g_strfreev(list_params);
+
+       autoscan = g_try_malloc0(sizeof(struct autoscan_params));
+       if (autoscan == NULL) {
+               DBG("Could not allocate memory for autoscan");
+               return NULL;
+       }
+
+       DBG("base %d - limit %d", base, limit);
+       autoscan->base = base;
+       autoscan->limit = limit;
+
+       return autoscan;
 }
 
-static void wifi_remove(struct connman_device *device)
+static void setup_autoscan(struct wifi_data *wifi)
 {
-       struct wifi_data *wifi = connman_device_get_data(device);
+       if (wifi->autoscan == NULL)
+               wifi->autoscan = parse_autoscan_params(AUTOSCAN_DEFAULT);
 
-       DBG("device %p wifi %p", device, wifi);
-
-       if (wifi == NULL)
-               return;
+       start_autoscan(wifi->device);
+}
 
-       iface_list = g_list_remove(iface_list, wifi);
+#if defined TIZEN_EXT
+static int wifi_enable(struct connman_device *device);
 
-       remove_networks(device, wifi);
+static gboolean throw_wifi_enable(gpointer user_data)
+{
+       struct wifi_data *wifi = user_data;
+       struct connman_device *device = wifi->device;
 
-       connman_device_set_data(device, NULL);
-       connman_device_unref(wifi->device);
-       connman_rtnl_remove_watch(wifi->watch);
+       if (device != NULL)
+               wifi_enable(device);
 
-       g_supplicant_interface_set_data(wifi->interface, NULL);
+       wifi->timer_wifi_enable = 0;
 
-       g_free(wifi->identifier);
-       g_free(wifi);
+       return FALSE;
 }
+#endif
 
 static void interface_create_callback(int result,
                                        GSupplicantInterface *interface,
@@ -223,14 +770,20 @@ static void interface_create_callback(int result,
                                g_supplicant_interface_get_ifname(interface),
                                wifi);
 
-       if (result < 0 || wifi == NULL)
+#if defined TIZEN_EXT
+       if (wifi == NULL)
                return;
 
-#if defined TIZEN_EXT
-       if (g_list_find(iface_list, wifi) == NULL) {
+       if (result < 0) {
+               wifi->timer_wifi_enable = g_timeout_add_seconds(1,
+                               throw_wifi_enable, wifi);
                return;
        }
+#else
+       if (result < 0 || wifi == NULL)
+               return;
 #endif
+
        wifi->interface = interface;
        g_supplicant_interface_set_data(interface, wifi);
 
@@ -245,25 +798,14 @@ static void interface_create_callback(int result,
        }
 
        connman_device_set_powered(wifi->device, TRUE);
-}
 
-static void interface_remove_callback(int result,
-                                       GSupplicantInterface *interface,
-                                                       void *user_data)
-{
-       struct wifi_data *wifi;
-
-       wifi = g_supplicant_interface_get_data(interface);
-
-       DBG("result %d wifi %p", result, wifi);
-
-       if (result < 0 || wifi == NULL)
+       if (connman_setting_get_bool("BackgroundScanning") == FALSE)
                return;
 
-       wifi->interface = NULL;
+       /* Setting up automatic scanning */
+       setup_autoscan(wifi);
 }
 
-
 static int wifi_enable(struct connman_device *device)
 {
        struct wifi_data *wifi = connman_device_get_data(device);
@@ -273,6 +815,9 @@ static int wifi_enable(struct connman_device *device)
 
        DBG("device %p %p", device, wifi);
 
+       if (wifi == NULL)
+               return -ENODEV;
+
        ret = g_supplicant_interface_create(interface, driver, NULL,
                                                interface_create_callback,
                                                        wifi);
@@ -287,7 +832,10 @@ static int wifi_disable(struct connman_device *device)
        struct wifi_data *wifi = connman_device_get_data(device);
        int ret;
 
-       DBG("device %p", device);
+       DBG("device %p wifi %p", device, wifi);
+
+       if (wifi == NULL)
+               return -ENODEV;
 
        wifi->connected = FALSE;
        wifi->disconnecting = FALSE;
@@ -295,7 +843,7 @@ static int wifi_disable(struct connman_device *device)
        if (wifi->pending_network != NULL)
                wifi->pending_network = NULL;
 
-       remove_networks(device, wifi);
+       stop_autoscan(device);
 
        /* In case of a user scan, device is still referenced */
        if (connman_device_get_scanning(device) == TRUE) {
@@ -303,66 +851,23 @@ static int wifi_disable(struct connman_device *device)
                connman_device_unref(wifi->device);
        }
 
-       ret = g_supplicant_interface_remove(wifi->interface,
-                                               interface_remove_callback,
-                                               NULL);
-       if (ret < 0)
-               return ret;
-
-       return -EINPROGRESS;
-}
-
-static void scan_callback(int result, GSupplicantInterface *interface,
-                                               void *user_data)
-{
-       struct connman_device *device = user_data;
-
-       DBG("result %d", result);
-
-       if (result < 0)
-               connman_device_reset_scanning(device);
-       else
-               connman_device_set_scanning(device, FALSE);
-       connman_device_unref(device);
-}
-
-static int add_scan_param(gchar *hex_ssid, int freq,
-                       GSupplicantScanParams *scan_data,
-                       int driver_max_scan_ssids)
-{
-       unsigned int i;
-
-       if (driver_max_scan_ssids > scan_data->num_ssids && hex_ssid != NULL) {
-               gchar *ssid;
-               unsigned int j = 0, hex;
-               size_t hex_ssid_len = strlen(hex_ssid);
-
-               ssid = g_try_malloc0(hex_ssid_len / 2);
-               if (ssid == NULL)
-                       return -ENOMEM;
-
-               for (i = 0; i < hex_ssid_len; i += 2) {
-                       sscanf(hex_ssid + i, "%02x", &hex);
-                       ssid[j++] = hex;
-               }
+       remove_networks(device, wifi);
 
-               memcpy(scan_data->ssids[scan_data->num_ssids].ssid, ssid, j);
-               scan_data->ssids[scan_data->num_ssids].ssid_len = j;
-               scan_data->num_ssids++;
+#if defined TIZEN_EXT
+       wifi->scan_pending_network = NULL;
+       wifi->interface_disconnected_network = NULL;
 
-               g_free(ssid);
+       if (is_wifi_notifier_registered == TRUE) {
+               connman_notifier_unregister(&notifier);
+               is_wifi_notifier_registered = FALSE;
        }
+#endif
 
-       /* Don't add duplicate entries */
-       for (i = 0; i < G_SUPPLICANT_MAX_FAST_SCAN; i++) {
-               if (scan_data->freqs[i] == 0) {
-                       scan_data->freqs[i] = freq;
-                       break;
-               } else if (scan_data->freqs[i] == freq)
-                       break;
-       }
+       ret = g_supplicant_interface_remove(wifi->interface, NULL, NULL);
+       if (ret < 0)
+               return ret;
 
-       return 0;
+       return -EINPROGRESS;
 }
 
 struct last_connected {
@@ -418,12 +923,13 @@ static int get_latest_connections(int max_ssids,
                        continue;
 
                keyfile = connman_storage_load_service(services[i]);
+               if (keyfile == NULL)
+                       continue;
 
                str = g_key_file_get_string(keyfile,
                                        services[i], "Favorite", NULL);
                if (str == NULL || g_strcmp0(str, "true")) {
-                       if (str)
-                               g_free(str);
+                       g_free(str);
                        g_key_file_free(keyfile);
                        continue;
                }
@@ -432,8 +938,7 @@ static int get_latest_connections(int max_ssids,
                str = g_key_file_get_string(keyfile,
                                        services[i], "AutoConnect", NULL);
                if (str == NULL || g_strcmp0(str, "true")) {
-                       if (str)
-                               g_free(str);
+                       g_free(str);
                        g_key_file_free(keyfile);
                        continue;
                }
@@ -441,10 +946,12 @@ static int get_latest_connections(int max_ssids,
 
                str = g_key_file_get_string(keyfile,
                                        services[i], "Modified", NULL);
-               if (str != NULL) {
-                       g_time_val_from_iso8601(str, &modified);
-                       g_free(str);
+               if (str == NULL) {
+                       g_key_file_free(keyfile);
+                       continue;
                }
+               g_time_val_from_iso8601(str, &modified);
+               g_free(str);
 
                ssid = g_key_file_get_string(keyfile,
                                        services[i], "SSID", NULL);
@@ -475,8 +982,7 @@ static int get_latest_connections(int max_ssids,
 
        g_strfreev(services);
 
-       num_ssids = num_ssids > G_SUPPLICANT_MAX_FAST_SCAN ?
-               G_SUPPLICANT_MAX_FAST_SCAN : num_ssids;
+       num_ssids = num_ssids > max_ssids ? max_ssids : num_ssids;
 
        iter = g_sequence_get_begin_iter(latest_list);
 
@@ -497,8 +1003,20 @@ static int get_latest_connections(int max_ssids,
 
 static int wifi_scan(struct connman_device *device)
 {
+       reset_autoscan(device);
+
+       return throw_wifi_scan(device, scan_callback_hidden);
+}
+
+static int wifi_scan_fast(struct connman_device *device)
+{
        struct wifi_data *wifi = connman_device_get_data(device);
+       GSupplicantScanParams *scan_params = NULL;
        int ret;
+       int driver_max_ssids = 0;
+
+       if (wifi == NULL)
+               return -ENODEV;
 
        DBG("device %p %p", device, wifi->interface);
 
@@ -508,56 +1026,155 @@ static int wifi_scan(struct connman_device *device)
        if (connman_device_get_scanning(device) == TRUE)
                return -EALREADY;
 
+       driver_max_ssids = g_supplicant_interface_get_max_scan_ssids(
+                                                       wifi->interface);
+       DBG("max ssids %d", driver_max_ssids);
+       if (driver_max_ssids == 0)
+               return wifi_scan(device);
+
+       scan_params = g_try_malloc0(sizeof(GSupplicantScanParams));
+       if (scan_params == NULL)
+               return -ENOMEM;
+
+       ret = get_latest_connections(driver_max_ssids, scan_params);
+       if (ret <= 0) {
+               g_supplicant_free_scan_params(scan_params);
+               return wifi_scan(device);
+       }
+
        connman_device_ref(device);
-       ret = g_supplicant_interface_scan(wifi->interface, NULL,
-                                       scan_callback, device);
+
+       reset_autoscan(device);
+
+       ret = g_supplicant_interface_scan(wifi->interface, scan_params,
+                                               scan_callback, device);
        if (ret == 0)
                connman_device_set_scanning(device, TRUE);
-       else
+       else {
+               g_supplicant_free_scan_params(scan_params);
                connman_device_unref(device);
+       }
+
+#if defined TIZEN_EXT
+       if (ret == 0)
+               wifi_first_scan = TRUE;
+#endif
 
        return ret;
 }
 
-static int wifi_scan_fast(struct connman_device *device)
+/*
+ * This func is only used when connecting to this specific AP first time.
+ * It is not used when system autoconnects to hidden AP.
+ */
+static int wifi_scan_hidden(struct connman_device *device,
+               const char *ssid, unsigned int ssid_len,
+               const char *identity, const char* passphrase,
+               gpointer user_data)
 {
        struct wifi_data *wifi = connman_device_get_data(device);
        GSupplicantScanParams *scan_params = NULL;
+       struct scan_ssid *scan_ssid;
+       struct hidden_params *hidden;
        int ret;
-       int driver_max_ssids = 0;
+#if defined TIZEN_EXT
+       connman_bool_t scanning;
+#endif
 
-       DBG("device %p %p", device, wifi->interface);
+       if (wifi == NULL)
+               return -ENODEV;
 
+#if !defined TIZEN_EXT
+       DBG("hidden SSID %s", ssid);
+#endif
+
+#if defined TIZEN_EXT
        if (wifi->tethering == TRUE)
                return 0;
+#else
+       if (wifi->tethering == TRUE || wifi->hidden != NULL)
+               return -EBUSY;
+#endif
 
+       if (ssid == NULL || ssid_len == 0 || ssid_len > 32)
+               return -EINVAL;
+
+#if defined TIZEN_EXT
+       scanning = connman_device_get_scanning(device);
+       if (scanning == TRUE && wifi->hidden != NULL && wifi->postpone_hidden == TRUE)
+               return -EALREADY;
+#else
        if (connman_device_get_scanning(device) == TRUE)
                return -EALREADY;
-
-       driver_max_ssids = g_supplicant_interface_get_max_scan_ssids(
-                                                       wifi->interface);
-       DBG("max ssids %d", driver_max_ssids);
-       if (driver_max_ssids == 0)
-               return wifi_scan(device);
+#endif
 
        scan_params = g_try_malloc0(sizeof(GSupplicantScanParams));
        if (scan_params == NULL)
                return -ENOMEM;
 
-       ret = get_latest_connections(driver_max_ssids, scan_params);
-       if (ret <= 0) {
+       scan_ssid = g_try_new(struct scan_ssid, 1);
+       if (scan_ssid == NULL) {
                g_free(scan_params);
-               return wifi_scan(device);
+               return -ENOMEM;
+       }
+
+       memcpy(scan_ssid->ssid, ssid, ssid_len);
+       scan_ssid->ssid_len = ssid_len;
+       scan_params->ssids = g_slist_prepend(scan_params->ssids, scan_ssid);
+
+       scan_params->num_ssids = 1;
+
+       hidden = g_try_new0(struct hidden_params, 1);
+       if (hidden == NULL) {
+#if defined TIZEN_EXT
+               g_supplicant_free_scan_params(scan_params);
+#else
+               g_free(scan_params);
+#endif
+               return -ENOMEM;
+       }
+#if defined TIZEN_EXT
+       if (wifi->hidden != NULL) {
+               hidden_free(wifi->hidden);
+               wifi->hidden = NULL;
        }
+#endif
+       memcpy(hidden->ssid, ssid, ssid_len);
+       hidden->ssid_len = ssid_len;
+       hidden->identity = g_strdup(identity);
+       hidden->passphrase = g_strdup(passphrase);
+       hidden->user_data = user_data;
+       wifi->hidden = hidden;
+
+#if defined TIZEN_EXT
+       if (scanning == TRUE) {
+               /* Let's keep this active scan for later,
+                * when current scan will be over. */
+               wifi->postpone_hidden = TRUE;
+               hidden->scan_params = scan_params;
+
+               return 0;
+       }
+#endif
 
        connman_device_ref(device);
+
+       reset_autoscan(device);
+
        ret = g_supplicant_interface_scan(wifi->interface, scan_params,
-                                               scan_callback, device);
+                       scan_callback, device);
        if (ret == 0)
                connman_device_set_scanning(device, TRUE);
        else {
-               g_free(scan_params);
+#if defined TIZEN_EXT
+               g_supplicant_free_scan_params(scan_params);
                connman_device_unref(device);
+#else
+               connman_device_unref(device);
+               g_supplicant_free_scan_params(scan_params);
+#endif
+               hidden_free(wifi->hidden);
+               wifi->hidden = NULL;
        }
 
        return ret;
@@ -573,6 +1190,7 @@ static struct connman_device_driver wifi_ng_driver = {
        .disable        = wifi_disable,
        .scan           = wifi_scan,
        .scan_fast      = wifi_scan_fast,
+       .scan_hidden    = wifi_scan_hidden,
 };
 
 static void system_ready(void)
@@ -612,48 +1230,45 @@ static void network_remove(struct connman_network *network)
                return;
 
        wifi->network = NULL;
+
+#if defined TIZEN_EXT
+       wifi->disconnecting = FALSE;
+
+       if (wifi->pending_network == network)
+               wifi->pending_network = NULL;
+
+       if (wifi->scan_pending_network == network)
+               wifi->scan_pending_network = NULL;
+
+       if (wifi->interface_disconnected_network == network)
+               wifi->interface_disconnected_network = NULL;
+#endif
 }
 
 static void connect_callback(int result, GSupplicantInterface *interface,
                                                        void *user_data)
 {
 #if defined TIZEN_EXT
-       struct callback_data *cb_data = user_data;
-       struct connman_network *network;
+       GList *list;
        struct wifi_data *wifi;
-
-       if (cb_data == NULL)
-               return;
-
-       wifi = cb_data->wifi_inf_data;
-
-       if (wifi == NULL) {
-               g_free(cb_data);
-               return;
-       }
-
-       if (g_list_find(iface_list, wifi) == NULL) {
-               g_free(cb_data);
-               return;
-       }
-
-       network = cb_data->data;
-#else
-       struct connman_network *network = user_data;
 #endif
+       struct connman_network *network = user_data;
 
        DBG("network %p result %d", network, result);
 
 #if defined TIZEN_EXT
-       if (wifi->networks == NULL) {
-               g_free(cb_data);
-               return;
-       }
+       for (list = iface_list; list; list = list->next) {
+               wifi = list->data;
 
-       if (g_slist_find(wifi->networks, network) == NULL) {
-               g_free(cb_data);
-               return;
+               if (wifi && wifi->network == network)
+                       goto found;
        }
+
+       /* wifi_data may be invalid because wifi is already disabled */
+       return;
+
+found:
+       wifi->interface_disconnected_network = NULL;
 #endif
        if (result == -ENOKEY) {
                connman_network_set_error(network,
@@ -662,9 +1277,8 @@ static void connect_callback(int result, GSupplicantInterface *interface,
                connman_network_set_error(network,
                                        CONNMAN_NETWORK_ERROR_CONFIGURE_FAIL);
        }
-#if defined TIZEN_EXT
-       g_free(cb_data);
-#endif
+
+       connman_network_unref(network);
 }
 
 static GSupplicantSecurity network_security(const char *security)
@@ -745,15 +1359,18 @@ static void ssid_init(GSupplicantSSID *ssid, struct connman_network *network)
        ssid->use_wps = connman_network_get_bool(network, "WiFi.UseWPS");
        ssid->pin_wps = connman_network_get_string(network, "WiFi.PinWPS");
 
+#if defined TIZEN_EXT
+       ssid->bssid = connman_network_get_bssid(network);
+#endif
+
+       if (connman_setting_get_bool("BackgroundScanning") == TRUE)
+               ssid->bgscan = BGSCAN_DEFAULT;
 }
 
 static int network_connect(struct connman_network *network)
 {
        struct connman_device *device = connman_network_get_device(network);
        struct wifi_data *wifi;
-#if defined TIZEN_EXT
-       struct callback_data *cb_data;
-#endif
        GSupplicantInterface *interface;
        GSupplicantSSID *ssid;
 
@@ -770,32 +1387,37 @@ static int network_connect(struct connman_network *network)
        if (ssid == NULL)
                return -ENOMEM;
 
+       interface = wifi->interface;
+
+       ssid_init(ssid, network);
+
 #if defined TIZEN_EXT
-       cb_data = g_try_malloc0(sizeof(struct callback_data));
-       if (cb_data == NULL) {
+       if (wifi->interface_disconnected_network == network) {
                g_free(ssid);
-               return -ENOMEM;
+               throw_wifi_scan(device, scan_callback);
+
+               if (wifi->disconnecting != TRUE) {
+                       wifi->scan_pending_network = network;
+                       wifi->interface_disconnected_network = NULL;
+               }
+
+               return -EINPROGRESS;
        }
 #endif
-       interface = wifi->interface;
-
-       ssid_init(ssid, network);
 
-       if (wifi->disconnecting == TRUE)
+       if (wifi->disconnecting == TRUE) {
                wifi->pending_network = network;
-       else {
-               wifi->network = network;
+               g_free(ssid);
+       } else {
+               wifi->network = connman_network_ref(network);
                wifi->retries = 0;
-
 #if defined TIZEN_EXT
-               cb_data->wifi_inf_data = wifi;
-               cb_data->data = network;
-               return g_supplicant_interface_connect(interface, ssid,
-                                               connect_callback, cb_data);
-#else
+               wifi->interface_disconnected_network = NULL;
+               wifi->scan_pending_network = NULL;
+#endif
+
                return g_supplicant_interface_connect(interface, ssid,
                                                connect_callback, network);
-#endif
        }
 
        return -EINPROGRESS;
@@ -805,32 +1427,41 @@ static void disconnect_callback(int result, GSupplicantInterface *interface,
                                                                void *user_data)
 {
 #if defined TIZEN_EXT
-       struct callback_data *cb_data = user_data;
+       GList *list;
        struct wifi_data *wifi;
+       struct connman_network *network = user_data;
 
-       DBG("");
+       DBG("network %p result %d", network, result);
 
-       if (cb_data == NULL)
-               return;
+       for (list = iface_list; list; list = list->next) {
+               wifi = list->data;
 
-       wifi = cb_data->wifi_inf_data;
+               if (wifi->network == NULL && wifi->disconnecting == TRUE)
+                       wifi->disconnecting = FALSE;
 
-       if (wifi == NULL) {
-               g_free(cb_data);
-               return;
+               if (wifi->network == network)
+                       goto found;
        }
 
-       if (g_list_find(iface_list, wifi) == NULL) {
-               g_free(cb_data);
-               return;
-       }
+       /* wifi_data may be invalid because wifi is already disabled */
+       return;
+
+found:
 #else
        struct wifi_data *wifi = user_data;
 #endif
 
+       DBG("result %d supplicant interface %p wifi %p",
+                       result, interface, wifi);
+
+       if (result == -ECONNABORTED) {
+               DBG("wifi interface no longer available");
+               return;
+       }
+
        if (wifi->network != NULL) {
                /*
-                * if result < 0 supplican return an error because
+                * if result < 0 supplicant return an error because
                 * the network is not current.
                 * we wont receive G_SUPPLICANT_STATE_DISCONNECTED since it
                 * failed, call connman_network_set_connected to report
@@ -849,19 +1480,17 @@ static void disconnect_callback(int result, GSupplicantInterface *interface,
                wifi->pending_network = NULL;
        }
 
-#if defined TIZEN_EXT
-       g_free(cb_data);
-#endif
+       start_autoscan(wifi->device);
 }
 
 static int network_disconnect(struct connman_network *network)
 {
        struct connman_device *device = connman_network_get_device(network);
-#if defined TIZEN_EXT
-       struct callback_data *cb_data;
-#endif
        struct wifi_data *wifi;
        int err;
+#if defined TIZEN_EXT
+       struct connman_service *service;
+#endif
 
        DBG("network %p", network);
 
@@ -869,6 +1498,31 @@ static int network_disconnect(struct connman_network *network)
        if (wifi == NULL || wifi->interface == NULL)
                return -ENODEV;
 
+#if defined TIZEN_EXT
+       if (connman_network_get_associating(network) == TRUE) {
+               connman_network_clear_associating(network);
+               connman_network_set_bool(network, "WiFi.UseWPS", FALSE);
+       } else {
+               service = connman_service_lookup_from_network(network);
+
+               if (service != NULL &&
+                       (__connman_service_is_connected_state(service,
+                                       CONNMAN_IPCONFIG_TYPE_IPV4) == FALSE &&
+                       __connman_service_is_connected_state(service,
+                                       CONNMAN_IPCONFIG_TYPE_IPV6) == FALSE) &&
+                       (connman_service_get_favorite(service) == FALSE))
+                                       __connman_service_set_passphrase(service, NULL);
+       }
+
+       if (wifi->pending_network == network)
+               wifi->pending_network = NULL;
+
+       if (wifi->scan_pending_network == network)
+               wifi->scan_pending_network = NULL;
+
+       if (wifi->interface_disconnected_network == network)
+               wifi->interface_disconnected_network = NULL;
+#endif
        connman_network_set_associating(network, FALSE);
 
        if (wifi->disconnecting == TRUE)
@@ -877,21 +1531,13 @@ static int network_disconnect(struct connman_network *network)
        wifi->disconnecting = TRUE;
 
 #if defined TIZEN_EXT
-       cb_data = g_try_malloc0(sizeof(struct callback_data));
-
-       if (cb_data == NULL) {
-               return -ENOMEM;
-       }
-
-       cb_data->wifi_inf_data = wifi;
-       cb_data->data = NULL;
-
        err = g_supplicant_interface_disconnect(wifi->interface,
-                                               disconnect_callback, cb_data);
+                                               disconnect_callback, network);
 #else
        err = g_supplicant_interface_disconnect(wifi->interface,
                                                disconnect_callback, wifi);
 #endif
+
        if (err < 0)
                wifi->disconnecting = FALSE;
 
@@ -944,6 +1590,7 @@ static connman_bool_t is_idle(struct wifi_data *wifi)
 
        switch (wifi->state) {
        case G_SUPPLICANT_STATE_UNKNOWN:
+       case G_SUPPLICANT_STATE_DISABLED:
        case G_SUPPLICANT_STATE_DISCONNECTED:
        case G_SUPPLICANT_STATE_INACTIVE:
        case G_SUPPLICANT_STATE_SCANNING:
@@ -973,6 +1620,7 @@ static connman_bool_t is_idle_wps(GSupplicantInterface *interface,
         * actually means that we are idling. */
        switch (wifi->state) {
        case G_SUPPLICANT_STATE_UNKNOWN:
+       case G_SUPPLICANT_STATE_DISABLED:
        case G_SUPPLICANT_STATE_DISCONNECTED:
        case G_SUPPLICANT_STATE_INACTIVE:
        case G_SUPPLICANT_STATE_SCANNING:
@@ -1013,8 +1661,13 @@ static connman_bool_t handle_wps_completion(GSupplicantInterface *interface,
                if (wps_ssid == NULL || wps_ssid_len != ssid_len ||
                                memcmp(ssid, wps_ssid, ssid_len) != 0) {
                        connman_network_set_associating(network, FALSE);
+#if defined TIZEN_EXT
+                       g_supplicant_interface_disconnect(wifi->interface,
+                                               disconnect_callback, wifi->network);
+#else
                        g_supplicant_interface_disconnect(wifi->interface,
                                                disconnect_callback, wifi);
+#endif
                        return FALSE;
                }
 
@@ -1032,25 +1685,88 @@ static connman_bool_t handle_4way_handshake_failure(GSupplicantInterface *interf
                                        struct connman_network *network,
                                        struct wifi_data *wifi)
 {
+#if defined TIZEN_EXT
+       const char *security;
+
+       security = connman_network_get_string(network, "WiFi.Security");
+
+       if (g_str_equal(security, "ieee8021x") == TRUE &&
+                       wifi->state == G_SUPPLICANT_STATE_ASSOCIATED) {
+               wifi->retries = 0;
+               connman_network_set_error(network, CONNMAN_NETWORK_ERROR_INVALID_KEY);
+
+               return FALSE;
+       }
+
        if (wifi->state != G_SUPPLICANT_STATE_4WAY_HANDSHAKE)
                return FALSE;
+#else
+       struct connman_service *service;
 
-       wifi->retries++;
+       if (wifi->state != G_SUPPLICANT_STATE_4WAY_HANDSHAKE)
+               return FALSE;
 
-       if (wifi->retries < MAXIMUM_RETRIES)
-               return TRUE;
+       service = connman_service_lookup_from_network(network);
+       if (service == NULL)
+               return FALSE;
+#endif
+
+#if !defined TIZEN_EXT
+       wifi->retries++;
 
-       /* We disable the selected network, if not then
-        * wpa_supplicant will loop retrying */
-       if (g_supplicant_interface_enable_selected_network(interface,
-                                                               FALSE) != 0)
-               DBG("Could not disables selected network");
+       if (connman_service_get_favorite(service) == TRUE) {
+               if (wifi->retries < FAVORITE_MAXIMUM_RETRIES)
+                       return TRUE;
+       }
+#endif
 
+       wifi->retries = 0;
        connman_network_set_error(network, CONNMAN_NETWORK_ERROR_INVALID_KEY);
 
        return FALSE;
 }
 
+#if defined TIZEN_EXT
+static connman_bool_t handle_wifi_assoc_retry(struct connman_network *network,
+                                       struct wifi_data *wifi)
+{
+       const char *security;
+
+       if (!wifi->network || wifi->connected || wifi->disconnecting ||
+                       connman_network_get_connecting(network) != TRUE) {
+               wifi->assoc_retry_count = 0;
+               return FALSE;
+       }
+
+       if (wifi->state != G_SUPPLICANT_STATE_ASSOCIATING &&
+                       wifi->state != G_SUPPLICANT_STATE_ASSOCIATED) {
+               wifi->assoc_retry_count = 0;
+               return FALSE;
+       }
+
+       security = connman_network_get_string(network, "WiFi.Security");
+       if (g_str_equal(security, "ieee8021x") == TRUE &&
+                       wifi->state == G_SUPPLICANT_STATE_ASSOCIATED) {
+               wifi->assoc_retry_count = 0;
+               return FALSE;
+       }
+
+       if (++wifi->assoc_retry_count >= TIZEN_ASSOC_RETRY_COUNT) {
+               wifi->assoc_retry_count = 0;
+
+               /* Honestly it's not an invalid-key error,
+                * however QA team recommends that the invalid-key error
+                * might be better to display for user experience.
+                */
+               connman_network_set_error(network, CONNMAN_NETWORK_ERROR_INVALID_KEY);
+
+               return FALSE;
+       }
+
+       return TRUE;
+}
+#endif
+
 static void interface_state(GSupplicantInterface *interface)
 {
        struct connman_network *network;
@@ -1078,29 +1794,32 @@ static void interface_state(GSupplicantInterface *interface)
 
        case G_SUPPLICANT_STATE_AUTHENTICATING:
        case G_SUPPLICANT_STATE_ASSOCIATING:
-#if !defined TIZEN_EXT
-               /*
-                * Dec. 2nd, 2011. TIZEN
-                *
-                * When associating state dbus signal is delivered with delay after disconnect
-                * connman's service state becomes associating state and connman ignores
-                * another state dbus signal since this time. Thus the associating state dbus signal
-                * should be ignored.
-                * It is reasonable because associating state is set also when connman try to connect.
-                */
-
-               connman_network_set_associating(network, TRUE);
+#if defined TIZEN_EXT
+               reset_autoscan(device);
+#else
+               stop_autoscan(device);
 #endif
+
+               if (wifi->connected == FALSE)
+                       connman_network_set_associating(network, TRUE);
+
                break;
 
        case G_SUPPLICANT_STATE_COMPLETED:
+#if defined TIZEN_EXT
+               /* though it should be already reset: */
+               reset_autoscan(device);
+
+               wifi->assoc_retry_count = 0;
+#else
+               /* though it should be already stopped: */
+               stop_autoscan(device);
+#endif
+
                if (handle_wps_completion(interface, network, device, wifi) ==
                                                                        FALSE)
                        break;
 
-               /* reset scan trigger and schedule background scan */
-               connman_device_schedule_scan(device);
-
                connman_network_set_connected(network, TRUE);
                break;
 
@@ -1113,8 +1832,16 @@ static void interface_state(GSupplicantInterface *interface)
                 */
                wps = connman_network_get_bool(network, "WiFi.UseWPS");
                if (wps == TRUE)
+#if defined TIZEN_EXT
+               {
+                       connman_network_set_error(network,
+                                               CONNMAN_NETWORK_ERROR_ASSOCIATE_FAIL);
+#endif
                        if (is_idle_wps(interface, wifi) == TRUE)
                                break;
+#if defined TIZEN_EXT
+               }
+#endif
 
                if (is_idle(wifi))
                        break;
@@ -1127,27 +1854,62 @@ static void interface_state(GSupplicantInterface *interface)
                                                network, wifi) == TRUE)
                        break;
 
-#if !defined TIZEN_EXT
-               /*
-                * Dec. 2nd, 2011. TIZEN
-                *
-                * There is not necessary to change associating variable to false because
-                * connman_network_set_connected sets it.
-                * Moreover, it cuases wrong operation when connman gets disconnect state dbus signal
-                * during connecting
-                *
-                */
+               /* We disable the selected network, if not then
+                * wpa_supplicant will loop retrying */
+               if (g_supplicant_interface_enable_selected_network(interface,
+                                               FALSE) != 0)
+                       DBG("Could not disables selected network");
 
-               connman_network_set_associating(network, FALSE);
+#if defined TIZEN_EXT
+               /* Some of Wi-Fi networks are not comply Wi-Fi specification.
+                * Retry association until its retry count is expired */
+               if (handle_wifi_assoc_retry(network, wifi) == TRUE) {
+                       throw_wifi_scan(wifi->device, scan_callback);
+                       wifi->scan_pending_network = wifi->network;
+                       break;
+               }
+
+               /* To avoid unnecessary repeated association in wpa_supplicant,
+                * "RemoveNetwork" should be made when Wi-Fi is disconnected */
+               if (wps != TRUE && wifi->network && wifi->disconnecting == FALSE) {
+                       int err;
+
+                       wifi->disconnecting = TRUE;
+                       err = g_supplicant_interface_disconnect(wifi->interface,
+                                                       disconnect_callback, wifi->network);
+                       if (err < 0)
+                               wifi->disconnecting = FALSE;
+
+                       if (wifi->connected)
+                               wifi->interface_disconnected_network = wifi->network;
+                       else
+                               wifi->interface_disconnected_network = NULL;
+
+                       connman_network_set_connected(network, FALSE);
+                       connman_network_set_associating(network, FALSE);
+
+                       start_autoscan(device);
+
+                       break;
+               }
 #endif
+
                connman_network_set_connected(network, FALSE);
+               connman_network_set_associating(network, FALSE);
+               wifi->disconnecting = FALSE;
+
+               start_autoscan(device);
+
                break;
 
        case G_SUPPLICANT_STATE_INACTIVE:
                connman_network_set_associating(network, FALSE);
+               start_autoscan(device);
+
                break;
 
        case G_SUPPLICANT_STATE_UNKNOWN:
+       case G_SUPPLICANT_STATE_DISABLED:
        case G_SUPPLICANT_STATE_ASSOCIATED:
        case G_SUPPLICANT_STATE_4WAY_HANDSHAKE:
        case G_SUPPLICANT_STATE_GROUP_HANDSHAKE:
@@ -1156,6 +1918,38 @@ static void interface_state(GSupplicantInterface *interface)
 
        wifi->state = state;
 
+       /* Saving wpa_s state policy:
+        * If connected and if the state changes are roaming related:
+        * --> We stay connected
+        * If completed
+        * --> We are connected
+        * All other case:
+        * --> We are not connected
+        * */
+       switch (state) {
+#if defined TIZEN_EXT
+       case G_SUPPLICANT_STATE_SCANNING:
+               break;
+#endif
+       case G_SUPPLICANT_STATE_AUTHENTICATING:
+       case G_SUPPLICANT_STATE_ASSOCIATING:
+       case G_SUPPLICANT_STATE_ASSOCIATED:
+       case G_SUPPLICANT_STATE_4WAY_HANDSHAKE:
+       case G_SUPPLICANT_STATE_GROUP_HANDSHAKE:
+               if (wifi->connected == TRUE)
+                       connman_warn("Probably roaming right now!"
+                                               " Staying connected...");
+               else
+                       wifi->connected = FALSE;
+               break;
+       case G_SUPPLICANT_STATE_COMPLETED:
+               wifi->connected = TRUE;
+               break;
+       default:
+               wifi->connected = FALSE;
+               break;
+       }
+
        DBG("DONE");
 }
 
@@ -1172,10 +1966,11 @@ static void interface_removed(GSupplicantInterface *interface)
                return;
 
        if (wifi == NULL || wifi->device == NULL) {
-               connman_error("Wrong wifi pointer");
+               DBG("wifi interface already removed");
                return;
        }
 
+       wifi->interface = NULL;
        connman_device_set_powered(wifi->device, FALSE);
 }
 
@@ -1186,7 +1981,19 @@ static void scan_started(GSupplicantInterface *interface)
 
 static void scan_finished(GSupplicantInterface *interface)
 {
+#if defined TIZEN_EXT
+       struct wifi_data *wifi;
+#endif
+
        DBG("");
+
+#if defined TIZEN_EXT
+       wifi = g_supplicant_interface_get_data(interface);
+       if (wifi && wifi->scan_pending_network) {
+               network_connect(wifi->scan_pending_network);
+               wifi->scan_pending_network = NULL;
+       }
+#endif
 }
 
 static unsigned char calculate_strength(GSupplicantNetwork *supplicant_network)
@@ -1209,6 +2016,9 @@ static void network_added(GSupplicantNetwork *supplicant_network)
        const unsigned char *ssid;
        unsigned int ssid_len;
        connman_bool_t wps;
+       connman_bool_t wps_pbc;
+       connman_bool_t wps_ready;
+       connman_bool_t wps_advertizing;
 
        DBG("");
 
@@ -1219,6 +2029,10 @@ static void network_added(GSupplicantNetwork *supplicant_network)
        security = g_supplicant_network_get_security(supplicant_network);
        group = g_supplicant_network_get_identifier(supplicant_network);
        wps = g_supplicant_network_get_wps(supplicant_network);
+       wps_pbc = g_supplicant_network_is_wps_pbc(supplicant_network);
+       wps_ready = g_supplicant_network_is_wps_active(supplicant_network);
+       wps_advertizing = g_supplicant_network_is_wps_advertizing(
+                                                       supplicant_network);
        mode = g_supplicant_network_get_mode(supplicant_network);
 
        if (wifi == NULL)
@@ -1241,7 +2055,7 @@ static void network_added(GSupplicantNetwork *supplicant_network)
                        return;
                }
 
-               wifi->networks = g_slist_append(wifi->networks, network);
+               wifi->networks = g_slist_prepend(wifi->networks, network);
        }
 
        if (name != NULL && name[0] != '\0')
@@ -1254,6 +2068,19 @@ static void network_added(GSupplicantNetwork *supplicant_network)
                                calculate_strength(supplicant_network));
        connman_network_set_bool(network, "WiFi.WPS", wps);
 
+       if (wps == TRUE) {
+               /* Is AP advertizing for WPS association?
+                * If so, we decide to use WPS by default */
+               if (wps_ready == TRUE && wps_pbc == TRUE &&
+                                               wps_advertizing == TRUE) {
+#if !defined TIZEN_EXT
+                       connman_network_set_bool(network, "WiFi.UseWPS", TRUE);
+#else
+                       DBG("wps is activating by ap but ignore it.");
+#endif
+               }
+       }
+
        connman_network_set_frequency(network,
                        g_supplicant_network_get_frequency(supplicant_network));
 
@@ -1269,8 +2096,31 @@ static void network_added(GSupplicantNetwork *supplicant_network)
        connman_network_set_available(network, TRUE);
        connman_network_set_string(network, "WiFi.Mode", mode);
 
+#if defined TIZEN_EXT
+       if (group != NULL)
+#else
        if (ssid != NULL)
+#endif
                connman_network_set_group(network, group);
+
+#if defined TIZEN_EXT
+       if (wifi_first_scan == TRUE)
+               found_with_first_scan = TRUE;
+#endif
+
+       if (wifi->hidden != NULL && ssid != NULL) {
+               if (wifi->hidden->ssid_len == ssid_len &&
+                               memcmp(wifi->hidden->ssid, ssid,
+                                               ssid_len) == 0) {
+                       connman_network_connect_hidden(network,
+                                       wifi->hidden->identity,
+                                       wifi->hidden->passphrase,
+                                       wifi->hidden->user_data);
+                       wifi->hidden->user_data = NULL;
+                       hidden_free(wifi->hidden);
+                       wifi->hidden = NULL;
+               }
+       }
 }
 
 static void network_removed(GSupplicantNetwork *network)
@@ -1294,6 +2144,14 @@ static void network_removed(GSupplicantNetwork *network)
        if (connman_network == NULL)
                return;
 
+#if defined TIZEN_EXT
+       if (connman_network == wifi->scan_pending_network)
+               wifi->scan_pending_network = NULL;
+
+       if (connman_network == wifi->interface_disconnected_network)
+               wifi->interface_disconnected_network = NULL;
+#endif
+
        wifi->networks = g_slist_remove(wifi->networks, connman_network);
 
        connman_device_remove_network(wifi->device, connman_network);
@@ -1306,6 +2164,11 @@ static void network_changed(GSupplicantNetwork *network, const char *property)
        struct wifi_data *wifi;
        const char *name, *identifier;
        struct connman_network *connman_network;
+#if defined TIZEN_EXT
+       const unsigned char *bssid;
+       unsigned int maxrate;
+       connman_uint16_t frequency;
+#endif
 
        interface = g_supplicant_network_get_interface(network);
        wifi = g_supplicant_interface_get_data(interface);
@@ -1328,10 +2191,6 @@ static void network_changed(GSupplicantNetwork *network, const char *property)
        }
 
 #if defined TIZEN_EXT
-       const unsigned char *bssid;
-       unsigned int maxrate;
-       connman_uint16_t frequency;
-
        bssid = g_supplicant_network_get_bssid(network);
        maxrate = g_supplicant_network_get_maxrate(network);
        frequency = g_supplicant_network_get_frequency(network);
@@ -1342,6 +2201,90 @@ static void network_changed(GSupplicantNetwork *network, const char *property)
 #endif
 }
 
+#if defined TIZEN_EXT
+static void system_power_off(void)
+{
+       GList *list;
+       struct wifi_data *wifi;
+
+       if (connman_setting_get_bool("WiFiDHCPRelease") == TRUE) {
+               for (list = iface_list; list; list = list->next) {
+                       wifi = list->data;
+
+                       if (wifi->network != NULL)
+                               __connman_dhcp_stop(wifi->network);
+               }
+       }
+}
+
+static void network_merged(GSupplicantNetwork *network)
+{
+       GSupplicantInterface *interface;
+       GSupplicantState state;
+       struct wifi_data *wifi;
+       const char *identifier;
+       struct connman_network *connman_network;
+       unsigned int ishs20AP = 0;
+       char *temp = NULL;
+
+       interface = g_supplicant_network_get_interface(network);
+       if (interface == NULL)
+               return;
+
+       state = g_supplicant_interface_get_state(interface);
+       if (state < G_SUPPLICANT_STATE_AUTHENTICATING)
+               return;
+
+       wifi = g_supplicant_interface_get_data(interface);
+       if (wifi == NULL)
+               return;
+
+       identifier = g_supplicant_network_get_identifier(network);
+
+       connman_network = connman_device_get_network(wifi->device, identifier);
+       if (connman_network == NULL)
+               return;
+
+       DBG("merged identifier %s", identifier);
+
+       if (wifi->connected == FALSE) {
+               switch (state) {
+               case G_SUPPLICANT_STATE_AUTHENTICATING:
+               case G_SUPPLICANT_STATE_ASSOCIATING:
+               case G_SUPPLICANT_STATE_ASSOCIATED:
+               case G_SUPPLICANT_STATE_4WAY_HANDSHAKE:
+               case G_SUPPLICANT_STATE_GROUP_HANDSHAKE:
+                       connman_network_set_associating(connman_network, TRUE);
+                       break;
+               case G_SUPPLICANT_STATE_COMPLETED:
+                       connman_network_set_connected(connman_network, TRUE);
+                       break;
+               default:
+                       DBG("Not handled the state : %d", state);
+                       break;
+               }
+       }
+
+       ishs20AP = g_supplicant_network_is_hs20AP(network);
+       connman_network_set_is_hs20AP(connman_network, ishs20AP);
+
+       if (ishs20AP &&
+               g_strcmp0(g_supplicant_network_get_security(network), "ieee8021x") == 0) {
+               temp = g_ascii_strdown(g_supplicant_network_get_eap(network), -1);
+               connman_network_set_string(connman_network, "WiFi.EAP",
+                               temp);
+               connman_network_set_string(connman_network, "WiFi.Identity",
+                               g_supplicant_network_get_identity(network));
+               connman_network_set_string(connman_network, "WiFi.Phase2",
+                               g_supplicant_network_get_phase2(network));
+
+               g_free(temp);
+       }
+
+       wifi->network = connman_network;
+}
+#endif
+
 static void debug(const char *str)
 {
        if (getenv("CONNMAN_SUPPLICANT_DEBUG"))
@@ -1359,6 +2302,10 @@ static const GSupplicantCallbacks callbacks = {
        .network_added          = network_added,
        .network_removed        = network_removed,
        .network_changed        = network_changed,
+#if defined TIZEN_EXT
+       .system_power_off       = system_power_off,
+       .network_merged = network_merged,
+#endif
        .debug                  = debug,
 };
 
@@ -1443,6 +2390,7 @@ static void ap_create_callback(int result,
                connman_technology_tethering_notify(info->technology, FALSE);
 
                g_free(info->ifname);
+               g_free(info->ssid);
                g_free(info);
                return;
        }
@@ -1470,6 +2418,7 @@ static void sta_remove_callback(int result,
                info->wifi->tethering = TRUE;
 
                g_free(info->ifname);
+               g_free(info->ssid);
                g_free(info);
                return;
        }
@@ -1545,6 +2494,7 @@ static int tech_set_tethering(struct connman_technology *technology,
                }
                info->ifname = g_strdup(ifname);
                if (info->ifname == NULL) {
+                       g_free(info->ssid);
                        g_free(info);
                        continue;
                }
diff --git a/resources/etc/rc.d/init.d/connman b/resources/etc/rc.d/init.d/connman
deleted file mode 100755 (executable)
index b1b3b0a..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/bin/sh
-HARDWARE_MODEL=`grep Hardware /proc/cpuinfo | awk "{print \\$3}"`
-/bin/echo "Hardware Model=${HARDWARE_MODEL}"
-
-case $HARDWARE_MODEL in
-               "SLP_PQ")       /bin/echo "This is PQ"
-                       /usr/sbin/connmand -W nl80211 &
-               ;;
-               "U1SLP" | "U1HD")       /bin/echo "This is U1SLP"
-                       /usr/sbin/connmand -W wext &
-               ;;
-               "SLP7_C210")    /bin/echo "This is C210"
-                       /usr/sbin/connmand -W wext &
-               ;;
-               "SLP10_C210")
-                       /usr/sbin/connmand -W wext &
-               ;;
-               *)
-                       /usr/sbin/connmand -W nl80211 &
-               ;;
-esac
diff --git a/resources/usr/sbin/connman.service b/resources/usr/sbin/connman.service
new file mode 100755 (executable)
index 0000000..67570cc
--- /dev/null
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+### Pre-condition of net-config for TIZEN-ConnMan service
+# Auto-activate net-config before launching connmand
+/usr/bin/dbus-send --system --dest=net.netconfig / net.netconfig.auto.activate
+
+/usr/sbin/connmand
index a1e5581..34f70b8 100644 (file)
@@ -1,4 +1,4 @@
 [D-BUS Service]
 Name=net.connman
-Exec=/etc/rc.d/init.d/connman
+Exec=/usr/sbin/connman.service
 User=root
index 8a7505e..ba476b5 100644 (file)
@@ -6,13 +6,10 @@ OfflineMode=false
 Enable=false
 
 [Bluetooth]
-Enable=false
+Enable=true
 
 [Wired]
 Enable=true
 
-[3G]
+[Cellular]
 Enable=true
-
-[WiMAX]
-Enable=false
index 911551c..e77b8cc 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2011  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
index 509fc95..ef81c94 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
index 8d2c3ff..3d46384 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *  Copyright (C) 2010  BMW Car IT GmbH. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
index 0dfcbf7..14aab76 100644 (file)
@@ -26,6 +26,7 @@
 
 #include <errno.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
@@ -48,149 +49,16 @@ static int tunnel_pending;
 static char *tunnel_ip_address;
 static GWeb *web;
 static guint web_request_id;
+static unsigned int newlink_watch;
+static unsigned int newlink_flags;
+static int newlink_timeout_id;
 
 #define STATUS_URL "http://ipv6.connman.net/online/status.html"
 
-#define NLMSG_TAIL(nmsg) \
-       ((struct rtattr *) (((void *)(nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len)))
-
 #ifndef IP_DF
 #define IP_DF          0x4000          /* Flag: "Don't Fragment"       */
 #endif
 
-struct rtnl_handle {
-       int                     fd;
-       struct sockaddr_nl      local;
-       struct sockaddr_nl      peer;
-       __u32                   seq;
-       __u32                   dump;
-};
-
-static int addattr32(struct nlmsghdr *n, int maxlen, int type, __u32 data)
-{
-       int len = RTA_LENGTH(4);
-       struct rtattr *rta;
-       if (NLMSG_ALIGN(n->nlmsg_len) + len > (unsigned int)maxlen) {
-               DBG("Error! max allowed bound %d exceeded", maxlen);
-               return -1;
-       }
-       rta = NLMSG_TAIL(n);
-       rta->rta_type = type;
-       rta->rta_len = len;
-       memcpy(RTA_DATA(rta), &data, 4);
-       n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len;
-
-       return 0;
-}
-
-static int addattr_l(struct nlmsghdr *n, int maxlen, int type,
-                       const void *data, int alen)
-{
-       int len = RTA_LENGTH(alen);
-       struct rtattr *rta;
-
-       if (NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len) >
-                                       (unsigned int)maxlen) {
-               DBG("addattr_l message exceeded bound of %d", maxlen);
-               return -1;
-       }
-       rta = NLMSG_TAIL(n);
-       rta->rta_type = type;
-       rta->rta_len = len;
-       memcpy(RTA_DATA(rta), data, alen);
-       n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len);
-
-       return 0;
-}
-
-static void rtnl_close(struct rtnl_handle *rth)
-{
-       if (rth->fd >= 0) {
-               close(rth->fd);
-               rth->fd = -1;
-       }
-}
-
-static int rtnl_open(struct rtnl_handle *rth)
-{
-       socklen_t addr_len;
-       int sndbuf = 1024;
-
-       memset(rth, 0, sizeof(*rth));
-
-       rth->fd = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE);
-       if (rth->fd < 0) {
-               connman_error("Can not open netlink socket: %s",
-                                               strerror(errno));
-               return -1;
-       }
-
-       if (setsockopt(rth->fd, SOL_SOCKET, SO_SNDBUF, &sndbuf,
-                       sizeof(sndbuf)) < 0) {
-               connman_error("SO_SNDBUF: %s", strerror(errno));
-               return -1;
-       }
-
-       memset(&rth->local, 0, sizeof(rth->local));
-       rth->local.nl_family = AF_NETLINK;
-       rth->local.nl_groups = 0;
-
-       if (bind(rth->fd, (struct sockaddr *)&rth->local,
-                       sizeof(rth->local)) < 0) {
-               connman_error("Can not bind netlink socket: %s",
-                                                       strerror(errno));
-               return -1;
-       }
-       addr_len = sizeof(rth->local);
-       if (getsockname(rth->fd, (struct sockaddr *)&rth->local,
-                               &addr_len) < 0) {
-               connman_error("Can not getsockname: %s", strerror(errno));
-               return -1;
-       }
-       if (addr_len != sizeof(rth->local)) {
-               connman_error("Wrong address length %d", addr_len);
-               return -1;
-       }
-       if (rth->local.nl_family != AF_NETLINK) {
-               connman_error("Wrong address family %d", rth->local.nl_family);
-               return -1;
-       }
-       rth->seq = time(NULL);
-
-       return 0;
-}
-
-static int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n)
-{
-       struct sockaddr_nl nladdr;
-       struct iovec iov = {
-               .iov_base = (void *)n,
-               .iov_len = n->nlmsg_len
-       };
-       struct msghdr msg = {
-               .msg_name = &nladdr,
-               .msg_namelen = sizeof(nladdr),
-               .msg_iov = &iov,
-               .msg_iovlen = 1,
-       };
-       unsigned seq;
-       int err;
-
-       memset(&nladdr, 0, sizeof(nladdr));
-       nladdr.nl_family = AF_NETLINK;
-
-       n->nlmsg_seq = seq = ++rtnl->seq;
-       n->nlmsg_flags |= NLM_F_ACK;
-
-       err = sendmsg(rtnl->fd, &msg, 0);
-       if (err < 0) {
-               connman_error("Can not talk to rtnetlink");
-               return err;
-       }
-
-       return 0;
-}
-
 static int tunnel_create(struct in_addr *addr)
 {
        struct ip_tunnel_parm p;
@@ -209,21 +77,23 @@ static int tunnel_create(struct in_addr *addr)
        p.iph.protocol = IPPROTO_IPV6;
        p.iph.saddr = addr->s_addr;
        p.iph.ttl = 64;
-       strncpy(p.name, "tun6to4", IFNAMSIZ);
+       strncpy(p.name, "tun6to4", sizeof(p.name) - 1);
 
-       strncpy(ifr.ifr_name, "sit0", IFNAMSIZ);
+       strncpy(ifr.ifr_name, "sit0", sizeof(ifr.ifr_name) - 1);
        ifr.ifr_ifru.ifru_data = (void *)&p;
        fd = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+       if (fd < 0)
+               return -errno;
        ret = ioctl(fd, SIOCADDTUNNEL, &ifr);
        if (ret)
                connman_error("add tunnel %s failed: %s", ifr.ifr_name,
                                                        strerror(errno));
        close(fd);
 
-       return ret;
+       return -ret;
 }
 
-static void tunnel_destroy()
+static void tunnel_destroy(void)
 {
        struct ip_tunnel_parm p;
        struct ifreq ifr;
@@ -241,9 +111,9 @@ static void tunnel_destroy()
        p.iph.version = 4;
        p.iph.ihl = 5;
        p.iph.protocol = IPPROTO_IPV6;
-       strncpy(p.name, "tun6to4", IFNAMSIZ);
+       strncpy(p.name, "tun6to4", sizeof(p.name) - 1);
 
-       strncpy(ifr.ifr_name, "tun6to4", IFNAMSIZ);
+       strncpy(ifr.ifr_name, "tun6to4", sizeof(ifr.ifr_name) - 1);
        ifr.ifr_ifru.ifru_data = (void *)&p;
        fd = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
        if (fd < 0) {
@@ -265,19 +135,13 @@ static void tunnel_destroy()
        tunnel_ip_address = NULL;
 }
 
-static int tunnel_add_route()
+static int tunnel_add_route(void)
 {
-       struct rtnl_handle rth;
+       struct __connman_inet_rtnl_handle rth;
        struct in6_addr addr6;
        int index;
        int ret = 0;
 
-       struct {
-               struct nlmsghdr n;
-               struct rtmsg    r;
-               char            buf[1024];
-       } req;
-
        /* ip -6 route add ::/0 via ::192.88.99.1 dev tun6to4 metric 1 */
 
        index = if_nametoindex("tun6to4");
@@ -286,60 +150,57 @@ static int tunnel_add_route()
                return -1;
        }
 
-       memset(&req, 0, sizeof(req));
+       memset(&rth, 0, sizeof(rth));
 
-       req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
-       req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL;
-       req.n.nlmsg_type = RTM_NEWROUTE;
-       req.r.rtm_family = AF_INET6;
-       req.r.rtm_table = RT_TABLE_MAIN;
-       req.r.rtm_protocol = RTPROT_BOOT;
-       req.r.rtm_scope = RT_SCOPE_UNIVERSE;
-       req.r.rtm_type = RTN_UNICAST;
-       req.r.rtm_dst_len = 0;
+       rth.req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
+       rth.req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL;
+       rth.req.n.nlmsg_type = RTM_NEWROUTE;
+       rth.req.u.r.rt.rtm_family = AF_INET6;
+       rth.req.u.r.rt.rtm_table = RT_TABLE_MAIN;
+       rth.req.u.r.rt.rtm_protocol = RTPROT_BOOT;
+       rth.req.u.r.rt.rtm_scope = RT_SCOPE_UNIVERSE;
+       rth.req.u.r.rt.rtm_type = RTN_UNICAST;
+       rth.req.u.r.rt.rtm_dst_len = 0;
 
        inet_pton(AF_INET6, "::192.88.99.1", &addr6);
 
-       addattr_l(&req.n, sizeof(req), RTA_GATEWAY, &addr6.s6_addr, 16);
-       addattr32(&req.n, sizeof(req), RTA_OIF, index);
-       addattr32(&req.n, sizeof(req), RTA_PRIORITY, 1);
+       __connman_inet_rtnl_addattr_l(&rth.req.n, sizeof(rth.req), RTA_GATEWAY,
+                                       &addr6.s6_addr, 16);
+       __connman_inet_rtnl_addattr32(&rth.req.n, sizeof(rth.req), RTA_OIF,
+                                       index);
+       __connman_inet_rtnl_addattr32(&rth.req.n, sizeof(rth.req),
+                                       RTA_PRIORITY, 1);
 
-       ret = rtnl_open(&rth);
+       ret = __connman_inet_rtnl_open(&rth);
        if (ret < 0)
                goto done;
 
-       ret = rtnl_talk(&rth, &req.n);
+       ret = __connman_inet_rtnl_send(&rth, &rth.req.n);
 
 done:
-       rtnl_close(&rth);
+       __connman_inet_rtnl_close(&rth);
        return ret;
 }
 
 static int tunnel_set_addr(unsigned int a, unsigned int b,
                        unsigned int c, unsigned int d)
 {
-       struct rtnl_handle rth;
+       struct __connman_inet_rtnl_handle rth;
        struct in6_addr addr6;
        char *ip6addr;
        int ret;
 
-       struct {
-               struct nlmsghdr n;
-               struct ifaddrmsg ifa;
-               char buf[256];
-       } req;
-
        /* ip -6 addr add dev tun6to4 2002:0102:0304::1/64 */
 
-       memset(&req, 0, sizeof(req));
+       memset(&rth, 0, sizeof(rth));
 
-       req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
-       req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL;
-       req.n.nlmsg_type = RTM_NEWADDR;
-       req.ifa.ifa_family = AF_INET6;
-       req.ifa.ifa_prefixlen = 64;
-       req.ifa.ifa_index = if_nametoindex("tun6to4");
-       if (req.ifa.ifa_index == 0) {
+       rth.req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
+       rth.req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL;
+       rth.req.n.nlmsg_type = RTM_NEWADDR;
+       rth.req.u.i.ifa.ifa_family = AF_INET6;
+       rth.req.u.i.ifa.ifa_prefixlen = 64;
+       rth.req.u.i.ifa.ifa_index = if_nametoindex("tun6to4");
+       if (rth.req.u.i.ifa.ifa_index == 0) {
                connman_error("Can not find device tun6to4");
                ret = -1;
                goto done;
@@ -350,17 +211,19 @@ static int tunnel_set_addr(unsigned int a, unsigned int b,
        DBG("ipv6 address %s", ip6addr);
        g_free(ip6addr);
 
-       addattr_l(&req.n, sizeof(req), IFA_LOCAL, &addr6.s6_addr, 16);
-       addattr_l(&req.n, sizeof(req), IFA_ADDRESS, &addr6.s6_addr, 16);
+       __connman_inet_rtnl_addattr_l(&rth.req.n, sizeof(rth.req), IFA_LOCAL,
+                                       &addr6.s6_addr, 16);
+       __connman_inet_rtnl_addattr_l(&rth.req.n, sizeof(rth.req), IFA_ADDRESS,
+                                       &addr6.s6_addr, 16);
 
-       ret = rtnl_open(&rth);
+       ret = __connman_inet_rtnl_open(&rth);
        if (ret < 0)
                goto done;
 
-       ret = rtnl_talk(&rth, &req.n);
+       ret = __connman_inet_rtnl_send(&rth, &rth.req.n);
 
 done:
-       rtnl_close(&rth);
+       __connman_inet_rtnl_close(&rth);
        return ret;
 }
 
@@ -393,6 +256,76 @@ static gboolean web_result(GWebResult *result, gpointer user_data)
        return FALSE;
 }
 
+static void web_debug(const char *str, void *data)
+{
+       connman_info("%s: %s\n", (const char *) data, str);
+}
+
+static gboolean newlink_timeout(gpointer user_data)
+{
+       /*
+        * Stop if the timeout has been cancelled already by tun_newlink()
+        */
+       if (newlink_timeout_id == 0)
+               return FALSE;
+
+       DBG("");
+
+       if (newlink_watch != 0) {
+               connman_rtnl_remove_watch(newlink_watch);
+               newlink_watch = 0;
+       }
+
+       newlink_flags = 0;
+
+       if (web_request_id == 0)
+               tunnel_destroy();
+
+       newlink_timeout_id = 0;
+
+       return FALSE;
+}
+
+static void tun_newlink(unsigned flags, unsigned change, void *user_data)
+{
+       int index = GPOINTER_TO_INT(user_data);
+
+       if ((newlink_flags & IFF_UP) == (flags & IFF_UP)) {
+               newlink_flags = flags;
+               return;
+       }
+
+       if (flags & IFF_UP) {
+               /*
+                * We try to verify that connectivity through tunnel works ok.
+                */
+               if (newlink_timeout_id > 0) {
+                       g_source_remove(newlink_timeout_id);
+                       newlink_timeout_id = 0;
+               }
+
+               web = g_web_new(index);
+               if (web == NULL) {
+                       tunnel_destroy();
+                       return;
+               }
+
+               g_web_set_accept(web, NULL);
+               g_web_set_user_agent(web, "ConnMan/%s", VERSION);
+               g_web_set_close_connection(web, TRUE);
+
+               if (getenv("CONNMAN_WEB_DEBUG"))
+                       g_web_set_debug(web, web_debug, "6to4");
+
+               web_request_id = g_web_request_get(web, STATUS_URL,
+                               web_result, NULL,  NULL);
+
+               newlink_timeout(NULL);
+       }
+
+       newlink_flags = flags;
+}
+
 static int init_6to4(struct in_addr *ip4addr)
 {
        unsigned int a, b, c, d;
@@ -430,17 +363,10 @@ static int init_6to4(struct in_addr *ip4addr)
        if (if_index < 0)
                goto error;
 
-       /* We try to verify that connectivity through tunnel works ok.
-        */
-       web = g_web_new(if_index);
-       if (web == NULL)
-               goto error;
-
-       g_web_set_accept(web, NULL);
-       g_web_set_user_agent(web, "ConnMan/%s", VERSION);
-       g_web_set_close_connection(web, TRUE);
+       newlink_watch = connman_rtnl_add_newlink_watch(if_index,
+                               tun_newlink, GINT_TO_POINTER(if_index));
 
-       web_request_id = g_web_request_get(web, STATUS_URL, web_result, NULL);
+       newlink_timeout_id = g_timeout_add_seconds(1, newlink_timeout, NULL);
 
        return 0;
 
@@ -449,12 +375,13 @@ error:
        return -1;
 }
 
-static void receive_rs_reply(struct nd_router_advert *reply, void *user_data)
+static void receive_rs_reply(struct nd_router_advert *reply,
+                       unsigned int length, void *user_data)
 {
        char *address = user_data;
        struct in_addr ip4addr;
 
-       DBG("reply %p address %s", reply, address);
+       DBG("reply %p len %d address %s", reply, length, address);
 
        /* We try to create tunnel if autoconfiguration did not work i.e.,
         * we did not receive any reply to router solicitation message.
@@ -513,7 +440,7 @@ int __connman_6to4_probe(struct connman_service *service)
                                        (a == 172 && (b >= 16 && b <= 31)))
                return -1;
 
-       index = connman_ipconfig_get_index(ip4config);
+       index = __connman_ipconfig_get_index(ip4config);
        ip_address = g_strdup(address);
        tunnel_pending = 1;
 
index 1c982ed..c903e53 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
@@ -85,6 +85,26 @@ int __connman_agent_unregister(const char *sender, const char *path)
        return 0;
 }
 
+static connman_bool_t check_reply_has_dict(DBusMessage *reply)
+{
+       const char *signature = DBUS_TYPE_ARRAY_AS_STRING
+               DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+               DBUS_TYPE_STRING_AS_STRING
+               DBUS_TYPE_VARIANT_AS_STRING
+               DBUS_DICT_ENTRY_END_CHAR_AS_STRING;
+
+       if (dbus_message_has_signature(reply, signature) == TRUE)
+               return TRUE;
+
+       connman_warn("Reply %s to %s from %s has wrong signature %s",
+                       signature,
+                       dbus_message_get_interface(reply),
+                       dbus_message_get_sender(reply),
+                       dbus_message_get_signature(reply));
+
+       return FALSE;
+}
+
 struct request_input_reply {
        struct connman_service *service;
        authentication_cb_t callback;
@@ -94,16 +114,27 @@ struct request_input_reply {
 static void request_input_passphrase_reply(DBusPendingCall *call, void *user_data)
 {
        struct request_input_reply *passphrase_reply = user_data;
+       connman_bool_t values_received = FALSE;
        connman_bool_t wps = FALSE;
+       const char *error = NULL;
        char *identity = NULL;
        char *passphrase = NULL;
        char *wpspin = NULL;
        char *key;
+       char *name = NULL;
+       int name_len = 0;
        DBusMessageIter iter, dict;
        DBusMessage *reply = dbus_pending_call_steal_reply(call);
 
-       if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR)
+       if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
+               error = dbus_message_get_error_name(reply);
                goto done;
+       }
+
+       if (check_reply_has_dict(reply) == FALSE)
+               goto done;
+
+       values_received = TRUE;
 
        dbus_message_iter_init(reply, &iter);
        dbus_message_iter_recurse(&iter, &dict);
@@ -141,33 +172,55 @@ static void request_input_passphrase_reply(DBusPendingCall *call, void *user_dat
                        dbus_message_iter_recurse(&entry, &value);
                        dbus_message_iter_get_basic(&value, &wpspin);
                        break;
+               } else if (g_str_equal(key, "Name")) {
+                       dbus_message_iter_next(&entry);
+                       if (dbus_message_iter_get_arg_type(&entry)
+                                                       != DBUS_TYPE_VARIANT)
+                               break;
+                       dbus_message_iter_recurse(&entry, &value);
+                       dbus_message_iter_get_basic(&value, &name);
+                       name_len = strlen(name);
+               } else if (g_str_equal(key, "SSID")) {
+#if defined TIZEN_EXT
+                       DBusMessageIter array;
+#endif
+                       dbus_message_iter_next(&entry);
+                       if (dbus_message_iter_get_arg_type(&entry)
+                                                       != DBUS_TYPE_VARIANT)
+                               break;
+#if defined TIZEN_EXT
+                       dbus_message_iter_recurse(&entry, &array);
+                       if (dbus_message_iter_get_arg_type(&array)
+                                                       != DBUS_TYPE_ARRAY)
+                               break;
+                       dbus_message_iter_recurse(&array, &value);
+                       if (dbus_message_iter_get_arg_type(&value)
+                                                       != DBUS_TYPE_BYTE)
+                               break;
+#else
+                       dbus_message_iter_recurse(&entry, &value);
+                       if (dbus_message_iter_get_arg_type(&value)
+                                                       != DBUS_TYPE_VARIANT)
+                               break;
+                       if (dbus_message_iter_get_element_type(&value)
+                                                       != DBUS_TYPE_VARIANT)
+                               break;
+#endif
+                       dbus_message_iter_get_fixed_array(&value, &name,
+                                                       &name_len);
                }
                dbus_message_iter_next(&dict);
        }
 
-       if (wps == TRUE) {
-               struct connman_network *network;
-
-               network = __connman_service_get_network(
-                                               passphrase_reply->service);
-               if (network == NULL)
-                       goto done;
-
-               connman_network_set_bool(network, "WiFi.UseWPS", wps);
-
-               if (wpspin != NULL && strlen(wpspin) > 0)
-                       connman_network_set_string(network,
-                                               "WiFi.PinWPS", wpspin);
-               else
-                       connman_network_set_string(network,
-                                               "WiFi.PinWPS", NULL);
-       }
-
 done:
-       passphrase_reply->callback(passphrase_reply->service, identity,
-                               passphrase, passphrase_reply->user_data);
+       passphrase_reply->callback(passphrase_reply->service, values_received,
+                               name, name_len,
+                               identity, passphrase,
+                               wps, wpspin, error,
+                               passphrase_reply->user_data);
        connman_service_unref(passphrase_reply->service);
        dbus_message_unref(reply);
+       dbus_pending_call_unref(call);
        g_free(passphrase_reply);
 }
 
@@ -198,7 +251,7 @@ static void request_input_append_identity(DBusMessageIter *iter,
 
        connman_dbus_dict_append_basic(iter, "Type",
                                DBUS_TYPE_STRING, &str);
-       str = "Mandatory";
+       str = "mandatory";
        connman_dbus_dict_append_basic(iter, "Requirement",
                                DBUS_TYPE_STRING, &str);
 }
@@ -234,7 +287,7 @@ static void request_input_append_passphrase(DBusMessageIter *iter,
        }
        connman_dbus_dict_append_basic(iter, "Type",
                                DBUS_TYPE_STRING, &value);
-       value = "Mandatory";
+       value = "mandatory";
        connman_dbus_dict_append_basic(iter, "Requirement",
                                DBUS_TYPE_STRING, &value);
 
@@ -257,6 +310,32 @@ static void request_input_append_wps(DBusMessageIter *iter, void *user_data)
                                DBUS_TYPE_STRING, &str);
 }
 
+static void request_input_append_name(DBusMessageIter *iter, void *user_data)
+{
+       const char *str = "string";
+
+       connman_dbus_dict_append_basic(iter, "Type",
+                               DBUS_TYPE_STRING, &str);
+       str = "mandatory";
+       connman_dbus_dict_append_basic(iter, "Requirement",
+                               DBUS_TYPE_STRING, &str);
+       connman_dbus_dict_append_array(iter, "Alternates",
+                               DBUS_TYPE_STRING,
+                               request_input_append_alternates,
+                               "SSID");
+}
+
+static void request_input_append_ssid(DBusMessageIter *iter, void *user_data)
+{
+       const char *str = "ssid";
+
+       connman_dbus_dict_append_basic(iter, "Type",
+                               DBUS_TYPE_STRING, &str);
+       str = "alternate";
+       connman_dbus_dict_append_basic(iter, "Requirement",
+                               DBUS_TYPE_STRING, &str);
+}
+
 static void request_input_append_password(DBusMessageIter *iter,
                                                        void *user_data)
 {
@@ -264,22 +343,66 @@ static void request_input_append_password(DBusMessageIter *iter,
 
        connman_dbus_dict_append_basic(iter, "Type",
                                DBUS_TYPE_STRING, &str);
-       str = "Mandatory";
+       str = "mandatory";
        connman_dbus_dict_append_basic(iter, "Requirement",
                                DBUS_TYPE_STRING, &str);
 }
 
+static void request_input_append_previouspassphrase(DBusMessageIter *iter,
+                                                       void *user_data)
+{
+       struct connman_service *service = user_data;
+       enum connman_service_security security;
+       const char *passphrase, *str = NULL;
+
+       passphrase = __connman_service_get_passphrase(service);
+
+       security = __connman_service_get_security(service);
+       switch (security) {
+       case CONNMAN_SERVICE_SECURITY_WEP:
+               str = "wep";
+               break;
+       case CONNMAN_SERVICE_SECURITY_PSK:
+               str  = "psk";
+               break;
+       /*
+        * This should never happen: no passphrase is set if security is not
+        * one of the above.*/
+       default:
+               break;
+       }
+
+       connman_dbus_dict_append_basic(iter, "Type",
+                               DBUS_TYPE_STRING, &str);
+
+       str = "informational";
+       connman_dbus_dict_append_basic(iter, "Requirement",
+                               DBUS_TYPE_STRING, &str);
+
+       connman_dbus_dict_append_basic(iter, "Value",
+                               DBUS_TYPE_STRING, &passphrase);
+}
+
 static void request_input_login_reply(DBusPendingCall *call, void *user_data)
 {
        struct request_input_reply *username_password_reply = user_data;
+       const char *error = NULL;
+       connman_bool_t values_received = FALSE;
        char *username = NULL;
        char *password = NULL;
        char *key;
        DBusMessageIter iter, dict;
        DBusMessage *reply = dbus_pending_call_steal_reply(call);
 
-       if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR)
+       if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
+               error = dbus_message_get_error_name(reply);
                goto done;
+       }
+
+       if (check_reply_has_dict(reply) == FALSE)
+               goto done;
+
+       values_received = TRUE;
 
        dbus_message_iter_init(reply, &iter);
        dbus_message_iter_recurse(&iter, &dict);
@@ -314,7 +437,9 @@ static void request_input_login_reply(DBusPendingCall *call, void *user_data)
 
 done:
        username_password_reply->callback(username_password_reply->service,
+                                       values_received, NULL, 0,
                                        username, password,
+                                       FALSE, NULL, error,
                                        username_password_reply->user_data);
        connman_service_unref(username_password_reply->service);
        dbus_message_unref(reply);
@@ -348,14 +473,29 @@ int __connman_agent_request_passphrase_input(struct connman_service *service,
 
        connman_dbus_dict_open(&iter, &dict);
 
+       if (__connman_service_is_hidden(service)) {
+               connman_dbus_dict_append_dict(&dict, "Name",
+                                       request_input_append_name, NULL);
+               connman_dbus_dict_append_dict(&dict, "SSID",
+                                       request_input_append_ssid, NULL);
+       }
+
        if (__connman_service_get_security(service) ==
                        CONNMAN_SERVICE_SECURITY_8021X) {
                connman_dbus_dict_append_dict(&dict, "Identity",
                                        request_input_append_identity, service);
        }
 
-       connman_dbus_dict_append_dict(&dict, "Passphrase",
-                               request_input_append_passphrase, service);
+       if (__connman_service_get_security(service) !=
+                       CONNMAN_SERVICE_SECURITY_NONE) {
+               connman_dbus_dict_append_dict(&dict, "Passphrase",
+                                       request_input_append_passphrase, service);
+
+               if (__connman_service_get_passphrase(service) != NULL)
+                       connman_dbus_dict_append_dict(&dict, "PreviousPassphrase",
+                                       request_input_append_previouspassphrase,
+                                       service);
+       }
 
        if (__connman_service_wps_enabled(service) == TRUE) {
            connman_dbus_dict_append_dict(&dict, "WPS",
@@ -370,8 +510,9 @@ int __connman_agent_request_passphrase_input(struct connman_service *service,
                return -ENOMEM;
        }
 
-       if (dbus_connection_send_with_reply(connection, message,
-                                               &call, -1) == FALSE) {
+       if (dbus_connection_send_with_reply(connection, message, &call,
+                                       connman_timeout_input_request())
+                       == FALSE) {
                dbus_message_unref(message);
                g_free(passphrase_reply);
                return -ESRCH;
@@ -392,7 +533,7 @@ int __connman_agent_request_passphrase_input(struct connman_service *service,
 
        dbus_message_unref(message);
 
-       return -EIO;
+       return -EINPROGRESS;
 }
 
 int __connman_agent_request_login_input(struct connman_service *service,
@@ -436,8 +577,9 @@ int __connman_agent_request_login_input(struct connman_service *service,
                return -ENOMEM;
        }
 
-       if (dbus_connection_send_with_reply(connection, message,
-                                                       &call, -1) == FALSE) {
+       if (dbus_connection_send_with_reply(connection, message, &call,
+                                       connman_timeout_input_request())
+                       == FALSE) {
                dbus_message_unref(message);
                g_free(username_password_reply);
                return -ESRCH;
@@ -458,7 +600,96 @@ int __connman_agent_request_login_input(struct connman_service *service,
 
        dbus_message_unref(message);
 
-       return -EIO;
+       return -EINPROGRESS;
+}
+
+struct request_browser_reply_data {
+       struct connman_service *service;
+       browser_authentication_cb_t callback;
+       void *user_data;
+};
+
+static void request_browser_reply(DBusPendingCall *call, void *user_data)
+{
+       struct request_browser_reply_data *browser_reply_data = user_data;
+       DBusMessage *reply = dbus_pending_call_steal_reply(call);
+       connman_bool_t result = FALSE;
+       const char *error = NULL;
+
+       if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
+               error = dbus_message_get_error_name(reply);
+               goto done;
+       }
+
+       result = TRUE;
+
+done:
+       browser_reply_data->callback(browser_reply_data->service, result,
+                                       error, browser_reply_data->user_data);
+       connman_service_unref(browser_reply_data->service);
+       dbus_message_unref(reply);
+       g_free(browser_reply_data);
+}
+
+int __connman_agent_request_browser(struct connman_service *service,
+                               browser_authentication_cb_t callback,
+                               const char *url, void *user_data)
+{
+       struct request_browser_reply_data *browser_reply_data;
+       DBusPendingCall *call;
+       DBusMessage *message;
+       DBusMessageIter iter;
+       const char *path;
+
+       if (service == NULL || agent_path == NULL || callback == NULL)
+               return -ESRCH;
+
+       if (url == NULL)
+               url = "";
+
+       message = dbus_message_new_method_call(agent_sender, agent_path,
+                                       CONNMAN_AGENT_INTERFACE,
+                                       "RequestBrowser");
+       if (message == NULL)
+               return -ENOMEM;
+
+       dbus_message_iter_init_append(message, &iter);
+
+       path = __connman_service_get_path(service);
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &path);
+
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &url);
+
+       browser_reply_data = g_try_new0(struct request_browser_reply_data, 1);
+       if (browser_reply_data == NULL) {
+               dbus_message_unref(message);
+               return -ENOMEM;
+       }
+
+       if (dbus_connection_send_with_reply(connection, message, &call,
+                                       connman_timeout_browser_launch())
+                       == FALSE) {
+               dbus_message_unref(message);
+               g_free(browser_reply_data);
+               return -ESRCH;
+       }
+
+       if (call == NULL) {
+               dbus_message_unref(message);
+               g_free(browser_reply_data);
+               return -ESRCH;
+       }
+
+       browser_reply_data->service = connman_service_ref(service);
+       browser_reply_data->callback = callback;
+       browser_reply_data->user_data = user_data;
+
+       dbus_pending_call_set_notify(call, request_browser_reply,
+                                               browser_reply_data, NULL);
+
+       dbus_message_unref(message);
+
+       return -EINPROGRESS;
 }
 
 struct report_error_data {
@@ -474,6 +705,9 @@ static void report_error_reply(DBusPendingCall *call, void *user_data)
        gboolean retry = FALSE;
        const char *dbus_err;
 
+       if (!reply)
+               goto out;
+
        if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
                dbus_err = dbus_message_get_error_name(reply);
                if (dbus_err != NULL &&
@@ -484,9 +718,11 @@ static void report_error_reply(DBusPendingCall *call, void *user_data)
 
        report_error->callback(report_error->service, retry,
                        report_error->user_data);
+out:
        connman_service_unref(report_error->service);
        g_free(report_error);
        dbus_message_unref(reply);
+       dbus_pending_call_unref(call);
 }
 
 int __connman_agent_report_error(struct connman_service *service,
@@ -523,8 +759,9 @@ int __connman_agent_report_error(struct connman_service *service,
                return -ENOMEM;
        }
 
-       if (dbus_connection_send_with_reply(connection, message,
-                                               &call, -1) == FALSE) {
+       if (dbus_connection_send_with_reply(connection, message, &call,
+                                       connman_timeout_input_request())
+                       == FALSE) {
                dbus_message_unref(message);
                g_free(report_error);
                return -ESRCH;
@@ -543,7 +780,7 @@ int __connman_agent_report_error(struct connman_service *service,
                                report_error, NULL);
        dbus_message_unref(message);
 
-       return -EIO;
+       return -EINPROGRESS;
 }
 
 int __connman_agent_init(void)
diff --git a/src/bridge.c b/src/bridge.c
new file mode 100644 (file)
index 0000000..e46cdda
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2012  BMW Car IT GmbH. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <linux/sockios.h>
+#include <string.h>
+#include <fcntl.h>
+#include <linux/if_tun.h>
+
+#include "connman.h"
+
+static int set_forward_delay(const char *name, unsigned int delay)
+{
+       FILE *f;
+       char *forward_delay_path;
+
+       forward_delay_path =
+               g_strdup_printf("/sys/class/net/%s/bridge/forward_delay", name);
+
+       if (forward_delay_path == NULL)
+               return -ENOMEM;
+
+       f = fopen(forward_delay_path, "r+");
+
+       g_free(forward_delay_path);
+
+       if (f == NULL)
+               return -errno;
+
+       fprintf(f, "%d", delay);
+
+       fclose(f);
+
+       return 0;
+}
+
+int __connman_bridge_create(const char *name)
+{
+       int sk, err;
+
+       DBG("name %s", name);
+
+       sk = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
+       if (sk < 0)
+               return -EOPNOTSUPP;
+
+       if (ioctl(sk, SIOCBRADDBR, name) == -1) {
+               err = -errno;
+               if (err != -EEXIST) {
+                       close(sk);
+                       return -EOPNOTSUPP;
+               }
+       }
+
+       err = set_forward_delay(name, 0);
+
+       if (err < 0)
+               ioctl(sk, SIOCBRDELBR, name);
+
+       close(sk);
+
+       return err;
+}
+
+int __connman_bridge_remove(const char *name)
+{
+       int sk, err;
+
+       DBG("name %s", name);
+
+       sk = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
+       if (sk < 0)
+               return -EOPNOTSUPP;
+
+       err = ioctl(sk, SIOCBRDELBR, name);
+
+       close(sk);
+
+       if (err < 0)
+               return -EOPNOTSUPP;
+
+       return 0;
+}
+
+int __connman_bridge_enable(const char *name, const char *gateway,
+                               const char *broadcast)
+{
+       int err, index;
+
+       index = connman_inet_ifindex(name);
+       if (index < 0)
+               return index;
+
+       err = __connman_inet_modify_address(RTM_NEWADDR,
+                       NLM_F_REPLACE | NLM_F_ACK, index, AF_INET,
+                                       gateway, NULL, 24, broadcast);
+       if (err < 0)
+               return err;
+
+       return connman_inet_ifup(index);
+}
+
+int __connman_bridge_disable(const char *name)
+{
+       int index;
+
+       index = connman_inet_ifindex(name);
+       if (index < 0)
+               return index;
+
+       return connman_inet_ifdown(index);
+}
index c98c502..4606fb6 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
@@ -45,7 +45,6 @@ static enum time_updates time_updates_config = TIME_UPDATES_AUTO;
 static enum timezone_updates timezone_updates_config = TIMEZONE_UPDATES_AUTO;
 
 static char *timezone_config = NULL;
-static char **timeservers_config = NULL;
 
 static const char *time_updates2string(enum time_updates value)
 {
@@ -98,14 +97,17 @@ static enum timezone_updates string2timezone_updates(const char *value)
 static void append_timeservers(DBusMessageIter *iter, void *user_data)
 {
        int i;
+       char **timeservers = __connman_timeserver_system_get();
 
-       if (timeservers_config == NULL)
+       if (timeservers == NULL)
                return;
 
-       for (i = 0; timeservers_config[i] != NULL; i++) {
+       for (i = 0; timeservers[i] != NULL; i++) {
                dbus_message_iter_append_basic(iter,
-                               DBUS_TYPE_STRING, &timeservers_config[i]);
+                               DBUS_TYPE_STRING, &timeservers[i]);
        }
+
+       g_strfreev(timeservers);
 }
 
 static DBusMessage *get_properties(DBusConnection *conn,
@@ -167,13 +169,25 @@ static DBusMessage *set_property(DBusConnection *conn,
        if (dbus_message_iter_init(msg, &iter) == FALSE)
                return __connman_error_invalid_arguments(msg);
 
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+               return __connman_error_invalid_arguments(msg);
+
        dbus_message_iter_get_basic(&iter, &name);
        dbus_message_iter_next(&iter);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
+               return __connman_error_invalid_arguments(msg);
+
        dbus_message_iter_recurse(&iter, &value);
 
        type = dbus_message_iter_get_arg_type(&value);
 
        if (g_str_equal(name, "Time") == TRUE) {
+#if defined TIZEN_EXT
+               /* Tizen updates time (ntp) by system service */
+
+               return __connman_error_permission_denied(msg);
+#else
                struct timeval tv;
                dbus_uint64_t newval;
 
@@ -194,6 +208,7 @@ static DBusMessage *set_property(DBusConnection *conn,
                connman_dbus_property_changed_basic(CONNMAN_MANAGER_PATH,
                                CONNMAN_CLOCK_INTERFACE, "Time",
                                DBUS_TYPE_UINT64, &newval);
+#endif
        } else if (g_str_equal(name, "TimeUpdates") == TRUE) {
                const char *strval;
                enum time_updates newval;
@@ -251,37 +266,44 @@ static DBusMessage *set_property(DBusConnection *conn,
                                DBUS_TYPE_STRING, &strval);
        } else if (g_str_equal(name, "Timeservers") == TRUE) {
                DBusMessageIter entry;
-               GString *str;
+               char **str = NULL;
+               GSList *list = NULL;
+               int count = 0;
 
                if (type != DBUS_TYPE_ARRAY)
                        return __connman_error_invalid_arguments(msg);
 
-               str = g_string_new(NULL);
-               if (str == NULL)
-                       return __connman_error_invalid_arguments(msg);
-
                dbus_message_iter_recurse(&value, &entry);
 
                while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING) {
                        const char *val;
+                       GSList *new_head;
 
                        dbus_message_iter_get_basic(&entry, &val);
-                       dbus_message_iter_next(&entry);
 
-                       if (str->len > 0)
-                               g_string_append_printf(str, " %s", val);
-                       else
-                               g_string_append(str, val);
+                       new_head = __connman_timeserver_add_list(list, val);
+                       if (list != new_head) {
+                               count++;
+                               list = new_head;
+                       }
+
+                       dbus_message_iter_next(&entry);
                }
 
-               g_strfreev(timeservers_config);
+               if (list != NULL) {
+                       str = g_new0(char *, count+1);
+
+                       while (list != NULL) {
+                               count--;
+                               str[count] = list->data;
+                               list = g_slist_delete_link(list, list);
+                       };
+               }
 
-               if (str->len > 0)
-                       timeservers_config = g_strsplit_set(str->str, " ", 0);
-               else
-                       timeservers_config = NULL;
+               __connman_timeserver_system_set(str);
 
-               g_string_free(str, TRUE);
+               if (str != NULL)
+                       g_strfreev(str);
 
                connman_dbus_property_changed_array(CONNMAN_MANAGER_PATH,
                                CONNMAN_CLOCK_INTERFACE, "Timeservers",
@@ -292,14 +314,19 @@ static DBusMessage *set_property(DBusConnection *conn,
        return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
 }
 
-static GDBusMethodTable clock_methods[] = {
-       { "GetProperties", "",   "a{sv}", get_properties },
-       { "SetProperty",   "sv", "",      set_property   },
+static const GDBusMethodTable clock_methods[] = {
+       { GDBUS_METHOD("GetProperties",
+                       NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
+                       get_properties) },
+       { GDBUS_METHOD("SetProperty",
+                       GDBUS_ARGS({ "name", "s" }, { "value", "v" }), NULL,
+                       set_property)   },
        { },
 };
 
-static GDBusSignalTable clock_signals[] = {
-       { "PropertyChanged", "sv" },
+static const GDBusSignalTable clock_signals[] = {
+       { GDBUS_SIGNAL("PropertyChanged",
+                       GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
        { },
 };
 
@@ -355,5 +382,4 @@ void __connman_clock_cleanup(void)
        __connman_timezone_cleanup();
 
        g_free(timezone_config);
-       g_strfreev(timeservers_config);
 }
index db15228..ed91a7c 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
@@ -48,6 +48,9 @@ struct connman_config_service {
        char *private_key_passphrase_type;
        char *phase2;
        char *passphrase;
+       GSList *service_identifiers;
+       char *config_ident; /* file prefix */
+       char *config_entry; /* entry name */
 };
 
 struct connman_config {
@@ -65,6 +68,7 @@ static int inotify_wd = -1;
 
 static GIOChannel *inotify_channel = NULL;
 static uint inotify_watch = 0;
+static connman_bool_t cleanup = FALSE;
 
 #define INTERNAL_CONFIG_PREFIX           "__internal"
 
@@ -125,26 +129,53 @@ static void unregister_config(gpointer data)
 
 static void unregister_service(gpointer data)
 {
-       struct connman_config_service *service = data;
-
-       connman_info("Removing service configuration %s", service->ident);
-
-       protected_services = g_slist_remove(protected_services, service);
-
-       g_free(service->ident);
-       g_free(service->type);
-       g_free(service->name);
-       g_free(service->ssid);
-       g_free(service->eap);
-       g_free(service->identity);
-       g_free(service->ca_cert_file);
-       g_free(service->client_cert_file);
-       g_free(service->private_key_file);
-       g_free(service->private_key_passphrase);
-       g_free(service->private_key_passphrase_type);
-       g_free(service->phase2);
-       g_free(service->passphrase);
-       g_free(service);
+       struct connman_config_service *config_service = data;
+       struct connman_service *service;
+       char *service_id;
+       GSList *list;
+
+       if (cleanup == TRUE)
+               goto free_only;
+
+       connman_info("Removing service configuration %s",
+                                               config_service->ident);
+
+       protected_services = g_slist_remove(protected_services,
+                                               config_service);
+
+       for (list = config_service->service_identifiers; list != NULL;
+                                                       list = list->next) {
+               service_id = list->data;
+
+               service = __connman_service_lookup_from_ident(service_id);
+               if (service != NULL) {
+                       __connman_service_set_immutable(service, FALSE);
+                       __connman_service_remove(service);
+               }
+
+               if (__connman_storage_remove_service(service_id) == FALSE)
+                       DBG("Could not remove all files for service %s",
+                                                               service_id);
+       }
+
+free_only:
+       g_free(config_service->ident);
+       g_free(config_service->type);
+       g_free(config_service->name);
+       g_free(config_service->ssid);
+       g_free(config_service->eap);
+       g_free(config_service->identity);
+       g_free(config_service->ca_cert_file);
+       g_free(config_service->client_cert_file);
+       g_free(config_service->private_key_file);
+       g_free(config_service->private_key_passphrase);
+       g_free(config_service->private_key_passphrase_type);
+       g_free(config_service->phase2);
+       g_free(config_service->passphrase);
+       g_slist_free_full(config_service->service_identifiers, g_free);
+       g_free(config_service->config_ident);
+       g_free(config_service->config_entry);
+       g_free(config_service);
 }
 
 static void check_keys(GKeyFile *keyfile, const char *group,
@@ -258,7 +289,13 @@ static int load_service(GKeyFile *keyfile, const char *group,
                }
 
                for (i = 0; i < hex_ssid_len; i += 2) {
-                       sscanf(hex_ssid + i, "%02x", &hex);
+                       if (sscanf(hex_ssid + i, "%02x", &hex) <= 0) {
+                               connman_warn("Invalid SSID %s", hex_ssid);
+                               g_free(ssid);
+                               g_free(hex_ssid);
+                               err = -EILSEQ;
+                               goto err;
+                       }
                        ssid[j++] = hex;
                }
 
@@ -347,13 +384,16 @@ static int load_service(GKeyFile *keyfile, const char *group,
                service->passphrase = str;
        }
 
+       service->config_ident = g_strdup(config->ident);
+       service->config_entry = g_strdup_printf("service_%s", service->ident);
+
        if (service_created)
                g_hash_table_insert(config->service_table, service->ident,
                                        service);
 
        if (config->protected == TRUE)
                protected_services =
-                       g_slist_append(protected_services, service);
+                       g_slist_prepend(protected_services, service);
 
        connman_info("Adding service configuration %s", service->ident);
 
@@ -378,7 +418,7 @@ static int load_config(struct connman_config *config)
        gsize length;
        char **groups;
        char *str;
-       gboolean protected;
+       gboolean protected, found = FALSE;
        int i;
 
        DBG("config %p", config);
@@ -408,14 +448,24 @@ static int load_config(struct connman_config *config)
                config->protected = protected;
        else
                config->protected = TRUE;
+#if defined TIZEN_EXT
+       g_clear_error(&error);
+#endif
 
        groups = g_key_file_get_groups(keyfile, &length);
 
        for (i = 0; groups[i] != NULL; i++) {
-               if (g_str_has_prefix(groups[i], "service_") == TRUE)
-                       load_service(keyfile, groups[i], config);
+               if (g_str_has_prefix(groups[i], "service_") == TRUE) {
+                       if (load_service(keyfile, groups[i], config) == 0)
+                               found = TRUE;
+               }
        }
 
+       if (found == FALSE)
+               connman_warn("Config file %s/%s.config does not contain any "
+                       "configuration that can be provisioned!",
+                       STORAGEDIR, config->ident);
+
        g_strfreev(groups);
 
        g_key_file_free(keyfile);
@@ -448,62 +498,6 @@ static struct connman_config *create_config(const char *ident)
        return config;
 }
 
-int __connman_config_load_service(GKeyFile *keyfile, const char *group,
-                                       connman_bool_t persistent)
-{
-       struct connman_config *config;
-       const char *service_name;
-       char *ident, *content = NULL;
-       gsize content_length;
-       int err;
-
-       service_name = group + strlen("service_");
-       ident = g_strdup_printf("%s_%s", INTERNAL_CONFIG_PREFIX, service_name);
-       if (ident == NULL)
-               return -ENOMEM;
-
-       DBG("ident %s", ident);
-
-       config = g_hash_table_lookup(config_table, ident);
-       if (config == NULL) {
-               config = create_config(ident);
-               if (config == NULL) {
-                       err = -ENOMEM;
-                       goto out;
-               }
-
-               config->protected = FALSE;
-       }
-
-       err = load_service(keyfile, group, config);
-       if (persistent == FALSE || err < 0)
-               goto out;
-
-       g_key_file_set_string(keyfile, "global", CONFIG_KEY_NAME,
-                                                       service_name);
-       g_key_file_set_string(keyfile, "global", CONFIG_KEY_DESC,
-                                               "Internal Config File");
-       g_key_file_set_boolean(keyfile, "global", CONFIG_KEY_PROT, FALSE);
-
-       content = g_key_file_to_data(keyfile, &content_length, NULL);
-       if (content == NULL) {
-               err = -EIO;
-               goto out;
-       }
-
-       DBG("Saving %zu bytes to %s", content_length, service_name);
-
-       __connman_storage_save_config(keyfile, ident);
-
-       return 0;
-
-out:
-       g_free(ident);
-       g_free(content);
-
-       return err;
-}
-
 static connman_bool_t validate_ident(const char *ident)
 {
        unsigned int i;
@@ -638,9 +632,20 @@ static gboolean inotify_data(GIOChannel *channel, GIOCondition cond,
 
                        config = g_hash_table_lookup(config_table, ident);
                        if (config != NULL) {
+                               int ret;
+
                                g_hash_table_remove_all(config->service_table);
                                load_config(config);
-                               __connman_service_provision_changed(ident);
+                               ret = __connman_service_provision_changed(ident);
+                               if (ret > 0) {
+                                       /*
+                                        * Re-scan the config file for any
+                                        * changes
+                                        */
+                                       g_hash_table_remove_all(config->service_table);
+                                       load_config(config);
+                                       __connman_service_provision_changed(ident);
+                               }
                        }
                }
 
@@ -726,10 +731,14 @@ void __connman_config_cleanup(void)
 {
        DBG("");
 
+       cleanup = TRUE;
+
        remove_watch();
 
        g_hash_table_destroy(config_table);
        config_table = NULL;
+
+       cleanup = FALSE;
 }
 
 static char *config_pem_fsid(const char *pem_file)
@@ -757,7 +766,7 @@ static void provision_service(gpointer key, gpointer value, gpointer user_data)
        struct connman_service *service = user_data;
        struct connman_config_service *config = value;
        struct connman_network *network;
-       const void *ssid;
+       const void *ssid, *service_id;
        unsigned int ssid_len;
 
        /* For now only WiFi service entries are supported */
@@ -782,9 +791,17 @@ static void provision_service(gpointer key, gpointer value, gpointer user_data)
        if (memcmp(config->ssid, ssid, ssid_len) != 0)
                return;
 
+       service_id = __connman_service_get_ident(service);
+       config->service_identifiers =
+               g_slist_prepend(config->service_identifiers,
+                               g_strdup(service_id));
+
        __connman_service_set_immutable(service, TRUE);
 
-       __connman_service_set_favorite(service, TRUE);
+       __connman_service_set_favorite_delayed(service, TRUE, TRUE);
+
+       __connman_service_set_config(service, config->config_ident,
+                                               config->config_entry);
 
        if (config->eap != NULL)
                __connman_service_set_string(service, "EAP", config->eap);
@@ -831,9 +848,17 @@ static void provision_service(gpointer key, gpointer value, gpointer user_data)
 
        if (config->phase2 != NULL)
                __connman_service_set_string(service, "Phase2", config->phase2);
+#if defined TIZEN_EXT
+       else
+               __connman_service_set_string(service, "Phase2", NULL);
+#endif
 
        if (config->passphrase != NULL)
                __connman_service_set_string(service, "Passphrase", config->passphrase);
+
+       __connman_service_mark_dirty();
+
+       __connman_service_save(service);
 }
 
 int __connman_config_provision_service(struct connman_service *service)
@@ -849,6 +874,12 @@ int __connman_config_provision_service(struct connman_service *service)
        if (type != CONNMAN_SERVICE_TYPE_WIFI)
                return -ENOSYS;
 
+#if defined TIZEN_EXT
+       if (__connman_service_get_security(service) ==
+                       CONNMAN_SERVICE_SECURITY_NONE)
+               return -ENOSYS;
+#endif
+
        g_hash_table_iter_init(&iter, config_table);
 
        while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
@@ -862,10 +893,11 @@ int __connman_config_provision_service(struct connman_service *service)
 }
 
 int __connman_config_provision_service_ident(struct connman_service *service,
-                                                       const char *ident)
+                       const char *ident, const char *file, const char *entry)
 {
        enum connman_service_type type;
        struct connman_config *config;
+       int ret = 0;
 
        DBG("service %p", service);
 
@@ -875,9 +907,47 @@ int __connman_config_provision_service_ident(struct connman_service *service,
                return -ENOSYS;
 
        config = g_hash_table_lookup(config_table, ident);
-       if(config != NULL)
+       if(config != NULL) {
+               GHashTableIter iter;
+               gpointer value, key;
+               gboolean found = FALSE;
+
+               g_hash_table_iter_init(&iter, config->service_table);
+
+               /*
+                * Check if we need to remove individual service if it
+                * is missing from config file.
+                */
+               if (file != NULL && entry != NULL) {
+                       while (g_hash_table_iter_next(&iter, &key,
+                                                       &value) == TRUE) {
+                               struct connman_config_service *config = value;
+
+                               if (g_strcmp0(config->config_ident,
+                                                               file) == 0 &&
+                                               g_strcmp0(config->config_entry,
+                                                               entry) == 0) {
+                                       found = TRUE;
+                                       break;
+                               }
+                       }
+
+                       DBG("found %d ident %s file %s entry %s", found, ident,
+                                                               file, entry);
+
+                       if (found == FALSE) {
+                               /*
+                                * The entry+8 will skip "service_" prefix
+                                */
+                               g_hash_table_remove(config->service_table,
+                                               entry + 8);
+                               ret = 1;
+                       }
+               }
+
                g_hash_table_foreach(config->service_table,
                                                provision_service, service);
+       }
 
-       return 0;
+       return ret;
 }
index 54f2b74..f88ef67 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *  Copyright (C) 2011  BMW Car IT GmbH. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
@@ -49,6 +49,7 @@ struct gateway_data {
        unsigned int order;
        struct gateway_config *ipv4_gateway;
        struct gateway_config *ipv6_gateway;
+       connman_bool_t default_checked;
 };
 
 static GHashTable *gateway_hash = NULL;
@@ -80,6 +81,160 @@ static struct gateway_config *find_gateway(int index, const char *gateway)
        return NULL;
 }
 
+static struct gateway_data *lookup_gateway_data(struct gateway_config *config)
+{
+       GHashTableIter iter;
+       gpointer value, key;
+
+       if (config == NULL)
+               return NULL;
+
+       g_hash_table_iter_init(&iter, gateway_hash);
+
+       while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
+               struct gateway_data *data = value;
+
+               if (data->ipv4_gateway != NULL &&
+                               data->ipv4_gateway == config)
+                       return data;
+
+               if (data->ipv6_gateway != NULL &&
+                               data->ipv6_gateway == config)
+                       return data;
+       }
+
+       return NULL;
+}
+
+/*
+ * Find the gateway that is serving the VPN link
+ */
+static struct gateway_data *find_phy_gateway(int index, const char *gateway)
+{
+       GHashTableIter iter;
+       gpointer value, key;
+
+       if (gateway == NULL)
+               return NULL;
+
+       g_hash_table_iter_init(&iter, gateway_hash);
+
+       while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
+               struct gateway_data *data = value;
+
+               if (data->ipv4_gateway != NULL && data->index != index &&
+                               g_str_equal(data->ipv4_gateway->gateway,
+                                       gateway) == TRUE)
+                       return data;
+
+               if (data->ipv6_gateway != NULL && data->index != index &&
+                               g_str_equal(data->ipv6_gateway->gateway,
+                                       gateway) == TRUE)
+                       return data;
+       }
+
+       return NULL;
+}
+
+static void set_vpn_routes(struct gateway_data *new_gateway,
+                       struct connman_service *service,
+                       const char *gateway,
+                       enum connman_ipconfig_type type,
+                       const char *peer,
+                       struct gateway_data *active_gateway)
+{
+       struct gateway_config *config;
+       struct gateway_data *data;
+       struct connman_ipconfig *ipconfig;
+       char *dest;
+       int index;
+
+       if (type == CONNMAN_IPCONFIG_TYPE_IPV4) {
+               ipconfig = __connman_service_get_ip4config(service);
+               config = new_gateway->ipv4_gateway;
+       } else if (type == CONNMAN_IPCONFIG_TYPE_IPV6) {
+               ipconfig = __connman_service_get_ip6config(service);
+               config = new_gateway->ipv6_gateway;
+       } else
+               return;
+
+       if (config == NULL)
+               goto done;
+
+       config->vpn = TRUE;
+       if (peer != NULL)
+               config->vpn_ip = g_strdup(peer);
+       else if (gateway != NULL)
+               config->vpn_ip = g_strdup(gateway);
+
+       index = __connman_ipconfig_get_index(ipconfig);
+       data = find_phy_gateway(index, gateway);
+
+       if (data == NULL)
+               goto done;
+
+       /*
+        * data->service points now to original
+        * service that is serving the VPN link
+        */
+       if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
+               ipconfig = __connman_service_get_ip4config(data->service);
+       else if (type == CONNMAN_IPCONFIG_TYPE_IPV6)
+               ipconfig = __connman_service_get_ip6config(data->service);
+       else
+               return;
+
+       if (ipconfig != NULL) {
+               const char *address;
+
+               address = __connman_ipconfig_get_local(ipconfig);
+               config->vpn_phy_ip = g_strdup(address);
+       }
+
+       config->vpn_phy_index = data->index;
+
+       DBG("vpn %s phy %s index %d", config->vpn_ip,
+               config->vpn_phy_ip, config->vpn_phy_index);
+
+done:
+       if (active_gateway == NULL)
+               return;
+
+       if (type == CONNMAN_IPCONFIG_TYPE_IPV4) {
+               /*
+                * Special route to VPN server via gateway. This
+                * is needed so that we can access hosts behind
+                * the VPN. The route might already exist depending
+                * on network topology.
+                */
+               if (active_gateway->ipv4_gateway == NULL)
+                       return;
+
+               if (g_strcmp0(active_gateway->ipv4_gateway->gateway,
+                                                       "0.0.0.0") != 0)
+                       dest = active_gateway->ipv4_gateway->gateway;
+               else
+                       dest = NULL;
+
+               connman_inet_add_host_route(active_gateway->index, gateway,
+                                                                       dest);
+
+       } else if (type == CONNMAN_IPCONFIG_TYPE_IPV6) {
+
+               if (active_gateway->ipv6_gateway == NULL)
+                       return;
+
+               if (g_strcmp0(active_gateway->ipv6_gateway->gateway,
+                                                               "::") != 0)
+                       dest = active_gateway->ipv6_gateway->gateway;
+               else
+                       dest = NULL;
+
+               connman_inet_add_ipv6_host_route(active_gateway->index,
+                                                               gateway, dest);
+       }
+}
+
 static int del_routes(struct gateway_data *data,
                        enum connman_ipconfig_type type)
 {
@@ -95,11 +250,6 @@ static int del_routes(struct gateway_data *data,
 
        if (do_ipv4 == TRUE && data->ipv4_gateway != NULL) {
                if (data->ipv4_gateway->vpn == TRUE) {
-                       if (data->ipv4_gateway->vpn_phy_index >= 0)
-                               connman_inet_del_host_route(
-                                       data->ipv4_gateway->vpn_phy_index,
-                                       data->ipv4_gateway->gateway);
-
                        status4 = connman_inet_clear_gateway_address(
                                                data->index,
                                                data->ipv4_gateway->vpn_ip);
@@ -119,11 +269,6 @@ static int del_routes(struct gateway_data *data,
 
        if (do_ipv6 == TRUE && data->ipv6_gateway != NULL) {
                if (data->ipv6_gateway->vpn == TRUE) {
-                       if (data->ipv6_gateway->vpn_phy_index >= 0)
-                               connman_inet_del_host_route(
-                                       data->ipv6_gateway->vpn_phy_index,
-                                       data->ipv6_gateway->gateway);
-
                        status6 = connman_inet_clear_ipv6_gateway_address(
                                                data->index,
                                                data->ipv6_gateway->vpn_ip);
@@ -227,27 +372,18 @@ static struct gateway_data *add_gateway(struct connman_service *service,
                        data->ipv4_gateway = old->ipv4_gateway;
                        old->ipv4_gateway = NULL;
                }
+       } else {
+               /*
+                * Only take a ref if we are adding new stuff to hash.
+                */
+               connman_service_ref(service);
        }
 
-       connman_service_ref(service);
        g_hash_table_replace(gateway_hash, service, data);
 
        return data;
 }
 
-static void connection_newgateway(int index, const char *gateway)
-{
-       struct gateway_config *config;
-
-       DBG("index %d gateway %s", index, gateway);
-
-       config = find_gateway(index, gateway);
-       if (config == NULL)
-               return;
-
-       config->active = TRUE;
-}
-
 static void set_default_gateway(struct gateway_data *data,
                                enum connman_ipconfig_type type)
 {
@@ -269,11 +405,15 @@ static void set_default_gateway(struct gateway_data *data,
                                        data->ipv4_gateway->vpn == TRUE) {
                connman_inet_set_gateway_address(data->index,
                                                data->ipv4_gateway->vpn_ip);
-               connman_inet_add_host_route(data->ipv4_gateway->vpn_phy_index,
-                                       data->ipv4_gateway->vpn_ip,
-                                       data->ipv4_gateway->vpn_phy_ip);
+               connman_inet_add_host_route(data->index,
+                                       data->ipv4_gateway->vpn_ip, NULL);
                data->ipv4_gateway->active = TRUE;
 
+               DBG("set %p index %d vpn %s index %d phy %s",
+                       data, data->index, data->ipv4_gateway->vpn_ip,
+                       data->ipv4_gateway->vpn_phy_index,
+                       data->ipv4_gateway->vpn_phy_ip);
+
                __connman_service_indicate_default(data->service);
 
                return;
@@ -283,12 +423,15 @@ static void set_default_gateway(struct gateway_data *data,
                                        data->ipv6_gateway->vpn == TRUE) {
                connman_inet_set_ipv6_gateway_address(data->index,
                                                data->ipv6_gateway->vpn_ip);
-               connman_inet_add_ipv6_host_route(
-                                       data->ipv6_gateway->vpn_phy_index,
-                                       data->ipv6_gateway->vpn_ip,
-                                       data->ipv6_gateway->vpn_phy_ip);
+               connman_inet_add_ipv6_host_route(data->index,
+                                       data->ipv6_gateway->vpn_ip, NULL);
                data->ipv6_gateway->active = TRUE;
 
+               DBG("set %p index %d vpn %s index %d phy %s",
+                       data, data->index, data->ipv6_gateway->vpn_ip,
+                       data->ipv6_gateway->vpn_phy_index,
+                       data->ipv6_gateway->vpn_phy_ip);
+
                __connman_service_indicate_default(data->service);
 
                return;
@@ -351,6 +494,11 @@ static void unset_default_gateway(struct gateway_data *data,
                                                data->ipv4_gateway->vpn_ip);
                data->ipv4_gateway->active = FALSE;
 
+               DBG("unset %p index %d vpn %s index %d phy %s",
+                       data, data->index, data->ipv4_gateway->vpn_ip,
+                       data->ipv4_gateway->vpn_phy_index,
+                       data->ipv4_gateway->vpn_phy_ip);
+
                return;
        }
 
@@ -362,6 +510,11 @@ static void unset_default_gateway(struct gateway_data *data,
                                                data->ipv6_gateway->vpn_ip);
                data->ipv6_gateway->active = FALSE;
 
+               DBG("unset %p index %d vpn %s index %d phy %s",
+                       data, data->index, data->ipv6_gateway->vpn_ip,
+                       data->ipv6_gateway->vpn_phy_index,
+                       data->ipv6_gateway->vpn_phy_ip);
+
                return;
        }
 
@@ -405,12 +558,119 @@ static struct gateway_data *find_default_gateway(void)
                if (found == NULL || data->order > order) {
                        found = data;
                        order = data->order;
+
+                       DBG("default %p order %d", found, order);
                }
        }
 
        return found;
 }
 
+static gboolean choose_default_gateway(struct gateway_data *data,
+                                       struct gateway_data *candidate)
+{
+       gboolean downgraded = FALSE;
+
+       /*
+        * If the current default is not active, then we mark
+        * this one as default. If the other one is already active
+        * we mark this one as non default.
+        */
+       if (data->ipv4_gateway != NULL) {
+               if (candidate->ipv4_gateway != NULL &&
+                               candidate->ipv4_gateway->active == FALSE) {
+                       DBG("ipv4 downgrading %p", candidate);
+                       unset_default_gateway(candidate,
+                                               CONNMAN_IPCONFIG_TYPE_IPV4);
+               }
+               if (candidate->ipv4_gateway != NULL &&
+                               candidate->ipv4_gateway->active == TRUE &&
+                               candidate->order > data->order) {
+                       DBG("ipv4 downgrading this %p", data);
+                       unset_default_gateway(data,
+                                               CONNMAN_IPCONFIG_TYPE_IPV4);
+                       downgraded = TRUE;
+               }
+       }
+
+       if (data->ipv6_gateway != NULL) {
+               if (candidate->ipv6_gateway != NULL &&
+                               candidate->ipv6_gateway->active == FALSE) {
+                       DBG("ipv6 downgrading %p", candidate);
+                       unset_default_gateway(candidate,
+                                               CONNMAN_IPCONFIG_TYPE_IPV6);
+               }
+
+               if (candidate->ipv6_gateway != NULL &&
+                               candidate->ipv6_gateway->active == TRUE &&
+                               candidate->order > data->order) {
+                       DBG("ipv6 downgrading this %p", data);
+                       unset_default_gateway(data,
+                                               CONNMAN_IPCONFIG_TYPE_IPV6);
+                       downgraded = TRUE;
+               }
+       }
+
+       return downgraded;
+}
+
+static void connection_newgateway(int index, const char *gateway)
+{
+       struct gateway_config *config;
+       struct gateway_data *data;
+       GHashTableIter iter;
+       gpointer value, key;
+       gboolean found = FALSE;
+
+       DBG("index %d gateway %s", index, gateway);
+
+       config = find_gateway(index, gateway);
+       if (config == NULL)
+               return;
+
+       config->active = TRUE;
+
+       /*
+        * It is possible that we have two default routes atm
+        * if there are two gateways waiting rtnl activation at the
+        * same time.
+        */
+       data = lookup_gateway_data(config);
+       if (data == NULL)
+               return;
+
+       if (data->default_checked == TRUE)
+               return;
+
+       /*
+        * The next checks are only done once, otherwise setting
+        * the default gateway could lead into rtnl forever loop.
+        */
+
+       g_hash_table_iter_init(&iter, gateway_hash);
+
+       while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
+               struct gateway_data *candidate = value;
+
+               if (candidate == data)
+                       continue;
+
+               found = choose_default_gateway(data, candidate);
+               if (found == TRUE)
+                       break;
+       }
+
+       if (found == FALSE) {
+               if (data->ipv4_gateway != NULL)
+                       set_default_gateway(data, CONNMAN_IPCONFIG_TYPE_IPV4);
+
+               if (data->ipv6_gateway != NULL)
+                       set_default_gateway(data, CONNMAN_IPCONFIG_TYPE_IPV6);
+       }
+
+       data->default_checked = TRUE;
+}
+
 static void remove_gateway(gpointer user_data)
 {
        struct gateway_data *data = user_data;
@@ -514,6 +774,80 @@ void __connman_connection_gateway_activate(struct connman_service *service,
                data->ipv6_gateway->active = TRUE;
 }
 
+static void add_host_route(int family, int index, const char *gateway,
+                       enum connman_service_type service_type)
+{
+       switch (family) {
+       case AF_INET:
+               if (g_strcmp0(gateway, "0.0.0.0") != 0) {
+                       /*
+                        * We must not set route to the phy dev gateway in
+                        * VPN link. The packets to VPN link might be routed
+                        * back to itself and not routed into phy link gateway.
+                        */
+                       if (service_type != CONNMAN_SERVICE_TYPE_VPN)
+                               connman_inet_add_host_route(index, gateway,
+                                                                       NULL);
+               } else {
+                       /*
+                        * Add host route to P-t-P link so that services can
+                        * be moved around and we can have some link to P-t-P
+                        * network (although those P-t-P links have limited
+                        * usage if default route is not directed to them)
+                        */
+                       char *dest;
+                       if (connman_inet_get_dest_addr(index, &dest) == 0) {
+                               connman_inet_add_host_route(index, dest, NULL);
+                               g_free(dest);
+                       }
+               }
+               break;
+
+       case AF_INET6:
+               if (g_strcmp0(gateway, "::") != 0) {
+                       if (service_type != CONNMAN_SERVICE_TYPE_VPN)
+                               connman_inet_add_ipv6_host_route(index,
+                                                               gateway, NULL);
+               } else {
+                       /* P-t-P link, add route to destination */
+                       char *dest;
+                       if (connman_inet_ipv6_get_dest_addr(index,
+                                                               &dest) == 0) {
+                               connman_inet_add_ipv6_host_route(index, dest,
+                                                               NULL);
+                               g_free(dest);
+                       }
+               }
+               break;
+       }
+}
+
+#if defined TIZEN_EXT
+static connman_bool_t __connman_service_is_mms_cellular_profile(
+               struct connman_service *cellular)
+{
+       char *suffix;
+       const char *path;
+       const char mms_suffix[] = "_2";
+       const char prepaid_mms_suffix[] = "_4";
+
+       if (connman_service_get_type(cellular) != CONNMAN_SERVICE_TYPE_CELLULAR)
+               return FALSE;
+
+       path = __connman_service_get_path(cellular);
+
+       suffix = strrchr(path, '_');
+
+       if (g_strcmp0(suffix, mms_suffix) == 0 ||
+                       g_strcmp0(suffix, prepaid_mms_suffix) == 0) {
+               DBG("MMS service: %s", path);
+               return TRUE;
+       }
+
+       return FALSE;
+}
+#endif
+
 int __connman_connection_gateway_add(struct connman_service *service,
                                        const char *gateway,
                                        enum connman_ipconfig_type type,
@@ -521,13 +855,14 @@ int __connman_connection_gateway_add(struct connman_service *service,
 {
        struct gateway_data *active_gateway = NULL;
        struct gateway_data *new_gateway = NULL;
+       enum connman_ipconfig_type type4 = CONNMAN_IPCONFIG_TYPE_UNKNOWN,
+               type6 = CONNMAN_IPCONFIG_TYPE_UNKNOWN;
+       enum connman_service_type service_type =
+                                       connman_service_get_type(service);
        int index;
 
        index = __connman_service_get_index(service);
 
-       DBG("service %p index %d gateway %s vpn ip %s type %d",
-               service, index, gateway, peer, type);
-
        /*
         * If gateway is NULL, it's a point to point link and the default
         * gateway for ipv4 is 0.0.0.0 and for ipv6 is ::, meaning the
@@ -539,93 +874,61 @@ int __connman_connection_gateway_add(struct connman_service *service,
        if (gateway == NULL && type == CONNMAN_IPCONFIG_TYPE_IPV6)
                gateway = "::";
 
-       active_gateway = find_active_gateway();
+#if defined TIZEN_EXT
+       if (__connman_service_is_mms_cellular_profile(service) == TRUE) {
+               /* MMS service should not be default gateway */
+
+               DBG("MMS service %p index %d gateway %s vpn ip %s type %d",
+                       service, index, gateway, peer, type);
+
+               if (type == CONNMAN_IPCONFIG_TYPE_IPV4) {
+                       add_host_route(AF_INET, index, gateway, service_type);
+                       __connman_service_nameserver_add_routes(service, gateway);
+                       type4 = CONNMAN_IPCONFIG_TYPE_IPV4;
+               }
+
+               if (type == CONNMAN_IPCONFIG_TYPE_IPV6) {
+                       add_host_route(AF_INET6, index, gateway, service_type);
+                       __connman_service_nameserver_add_routes(service, gateway);
+                       type6 = CONNMAN_IPCONFIG_TYPE_IPV6;
+               }
+
+               goto done;
+       }
+#endif
+       DBG("service %p index %d gateway %s vpn ip %s type %d",
+               service, index, gateway, peer, type);
+
        new_gateway = add_gateway(service, index, gateway, type);
        if (new_gateway == NULL)
                return -EINVAL;
 
-       if (type == CONNMAN_IPCONFIG_TYPE_IPV6 &&
-                       new_gateway->ipv6_gateway != NULL &&
-                       g_strcmp0(new_gateway->ipv6_gateway->gateway,
-                                                               "::") != 0)
-               connman_inet_add_ipv6_host_route(index,
-                                       new_gateway->ipv6_gateway->gateway,
-                                       NULL);
+       active_gateway = find_active_gateway();
 
-       if (type == CONNMAN_IPCONFIG_TYPE_IPV4 &&
-                       new_gateway->ipv4_gateway != NULL &&
-                       g_strcmp0(new_gateway->ipv4_gateway->gateway,
-                                                       "0.0.0.0") != 0)
-               connman_inet_add_host_route(index,
-                                       new_gateway->ipv4_gateway->gateway,
-                                       NULL);
+       DBG("active %p index %d new %p", active_gateway,
+               active_gateway ? active_gateway->index : -1, new_gateway);
 
        if (type == CONNMAN_IPCONFIG_TYPE_IPV4 &&
                                new_gateway->ipv4_gateway != NULL) {
+               add_host_route(AF_INET, index, gateway, service_type);
                __connman_service_nameserver_add_routes(service,
                                        new_gateway->ipv4_gateway->gateway);
-               __connman_service_ipconfig_indicate_state(service,
-                                               CONNMAN_SERVICE_STATE_READY,
-                                               CONNMAN_IPCONFIG_TYPE_IPV4);
+               type4 = CONNMAN_IPCONFIG_TYPE_IPV4;
        }
 
        if (type == CONNMAN_IPCONFIG_TYPE_IPV6 &&
                                new_gateway->ipv6_gateway != NULL) {
+               add_host_route(AF_INET6, index, gateway, service_type);
                __connman_service_nameserver_add_routes(service,
                                        new_gateway->ipv6_gateway->gateway);
-               __connman_service_ipconfig_indicate_state(service,
-                                               CONNMAN_SERVICE_STATE_READY,
-                                               CONNMAN_IPCONFIG_TYPE_IPV6);
+               type6 = CONNMAN_IPCONFIG_TYPE_IPV6;
        }
 
-       if (connman_service_get_type(service) == CONNMAN_SERVICE_TYPE_VPN) {
-               if (type == CONNMAN_IPCONFIG_TYPE_IPV4 &&
-                                       new_gateway->ipv4_gateway != NULL) {
-                       new_gateway->ipv4_gateway->vpn = TRUE;
-                       if (peer != NULL)
-                               new_gateway->ipv4_gateway->vpn_ip =
-                                                       g_strdup(peer);
-                       else if (gateway != NULL)
-                               new_gateway->ipv4_gateway->vpn_ip =
-                                                       g_strdup(gateway);
-                       if (active_gateway) {
-                               const char *new_ipv4_gateway;
-
-                               new_ipv4_gateway =
-                                       active_gateway->ipv4_gateway->gateway;
-                               if (new_ipv4_gateway != NULL &&
-                                        g_strcmp0(new_ipv4_gateway,
-                                                       "0.0.0.0") != 0)
-                                       new_gateway->ipv4_gateway->vpn_phy_ip =
-                                               g_strdup(new_ipv4_gateway);
+       if (service_type == CONNMAN_SERVICE_TYPE_VPN) {
 
-                               new_gateway->ipv4_gateway->vpn_phy_index =
-                                                       active_gateway->index;
-                       }
+               set_vpn_routes(new_gateway, service, gateway, type, peer,
+                                                       active_gateway);
 
-               } else if (type == CONNMAN_IPCONFIG_TYPE_IPV6 &&
-                                       new_gateway->ipv6_gateway != NULL) {
-                       new_gateway->ipv6_gateway->vpn = TRUE;
-                       if (peer != NULL)
-                               new_gateway->ipv6_gateway->vpn_ip =
-                                                       g_strdup(peer);
-                       else if (gateway != NULL)
-                               new_gateway->ipv6_gateway->vpn_ip =
-                                                       g_strdup(gateway);
-                       if (active_gateway) {
-                               const char *new_ipv6_gateway;
-
-                               new_ipv6_gateway =
-                                       active_gateway->ipv6_gateway->gateway;
-                               if (new_ipv6_gateway != NULL &&
-                                       g_strcmp0(new_ipv6_gateway, "::") != 0)
-                                       new_gateway->ipv6_gateway->vpn_phy_ip =
-                                               g_strdup(new_ipv6_gateway);
-
-                               new_gateway->ipv6_gateway->vpn_phy_index =
-                                                       active_gateway->index;
-                       }
-               }
        } else {
                if (type == CONNMAN_IPCONFIG_TYPE_IPV4 &&
                                        new_gateway->ipv4_gateway != NULL)
@@ -638,29 +941,39 @@ int __connman_connection_gateway_add(struct connman_service *service,
 
        if (active_gateway == NULL) {
                set_default_gateway(new_gateway, type);
-               return 0;
+               goto done;
        }
 
        if (type == CONNMAN_IPCONFIG_TYPE_IPV4 &&
                                new_gateway->ipv4_gateway != NULL &&
                                new_gateway->ipv4_gateway->vpn == TRUE) {
-               connman_inet_add_host_route(active_gateway->index,
-                                       new_gateway->ipv4_gateway->gateway,
-                                       active_gateway->ipv4_gateway->gateway);
-               connman_inet_clear_gateway_address(active_gateway->index,
+               if (__connman_service_is_split_routing(new_gateway->service) ==
+                                                                       FALSE)
+                       connman_inet_clear_gateway_address(
+                                       active_gateway->index,
                                        active_gateway->ipv4_gateway->gateway);
        }
 
        if (type == CONNMAN_IPCONFIG_TYPE_IPV6 &&
                                new_gateway->ipv6_gateway != NULL &&
                                new_gateway->ipv6_gateway->vpn == TRUE) {
-               connman_inet_add_ipv6_host_route(active_gateway->index,
-                                       new_gateway->ipv6_gateway->gateway,
-                                       active_gateway->ipv6_gateway->gateway);
-               connman_inet_clear_ipv6_gateway_address(active_gateway->index,
+               if (__connman_service_is_split_routing(new_gateway->service) ==
+                                                                       FALSE)
+                       connman_inet_clear_ipv6_gateway_address(
+                                       active_gateway->index,
                                        active_gateway->ipv6_gateway->gateway);
        }
 
+done:
+       if (type4 == CONNMAN_IPCONFIG_TYPE_IPV4)
+               __connman_service_ipconfig_indicate_state(service,
+                                               CONNMAN_SERVICE_STATE_READY,
+                                               CONNMAN_IPCONFIG_TYPE_IPV4);
+
+       if (type6 == CONNMAN_IPCONFIG_TYPE_IPV6)
+               __connman_service_ipconfig_indicate_state(service,
+                                               CONNMAN_SERVICE_STATE_READY,
+                                               CONNMAN_IPCONFIG_TYPE_IPV6);
        return 0;
 }
 
@@ -681,7 +994,7 @@ void __connman_connection_gateway_remove(struct connman_service *service,
        else
                do_ipv4 = do_ipv6 = TRUE;
 
-       __connman_service_nameserver_del_routes(service);
+       __connman_service_nameserver_del_routes(service, type);
 
        data = g_hash_table_lookup(gateway_hash, service);
        if (data == NULL)
@@ -708,8 +1021,6 @@ void __connman_connection_gateway_remove(struct connman_service *service,
                connman_inet_del_ipv6_host_route(data->index,
                                                data->ipv6_gateway->gateway);
 
-       __connman_service_nameserver_del_routes(service);
-
        err = disable_gateway(data, type);
 
        /*
@@ -743,43 +1054,95 @@ void __connman_connection_gateway_remove(struct connman_service *service,
 
 gboolean __connman_connection_update_gateway(void)
 {
-       struct gateway_data *active_gateway, *default_gateway;
+       struct gateway_data *default_gateway;
        gboolean updated = FALSE;
+       GHashTableIter iter;
+       gpointer value, key;
+#if defined TIZEN_EXT
+       static struct gateway_data *old_default = NULL;
+#endif
 
        if (gateway_hash == NULL)
                return updated;
 
-       active_gateway = find_active_gateway();
-
        update_order();
 
        default_gateway = find_default_gateway();
 
-       if (active_gateway && active_gateway != default_gateway) {
-               updated = TRUE;
+       __connman_service_update_ordering();
 
-               if (active_gateway->ipv4_gateway)
-                       unset_default_gateway(active_gateway,
-                                       CONNMAN_IPCONFIG_TYPE_IPV4);
+       DBG("default %p", default_gateway);
 
-               if (active_gateway->ipv6_gateway)
-                       unset_default_gateway(active_gateway,
-                                       CONNMAN_IPCONFIG_TYPE_IPV6);
+       /*
+        * There can be multiple active gateways so we need to
+        * check them all.
+        */
+       g_hash_table_iter_init(&iter, gateway_hash);
 
-               if (default_gateway) {
-                       if (default_gateway->ipv4_gateway)
-                               set_default_gateway(default_gateway,
+       while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
+               struct gateway_data *active_gateway = value;
+
+               if (active_gateway == default_gateway)
+                       continue;
+
+               if (active_gateway->ipv4_gateway != NULL &&
+                               active_gateway->ipv4_gateway->active == TRUE) {
+
+                       unset_default_gateway(active_gateway,
                                                CONNMAN_IPCONFIG_TYPE_IPV4);
+                       updated = TRUE;
+               }
+
+               if (active_gateway->ipv6_gateway != NULL &&
+                               active_gateway->ipv6_gateway->active == TRUE) {
 
-                       if (default_gateway->ipv6_gateway)
-                               set_default_gateway(default_gateway,
+                       unset_default_gateway(active_gateway,
                                                CONNMAN_IPCONFIG_TYPE_IPV6);
+                       updated = TRUE;
                }
        }
 
+#if defined TIZEN_EXT
+       if (updated == FALSE && old_default != default_gateway) {
+               updated = TRUE;
+               old_default = default_gateway;
+       }
+#endif
+       if (updated && default_gateway != NULL) {
+               if (default_gateway->ipv4_gateway)
+                       set_default_gateway(default_gateway,
+                                       CONNMAN_IPCONFIG_TYPE_IPV4);
+
+               if (default_gateway->ipv6_gateway)
+                       set_default_gateway(default_gateway,
+                                       CONNMAN_IPCONFIG_TYPE_IPV6);
+       }
+
        return updated;
 }
 
+int __connman_connection_get_vpn_index(int phy_index)
+{
+       GHashTableIter iter;
+       gpointer value, key;
+
+       g_hash_table_iter_init(&iter, gateway_hash);
+
+       while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
+               struct gateway_data *data = value;
+
+               if (data->ipv4_gateway != NULL &&
+                               data->ipv4_gateway->vpn_phy_index == phy_index)
+                       return data->index;
+
+               if (data->ipv6_gateway != NULL &&
+                               data->ipv6_gateway->vpn_phy_index == phy_index)
+                       return data->index;
+       }
+
+       return -1;
+}
+
 int __connman_connection_init(void)
 {
        int err;
index 42e473c..bc8c925 100644 (file)
@@ -6,6 +6,9 @@
         <allow send_destination="net.connman"/>
         <allow send_interface="net.connman.Agent"/>
         <allow send_interface="net.connman.Counter"/>
+        <allow send_interface="net.connman.Manager"/>
+        <allow send_interface="net.connman.Service"/>
+        <allow send_interface="net.connman.Technology"/>
         <allow send_interface="net.connman.Notification"/>
     </policy>
     <policy user="5000">
@@ -13,6 +16,9 @@
         <allow send_destination="net.connman"/>
         <allow send_interface="net.connman.Agent"/>
         <allow send_interface="net.connman.Counter"/>
+        <allow send_interface="net.connman.Manager"/>
+        <allow send_interface="net.connman.Service"/>
+        <allow send_interface="net.connman.Technology"/>
         <allow send_interface="net.connman.Notification"/>
     </policy>
     <policy at_console="true">
index 5444878..bb0be1e 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
@@ -25,6 +25,8 @@
 
 #include <connman/dbus.h>
 
+dbus_bool_t __connman_dbus_append_objpath_dict_array(DBusMessage *msg,
+               connman_dbus_append_cb_t function, void *user_data);
 int __connman_dbus_init(DBusConnection *conn);
 void __connman_dbus_cleanup(void);
 
@@ -82,15 +84,26 @@ void __connman_counter_cleanup(void);
 
 struct connman_service;
 
+int __connman_service_add_passphrase(struct connman_service *service,
+                                       const gchar *passphrase);
 typedef void (* authentication_cb_t) (struct connman_service *service,
+                               connman_bool_t values_received,
+                               const char *name, int name_len,
                                const char *identifier, const char *secret,
-                               void *user_data);
+                               gboolean wps, const char *wpspin,
+                               const char *error, void *user_data);
+typedef void (* browser_authentication_cb_t) (struct connman_service *service,
+                               connman_bool_t authentication_done,
+                               const char *error, void *user_data);
 typedef void (* report_error_cb_t) (struct connman_service *service,
                                gboolean retry, void *user_data);
 int __connman_agent_request_passphrase_input(struct connman_service *service,
                                authentication_cb_t callback, void *user_data);
 int __connman_agent_request_login_input(struct connman_service *service,
                                authentication_cb_t callback, void *user_data);
+int __connman_agent_request_browser(struct connman_service *service,
+                               browser_authentication_cb_t callback,
+                               const char *url, void *user_data);
 int __connman_agent_report_error(struct connman_service *service,
                                const char *error,
                                report_error_cb_t callback, void *user_data);
@@ -104,9 +117,6 @@ void __connman_log_cleanup(void);
 void __connman_log_enable(struct connman_debug_desc *start,
                                        struct connman_debug_desc *stop);
 
-void __connman_debug_list_available(DBusMessageIter *iter, void *user_data);
-void __connman_debug_list_enabled(DBusMessageIter *iter, void *user_data);
-
 #include <connman/option.h>
 
 #include <connman/setting.h>
@@ -123,42 +133,89 @@ void __connman_task_cleanup(void);
 
 #include <connman/inet.h>
 
+char **__connman_inet_get_running_interfaces(void);
 int __connman_inet_modify_address(int cmd, int flags, int index, int family,
                                const char *address,
                                const char *peer,
                                unsigned char prefixlen,
                                const char *broadcast);
+int __connman_inet_get_interface_address(int index, int family, void *address);
 
 #include <netinet/ip6.h>
 #include <netinet/icmp6.h>
 
 typedef void (*__connman_inet_rs_cb_t) (struct nd_router_advert *reply,
-                                       void *user_data);
+                                       unsigned int length, void *user_data);
 
 int __connman_inet_ipv6_send_rs(int index, int timeout,
                        __connman_inet_rs_cb_t callback, void *user_data);
 
+int __connman_refresh_rs_ipv6(struct connman_network *network, int index);
+
+GSList *__connman_inet_ipv6_get_prefixes(struct nd_router_advert *hdr,
+                                       unsigned int length);
+
+struct __connman_inet_rtnl_handle {
+       int                     fd;
+       struct sockaddr_nl      local;
+       struct sockaddr_nl      peer;
+       __u32                   seq;
+       __u32                   dump;
+
+       struct {
+               struct nlmsghdr n;
+               union {
+                       struct {
+                               struct rtmsg rt;
+                       } r;
+                       struct {
+                               struct ifaddrmsg ifa;
+                       } i;
+               } u;
+               char buf[1024];
+       } req;
+};
+
+int __connman_inet_rtnl_open(struct __connman_inet_rtnl_handle *rth);
+typedef void (*__connman_inet_rtnl_cb_t) (struct nlmsghdr *answer,
+                                       void *user_data);
+int __connman_inet_rtnl_talk(struct __connman_inet_rtnl_handle *rtnl,
+                       struct nlmsghdr *n, int timeout,
+                       __connman_inet_rtnl_cb_t callback, void *user_data);
+static inline
+int __connman_inet_rtnl_send(struct __connman_inet_rtnl_handle *rtnl,
+                                               struct nlmsghdr *n)
+{
+       return __connman_inet_rtnl_talk(rtnl, n, 0, NULL, NULL);
+}
+
+void __connman_inet_rtnl_close(struct __connman_inet_rtnl_handle *rth);
+int __connman_inet_rtnl_addattr_l(struct nlmsghdr *n, size_t max_length,
+                       int type, const void *data, size_t data_length);
+int __connman_inet_rtnl_addattr32(struct nlmsghdr *n, size_t maxlen,
+                       int type, __u32 data);
+
 #include <connman/resolver.h>
 
 int __connman_resolver_init(connman_bool_t dnsproxy);
 void __connman_resolver_cleanup(void);
-int __connman_resolvfile_append(const char *interface, const char *domain, const char *server);
-int __connman_resolvfile_remove(const char *interface, const char *domain, const char *server);
+int __connman_resolvfile_append(int index, const char *domain, const char *server);
+int __connman_resolvfile_remove(int index, const char *domain, const char *server);
+int __connman_resolver_redo_servers(int index);
 
-void __connman_storage_migrate(void);
-GKeyFile *__connman_storage_open_global();
-GKeyFile *__connman_storage_load_global();
-void __connman_storage_save_global(GKeyFile *keyfile);
-void __connman_storage_delete_global();
+GKeyFile *__connman_storage_open_global(void);
+GKeyFile *__connman_storage_load_global(void);
+int __connman_storage_save_global(GKeyFile *keyfile);
+void __connman_storage_delete_global(void);
 
 GKeyFile *__connman_storage_load_config(const char *ident);
-void __connman_storage_save_config(GKeyFile *keyfile, const char *ident);
-void __connman_storage_delete_config(const char *ident);
 
 GKeyFile *__connman_storage_open_service(const char *ident);
-void __connman_storage_save_service(GKeyFile *keyfile, const char *ident);
+int __connman_storage_save_service(GKeyFile *keyfile, const char *ident);
 GKeyFile *__connman_storage_load_provider(const char *identifier);
 void __connman_storage_save_provider(GKeyFile *keyfile, const char *identifier);
+char **__connman_storage_get_providers(void);
+gboolean __connman_storage_remove_service(const char *service_id);
 
 int __connman_detect_init(void);
 void __connman_detect_cleanup(void);
@@ -170,6 +227,53 @@ void __connman_proxy_cleanup(void);
 
 #include <connman/ipconfig.h>
 
+struct connman_ipaddress {
+       int family;
+       unsigned char prefixlen;
+       char *local;
+       char *peer;
+       char *broadcast;
+       char *gateway;
+};
+
+struct connman_ipconfig_ops {
+       void (*up) (struct connman_ipconfig *ipconfig);
+       void (*down) (struct connman_ipconfig *ipconfig);
+       void (*lower_up) (struct connman_ipconfig *ipconfig);
+       void (*lower_down) (struct connman_ipconfig *ipconfig);
+       void (*ip_bound) (struct connman_ipconfig *ipconfig);
+       void (*ip_release) (struct connman_ipconfig *ipconfig);
+       void (*route_set) (struct connman_ipconfig *ipconfig);
+       void (*route_unset) (struct connman_ipconfig *ipconfig);
+};
+
+struct connman_ipconfig *__connman_ipconfig_create(int index,
+                                       enum connman_ipconfig_type type);
+
+#define __connman_ipconfig_ref(ipconfig) \
+       __connman_ipconfig_ref_debug(ipconfig, __FILE__, __LINE__, __func__)
+#define __connman_ipconfig_unref(ipconfig) \
+       __connman_ipconfig_unref_debug(ipconfig, __FILE__, __LINE__, __func__)
+
+struct connman_ipconfig *
+__connman_ipconfig_ref_debug(struct connman_ipconfig *ipconfig,
+                       const char *file, int line, const char *caller);
+void __connman_ipconfig_unref_debug(struct connman_ipconfig *ipconfig,
+                       const char *file, int line, const char *caller);
+
+void *__connman_ipconfig_get_data(struct connman_ipconfig *ipconfig);
+void __connman_ipconfig_set_data(struct connman_ipconfig *ipconfig, void *data);
+
+int __connman_ipconfig_get_index(struct connman_ipconfig *ipconfig);
+const char *__connman_ipconfig_get_ifname(struct connman_ipconfig *ipconfig);
+
+void __connman_ipconfig_set_ops(struct connman_ipconfig *ipconfig,
+                               const struct connman_ipconfig_ops *ops);
+int __connman_ipconfig_set_method(struct connman_ipconfig *ipconfig,
+                                       enum connman_ipconfig_method method);
+void __connman_ipconfig_disable_ipv6(struct connman_ipconfig *ipconfig);
+void __connman_ipconfig_enable_ipv6(struct connman_ipconfig *ipconfig);
+
 int __connman_ipconfig_init(void);
 void __connman_ipconfig_cleanup(void);
 
@@ -195,7 +299,8 @@ enum connman_ipconfig_type __connman_ipconfig_get_config_type(
                                        struct connman_ipconfig *ipconfig);
 unsigned short __connman_ipconfig_get_type_from_index(int index);
 unsigned int __connman_ipconfig_get_flags_from_index(int index);
-const char *__connman_ipconfig_get_gateway_from_index(int index);
+const char *__connman_ipconfig_get_gateway_from_index(int index,
+       enum connman_ipconfig_type type);
 void __connman_ipconfig_set_index(struct connman_ipconfig *ipconfig, int index);
 
 const char *__connman_ipconfig_get_local(struct connman_ipconfig *ipconfig);
@@ -238,6 +343,7 @@ int __connman_ipconfig_address_unset(struct connman_ipconfig *ipconfig);
 #if defined TIZEN_EXT
 /*
  * Description: __connman_service_lookup_from_index cannot find correct service
+ *              e.g. same interface or same APN of cellular profile
  */
 int __connman_ipconfig_gateway_add(struct connman_ipconfig *ipconfig, struct connman_service *service);
 #else
@@ -257,6 +363,7 @@ int __connman_ipconfig_load(struct connman_ipconfig *ipconfig,
                GKeyFile *keyfile, const char *identifier, const char *prefix);
 int __connman_ipconfig_save(struct connman_ipconfig *ipconfig,
                GKeyFile *keyfile, const char *identifier, const char *prefix);
+gboolean __connman_ipconfig_ipv6_privacy_enabled(struct connman_ipconfig *ipconfig);
 
 int __connman_ipconfig_set_rp_filter();
 void __connman_ipconfig_unset_rp_filter(int old_value);
@@ -271,12 +378,36 @@ int __connman_utsname_set_domainname(const char *domainname);
 int __connman_timeserver_init(void);
 void __connman_timeserver_cleanup(void);
 
+char **__connman_timeserver_system_get();
+
+GSList *__connman_timeserver_add_list(GSList *server_list,
+               const char *timeserver);
+GSList *__connman_timeserver_get_all(struct connman_service *service);
+int __connman_timeserver_sync(struct connman_service *service);
+void __connman_timeserver_sync_next();
+
+#if defined TIZEN_EXT && defined TIZEN_RTC_TIMER
+int __connman_rtctimer_init(void);
+void __connman_rtctimer_cleanup(void);
+#endif
+
 typedef void (* dhcp_cb) (struct connman_network *network,
                                connman_bool_t success);
 int __connman_dhcp_start(struct connman_network *network, dhcp_cb callback);
 void __connman_dhcp_stop(struct connman_network *network);
 int __connman_dhcp_init(void);
 void __connman_dhcp_cleanup(void);
+int __connman_dhcpv6_init(void);
+void __connman_dhcpv6_cleanup(void);
+int __connman_dhcpv6_start_info(struct connman_network *network,
+                               dhcp_cb callback);
+void __connman_dhcpv6_stop(struct connman_network *network);
+int __connman_dhcpv6_start(struct connman_network *network,
+                               GSList *prefixes, dhcp_cb callback);
+int __connman_dhcpv6_start_renew(struct connman_network *network,
+                               dhcp_cb callback);
+int __connman_dhcpv6_start_release(struct connman_network *network,
+                               dhcp_cb callback);
 
 int __connman_ipv4_init(void);
 void __connman_ipv4_cleanup(void);
@@ -290,14 +421,14 @@ int __connman_connection_gateway_add(struct connman_service *service,
                                        const char *peer);
 void __connman_connection_gateway_remove(struct connman_service *service,
                                        enum connman_ipconfig_type type);
+int __connman_connection_get_vpn_index(int phy_index);
 
 gboolean __connman_connection_update_gateway(void);
 void __connman_connection_gateway_activate(struct connman_service *service,
                                        enum connman_ipconfig_type type);
 
-int __connman_ntp_start(const char *interface, const char *resolver,
-                                                       const char *server);
-void __connman_ntp_stop(const char *interface);
+int __connman_ntp_start(char *server);
+void __connman_ntp_stop();
 
 int __connman_wpad_init(void);
 void __connman_wpad_cleanup(void);
@@ -312,16 +443,16 @@ void __connman_wispr_stop(struct connman_service *service);
 
 #include <connman/technology.h>
 
-void __connman_technology_list(DBusMessageIter *iter, void *user_data);
+void __connman_technology_list_struct(DBusMessageIter *array);
 
 int __connman_technology_add_device(struct connman_device *device);
 int __connman_technology_remove_device(struct connman_device *device);
 int __connman_technology_enabled(enum connman_service_type type);
-int __connman_technology_enable(enum connman_service_type type, DBusMessage *msg);
 int __connman_technology_disabled(enum connman_service_type type);
-int __connman_technology_disable(enum connman_service_type type, DBusMessage *msg);
 int __connman_technology_set_offlinemode(connman_bool_t offlinemode);
 connman_bool_t __connman_technology_get_offlinemode(void);
+void __connman_technology_set_connected(enum connman_service_type type,
+                                       connman_bool_t connected);
 
 int __connman_technology_add_rfkill(unsigned int index,
                                        enum connman_service_type type,
@@ -334,6 +465,8 @@ int __connman_technology_update_rfkill(unsigned int index,
 int __connman_technology_remove_rfkill(unsigned int index,
                                        enum connman_service_type type);
 
+void __connman_technology_scan_started(struct connman_device *device);
+void __connman_technology_scan_stopped(struct connman_device *device);
 void __connman_technology_add_interface(enum connman_service_type type,
                                int index, const char *name, const char *ident);
 void __connman_technology_remove_interface(enum connman_service_type type,
@@ -349,13 +482,13 @@ void __connman_device_list(DBusMessageIter *iter, void *user_data);
 enum connman_service_type __connman_device_get_service_type(struct connman_device *device);
 struct connman_device *__connman_device_find_device(enum connman_service_type type);
 int __connman_device_request_scan(enum connman_service_type type);
+int __connman_device_request_hidden_scan(struct connman_device *device,
+                               const char *ssid, unsigned int ssid_len,
+                               const char *identity, const char *passphrase,
+                               gpointer user_data);
 
 connman_bool_t __connman_device_isfiltered(const char *devname);
 
-int __connman_device_get_phyindex(struct connman_device *device);
-void __connman_device_set_phyindex(struct connman_device *device,
-                                                       int phyindex);
-
 void __connman_device_set_network(struct connman_device *device,
                                        struct connman_network *network);
 void __connman_device_cleanup_networks(struct connman_device *device);
@@ -392,8 +525,6 @@ int __connman_network_set_ipconfig(struct connman_network *network,
                                struct connman_ipconfig *ipconfig_ipv4,
                                struct connman_ipconfig *ipconfig_ipv6);
 
-connman_bool_t __connman_network_has_driver(struct connman_network *network);
-
 const char *__connman_network_get_type(struct connman_network *network);
 const char *__connman_network_get_group(struct connman_network *network);
 const char *__connman_network_get_ident(struct connman_network *network);
@@ -405,13 +536,12 @@ void __connman_config_cleanup(void);
 int __connman_config_load_service(GKeyFile *keyfile, const char *group, connman_bool_t persistent);
 int __connman_config_provision_service(struct connman_service *service);
 int __connman_config_provision_service_ident(struct connman_service *service,
-                                                       const char *ident);
+               const char *ident, const char *file, const char *entry);
 
 int __connman_tethering_init(void);
 void __connman_tethering_cleanup(void);
 
 const char *__connman_tethering_get_bridge(void);
-void __connman_tethering_update_interface(const char *interface);
 void __connman_tethering_set_enabled(void);
 void __connman_tethering_set_disabled(void);
 
@@ -420,6 +550,9 @@ int __connman_private_network_release(const char *path);
 
 #include <connman/provider.h>
 
+connman_bool_t __connman_provider_check_routes(struct connman_provider *provider);
+int __connman_provider_append_user_route(struct connman_provider *provider,
+                       int family, const char *network, const char *netmask);
 void __connman_provider_append_properties(struct connman_provider *provider, DBusMessageIter *iter);
 void __connman_provider_list(DBusMessageIter *iter, void *user_data);
 int __connman_provider_create_and_connect(DBusMessage *msg);
@@ -429,7 +562,6 @@ int __connman_provider_indicate_state(struct connman_provider *provider,
 int __connman_provider_indicate_error(struct connman_provider *provider,
                                        enum connman_provider_error error);
 int __connman_provider_connect(struct connman_provider *provider);
-int __connman_provider_disconnect(struct connman_provider *provider);
 int __connman_provider_remove(const char *path);
 void __connman_provider_cleanup(void);
 int __connman_provider_init(void);
@@ -439,16 +571,14 @@ int __connman_provider_init(void);
 int __connman_service_init(void);
 void __connman_service_cleanup(void);
 
-void __connman_service_list(DBusMessageIter *iter, void *user_data);
 void __connman_service_list_struct(DBusMessageIter *iter);
-const char *__connman_service_default(void);
 
-void __connman_service_put(struct connman_service *service);
-
-struct connman_service *__connman_service_lookup_from_network(struct connman_network *network);
 struct connman_service *__connman_service_lookup_from_index(int index);
+struct connman_service *__connman_service_lookup_from_ident(const char *identifier);
 struct connman_service *__connman_service_create_from_network(struct connman_network *network);
 struct connman_service *__connman_service_create_from_provider(struct connman_provider *provider);
+connman_bool_t __connman_service_index_is_default(int index);
+struct connman_service *__connman_service_get_default(void);
 void __connman_service_update_from_network(struct connman_network *network);
 void __connman_service_remove_from_network(struct connman_network *network);
 void __connman_service_read_ip4config(struct connman_service *service);
@@ -463,20 +593,37 @@ struct connman_ipconfig *__connman_service_get_ip6config(
                                struct connman_service *service);
 struct connman_ipconfig *__connman_service_get_ipconfig(
                                struct connman_service *service, int family);
+connman_bool_t __connman_service_is_connected_state(struct connman_service *service,
+                                       enum connman_ipconfig_type type);
 const char *__connman_service_get_ident(struct connman_service *service);
 const char *__connman_service_get_path(struct connman_service *service);
 unsigned int __connman_service_get_order(struct connman_service *service);
+void __connman_service_update_ordering(void);
 struct connman_network *__connman_service_get_network(struct connman_service *service);
 enum connman_service_security __connman_service_get_security(struct connman_service *service);
 const char *__connman_service_get_phase2(struct connman_service *service);
 connman_bool_t __connman_service_wps_enabled(struct connman_service *service);
+#if defined TIZEN_EXT
+/*
+ * Returns profile count if there is any connected profiles
+ * that use same interface
+ */
+int __connman_service_get_connected_count_of_iface(struct connman_service *service);
+#endif
 int __connman_service_set_favorite(struct connman_service *service,
                                                connman_bool_t favorite);
+int __connman_service_set_favorite_delayed(struct connman_service *service,
+                                       connman_bool_t favorite,
+                                       gboolean delay_ordering);
 int __connman_service_set_immutable(struct connman_service *service,
                                                connman_bool_t immutable);
+void __connman_service_set_userconnect(struct connman_service *service,
+                                               connman_bool_t userconnect);
 
 void __connman_service_set_string(struct connman_service *service,
                                        const char *key, const char *value);
+int __connman_service_online_check_failed(struct connman_service *service,
+                                       enum connman_ipconfig_type type);
 int __connman_service_ipconfig_indicate_state(struct connman_service *service,
                                        enum connman_service_state new_state,
                                        enum connman_ipconfig_type type);
@@ -488,19 +635,24 @@ int __connman_service_indicate_error(struct connman_service *service,
                                        enum connman_service_error error);
 int __connman_service_clear_error(struct connman_service *service);
 int __connman_service_indicate_default(struct connman_service *service);
-int __connman_service_request_login(struct connman_service *service);
 
-int __connman_service_lookup(const char *pattern, const char **path);
 int __connman_service_connect(struct connman_service *service);
 int __connman_service_disconnect(struct connman_service *service);
 int __connman_service_disconnect_all(void);
-int __connman_service_create_and_connect(DBusMessage *msg);
-int __connman_service_provision(DBusMessage *msg);
 void __connman_service_auto_connect(void);
+gboolean __connman_service_remove(struct connman_service *service);
+void __connman_service_set_hidden_data(struct connman_service *service,
+                               gpointer user_data);
+void __connman_service_return_error(struct connman_service *service,
+                               int error, gpointer user_data);
+void __connman_service_reply_dbus_pending(DBusMessage *pending, int error);
 
-void __connman_service_provision_changed(const char *ident);
+int __connman_service_provision_changed(const char *ident);
+void __connman_service_set_config(struct connman_service *service,
+                               const char *file_id, const char *section);
 
 const char *__connman_service_type2string(enum connman_service_type type);
+enum connman_service_type __connman_service_string2type(const char *str);
 
 int __connman_service_nameserver_append(struct connman_service *service,
                                const char *nameserver, gboolean is_auto);
@@ -509,21 +661,21 @@ int __connman_service_nameserver_remove(struct connman_service *service,
 void __connman_service_nameserver_clear(struct connman_service *service);
 void __connman_service_nameserver_add_routes(struct connman_service *service,
                                                const char *gw);
-void __connman_service_nameserver_del_routes(struct connman_service *service);
+void __connman_service_nameserver_del_routes(struct connman_service *service,
+                                       enum connman_ipconfig_type type);
 int __connman_service_timeserver_append(struct connman_service *service,
                                                const char *timeserver);
 int __connman_service_timeserver_remove(struct connman_service *service,
                                                const char *timeserver);
+void __connman_service_timeserver_changed(struct connman_service *service,
+               GSList *ts_list);
 void __connman_service_set_pac(struct connman_service *service,
                                        const char *pac);
-#if defined TIZEN_EXT
-/*
- * Description: Telephony plug-in requires manual PROXY setting function
- */
-void __connman_service_set_proxy(struct connman_service *service,
-                                       const char *proxies);
-#endif
+connman_bool_t __connman_service_is_hidden(struct connman_service *service);
+connman_bool_t __connman_service_is_split_routing(struct connman_service *service);
+connman_bool_t __connman_service_index_is_split_routing(int index);
 int __connman_service_get_index(struct connman_service *service);
+void __connman_service_set_hidden(struct connman_service *service);
 void __connman_service_set_domainname(struct connman_service *service,
                                                const char *domainname);
 const char *__connman_service_get_domainname(struct connman_service *service);
@@ -535,8 +687,9 @@ void __connman_service_set_identity(struct connman_service *service,
                                        const char *identity);
 void __connman_service_set_agent_identity(struct connman_service *service,
                                                const char *agent_identity);
-void __connman_service_set_passphrase(struct connman_service *service,
-                                       const char* passphrase);
+int __connman_service_set_passphrase(struct connman_service *service,
+                                       const char *passphrase);
+const char *__connman_service_get_passphrase(struct connman_service *service);
 void __connman_service_set_agent_passphrase(struct connman_service *service,
                                                const char *agent_passphrase);
 
@@ -565,6 +718,8 @@ GSequence *__connman_service_get_list(struct connman_session *session,
 
 void __connman_service_session_inc(struct connman_service *service);
 connman_bool_t __connman_service_session_dec(struct connman_service *service);
+void __connman_service_mark_dirty();
+void __connman_service_save(struct connman_service *service);
 
 #include <connman/notifier.h>
 
@@ -574,23 +729,14 @@ void __connman_technology_cleanup(void);
 int __connman_notifier_init(void);
 void __connman_notifier_cleanup(void);
 
-void __connman_notifier_list_registered(DBusMessageIter *iter, void *user_data);
-void __connman_notifier_list_enabled(DBusMessageIter *iter, void *user_data);
-void __connman_notifier_list_connected(DBusMessageIter *iter, void *user_data);
-
-void __connman_notifier_register(enum connman_service_type type);
-void __connman_notifier_unregister(enum connman_service_type type);
 void __connman_notifier_service_add(struct connman_service *service,
                                        const char *name);
 void __connman_notifier_service_remove(struct connman_service *service);
-void __connman_notifier_enable(enum connman_service_type type);
-void __connman_notifier_disable(enum connman_service_type type);
+void __connman_notifier_enter_online(enum connman_service_type type);
+void __connman_notifier_leave_online(enum connman_service_type type);
 void __connman_notifier_connect(enum connman_service_type type);
 void __connman_notifier_disconnect(enum connman_service_type type);
 void __connman_notifier_offlinemode(connman_bool_t enabled);
-#if defined TIZEN_EXT
-void __connman_notifier_scan_completed(connman_bool_t success);
-#endif
 void __connman_notifier_default_changed(struct connman_service *service);
 void __connman_notifier_proxy_changed(struct connman_service *service);
 void __connman_notifier_service_state_changed(struct connman_service *service,
@@ -598,9 +744,7 @@ void __connman_notifier_service_state_changed(struct connman_service *service,
 void __connman_notifier_ipconfig_changed(struct connman_service *service,
                                        struct connman_ipconfig *ipconfig);
 
-connman_bool_t __connman_notifier_is_registered(enum connman_service_type type);
-connman_bool_t __connman_notifier_is_enabled(enum connman_service_type type);
-unsigned int __connman_notifier_count_connected(void);
+connman_bool_t __connman_notifier_is_connected(void);
 const char *__connman_notifier_get_state(void);
 
 #include <connman/rtnl.h>
@@ -655,12 +799,60 @@ int __connman_iptables_commit(const char *table_name);
 
 int __connman_dnsproxy_init(void);
 void __connman_dnsproxy_cleanup(void);
-int __connman_dnsproxy_add_listener(const char *interface);
-void __connman_dnsproxy_remove_listener(const char *interface);
-int __connman_dnsproxy_append(const char *interface, const char *domain, const char *server);
-int __connman_dnsproxy_remove(const char *interface, const char *domain, const char *server);
+int __connman_dnsproxy_add_listener(int index);
+void __connman_dnsproxy_remove_listener(int index);
+int __connman_dnsproxy_append(int index, const char *domain, const char *server);
+int __connman_dnsproxy_remove(int index, const char *domain, const char *server);
 void __connman_dnsproxy_flush(void);
 
 int __connman_6to4_probe(struct connman_service *service);
 void __connman_6to4_remove(struct connman_ipconfig *ipconfig);
 int __connman_6to4_check(struct connman_ipconfig *ipconfig);
+
+struct connman_ippool;
+
+typedef void (*ippool_collision_cb_t) (struct connman_ippool *pool,
+                                       void *user_data);
+
+int __connman_ippool_init(void);
+void __connman_ippool_cleanup(void);
+
+#define __connman_ippool_ref(ipconfig) \
+       __connman_ippool_ref_debug(ipconfig, __FILE__, __LINE__, __func__)
+#define __connman_ippool_unref(ipconfig) \
+       __connman_ippool_unref_debug(ipconfig, __FILE__, __LINE__, __func__)
+
+struct connman_ippool *__connman_ippool_ref_debug(struct connman_ippool *pool,
+                       const char *file, int line, const char *caller);
+void __connman_ippool_unref_debug(struct connman_ippool *pool,
+                       const char *file, int line, const char *caller);
+
+struct connman_ippool *__connman_ippool_create(int index,
+                                       unsigned int start,
+                                       unsigned int range,
+                                       ippool_collision_cb_t collision_cb,
+                                       void *user_data);
+
+const char *__connman_ippool_get_gateway(struct connman_ippool *pool);
+const char *__connman_ippool_get_broadcast(struct connman_ippool *pool);
+const char *__connman_ippool_get_subnet_mask(struct connman_ippool *pool);
+const char *__connman_ippool_get_start_ip(struct connman_ippool *pool);
+const char *__connman_ippool_get_end_ip(struct connman_ippool *pool);
+
+void __connman_ippool_newaddr(int index, const char *address,
+                               unsigned char prefixlen);
+void __connman_ippool_deladdr(int index, const char *address,
+                               unsigned char prefixlen);
+
+int __connman_bridge_create(const char *name);
+int __connman_bridge_remove(const char *name);
+int __connman_bridge_enable(const char *name, const char *gateway,
+                               const char *broadcast);
+int __connman_bridge_disable(const char *name);
+
+int __connman_nat_init(void);
+void __connman_nat_cleanup(void);
+
+int __connman_nat_enable(const char *name, const char *address,
+                               unsigned char prefixlen);
+void __connman_nat_disable(const char *name);
index a234a0c..2d2baaf 100644 (file)
@@ -1,11 +1,13 @@
 [Unit]
 Description=Connection service
-After=syslog.target
+After=tizen-system.target shutdown.target
 
 [Service]
 Type=dbus
 BusName=net.connman
-ExecStart=@prefix@/sbin/connmand -n
+RemainAfterExit=yes
+ExecStartPre=/usr/bin/dbus-send --system --dest=net.netconfig / net.netconfig.auto.activate
+ExecStart=/usr/sbin/connmand
 
 [Install]
 WantedBy=multi-user.target
index 7f1c014..76d91d4 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
@@ -56,7 +56,7 @@ static void remove_counter(gpointer user_data)
        g_free(counter);
 }
 
-static void owner_disconnect(DBusConnection *connection, void *user_data)
+static void owner_disconnect(DBusConnection *conn, void *user_data)
 {
        struct connman_counter *counter = user_data;
 
index fdcd886..2fb06f4 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
@@ -24,6 +24,7 @@
 #endif
 
 #include <string.h>
+#include <errno.h>
 #include <gdbus.h>
 
 #include "connman.h"
@@ -182,13 +183,32 @@ void connman_dbus_property_append_array(DBusMessageIter *iter,
 
        switch (type) {
        case DBUS_TYPE_STRING:
-               variant_sig = DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_STRING_AS_STRING;
+               variant_sig = DBUS_TYPE_ARRAY_AS_STRING
+                               DBUS_TYPE_STRING_AS_STRING;
                array_sig = DBUS_TYPE_STRING_AS_STRING;
                break;
        case DBUS_TYPE_OBJECT_PATH:
-               variant_sig = DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_OBJECT_PATH_AS_STRING;
+               variant_sig = DBUS_TYPE_ARRAY_AS_STRING
+                               DBUS_TYPE_OBJECT_PATH_AS_STRING;
                array_sig = DBUS_TYPE_OBJECT_PATH_AS_STRING;
                break;
+       case DBUS_TYPE_DICT_ENTRY:
+               variant_sig = DBUS_TYPE_ARRAY_AS_STRING
+                               DBUS_STRUCT_BEGIN_CHAR_AS_STRING
+                               DBUS_TYPE_ARRAY_AS_STRING
+                                       DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                                               DBUS_TYPE_STRING_AS_STRING
+                                               DBUS_TYPE_VARIANT_AS_STRING
+                                       DBUS_DICT_ENTRY_END_CHAR_AS_STRING
+                               DBUS_STRUCT_END_CHAR_AS_STRING;
+               array_sig = DBUS_STRUCT_BEGIN_CHAR_AS_STRING
+                               DBUS_TYPE_ARRAY_AS_STRING
+                                       DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                                               DBUS_TYPE_STRING_AS_STRING
+                                               DBUS_TYPE_VARIANT_AS_STRING
+                                       DBUS_DICT_ENTRY_END_CHAR_AS_STRING
+                               DBUS_STRUCT_END_CHAR_AS_STRING;
+               break;
        default:
                return;
        }
@@ -231,34 +251,6 @@ dbus_bool_t connman_dbus_property_changed_basic(const char *path,
        return TRUE;
 }
 
-#if defined TIZEN_EXT
-/*
- * Description: Extends state_changed signal to notify service error cause
- */
-dbus_bool_t connman_dbus_service_property_changed_with_error_cause(const char *path,
-                       const char *interface, const char *key1, int type1, void *val1,
-                       const char *key2, int type2, void *val2)
-{
-       DBusMessage *signal;
-       DBusMessageIter iter;
-
-       if (path == NULL)
-               return FALSE;
-
-       signal = dbus_message_new_signal(path, interface, "PropertyChanged");
-       if (signal == NULL)
-               return FALSE;
-
-       dbus_message_iter_init_append(signal, &iter);
-       connman_dbus_property_append_basic(&iter, key1, type1, val1);
-       connman_dbus_property_append_basic(&iter, key2, type2, val2);
-
-       g_dbus_send_message(connection, signal);
-
-       return TRUE;
-}
-#endif
-
 dbus_bool_t connman_dbus_property_changed_dict(const char *path,
                                const char *interface, const char *key,
                        connman_dbus_append_cb_t function, void *user_data)
@@ -390,37 +382,31 @@ dbus_bool_t connman_dbus_setting_changed_array(const char *owner,
        return TRUE;
 }
 
-#if defined TIZEN_EXT
-/*
- * August 22nd, 2011. TIZEN
- *
- * This part is added to send a DBus signal which means scan is completed
- * because scan UX of a Wi-Fi setting application has an active scan procedure
- * and it needs scan complete signal whether success or not
- */
-
-dbus_bool_t connman_dbus_scan_completed_basic(const char *path,
-                               const char *interface, int type, void *val)
+dbus_bool_t __connman_dbus_append_objpath_dict_array(DBusMessage *msg,
+               connman_dbus_append_cb_t function, void *user_data)
 {
-       DBusMessage *signal;
-       DBusMessageIter iter;
+       DBusMessageIter iter, array;
 
-       if (path == NULL)
+       if (msg == NULL || function == NULL)
                return FALSE;
 
-       signal = dbus_message_new_signal(path, interface, "ScanCompleted");
-       if (signal == NULL)
-               return FALSE;
+       dbus_message_iter_init_append(msg, &iter);
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                       DBUS_STRUCT_BEGIN_CHAR_AS_STRING
+                       DBUS_TYPE_OBJECT_PATH_AS_STRING
+                       DBUS_TYPE_ARRAY_AS_STRING
+                               DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                                       DBUS_TYPE_STRING_AS_STRING
+                                       DBUS_TYPE_VARIANT_AS_STRING
+                               DBUS_DICT_ENTRY_END_CHAR_AS_STRING
+                       DBUS_STRUCT_END_CHAR_AS_STRING, &array);
 
-       dbus_message_iter_init_append(signal, &iter);
-       
-       dbus_message_iter_append_basic(&iter, type, val);
+       function(&array, user_data);
 
-       g_dbus_send_message(connection, signal);
+       dbus_message_iter_close_container(&iter, &array);
 
        return TRUE;
 }
-#endif
 
 DBusConnection *connman_dbus_get_connection(void)
 {
index 39ab06a..f8e7070 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
@@ -56,7 +56,6 @@ static void detect_newlink(unsigned short type, int index,
        switch (devtype) {
        case CONNMAN_DEVICE_TYPE_UNKNOWN:
        case CONNMAN_DEVICE_TYPE_VENDOR:
-       case CONNMAN_DEVICE_TYPE_WIMAX:
        case CONNMAN_DEVICE_TYPE_BLUETOOTH:
        case CONNMAN_DEVICE_TYPE_CELLULAR:
        case CONNMAN_DEVICE_TYPE_GPS:
@@ -68,8 +67,15 @@ static void detect_newlink(unsigned short type, int index,
        }
 
        device = find_device(index);
+#if defined TIZEN_EXT
+       if (device != NULL) {
+               connman_inet_update_device_ident(device);
+               return;
+       }
+#else
        if (device != NULL)
                return;
+#endif
 
        device = connman_inet_create_device(index);
        if (device == NULL)
@@ -80,7 +86,7 @@ static void detect_newlink(unsigned short type, int index,
                return;
        }
 
-       device_list = g_slist_append(device_list, device);
+       device_list = g_slist_prepend(device_list, device);
 }
 
 static void detect_dellink(unsigned short type, int index,
index af5c436..4ba1814 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
@@ -29,7 +29,7 @@
 #include "connman.h"
 
 #if defined TIZEN_EXT
-#define CONNMAN_SIG_WIFI_REFCOUNT_GROUP_NAME "connman_significant_wifi_profile_refcount"
+#define SIG_WIFI_REFCOUNT_NAME "connman_significant_wifi_refcount"
 #endif
 
 static GSList *device_list = NULL;
@@ -51,8 +51,6 @@ struct connman_device {
        connman_bool_t scanning;
        connman_bool_t disconnected;
        connman_bool_t reconnect;
-       connman_uint16_t scan_interval;
-       connman_uint16_t backoff_interval;
        char *name;
        char *node;
        char *address;
@@ -60,9 +58,7 @@ struct connman_device {
        char *ident;
        char *path;
        char *devname;
-       int phyindex;
        int index;
-       guint scan_timeout;
        guint pending_timeout;
 
        struct connman_device_driver *driver;
@@ -75,57 +71,37 @@ struct connman_device {
 #if defined TIZEN_EXT
        /* It contains number of favorite Wi-Fi profile.
         * If significant wifi profile is 0, Wi-Fi device does not trigger scan. */
-       gint significant_wifi_profile_refcount;
+       int significant_wifi_refcount;
 #endif
 };
 
-#define SCAN_INITIAL_DELAY 10
-
 #if defined TIZEN_EXT
-static void __connman_device_set_significant_wifi_profile_refcount(struct connman_device *device, gint refcount)
+static void __connman_device_set_sig_wifi_profile_refcount(
+                               struct connman_device *device, int refcount)
 {
-       g_atomic_int_set(&device->significant_wifi_profile_refcount, refcount);
+       __sync_synchronize();
+       device->significant_wifi_refcount = refcount;
 }
 
-void connman_device_significant_wifi_profile_ref(struct connman_device *device)
+void connman_device_sig_wifi_profile_ref(struct connman_device *device)
 {
-       g_atomic_int_inc(&device->significant_wifi_profile_refcount);
+       __sync_fetch_and_add(&device->significant_wifi_refcount, 1);
 }
 
-connman_bool_t connman_device_significant_wifi_profile_unref_and_test(struct connman_device *device)
+connman_bool_t connman_device_sig_wifi_profile_unref_and_test(
+                               struct connman_device *device)
 {
-       if (device->significant_wifi_profile_refcount > 0)
-               return (connman_bool_t)g_atomic_int_dec_and_test(&device->significant_wifi_profile_refcount);
+       __sync_synchronize();
+       if (device->significant_wifi_refcount < 1)
+               return TRUE;
+
+       if (__sync_sub_and_fetch(&device->significant_wifi_refcount, 1) == 0)
+               return TRUE;
 
        return FALSE;
 }
 #endif
 
-static gboolean device_scan_trigger(gpointer user_data)
-{
-       struct connman_device *device = user_data;
-
-       DBG("device %p", device);
-
-       if (device->driver == NULL) {
-               device->scan_timeout = 0;
-               return FALSE;
-       }
-
-       if (device->driver->scan)
-               device->driver->scan(device);
-
-       return TRUE;
-}
-
-static void clear_scan_trigger(struct connman_device *device)
-{
-       if (device->scan_timeout > 0) {
-               g_source_remove(device->scan_timeout);
-               device->scan_timeout = 0;
-       }
-}
-
 static void clear_pending_trigger(struct connman_device *device)
 {
        if (device->pending_timeout > 0) {
@@ -134,44 +110,6 @@ static void clear_pending_trigger(struct connman_device *device)
        }
 }
 
-static void reset_scan_trigger(struct connman_device *device)
-{
-       clear_scan_trigger(device);
-
-       if (device->scan_interval > 0) {
-               guint interval;
-
-               if (g_hash_table_size(device->networks) == 0) {
-                       if (device->backoff_interval >= device->scan_interval)
-                               device->backoff_interval = SCAN_INITIAL_DELAY;
-                       interval = device->backoff_interval;
-               } else
-                       interval = device->scan_interval;
-
-               DBG("interval %d", interval);
-
-               device->scan_timeout = g_timeout_add_seconds(interval,
-                                       device_scan_trigger, device);
-
-               device->backoff_interval *= 2;
-               if (device->backoff_interval > device->scan_interval)
-                       device->backoff_interval = device->scan_interval;
-       }
-}
-
-static void force_scan_trigger(struct connman_device *device)
-{
-       clear_scan_trigger(device);
-
-       device->scan_timeout = g_timeout_add_seconds(5,
-                                       device_scan_trigger, device);
-}
-
-void connman_device_schedule_scan(struct connman_device *device)
-{
-       reset_scan_trigger(device);
-}
-
 static const char *type2description(enum connman_device_type type)
 {
        switch (type) {
@@ -182,8 +120,6 @@ static const char *type2description(enum connman_device_type type)
                return "Ethernet";
        case CONNMAN_DEVICE_TYPE_WIFI:
                return "Wireless";
-       case CONNMAN_DEVICE_TYPE_WIMAX:
-               return "WiMAX";
        case CONNMAN_DEVICE_TYPE_BLUETOOTH:
                return "Bluetooth";
        case CONNMAN_DEVICE_TYPE_GPS:
@@ -208,8 +144,6 @@ static const char *type2string(enum connman_device_type type)
                return "ethernet";
        case CONNMAN_DEVICE_TYPE_WIFI:
                return "wifi";
-       case CONNMAN_DEVICE_TYPE_WIMAX:
-               return "wimax";
        case CONNMAN_DEVICE_TYPE_BLUETOOTH:
                return "bluetooth";
        case CONNMAN_DEVICE_TYPE_GPS:
@@ -237,8 +171,6 @@ enum connman_service_type __connman_device_get_service_type(struct connman_devic
                return CONNMAN_SERVICE_TYPE_ETHERNET;
        case CONNMAN_DEVICE_TYPE_WIFI:
                return CONNMAN_SERVICE_TYPE_WIFI;
-       case CONNMAN_DEVICE_TYPE_WIMAX:
-               return CONNMAN_SERVICE_TYPE_WIMAX;
        case CONNMAN_DEVICE_TYPE_BLUETOOTH:
                return CONNMAN_SERVICE_TYPE_BLUETOOTH;
        case CONNMAN_DEVICE_TYPE_CELLULAR:
@@ -318,9 +250,6 @@ int __connman_device_disable(struct connman_device *device)
 
        DBG("device %p", device);
 
-       if (!device->driver || !device->driver->disable)
-               return -EOPNOTSUPP;
-
        /* Ongoing power enable request */
        if (device->powered_pending == PENDING_ENABLE)
                return -EBUSY;
@@ -334,11 +263,9 @@ int __connman_device_disable(struct connman_device *device)
        device->powered_pending = PENDING_DISABLE;
        device->reconnect = FALSE;
 
-       clear_scan_trigger(device);
-
        if (device->network) {
                struct connman_service *service =
-                       __connman_service_lookup_from_network(device->network);
+                       connman_service_lookup_from_network(device->network);
 
                if (service != NULL)
                        __connman_service_disconnect(service);
@@ -346,13 +273,11 @@ int __connman_device_disable(struct connman_device *device)
                        connman_network_set_connected(device->network, FALSE);
        }
 
-       err = device->driver->disable(device);
-       if (err == 0) {
-               connman_device_set_powered(device, FALSE);
-               goto done;
-       }
+       if (!device->driver || !device->driver->disable)
+               return -EOPNOTSUPP;
 
-       if (err == -EALREADY) {
+       err = device->driver->disable(device);
+       if (err == 0 || err == -EALREADY) {
                connman_device_set_powered(device, FALSE);
                goto done;
        }
@@ -484,7 +409,6 @@ static void device_destruct(struct connman_device *device)
        DBG("device %p name %s", device, device->name);
 
        clear_pending_trigger(device);
-       clear_scan_trigger(device);
 
        g_free(device->ident);
        g_free(device->node);
@@ -503,32 +427,36 @@ static void device_destruct(struct connman_device *device)
 }
 
 #if defined TIZEN_EXT
-connman_bool_t connman_device_load_significant_wifi_profile_refcount_from_storage(struct connman_device *device)
+connman_bool_t connman_device_load_sig_wifi_profile_refcount_from_storage(
+                                               struct connman_device *device)
 {
        GKeyFile *keyfile = NULL;
 
        keyfile = __connman_storage_load_global();
 
-       if (g_key_file_has_group(keyfile, CONNMAN_SIG_WIFI_REFCOUNT_GROUP_NAME) == FALSE) {
+       if (g_key_file_has_group(keyfile, SIG_WIFI_REFCOUNT_NAME) == FALSE) {
                g_key_file_free(keyfile);
                return FALSE;
        }
 
-       __connman_device_set_significant_wifi_profile_refcount(device,
-                       g_key_file_get_integer(keyfile, CONNMAN_SIG_WIFI_REFCOUNT_GROUP_NAME, "ReferenceCount", NULL));
+       __connman_device_set_sig_wifi_profile_refcount(device,
+                       g_key_file_get_integer(keyfile, SIG_WIFI_REFCOUNT_NAME,
+                       "ReferenceCount", NULL));
 
        g_key_file_free(keyfile);
        return TRUE;
 }
 
-connman_bool_t connman_device_save_significant_wifi_profile_refcount_to_storage(struct connman_device *device)
+connman_bool_t connman_device_save_sig_wifi_profile_refcount2storage(
+                                               struct connman_device *device)
 {
        GKeyFile *keyfile = NULL;
 
        keyfile = __connman_storage_load_global();
 
-       g_key_file_set_integer(keyfile, CONNMAN_SIG_WIFI_REFCOUNT_GROUP_NAME, "ReferenceCount",
-                       device->significant_wifi_profile_refcount);
+       __sync_synchronize();
+       g_key_file_set_integer(keyfile, SIG_WIFI_REFCOUNT_NAME,
+                       "ReferenceCount", device->significant_wifi_refcount);
 
        __connman_storage_save_global(keyfile);
 
@@ -550,7 +478,6 @@ struct connman_device *connman_device_create(const char *node,
                                                enum connman_device_type type)
 {
        struct connman_device *device;
-       connman_bool_t bg_scan;
 
        DBG("node %s type %d", node, type);
 
@@ -562,48 +489,24 @@ struct connman_device *connman_device_create(const char *node,
 
        device->refcount = 1;
 
-       bg_scan = connman_setting_get_bool("BackgroundScanning");
-
 #if defined TIZEN_EXT
        if (type == CONNMAN_DEVICE_TYPE_WIFI) {
-               /* Load significant_wifi_profile_refcount */
-               if (connman_device_load_significant_wifi_profile_refcount_from_storage(device) == FALSE) {
-                       __connman_device_set_significant_wifi_profile_refcount(device, 0);
-                       connman_device_save_significant_wifi_profile_refcount_to_storage(device);
+               if (connman_device_load_sig_wifi_profile_refcount_from_storage(device)
+                                                                       == FALSE) {
+                       __connman_device_set_sig_wifi_profile_refcount(device, 0);
+
+                       connman_device_save_sig_wifi_profile_refcount2storage(device);
                }
        } else
-               __connman_device_set_significant_wifi_profile_refcount(device, 0);
+               __connman_device_set_sig_wifi_profile_refcount(device, 0);
 #endif
        device->type = type;
        device->name = g_strdup(type2description(device->type));
 
-       device->phyindex = -1;
-
-       device->backoff_interval = SCAN_INITIAL_DELAY;
-
-       switch (type) {
-       case CONNMAN_DEVICE_TYPE_UNKNOWN:
-       case CONNMAN_DEVICE_TYPE_ETHERNET:
-       case CONNMAN_DEVICE_TYPE_WIMAX:
-       case CONNMAN_DEVICE_TYPE_BLUETOOTH:
-       case CONNMAN_DEVICE_TYPE_CELLULAR:
-       case CONNMAN_DEVICE_TYPE_GPS:
-       case CONNMAN_DEVICE_TYPE_GADGET:
-       case CONNMAN_DEVICE_TYPE_VENDOR:
-               device->scan_interval = 0;
-               break;
-       case CONNMAN_DEVICE_TYPE_WIFI:
-               if (bg_scan == TRUE)
-                       device->scan_interval = 300;
-               else
-                       device->scan_interval = 0;
-               break;
-       }
-
        device->networks = g_hash_table_new_full(g_str_hash, g_str_equal,
                                                g_free, free_network);
 
-       device_list = g_slist_append(device_list, device);
+       device_list = g_slist_prepend(device_list, device);
 
        return device;
 }
@@ -614,9 +517,11 @@ struct connman_device *connman_device_create(const char *node,
  *
  * Increase reference counter of device
  */
-struct connman_device *connman_device_ref(struct connman_device *device)
+struct connman_device *connman_device_ref_debug(struct connman_device *device,
+                               const char *file, int line, const char *caller)
 {
-       DBG("%p", device);
+       DBG("%p ref %d by %s:%d:%s()", device, device->refcount + 1,
+               file, line, caller);
 
        __sync_fetch_and_add(&device->refcount, 1);
 
@@ -629,8 +534,12 @@ struct connman_device *connman_device_ref(struct connman_device *device)
  *
  * Decrease reference counter of device
  */
-void connman_device_unref(struct connman_device *device)
+void connman_device_unref_debug(struct connman_device *device,
+                               const char *file, int line, const char *caller)
 {
+       DBG("%p ref %d by %s:%d:%s()", device, device->refcount - 1,
+               file, line, caller);
+
        if (__sync_fetch_and_sub(&device->refcount, 1) != 1)
                return;
 
@@ -683,17 +592,6 @@ int connman_device_get_index(struct connman_device *device)
        return device->index;
 }
 
-int __connman_device_get_phyindex(struct connman_device *device)
-{
-       return device->phyindex;
-}
-
-void __connman_device_set_phyindex(struct connman_device *device,
-                                                       int phyindex)
-{
-       device->phyindex = phyindex;
-}
-
 /**
  * connman_device_set_interface:
  * @device: device structure
@@ -762,19 +660,16 @@ int connman_device_set_powered(struct connman_device *device,
 
        type = __connman_device_get_service_type(device);
 
-       if (device->powered == TRUE)
-               __connman_technology_enabled(type);
-       else
+       if (device->powered == FALSE) {
                __connman_technology_disabled(type);
-
-       if (powered == FALSE)
                return 0;
+       }
+
+       __connman_technology_enabled(type);
 
        connman_device_set_disconnected(device, FALSE);
        device->scanning = FALSE;
 
-       reset_scan_trigger(device);
-
        if (device->driver && device->driver->scan_fast)
                device->driver->scan_fast(device);
        else if (device->driver && device->driver->scan)
@@ -783,15 +678,14 @@ int connman_device_set_powered(struct connman_device *device,
        return 0;
 }
 
-static int device_scan(struct connman_device *device)
+connman_bool_t connman_device_get_powered(struct connman_device *device)
 {
-       if (!device->driver || !device->driver->scan)
-               return -EOPNOTSUPP;
-
-       if (device->powered == FALSE)
-               return -ENOLINK;
+       return device->powered;
+}
 
 #if defined TIZEN_EXT
+static gboolean allow_wifi_scan(struct connman_device *device)
+{
        GHashTableIter iter;
        gpointer key, value;
 
@@ -800,21 +694,30 @@ static int device_scan(struct connman_device *device)
        while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
                struct connman_network *network = value;
 
-               DBG("network type %s", __connman_network_get_type(network));
-               if (connman_network_get_type(network) != CONNMAN_NETWORK_TYPE_WIFI)
-                       continue;
+               if (connman_network_get_connecting(network) == TRUE)
+                       return FALSE;
 
-               /* If there is connecting network, don't try to scan. */
-               if (connman_network_get_connecting(network) == TRUE ||
-                               connman_network_get_associating(network) == TRUE) {
-                       DBG("network(%s) is connecting", connman_network_get_string(network, "Name"));
-                       return 0;
-               }
+               if (connman_network_get_associating(network) == TRUE)
+                       return FALSE;
        }
+
+       return TRUE;
+}
 #endif
 
-       reset_scan_trigger(device);
+static int device_scan(struct connman_device *device)
+{
+       if (!device->driver || !device->driver->scan)
+               return -EOPNOTSUPP;
+
+       if (device->powered == FALSE)
+               return -ENOLINK;
 
+#if defined TIZEN_EXT
+       if (device->type == CONNMAN_DEVICE_TYPE_WIFI &&
+                               allow_wifi_scan(device) == FALSE)
+               return -EBUSY;
+#endif
        return device->driver->scan(device);
 }
 
@@ -867,7 +770,8 @@ static void mark_network_unavailable(gpointer key, gpointer value,
 {
        struct connman_network *network = value;
 
-       if (connman_network_get_connected(network) == TRUE)
+       if (connman_network_get_connected(network) == TRUE ||
+                       connman_network_get_connecting(network) == TRUE)
                return;
 
        connman_network_set_available(network, FALSE);
@@ -900,21 +804,8 @@ connman_bool_t connman_device_get_scanning(struct connman_device *device)
 
 void connman_device_reset_scanning(struct connman_device *device)
 {
-       device->scanning = FALSE;
-
        g_hash_table_foreach(device->networks,
                                mark_network_available, NULL);
-
-#if defined TIZEN_EXT
-       /*
-        * August 22nd, 2011. TIZEN
-        *
-        * This part is added to send a DBus signal which means scan is completed
-        * because scan UX of a Wi-Fi setting application has an active scan procedure
-        * and it needs scan complete signal whether success or not
-        */
-       __connman_notifier_scan_completed(FALSE);
-#endif
 }
 
 /**
@@ -938,7 +829,7 @@ int connman_device_set_scanning(struct connman_device *device,
        device->scanning = scanning;
 
        if (scanning == TRUE) {
-               reset_scan_trigger(device);
+               __connman_technology_scan_started(device);
 
                g_hash_table_foreach(device->networks,
                                        mark_network_unavailable, NULL);
@@ -948,16 +839,7 @@ int connman_device_set_scanning(struct connman_device *device,
 
        __connman_device_cleanup_networks(device);
 
-#if defined TIZEN_EXT
-       /*
-        * August 22nd, 2011. TIZEN
-        *
-        * This part is added to send a DBus signal which means scan is completed
-        * because scan UX of a Wi-Fi setting application has an active scan procedure
-        * and it needs scan complete signal whether success or not
-        */
-       __connman_notifier_scan_completed(TRUE);
-#endif
+       __connman_technology_scan_stopped(device);
 
        __connman_service_auto_connect();
 
@@ -981,20 +863,6 @@ int connman_device_set_disconnected(struct connman_device *device,
 
        device->disconnected = disconnected;
 
-#if defined TIZEN_EXT
-       if (device->type == CONNMAN_DEVICE_TYPE_CELLULAR)
-       {
-               DBG("Cellular device does not need to scan.");
-               return 0;
-       }
-#endif
-
-       if (disconnected == TRUE)
-       {
-               force_scan_trigger(device);
-               device->backoff_interval = SCAN_INITIAL_DELAY;
-       }
-
        return 0;
 }
 
@@ -1088,7 +956,7 @@ int connman_device_add_network(struct connman_device *device,
 
        __connman_network_set_device(network, device);
 
-       g_hash_table_insert(device->networks, g_strdup(identifier),
+       g_hash_table_replace(device->networks, g_strdup(identifier),
                                                                network);
 
        return 0;
@@ -1104,7 +972,9 @@ int connman_device_add_network(struct connman_device *device,
 struct connman_network *connman_device_get_network(struct connman_device *device,
                                                        const char *identifier)
 {
+#if !defined TIZEN_EXT
        DBG("device %p identifier %s", device, identifier);
+#endif
 
        return g_hash_table_lookup(device->networks, identifier);
 }
@@ -1279,6 +1149,8 @@ struct connman_device *__connman_device_find_device(
 
 int __connman_device_request_scan(enum connman_service_type type)
 {
+       connman_bool_t success = FALSE;
+       int last_err = -ENOSYS;
        GSList *list;
        int err;
 
@@ -1291,9 +1163,8 @@ int __connman_device_request_scan(enum connman_service_type type)
        case CONNMAN_SERVICE_TYPE_GPS:
        case CONNMAN_SERVICE_TYPE_VPN:
        case CONNMAN_SERVICE_TYPE_GADGET:
-               return 0;
+               return -EOPNOTSUPP;
        case CONNMAN_SERVICE_TYPE_WIFI:
-       case CONNMAN_SERVICE_TYPE_WIMAX:
                break;
        }
 
@@ -1308,30 +1179,56 @@ int __connman_device_request_scan(enum connman_service_type type)
                }
 
                err = device_scan(device);
-               if (err < 0 && err != -EINPROGRESS) {
-                       DBG("err %d", err);
-                       /* XXX maybe only a continue? */
-                       return err;
+               if (err == 0 || err == -EALREADY || err == -EINPROGRESS) {
+                       success = TRUE;
+               } else {
+                       last_err = err;
+                       DBG("device %p err %d", device, err);
                }
        }
 
-       return 0;
+       if (success == TRUE)
+               return 0;
+
+       return last_err;
+}
+
+int __connman_device_request_hidden_scan(struct connman_device *device,
+                               const char *ssid, unsigned int ssid_len,
+                               const char *identity, const char *passphrase,
+                               void *user_data)
+{
+       DBG("device %p", device);
+
+       if (device == NULL || device->driver == NULL ||
+                       device->driver->scan_hidden == NULL)
+               return -EINVAL;
+
+       return device->driver->scan_hidden(device, ssid, ssid_len,
+                                       identity, passphrase, user_data);
 }
 
 connman_bool_t __connman_device_isfiltered(const char *devname)
 {
        char **pattern;
+       char **blacklisted_interfaces;
+       gboolean match;
 
        if (device_filter == NULL)
                goto nodevice;
 
-       for (pattern = device_filter; *pattern; pattern++) {
-               if (g_pattern_match_simple(*pattern, devname) == FALSE) {
-                       DBG("ignoring device %s (match)", devname);
-                       return TRUE;
+       for (pattern = device_filter, match = FALSE; *pattern; pattern++) {
+               if (g_pattern_match_simple(*pattern, devname) == TRUE) {
+                       match = TRUE;
+                       break;
                }
        }
 
+       if (match == FALSE) {
+               DBG("ignoring device %s (match)", devname);
+               return TRUE;
+       }
+
 nodevice:
        if (g_pattern_match_simple("dummy*", devname) == TRUE) {
                DBG("ignoring dummy networking devices");
@@ -1339,7 +1236,7 @@ nodevice:
        }
 
        if (nodevice_filter == NULL)
-               return FALSE;
+               goto list;
 
        for (pattern = nodevice_filter; *pattern; pattern++) {
                if (g_pattern_match_simple(*pattern, devname) == TRUE) {
@@ -1348,9 +1245,70 @@ nodevice:
                }
        }
 
+list:
+       blacklisted_interfaces =
+               connman_setting_get_string_list("NetworkInterfaceBlacklist");
+       if (blacklisted_interfaces == NULL)
+               return FALSE;
+
+       for (pattern = blacklisted_interfaces; *pattern; pattern++) {
+               if (g_str_has_prefix(devname, *pattern) == TRUE) {
+                       DBG("ignoring device %s (blacklist)", devname);
+                       return TRUE;
+               }
+       }
+
        return FALSE;
 }
 
+static void cleanup_devices(void)
+{
+       /*
+        * Check what interfaces are currently up and if connman is
+        * suppose to handle the interface, then cleanup the mess
+        * related to that interface. There might be weird routes etc
+        * that are related to that interface and that might confuse
+        * connmand. So in this case we just turn the interface down
+        * so that kernel removes routes/addresses automatically and
+        * then proceed the startup.
+        *
+        * Note that this cleanup must be done before rtnl/detect code
+        * has activated interface watches.
+        */
+
+       char **interfaces;
+       int i;
+
+       interfaces = __connman_inet_get_running_interfaces();
+
+       if (interfaces == NULL)
+               return;
+
+       for (i = 0; interfaces[i] != NULL; i++) {
+               connman_bool_t filtered;
+               int index;
+
+               filtered = __connman_device_isfiltered(interfaces[i]);
+               if (filtered == TRUE)
+                       continue;
+
+               index = connman_inet_ifindex(interfaces[i]);
+               if (index < 0)
+                       continue;
+
+               DBG("cleaning up %s index %d", interfaces[i], index);
+
+               connman_inet_ifdown(index);
+
+               /*
+                * ConnMan will turn the interface UP automatically so
+                * no need to do it here.
+                */
+       }
+
+       g_strfreev(interfaces);
+}
+
 int __connman_device_init(const char *device, const char *nodevice)
 {
        DBG("");
@@ -1361,6 +1319,8 @@ int __connman_device_init(const char *device, const char *nodevice)
        if (nodevice != NULL)
                nodevice_filter = g_strsplit(nodevice, ",", -1);
 
+       cleanup_devices();
+
        return 0;
 }
 
index 143ea1b..ef2b940 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
@@ -40,7 +40,7 @@ struct connman_dhcp {
        dhcp_cb callback;
 
        char **nameservers;
-       char *timeserver;
+       char **timeservers;
        char *pac;
 
        GDHCPClient *dhcp_client;
@@ -51,11 +51,11 @@ static GHashTable *network_table;
 static void dhcp_free(struct connman_dhcp *dhcp)
 {
        g_strfreev(dhcp->nameservers);
-       g_free(dhcp->timeserver);
+       g_strfreev(dhcp->timeservers);
        g_free(dhcp->pac);
 
        dhcp->nameservers = NULL;
-       dhcp->timeserver = NULL;
+       dhcp->timeservers = NULL;
        dhcp->pac = NULL;
 }
 
@@ -82,7 +82,7 @@ static void dhcp_invalidate(struct connman_dhcp *dhcp, connman_bool_t callback)
        if (dhcp == NULL)
                return;
 
-       service = __connman_service_lookup_from_network(dhcp->network);
+       service = connman_service_lookup_from_network(dhcp->network);
        if (service == NULL)
                goto out;
 
@@ -94,7 +94,13 @@ static void dhcp_invalidate(struct connman_dhcp *dhcp, connman_bool_t callback)
 
        __connman_service_set_domainname(service, NULL);
        __connman_service_set_pac(service, NULL);
-       __connman_service_timeserver_remove(service, dhcp->timeserver);
+
+       if (dhcp->timeservers != NULL) {
+               for (i = 0; dhcp->timeservers[i] != NULL; i++) {
+                       __connman_service_timeserver_remove(service,
+                                                       dhcp->timeservers[i]);
+               }
+       }
 
        if (dhcp->nameservers != NULL) {
                for (i = 0; dhcp->nameservers[i] != NULL; i++) {
@@ -181,7 +187,7 @@ static void lease_available_cb(GDHCPClient *dhcp_client, gpointer user_data)
        char *address, *netmask = NULL, *gateway = NULL;
        const char *c_address, *c_gateway;
        char *domainname = NULL, *hostname = NULL;
-       char **nameservers, *timeserver = NULL, *pac = NULL;
+       char **nameservers, **timeservers, *pac = NULL;
        int ns_entries;
        struct connman_ipconfig *ipconfig;
        struct connman_service *service;
@@ -191,7 +197,7 @@ static void lease_available_cb(GDHCPClient *dhcp_client, gpointer user_data)
 
        DBG("Lease available");
 
-       service = __connman_service_lookup_from_network(dhcp->network);
+       service = connman_service_lookup_from_network(dhcp->network);
        if (service == NULL) {
                connman_error("Can not lookup service");
                return;
@@ -238,8 +244,7 @@ static void lease_available_cb(GDHCPClient *dhcp_client, gpointer user_data)
                ip_change = FALSE;
 
        option = g_dhcp_client_get_option(dhcp_client, G_DHCP_DNS_SERVER);
-       for (ns_entries = 0, list = option; list; list = list->next)
-               ns_entries += 1;
+       ns_entries = g_list_length(option);
        nameservers = g_try_new0(char *, ns_entries + 1);
        if (nameservers != NULL) {
                for (i = 0, list = option; list; list = list->next, i++)
@@ -256,14 +261,19 @@ static void lease_available_cb(GDHCPClient *dhcp_client, gpointer user_data)
                hostname = g_strdup(option->data);
 
        option = g_dhcp_client_get_option(dhcp_client, G_DHCP_NTP_SERVER);
-       if (option != NULL)
-               timeserver = g_strdup(option->data);
+       ns_entries = g_list_length(option);
+       timeservers = g_try_new0(char *, ns_entries + 1);
+       if (timeservers != NULL) {
+               for (i = 0, list = option; list; list = list->next, i++)
+                       timeservers[i] = g_strdup(list->data);
+               timeservers[ns_entries] = NULL;
+       }
 
        option = g_dhcp_client_get_option(dhcp_client, 252);
        if (option != NULL)
                pac = g_strdup(option->data);
 
-       connman_ipconfig_set_method(ipconfig, CONNMAN_IPCONFIG_METHOD_DHCP);
+       __connman_ipconfig_set_method(ipconfig, CONNMAN_IPCONFIG_METHOD_DHCP);
 
        if (ip_change == TRUE) {
                __connman_ipconfig_set_local(ipconfig, address);
@@ -282,7 +292,8 @@ static void lease_available_cb(GDHCPClient *dhcp_client, gpointer user_data)
 
                dhcp->nameservers = nameservers;
 
-               for (i = 0; dhcp->nameservers[i] != NULL; i++) {
+               for (i = 0; dhcp->nameservers != NULL &&
+                                       dhcp->nameservers[i] != NULL; i++) {
                        __connman_service_nameserver_append(service,
                                                dhcp->nameservers[i], FALSE);
                }
@@ -290,18 +301,24 @@ static void lease_available_cb(GDHCPClient *dhcp_client, gpointer user_data)
                g_strfreev(nameservers);
        }
 
-       if (g_strcmp0(timeserver, dhcp->timeserver) != 0) {
-               if (dhcp->timeserver != NULL) {
-                       __connman_service_timeserver_remove(service,
-                                                       dhcp->timeserver);
-                       g_free(dhcp->timeserver);
+       if (compare_string_arrays(timeservers, dhcp->timeservers) == FALSE) {
+               if (dhcp->timeservers != NULL) {
+                       for (i = 0; dhcp->timeservers[i] != NULL; i++) {
+                               __connman_service_timeserver_remove(service,
+                                                       dhcp->timeservers[i]);
+                       }
+                       g_strfreev(dhcp->timeservers);
                }
 
-               dhcp->timeserver = timeserver;
+               dhcp->timeservers = timeservers;
 
-               if (dhcp->timeserver != NULL)
+               for (i = 0; dhcp->timeservers != NULL &&
+                                        dhcp->timeservers[i] != NULL; i++) {
                        __connman_service_timeserver_append(service,
-                                                       dhcp->timeserver);
+                                                       dhcp->timeservers[i]);
+               }
+       } else {
+               g_strfreev(timeservers);
        }
 
        if (g_strcmp0(pac, dhcp->pac) != 0) {
@@ -322,7 +339,10 @@ static void lease_available_cb(GDHCPClient *dhcp_client, gpointer user_data)
        if (ip_change == TRUE)
                dhcp_valid(dhcp);
 
+#if !defined TIZEN_EXT
+       /* Temporarily disable 6to4 */
        __connman_6to4_probe(service);
+#endif
 
        g_free(address);
        g_free(netmask);
@@ -334,36 +354,27 @@ static void lease_available_cb(GDHCPClient *dhcp_client, gpointer user_data)
 static void ipv4ll_available_cb(GDHCPClient *dhcp_client, gpointer user_data)
 {
        struct connman_dhcp *dhcp = user_data;
+#if !defined TIZEN_EXT
        char *address, *netmask;
        struct connman_service *service;
        struct connman_ipconfig *ipconfig;
        unsigned char prefixlen;
+#endif
 
        DBG("IPV4LL available");
 
 #if defined TIZEN_EXT
        /*
         * Description: When DHCP is failed,
-        *              most of naive users cannot understand auto-generated IP
-        *              (IPV4 link local) and serious troubles to make Internet connection.
+        *       most of normal users cannot understand auto-generated IP
+        *      (IPV4 link local) and serious troubles to make Internet connection.
         */
        dhcp_invalidate(dhcp, TRUE);
 
-       service = __connman_service_lookup_from_network(dhcp->network);
-       if (service == NULL)
-               return;
-
-       __connman_service_ipconfig_indicate_state(service,
-                       CONNMAN_SERVICE_STATE_IDLE,
-                       CONNMAN_IPCONFIG_TYPE_IPV4);
-       __connman_service_ipconfig_indicate_state(service,
-                       CONNMAN_SERVICE_STATE_IDLE,
-                       CONNMAN_IPCONFIG_TYPE_IPV6);
-
-       return;
-#endif
-
-       service = __connman_service_lookup_from_network(dhcp->network);
+       connman_network_set_error(dhcp->network,
+                               CONNMAN_NETWORK_ERROR_DHCP_FAIL);
+#else
+       service = connman_service_lookup_from_network(dhcp->network);
        if (service == NULL)
                return;
 
@@ -376,7 +387,7 @@ static void ipv4ll_available_cb(GDHCPClient *dhcp_client, gpointer user_data)
 
        prefixlen = __connman_ipconfig_netmask_prefix_len(netmask);
 
-       connman_ipconfig_set_method(ipconfig, CONNMAN_IPCONFIG_METHOD_DHCP);
+       __connman_ipconfig_set_method(ipconfig, CONNMAN_IPCONFIG_METHOD_DHCP);
        __connman_ipconfig_set_local(ipconfig, address);
        __connman_ipconfig_set_prefixlen(ipconfig, prefixlen);
        __connman_ipconfig_set_gateway(ipconfig, NULL);
@@ -385,6 +396,7 @@ static void ipv4ll_available_cb(GDHCPClient *dhcp_client, gpointer user_data)
 
        g_free(address);
        g_free(netmask);
+#endif
 }
 
 static void dhcp_debug(const char *str, void *data)
@@ -415,6 +427,8 @@ static int dhcp_request(struct connman_dhcp *dhcp)
        if (getenv("CONNMAN_DHCP_DEBUG"))
                g_dhcp_client_set_debug(dhcp_client, dhcp_debug, "DHCP");
 
+       g_dhcp_client_set_id(dhcp_client);
+
        hostname = connman_utsname_get_hostname();
        if (hostname != NULL)
                g_dhcp_client_set_send(dhcp_client, G_DHCP_HOST_NAME, hostname);
@@ -446,7 +460,7 @@ static int dhcp_request(struct connman_dhcp *dhcp)
 
        dhcp->dhcp_client = dhcp_client;
 
-       service = __connman_service_lookup_from_network(dhcp->network);
+       service = connman_service_lookup_from_network(dhcp->network);
        ipconfig = __connman_service_get_ip4config(service);
 
 #if defined TIZEN_EXT
@@ -469,9 +483,24 @@ static int dhcp_release(struct connman_dhcp *dhcp)
        if (dhcp->dhcp_client == NULL)
                return 0;
 
+#if !defined TIZEN_EXT
        g_dhcp_client_stop(dhcp->dhcp_client);
+#endif
        g_dhcp_client_unref(dhcp->dhcp_client);
 
+#if defined TIZEN_EXT
+       if (connman_setting_get_bool("WiFiDHCPRelease") == TRUE) {
+               struct connman_service *service =
+                               connman_service_lookup_from_network(dhcp->network);
+
+               struct connman_ipconfig *ipconfig =
+                               __connman_service_get_ip4config(service);
+
+               __connman_ipconfig_set_dhcp_address(ipconfig, NULL);
+               __connman_service_save(service);
+       }
+#endif
+
        dhcp->dhcp_client = NULL;
 
        return 0;
@@ -483,6 +512,10 @@ static void remove_network(gpointer user_data)
 
        DBG("dhcp %p", dhcp);
 
+#if defined TIZEN_EXT
+       if (dhcp->dhcp_client != NULL)
+               g_dhcp_client_stop(dhcp->dhcp_client);
+#endif
        dhcp_invalidate(dhcp, FALSE);
        dhcp_release(dhcp);
 
diff --git a/src/dhcpv6.c b/src/dhcpv6.c
new file mode 100644 (file)
index 0000000..496829f
--- /dev/null
@@ -0,0 +1,1437 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2012  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <connman/ipconfig.h>
+#include <connman/storage.h>
+
+#include <gdhcp/gdhcp.h>
+
+#include <glib.h>
+
+#include "connman.h"
+
+/* Transmission params in msec, RFC 3315 chapter 5.5 */
+#define INF_MAX_DELAY  (1 * 1000)
+#define INF_TIMEOUT    (1 * 1000)
+#define INF_MAX_RT     (120 * 1000)
+#define SOL_MAX_DELAY   (1 * 1000)
+#define SOL_TIMEOUT     (1 * 1000)
+#define SOL_MAX_RT      (120 * 1000)
+#define REQ_TIMEOUT    (1 * 1000)
+#define REQ_MAX_RT     (30 * 1000)
+#define REQ_MAX_RC     10
+#define REN_TIMEOUT     (10 * 1000)
+#define REN_MAX_RT      (600 * 1000)
+#define REB_TIMEOUT     (10 * 1000)
+#define REB_MAX_RT      (600 * 1000)
+#define CNF_MAX_DELAY   (1 * 1000)
+#define CNF_TIMEOUT    (1 * 1000)
+#define CNF_MAX_RT     (4 * 1000)
+#define CNF_MAX_RD     (10 * 1000)
+
+
+struct connman_dhcpv6 {
+       struct connman_network *network;
+       dhcp_cb callback;
+
+       char **nameservers;
+       char **timeservers;
+
+       GDHCPClient *dhcp_client;
+
+       guint timeout;          /* operation timeout in msec */
+       guint MRD;              /* max operation timeout in msec */
+       guint RT;               /* in msec */
+       gboolean use_ta;        /* set to TRUE if IPv6 privacy is enabled */
+       GSList *prefixes;       /* network prefixes from radvd */
+       int request_count;      /* how many times REQUEST have been sent */
+       gboolean stateless;     /* TRUE if stateless DHCPv6 is used */
+       gboolean started;       /* TRUE if we have DHCPv6 started */
+};
+
+static GHashTable *network_table;
+
+static int dhcpv6_request(struct connman_dhcpv6 *dhcp, gboolean add_addresses);
+
+static void clear_timer(struct connman_dhcpv6 *dhcp)
+{
+       if (dhcp->timeout > 0) {
+               g_source_remove(dhcp->timeout);
+               dhcp->timeout = 0;
+       }
+
+       if (dhcp->MRD > 0) {
+               g_source_remove(dhcp->MRD);
+               dhcp->MRD = 0;
+       }
+}
+
+static inline float get_random(void)
+{
+       return (rand() % 200 - 100) / 1000.0;
+}
+
+/* Calculate a random delay, RFC 3315 chapter 14 */
+/* RT and MRT are milliseconds */
+static guint calc_delay(guint RT, guint MRT)
+{
+       float delay = get_random();
+       float rt = RT * (2 + delay);
+
+       if (rt > MRT)
+               rt = MRT * (1 + delay);
+
+       if (rt < 0)
+               rt = MRT;
+
+       return (guint)rt;
+}
+
+static void free_prefix(gpointer data, gpointer user_data)
+{
+       g_free(data);
+}
+
+static void dhcpv6_free(struct connman_dhcpv6 *dhcp)
+{
+       g_strfreev(dhcp->nameservers);
+       g_strfreev(dhcp->timeservers);
+
+       dhcp->nameservers = NULL;
+       dhcp->timeservers = NULL;
+       dhcp->started = FALSE;
+
+       g_slist_foreach(dhcp->prefixes, free_prefix, NULL);
+       g_slist_free(dhcp->prefixes);
+}
+
+static gboolean compare_string_arrays(char **array_a, char **array_b)
+{
+       int i;
+
+       if (array_a == NULL || array_b == NULL)
+               return FALSE;
+
+       if (g_strv_length(array_a) != g_strv_length(array_b))
+               return FALSE;
+
+       for (i = 0; array_a[i] != NULL && array_b[i] != NULL; i++)
+               if (g_strcmp0(array_a[i], array_b[i]) != 0)
+                       return FALSE;
+
+       return TRUE;
+}
+
+static void dhcpv6_debug(const char *str, void *data)
+{
+       connman_info("%s: %s\n", (const char *) data, str);
+}
+
+static gchar *convert_to_hex(unsigned char *buf, int len)
+{
+       gchar *ret = g_try_malloc(len * 2 + 1);
+       int i;
+
+       for (i = 0; ret != NULL && i < len; i++)
+               g_snprintf(ret + i * 2, 3, "%02x", buf[i]);
+
+       return ret;
+}
+
+/*
+ * DUID should not change over time so save it to file.
+ * See RFC 3315 chapter 9 for details.
+ */
+static int set_duid(struct connman_service *service,
+                       struct connman_network *network,
+                       GDHCPClient *dhcp_client, int index)
+{
+       GKeyFile *keyfile;
+       const char *ident;
+       char *hex_duid;
+       unsigned char *duid;
+       int duid_len;
+
+       ident = __connman_service_get_ident(service);
+
+       keyfile = connman_storage_load_service(ident);
+       if (keyfile == NULL)
+               return -EINVAL;
+
+       hex_duid = g_key_file_get_string(keyfile, ident, "IPv6.DHCP.DUID",
+                                       NULL);
+       if (hex_duid != NULL) {
+               unsigned int i, j = 0, hex;
+               size_t hex_duid_len = strlen(hex_duid);
+
+               duid = g_try_malloc0(hex_duid_len / 2);
+               if (duid == NULL) {
+                       g_key_file_free(keyfile);
+                       g_free(hex_duid);
+                       return -ENOMEM;
+               }
+
+               for (i = 0; i < hex_duid_len; i += 2) {
+                       sscanf(hex_duid + i, "%02x", &hex);
+                       duid[j++] = hex;
+               }
+
+               duid_len = hex_duid_len / 2;
+       } else {
+               int ret;
+               int type = __connman_ipconfig_get_type_from_index(index);
+
+               ret = g_dhcpv6_create_duid(G_DHCPV6_DUID_LLT, index, type,
+                                       &duid, &duid_len);
+               if (ret < 0) {
+                       g_key_file_free(keyfile);
+                       return ret;
+               }
+
+               hex_duid = convert_to_hex(duid, duid_len);
+               if (hex_duid == NULL) {
+                       g_key_file_free(keyfile);
+                       return -ENOMEM;
+               }
+
+               g_key_file_set_string(keyfile, ident, "IPv6.DHCP.DUID",
+                               hex_duid);
+
+               __connman_storage_save_service(keyfile, ident);
+       }
+       g_free(hex_duid);
+
+       g_key_file_free(keyfile);
+
+       g_dhcpv6_client_set_duid(dhcp_client, duid, duid_len);
+
+       return 0;
+}
+
+static void clear_callbacks(GDHCPClient *dhcp_client)
+{
+       g_dhcp_client_register_event(dhcp_client,
+                               G_DHCP_CLIENT_EVENT_SOLICITATION,
+                               NULL, NULL);
+
+       g_dhcp_client_register_event(dhcp_client,
+                               G_DHCP_CLIENT_EVENT_ADVERTISE,
+                               NULL, NULL);
+
+       g_dhcp_client_register_event(dhcp_client,
+                               G_DHCP_CLIENT_EVENT_REQUEST,
+                               NULL, NULL);
+
+       g_dhcp_client_register_event(dhcp_client,
+                               G_DHCP_CLIENT_EVENT_CONFIRM,
+                               NULL, NULL);
+
+       g_dhcp_client_register_event(dhcp_client,
+                               G_DHCP_CLIENT_EVENT_RENEW,
+                               NULL, NULL);
+
+       g_dhcp_client_register_event(dhcp_client,
+                               G_DHCP_CLIENT_EVENT_REBIND,
+                               NULL, NULL);
+
+       g_dhcp_client_register_event(dhcp_client,
+                               G_DHCP_CLIENT_EVENT_RELEASE,
+                               NULL, NULL);
+
+       g_dhcp_client_register_event(dhcp_client,
+                               G_DHCP_CLIENT_EVENT_INFORMATION_REQ,
+                               NULL, NULL);
+}
+
+static void info_req_cb(GDHCPClient *dhcp_client, gpointer user_data)
+{
+       struct connman_dhcpv6 *dhcp = user_data;
+       struct connman_service *service;
+       int entries, i;
+       GList *option, *list;
+       char **nameservers, **timeservers;
+
+       DBG("dhcpv6 information-request %p", dhcp);
+
+       service = connman_service_lookup_from_network(dhcp->network);
+       if (service == NULL) {
+               connman_error("Can not lookup service");
+               return;
+       }
+
+       g_dhcpv6_client_clear_retransmit(dhcp_client);
+
+       option = g_dhcp_client_get_option(dhcp_client, G_DHCPV6_DNS_SERVERS);
+       entries = g_list_length(option);
+
+       nameservers = g_try_new0(char *, entries + 1);
+       if (nameservers != NULL) {
+               for (i = 0, list = option; list; list = list->next, i++)
+                       nameservers[i] = g_strdup(list->data);
+       }
+
+       if (compare_string_arrays(nameservers, dhcp->nameservers) == FALSE) {
+               if (dhcp->nameservers != NULL) {
+                       for (i = 0; dhcp->nameservers[i] != NULL; i++)
+                               __connman_service_nameserver_remove(service,
+                                                       dhcp->nameservers[i],
+                                                       FALSE);
+                       g_strfreev(dhcp->nameservers);
+               }
+
+               dhcp->nameservers = nameservers;
+
+               for (i = 0; dhcp->nameservers != NULL &&
+                                       dhcp->nameservers[i] != NULL; i++)
+                       __connman_service_nameserver_append(service,
+                                               dhcp->nameservers[i],
+                                               FALSE);
+       } else
+               g_strfreev(nameservers);
+
+
+       option = g_dhcp_client_get_option(dhcp_client, G_DHCPV6_SNTP_SERVERS);
+       entries = g_list_length(option);
+
+       timeservers = g_try_new0(char *, entries + 1);
+       if (timeservers != NULL) {
+               for (i = 0, list = option; list; list = list->next, i++)
+                       timeservers[i] = g_strdup(list->data);
+       }
+
+       if (compare_string_arrays(timeservers, dhcp->timeservers) == FALSE) {
+               if (dhcp->timeservers != NULL) {
+                       for (i = 0; dhcp->timeservers[i] != NULL; i++)
+                               __connman_service_timeserver_remove(service,
+                                                       dhcp->timeservers[i]);
+                       g_strfreev(dhcp->timeservers);
+               }
+
+               dhcp->timeservers = timeservers;
+
+               for (i = 0; dhcp->timeservers != NULL &&
+                                       dhcp->timeservers[i] != NULL; i++)
+                       __connman_service_timeserver_append(service,
+                                                       dhcp->timeservers[i]);
+       } else
+               g_strfreev(timeservers);
+
+
+       if (dhcp->callback != NULL) {
+               uint16_t status = g_dhcpv6_client_get_status(dhcp_client);
+               dhcp->callback(dhcp->network, status == 0 ? TRUE : FALSE);
+       }
+}
+
+static int dhcpv6_info_request(struct connman_dhcpv6 *dhcp)
+{
+       struct connman_service *service;
+       GDHCPClient *dhcp_client;
+       GDHCPClientError error;
+       int index, ret;
+
+       DBG("dhcp %p", dhcp);
+
+       index = connman_network_get_index(dhcp->network);
+
+       dhcp_client = g_dhcp_client_new(G_DHCP_IPV6, index, &error);
+       if (error != G_DHCP_CLIENT_ERROR_NONE) {
+               clear_timer(dhcp);
+               return -EINVAL;
+       }
+
+       if (getenv("CONNMAN_DHCPV6_DEBUG"))
+               g_dhcp_client_set_debug(dhcp_client, dhcpv6_debug, "DHCPv6");
+
+       service = connman_service_lookup_from_network(dhcp->network);
+       if (service == NULL) {
+               clear_timer(dhcp);
+               g_dhcp_client_unref(dhcp_client);
+               return -EINVAL;
+       }
+
+       ret = set_duid(service, dhcp->network, dhcp_client, index);
+       if (ret < 0) {
+               clear_timer(dhcp);
+               g_dhcp_client_unref(dhcp_client);
+               return ret;
+       }
+
+       g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
+       g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
+       g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
+
+       g_dhcpv6_client_set_oro(dhcp_client, 2, G_DHCPV6_DNS_SERVERS,
+                               G_DHCPV6_SNTP_SERVERS);
+
+       g_dhcp_client_register_event(dhcp_client,
+                       G_DHCP_CLIENT_EVENT_INFORMATION_REQ, info_req_cb, dhcp);
+
+       dhcp->dhcp_client = dhcp_client;
+
+       return g_dhcp_client_start(dhcp_client, NULL);
+}
+
+static int check_ipv6_addr_prefix(GSList *prefixes, char *address)
+{
+       struct in6_addr addr_prefix, addr;
+       GSList *list;
+       int ret = 128, len;
+
+       for (list = prefixes; list; list = list->next) {
+               char *prefix = list->data;
+               const char *slash = g_strrstr(prefix, "/");
+               const unsigned char bits[] = { 0x00, 0xFE, 0xFC, 0xF8,
+                                               0xF0, 0xE0, 0xC0, 0x80 };
+               int left, count, i, plen;
+
+               if (slash == NULL)
+                       continue;
+
+               prefix = g_strndup(prefix, slash - prefix);
+               len = strtol(slash + 1, NULL, 10);
+               plen = 128 - len;
+
+               count = plen / 8;
+               left = plen % 8;
+               i = 16 - count;
+
+               inet_pton(AF_INET6, prefix, &addr_prefix);
+               inet_pton(AF_INET6, address, &addr);
+
+               memset(&addr_prefix.s6_addr[i], 0, count);
+               memset(&addr.s6_addr[i], 0, count);
+
+               if (left) {
+                       addr_prefix.s6_addr[i - 1] &= bits[left];
+                       addr.s6_addr[i - 1] &= bits[left];
+               }
+
+               g_free(prefix);
+
+               if (memcmp(&addr_prefix, &addr, 16) == 0) {
+                       ret = len;
+                       break;
+               }
+       }
+
+       return ret;
+}
+
+static int set_addresses(GDHCPClient *dhcp_client,
+                                               struct connman_dhcpv6 *dhcp)
+{
+       struct connman_service *service;
+       struct connman_ipconfig *ipconfig;
+       int entries, i;
+       GList *option, *list;
+       char **nameservers, **timeservers;
+       const char *c_address;
+       char *address = NULL;
+
+       service = connman_service_lookup_from_network(dhcp->network);
+       if (service == NULL) {
+               connman_error("Can not lookup service");
+               return -EINVAL;
+       }
+
+       ipconfig = __connman_service_get_ip6config(service);
+       if (ipconfig == NULL) {
+               connman_error("Could not lookup ip6config");
+               return -EINVAL;
+       }
+
+       option = g_dhcp_client_get_option(dhcp_client, G_DHCPV6_DNS_SERVERS);
+       entries = g_list_length(option);
+
+       nameservers = g_try_new0(char *, entries + 1);
+       if (nameservers != NULL) {
+               for (i = 0, list = option; list; list = list->next, i++)
+                       nameservers[i] = g_strdup(list->data);
+       }
+
+       if (compare_string_arrays(nameservers, dhcp->nameservers) == FALSE) {
+               if (dhcp->nameservers != NULL) {
+                       for (i = 0; dhcp->nameservers[i] != NULL; i++)
+                               __connman_service_nameserver_remove(service,
+                                                       dhcp->nameservers[i],
+                                                       FALSE);
+                       g_strfreev(dhcp->nameservers);
+               }
+
+               dhcp->nameservers = nameservers;
+
+               for (i = 0; dhcp->nameservers != NULL &&
+                                       dhcp->nameservers[i] != NULL; i++)
+                       __connman_service_nameserver_append(service,
+                                                       dhcp->nameservers[i],
+                                                       FALSE);
+       } else
+               g_strfreev(nameservers);
+
+
+       option = g_dhcp_client_get_option(dhcp_client, G_DHCPV6_SNTP_SERVERS);
+       entries = g_list_length(option);
+
+       timeservers = g_try_new0(char *, entries + 1);
+       if (timeservers != NULL) {
+               for (i = 0, list = option; list; list = list->next, i++)
+                       timeservers[i] = g_strdup(list->data);
+       }
+
+       if (compare_string_arrays(timeservers, dhcp->timeservers) == FALSE) {
+               if (dhcp->timeservers != NULL) {
+                       for (i = 0; dhcp->timeservers[i] != NULL; i++)
+                               __connman_service_timeserver_remove(service,
+                                                       dhcp->timeservers[i]);
+                       g_strfreev(dhcp->timeservers);
+               }
+
+               dhcp->timeservers = timeservers;
+
+               for (i = 0; dhcp->timeservers != NULL &&
+                                       dhcp->timeservers[i] != NULL; i++)
+                       __connman_service_timeserver_append(service,
+                                                       dhcp->timeservers[i]);
+       } else
+               g_strfreev(timeservers);
+
+
+       option = g_dhcp_client_get_option(dhcp_client, G_DHCPV6_IA_NA);
+       if (option != NULL)
+               address = g_strdup(option->data);
+       else {
+               option = g_dhcp_client_get_option(dhcp_client, G_DHCPV6_IA_TA);
+               if (option != NULL)
+                       address = g_strdup(option->data);
+       }
+
+       c_address = __connman_ipconfig_get_local(ipconfig);
+
+       if (address != NULL &&
+                       ((c_address != NULL &&
+                               g_strcmp0(address, c_address) != 0) ||
+                       (c_address == NULL))) {
+               int prefix_len;
+
+               /* Is this prefix part of the subnet we are suppose to use? */
+               prefix_len = check_ipv6_addr_prefix(dhcp->prefixes, address);
+
+               __connman_ipconfig_set_local(ipconfig, address);
+               __connman_ipconfig_set_prefixlen(ipconfig, prefix_len);
+
+               DBG("new address %s/%d", address, prefix_len);
+
+               __connman_ipconfig_set_dhcp_address(ipconfig, address);
+               __connman_service_save(service);
+       }
+
+       g_free(address);
+
+       return 0;
+}
+
+static void re_cb(GDHCPClient *dhcp_client, gpointer user_data)
+{
+       struct connman_dhcpv6 *dhcp = user_data;
+       uint16_t status;
+       int ret;
+
+       ret = set_addresses(dhcp_client, dhcp);
+
+       status = g_dhcpv6_client_get_status(dhcp_client);
+
+       DBG("dhcpv6 cb msg %p ret %d status %d", dhcp, ret, status);
+
+       if (ret < 0) {
+               if (dhcp->callback != NULL)
+                       dhcp->callback(dhcp->network, FALSE);
+               return;
+       }
+
+       if (status  == G_DHCPV6_ERROR_BINDING) {
+               /* RFC 3315, 18.1.8 */
+               dhcpv6_request(dhcp, FALSE);
+       } else {
+               if (dhcp->callback != NULL)
+                       dhcp->callback(dhcp->network,
+                                               status == 0 ? TRUE : FALSE);
+       }
+}
+
+static void rebind_cb(GDHCPClient *dhcp_client, gpointer user_data)
+{
+       DBG("");
+
+       g_dhcpv6_client_reset_rebind(dhcp_client);
+       g_dhcpv6_client_reset_renew(dhcp_client);
+       g_dhcpv6_client_clear_retransmit(dhcp_client);
+
+       re_cb(dhcp_client, user_data);
+}
+
+static int dhcpv6_rebind(struct connman_dhcpv6 *dhcp)
+{
+       GDHCPClient *dhcp_client;
+
+       DBG("dhcp %p", dhcp);
+
+       dhcp_client = dhcp->dhcp_client;
+
+       g_dhcp_client_clear_requests(dhcp_client);
+
+       g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
+       g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
+       g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
+
+       g_dhcpv6_client_set_oro(dhcp_client, 2, G_DHCPV6_DNS_SERVERS,
+                               G_DHCPV6_SNTP_SERVERS);
+
+       g_dhcpv6_client_set_ia(dhcp_client,
+                       connman_network_get_index(dhcp->network),
+                       dhcp->use_ta == TRUE ? G_DHCPV6_IA_TA : G_DHCPV6_IA_NA,
+                       NULL, NULL, FALSE, NULL);
+
+       clear_callbacks(dhcp_client);
+
+       g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_REBIND,
+                                       rebind_cb, dhcp);
+
+       dhcp->dhcp_client = dhcp_client;
+
+       return g_dhcp_client_start(dhcp_client, NULL);
+}
+
+static gboolean dhcpv6_restart(gpointer user_data)
+{
+       struct connman_dhcpv6 *dhcp = user_data;
+
+       if (dhcp->callback != NULL)
+               dhcp->callback(dhcp->network, FALSE);
+
+       return FALSE;
+}
+
+/*
+ * Check if we need to restart the solicitation procedure. This
+ * is done if all the addresses have expired. RFC 3315, 18.1.4
+ */
+static int check_restart(struct connman_dhcpv6 *dhcp)
+{
+       time_t current, expired;
+
+       g_dhcpv6_client_get_timeouts(dhcp->dhcp_client, NULL, NULL,
+                               NULL, NULL, &expired);
+       current = time(NULL);
+
+       if (current > expired) {
+               DBG("expired by %d secs", (int)(current - expired));
+
+               g_timeout_add(0, dhcpv6_restart, dhcp);
+
+               return -ETIMEDOUT;
+       }
+
+       return 0;
+}
+
+static gboolean timeout_rebind(gpointer user_data)
+{
+       struct connman_dhcpv6 *dhcp = user_data;
+
+       if (check_restart(dhcp) < 0)
+               return FALSE;
+
+       dhcp->RT = calc_delay(dhcp->RT, REB_MAX_RT);
+
+       DBG("rebind RT timeout %d msec", dhcp->RT);
+
+       dhcp->timeout = g_timeout_add(dhcp->RT, timeout_rebind, dhcp);
+
+       g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
+
+       g_dhcp_client_start(dhcp->dhcp_client, NULL);
+
+       return FALSE;
+}
+
+static gboolean start_rebind(gpointer user_data)
+{
+       struct connman_dhcpv6 *dhcp = user_data;
+
+       dhcp->RT = REB_TIMEOUT * (1 + get_random());
+
+       DBG("rebind initial RT timeout %d msec", dhcp->RT);
+
+       dhcp->timeout = g_timeout_add(dhcp->RT, timeout_rebind, dhcp);
+
+       dhcpv6_rebind(dhcp);
+
+       return FALSE;
+}
+
+static void request_cb(GDHCPClient *dhcp_client, gpointer user_data)
+{
+       DBG("");
+
+       g_dhcpv6_client_clear_retransmit(dhcp_client);
+
+       re_cb(dhcp_client, user_data);
+}
+
+static int dhcpv6_request(struct connman_dhcpv6 *dhcp,
+                       gboolean add_addresses)
+{
+       GDHCPClient *dhcp_client;
+       uint32_t T1, T2;
+
+       DBG("dhcp %p add %d", dhcp, add_addresses);
+
+       dhcp_client = dhcp->dhcp_client;
+
+       g_dhcp_client_clear_requests(dhcp_client);
+
+       g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
+       g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SERVERID);
+       g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
+       g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
+
+       g_dhcpv6_client_set_oro(dhcp_client, 2, G_DHCPV6_DNS_SERVERS,
+                               G_DHCPV6_SNTP_SERVERS);
+
+       g_dhcpv6_client_get_timeouts(dhcp_client, &T1, &T2, NULL, NULL, NULL);
+       g_dhcpv6_client_set_ia(dhcp_client,
+                       connman_network_get_index(dhcp->network),
+                       dhcp->use_ta == TRUE ? G_DHCPV6_IA_TA : G_DHCPV6_IA_NA,
+                       &T1, &T2, add_addresses, NULL);
+
+       clear_callbacks(dhcp_client);
+
+       g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_REQUEST,
+                                       request_cb, dhcp);
+
+       dhcp->dhcp_client = dhcp_client;
+
+       return g_dhcp_client_start(dhcp_client, NULL);
+}
+
+static gboolean timeout_request(gpointer user_data)
+{
+       struct connman_dhcpv6 *dhcp = user_data;
+
+       if (dhcp->request_count >= REQ_MAX_RC) {
+               DBG("max request retry attempts %d", dhcp->request_count);
+               dhcp->request_count = 0;
+               if (dhcp->callback != NULL)
+                       dhcp->callback(dhcp->network, FALSE);
+               return FALSE;
+       }
+
+       dhcp->request_count++;
+
+       dhcp->RT = calc_delay(dhcp->RT, REQ_MAX_RT);
+       DBG("request RT timeout %d msec", dhcp->RT);
+       dhcp->timeout = g_timeout_add(dhcp->RT, timeout_request, dhcp);
+
+       g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
+
+       g_dhcp_client_start(dhcp->dhcp_client, NULL);
+
+       return FALSE;
+}
+
+static void renew_cb(GDHCPClient *dhcp_client, gpointer user_data)
+{
+       DBG("");
+
+       g_dhcpv6_client_reset_renew(dhcp_client);
+       g_dhcpv6_client_clear_retransmit(dhcp_client);
+
+       re_cb(dhcp_client, user_data);
+}
+
+static int dhcpv6_renew(struct connman_dhcpv6 *dhcp)
+{
+       GDHCPClient *dhcp_client;
+       uint32_t T1, T2;
+
+       DBG("dhcp %p", dhcp);
+
+       dhcp_client = dhcp->dhcp_client;
+
+       g_dhcp_client_clear_requests(dhcp_client);
+
+       g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
+       g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SERVERID);
+       g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
+       g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
+
+       g_dhcpv6_client_set_oro(dhcp_client, 2, G_DHCPV6_DNS_SERVERS,
+                               G_DHCPV6_SNTP_SERVERS);
+
+       g_dhcpv6_client_get_timeouts(dhcp_client, &T1, &T2, NULL, NULL, NULL);
+       g_dhcpv6_client_set_ia(dhcp_client,
+                       connman_network_get_index(dhcp->network),
+                       dhcp->use_ta == TRUE ? G_DHCPV6_IA_TA : G_DHCPV6_IA_NA,
+                       &T1, &T2, TRUE, NULL);
+
+       clear_callbacks(dhcp_client);
+
+       g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_RENEW,
+                                       renew_cb, dhcp);
+
+       dhcp->dhcp_client = dhcp_client;
+
+       return g_dhcp_client_start(dhcp_client, NULL);
+}
+
+static gboolean timeout_renew(gpointer user_data)
+{
+       struct connman_dhcpv6 *dhcp = user_data;
+
+       if (check_restart(dhcp) < 0)
+               return FALSE;
+
+       dhcp->RT = calc_delay(dhcp->RT, REN_MAX_RT);
+
+       DBG("renew RT timeout %d msec", dhcp->RT);
+
+       dhcp->timeout = g_timeout_add(dhcp->RT, timeout_renew, dhcp);
+
+       g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
+
+       g_dhcp_client_start(dhcp->dhcp_client, NULL);
+
+       return FALSE;
+}
+
+static gboolean start_renew(gpointer user_data)
+{
+       struct connman_dhcpv6 *dhcp = user_data;
+
+       dhcp->RT = REN_TIMEOUT * (1 + get_random());
+
+       DBG("renew initial RT timeout %d msec", dhcp->RT);
+
+       dhcp->timeout = g_timeout_add(dhcp->RT, timeout_renew, dhcp);
+
+       dhcpv6_renew(dhcp);
+
+       return FALSE;
+}
+
+int __connman_dhcpv6_start_renew(struct connman_network *network,
+                                                       dhcp_cb callback)
+{
+       struct connman_dhcpv6 *dhcp;
+       uint32_t T1, T2;
+       time_t last_renew, last_rebind, current, expired;
+
+       dhcp = g_hash_table_lookup(network_table, network);
+       if (dhcp == NULL)
+               return -ENOENT;
+
+       DBG("network %p dhcp %p", network, dhcp);
+
+       clear_timer(dhcp);
+
+       g_dhcpv6_client_get_timeouts(dhcp->dhcp_client, &T1, &T2,
+                               &last_renew, &last_rebind, &expired);
+
+       current = time(NULL);
+
+       DBG("T1 %u T2 %u expires %lu current %lu", T1, T2,
+               (unsigned long)expired, current);
+
+       if (T1 == 0xffffffff)
+               /* RFC 3315, 22.4 */
+               return 0;
+
+       if (T1 == 0)
+               /* RFC 3315, 22.4
+                * Client can choose the timeout.
+                */
+               T1 = 1800;
+
+       /* RFC 3315, 18.1.4, start solicit if expired */
+       if (current > expired) {
+               DBG("expired by %d secs", (int)(current - expired));
+               return -ETIMEDOUT;
+       }
+
+       dhcp->callback = callback;
+
+       if (T2 != 0xffffffff && T2 > 0 &&
+                       (unsigned)current > (unsigned)last_rebind + T2) {
+               int timeout;
+
+               /* RFC 3315, chapter 18.1.3, start rebind */
+               if ((unsigned)current > (unsigned)last_renew + T1)
+                       timeout = 0;
+               else
+                       timeout = last_renew - current + T1;
+
+               /*
+                * If we just did a renew, do not restart the rebind
+                * immediately.
+                */
+               dhcp->timeout = g_timeout_add_seconds(timeout, start_rebind,
+                                               dhcp);
+       } else {
+               DBG("renew after %d secs", T1);
+
+               dhcp->timeout = g_timeout_add_seconds(T1, start_renew, dhcp);
+       }
+       return 0;
+}
+
+int __connman_dhcpv6_start_release(struct connman_network *network,
+                               dhcp_cb callback)
+{
+       struct connman_dhcpv6 *dhcp;
+       GDHCPClient *dhcp_client;
+
+       if (network_table == NULL)
+               return 0;   /* we are already released */
+
+       dhcp = g_hash_table_lookup(network_table, network);
+       if (dhcp == NULL)
+               return -ENOENT;
+
+       DBG("network %p dhcp %p client %p stateless %d", network, dhcp,
+                                       dhcp->dhcp_client, dhcp->stateless);
+
+       if (dhcp->stateless == TRUE)
+               return -EINVAL;
+
+       clear_timer(dhcp);
+
+       dhcp_client = dhcp->dhcp_client;
+       if (dhcp_client == NULL) {
+               /*
+                * We had started the DHCPv6 handshaking i.e., we have called
+                * __connman_dhcpv6_start() but it has not yet sent
+                * a solicitation message to server. This means that we do not
+                * have DHCPv6 configured yet so we can just quit here.
+                */
+               DBG("DHCPv6 was not started");
+               return 0;
+       }
+
+       g_dhcp_client_clear_requests(dhcp_client);
+       g_dhcp_client_clear_values(dhcp_client);
+
+       g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
+       g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SERVERID);
+
+       g_dhcpv6_client_set_ia(dhcp_client,
+                       connman_network_get_index(dhcp->network),
+                       dhcp->use_ta == TRUE ? G_DHCPV6_IA_TA : G_DHCPV6_IA_NA,
+                       NULL, NULL, TRUE, NULL);
+
+       clear_callbacks(dhcp_client);
+
+       /*
+        * We do not register callback here because the answer might take too
+        * long time and network code might be in the middle of the disconnect.
+        * So we just inform the server that we are done with the addresses
+        * but ignore the reply from server. This is allowed by RFC 3315
+        * chapter 18.1.6.
+        */
+
+       dhcp->dhcp_client = dhcp_client;
+
+       return g_dhcp_client_start(dhcp_client, NULL);
+}
+
+static int dhcpv6_release(struct connman_dhcpv6 *dhcp)
+{
+       DBG("dhcp %p", dhcp);
+
+       clear_timer(dhcp);
+
+       dhcpv6_free(dhcp);
+
+       if (dhcp->dhcp_client == NULL)
+               return 0;
+
+       g_dhcp_client_stop(dhcp->dhcp_client);
+       g_dhcp_client_unref(dhcp->dhcp_client);
+
+       dhcp->dhcp_client = NULL;
+
+       return 0;
+}
+
+static void remove_network(gpointer user_data)
+{
+       struct connman_dhcpv6 *dhcp = user_data;
+
+       DBG("dhcp %p", dhcp);
+
+       dhcpv6_release(dhcp);
+
+       g_free(dhcp);
+}
+
+static gboolean timeout_info_req(gpointer user_data)
+{
+       struct connman_dhcpv6 *dhcp = user_data;
+
+       dhcp->RT = calc_delay(dhcp->RT, INF_MAX_RT);
+
+       DBG("info RT timeout %d msec", dhcp->RT);
+
+       dhcp->timeout = g_timeout_add(dhcp->RT, timeout_info_req, dhcp);
+
+       g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
+
+       g_dhcp_client_start(dhcp->dhcp_client, NULL);
+
+       return FALSE;
+}
+
+static gboolean start_info_req(gpointer user_data)
+{
+       struct connman_dhcpv6 *dhcp = user_data;
+
+       /* Set the retransmission timeout, RFC 3315 chapter 14 */
+       dhcp->RT = INF_TIMEOUT * (1 + get_random());
+
+       DBG("info initial RT timeout %d msec", dhcp->RT);
+
+       dhcp->timeout = g_timeout_add(dhcp->RT, timeout_info_req, dhcp);
+
+       dhcpv6_info_request(dhcp);
+
+       return FALSE;
+}
+
+int __connman_dhcpv6_start_info(struct connman_network *network,
+                               dhcp_cb callback)
+{
+       struct connman_dhcpv6 *dhcp;
+       int delay;
+
+       DBG("");
+
+       if (network_table != NULL) {
+               dhcp = g_hash_table_lookup(network_table, network);
+               if (dhcp != NULL && dhcp->started == TRUE)
+                       return -EBUSY;
+       }
+
+       dhcp = g_try_new0(struct connman_dhcpv6, 1);
+       if (dhcp == NULL)
+               return -ENOMEM;
+
+       dhcp->network = network;
+       dhcp->callback = callback;
+       dhcp->stateless = TRUE;
+       dhcp->started = TRUE;
+
+       connman_network_ref(network);
+
+       DBG("replace network %p dhcp %p", network, dhcp);
+
+       g_hash_table_replace(network_table, network, dhcp);
+
+       /* Initial timeout, RFC 3315, 18.1.5 */
+       delay = rand() % 1000;
+
+       dhcp->timeout = g_timeout_add(delay, start_info_req, dhcp);
+
+       return 0;
+}
+
+static void advertise_cb(GDHCPClient *dhcp_client, gpointer user_data)
+{
+       struct connman_dhcpv6 *dhcp = user_data;
+
+       DBG("dhcpv6 advertise msg %p", dhcp);
+
+       clear_timer(dhcp);
+
+       g_dhcpv6_client_clear_retransmit(dhcp_client);
+
+       if (g_dhcpv6_client_get_status(dhcp_client) != 0) {
+               if (dhcp->callback != NULL)
+                       dhcp->callback(dhcp->network, FALSE);
+               return;
+       }
+
+       dhcp->RT = REQ_TIMEOUT * (1 + get_random());
+       DBG("request initial RT timeout %d msec", dhcp->RT);
+       dhcp->timeout = g_timeout_add(dhcp->RT, timeout_request, dhcp);
+
+       dhcp->request_count = 1;
+
+       dhcpv6_request(dhcp, TRUE);
+}
+
+static void solicitation_cb(GDHCPClient *dhcp_client, gpointer user_data)
+{
+       /* We get here if server supports rapid commit */
+       struct connman_dhcpv6 *dhcp = user_data;
+
+       DBG("dhcpv6 solicitation msg %p", dhcp);
+
+       clear_timer(dhcp);
+
+       set_addresses(dhcp_client, dhcp);
+
+       g_dhcpv6_client_clear_retransmit(dhcp_client);
+}
+
+static gboolean timeout_solicitation(gpointer user_data)
+{
+       struct connman_dhcpv6 *dhcp = user_data;
+
+       dhcp->RT = calc_delay(dhcp->RT, SOL_MAX_RT);
+
+       DBG("solicit RT timeout %d msec", dhcp->RT);
+
+       dhcp->timeout = g_timeout_add(dhcp->RT, timeout_solicitation, dhcp);
+
+       g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
+
+       g_dhcp_client_start(dhcp->dhcp_client, NULL);
+
+       return FALSE;
+}
+
+static int dhcpv6_solicitation(struct connman_dhcpv6 *dhcp)
+{
+       struct connman_service *service;
+       struct connman_ipconfig *ipconfig_ipv6;
+       GDHCPClient *dhcp_client;
+       GDHCPClientError error;
+       int index, ret;
+
+       DBG("dhcp %p", dhcp);
+
+       index = connman_network_get_index(dhcp->network);
+
+       dhcp_client = g_dhcp_client_new(G_DHCP_IPV6, index, &error);
+       if (error != G_DHCP_CLIENT_ERROR_NONE) {
+               clear_timer(dhcp);
+               return -EINVAL;
+       }
+
+       if (getenv("CONNMAN_DHCPV6_DEBUG"))
+               g_dhcp_client_set_debug(dhcp_client, dhcpv6_debug, "DHCPv6");
+
+       service = connman_service_lookup_from_network(dhcp->network);
+       if (service == NULL) {
+               clear_timer(dhcp);
+               g_dhcp_client_unref(dhcp_client);
+               return -EINVAL;
+       }
+
+       ret = set_duid(service, dhcp->network, dhcp_client, index);
+       if (ret < 0) {
+               clear_timer(dhcp);
+               g_dhcp_client_unref(dhcp_client);
+               return ret;
+       }
+
+       g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
+       g_dhcp_client_set_request(dhcp_client, G_DHCPV6_RAPID_COMMIT);
+       g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
+       g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
+
+       g_dhcpv6_client_set_oro(dhcp_client, 2, G_DHCPV6_DNS_SERVERS,
+                               G_DHCPV6_SNTP_SERVERS);
+
+       ipconfig_ipv6 = __connman_service_get_ip6config(service);
+       dhcp->use_ta = __connman_ipconfig_ipv6_privacy_enabled(ipconfig_ipv6);
+
+       g_dhcpv6_client_set_ia(dhcp_client, index,
+                       dhcp->use_ta == TRUE ? G_DHCPV6_IA_TA : G_DHCPV6_IA_NA,
+                       NULL, NULL, FALSE, NULL);
+
+       clear_callbacks(dhcp_client);
+
+       g_dhcp_client_register_event(dhcp_client,
+                               G_DHCP_CLIENT_EVENT_SOLICITATION,
+                               solicitation_cb, dhcp);
+
+       g_dhcp_client_register_event(dhcp_client,
+                               G_DHCP_CLIENT_EVENT_ADVERTISE,
+                               advertise_cb, dhcp);
+
+       dhcp->dhcp_client = dhcp_client;
+
+       return g_dhcp_client_start(dhcp_client, NULL);
+}
+
+static gboolean start_solicitation(gpointer user_data)
+{
+       struct connman_dhcpv6 *dhcp = user_data;
+
+       /* Set the retransmission timeout, RFC 3315 chapter 14 */
+       dhcp->RT = SOL_TIMEOUT * (1 + get_random());
+
+       DBG("solicit initial RT timeout %d msec", dhcp->RT);
+
+       dhcp->timeout = g_timeout_add(dhcp->RT, timeout_solicitation, dhcp);
+
+       dhcpv6_solicitation(dhcp);
+
+       return FALSE;
+}
+
+static void confirm_cb(GDHCPClient *dhcp_client, gpointer user_data)
+{
+       struct connman_dhcpv6 *dhcp = user_data;
+       int status = g_dhcpv6_client_get_status(dhcp_client);
+
+       DBG("dhcpv6 confirm msg %p status %d", dhcp, status);
+
+       clear_timer(dhcp);
+
+       set_addresses(dhcp_client, dhcp);
+
+       g_dhcpv6_client_clear_retransmit(dhcp_client);
+
+       /*
+        * If confirm fails, start from scratch.
+        */
+       if (status != 0) {
+               g_dhcp_client_unref(dhcp->dhcp_client);
+               start_solicitation(dhcp);
+       } else if (dhcp->callback != NULL)
+               dhcp->callback(dhcp->network, TRUE);
+}
+
+static int dhcpv6_confirm(struct connman_dhcpv6 *dhcp)
+{
+       GDHCPClient *dhcp_client;
+       GDHCPClientError error;
+       struct connman_service *service;
+       struct connman_ipconfig *ipconfig_ipv6;
+       int index, ret;
+
+       DBG("dhcp %p", dhcp);
+
+       index = connman_network_get_index(dhcp->network);
+
+       dhcp_client = g_dhcp_client_new(G_DHCP_IPV6, index, &error);
+       if (error != G_DHCP_CLIENT_ERROR_NONE) {
+               clear_timer(dhcp);
+               return -EINVAL;
+       }
+
+       if (getenv("CONNMAN_DHCPV6_DEBUG"))
+               g_dhcp_client_set_debug(dhcp_client, dhcpv6_debug, "DHCPv6");
+
+       service = connman_service_lookup_from_network(dhcp->network);
+       if (service == NULL) {
+               clear_timer(dhcp);
+               g_dhcp_client_unref(dhcp_client);
+               return -EINVAL;
+       }
+
+       ret = set_duid(service, dhcp->network, dhcp_client, index);
+       if (ret < 0) {
+               clear_timer(dhcp);
+               g_dhcp_client_unref(dhcp_client);
+               return ret;
+       }
+
+       g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
+       g_dhcp_client_set_request(dhcp_client, G_DHCPV6_RAPID_COMMIT);
+       g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
+       g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
+
+       g_dhcpv6_client_set_oro(dhcp_client, 2, G_DHCPV6_DNS_SERVERS,
+                               G_DHCPV6_SNTP_SERVERS);
+
+       ipconfig_ipv6 = __connman_service_get_ip6config(service);
+       dhcp->use_ta = __connman_ipconfig_ipv6_privacy_enabled(ipconfig_ipv6);
+
+       g_dhcpv6_client_set_ia(dhcp_client, index,
+                       dhcp->use_ta == TRUE ? G_DHCPV6_IA_TA : G_DHCPV6_IA_NA,
+                       NULL, NULL, TRUE,
+                       __connman_ipconfig_get_dhcp_address(ipconfig_ipv6));
+
+       clear_callbacks(dhcp_client);
+
+       g_dhcp_client_register_event(dhcp_client,
+                               G_DHCP_CLIENT_EVENT_CONFIRM,
+                               confirm_cb, dhcp);
+
+       dhcp->dhcp_client = dhcp_client;
+
+       return g_dhcp_client_start(dhcp_client, NULL);
+}
+
+static gboolean timeout_confirm(gpointer user_data)
+{
+       struct connman_dhcpv6 *dhcp = user_data;
+
+       dhcp->RT = calc_delay(dhcp->RT, CNF_MAX_RT);
+
+       DBG("confirm RT timeout %d msec", dhcp->RT);
+
+       dhcp->timeout = g_timeout_add(dhcp->RT, timeout_confirm, dhcp);
+
+       g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
+
+       g_dhcp_client_start(dhcp->dhcp_client, NULL);
+
+       return FALSE;
+}
+
+static gboolean timeout_max_confirm(gpointer user_data)
+{
+       struct connman_dhcpv6 *dhcp = user_data;
+
+       dhcp->MRD = 0;
+
+       clear_timer(dhcp);
+
+       DBG("confirm max retransmit duration timeout");
+
+       g_dhcpv6_client_clear_retransmit(dhcp->dhcp_client);
+
+       if (dhcp->callback != NULL)
+               dhcp->callback(dhcp->network, FALSE);
+
+       return FALSE;
+}
+
+static gboolean start_confirm(gpointer user_data)
+{
+       struct connman_dhcpv6 *dhcp = user_data;
+
+       /* Set the confirm timeout, RFC 3315 chapter 14 */
+       dhcp->RT = CNF_TIMEOUT * (1 + get_random());
+
+       DBG("confirm initial RT timeout %d msec", dhcp->RT);
+
+       dhcp->timeout = g_timeout_add(dhcp->RT, timeout_confirm, dhcp);
+       dhcp->MRD = g_timeout_add(CNF_MAX_RD, timeout_max_confirm, dhcp);
+
+       dhcpv6_confirm(dhcp);
+
+       return FALSE;
+}
+
+int __connman_dhcpv6_start(struct connman_network *network,
+                               GSList *prefixes, dhcp_cb callback)
+{
+       struct connman_service *service;
+       struct connman_ipconfig *ipconfig_ipv6;
+       struct connman_dhcpv6 *dhcp;
+       char *last_address;
+       int delay;
+
+       DBG("");
+
+       if (network_table != NULL) {
+               dhcp = g_hash_table_lookup(network_table, network);
+               if (dhcp != NULL && dhcp->started == TRUE)
+                       return -EBUSY;
+       }
+
+       service = connman_service_lookup_from_network(network);
+       if (service == NULL)
+               return -EINVAL;
+
+       dhcp = g_try_new0(struct connman_dhcpv6, 1);
+       if (dhcp == NULL)
+               return -ENOMEM;
+
+       dhcp->network = network;
+       dhcp->callback = callback;
+       dhcp->prefixes = prefixes;
+       dhcp->started = TRUE;
+
+       connman_network_ref(network);
+
+       DBG("replace network %p dhcp %p", network, dhcp);
+
+       g_hash_table_replace(network_table, network, dhcp);
+
+       /* Initial timeout, RFC 3315, 17.1.2 */
+       delay = rand() % 1000;
+
+       ipconfig_ipv6 = __connman_service_get_ip6config(service);
+       last_address = __connman_ipconfig_get_dhcp_address(ipconfig_ipv6);
+
+       if (prefixes != NULL && last_address != NULL &&
+                       check_ipv6_addr_prefix(prefixes,
+                                               last_address) != 128) {
+               /*
+                * So we are in the same subnet
+                * RFC 3315, chapter 18.1.2 Confirm message
+                */
+               dhcp->timeout = g_timeout_add(delay, start_confirm, dhcp);
+       } else {
+               /*
+                * Start from scratch.
+                * RFC 3315, chapter 17.1.2 Solicitation message
+                */
+               dhcp->timeout = g_timeout_add(delay, start_solicitation, dhcp);
+       }
+
+       return 0;
+}
+
+void __connman_dhcpv6_stop(struct connman_network *network)
+{
+       DBG("");
+
+       if (network_table == NULL)
+               return;
+
+       if (g_hash_table_remove(network_table, network) == TRUE)
+               connman_network_unref(network);
+}
+
+int __connman_dhcpv6_init(void)
+{
+       DBG("");
+
+       srand(time(NULL));
+
+       network_table = g_hash_table_new_full(g_direct_hash, g_direct_equal,
+                                                       NULL, remove_network);
+
+       return 0;
+}
+
+void __connman_dhcpv6_cleanup(void)
+{
+       DBG("");
+
+       g_hash_table_destroy(network_table);
+       network_table = NULL;
+}
index 21512c6..ab468e2 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2014  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
@@ -24,6 +24,7 @@
 #endif
 
 #include <errno.h>
+#include <stdlib.h>
 #include <unistd.h>
 #include <string.h>
 #include <stdint.h>
 #include <netinet/in.h>
 #include <sys/types.h>
 #include <sys/socket.h>
+#include <fcntl.h>
 #include <netdb.h>
+#include <resolv.h>
+#include <gweb/gresolv.h>
 
 #include <glib.h>
 
 #include "connman.h"
 
+#if defined TIZEN_EXT
+#include <sys/smack.h>
+#endif
+
 #if __BYTE_ORDER == __LITTLE_ENDIAN
 struct domain_hdr {
        uint16_t id;
@@ -80,9 +88,11 @@ struct partial_reply {
 };
 
 struct server_data {
-       char *interface;
+       int index;
        GList *domains;
        char *server;
+       struct sockaddr *server_addr;
+       socklen_t server_addr_len;
        int protocol;
        GIOChannel *channel;
        guint watch;
@@ -100,6 +110,7 @@ struct request_data {
        socklen_t sa_len;
        int client_sk;
        int protocol;
+       int family;
        guint16 srcid;
        guint16 dstid;
        guint16 altid;
@@ -117,18 +128,116 @@ struct request_data {
 };
 
 struct listener_data {
-       char *ifname;
-       GIOChannel *udp_listener_channel;
-       guint udp_listener_watch;
-       GIOChannel *tcp_listener_channel;
-       guint tcp_listener_watch;
+       int index;
+
+       GIOChannel *udp4_listener_channel;
+       GIOChannel *tcp4_listener_channel;
+       guint udp4_listener_watch;
+       guint tcp4_listener_watch;
+
+       GIOChannel *udp6_listener_channel;
+       GIOChannel *tcp6_listener_channel;
+       guint udp6_listener_watch;
+       guint tcp6_listener_watch;
+};
+
+/*
+ * The TCP client requires some extra handling as we need to
+ * be prepared to receive also partial DNS requests.
+ */
+struct tcp_partial_client_data {
+       int family;
+       struct listener_data *ifdata;
+       GIOChannel *channel;
+       guint watch;
+       unsigned char *buf;
+       unsigned int buf_end;
+       guint timeout;
+};
+
+struct cache_data {
+       time_t inserted;
+       time_t valid_until;
+       time_t cache_until;
+       int timeout;
+       uint16_t type;
+       uint16_t answers;
+       unsigned int data_len;
+       unsigned char *data; /* contains DNS header + body */
+};
+
+struct cache_entry {
+       char *key;
+       gboolean want_refresh;
+       int hits;
+       struct cache_data *ipv4;
+       struct cache_data *ipv6;
 };
 
+struct domain_question {
+       uint16_t type;
+       uint16_t class;
+} __attribute__ ((packed));
+
+struct domain_rr {
+       uint16_t type;
+       uint16_t class;
+       uint32_t ttl;
+       uint16_t rdlen;
+} __attribute__ ((packed));
+
+/*
+ * Max length of the DNS TCP packet.
+ */
+#define TCP_MAX_BUF_LEN 4096
+
+/*
+ * We limit how long the cached DNS entry stays in the cache.
+ * By default the TTL (time-to-live) of the DNS response is used
+ * when setting the cache entry life time. The value is in seconds.
+ */
+#if defined TIZEN_EXT
+#define MAX_CACHE_TTL (60 * 60)
+#else
+#define MAX_CACHE_TTL (60 * 30)
+#endif
+/*
+ * Also limit the other end, cache at least for 30 seconds.
+ */
+#define MIN_CACHE_TTL (30)
+
+/*
+ * We limit the cache size to some sane value so that cached data does
+ * not occupy too much memory. Each cached entry occupies on average
+ * about 100 bytes memory (depending on DNS name length).
+ * Example: caching www.connman.net uses 97 bytes memory.
+ * The value is the max amount of cached DNS responses (count).
+ */
+#define MAX_CACHE_SIZE 256
+
+static int cache_size;
+static GHashTable *cache;
+static int cache_refcount;
 static GSList *server_list = NULL;
+#if defined TIZEN_EXT
+static GSList *server_list_sec = NULL;
+#endif
 static GSList *request_list = NULL;
-static GSList *request_pending_list = NULL;
-static guint16 request_id = 0x0000;
 static GHashTable *listener_table = NULL;
+static time_t next_refresh;
+static GHashTable *partial_tcp_req_table;
+
+#if defined TIZEN_EXT
+static void destroy_server_sec(struct server_data *server);
+static struct server_data *create_server_sec(int index,
+               const char *domain, const char *server,
+               int protocol);
+#endif
+
+static guint16 get_id(void)
+{
+       return random();
+}
 
 static int protocol_offset(int protocol)
 {
@@ -145,6 +254,27 @@ static int protocol_offset(int protocol)
 
 }
 
+/*
+ * There is a power and efficiency benefit to have entries
+ * in our cache expire at the same time. To this extend,
+ * we round down the cache valid time to common boundaries.
+ */
+static time_t round_down_ttl(time_t end_time, int ttl)
+{
+       if (ttl < 15)
+               return end_time;
+
+       /* Less than 5 minutes, round to 10 second boundary */
+       if (ttl < 300) {
+               end_time = end_time / 10;
+               end_time = end_time * 10;
+       } else { /* 5 or more minutes, round to 30 seconds */
+               end_time = end_time / 30;
+               end_time = end_time * 30;
+       }
+       return end_time;
+}
+
 static struct request_data *find_request(guint16 id)
 {
        GSList *list;
@@ -159,28 +289,28 @@ static struct request_data *find_request(guint16 id)
        return NULL;
 }
 
-static struct server_data *find_server(const char *interface,
+static struct server_data *find_server(int index,
                                        const char *server,
                                                int protocol)
 {
        GSList *list;
 
-       DBG("interface %s server %s", interface, server);
+       DBG("index %d server %s proto %d", index, server, protocol);
 
        for (list = server_list; list; list = list->next) {
                struct server_data *data = list->data;
 
-               if (interface == NULL && data->interface == NULL &&
-                               g_str_equal(data->server, server) == TRUE &&
+               if (index < 0 && data->index < 0 &&
+                               g_str_equal(data->server, server) &&
                                data->protocol == protocol)
                        return data;
 
-               if (interface == NULL ||
-                               data->interface == NULL || data->server == NULL)
+               if (index < 0 ||
+                               data->index < 0 || !data->server)
                        continue;
 
-               if (g_str_equal(data->interface, interface) == TRUE &&
-                               g_str_equal(data->server, server) == TRUE &&
+               if (data->index == index &&
+                               g_str_equal(data->server, server) &&
                                data->protocol == protocol)
                        return data;
        }
@@ -188,6 +318,168 @@ static struct server_data *find_server(const char *interface,
        return NULL;
 }
 
+/* we can keep using the same resolve's */
+static GResolv *ipv4_resolve;
+static GResolv *ipv6_resolve;
+
+static void dummy_resolve_func(GResolvResultStatus status,
+                                       char **results, gpointer user_data)
+{
+}
+
+/*
+ * Refresh a DNS entry, but also age the hit count a bit */
+static void refresh_dns_entry(struct cache_entry *entry, char *name)
+{
+       int age = 1;
+
+       if (!ipv4_resolve) {
+               ipv4_resolve = g_resolv_new(0);
+               g_resolv_set_address_family(ipv4_resolve, AF_INET);
+               g_resolv_add_nameserver(ipv4_resolve, "127.0.0.1", 53, 0);
+       }
+
+       if (!ipv6_resolve) {
+               ipv6_resolve = g_resolv_new(0);
+               g_resolv_set_address_family(ipv6_resolve, AF_INET6);
+               g_resolv_add_nameserver(ipv6_resolve, "::1", 53, 0);
+       }
+
+       if (!entry->ipv4) {
+               DBG("Refresing A record for %s", name);
+               g_resolv_lookup_hostname(ipv4_resolve, name,
+                                       dummy_resolve_func, NULL);
+               age = 4;
+       }
+
+       if (!entry->ipv6) {
+               DBG("Refresing AAAA record for %s", name);
+               g_resolv_lookup_hostname(ipv6_resolve, name,
+                                       dummy_resolve_func, NULL);
+               age = 4;
+       }
+
+       entry->hits -= age;
+       if (entry->hits < 0)
+               entry->hits = 0;
+}
+
+static int dns_name_length(unsigned char *buf)
+{
+       if ((buf[0] & NS_CMPRSFLGS) == NS_CMPRSFLGS) /* compressed name */
+               return 2;
+       return strlen((char *)buf);
+}
+
+static void update_cached_ttl(unsigned char *buf, int len, int new_ttl)
+{
+       unsigned char *c;
+       uint16_t w;
+       int l;
+
+       /* skip the header */
+       c = buf + 12;
+       len -= 12;
+
+       /* skip the query, which is a name and 2 16 bit words */
+       l = dns_name_length(c);
+       c += l;
+       len -= l;
+       c += 4;
+       len -= 4;
+
+       /* now we get the answer records */
+
+       while (len > 0) {
+               /* first a name */
+               l = dns_name_length(c);
+               c += l;
+               len -= l;
+               if (len < 0)
+                       break;
+               /* then type + class, 2 bytes each */
+               c += 4;
+               len -= 4;
+               if (len < 0)
+                       break;
+
+               /* now the 4 byte TTL field */
+               c[0] = new_ttl >> 24 & 0xff;
+               c[1] = new_ttl >> 16 & 0xff;
+               c[2] = new_ttl >> 8 & 0xff;
+               c[3] = new_ttl & 0xff;
+               c += 4;
+               len -= 4;
+               if (len < 0)
+                       break;
+
+               /* now the 2 byte rdlen field */
+               w = c[0] << 8 | c[1];
+               c += w + 2;
+               len -= w + 2;
+       }
+}
+
+static void send_cached_response(int sk, unsigned char *buf, int len,
+                               const struct sockaddr *to, socklen_t tolen,
+                               int protocol, int id, uint16_t answers, int ttl)
+{
+       struct domain_hdr *hdr;
+       unsigned char *ptr = buf;
+       int err, offset, dns_len, adj_len = len - 2;
+
+       /*
+        * The cached packet contains always the TCP offset (two bytes)
+        * so skip them for UDP.
+        */
+       switch (protocol) {
+       case IPPROTO_UDP:
+               ptr += 2;
+               len -= 2;
+               dns_len = len;
+               offset = 0;
+               break;
+       case IPPROTO_TCP:
+               offset = 2;
+               dns_len = ptr[0] * 256 + ptr[1];
+               break;
+       default:
+               return;
+       }
+
+       if (len < 12)
+               return;
+
+       hdr = (void *) (ptr + offset);
+
+       hdr->id = id;
+       hdr->qr = 1;
+       hdr->rcode = ns_r_noerror;
+       hdr->ancount = htons(answers);
+       hdr->nscount = 0;
+       hdr->arcount = 0;
+
+       /* if this is a negative reply, we are authorative */
+       if (answers == 0)
+               hdr->aa = 1;
+       else
+               update_cached_ttl((unsigned char *)hdr, adj_len, ttl);
+
+       DBG("sk %d id 0x%04x answers %d ptr %p length %d dns %d",
+               sk, hdr->id, answers, ptr, len, dns_len);
+
+       err = sendto(sk, ptr, len, MSG_NOSIGNAL, to, tolen);
+       if (err < 0) {
+               connman_error("Cannot send cached DNS response: %s",
+                               strerror(errno));
+               return;
+       }
+
+       if (err != len || (dns_len != (len - 2) && protocol == IPPROTO_TCP) ||
+                               (dns_len != len && protocol == IPPROTO_UDP))
+               DBG("Packet length mismatch, sent %d wanted %d dns %d",
+                       err, len, dns_len);
+}
 
 static void send_response(int sk, unsigned char *buf, int len,
                                const struct sockaddr *to, socklen_t tolen,
@@ -196,7 +488,7 @@ static void send_response(int sk, unsigned char *buf, int len,
        struct domain_hdr *hdr;
        int err, offset = protocol_offset(protocol);
 
-       DBG("");
+       DBG("sk %d", sk);
 
        if (offset < 0)
                return;
@@ -209,42 +501,74 @@ static void send_response(int sk, unsigned char *buf, int len,
        DBG("id 0x%04x qr %d opcode %d", hdr->id, hdr->qr, hdr->opcode);
 
        hdr->qr = 1;
-       hdr->rcode = 2;
+       hdr->rcode = ns_r_servfail;
 
        hdr->ancount = 0;
        hdr->nscount = 0;
        hdr->arcount = 0;
 
-       err = sendto(sk, buf, len, 0, to, tolen);
+       err = sendto(sk, buf, len, MSG_NOSIGNAL, to, tolen);
        if (err < 0) {
-               connman_error("Failed to send DNS response: %s",
-                               strerror(errno));
+               connman_error("Failed to send DNS response to %d: %s",
+                               sk, strerror(errno));
                return;
        }
 }
 
+static int get_req_udp_socket(struct request_data *req)
+{
+       GIOChannel *channel;
+
+       if (req->family == AF_INET)
+               channel = req->ifdata->udp4_listener_channel;
+       else
+               channel = req->ifdata->udp6_listener_channel;
+
+       if (!channel)
+               return -1;
+
+       return g_io_channel_unix_get_fd(channel);
+}
+
+static void destroy_request_data(struct request_data *req)
+{
+       if (req->timeout > 0)
+               g_source_remove(req->timeout);
+
+       g_free(req->resp);
+       g_free(req->request);
+       g_free(req->name);
+       g_free(req);
+}
+
 static gboolean request_timeout(gpointer user_data)
 {
        struct request_data *req = user_data;
-       struct listener_data *ifdata;
-
-       DBG("id 0x%04x", req->srcid);
 
-       if (req == NULL)
+       if (!req)
                return FALSE;
 
-       ifdata = req->ifdata;
+       DBG("id 0x%04x", req->srcid);
 
        request_list = g_slist_remove(request_list, req);
        req->numserv--;
 
-       if (req->resplen > 0 && req->resp != NULL) {
+       if (req->resplen > 0 && req->resp) {
                int sk, err;
 
-               sk = g_io_channel_unix_get_fd(ifdata->udp_listener_channel);
+               if (req->protocol == IPPROTO_UDP) {
+                       sk = get_req_udp_socket(req);
+                       if (sk < 0)
+                               return FALSE;
 
-               err = sendto(sk, req->resp, req->resplen, 0,
-                                               &req->sa, req->sa_len);
+                       err = sendto(sk, req->resp, req->resplen, MSG_NOSIGNAL,
+                               &req->sa, req->sa_len);
+               } else {
+                       sk = req->client_sk;
+                       err = send(sk, req->resp, req->resplen, MSG_NOSIGNAL);
+                       if (err < 0)
+                               close(sk);
+               }
                if (err < 0)
                        return FALSE;
        } else if (req->request && req->numserv == 0) {
@@ -261,15 +585,27 @@ static gboolean request_timeout(gpointer user_data)
 
                        hdr = (void *) (req->request);
                        hdr->id = req->srcid;
-                       sk = g_io_channel_unix_get_fd(
-                                               ifdata->udp_listener_channel);
-                       send_response(sk, req->request, req->request_len,
-                                       &req->sa, req->sa_len, IPPROTO_UDP);
+
+                       sk = get_req_udp_socket(req);
+                       if (sk >= 0)
+                               send_response(sk, req->request,
+                                       req->request_len, &req->sa,
+                                       req->sa_len, IPPROTO_UDP);
                }
        }
 
-       g_free(req->resp);
-       g_free(req);
+       /*
+        * We cannot leave TCP client hanging so just kick it out
+        * if we get a request timeout from server.
+        */
+       if (req->protocol == IPPROTO_TCP) {
+               DBG("client %d removed", req->client_sk);
+               g_hash_table_remove(partial_tcp_req_table,
+                               GINT_TO_POINTER(req->client_sk));
+       }
+
+       req->timeout = 0;
+       destroy_request_data(req);
 
        return FALSE;
 }
@@ -278,53 +614,50 @@ static int append_query(unsigned char *buf, unsigned int size,
                                const char *query, const char *domain)
 {
        unsigned char *ptr = buf;
-       char *offset;
        int len;
 
        DBG("query %s domain %s", query, domain);
 
-       offset = (char *) query;
-       while (offset != NULL) {
-               char *tmp;
+       while (query) {
+               const char *tmp;
 
-               tmp = strchr(offset, '.');
-               if (tmp == NULL) {
-                       len = strlen(offset);
+               tmp = strchr(query, '.');
+               if (!tmp) {
+                       len = strlen(query);
                        if (len == 0)
                                break;
                        *ptr = len;
-                       memcpy(ptr + 1, offset, len);
+                       memcpy(ptr + 1, query, len);
                        ptr += len + 1;
                        break;
                }
 
-               *ptr = tmp - offset;
-               memcpy(ptr + 1, offset, tmp - offset);
-               ptr += tmp - offset + 1;
+               *ptr = tmp - query;
+               memcpy(ptr + 1, query, tmp - query);
+               ptr += tmp - query + 1;
 
-               offset = tmp + 1;
+               query = tmp + 1;
        }
 
-       offset = (char *) domain;
-       while (offset != NULL) {
-               char *tmp;
+       while (domain) {
+               const char *tmp;
 
-               tmp = strchr(offset, '.');
-               if (tmp == NULL) {
-                       len = strlen(offset);
+               tmp = strchr(domain, '.');
+               if (!tmp) {
+                       len = strlen(domain);
                        if (len == 0)
                                break;
                        *ptr = len;
-                       memcpy(ptr + 1, offset, len);
+                       memcpy(ptr + 1, domain, len);
                        ptr += len + 1;
                        break;
                }
 
-               *ptr = tmp - offset;
-               memcpy(ptr + 1, offset, tmp - offset);
-               ptr += tmp - offset + 1;
+               *ptr = tmp - domain;
+               memcpy(ptr + 1, domain, tmp - domain);
+               ptr += tmp - domain + 1;
 
-               offset = tmp + 1;
+               domain = tmp + 1;
        }
 
        *ptr++ = 0x00;
@@ -332,201 +665,1589 @@ static int append_query(unsigned char *buf, unsigned int size,
        return ptr - buf;
 }
 
-static int ns_resolv(struct server_data *server, struct request_data *req,
-                               gpointer request, gpointer name)
+static gboolean cache_check_is_valid(struct cache_data *data,
+                               time_t current_time)
 {
-       GList *list;
-       int sk, err;
-       char *dot, *lookup = (char *) name;
-
-       sk = g_io_channel_unix_get_fd(server->channel);
-
-       err = send(sk, request, req->request_len, 0);
+       if (!data)
+               return FALSE;
 
-       req->numserv++;
+       if (data->cache_until < current_time)
+               return FALSE;
 
-       /* If we have more than one dot, we don't add domains */
-       dot = strchr(lookup, '.');
-       if (dot != NULL && dot != lookup + strlen(lookup) - 1)
-               return 0;
+       return TRUE;
+}
 
-       if (server->domains != NULL && server->domains->data != NULL)
-               req->append_domain = TRUE;
+/*
+ * remove stale cached entries so that they can be refreshed
+ */
+static void cache_enforce_validity(struct cache_entry *entry)
+{
+       time_t current_time = time(NULL);
 
-       for (list = server->domains; list; list = list->next) {
-               char *domain;
-               unsigned char alt[1024];
-               struct domain_hdr *hdr = (void *) &alt;
-               int altlen, domlen, offset;
+       if (!cache_check_is_valid(entry->ipv4, current_time)
+                                                       && entry->ipv4) {
+               DBG("cache timeout \"%s\" type A", entry->key);
+               g_free(entry->ipv4->data);
+               g_free(entry->ipv4);
+               entry->ipv4 = NULL;
 
-               domain = list->data;
+       }
 
-               if (domain == NULL)
-                       continue;
+       if (!cache_check_is_valid(entry->ipv6, current_time)
+                                                       && entry->ipv6) {
+               DBG("cache timeout \"%s\" type AAAA", entry->key);
+               g_free(entry->ipv6->data);
+               g_free(entry->ipv6);
+               entry->ipv6 = NULL;
+       }
+}
 
-               offset = protocol_offset(server->protocol);
-               if (offset < 0)
-                       return offset;
+static uint16_t cache_check_validity(char *question, uint16_t type,
+                               struct cache_entry *entry)
+{
+       time_t current_time = time(NULL);
+       gboolean want_refresh = FALSE;
 
-               domlen = strlen(domain) + 1;
-               if (domlen < 5)
-                       return -EINVAL;
+       /*
+        * if we have a popular entry, we want a refresh instead of
+        * total destruction of the entry.
+        */
+       if (entry->hits > 2)
+               want_refresh = TRUE;
 
-               alt[offset] = req->altid & 0xff;
-               alt[offset + 1] = req->altid >> 8;
+       cache_enforce_validity(entry);
 
-               memcpy(alt + offset + 2, request + offset + 2, 10);
-               hdr->qdcount = htons(1);
+       switch (type) {
+       case 1:         /* IPv4 */
+               if (!cache_check_is_valid(entry->ipv4, current_time)) {
+                       DBG("cache %s \"%s\" type A", entry->ipv4 ?
+                                       "timeout" : "entry missing", question);
 
-               altlen = append_query(alt + offset + 12, sizeof(alt) - 12,
-                                       name, domain);
-               if (altlen < 0)
-                       return -EINVAL;
+                       if (want_refresh)
+                               entry->want_refresh = TRUE;
 
-               altlen += 12;
+                       /*
+                        * We do not remove cache entry if there is still
+                        * valid IPv6 entry found in the cache.
+                        */
+                       if (!cache_check_is_valid(entry->ipv6, current_time) && !want_refresh) {
+                               g_hash_table_remove(cache, question);
+                               type = 0;
+                       }
+               }
+               break;
 
-               memcpy(alt + offset + altlen,
-                       request + offset + altlen - domlen,
-                               req->request_len - altlen - offset + domlen);
+       case 28:        /* IPv6 */
+               if (!cache_check_is_valid(entry->ipv6, current_time)) {
+                       DBG("cache %s \"%s\" type AAAA", entry->ipv6 ?
+                                       "timeout" : "entry missing", question);
 
-               if (server->protocol == IPPROTO_TCP) {
-                       int req_len = req->request_len + domlen - 2;
+                       if (want_refresh)
+                               entry->want_refresh = TRUE;
 
-                       alt[0] = (req_len >> 8) & 0xff;
-                       alt[1] = req_len & 0xff;
+                       if (!cache_check_is_valid(entry->ipv4, current_time) && !want_refresh) {
+                               g_hash_table_remove(cache, question);
+                               type = 0;
+                       }
                }
-
-               err = send(sk, alt, req->request_len + domlen, 0);
-               if (err < 0)
-                       return -EIO;
-
-               req->numserv++;
+               break;
        }
 
-       return 0;
+       return type;
 }
 
-static int forward_dns_reply(unsigned char *reply, int reply_len, int protocol)
+static void cache_element_destroy(gpointer value)
 {
-       struct domain_hdr *hdr;
-       struct request_data *req;
-       int dns_id, sk, err, offset = protocol_offset(protocol);
-       struct listener_data *ifdata;
+       struct cache_entry *entry = value;
 
-       if (offset < 0)
-               return offset;
+       if (!entry)
+               return;
 
-       hdr = (void *)(reply + offset);
-       dns_id = reply[offset] | reply[offset + 1] << 8;
+       if (entry->ipv4) {
+               g_free(entry->ipv4->data);
+               g_free(entry->ipv4);
+       }
 
-       DBG("Received %d bytes (id 0x%04x)", reply_len, dns_id);
+       if (entry->ipv6) {
+               g_free(entry->ipv6->data);
+               g_free(entry->ipv6);
+       }
 
-       req = find_request(dns_id);
-       if (req == NULL)
-               return -EINVAL;
+       g_free(entry->key);
+       g_free(entry);
 
-       DBG("id 0x%04x rcode %d", hdr->id, hdr->rcode);
+       if (--cache_size < 0)
+               cache_size = 0;
+}
 
-       ifdata = req->ifdata;
+static gboolean try_remove_cache(gpointer user_data)
+{
+       if (__sync_fetch_and_sub(&cache_refcount, 1) == 1) {
+               DBG("No cache users, removing it.");
 
-       reply[offset] = req->srcid & 0xff;
-       reply[offset + 1] = req->srcid >> 8;
+               g_hash_table_destroy(cache);
+               cache = NULL;
+       }
 
-       req->numresp++;
+       return FALSE;
+}
 
-       if (hdr->rcode == 0 || req->resp == NULL) {
+static void create_cache(void)
+{
+       if (__sync_fetch_and_add(&cache_refcount, 1) == 0)
+               cache = g_hash_table_new_full(g_str_hash,
+                                       g_str_equal,
+                                       NULL,
+                                       cache_element_destroy);
+}
 
-               /*
-                * If the domain name was append
-                * remove it before forwarding the reply.
-                */
-               if (req->append_domain == TRUE) {
-                       unsigned char *ptr;
-                       uint8_t host_len;
-                       unsigned int domain_len;
+static struct cache_entry *cache_check(gpointer request, int *qtype, int proto)
+{
+       char *question;
+       struct cache_entry *entry;
+       struct domain_question *q;
+       uint16_t type;
+       int offset, proto_offset;
 
-                       /*
-                        * ptr points to the first char of the hostname.
-                        * ->hostname.domain.net
-                        */
-                       ptr = reply + offset + sizeof(struct domain_hdr);
-                       host_len = *ptr;
-                       domain_len = strlen((const char *)ptr) - host_len - 1;
+       if (!request)
+               return NULL;
 
-                       /*
-                        * remove the domain name and replaced it by the end
-                        * of reply.
-                        */
-                       memmove(ptr + host_len + 1,
-                               ptr + host_len + domain_len + 1,
-                               reply_len - (ptr - reply + domain_len));
+       proto_offset = protocol_offset(proto);
+       if (proto_offset < 0)
+               return NULL;
 
-                       reply_len = reply_len - domain_len;
-               }
+       question = request + proto_offset + 12;
 
-               g_free(req->resp);
-               req->resplen = 0;
+       offset = strlen(question) + 1;
+       q = (void *) (question + offset);
+       type = ntohs(q->type);
 
-               req->resp = g_try_malloc(reply_len);
-               if (req->resp == NULL)
-                       return -ENOMEM;
+       /* We only cache either A (1) or AAAA (28) requests */
+       if (type != 1 && type != 28)
+               return NULL;
 
-               memcpy(req->resp, reply, reply_len);
-               req->resplen = reply_len;
+       if (!cache) {
+               create_cache();
+               return NULL;
        }
 
-       if (hdr->rcode > 0 && req->numresp < req->numserv)
-               return -EINVAL;
+       entry = g_hash_table_lookup(cache, question);
+       if (!entry)
+               return NULL;
 
-       if (req->timeout > 0)
-               g_source_remove(req->timeout);
+       type = cache_check_validity(question, type, entry);
+       if (type == 0)
+               return NULL;
 
-       request_list = g_slist_remove(request_list, req);
+       *qtype = type;
+       return entry;
+}
 
-       if (protocol == IPPROTO_UDP) {
-               sk = g_io_channel_unix_get_fd(ifdata->udp_listener_channel);
-               err = sendto(sk, req->resp, req->resplen, 0,
-                            &req->sa, req->sa_len);
-       } else {
-               sk = req->client_sk;
-               err = send(sk, req->resp, req->resplen, 0);
-               close(sk);
+/*
+ * Get a label/name from DNS resource record. The function decompresses the
+ * label if necessary. The function does not convert the name to presentation
+ * form. This means that the result string will contain label lengths instead
+ * of dots between labels. We intentionally do not want to convert to dotted
+ * format so that we can cache the wire format string directly.
+ */
+static int get_name(int counter,
+               unsigned char *pkt, unsigned char *start, unsigned char *max,
+               unsigned char *output, int output_max, int *output_len,
+               unsigned char **end, char *name, int *name_len)
+{
+       unsigned char *p;
+
+       /* Limit recursion to 10 (this means up to 10 labels in domain name) */
+       if (counter > 10)
+               return -EINVAL;
+
+       p = start;
+       while (*p) {
+               if ((*p & NS_CMPRSFLGS) == NS_CMPRSFLGS) {
+                       uint16_t offset = (*p & 0x3F) * 256 + *(p + 1);
+
+                       if (offset >= max - pkt)
+                               return -ENOBUFS;
+
+                       if (!*end)
+                               *end = p + 2;
+
+                       return get_name(counter + 1, pkt, pkt + offset, max,
+                                       output, output_max, output_len, end,
+                                       name, name_len);
+               } else {
+                       unsigned label_len = *p;
+
+                       if (pkt + label_len > max)
+                               return -ENOBUFS;
+
+                       if (*output_len > output_max)
+                               return -ENOBUFS;
+
+                       /*
+                        * We need the original name in order to check
+                        * if this answer is the correct one.
+                        */
+                       name[(*name_len)++] = label_len;
+                       memcpy(name + *name_len, p + 1, label_len + 1);
+                       *name_len += label_len;
+
+                       /* We compress the result */
+                       output[0] = NS_CMPRSFLGS;
+                       output[1] = 0x0C;
+                       *output_len = 2;
+
+                       p += label_len + 1;
+
+                       if (!*end)
+                               *end = p;
+
+                       if (p >= max)
+                               return -ENOBUFS;
+               }
+       }
+
+       return 0;
+}
+
+static int parse_rr(unsigned char *buf, unsigned char *start,
+                       unsigned char *max,
+                       unsigned char *response, unsigned int *response_size,
+                       uint16_t *type, uint16_t *class, int *ttl, int *rdlen,
+                       unsigned char **end,
+                       char *name)
+{
+       struct domain_rr *rr;
+       int err, offset;
+       int name_len = 0, output_len = 0, max_rsp = *response_size;
+
+       err = get_name(0, buf, start, max, response, max_rsp,
+               &output_len, end, name, &name_len);
+       if (err < 0)
+               return err;
+
+       offset = output_len;
+
+       if ((unsigned int) offset > *response_size)
+               return -ENOBUFS;
+
+       rr = (void *) (*end);
+
+       if (!rr)
+               return -EINVAL;
+
+       *type = ntohs(rr->type);
+       *class = ntohs(rr->class);
+       *ttl = ntohl(rr->ttl);
+       *rdlen = ntohs(rr->rdlen);
+
+       if (*ttl < 0)
+               return -EINVAL;
+
+       memcpy(response + offset, *end, sizeof(struct domain_rr));
+
+       offset += sizeof(struct domain_rr);
+       *end += sizeof(struct domain_rr);
+
+       if ((unsigned int) (offset + *rdlen) > *response_size)
+               return -ENOBUFS;
+
+       memcpy(response + offset, *end, *rdlen);
+
+       *end += *rdlen;
+
+       *response_size = offset + *rdlen;
+
+       return 0;
+}
+
+static gboolean check_alias(GSList *aliases, char *name)
+{
+       GSList *list;
+
+       if (aliases) {
+               for (list = aliases; list; list = list->next) {
+                       int len = strlen((char *)list->data);
+                       if (strncmp((char *)list->data, name, len) == 0)
+                               return TRUE;
+               }
+       }
+
+       return FALSE;
+}
+
+static int parse_response(unsigned char *buf, int buflen,
+                       char *question, int qlen,
+                       uint16_t *type, uint16_t *class, int *ttl,
+                       unsigned char *response, unsigned int *response_len,
+                       uint16_t *answers)
+{
+       struct domain_hdr *hdr = (void *) buf;
+       struct domain_question *q;
+       unsigned char *ptr;
+       uint16_t qdcount = ntohs(hdr->qdcount);
+       uint16_t ancount = ntohs(hdr->ancount);
+       int err, i;
+       uint16_t qtype, qclass;
+       unsigned char *next = NULL;
+       unsigned int maxlen = *response_len;
+       GSList *aliases = NULL, *list;
+       char name[NS_MAXDNAME + 1];
+
+       if (buflen < 12)
+               return -EINVAL;
+
+       DBG("qr %d qdcount %d", hdr->qr, qdcount);
+
+       /* We currently only cache responses where question count is 1 */
+       if (hdr->qr != 1 || qdcount != 1)
+               return -EINVAL;
+
+       ptr = buf + sizeof(struct domain_hdr);
+
+       strncpy(question, (char *) ptr, qlen);
+       qlen = strlen(question);
+       ptr += qlen + 1; /* skip \0 */
+
+       q = (void *) ptr;
+       qtype = ntohs(q->type);
+
+       /* We cache only A and AAAA records */
+       if (qtype != 1 && qtype != 28)
+               return -ENOMSG;
+
+       qclass = ntohs(q->class);
+
+       ptr += 2 + 2; /* ptr points now to answers */
+
+       err = -ENOMSG;
+       *response_len = 0;
+       *answers = 0;
+
+       memset(name, 0, sizeof(name));
+
+       /*
+        * We have a bunch of answers (like A, AAAA, CNAME etc) to
+        * A or AAAA question. We traverse the answers and parse the
+        * resource records. Only A and AAAA records are cached, all
+        * the other records in answers are skipped.
+        */
+       for (i = 0; i < ancount; i++) {
+               /*
+                * Get one address at a time to this buffer.
+                * The max size of the answer is
+                *   2 (pointer) + 2 (type) + 2 (class) +
+                *   4 (ttl) + 2 (rdlen) + addr (16 or 4) = 28
+                * for A or AAAA record.
+                * For CNAME the size can be bigger.
+                */
+               unsigned char rsp[NS_MAXCDNAME];
+               unsigned int rsp_len = sizeof(rsp) - 1;
+               int ret, rdlen;
+
+               memset(rsp, 0, sizeof(rsp));
+
+               ret = parse_rr(buf, ptr, buf + buflen, rsp, &rsp_len,
+                       type, class, ttl, &rdlen, &next, name);
+               if (ret != 0) {
+                       err = ret;
+                       goto out;
+               }
+
+               /*
+                * Now rsp contains compressed or uncompressed resource
+                * record. Next we check if this record answers the question.
+                * The name var contains the uncompressed label.
+                * One tricky bit is the CNAME records as they alias
+                * the name we might be interested in.
+                */
+
+               /*
+                * Go to next answer if the class is not the one we are
+                * looking for.
+                */
+               if (*class != qclass) {
+                       ptr = next;
+                       next = NULL;
+                       continue;
+               }
+
+               /*
+                * Try to resolve aliases also, type is CNAME(5).
+                * This is important as otherwise the aliased names would not
+                * be cached at all as the cache would not contain the aliased
+                * question.
+                *
+                * If any CNAME is found in DNS packet, then we cache the alias
+                * IP address instead of the question (as the server
+                * said that question has only an alias).
+                * This means in practice that if e.g., ipv6.google.com is
+                * queried, DNS server returns CNAME of that name which is
+                * ipv6.l.google.com. We then cache the address of the CNAME
+                * but return the question name to client. So the alias
+                * status of the name is not saved in cache and thus not
+                * returned to the client. We do not return DNS packets from
+                * cache to client saying that ipv6.google.com is an alias to
+                * ipv6.l.google.com but we return instead a DNS packet that
+                * says ipv6.google.com has address xxx which is in fact the
+                * address of ipv6.l.google.com. For caching purposes this
+                * should not cause any issues.
+                */
+               if (*type == 5 && strncmp(question, name, qlen) == 0) {
+                       /*
+                        * So now the alias answered the question. This is
+                        * not very useful from caching point of view as
+                        * the following A or AAAA records will not match the
+                        * question. We need to find the real A/AAAA record
+                        * of the alias and cache that.
+                        */
+                       unsigned char *end = NULL;
+                       int name_len = 0, output_len = 0;
+
+                       memset(rsp, 0, sizeof(rsp));
+                       rsp_len = sizeof(rsp) - 1;
+
+                       /*
+                        * Alias is in rdata part of the message,
+                        * and next-rdlen points to it. So we need to get
+                        * the real name of the alias.
+                        */
+                       ret = get_name(0, buf, next - rdlen, buf + buflen,
+                                       rsp, rsp_len, &output_len, &end,
+                                       name, &name_len);
+                       if (ret != 0) {
+                               /* just ignore the error at this point */
+                               ptr = next;
+                               next = NULL;
+                               continue;
+                       }
+
+                       /*
+                        * We should now have the alias of the entry we might
+                        * want to cache. Just remember it for a while.
+                        * We check the alias list when we have parsed the
+                        * A or AAAA record.
+                        */
+                       aliases = g_slist_prepend(aliases, g_strdup(name));
+
+                       ptr = next;
+                       next = NULL;
+                       continue;
+               }
+
+               if (*type == qtype) {
+                       /*
+                        * We found correct type (A or AAAA)
+                        */
+                       if (check_alias(aliases, name) ||
+                               (!aliases && strncmp(question, name,
+                                                       qlen) == 0)) {
+                               /*
+                                * We found an alias or the name of the rr
+                                * matches the question. If so, we append
+                                * the compressed label to the cache.
+                                * The end result is a response buffer that
+                                * will contain one or more cached and
+                                * compressed resource records.
+                                */
+                               if (*response_len + rsp_len > maxlen) {
+                                       err = -ENOBUFS;
+                                       goto out;
+                               }
+                               memcpy(response + *response_len, rsp, rsp_len);
+                               *response_len += rsp_len;
+                               (*answers)++;
+                               err = 0;
+                       }
+               }
+
+               ptr = next;
+               next = NULL;
+       }
+
+out:
+       for (list = aliases; list; list = list->next)
+               g_free(list->data);
+       g_slist_free(aliases);
+
+       return err;
+}
+
+struct cache_timeout {
+       time_t current_time;
+       int max_timeout;
+       int try_harder;
+};
+
+static gboolean cache_check_entry(gpointer key, gpointer value,
+                                       gpointer user_data)
+{
+       struct cache_timeout *data = user_data;
+       struct cache_entry *entry = value;
+       int max_timeout;
+
+       /* Scale the number of hits by half as part of cache aging */
+
+       entry->hits /= 2;
+
+       /*
+        * If either IPv4 or IPv6 cached entry has expired, we
+        * remove both from the cache.
+        */
+
+       if (entry->ipv4 && entry->ipv4->timeout > 0) {
+               max_timeout = entry->ipv4->cache_until;
+               if (max_timeout > data->max_timeout)
+                       data->max_timeout = max_timeout;
+
+               if (entry->ipv4->cache_until < data->current_time)
+                       return TRUE;
+       }
+
+       if (entry->ipv6 && entry->ipv6->timeout > 0) {
+               max_timeout = entry->ipv6->cache_until;
+               if (max_timeout > data->max_timeout)
+                       data->max_timeout = max_timeout;
+
+               if (entry->ipv6->cache_until < data->current_time)
+                       return TRUE;
+       }
+
+       /*
+        * if we're asked to try harder, also remove entries that have
+        * few hits
+        */
+       if (data->try_harder && entry->hits < 4)
+               return TRUE;
+
+       return FALSE;
+}
+
+static void cache_cleanup(void)
+{
+       static int max_timeout;
+       struct cache_timeout data;
+       int count = 0;
+
+       data.current_time = time(NULL);
+       data.max_timeout = 0;
+       data.try_harder = 0;
+
+       /*
+        * In the first pass, we only remove entries that have timed out.
+        * We use a cache of the first time to expire to do this only
+        * when it makes sense.
+        */
+       if (max_timeout <= data.current_time) {
+               count = g_hash_table_foreach_remove(cache, cache_check_entry,
+                                               &data);
+       }
+       DBG("removed %d in the first pass", count);
+
+       /*
+        * In the second pass, if the first pass turned up blank,
+        * we also expire entries with a low hit count,
+        * while aging the hit count at the same time.
+        */
+       data.try_harder = 1;
+       if (count == 0)
+               count = g_hash_table_foreach_remove(cache, cache_check_entry,
+                                               &data);
+
+       if (count == 0)
+               /*
+                * If we could not remove anything, then remember
+                * what is the max timeout and do nothing if we
+                * have not yet reached it. This will prevent
+                * constant traversal of the cache if it is full.
+                */
+               max_timeout = data.max_timeout;
+       else
+               max_timeout = 0;
+}
+
+static gboolean cache_invalidate_entry(gpointer key, gpointer value,
+                                       gpointer user_data)
+{
+       struct cache_entry *entry = value;
+
+       /* first, delete any expired elements */
+       cache_enforce_validity(entry);
+
+       /* if anything is not expired, mark the entry for refresh */
+       if (entry->hits > 0 && (entry->ipv4 || entry->ipv6))
+               entry->want_refresh = TRUE;
+
+       /* delete the cached data */
+       if (entry->ipv4) {
+               g_free(entry->ipv4->data);
+               g_free(entry->ipv4);
+               entry->ipv4 = NULL;
+       }
+
+       if (entry->ipv6) {
+               g_free(entry->ipv6->data);
+               g_free(entry->ipv6);
+               entry->ipv6 = NULL;
+       }
+
+       /* keep the entry if we want it refreshed, delete it otherwise */
+       if (entry->want_refresh)
+               return FALSE;
+       else
+               return TRUE;
+}
+
+/*
+ * cache_invalidate is called from places where the DNS landscape
+ * has changed, say because connections are added or we entered a VPN.
+ * The logic is to wipe all cache data, but mark all non-expired
+ * parts of the cache for refresh rather than deleting the whole cache.
+ */
+static void cache_invalidate(void)
+{
+       DBG("Invalidating the DNS cache %p", cache);
+
+       if (!cache)
+               return;
+
+       g_hash_table_foreach_remove(cache, cache_invalidate_entry, NULL);
+}
+
+static void cache_refresh_entry(struct cache_entry *entry)
+{
+
+       cache_enforce_validity(entry);
+
+       if (entry->hits > 2 && !entry->ipv4)
+               entry->want_refresh = TRUE;
+       if (entry->hits > 2 && !entry->ipv6)
+               entry->want_refresh = TRUE;
+
+       if (entry->want_refresh) {
+               char *c;
+               char dns_name[NS_MAXDNAME + 1];
+               entry->want_refresh = FALSE;
+
+               /* turn a DNS name into a hostname with dots */
+               strncpy(dns_name, entry->key, NS_MAXDNAME);
+               c = dns_name;
+               while (c && *c) {
+                       int jump;
+                       jump = *c;
+                       *c = '.';
+                       c += jump + 1;
+               }
+               DBG("Refreshing %s\n", dns_name);
+               /* then refresh the hostname */
+               refresh_dns_entry(entry, &dns_name[1]);
+       }
+}
+
+static void cache_refresh_iterator(gpointer key, gpointer value,
+                                       gpointer user_data)
+{
+       struct cache_entry *entry = value;
+
+       cache_refresh_entry(entry);
+}
+
+static void cache_refresh(void)
+{
+       if (!cache)
+               return;
+
+       g_hash_table_foreach(cache, cache_refresh_iterator, NULL);
+}
+
+static int reply_query_type(unsigned char *msg, int len)
+{
+       unsigned char *c;
+       int l;
+       int type;
+
+       /* skip the header */
+       c = msg + sizeof(struct domain_hdr);
+       len -= sizeof(struct domain_hdr);
+
+       if (len < 0)
+               return 0;
+
+       /* now the query, which is a name and 2 16 bit words */
+       l = dns_name_length(c) + 1;
+       c += l;
+       type = c[0] << 8 | c[1];
+
+       return type;
+}
+
+static int cache_update(struct server_data *srv, unsigned char *msg,
+                       unsigned int msg_len)
+{
+       int offset = protocol_offset(srv->protocol);
+       int err, qlen, ttl = 0;
+       uint16_t answers = 0, type = 0, class = 0;
+       struct domain_hdr *hdr = (void *)(msg + offset);
+       struct domain_question *q;
+       struct cache_entry *entry;
+       struct cache_data *data;
+       char question[NS_MAXDNAME + 1];
+       unsigned char response[NS_MAXDNAME + 1];
+       unsigned char *ptr;
+       unsigned int rsplen;
+       gboolean new_entry = TRUE;
+       time_t current_time;
+
+       if (cache_size >= MAX_CACHE_SIZE) {
+               cache_cleanup();
+               if (cache_size >= MAX_CACHE_SIZE)
+                       return 0;
+       }
+
+       current_time = time(NULL);
+
+       /* don't do a cache refresh more than twice a minute */
+       if (next_refresh < current_time) {
+               cache_refresh();
+               next_refresh = current_time + 30;
+       }
+
+       if (offset < 0)
+               return 0;
+
+       DBG("offset %d hdr %p msg %p rcode %d", offset, hdr, msg, hdr->rcode);
+
+       /* Continue only if response code is 0 (=ok) */
+       if (hdr->rcode != ns_r_noerror)
+               return 0;
+
+       if (!cache)
+               create_cache();
+
+       rsplen = sizeof(response) - 1;
+       question[sizeof(question) - 1] = '\0';
+
+       err = parse_response(msg + offset, msg_len - offset,
+                               question, sizeof(question) - 1,
+                               &type, &class, &ttl,
+                               response, &rsplen, &answers);
+
+       /*
+        * special case: if we do a ipv6 lookup and get no result
+        * for a record that's already in our ipv4 cache.. we want
+        * to cache the negative response.
+        */
+       if ((err == -ENOMSG || err == -ENOBUFS) &&
+                       reply_query_type(msg + offset,
+                                       msg_len - offset) == 28) {
+               entry = g_hash_table_lookup(cache, question);
+               if (entry && entry->ipv4 && !entry->ipv6) {
+                       int cache_offset = 0;
+
+                       data = g_try_new(struct cache_data, 1);
+                       if (!data)
+                               return -ENOMEM;
+                       data->inserted = entry->ipv4->inserted;
+                       data->type = type;
+                       data->answers = ntohs(hdr->ancount);
+                       data->timeout = entry->ipv4->timeout;
+                       if (srv->protocol == IPPROTO_UDP)
+                               cache_offset = 2;
+                       data->data_len = msg_len + cache_offset;
+                       data->data = ptr = g_malloc(data->data_len);
+                       ptr[0] = (data->data_len - 2) / 256;
+                       ptr[1] = (data->data_len - 2) - ptr[0] * 256;
+                       if (srv->protocol == IPPROTO_UDP)
+                               ptr += 2;
+                       data->valid_until = entry->ipv4->valid_until;
+                       data->cache_until = entry->ipv4->cache_until;
+                       memcpy(ptr, msg, msg_len);
+                       entry->ipv6 = data;
+                       /*
+                        * we will get a "hit" when we serve the response
+                        * out of the cache
+                        */
+                       entry->hits--;
+                       if (entry->hits < 0)
+                               entry->hits = 0;
+                       return 0;
+               }
+       }
+
+       if (err < 0 || ttl == 0)
+               return 0;
+
+       qlen = strlen(question);
+
+       /*
+        * If the cache contains already data, check if the
+        * type of the cached data is the same and do not add
+        * to cache if data is already there.
+        * This is needed so that we can cache both A and AAAA
+        * records for the same name.
+        */
+       entry = g_hash_table_lookup(cache, question);
+       if (!entry) {
+               entry = g_try_new(struct cache_entry, 1);
+               if (!entry)
+                       return -ENOMEM;
+
+               data = g_try_new(struct cache_data, 1);
+               if (!data) {
+                       g_free(entry);
+                       return -ENOMEM;
+               }
+
+               entry->key = g_strdup(question);
+               entry->ipv4 = entry->ipv6 = NULL;
+               entry->want_refresh = FALSE;
+               entry->hits = 0;
+
+               if (type == 1)
+                       entry->ipv4 = data;
+               else
+                       entry->ipv6 = data;
+       } else {
+               if (type == 1 && entry->ipv4)
+                       return 0;
+
+               if (type == 28 && entry->ipv6)
+                       return 0;
+
+               data = g_try_new(struct cache_data, 1);
+               if (!data)
+                       return -ENOMEM;
+
+               if (type == 1)
+                       entry->ipv4 = data;
+               else
+                       entry->ipv6 = data;
+
+               /*
+                * compensate for the hit we'll get for serving
+                * the response out of the cache
+                */
+               entry->hits--;
+               if (entry->hits < 0)
+                       entry->hits = 0;
+
+               new_entry = FALSE;
+       }
+
+       if (ttl < MIN_CACHE_TTL)
+               ttl = MIN_CACHE_TTL;
+
+       data->inserted = current_time;
+       data->type = type;
+       data->answers = answers;
+       data->timeout = ttl;
+       /*
+        * The "2" in start of the length is the TCP offset. We allocate it
+        * here even for UDP packet because it simplifies the sending
+        * of cached packet.
+        */
+       data->data_len = 2 + 12 + qlen + 1 + 2 + 2 + rsplen;
+       data->data = ptr = g_malloc(data->data_len);
+       data->valid_until = current_time + ttl;
+
+       /*
+        * Restrict the cached DNS record TTL to some sane value
+        * in order to prevent data staying in the cache too long.
+        */
+       if (ttl > MAX_CACHE_TTL)
+               ttl = MAX_CACHE_TTL;
+
+       data->cache_until = round_down_ttl(current_time + ttl, ttl);
+
+       if (!data->data) {
+               g_free(entry->key);
+               g_free(data);
+               g_free(entry);
+               return -ENOMEM;
+       }
+
+       /*
+        * We cache the two extra bytes at the start of the message
+        * in a TCP packet. When sending UDP packet, we skip the first
+        * two bytes. This way we do not need to know the format
+        * (UDP/TCP) of the cached message.
+        */
+       if (srv->protocol == IPPROTO_UDP)
+               memcpy(ptr + 2, msg, offset + 12);
+       else
+               memcpy(ptr, msg, offset + 12);
+
+       ptr[0] = (data->data_len - 2) / 256;
+       ptr[1] = (data->data_len - 2) - ptr[0] * 256;
+       if (srv->protocol == IPPROTO_UDP)
+               ptr += 2;
+
+       memcpy(ptr + offset + 12, question, qlen + 1); /* copy also the \0 */
+
+       q = (void *) (ptr + offset + 12 + qlen + 1);
+       q->type = htons(type);
+       q->class = htons(class);
+       memcpy(ptr + offset + 12 + qlen + 1 + sizeof(struct domain_question),
+               response, rsplen);
+
+       if (new_entry) {
+               g_hash_table_replace(cache, entry->key, entry);
+               cache_size++;
+       }
+
+       DBG("cache %d %squestion \"%s\" type %d ttl %d size %zd packet %u "
+                                                               "dns len %u",
+               cache_size, new_entry ? "new " : "old ",
+               question, type, ttl,
+               sizeof(*entry) + sizeof(*data) + data->data_len + qlen,
+               data->data_len,
+               srv->protocol == IPPROTO_TCP ?
+                       (unsigned int)(data->data[0] * 256 + data->data[1]) :
+                       data->data_len);
+
+       return 0;
+}
+
+static int ns_resolv(struct server_data *server, struct request_data *req,
+                               gpointer request, gpointer name)
+{
+       GList *list;
+       int sk, err, type = 0;
+       char *dot, *lookup = (char *) name;
+       struct cache_entry *entry;
+
+       entry = cache_check(request, &type, req->protocol);
+       if (entry) {
+               int ttl_left = 0;
+               struct cache_data *data;
+
+               DBG("cache hit %s type %s", lookup, type == 1 ? "A" : "AAAA");
+               if (type == 1)
+                       data = entry->ipv4;
+               else
+                       data = entry->ipv6;
+
+               if (data) {
+                       ttl_left = data->valid_until - time(NULL);
+                       entry->hits++;
+               }
+
+               if (data && req->protocol == IPPROTO_TCP) {
+                       send_cached_response(req->client_sk, data->data,
+                                       data->data_len, NULL, 0, IPPROTO_TCP,
+                                       req->srcid, data->answers, ttl_left);
+                       return 1;
+               }
+
+               if (data && req->protocol == IPPROTO_UDP) {
+                       int udp_sk = get_req_udp_socket(req);
+
+                       if (udp_sk < 0)
+                               return -EIO;
+
+                       send_cached_response(udp_sk, data->data,
+                               data->data_len, &req->sa, req->sa_len,
+                               IPPROTO_UDP, req->srcid, data->answers,
+                               ttl_left);
+                       return 1;
+               }
+       }
+#if defined TIZEN_EXT
+       if (server->protocol == IPPROTO_UDP) {
+               GList *domains;
+               struct server_data *new_server = NULL;
+
+               new_server = create_server_sec(server->index, NULL,
+                                               server->server, IPPROTO_UDP);
+
+               if (new_server != NULL) {
+                       for (domains = server->domains; domains;
+                                               domains = domains->next) {
+                               char *dom = domains->data;
+
+                               DBG("Adding domain %s to %s",
+                                               dom, new_server->server);
+
+                               new_server->domains = g_list_append(
+                                               new_server->domains,
+                                                       g_strdup(dom));
+                       }
+
+                       server = new_server;
+               }
+       }
+#endif
+       sk = g_io_channel_unix_get_fd(server->channel);
+
+       err = sendto(sk, request, req->request_len, MSG_NOSIGNAL,
+                       server->server_addr, server->server_addr_len);
+       if (err < 0) {
+               DBG("Cannot send message to server %s sock %d "
+                       "protocol %d (%s/%d)",
+                       server->server, sk, server->protocol,
+                       strerror(errno), errno);
+               return -EIO;
+       }
+
+       req->numserv++;
+
+       /* If we have more than one dot, we don't add domains */
+       dot = strchr(lookup, '.');
+       if (dot && dot != lookup + strlen(lookup) - 1)
+               return 0;
+
+       if (server->domains && server->domains->data)
+               req->append_domain = TRUE;
+
+       for (list = server->domains; list; list = list->next) {
+               char *domain;
+               unsigned char alt[1024];
+               struct domain_hdr *hdr = (void *) &alt;
+               int altlen, domlen, offset;
+
+               domain = list->data;
+
+               if (!domain)
+                       continue;
+
+               offset = protocol_offset(server->protocol);
+               if (offset < 0)
+                       return offset;
+
+               domlen = strlen(domain) + 1;
+               if (domlen < 5)
+                       return -EINVAL;
+
+               alt[offset] = req->altid & 0xff;
+               alt[offset + 1] = req->altid >> 8;
+
+               memcpy(alt + offset + 2, request + offset + 2, 10);
+               hdr->qdcount = htons(1);
+
+               altlen = append_query(alt + offset + 12, sizeof(alt) - 12,
+                                       name, domain);
+               if (altlen < 0)
+                       return -EINVAL;
+
+               altlen += 12;
+
+               memcpy(alt + offset + altlen,
+                       request + offset + altlen - domlen,
+                               req->request_len - altlen - offset + domlen);
+
+               if (server->protocol == IPPROTO_TCP) {
+                       int req_len = req->request_len + domlen - 2;
+
+                       alt[0] = (req_len >> 8) & 0xff;
+                       alt[1] = req_len & 0xff;
+               }
+
+               DBG("req %p dstid 0x%04x altid 0x%04x", req, req->dstid,
+                               req->altid);
+
+               err = send(sk, alt, req->request_len + domlen, MSG_NOSIGNAL);
+               if (err < 0)
+                       return -EIO;
+
+               req->numserv++;
+       }
+
+       return 0;
+}
+
+static char *convert_label(char *start, char *end, char *ptr, char *uptr,
+                       int remaining_len, int *used_comp, int *used_uncomp)
+{
+       int pos, comp_pos;
+       char name[NS_MAXLABEL];
+
+       pos = dn_expand((u_char *)start, (u_char *)end, (u_char *)ptr,
+                       name, NS_MAXLABEL);
+       if (pos < 0) {
+               DBG("uncompress error [%d/%s]", errno, strerror(errno));
+               goto out;
+       }
+
+       /*
+        * We need to compress back the name so that we get back to internal
+        * label presentation.
+        */
+       comp_pos = dn_comp(name, (u_char *)uptr, remaining_len, NULL, NULL);
+       if (comp_pos < 0) {
+               DBG("compress error [%d/%s]", errno, strerror(errno));
+               goto out;
+       }
+
+       *used_comp = pos;
+       *used_uncomp = comp_pos;
+
+       return ptr;
+
+out:
+       return NULL;
+}
+
+static char *uncompress(int16_t field_count, char *start, char *end,
+                       char *ptr, char *uncompressed, int uncomp_len,
+                       char **uncompressed_ptr)
+{
+       char *uptr = *uncompressed_ptr; /* position in result buffer */
+
+       DBG("count %d ptr %p end %p uptr %p", field_count, ptr, end, uptr);
+
+       while (field_count-- > 0 && ptr < end) {
+               int dlen;               /* data field length */
+               int ulen;               /* uncompress length */
+               int pos;                /* position in compressed string */
+               char name[NS_MAXLABEL]; /* tmp label */
+               uint16_t dns_type, dns_class;
+               int comp_pos;
+
+               if (!convert_label(start, end, ptr, name, NS_MAXLABEL,
+                                       &pos, &comp_pos))
+                       goto out;
+
+               /*
+                * Copy the uncompressed resource record, type, class and \0 to
+                * tmp buffer.
+                */
+
+               ulen = strlen(name);
+               strncpy(uptr, name, uncomp_len - (uptr - uncompressed));
+
+               DBG("pos %d ulen %d left %d name %s", pos, ulen,
+                       (int)(uncomp_len - (uptr - uncompressed)), uptr);
+
+               uptr += ulen;
+               *uptr++ = '\0';
+
+               ptr += pos;
+
+               /*
+                * We copy also the fixed portion of the result (type, class,
+                * ttl, address length and the address)
+                */
+               memcpy(uptr, ptr, NS_RRFIXEDSZ);
+
+               dns_type = uptr[0] << 8 | uptr[1];
+               dns_class = uptr[2] << 8 | uptr[3];
+
+               if (dns_class != ns_c_in)
+                       goto out;
+
+               ptr += NS_RRFIXEDSZ;
+               uptr += NS_RRFIXEDSZ;
+
+               /*
+                * Then the variable portion of the result (data length).
+                * Typically this portion is also compressed
+                * so we need to uncompress it also when necessary.
+                */
+               if (dns_type == ns_t_cname) {
+                       if (!convert_label(start, end, ptr, uptr,
+                                       uncomp_len - (uptr - uncompressed),
+                                               &pos, &comp_pos))
+                               goto out;
+
+                       uptr[-2] = comp_pos << 8;
+                       uptr[-1] = comp_pos & 0xff;
+
+                       uptr += comp_pos;
+                       ptr += pos;
+
+               } else if (dns_type == ns_t_a || dns_type == ns_t_aaaa) {
+                       dlen = uptr[-2] << 8 | uptr[-1];
+
+                       if (ptr + dlen > end) {
+                               DBG("data len %d too long", dlen);
+                               goto out;
+                       }
+
+                       memcpy(uptr, ptr, dlen);
+                       uptr += dlen;
+                       ptr += dlen;
+
+               } else if (dns_type == ns_t_soa) {
+                       int total_len = 0;
+                       char *len_ptr;
+
+                       /* Primary name server expansion */
+                       if (!convert_label(start, end, ptr, uptr,
+                                       uncomp_len - (uptr - uncompressed),
+                                               &pos, &comp_pos))
+                               goto out;
+
+                       total_len += comp_pos;
+                       len_ptr = &uptr[-2];
+                       ptr += pos;
+                       uptr += comp_pos;
+
+                       /* Responsible authority's mailbox */
+                       if (!convert_label(start, end, ptr, uptr,
+                                       uncomp_len - (uptr - uncompressed),
+                                               &pos, &comp_pos))
+                               goto out;
+
+                       total_len += comp_pos;
+                       ptr += pos;
+                       uptr += comp_pos;
+
+                       /*
+                        * Copy rest of the soa fields (serial number,
+                        * refresh interval, retry interval, expiration
+                        * limit and minimum ttl). They are 20 bytes long.
+                        */
+                       memcpy(uptr, ptr, 20);
+                       uptr += 20;
+                       ptr += 20;
+                       total_len += 20;
+
+                       /*
+                        * Finally fix the length of the data part
+                        */
+                       len_ptr[0] = total_len << 8;
+                       len_ptr[1] = total_len & 0xff;
+               }
+
+               *uncompressed_ptr = uptr;
+       }
+
+       return ptr;
+
+out:
+       return NULL;
+}
+
+static int strip_domains(char *name, char *answers, int maxlen)
+{
+       uint16_t data_len;
+       int name_len = strlen(name);
+       char *ptr, *start = answers, *end = answers + maxlen;
+
+       while (maxlen > 0) {
+               ptr = strstr(answers, name);
+               if (ptr) {
+                       char *domain = ptr + name_len;
+
+                       if (*domain) {
+                               int domain_len = strlen(domain);
+
+                               memmove(answers + name_len,
+                                       domain + domain_len,
+                                       end - (domain + domain_len));
+
+                               end -= domain_len;
+                               maxlen -= domain_len;
+                       }
+               }
+
+               answers += strlen(answers) + 1;
+               answers += 2 + 2 + 4;  /* skip type, class and ttl fields */
+
+               data_len = answers[0] << 8 | answers[1];
+               answers += 2; /* skip the length field */
+
+               if (answers + data_len > end)
+                       return -EINVAL;
+
+               answers += data_len;
+               maxlen -= answers - ptr;
        }
 
-       g_free(req->resp);
-       g_free(req);
+       return end - start;
+}
+
+static int forward_dns_reply(unsigned char *reply, int reply_len, int protocol,
+                               struct server_data *data)
+{
+       struct domain_hdr *hdr;
+       struct request_data *req;
+       int dns_id, sk, err, offset = protocol_offset(protocol);
+
+       if (offset < 0)
+               return offset;
+
+       hdr = (void *)(reply + offset);
+       dns_id = reply[offset] | reply[offset + 1] << 8;
+
+       DBG("Received %d bytes (id 0x%04x)", reply_len, dns_id);
+
+       req = find_request(dns_id);
+       if (!req)
+               return -EINVAL;
+
+       DBG("req %p dstid 0x%04x altid 0x%04x rcode %d",
+                       req, req->dstid, req->altid, hdr->rcode);
+
+       reply[offset] = req->srcid & 0xff;
+       reply[offset + 1] = req->srcid >> 8;
+
+       req->numresp++;
+
+       if (hdr->rcode == ns_r_noerror || !req->resp) {
+               unsigned char *new_reply = NULL;
+
+               /*
+                * If the domain name was append
+                * remove it before forwarding the reply.
+                * If there were more than one question, then this
+                * domain name ripping can be hairy so avoid that
+                * and bail out in that that case.
+                *
+                * The reason we are doing this magic is that if the
+                * user's DNS client tries to resolv hostname without
+                * domain part, it also expects to get the result without
+                * a domain name part.
+                */
+               if (req->append_domain && ntohs(hdr->qdcount) == 1) {
+                       uint16_t domain_len = 0;
+                       uint16_t header_len;
+                       uint16_t dns_type, dns_class;
+                       uint8_t host_len, dns_type_pos;
+                       char uncompressed[NS_MAXDNAME], *uptr;
+                       char *ptr, *eom = (char *)reply + reply_len;
+
+                       /*
+                        * ptr points to the first char of the hostname.
+                        * ->hostname.domain.net
+                        */
+                       header_len = offset + sizeof(struct domain_hdr);
+                       ptr = (char *)reply + header_len;
+
+                       host_len = *ptr;
+                       if (host_len > 0)
+                               domain_len = strnlen(ptr + 1 + host_len,
+                                               reply_len - header_len);
+
+                       /*
+                        * If the query type is anything other than A or AAAA,
+                        * then bail out and pass the message as is.
+                        * We only want to deal with IPv4 or IPv6 addresses.
+                        */
+                       dns_type_pos = host_len + 1 + domain_len + 1;
+
+                       dns_type = ptr[dns_type_pos] << 8 |
+                                                       ptr[dns_type_pos + 1];
+                       dns_class = ptr[dns_type_pos + 2] << 8 |
+                                                       ptr[dns_type_pos + 3];
+                       if (dns_type != ns_t_a && dns_type != ns_t_aaaa &&
+                                       dns_class != ns_c_in) {
+                               DBG("Pass msg dns type %d class %d",
+                                       dns_type, dns_class);
+                               goto pass;
+                       }
+
+                       /*
+                        * Remove the domain name and replace it by the end
+                        * of reply. Check if the domain is really there
+                        * before trying to copy the data. We also need to
+                        * uncompress the answers if necessary.
+                        * The domain_len can be 0 because if the original
+                        * query did not contain a domain name, then we are
+                        * sending two packets, first without the domain name
+                        * and the second packet with domain name.
+                        * The append_domain is set to true even if we sent
+                        * the first packet without domain name. In this
+                        * case we end up in this branch.
+                        */
+                       if (domain_len > 0) {
+                               int len = host_len + 1;
+                               int new_len, fixed_len;
+                               char *answers;
+
+                               /*
+                                * First copy host (without domain name) into
+                                * tmp buffer.
+                                */
+                               uptr = &uncompressed[0];
+                               memcpy(uptr, ptr, len);
+
+                               uptr[len] = '\0'; /* host termination */
+                               uptr += len + 1;
+
+                               /*
+                                * Copy type and class fields of the question.
+                                */
+                               ptr += len + domain_len + 1;
+                               memcpy(uptr, ptr, NS_QFIXEDSZ);
+
+                               /*
+                                * ptr points to answers after this
+                                */
+                               ptr += NS_QFIXEDSZ;
+                               uptr += NS_QFIXEDSZ;
+                               answers = uptr;
+                               fixed_len = answers - uncompressed;
+
+                               /*
+                                * We then uncompress the result to buffer
+                                * so that we can rip off the domain name
+                                * part from the question. First answers,
+                                * then name server (authority) information,
+                                * and finally additional record info.
+                                */
+
+                               ptr = uncompress(ntohs(hdr->ancount),
+                                               (char *)reply + offset, eom,
+                                               ptr, uncompressed, NS_MAXDNAME,
+                                               &uptr);
+                               if (ptr == NULL)
+                                       goto out;
+
+                               ptr = uncompress(ntohs(hdr->nscount),
+                                               (char *)reply + offset, eom,
+                                               ptr, uncompressed, NS_MAXDNAME,
+                                               &uptr);
+                               if (ptr == NULL)
+                                       goto out;
+
+                               ptr = uncompress(ntohs(hdr->arcount),
+                                               (char *)reply + offset, eom,
+                                               ptr, uncompressed, NS_MAXDNAME,
+                                               &uptr);
+                               if (ptr == NULL)
+                                       goto out;
+
+                               /*
+                                * The uncompressed buffer now contains almost
+                                * valid response. Final step is to get rid of
+                                * the domain name because at least glibc
+                                * gethostbyname() implementation does extra
+                                * checks and expects to find an answer without
+                                * domain name if we asked a query without
+                                * domain part. Note that glibc getaddrinfo()
+                                * works differently and accepts FQDN in answer
+                                */
+                               new_len = strip_domains(uncompressed, answers,
+                                                       uptr - answers);
+                               if (new_len < 0) {
+                                       DBG("Corrupted packet");
+                                       return -EINVAL;
+                               }
+
+                               /*
+                                * Because we have now uncompressed the answers
+                                * we might have to create a bigger buffer to
+                                * hold all that data.
+                                */
+
+                               reply_len = header_len + new_len + fixed_len;
+
+                               new_reply = g_try_malloc(reply_len);
+                               if (!new_reply)
+                                       return -ENOMEM;
+
+                               memcpy(new_reply, reply, header_len);
+                               memcpy(new_reply + header_len, uncompressed,
+                                       new_len + fixed_len);
+
+                               reply = new_reply;
+                       }
+               }
+
+       pass:
+               g_free(req->resp);
+               req->resplen = 0;
+
+               req->resp = g_try_malloc(reply_len);
+               if (!req->resp)
+                       return -ENOMEM;
+
+               memcpy(req->resp, reply, reply_len);
+               req->resplen = reply_len;
+
+               cache_update(data, reply, reply_len);
+
+               g_free(new_reply);
+       }
+
+out:
+       if (req->numresp < req->numserv) {
+               if (hdr->rcode > ns_r_noerror) {
+                       return -EINVAL;
+               } else if (hdr->ancount == 0 && req->append_domain) {
+                       return -EINVAL;
+               }
+       }
+
+       request_list = g_slist_remove(request_list, req);
+
+       if (protocol == IPPROTO_UDP) {
+               sk = get_req_udp_socket(req);
+               if (sk < 0) {
+                       errno = -EIO;
+                       err = -EIO;
+               } else
+                       err = sendto(sk, req->resp, req->resplen, 0,
+                               &req->sa, req->sa_len);
+       } else {
+               sk = req->client_sk;
+               err = send(sk, req->resp, req->resplen, MSG_NOSIGNAL);
+       }
+
+       if (err < 0)
+               DBG("Cannot send msg, sk %d proto %d errno %d/%s", sk,
+                       protocol, errno, strerror(errno));
+       else
+               DBG("proto %d sent %d bytes to %d", protocol, err, sk);
+
+       destroy_request_data(req);
 
        return err;
 }
 
-static void destroy_server(struct server_data *server)
+static void server_destroy_socket(struct server_data *data)
 {
-       GList *list;
+       DBG("index %d server %s proto %d", data->index,
+                                       data->server, data->protocol);
 
-       DBG("interface %s server %s", server->interface, server->server);
+       if (data->watch > 0) {
+               g_source_remove(data->watch);
+               data->watch = 0;
+       }
 
-       server_list = g_slist_remove(server_list, server);
+       if (data->timeout > 0) {
+               g_source_remove(data->timeout);
+               data->timeout = 0;
+       }
 
-       if (server->watch > 0)
-               g_source_remove(server->watch);
+       if (data->channel) {
+               g_io_channel_shutdown(data->channel, TRUE, NULL);
+               g_io_channel_unref(data->channel);
+               data->channel = NULL;
+       }
+
+       g_free(data->incoming_reply);
+       data->incoming_reply = NULL;
+}
 
-       if (server->timeout > 0)
-               g_source_remove(server->timeout);
+static void destroy_server(struct server_data *server)
+{
+       DBG("index %d server %s sock %d", server->index, server->server,
+                       server->channel ?
+                       g_io_channel_unix_get_fd(server->channel): -1);
 
-       g_io_channel_unref(server->channel);
+       server_list = g_slist_remove(server_list, server);
+       server_destroy_socket(server);
 
-       if (server->protocol == IPPROTO_UDP)
-               connman_info("Removing DNS server %s", server->server);
+       if (server->protocol == IPPROTO_UDP && server->enabled)
+               DBG("Removing DNS server %s", server->server);
 
-       g_free(server->incoming_reply);
        g_free(server->server);
-       for (list = server->domains; list; list = list->next) {
-               char *domain = list->data;
+       g_list_free_full(server->domains, g_free);
+       g_free(server->server_addr);
+
+       /*
+        * We do not remove cache right away but delay it few seconds.
+        * The idea is that when IPv6 DNS server is added via RDNSS, it has a
+        * lifetime. When the lifetime expires we decrease the refcount so it
+        * is possible that the cache is then removed. Because a new DNS server
+        * is usually created almost immediately we would then loose the cache
+        * without any good reason. The small delay allows the new RDNSS to
+        * create a new DNS server instance and the refcount does not go to 0.
+        */
+       g_timeout_add_seconds(3, try_remove_cache, NULL);
 
-               server->domains = g_list_remove(server->domains, domain);
-               g_free(domain);
-       }
-       g_free(server->interface);
        g_free(server);
 }
 
@@ -535,12 +2256,11 @@ static gboolean udp_server_event(GIOChannel *channel, GIOCondition condition,
 {
        unsigned char buf[4096];
        int sk, err, len;
+       struct server_data *data = user_data;
 
        if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
-               struct server_data *data = user_data;
-
                connman_error("Error with UDP server %s", data->server);
-               data->watch = 0;
+               server_destroy_socket(data);
                return FALSE;
        }
 
@@ -550,10 +2270,23 @@ static gboolean udp_server_event(GIOChannel *channel, GIOCondition condition,
        if (len < 12)
                return TRUE;
 
-       err = forward_dns_reply(buf, len, IPPROTO_UDP);
+       err = forward_dns_reply(buf, len, IPPROTO_UDP, data);
        if (err < 0)
                return TRUE;
 
+#if defined TIZEN_EXT
+       GSList *list;
+
+       for (list = server_list_sec; list; list = list->next) {
+               struct server_data *new_data = list->data;
+
+               if (new_data == data) {
+                       destroy_server_sec(data);
+                       return TRUE;
+               }
+       }
+#endif
+
        return TRUE;
 }
 
@@ -570,7 +2303,7 @@ static gboolean tcp_server_event(GIOChannel *channel, GIOCondition condition,
        if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
                GSList *list;
 hangup:
-               DBG("TCP server channel closed");
+               DBG("TCP server channel closed, sk %d", sk);
 
                /*
                 * Discard any partial response which is buffered; better
@@ -586,7 +2319,7 @@ hangup:
                        if (req->protocol == IPPROTO_UDP)
                                continue;
 
-                       if (req->request == NULL)
+                       if (!req->request)
                                continue;
 
                        /*
@@ -613,11 +2346,12 @@ hangup:
        if ((condition & G_IO_OUT) && !server->connected) {
                GSList *list;
                GList *domains;
+               gboolean no_request_sent = TRUE;
                struct server_data *udp_server;
 
-               udp_server = find_server(server->interface, server->server,
+               udp_server = find_server(server->index, server->server,
                                                                IPPROTO_UDP);
-               if (udp_server != NULL) {
+               if (udp_server) {
                        for (domains = udp_server->domains; domains;
                                                domains = domains->next) {
                                char *dom = domains->data;
@@ -638,20 +2372,48 @@ hangup:
                        server->timeout = 0;
                }
 
-               for (list = request_list; list; list = list->next) {
+               for (list = request_list; list; ) {
                        struct request_data *req = list->data;
+                       int status;
 
-                       if (req->protocol == IPPROTO_UDP)
+                       if (req->protocol == IPPROTO_UDP) {
+                               list = list->next;
                                continue;
+                       }
 
                        DBG("Sending req %s over TCP", (char *)req->name);
 
+                       status = ns_resolv(server, req,
+                                               req->request, req->name);
+                       if (status > 0) {
+                               /*
+                                * A cached result was sent,
+                                * so the request can be released
+                                */
+                               list = list->next;
+                               request_list = g_slist_remove(request_list, req);
+                               destroy_request_data(req);
+                               continue;
+                       }
+
+                       if (status < 0) {
+                               list = list->next;
+                               continue;
+                       }
+
+                       no_request_sent = FALSE;
+
                        if (req->timeout > 0)
                                g_source_remove(req->timeout);
 
                        req->timeout = g_timeout_add_seconds(30,
                                                request_timeout, req);
-                       ns_resolv(server, req, req->request, req->name);
+                       list = list->next;
+               }
+
+               if (no_request_sent) {
+                       destroy_server(server);
+                       return FALSE;
                }
 
        } else if (condition & G_IO_IN) {
@@ -678,7 +2440,7 @@ hangup:
                        reply_len = reply_len_buf[1] | reply_len_buf[0] << 8;
                        reply_len += 2;
 
-                       DBG("TCP reply %d bytes", reply_len);
+                       DBG("TCP reply %d bytes from %d", reply_len, sk);
 
                        reply = g_try_malloc(sizeof(*reply) + reply_len + 2);
                        if (!reply)
@@ -707,42 +2469,296 @@ hangup:
                        reply->received += bytes_recv;
                }
 
-               forward_dns_reply(reply->buf, reply->received, IPPROTO_TCP);
+               forward_dns_reply(reply->buf, reply->received, IPPROTO_TCP,
+                                       server);
+
+               g_free(reply);
+               server->incoming_reply = NULL;
+
+               destroy_server(server);
+
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+static gboolean tcp_idle_timeout(gpointer user_data)
+{
+       struct server_data *server = user_data;
+
+       DBG("");
+
+       if (!server)
+               return FALSE;
+
+       destroy_server(server);
+
+       return FALSE;
+}
+
+static int server_create_socket(struct server_data *data)
+{
+       int sk, err;
+       char *interface;
+
+       DBG("index %d server %s proto %d", data->index,
+                                       data->server, data->protocol);
+
+       sk = socket(data->server_addr->sa_family,
+               data->protocol == IPPROTO_TCP ? SOCK_STREAM : SOCK_DGRAM,
+               data->protocol);
+       if (sk < 0) {
+               err = errno;
+               connman_error("Failed to create server %s socket",
+                                                       data->server);
+               server_destroy_socket(data);
+               return -err;
+       }
+
+       DBG("sk %d", sk);
+
+       interface = connman_inet_ifname(data->index);
+       if (interface) {
+               if (setsockopt(sk, SOL_SOCKET, SO_BINDTODEVICE,
+                                       interface,
+                                       strlen(interface) + 1) < 0) {
+                       err = errno;
+                       connman_error("Failed to bind server %s "
+                                               "to interface %s",
+                                               data->server, interface);
+                       close(sk);
+                       server_destroy_socket(data);
+                       g_free(interface);
+                       return -err;
+               }
+               g_free(interface);
+       }
+
+       data->channel = g_io_channel_unix_new(sk);
+       if (!data->channel) {
+               connman_error("Failed to create server %s channel",
+                                                       data->server);
+               close(sk);
+               server_destroy_socket(data);
+               return -ENOMEM;
+       }
+
+       g_io_channel_set_close_on_unref(data->channel, TRUE);
+
+       if (data->protocol == IPPROTO_TCP) {
+               g_io_channel_set_flags(data->channel, G_IO_FLAG_NONBLOCK, NULL);
+               data->watch = g_io_add_watch(data->channel,
+                       G_IO_OUT | G_IO_IN | G_IO_HUP | G_IO_NVAL | G_IO_ERR,
+                                               tcp_server_event, data);
+               data->timeout = g_timeout_add_seconds(30, tcp_idle_timeout,
+                                                               data);
+       } else
+               data->watch = g_io_add_watch(data->channel,
+                       G_IO_IN | G_IO_NVAL | G_IO_ERR | G_IO_HUP,
+                                               udp_server_event, data);
+
+       if (connect(sk, data->server_addr, data->server_addr_len) < 0) {
+               err = errno;
+
+               if ((data->protocol == IPPROTO_TCP && errno != EINPROGRESS) ||
+                               data->protocol == IPPROTO_UDP) {
+
+                       connman_error("Failed to connect to server %s",
+                                                               data->server);
+                       server_destroy_socket(data);
+                       return -err;
+               }
+       }
+
+       create_cache();
+
+       return 0;
+}
+
+#if defined TIZEN_EXT
+
+static void destroy_server_sec(struct server_data *server)
+{
+       GList *list;
+
+       DBG("index %d server %s sock %d", server->index, server->server,
+                       server->channel != NULL ?
+                       g_io_channel_unix_get_fd(server->channel): -1);
+
+       server_list_sec = g_slist_remove(server_list_sec, server);
+       server_destroy_socket(server);
+
+       if (server->protocol == IPPROTO_UDP && server->enabled)
+               DBG("Removing DNS server %s", server->server);
+
+       g_free(server->server);
+       for (list = server->domains; list; list = list->next) {
+               char *domain = list->data;
+
+               server->domains = g_list_remove(server->domains, domain);
+               g_free(domain);
+       }
+       g_free(server->server_addr);
+
+       /*
+        * We do not remove cache right away but delay it few seconds.
+        * The idea is that when IPv6 DNS server is added via RDNSS, it has a
+        * lifetime. When the lifetime expires we decrease the refcount so it
+        * is possible that the cache is then removed. Because a new DNS server
+        * is usually created almost immediately we would then loose the cache
+        * without any good reason. The small delay allows the new RDNSS to
+        * create a new DNS server instance and the refcount does not go to 0.
+        */
+       /* TODO: Need to check this */
+       /* g_timeout_add_seconds(3, try_remove_cache, NULL); */
+
+       g_free(server);
+}
+
+static void destroy_all_server_sec()
+{
+       GSList *list;
+
+       DBG("remove all dns server");
+
+       for (list = server_list_sec; list; list = list->next) {
+               struct server_data *server = list->data;
+               destroy_server_sec(server);
+       }
+       server_list_sec = NULL;
+}
+
+static gboolean sec_udp_idle_timeout(gpointer user_data)
+{
+       struct server_data *server = user_data;
+
+       DBG("");
+
+       if (server == NULL)
+               return FALSE;
+
+       destroy_server_sec(server);
+
+       return FALSE;
+}
+
+static struct server_data *create_server_sec(int index,
+                                       const char *domain, const char *server,
+                                       int protocol)
+{
+       struct server_data *data;
+       struct addrinfo hints, *rp;
+       int ret;
+
+       DBG("index %d server %s", index, server);
+
+       data = g_try_new0(struct server_data, 1);
+       if (data == NULL) {
+               connman_error("Failed to allocate server %s data", server);
+               return NULL;
+       }
+
+       data->index = index;
+       if (domain)
+               data->domains = g_list_append(data->domains, g_strdup(domain));
+       data->server = g_strdup(server);
+       data->protocol = protocol;
+
+       memset(&hints, 0, sizeof(hints));
+
+       switch (protocol) {
+       case IPPROTO_UDP:
+               hints.ai_socktype = SOCK_DGRAM;
+               break;
+
+       case IPPROTO_TCP:
+               hints.ai_socktype = SOCK_STREAM;
+               break;
+
+       default:
+               destroy_server_sec(data);
+               return NULL;
+       }
+       hints.ai_family = AF_UNSPEC;
+       hints.ai_flags = AI_NUMERICSERV | AI_NUMERICHOST;
+
+       ret = getaddrinfo(data->server, "53", &hints, &rp);
+       if (ret) {
+               connman_error("Failed to parse server %s address: %s\n",
+                               data->server, gai_strerror(ret));
+               destroy_server_sec(data);
+               return NULL;
+       }
 
-               g_free(reply);
-               server->incoming_reply = NULL;
+       /* Do not blindly copy this code elsewhere; it doesn't loop over the
+          results using ->ai_next as it should. That's OK in *this* case
+          because it was a numeric lookup; we *know* there's only one. */
 
-               destroy_server(server);
+       data->server_addr_len = rp->ai_addrlen;
 
-               return FALSE;
+       switch (rp->ai_family) {
+       case AF_INET:
+               data->server_addr = (struct sockaddr *)
+                                       g_try_new0(struct sockaddr_in, 1);
+               break;
+       case AF_INET6:
+               data->server_addr = (struct sockaddr *)
+                                       g_try_new0(struct sockaddr_in6, 1);
+               break;
+       default:
+               connman_error("Wrong address family %d", rp->ai_family);
+               break;
        }
+       if (data->server_addr == NULL) {
+               freeaddrinfo(rp);
+               destroy_server_sec(data);
+               return NULL;
+       }
+       memcpy(data->server_addr, rp->ai_addr, rp->ai_addrlen);
+       freeaddrinfo(rp);
 
-       return TRUE;
-}
-
-static gboolean tcp_idle_timeout(gpointer user_data)
-{
-       struct server_data *server = user_data;
+       if (server_create_socket(data) != 0) {
+               destroy_server_sec(data);
+               return NULL;
+       }
 
-       DBG("");
+       if (protocol == IPPROTO_UDP) {
+               /* Enable new servers by default */
+               data->enabled = TRUE;
+               DBG("Adding DNS server %s", data->server);
 
-       if (server == NULL)
-               return FALSE;
+               data->timeout = g_timeout_add_seconds(30, sec_udp_idle_timeout,
+                                                               data);
 
-       destroy_server(server);
+               server_list_sec = g_slist_append(server_list_sec, data);
+       }
 
-       return FALSE;
+       return data;
 }
+#endif
 
-static struct server_data *create_server(const char *interface,
+static struct server_data *create_server(int index,
                                        const char *domain, const char *server,
                                        int protocol)
 {
-       struct addrinfo hints, *rp;
        struct server_data *data;
-       int sk, ret;
+       struct addrinfo hints, *rp;
+       int ret;
 
-       DBG("interface %s server %s", interface, server);
+       DBG("index %d server %s", index, server);
+
+       data = g_try_new0(struct server_data, 1);
+       if (!data) {
+               connman_error("Failed to allocate server %s data", server);
+               return NULL;
+       }
+
+       data->index = index;
+       if (domain)
+               data->domains = g_list_append(data->domains, g_strdup(domain));
+       data->server = g_strdup(server);
+       data->protocol = protocol;
 
        memset(&hints, 0, sizeof(hints));
 
@@ -756,118 +2772,64 @@ static struct server_data *create_server(const char *interface,
                break;
 
        default:
+               destroy_server(data);
                return NULL;
        }
        hints.ai_family = AF_UNSPEC;
-       hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV | AI_NUMERICHOST;
+       hints.ai_flags = AI_NUMERICSERV | AI_NUMERICHOST;
 
-       ret = getaddrinfo(server, "53", &hints, &rp);
+       ret = getaddrinfo(data->server, "53", &hints, &rp);
        if (ret) {
                connman_error("Failed to parse server %s address: %s\n",
-                             server, gai_strerror(ret));
+                             data->server, gai_strerror(ret));
+               destroy_server(data);
                return NULL;
        }
+
        /* Do not blindly copy this code elsewhere; it doesn't loop over the
           results using ->ai_next as it should. That's OK in *this* case
           because it was a numeric lookup; we *know* there's only one. */
 
-       sk = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
-       if (sk < 0) {
-               connman_error("Failed to create server %s socket", server);
-               freeaddrinfo(rp);
-               return NULL;
-       }
-
-       if (interface != NULL) {
-               if (setsockopt(sk, SOL_SOCKET, SO_BINDTODEVICE,
-                               interface, strlen(interface) + 1) < 0) {
-                       connman_error("Failed to bind server %s "
-                                               "to interface %s",
-                                                       server, interface);
-                       freeaddrinfo(rp);
-                       close(sk);
-                       return NULL;
-               }
-       }
+       data->server_addr_len = rp->ai_addrlen;
 
-       data = g_try_new0(struct server_data, 1);
-       if (data == NULL) {
-               connman_error("Failed to allocate server %s data", server);
-               freeaddrinfo(rp);
-               close(sk);
-               return NULL;
+       switch (rp->ai_family) {
+       case AF_INET:
+               data->server_addr = (struct sockaddr *)
+                                       g_try_new0(struct sockaddr_in, 1);
+               break;
+       case AF_INET6:
+               data->server_addr = (struct sockaddr *)
+                                       g_try_new0(struct sockaddr_in6, 1);
+               break;
+       default:
+               connman_error("Wrong address family %d", rp->ai_family);
+               break;
        }
-
-       data->channel = g_io_channel_unix_new(sk);
-       if (data->channel == NULL) {
-               connman_error("Failed to create server %s channel", server);
+       if (!data->server_addr) {
                freeaddrinfo(rp);
-               close(sk);
-               g_free(data);
+               destroy_server(data);
                return NULL;
        }
-
-       g_io_channel_set_close_on_unref(data->channel, TRUE);
-
-       if (protocol == IPPROTO_TCP) {
-               g_io_channel_set_flags(data->channel, G_IO_FLAG_NONBLOCK, NULL);
-               data->watch = g_io_add_watch(data->channel,
-                       G_IO_OUT | G_IO_IN | G_IO_HUP | G_IO_NVAL | G_IO_ERR,
-                                               tcp_server_event, data);
-               data->timeout = g_timeout_add_seconds(30, tcp_idle_timeout,
-                                                               data);
-       } else
-               data->watch = g_io_add_watch(data->channel,
-                       G_IO_IN | G_IO_NVAL | G_IO_ERR | G_IO_HUP,
-                                               udp_server_event, data);
-
-       data->interface = g_strdup(interface);
-       if (domain)
-               data->domains = g_list_append(data->domains, g_strdup(domain));
-       data->server = g_strdup(server);
-       data->protocol = protocol;
-
-       ret = connect(sk, rp->ai_addr, rp->ai_addrlen);
+       memcpy(data->server_addr, rp->ai_addr, rp->ai_addrlen);
        freeaddrinfo(rp);
-       if (ret < 0) {
-               if ((protocol == IPPROTO_TCP && errno != EINPROGRESS) ||
-                               protocol == IPPROTO_UDP) {
-                       GList *list;
-
-                       connman_error("Failed to connect to server %s", server);
-                       if (data->watch > 0)
-                               g_source_remove(data->watch);
-                       if (data->timeout > 0)
-                               g_source_remove(data->timeout);
-
-                       g_io_channel_unref(data->channel);
-                       close(sk);
 
-                       g_free(data->server);
-                       g_free(data->interface);
-                       for (list = data->domains; list; list = list->next) {
-                               char *domain = list->data;
-
-                               data->domains = g_list_remove(data->domains,
-                                                                       domain);
-                               g_free(domain);
-                       }
-                       g_free(data);
-                       return NULL;
-               }
+       if (server_create_socket(data) != 0) {
+               destroy_server(data);
+               return NULL;
        }
 
        if (protocol == IPPROTO_UDP) {
-               /* Enable new servers by default */
-               data->enabled = TRUE;
-               connman_info("Adding DNS server %s", data->server);
+               if (__connman_service_index_is_default(data->index) ||
+                               __connman_service_index_is_split_routing(
+                                                               data->index)) {
+                       data->enabled = TRUE;
+                       DBG("Adding DNS server %s", data->server);
+               }
 
                server_list = g_slist_append(server_list, data);
-
-               return data;
        }
 
-       return NULL;
+       return data;
 }
 
 static gboolean resolv(struct request_data *req,
@@ -878,30 +2840,37 @@ static gboolean resolv(struct request_data *req,
        for (list = server_list; list; list = list->next) {
                struct server_data *data = list->data;
 
+               if (data->protocol == IPPROTO_TCP) {
+                       DBG("server %s ignored proto TCP", data->server);
+                       continue;
+               }
+
                DBG("server %s enabled %d", data->server, data->enabled);
 
-               if (data->enabled == FALSE)
+               if (!data->enabled)
                        continue;
 
-               if (data->watch == 0 && data->protocol == IPPROTO_UDP)
-                       data->watch = g_io_add_watch(data->channel,
-                               G_IO_IN | G_IO_NVAL | G_IO_ERR | G_IO_HUP,
-                                               udp_server_event, data);
+               if (!data->channel && data->protocol == IPPROTO_UDP) {
+                       if (server_create_socket(data) < 0) {
+                               DBG("socket creation failed while resolving");
+                               continue;
+                       }
+               }
 
-               if (ns_resolv(data, req, request, name) < 0)
-                       continue;
+               if (ns_resolv(data, req, request, name) > 0)
+                       return TRUE;
        }
 
-       return TRUE;
+       return FALSE;
 }
 
-static void append_domain(const char *interface, const char *domain)
+static void append_domain(int index, const char *domain)
 {
        GSList *list;
 
-       DBG("interface %s domain %s", interface, domain);
+       DBG("index %d domain %s", index, domain);
 
-       if (domain == NULL)
+       if (!domain)
                return;
 
        for (list = server_list; list; list = list->next) {
@@ -910,10 +2879,10 @@ static void append_domain(const char *interface, const char *domain)
                char *dom;
                gboolean dom_found = FALSE;
 
-               if (data->interface == NULL)
+               if (data->index < 0)
                        continue;
 
-               if (g_str_equal(data->interface, interface) == FALSE)
+               if (data->index != index)
                        continue;
 
                for (dom_list = data->domains; dom_list;
@@ -926,71 +2895,80 @@ static void append_domain(const char *interface, const char *domain)
                        }
                }
 
-               if (dom_found == FALSE) {
+               if (!dom_found) {
                        data->domains =
                                g_list_append(data->domains, g_strdup(domain));
                }
        }
 }
 
-int __connman_dnsproxy_append(const char *interface, const char *domain,
+int __connman_dnsproxy_append(int index, const char *domain,
                                                        const char *server)
 {
        struct server_data *data;
 
-       DBG("interface %s server %s", interface, server);
+       DBG("index %d server %s", index, server);
 
-       if (server == NULL && domain == NULL)
+       if (!server && !domain)
                return -EINVAL;
 
-       if (server == NULL) {
-               append_domain(interface, domain);
+       if (!server) {
+               append_domain(index, domain);
 
                return 0;
        }
 
-       if (g_str_equal(server, "127.0.0.1") == TRUE)
+       if (g_str_equal(server, "127.0.0.1"))
+               return -ENODEV;
+
+       if (g_str_equal(server, "::1"))
                return -ENODEV;
 
-       data = find_server(interface, server, IPPROTO_UDP);
-       if (data != NULL) {
-               append_domain(interface, domain);
+       data = find_server(index, server, IPPROTO_UDP);
+       if (data) {
+               append_domain(index, domain);
                return 0;
        }
 
-       data = create_server(interface, domain, server, IPPROTO_UDP);
-       if (data == NULL)
+       data = create_server(index, domain, server, IPPROTO_UDP);
+       if (!data)
                return -EIO;
 
        return 0;
 }
 
-static void remove_server(const char *interface, const char *domain,
+static void remove_server(int index, const char *domain,
                        const char *server, int protocol)
 {
        struct server_data *data;
 
-       data = find_server(interface, server, protocol);
-       if (data == NULL)
+       data = find_server(index, server, protocol);
+       if (!data)
                return;
 
        destroy_server(data);
 }
 
-int __connman_dnsproxy_remove(const char *interface, const char *domain,
+int __connman_dnsproxy_remove(int index, const char *domain,
                                                        const char *server)
 {
-       DBG("interface %s server %s", interface, server);
+       DBG("index %d server %s", index, server);
 
-       if (server == NULL)
+       if (!server)
                return -EINVAL;
 
-       if (g_str_equal(server, "127.0.0.1") == TRUE)
+       if (g_str_equal(server, "127.0.0.1"))
+               return -ENODEV;
+
+       if (g_str_equal(server, "::1"))
                return -ENODEV;
 
-       remove_server(interface, domain, server, IPPROTO_UDP);
-       remove_server(interface, domain, server, IPPROTO_TCP);
+       remove_server(index, domain, server, IPPROTO_UDP);
+       remove_server(index, domain, server, IPPROTO_TCP);
 
+#if defined TIZEN_EXT
+       destroy_all_server_sec();
+#endif
        return 0;
 }
 
@@ -998,17 +2976,26 @@ void __connman_dnsproxy_flush(void)
 {
        GSList *list;
 
-       list = request_pending_list;
+       list = request_list;
        while (list) {
                struct request_data *req = list->data;
 
                list = list->next;
 
-               request_pending_list =
-                               g_slist_remove(request_pending_list, req);
-               resolv(req, req->request, req->name);
-               g_free(req->request);
-               g_free(req->name);
+               if (resolv(req, req->request, req->name)) {
+                       /*
+                        * A cached result was sent,
+                        * so the request can be released
+                        */
+                       request_list =
+                               g_slist_remove(request_list, req);
+                       destroy_request_data(req);
+                       continue;
+               }
+
+               if (req->timeout > 0)
+                       g_source_remove(req->timeout);
+               req->timeout = g_timeout_add_seconds(5, request_timeout, req);
        }
 }
 
@@ -1021,12 +3008,15 @@ static void dnsproxy_offline_mode(connman_bool_t enabled)
        for (list = server_list; list; list = list->next) {
                struct server_data *data = list->data;
 
-               if (enabled == FALSE) {
-                       connman_info("Enabling DNS server %s", data->server);
+               if (!enabled) {
+                       DBG("Enabling DNS server %s", data->server);
                        data->enabled = TRUE;
+                       cache_invalidate();
+                       cache_refresh();
                } else {
-                       connman_info("Disabling DNS server %s", data->server);
+                       DBG("Disabling DNS server %s", data->server);
                        data->enabled = FALSE;
+                       cache_invalidate();
                }
        }
 }
@@ -1034,33 +3024,36 @@ static void dnsproxy_offline_mode(connman_bool_t enabled)
 static void dnsproxy_default_changed(struct connman_service *service)
 {
        GSList *list;
-       char *interface;
+       int index;
 
        DBG("service %p", service);
 
-       if (service == NULL) {
+       /* DNS has changed, invalidate the cache */
+       cache_invalidate();
+
+       if (!service) {
                /* When no services are active, then disable DNS proxying */
                dnsproxy_offline_mode(TRUE);
                return;
        }
 
-       interface = connman_service_get_interface(service);
-       if (interface == NULL)
+       index = __connman_service_get_index(service);
+       if (index < 0)
                return;
 
        for (list = server_list; list; list = list->next) {
                struct server_data *data = list->data;
 
-               if (g_strcmp0(data->interface, interface) == 0) {
-                       connman_info("Enabling DNS server %s", data->server);
+               if (data->index == index) {
+                       DBG("Enabling DNS server %s", data->server);
                        data->enabled = TRUE;
                } else {
-                       connman_info("Disabling DNS server %s", data->server);
+                       DBG("Disabling DNS server %s", data->server);
                        data->enabled = FALSE;
                }
        }
 
-       g_free(interface);
+       cache_refresh();
 }
 
 static struct connman_notifier dnsproxy_notifier = {
@@ -1091,31 +3084,46 @@ static int parse_request(unsigned char *buf, int len,
        if (hdr->qr != 0 || qdcount != 1)
                return -EINVAL;
 
-       memset(name, 0, size);
+       name[0] = '\0';
 
        ptr = buf + sizeof(struct domain_hdr);
        remain = len - sizeof(struct domain_hdr);
 
        while (remain > 0) {
-               uint8_t len = *ptr;
+               uint8_t label_len = *ptr;
 
-               if (len == 0x00) {
+               if (label_len == 0x00) {
                        last_label = (char *) (ptr + 1);
                        break;
                }
 
-               if (used + len + 1 > size)
+               if (used + label_len + 1 > size)
                        return -ENOBUFS;
 
-               strncat(name, (char *) (ptr + 1), len);
+               strncat(name, (char *) (ptr + 1), label_len);
                strcat(name, ".");
 
-               used += len + 1;
+               used += label_len + 1;
 
-               ptr += len + 1;
-               remain -= len + 1;
+               ptr += label_len + 1;
+               remain -= label_len + 1;
        }
 
+#if defined TIZEN_EXT
+       /* parse DNS query type either A or AAAA
+        * enforce to drop AAAA temporarily (IPv6 not supported)
+        */
+       if (last_label != NULL) {
+               uint16_t *type_p = (uint16_t *)last_label;
+               uint16_t type = ntohs(*type_p);
+
+               if (type == 0x1c) {
+                       DBG("query %s is type AAAA(0x%x)", name, type);
+                       return -ENOENT;
+               }
+       }
+#endif
+
        if (last_label && arcount && remain >= 9 && last_label[4] == 0 &&
                                !memcmp(last_label + 5, opt_edns0_type, 2)) {
                uint16_t edns0_bufsize;
@@ -1138,159 +3146,582 @@ static int parse_request(unsigned char *buf, int len,
                }
        }
 
-       DBG("query %s", name);
+       DBG("query %s", name);
+
+       return 0;
+}
+
+static void client_reset(struct tcp_partial_client_data *client)
+{
+       if (!client)
+               return;
+
+       if (client->channel) {
+               DBG("client %d closing",
+                       g_io_channel_unix_get_fd(client->channel));
+
+               g_io_channel_unref(client->channel);
+               client->channel = NULL;
+       }
+
+       if (client->watch > 0) {
+               g_source_remove(client->watch);
+               client->watch = 0;
+       }
+
+       if (client->timeout > 0) {
+               g_source_remove(client->timeout);
+               client->timeout = 0;
+       }
+
+       g_free(client->buf);
+       client->buf = NULL;
+
+       client->buf_end = 0;
+}
+
+static unsigned int get_msg_len(unsigned char *buf)
+{
+       return buf[0]<<8 | buf[1];
+}
+
+static gboolean read_tcp_data(struct tcp_partial_client_data *client,
+                               void *client_addr, socklen_t client_addr_len,
+                               int read_len)
+{
+       char query[TCP_MAX_BUF_LEN];
+       struct request_data *req;
+       int client_sk, err;
+       unsigned int msg_len;
+       GSList *list;
+       gboolean waiting_for_connect = FALSE;
+       int qtype = 0;
+       struct cache_entry *entry;
+
+       client_sk = g_io_channel_unix_get_fd(client->channel);
+
+       if (read_len == 0) {
+               DBG("client %d closed, pending %d bytes",
+                       client_sk, client->buf_end);
+               g_hash_table_remove(partial_tcp_req_table,
+                                       GINT_TO_POINTER(client_sk));
+               return FALSE;
+       }
+
+       DBG("client %d received %d bytes", client_sk, read_len);
+
+       client->buf_end += read_len;
+
+       if (client->buf_end < 2)
+               return TRUE;
+
+       msg_len = get_msg_len(client->buf);
+       if (msg_len > TCP_MAX_BUF_LEN) {
+               DBG("client %d sent too much data %d", client_sk, msg_len);
+               g_hash_table_remove(partial_tcp_req_table,
+                                       GINT_TO_POINTER(client_sk));
+               return FALSE;
+       }
+
+read_another:
+       DBG("client %d msg len %d end %d past end %d", client_sk, msg_len,
+               client->buf_end, client->buf_end - (msg_len + 2));
+
+       if (client->buf_end < (msg_len + 2)) {
+               DBG("client %d still missing %d bytes",
+                       client_sk,
+                       msg_len + 2 - client->buf_end);
+               return TRUE;
+       }
+
+       DBG("client %d all data %d received", client_sk, msg_len);
+
+       err = parse_request(client->buf + 2, msg_len,
+                       query, sizeof(query));
+       if (err < 0 || (g_slist_length(server_list) == 0)) {
+               send_response(client_sk, client->buf, msg_len + 2,
+                       NULL, 0, IPPROTO_TCP);
+               return TRUE;
+       }
+
+       req = g_try_new0(struct request_data, 1);
+       if (!req)
+               return TRUE;
+
+       memcpy(&req->sa, client_addr, client_addr_len);
+       req->sa_len = client_addr_len;
+       req->client_sk = client_sk;
+       req->protocol = IPPROTO_TCP;
+       req->family = client->family;
+
+       req->srcid = client->buf[2] | (client->buf[3] << 8);
+       req->dstid = get_id();
+       req->altid = get_id();
+       req->request_len = msg_len + 2;
+
+       client->buf[2] = req->dstid & 0xff;
+       client->buf[3] = req->dstid >> 8;
+
+       req->numserv = 0;
+       req->ifdata = client->ifdata;
+       req->append_domain = FALSE;
+
+       /*
+        * Check if the answer is found in the cache before
+        * creating sockets to the server.
+        */
+       entry = cache_check(client->buf, &qtype, IPPROTO_TCP);
+       if (entry) {
+               int ttl_left = 0;
+               struct cache_data *data;
+
+               DBG("cache hit %s type %s", query, qtype == 1 ? "A" : "AAAA");
+               if (qtype == 1)
+                       data = entry->ipv4;
+               else
+                       data = entry->ipv6;
+
+               if (data) {
+                       ttl_left = data->valid_until - time(NULL);
+                       entry->hits++;
+
+                       send_cached_response(client_sk, data->data,
+                                       data->data_len, NULL, 0, IPPROTO_TCP,
+                                       req->srcid, data->answers, ttl_left);
+
+                       g_free(req);
+                       goto out;
+               } else
+                       DBG("data missing, ignoring cache for this query");
+       }
+
+       for (list = server_list; list; list = list->next) {
+               struct server_data *data = list->data;
+
+               if (data->protocol != IPPROTO_UDP || !data->enabled)
+                       continue;
+
+               if (!create_server(data->index, NULL, data->server,
+                                       IPPROTO_TCP))
+                       continue;
+
+               waiting_for_connect = TRUE;
+       }
+
+       if (!waiting_for_connect) {
+               /* No server is waiting for connect */
+               send_response(client_sk, client->buf,
+                       req->request_len, NULL, 0, IPPROTO_TCP);
+               g_free(req);
+               return TRUE;
+       }
+
+       /*
+        * The server is not connected yet.
+        * Copy the relevant buffers.
+        * The request will actually be sent once we're
+        * properly connected over TCP to the nameserver.
+        */
+       req->request = g_try_malloc0(req->request_len);
+       if (!req->request) {
+               send_response(client_sk, client->buf,
+                       req->request_len, NULL, 0, IPPROTO_TCP);
+               g_free(req);
+               goto out;
+       }
+       memcpy(req->request, client->buf, req->request_len);
+
+       req->name = g_try_malloc0(sizeof(query));
+       if (!req->name) {
+               send_response(client_sk, client->buf,
+                       req->request_len, NULL, 0, IPPROTO_TCP);
+               g_free(req->request);
+               g_free(req);
+               goto out;
+       }
+       memcpy(req->name, query, sizeof(query));
+
+       req->timeout = g_timeout_add_seconds(30, request_timeout, req);
+
+       request_list = g_slist_append(request_list, req);
+
+out:
+       if (client->buf_end > (msg_len + 2)) {
+               DBG("client %d buf %p -> %p end %d len %d new %d",
+                       client_sk,
+                       client->buf + msg_len + 2,
+                       client->buf, client->buf_end,
+                       TCP_MAX_BUF_LEN - client->buf_end,
+                       client->buf_end - (msg_len + 2));
+               memmove(client->buf, client->buf + msg_len + 2,
+                       TCP_MAX_BUF_LEN - client->buf_end);
+               client->buf_end = client->buf_end - (msg_len + 2);
+
+               /*
+                * If we have a full message waiting, just read it
+                * immediately.
+                */
+               msg_len = get_msg_len(client->buf);
+               if ((msg_len + 2) == client->buf_end) {
+                       DBG("client %d reading another %d bytes", client_sk,
+                                                               msg_len + 2);
+                       goto read_another;
+               }
+       } else {
+               DBG("client %d clearing reading buffer", client_sk);
+
+               client->buf_end = 0;
+               memset(client->buf, 0, TCP_MAX_BUF_LEN);
+
+               /*
+                * We received all the packets from client so we must also
+                * remove the timeout handler here otherwise we might get
+                * timeout while waiting the results from server.
+                */
+               g_source_remove(client->timeout);
+               client->timeout = 0;
+       }
+
+       return TRUE;
+}
+
+static gboolean tcp_client_event(GIOChannel *channel, GIOCondition condition,
+                               gpointer user_data)
+{
+       struct tcp_partial_client_data *client = user_data;
+       struct sockaddr_in6 client_addr6;
+       socklen_t client_addr6_len = sizeof(client_addr6);
+       struct sockaddr_in client_addr4;
+       socklen_t client_addr4_len = sizeof(client_addr4);
+       void *client_addr;
+       socklen_t *client_addr_len;
+       int len, client_sk;
+
+       client_sk = g_io_channel_unix_get_fd(channel);
+
+       if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
+               g_hash_table_remove(partial_tcp_req_table,
+                                       GINT_TO_POINTER(client_sk));
+
+               connman_error("Error with TCP client %d channel", client_sk);
+               return FALSE;
+       }
+
+       switch (client->family) {
+       case AF_INET:
+               client_addr = &client_addr4;
+               client_addr_len = &client_addr4_len;
+               break;
+       case AF_INET6:
+               client_addr = &client_addr6;
+               client_addr_len = &client_addr6_len;
+               break;
+       default:
+               g_hash_table_remove(partial_tcp_req_table,
+                                       GINT_TO_POINTER(client_sk));
+               connman_error("client %p corrupted", client);
+               return FALSE;
+       }
+
+       len = recvfrom(client_sk, client->buf + client->buf_end,
+                       TCP_MAX_BUF_LEN - client->buf_end, 0,
+                       client_addr, client_addr_len);
+       if (len < 0) {
+               if (errno == EAGAIN || errno == EWOULDBLOCK)
+                       return TRUE;
+
+               DBG("client %d cannot read errno %d/%s", client_sk, -errno,
+                       strerror(errno));
+               g_hash_table_remove(partial_tcp_req_table,
+                                       GINT_TO_POINTER(client_sk));
+               return FALSE;
+       }
+
+       return read_tcp_data(client, client_addr, *client_addr_len, len);
+}
+
+static gboolean client_timeout(gpointer user_data)
+{
+       struct tcp_partial_client_data *client = user_data;
+       int sock;
+
+       sock = g_io_channel_unix_get_fd(client->channel);
 
-       return 0;
+       DBG("client %d timeout pending %d bytes", sock, client->buf_end);
+
+       g_hash_table_remove(partial_tcp_req_table, GINT_TO_POINTER(sock));
+
+       return FALSE;
 }
 
-static gboolean tcp_listener_event(GIOChannel *channel, GIOCondition condition,
-                                                       gpointer user_data)
+#if defined TIZEN_EXT
+static void recover_listener(GIOChannel *channel, struct listener_data *ifdata)
 {
-       unsigned char buf[768];
-       char query[512];
-       struct request_data *req;
-       struct server_data *server;
-       int sk, client_sk, len, err;
-       struct sockaddr_in6 client_addr;
-       socklen_t client_addr_len = sizeof(client_addr);
-       GSList *list;
-       struct listener_data *ifdata = user_data;
+       int sk, index;
+
+       index = ifdata->index;
+
+       sk = g_io_channel_unix_get_fd(channel);
+       close(sk);
 
-       DBG("condition 0x%x", condition);
+       __connman_dnsproxy_remove_listener(index);
+
+       if (__connman_dnsproxy_add_listener(index) == 0)
+               DBG("listener %d successfully recovered", index);
+}
+#endif
+
+static gboolean tcp_listener_event(GIOChannel *channel, GIOCondition condition,
+                               struct listener_data *ifdata, int family,
+                               guint *listener_watch)
+{
+       int sk, client_sk, len;
+       unsigned int msg_len;
+       struct tcp_partial_client_data *client;
+       struct sockaddr_in6 client_addr6;
+       socklen_t client_addr6_len = sizeof(client_addr6);
+       struct sockaddr_in client_addr4;
+       socklen_t client_addr4_len = sizeof(client_addr4);
+       void *client_addr;
+       socklen_t *client_addr_len;
+       struct timeval tv;
+       fd_set readfds;
+
+       DBG("condition 0x%02x channel %p ifdata %p family %d",
+               condition, channel, ifdata, family);
 
        if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
-               if (ifdata->tcp_listener_watch > 0)
-                       g_source_remove(ifdata->tcp_listener_watch);
-               ifdata->tcp_listener_watch = 0;
+#if defined TIZEN_EXT
+               connman_error("Error %d with TCP listener channel", condition);
+
+               recover_listener(channel, ifdata);
+#else
+               if (*listener_watch > 0)
+                       g_source_remove(*listener_watch);
+               *listener_watch = 0;
 
                connman_error("Error with TCP listener channel");
+#endif
 
                return FALSE;
        }
 
        sk = g_io_channel_unix_get_fd(channel);
 
-       client_sk = accept(sk, (void *)&client_addr, &client_addr_len);
+       if (family == AF_INET) {
+               client_addr = &client_addr4;
+               client_addr_len = &client_addr4_len;
+       } else {
+               client_addr = &client_addr6;
+               client_addr_len = &client_addr6_len;
+       }
+
+       tv.tv_sec = tv.tv_usec = 0;
+       FD_ZERO(&readfds);
+       FD_SET(sk, &readfds);
+
+       select(sk + 1, &readfds, NULL, NULL, &tv);
+       if (FD_ISSET(sk, &readfds)) {
+               client_sk = accept(sk, client_addr, client_addr_len);
+               DBG("client %d accepted", client_sk);
+       } else {
+               DBG("No data to read from master %d, waiting.", sk);
+               return TRUE;
+       }
+
        if (client_sk < 0) {
                connman_error("Accept failure on TCP listener");
-               ifdata->tcp_listener_watch = 0;
+               *listener_watch = 0;
                return FALSE;
        }
 
-       len = recv(client_sk, buf, sizeof(buf), 0);
-       if (len < 2)
-               return TRUE;
+       fcntl(client_sk, F_SETFL, O_NONBLOCK);
 
-       DBG("Received %d bytes (id 0x%04x)", len, buf[2] | buf[3] << 8);
+       client = g_hash_table_lookup(partial_tcp_req_table,
+                                       GINT_TO_POINTER(client_sk));
+       if (!client) {
+               client = g_try_new0(struct tcp_partial_client_data, 1);
+               if (!client) {
+                       close(client_sk);
+                       return FALSE;
+               }
 
-       err = parse_request(buf + 2, len - 2, query, sizeof(query));
-       if (err < 0 || (g_slist_length(server_list) == 0)) {
-               send_response(client_sk, buf, len, NULL, 0, IPPROTO_TCP);
-               return TRUE;
+               g_hash_table_insert(partial_tcp_req_table,
+                                       GINT_TO_POINTER(client_sk),
+                                       client);
+
+               client->channel = g_io_channel_unix_new(client_sk);
+               g_io_channel_set_close_on_unref(client->channel, TRUE);
+
+               client->watch = g_io_add_watch(client->channel,
+                                               G_IO_IN, tcp_client_event,
+                                               (gpointer)client);
+
+               client->ifdata = ifdata;
+
+               DBG("client %d created %p", client_sk, client);
+       } else {
+               DBG("client %d already exists %p", client_sk, client);
        }
 
-       req = g_try_new0(struct request_data, 1);
-       if (req == NULL)
+       if (!client->buf) {
+               client->buf = g_try_malloc(TCP_MAX_BUF_LEN);
+               if (!client->buf)
+                       return FALSE;
+       }
+       memset(client->buf, 0, TCP_MAX_BUF_LEN);
+       client->buf_end = 0;
+       client->family = family;
+
+       if (client->timeout == 0)
+               client->timeout = g_timeout_add_seconds(2, client_timeout,
+                                                       client);
+
+       /*
+        * Check how much data there is. If all is there, then we can
+        * proceed normally, otherwise read the bits until everything
+        * is received or timeout occurs.
+        */
+       len = recv(client_sk, client->buf, TCP_MAX_BUF_LEN, 0);
+       if (len < 0) {
+               if (errno == EAGAIN || errno == EWOULDBLOCK) {
+                       DBG("client %d no data to read, waiting", client_sk);
+                       return TRUE;
+               }
+
+               DBG("client %d cannot read errno %d/%s", client_sk, -errno,
+                       strerror(errno));
+               g_hash_table_remove(partial_tcp_req_table,
+                                       GINT_TO_POINTER(client_sk));
                return TRUE;
+       }
 
-       memcpy(&req->sa, &client_addr, client_addr_len);
-       req->sa_len = client_addr_len;
-       req->client_sk = client_sk;
-       req->protocol = IPPROTO_TCP;
+       if (len < 2) {
+               DBG("client %d not enough data to read, waiting", client_sk);
+               client->buf_end += len;
+               return TRUE;
+       }
 
-       request_id += 2;
-       if (request_id == 0x0000 || request_id == 0xffff)
-               request_id += 2;
+       msg_len = get_msg_len(client->buf);
+       if (msg_len > TCP_MAX_BUF_LEN) {
+               DBG("client %d invalid message length %u ignoring packet",
+                       client_sk, msg_len);
+               g_hash_table_remove(partial_tcp_req_table,
+                                       GINT_TO_POINTER(client_sk));
+               return TRUE;
+       }
 
-       req->srcid = buf[2] | (buf[3] << 8);
-       req->dstid = request_id;
-       req->altid = request_id + 1;
-       req->request_len = len;
+       /*
+        * The packet length bytes do not contain the total message length,
+        * that is the reason to -2 below.
+        */
+#if defined TIZEN_EXT
+       if (msg_len > (unsigned int)(len - 2)) {
+#else
+       if (msg_len != (unsigned int)(len - 2)) {
+#endif
+               DBG("client %d sent %d bytes but expecting %u pending %d",
+                       client_sk, len, msg_len + 2, msg_len + 2 - len);
 
-       buf[2] = req->dstid & 0xff;
-       buf[3] = req->dstid >> 8;
+               client->buf_end += len;
+               return TRUE;
+       }
 
-       req->numserv = 0;
-       req->ifdata = (struct listener_data *) ifdata;
-       req->append_domain = FALSE;
-       request_list = g_slist_append(request_list, req);
+       return read_tcp_data(client, client_addr, *client_addr_len, len);
+}
 
-       for (list = server_list; list; list = list->next) {
-               struct server_data *data = list->data;
-               GList *domains;
+static gboolean tcp4_listener_event(GIOChannel *channel, GIOCondition condition,
+                               gpointer user_data)
+{
+       struct listener_data *ifdata = user_data;
 
-               if (data->protocol != IPPROTO_UDP || data->enabled == FALSE)
-                       continue;
+       return tcp_listener_event(channel, condition, ifdata, AF_INET,
+                               &ifdata->tcp4_listener_watch);
+}
 
-               server = create_server(data->interface, NULL,
-                                       data->server, IPPROTO_TCP);
+static gboolean tcp6_listener_event(GIOChannel *channel, GIOCondition condition,
+                               gpointer user_data)
+{
+       struct listener_data *ifdata = user_data;
 
-               /*
-                * If server is NULL, we're not connected yet.
-                * Copy the relevant buffers and continue with
-                * the next nameserver.
-                * The request will actually be sent once we're
-                * properly connected over TCP to this nameserver.
-                */
-               if (server == NULL) {
-                       req->request = g_try_malloc0(req->request_len);
-                       if (req->request == NULL)
-                               return TRUE;
+       return tcp_listener_event(channel, condition, user_data, AF_INET6,
+                               &ifdata->tcp6_listener_watch);
+}
 
-                       memcpy(req->request, buf, req->request_len);
+#if defined TIZEN_EXT
+/* Temporarily disable AAAA type to enhance performance (IPv6 not supported) */
+static void __send_response_not_implemented(int sk, unsigned char *buf, int len,
+                               const struct sockaddr *to, socklen_t tolen,
+                               int protocol)
+{
+       struct domain_hdr *hdr;
+       int err, offset = protocol_offset(protocol);
 
-                       req->name = g_try_malloc0(sizeof(query));
-                       if (req->name == NULL) {
-                               g_free(req->request);
-                               return TRUE;
-                       }
-                       memcpy(req->name, query, sizeof(query));
+       DBG("sk %d", sk);
 
-                       continue;
-               }
+       if (offset < 0)
+               return;
 
-               if (req->timeout > 0)
-                       g_source_remove(req->timeout);
+       if (len < 12)
+               return;
 
-               for (domains = data->domains; domains;
-                               domains = domains->next) {
-                       char *dom = domains->data;
+       hdr = (void *) (buf + offset);
 
-                       DBG("Adding domain %s to %s", dom, server->server);
+       DBG("id 0x%04x qr %d opcode %d", hdr->id, hdr->qr, hdr->opcode);
 
-                       server->domains = g_list_append(server->domains,
-                                               g_strdup(dom));
-               }
+       hdr->qr = 1;
+       hdr->rcode = 4;
 
-               req->timeout = g_timeout_add_seconds(30, request_timeout, req);
-               ns_resolv(server, req, buf, query);
-       }
+       hdr->ancount = 0;
+       hdr->nscount = 0;
+       hdr->arcount = 0;
 
-       return TRUE;
+       err = sendto(sk, buf, len, MSG_NOSIGNAL, to, tolen);
+       if (err < 0)
+               connman_error("Failed to send DNS response to %d: %s",
+                               sk, strerror(errno));
 }
+#endif
 
 static gboolean udp_listener_event(GIOChannel *channel, GIOCondition condition,
-                                                       gpointer user_data)
+                               struct listener_data *ifdata, int family,
+                               guint *listener_watch)
 {
        unsigned char buf[768];
        char query[512];
        struct request_data *req;
-       struct sockaddr_in6 client_addr;
-       socklen_t client_addr_len = sizeof(client_addr);
+       struct sockaddr_in6 client_addr6;
+       socklen_t client_addr6_len = sizeof(client_addr6);
+       struct sockaddr_in client_addr4;
+       socklen_t client_addr4_len = sizeof(client_addr4);
+       void *client_addr;
+       socklen_t *client_addr_len;
        int sk, err, len;
-       struct listener_data *ifdata = user_data;
 
        if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
+#if defined TIZEN_EXT
+               connman_error("Error %d with UDP listener channel", condition);
+
+               recover_listener(channel, ifdata);
+#else
                connman_error("Error with UDP listener channel");
-               ifdata->udp_listener_watch = 0;
+               *listener_watch = 0;
+#endif
                return FALSE;
        }
 
        sk = g_io_channel_unix_get_fd(channel);
 
-       memset(&client_addr, 0, client_addr_len);
-       len = recvfrom(sk, buf, sizeof(buf), 0, (void *)&client_addr,
-                      &client_addr_len);
+       if (family == AF_INET) {
+               client_addr = &client_addr4;
+               client_addr_len = &client_addr4_len;
+       } else {
+               client_addr = &client_addr6;
+               client_addr_len = &client_addr6_len;
+       }
+
+       memset(client_addr, 0, *client_addr_len);
+       len = recvfrom(sk, buf, sizeof(buf), 0, client_addr, client_addr_len);
        if (len < 2)
                return TRUE;
 
@@ -1298,42 +3729,80 @@ static gboolean udp_listener_event(GIOChannel *channel, GIOCondition condition,
 
        err = parse_request(buf, len, query, sizeof(query));
        if (err < 0 || (g_slist_length(server_list) == 0)) {
-               send_response(sk, buf, len, (void *)&client_addr,
-                               client_addr_len, IPPROTO_UDP);
+#if defined TIZEN_EXT
+               if (err == -ENOENT) {
+                       /* Temporarily disable AAAA type to enhance performance
+                        * (IPv6 not supported)
+                        */
+                       __send_response_not_implemented(sk, buf, len, client_addr,
+                                                                       *client_addr_len, IPPROTO_UDP);
+                       return TRUE;
+               }
+#endif
+               send_response(sk, buf, len, client_addr,
+                               *client_addr_len, IPPROTO_UDP);
                return TRUE;
        }
 
        req = g_try_new0(struct request_data, 1);
-       if (req == NULL)
+       if (!req)
                return TRUE;
 
-       memcpy(&req->sa, &client_addr, client_addr_len);
-       req->sa_len = client_addr_len;
+       memcpy(&req->sa, client_addr, *client_addr_len);
+       req->sa_len = *client_addr_len;
        req->client_sk = 0;
        req->protocol = IPPROTO_UDP;
-
-       request_id += 2;
-       if (request_id == 0x0000 || request_id == 0xffff)
-               request_id += 2;
+       req->family = family;
 
        req->srcid = buf[0] | (buf[1] << 8);
-       req->dstid = request_id;
-       req->altid = request_id + 1;
+       req->dstid = get_id();
+       req->altid = get_id();
        req->request_len = len;
 
        buf[0] = req->dstid & 0xff;
        buf[1] = req->dstid >> 8;
 
        req->numserv = 0;
-       req->ifdata = (struct listener_data *) ifdata;
-       req->timeout = g_timeout_add_seconds(5, request_timeout, req);
+       req->ifdata = ifdata;
        req->append_domain = FALSE;
+
+       if (resolv(req, buf, query)) {
+               /* a cached result was sent, so the request can be released */
+               g_free(req);
+               return TRUE;
+       }
+
+#if defined TIZEN_EXT
+       DBG("req %p dstid 0x%04x altid 0x%04x", req, req->dstid, req->altid);
+
+       req->timeout = g_timeout_add_seconds(30, request_timeout, req);
+#else
+       req->timeout = g_timeout_add_seconds(5, request_timeout, req);
+#endif
        request_list = g_slist_append(request_list, req);
 
-       return resolv(req, buf, query);
+       return TRUE;
 }
 
-static int create_dns_listener(int protocol, struct listener_data *ifdata)
+static gboolean udp4_listener_event(GIOChannel *channel, GIOCondition condition,
+                               gpointer user_data)
+{
+       struct listener_data *ifdata = user_data;
+
+       return udp_listener_event(channel, condition, ifdata, AF_INET,
+                               &ifdata->udp4_listener_watch);
+}
+
+static gboolean udp6_listener_event(GIOChannel *channel, GIOCondition condition,
+                               gpointer user_data)
+{
+       struct listener_data *ifdata = user_data;
+
+       return udp_listener_event(channel, condition, user_data, AF_INET6,
+                               &ifdata->udp6_listener_watch);
+}
+
+static GIOChannel *get_listener(int family, int protocol, int index)
 {
        GIOChannel *channel;
        const char *proto;
@@ -1343,11 +3812,15 @@ static int create_dns_listener(int protocol, struct listener_data *ifdata)
                struct sockaddr_in sin;
        } s;
        socklen_t slen;
-       int sk, type, v6only = 0;
-       int family = AF_INET6;
-
+       int sk, type;
+#if !defined TIZEN_EXT
+       char *interface;
+#endif
+#if defined TIZEN_EXT
+       int option;
+#endif
 
-       DBG("interface %s", ifdata->ifname);
+       DBG("family %d protocol %d index %d", family, protocol, index);
 
        switch (protocol) {
        case IPPROTO_UDP:
@@ -1361,158 +3834,282 @@ static int create_dns_listener(int protocol, struct listener_data *ifdata)
                break;
 
        default:
-               return -EINVAL;
+               return NULL;
        }
 
        sk = socket(family, type, protocol);
        if (sk < 0 && family == AF_INET6 && errno == EAFNOSUPPORT) {
-               connman_error("No IPv6 support; DNS proxy listening only on Legacy IP");
-               family = AF_INET;
-               sk = socket(family, type, protocol);
+               connman_error("No IPv6 support");
+               return NULL;
        }
+
        if (sk < 0) {
                connman_error("Failed to create %s listener socket", proto);
-               return -EIO;
+               return NULL;
        }
 
-       if (setsockopt(sk, SOL_SOCKET, SO_BINDTODEVICE,
-                                       ifdata->ifname,
-                                       strlen(ifdata->ifname) + 1) < 0) {
-               connman_error("Failed to bind %s listener interface", proto);
-               close(sk);
-               return -EIO;
-       }
-       /* Ensure it accepts Legacy IP connections too */
-       if (family == AF_INET6 &&
-                       setsockopt(sk, SOL_IPV6, IPV6_V6ONLY,
-                                       &v6only, sizeof(v6only)) < 0) {
-               connman_error("Failed to clear V6ONLY on %s listener socket",
-                             proto);
+#if !defined TIZEN_EXT
+       /* ConnMan listens DNS from multiple interfaces
+        * E.g. various technology based and tethering interfaces
+        */
+       interface = connman_inet_ifname(index);
+       if (!interface || setsockopt(sk, SOL_SOCKET, SO_BINDTODEVICE,
+                                       interface,
+                                       strlen(interface) + 1) < 0) {
+               connman_error("Failed to bind %s listener interface "
+                       "for %s (%d/%s)",
+                       proto, family == AF_INET ? "IPv4" : "IPv6",
+                       -errno, strerror(errno));
                close(sk);
-               return -EIO;
+               g_free(interface);
+               return NULL;
        }
+       g_free(interface);
+#endif
 
-       if (family == AF_INET) {
+       if (family == AF_INET6) {
+               memset(&s.sin6, 0, sizeof(s.sin6));
+               s.sin6.sin6_family = AF_INET6;
+               s.sin6.sin6_port = htons(53);
+               slen = sizeof(s.sin6);
+#if defined TIZEN_EXT
+               s.sin6.sin6_addr = in6addr_any;
+#else
+               if (__connman_inet_get_interface_address(index,
+                                               AF_INET6,
+                                               &s.sin6.sin6_addr) < 0) {
+                       /* So we could not find suitable IPv6 address for
+                        * the interface. This could happen if we have
+                        * disabled IPv6 for the interface.
+                        */
+                       close(sk);
+                       return NULL;
+               }
+#endif
+
+       } else if (family == AF_INET) {
                memset(&s.sin, 0, sizeof(s.sin));
                s.sin.sin_family = AF_INET;
                s.sin.sin_port = htons(53);
-               s.sin.sin_addr.s_addr = htonl(INADDR_ANY);
                slen = sizeof(s.sin);
+#if defined TIZEN_EXT
+               s.sin.sin_addr.s_addr = htonl(INADDR_ANY);
+#else
+               if (__connman_inet_get_interface_address(index,
+                                               AF_INET,
+                                               &s.sin.sin_addr) < 0) {
+                       close(sk);
+                       return NULL;
+               }
+#endif
        } else {
-               memset(&s.sin6, 0, sizeof(s.sin6));
-               s.sin6.sin6_family = AF_INET6;
-               s.sin6.sin6_port = htons(53);
-               s.sin6.sin6_addr = in6addr_any;
-               slen = sizeof(s.sin6);
+               close(sk);
+               return NULL;
        }
 
+#if defined TIZEN_EXT
+       if (smack_fsetlabel(sk, "system::use_internet", SMACK_LABEL_IPOUT) != 0)
+               connman_error("Failed to label system::use_internet");
+
+       if (smack_fsetlabel(sk, "system::use_internet", SMACK_LABEL_IPIN) != 0)
+               connman_error("Failed to label system::use_internet");
+#endif
+#if defined TIZEN_EXT
+       /* When ConnMan crashed,
+        * probably DNS listener cannot bind existing address */
+       option = 1;
+       setsockopt(sk, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option));
+#endif
        if (bind(sk, &s.sa, slen) < 0) {
                connman_error("Failed to bind %s listener socket", proto);
                close(sk);
-               return -EIO;
+               return NULL;
        }
 
-       if (protocol == IPPROTO_TCP && listen(sk, 10) < 0) {
-               connman_error("Failed to listen on TCP socket");
-               close(sk);
-               return -EIO;
+       if (protocol == IPPROTO_TCP) {
+
+               if (listen(sk, 10) < 0) {
+                       connman_error("Failed to listen on TCP socket %d/%s",
+                               -errno, strerror(errno));
+                       close(sk);
+                       return NULL;
+               }
+
+               fcntl(sk, F_SETFL, O_NONBLOCK);
        }
 
        channel = g_io_channel_unix_new(sk);
-       if (channel == NULL) {
+       if (!channel) {
                connman_error("Failed to create %s listener channel", proto);
                close(sk);
-               return -EIO;
+               return NULL;
        }
 
        g_io_channel_set_close_on_unref(channel, TRUE);
 
+       return channel;
+}
+
+#define UDP_IPv4_FAILED 0x01
+#define TCP_IPv4_FAILED 0x02
+#define UDP_IPv6_FAILED 0x04
+#define TCP_IPv6_FAILED 0x08
+#define UDP_FAILED (UDP_IPv4_FAILED | UDP_IPv6_FAILED)
+#define TCP_FAILED (TCP_IPv4_FAILED | TCP_IPv6_FAILED)
+#define IPv6_FAILED (UDP_IPv6_FAILED | TCP_IPv6_FAILED)
+#define IPv4_FAILED (UDP_IPv4_FAILED | TCP_IPv4_FAILED)
+
+static int create_dns_listener(int protocol, struct listener_data *ifdata)
+{
+       int ret = 0;
+
        if (protocol == IPPROTO_TCP) {
-               ifdata->tcp_listener_channel = channel;
-               ifdata->tcp_listener_watch = g_io_add_watch(channel,
-                               G_IO_IN, tcp_listener_event, (gpointer) ifdata);
+               ifdata->tcp4_listener_channel = get_listener(AF_INET, protocol,
+                                                       ifdata->index);
+               if (ifdata->tcp4_listener_channel)
+#if defined TIZEN_EXT
+                       ifdata->tcp4_listener_watch =
+                               g_io_add_watch(ifdata->tcp4_listener_channel,
+                                       G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+                                       tcp4_listener_event, (gpointer)ifdata);
+#else
+                       ifdata->tcp4_listener_watch =
+                               g_io_add_watch(ifdata->tcp4_listener_channel,
+                                       G_IO_IN, tcp4_listener_event,
+                                       (gpointer)ifdata);
+#endif
+               else
+                       ret |= TCP_IPv4_FAILED;
+
+               ifdata->tcp6_listener_channel = get_listener(AF_INET6, protocol,
+                                                       ifdata->index);
+               if (ifdata->tcp6_listener_channel)
+#if defined TIZEN_EXT
+                       ifdata->tcp6_listener_watch =
+                               g_io_add_watch(ifdata->tcp6_listener_channel,
+                                       G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+                                       tcp6_listener_event, (gpointer)ifdata);
+#else
+                       ifdata->tcp6_listener_watch =
+                               g_io_add_watch(ifdata->tcp6_listener_channel,
+                                       G_IO_IN, tcp6_listener_event,
+                                       (gpointer)ifdata);
+#endif
+               else
+                       ret |= TCP_IPv6_FAILED;
        } else {
-               ifdata->udp_listener_channel = channel;
-               ifdata->udp_listener_watch = g_io_add_watch(channel,
-                               G_IO_IN, udp_listener_event, (gpointer) ifdata);
+               ifdata->udp4_listener_channel = get_listener(AF_INET, protocol,
+                                                       ifdata->index);
+               if (ifdata->udp4_listener_channel)
+#if defined TIZEN_EXT
+                       ifdata->udp4_listener_watch =
+                               g_io_add_watch(ifdata->udp4_listener_channel,
+                                       G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+                                       udp4_listener_event, (gpointer)ifdata);
+#else
+                       ifdata->udp4_listener_watch =
+                               g_io_add_watch(ifdata->udp4_listener_channel,
+                                       G_IO_IN, udp4_listener_event,
+                                       (gpointer)ifdata);
+#endif
+               else
+                       ret |= UDP_IPv4_FAILED;
+
+               ifdata->udp6_listener_channel = get_listener(AF_INET6, protocol,
+                                                       ifdata->index);
+               if (ifdata->udp6_listener_channel)
+#if defined TIZEN_EXT
+                       ifdata->udp6_listener_watch =
+                               g_io_add_watch(ifdata->udp6_listener_channel,
+                                       G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+                                       udp6_listener_event, (gpointer)ifdata);
+#else
+                       ifdata->udp6_listener_watch =
+                               g_io_add_watch(ifdata->udp6_listener_channel,
+                                       G_IO_IN, udp6_listener_event,
+                                       (gpointer)ifdata);
+#endif
+               else
+                       ret |= UDP_IPv6_FAILED;
        }
 
-       return 0;
+       return ret;
 }
 
 static void destroy_udp_listener(struct listener_data *ifdata)
 {
-       DBG("interface %s", ifdata->ifname);
+       DBG("index %d", ifdata->index);
+
+       if (ifdata->udp4_listener_watch > 0)
+               g_source_remove(ifdata->udp4_listener_watch);
 
-       if (ifdata->udp_listener_watch > 0)
-               g_source_remove(ifdata->udp_listener_watch);
+       if (ifdata->udp6_listener_watch > 0)
+               g_source_remove(ifdata->udp6_listener_watch);
 
-       g_io_channel_unref(ifdata->udp_listener_channel);
+       if (ifdata->udp4_listener_channel)
+               g_io_channel_unref(ifdata->udp4_listener_channel);
+       if (ifdata->udp6_listener_channel)
+               g_io_channel_unref(ifdata->udp6_listener_channel);
 }
 
 static void destroy_tcp_listener(struct listener_data *ifdata)
 {
-       DBG("interface %s", ifdata->ifname);
+       DBG("index %d", ifdata->index);
 
-       if (ifdata->tcp_listener_watch > 0)
-               g_source_remove(ifdata->tcp_listener_watch);
+       if (ifdata->tcp4_listener_watch > 0)
+               g_source_remove(ifdata->tcp4_listener_watch);
+       if (ifdata->tcp6_listener_watch > 0)
+               g_source_remove(ifdata->tcp6_listener_watch);
 
-       g_io_channel_unref(ifdata->tcp_listener_channel);
+       if (ifdata->tcp4_listener_channel)
+               g_io_channel_unref(ifdata->tcp4_listener_channel);
+       if (ifdata->tcp6_listener_channel)
+               g_io_channel_unref(ifdata->tcp6_listener_channel);
 }
 
 static int create_listener(struct listener_data *ifdata)
 {
-       int err;
+       int err, index;
 
        err = create_dns_listener(IPPROTO_UDP, ifdata);
-       if (err < 0)
-               return err;
+       if ((err & UDP_FAILED) == UDP_FAILED)
+               return -EIO;
 
-       err = create_dns_listener(IPPROTO_TCP, ifdata);
-       if (err < 0) {
+       err |= create_dns_listener(IPPROTO_TCP, ifdata);
+       if ((err & TCP_FAILED) == TCP_FAILED) {
                destroy_udp_listener(ifdata);
-               return err;
+               return -EIO;
        }
 
-       if (g_strcmp0(ifdata->ifname, "lo") == 0)
-               __connman_resolvfile_append("lo", NULL, "127.0.0.1");
+       index = connman_inet_ifindex("lo");
+       if (ifdata->index == index) {
+               if ((err & IPv6_FAILED) != IPv6_FAILED)
+                       __connman_resolvfile_append(index, NULL, "::1");
+
+               if ((err & IPv4_FAILED) != IPv4_FAILED)
+                       __connman_resolvfile_append(index, NULL, "127.0.0.1");
+       }
 
        return 0;
 }
 
 static void destroy_listener(struct listener_data *ifdata)
 {
+       int index;
        GSList *list;
 
-       if (g_strcmp0(ifdata->ifname, "lo") == 0)
-               __connman_resolvfile_remove("lo", NULL, "127.0.0.1");
-
-       for (list = request_pending_list; list; list = list->next) {
-               struct request_data *req = list->data;
-
-               DBG("Dropping pending request (id 0x%04x -> 0x%04x)",
-                                               req->srcid, req->dstid);
-
-               g_free(req->resp);
-               g_free(req->request);
-               g_free(req->name);
-               g_free(req);
-               list->data = NULL;
+       index = connman_inet_ifindex("lo");
+       if (ifdata->index == index) {
+               __connman_resolvfile_remove(index, NULL, "127.0.0.1");
+               __connman_resolvfile_remove(index, NULL, "::1");
        }
 
-       g_slist_free(request_pending_list);
-       request_pending_list = NULL;
-
        for (list = request_list; list; list = list->next) {
                struct request_data *req = list->data;
 
                DBG("Dropping request (id 0x%04x -> 0x%04x)",
                                                req->srcid, req->dstid);
-
-               g_free(req->resp);
-               g_free(req->request);
-               g_free(req->name);
-               g_free(req);
+               destroy_request_data(req);
                list->data = NULL;
        }
 
@@ -1523,67 +4120,102 @@ static void destroy_listener(struct listener_data *ifdata)
        destroy_udp_listener(ifdata);
 }
 
-int __connman_dnsproxy_add_listener(const char *interface)
+int __connman_dnsproxy_add_listener(int index)
 {
        struct listener_data *ifdata;
        int err;
 
-       DBG("interface %s", interface);
+       DBG("index %d", index);
+
+       if (index < 0)
+               return -EINVAL;
+
+       if (!listener_table)
+               return -ENOENT;
 
-       if (g_hash_table_lookup(listener_table, interface) != NULL)
+       if (g_hash_table_lookup(listener_table, GINT_TO_POINTER(index)))
                return 0;
 
        ifdata = g_try_new0(struct listener_data, 1);
-       if (ifdata == NULL)
+       if (!ifdata)
                return -ENOMEM;
 
-       ifdata->ifname = g_strdup(interface);
-       ifdata->udp_listener_channel = NULL;
-       ifdata->udp_listener_watch = 0;
-       ifdata->tcp_listener_channel = NULL;
-       ifdata->tcp_listener_watch = 0;
+       ifdata->index = index;
+       ifdata->udp4_listener_channel = NULL;
+       ifdata->udp4_listener_watch = 0;
+       ifdata->tcp4_listener_channel = NULL;
+       ifdata->tcp4_listener_watch = 0;
+       ifdata->udp6_listener_channel = NULL;
+       ifdata->udp6_listener_watch = 0;
+       ifdata->tcp6_listener_channel = NULL;
+       ifdata->tcp6_listener_watch = 0;
 
        err = create_listener(ifdata);
        if (err < 0) {
-               connman_error("Couldn't create listener for %s err %d",
-                               interface, err);
-               g_free(ifdata->ifname);
+               connman_error("Couldn't create listener for index %d err %d",
+                               index, err);
                g_free(ifdata);
                return err;
        }
-       g_hash_table_insert(listener_table, ifdata->ifname, ifdata);
+       g_hash_table_insert(listener_table, GINT_TO_POINTER(ifdata->index),
+                       ifdata);
        return 0;
 }
 
-void __connman_dnsproxy_remove_listener(const char *interface)
+void __connman_dnsproxy_remove_listener(int index)
 {
        struct listener_data *ifdata;
 
-       DBG("interface %s", interface);
+       DBG("index %d", index);
+
+       if (!listener_table)
+               return;
 
-       ifdata = g_hash_table_lookup(listener_table, interface);
-       if (ifdata == NULL)
+       ifdata = g_hash_table_lookup(listener_table, GINT_TO_POINTER(index));
+       if (!ifdata)
                return;
 
        destroy_listener(ifdata);
 
-       g_hash_table_remove(listener_table, interface);
+       g_hash_table_remove(listener_table, GINT_TO_POINTER(index));
 }
 
 static void remove_listener(gpointer key, gpointer value, gpointer user_data)
 {
-       __connman_dnsproxy_remove_listener(key);
+       int index = GPOINTER_TO_INT(key);
+       struct listener_data *ifdata = value;
+
+       DBG("index %d", index);
+
+       destroy_listener(ifdata);
+}
+
+static void free_partial_reqs(gpointer value)
+{
+       struct tcp_partial_client_data *data = value;
+
+       client_reset(data);
+       g_free(data);
 }
 
 int __connman_dnsproxy_init(void)
 {
-       int err;
+       int err, index;
 
        DBG("");
 
-       listener_table = g_hash_table_new_full(g_str_hash, g_str_equal,
-                                                       g_free, g_free);
-       err = __connman_dnsproxy_add_listener("lo");
+       srandom(time(NULL));
+
+       listener_table = g_hash_table_new_full(g_direct_hash, g_direct_equal,
+                                                       NULL, g_free);
+
+       partial_tcp_req_table = g_hash_table_new_full(g_direct_hash,
+                                                       g_direct_equal,
+                                                       NULL,
+                                                       free_partial_reqs);
+
+       index = connman_inet_ifindex("lo");
+       err = __connman_dnsproxy_add_listener(index);
        if (err < 0)
                return err;
 
@@ -1594,8 +4226,9 @@ int __connman_dnsproxy_init(void)
        return 0;
 
 destroy:
-       __connman_dnsproxy_remove_listener("lo");
+       __connman_dnsproxy_remove_listener(index);
        g_hash_table_destroy(listener_table);
+       g_hash_table_destroy(partial_tcp_req_table);
 
        return err;
 }
@@ -1609,4 +4242,6 @@ void __connman_dnsproxy_cleanup(void)
        g_hash_table_foreach(listener_table, remove_listener, NULL);
 
        g_hash_table_destroy(listener_table);
+
+       g_hash_table_destroy(partial_tcp_req_table);
 }
index 7f6b8c9..955b4b8 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
@@ -58,10 +58,12 @@ DBusMessage *__connman_error_failed(DBusMessage *msg, int errnum)
        case EISCONN:
                return __connman_error_already_connected(msg);
        case ENOTCONN:
+       case ECONNREFUSED:
                return __connman_error_not_connected(msg);
        case ETIMEDOUT:
                return __connman_error_operation_timeout(msg);
        case EALREADY:
+       case EINPROGRESS:
                return __connman_error_in_progress(msg);
        case ENOKEY:
                return __connman_error_passphrase_required(msg);
index a2a66da..b363567 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *  Copyright (C) 2003-2005  Go-Core Project
  *  Copyright (C) 2003-2006  Helsinki University of Technology
  *
@@ -35,6 +35,7 @@
 #include <sys/ioctl.h>
 #include <sys/socket.h>
 #include <linux/sockios.h>
+#include <netdb.h>
 #include <arpa/inet.h>
 #include <net/route.h>
 #include <net/ethernet.h>
@@ -43,6 +44,8 @@
 #include <netinet/icmp6.h>
 #include <fcntl.h>
 #include <linux/if_tun.h>
+#include <ctype.h>
+#include <ifaddrs.h>
 
 #include "connman.h"
 
@@ -50,8 +53,8 @@
        ((struct rtattr *) (((uint8_t*) (nmsg)) +       \
        NLMSG_ALIGN((nmsg)->nlmsg_len)))
 
-static int add_rtattr(struct nlmsghdr *n, size_t max_length, int type,
-                               const void *data, size_t data_length)
+int __connman_inet_rtnl_addattr_l(struct nlmsghdr *n, size_t max_length,
+                               int type, const void *data, size_t data_length)
 {
        size_t length;
        struct rtattr *rta;
@@ -128,26 +131,41 @@ int __connman_inet_modify_address(int cmd, int flags,
                        if (inet_pton(AF_INET, peer, &ipv4_dest) < 1)
                                return -1;
 
-                       if ((err = add_rtattr(header, sizeof(request),
-                                       IFA_ADDRESS,
-                                       &ipv4_dest, sizeof(ipv4_dest))) < 0)
-                       return err;
+                       err = __connman_inet_rtnl_addattr_l(header,
+                                                       sizeof(request),
+                                                       IFA_ADDRESS,
+                                                       &ipv4_dest,
+                                                       sizeof(ipv4_dest));
+                       if (err < 0)
+                               return err;
                }
 
-               if ((err = add_rtattr(header, sizeof(request), IFA_LOCAL,
-                               &ipv4_addr, sizeof(ipv4_addr))) < 0)
+               err = __connman_inet_rtnl_addattr_l(header,
+                                               sizeof(request),
+                                               IFA_LOCAL,
+                                               &ipv4_addr,
+                                               sizeof(ipv4_addr));
+               if (err < 0)
                        return err;
 
-               if ((err = add_rtattr(header, sizeof(request), IFA_BROADCAST,
-                               &ipv4_bcast, sizeof(ipv4_bcast))) < 0)
+               err = __connman_inet_rtnl_addattr_l(header,
+                                               sizeof(request),
+                                               IFA_BROADCAST,
+                                               &ipv4_bcast,
+                                               sizeof(ipv4_bcast));
+               if (err < 0)
                        return err;
 
        } else if (family == AF_INET6) {
                if (inet_pton(AF_INET6, address, &ipv6_addr) < 1)
                        return -1;
 
-               if ((err = add_rtattr(header, sizeof(request), IFA_LOCAL,
-                               &ipv6_addr, sizeof(ipv6_addr))) < 0)
+               err = __connman_inet_rtnl_addattr_l(header,
+                                               sizeof(request),
+                                               IFA_LOCAL,
+                                               &ipv6_addr,
+                                               sizeof(ipv6_addr));
+               if (err < 0)
                        return err;
        }
 
@@ -183,7 +201,7 @@ int connman_inet_ifindex(const char *name)
                return -1;
 
        memset(&ifr, 0, sizeof(ifr));
-       strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
+       strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name) - 1);
 
        err = ioctl(sk, SIOCGIFINDEX, &ifr);
 
@@ -217,7 +235,7 @@ char *connman_inet_ifname(int index)
        if (err < 0)
                return NULL;
 
-       return strdup(ifr.ifr_name);
+       return g_strdup(ifr.ifr_name);
 }
 
 short int connman_inet_ifflags(int index)
@@ -277,7 +295,7 @@ int connman_inet_ifup(int index)
                goto done;
        }
 
-       ifr.ifr_flags |= IFF_UP;
+       ifr.ifr_flags |= (IFF_UP|IFF_DYNAMIC);
 
        if (ioctl(sk, SIOCSIFFLAGS, &ifr) < 0) {
                err = -errno;
@@ -294,7 +312,8 @@ done:
 
 int connman_inet_ifdown(int index)
 {
-       struct ifreq ifr;
+       struct ifreq ifr, addr_ifr;
+       struct sockaddr_in *addr;
        int sk, err;
 
        sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
@@ -314,12 +333,19 @@ int connman_inet_ifdown(int index)
                goto done;
        }
 
+       memset(&addr_ifr, 0, sizeof(addr_ifr));
+       memcpy(&addr_ifr.ifr_name, &ifr.ifr_name, sizeof(ifr.ifr_name) - 1);
+       addr = (struct sockaddr_in *)&addr_ifr.ifr_addr;
+       addr->sin_family = AF_INET;
+       if (ioctl(sk, SIOCSIFADDR, &addr_ifr) < 0)
+               connman_warn("Could not clear IPv4 address index %d", index);
+
        if (!(ifr.ifr_flags & IFF_UP)) {
                err = -EALREADY;
                goto done;
        }
 
-       ifr.ifr_flags &= ~IFF_UP;
+       ifr.ifr_flags = (ifr.ifr_flags & ~IFF_UP) | IFF_DYNAMIC;
 
        if (ioctl(sk, SIOCSIFFLAGS, &ifr) < 0)
                err = -errno;
@@ -404,7 +430,7 @@ static char *index2ident(int index, const char *prefix)
 
        len = prefix ? strlen(prefix) + 18 : 18;
 
-       str = malloc(len);
+       str = g_malloc(len);
        if (!str)
                return NULL;
 
@@ -467,7 +493,7 @@ struct connman_device *connman_inet_create_device(int index)
 
        if (__connman_device_isfiltered(devname) == TRUE) {
                connman_info("Ignoring interface %s (filtered)", devname);
-               free(devname);
+               g_free(devname);
                return NULL;
        }
 
@@ -476,12 +502,11 @@ struct connman_device *connman_inet_create_device(int index)
        switch (type) {
        case CONNMAN_DEVICE_TYPE_UNKNOWN:
                connman_info("Ignoring interface %s (type unknown)", devname);
-               free(devname);
+               g_free(devname);
                return NULL;
        case CONNMAN_DEVICE_TYPE_ETHERNET:
        case CONNMAN_DEVICE_TYPE_GADGET:
        case CONNMAN_DEVICE_TYPE_WIFI:
-       case CONNMAN_DEVICE_TYPE_WIMAX:
                name = index2ident(index, "");
                addr = index2addr(index);
                break;
@@ -489,7 +514,7 @@ struct connman_device *connman_inet_create_device(int index)
        case CONNMAN_DEVICE_TYPE_CELLULAR:
        case CONNMAN_DEVICE_TYPE_GPS:
        case CONNMAN_DEVICE_TYPE_VENDOR:
-               name = strdup(devname);
+               name = g_strdup(devname);
                break;
        }
 
@@ -507,7 +532,6 @@ struct connman_device *connman_inet_create_device(int index)
                ident = index2ident(index, NULL);
                break;
        case CONNMAN_DEVICE_TYPE_WIFI:
-       case CONNMAN_DEVICE_TYPE_WIMAX:
                ident = index2ident(index, NULL);
                break;
        case CONNMAN_DEVICE_TYPE_BLUETOOTH:
@@ -522,19 +546,75 @@ struct connman_device *connman_inet_create_device(int index)
 
        if (ident != NULL) {
                connman_device_set_ident(device, ident);
-               free(ident);
+               g_free(ident);
        }
 
        connman_device_set_string(device, "Address", addr);
 
 done:
-       free(devname);
-       free(name);
-       free(addr);
+       g_free(devname);
+       g_free(name);
+       g_free(addr);
 
        return device;
 }
 
+#if defined TIZEN_EXT
+void connman_inet_update_device_ident(struct connman_device *device)
+{
+       int index;
+       enum connman_device_type type;
+       char *ident = NULL, *addr = NULL;
+
+       index = connman_device_get_index(device);
+       type = connman_device_get_type(device);
+
+       switch (type) {
+       case CONNMAN_DEVICE_TYPE_UNKNOWN:
+               return;
+       case CONNMAN_DEVICE_TYPE_ETHERNET:
+       case CONNMAN_DEVICE_TYPE_GADGET:
+       case CONNMAN_DEVICE_TYPE_WIFI:
+               addr = index2addr(index);
+               break;
+       case CONNMAN_DEVICE_TYPE_BLUETOOTH:
+       case CONNMAN_DEVICE_TYPE_CELLULAR:
+       case CONNMAN_DEVICE_TYPE_GPS:
+       case CONNMAN_DEVICE_TYPE_VENDOR:
+               break;
+       }
+
+       switch (type) {
+       case CONNMAN_DEVICE_TYPE_UNKNOWN:
+       case CONNMAN_DEVICE_TYPE_VENDOR:
+       case CONNMAN_DEVICE_TYPE_GPS:
+               break;
+       case CONNMAN_DEVICE_TYPE_ETHERNET:
+       case CONNMAN_DEVICE_TYPE_GADGET:
+               ident = index2ident(index, NULL);
+               break;
+       case CONNMAN_DEVICE_TYPE_WIFI:
+               ident = index2ident(index, NULL);
+               break;
+       case CONNMAN_DEVICE_TYPE_BLUETOOTH:
+               break;
+       case CONNMAN_DEVICE_TYPE_CELLULAR:
+               ident = index2ident(index, NULL);
+               break;
+       }
+
+       if (ident != NULL) {
+               connman_device_set_ident(device, ident);
+               g_free(ident);
+       }
+
+       if (addr != NULL) {
+               connman_device_set_string(device, "Address", addr);
+               g_free(addr);
+       }
+}
+#endif
+
 struct in6_ifreq {
        struct in6_addr ifr6_addr;
        __u32 ifr6_prefixlen;
@@ -601,6 +681,9 @@ int connman_inet_clear_ipv6_address(int index, const char *address,
 
        DBG("index %d address %s prefix_len %d", index, address, prefix_len);
 
+       if (address == NULL)
+               return -EINVAL;
+
        err = __connman_inet_modify_address(RTM_DELADDR, 0, index, AF_INET6,
                                address, NULL, prefix_len, NULL);
        if (err < 0) {
@@ -624,6 +707,9 @@ int connman_inet_clear_address(int index, struct connman_ipaddress *ipaddress)
 
        DBG("index %d address %s prefix_len %d", index, address, prefix_len);
 
+       if (address == NULL)
+               return -EINVAL;
+
        err = __connman_inet_modify_address(RTM_DELADDR, 0, index, AF_INET,
                                address, peer, prefix_len, broadcast);
        if (err < 0) {
@@ -652,18 +738,24 @@ int connman_inet_add_network_route(int index, const char *host,
        struct ifreq ifr;
        struct rtentry rt;
        struct sockaddr_in addr;
-       int sk, err;
+       int sk, err = 0;
+
+       DBG("index %d host %s gateway %s netmask %s", index,
+               host, gateway, netmask);
 
        sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
-       if (sk < 0)
-               return -1;
+       if (sk < 0) {
+               err = -errno;
+               goto out;
+       }
 
        memset(&ifr, 0, sizeof(ifr));
        ifr.ifr_ifindex = index;
 
        if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
+               err = -errno;
                close(sk);
-               return -1;
+               goto out;
        }
 
        DBG("ifname %s", ifr.ifr_name);
@@ -699,13 +791,16 @@ int connman_inet_add_network_route(int index, const char *host,
 
        rt.rt_dev = ifr.ifr_name;
 
-       err = ioctl(sk, SIOCADDRT, &rt);
-       if (err < 0)
-               connman_error("Adding host route failed (%s)",
-                                                       strerror(errno));
+       if (ioctl(sk, SIOCADDRT, &rt) < 0 && errno != EEXIST)
+               err = -errno;
 
        close(sk);
 
+out:
+       if (err < 0)
+               connman_error("Adding host route failed (%s)",
+                                                       strerror(-err));
+
        return err;
 }
 
@@ -714,18 +809,23 @@ int connman_inet_del_network_route(int index, const char *host)
        struct ifreq ifr;
        struct rtentry rt;
        struct sockaddr_in addr;
-       int sk, err;
+       int sk, err = 0;
+
+       DBG("index %d host %s", index, host);
 
        sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
-       if (sk < 0)
-               return -1;
+       if (sk < 0) {
+               err = -errno;
+               goto out;
+       }
 
        memset(&ifr, 0, sizeof(ifr));
        ifr.ifr_ifindex = index;
 
        if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
+               err = -errno;
                close(sk);
-               return -1;
+               goto out;
        }
 
        DBG("ifname %s", ifr.ifr_name);
@@ -740,13 +840,16 @@ int connman_inet_del_network_route(int index, const char *host)
 
        rt.rt_dev = ifr.ifr_name;
 
-       err = ioctl(sk, SIOCDELRT, &rt);
-       if (err < 0)
-               connman_error("Deleting host route failed (%s)",
-                                                       strerror(errno));
+       if (ioctl(sk, SIOCDELRT, &rt) < 0 && errno != ESRCH)
+               err = -errno;
 
        close(sk);
 
+out:
+       if (err < 0)
+               connman_error("Deleting host route failed (%s)",
+                                                       strerror(-err));
+
        return err;
 }
 
@@ -754,7 +857,7 @@ int connman_inet_del_ipv6_network_route(int index, const char *host,
                                                unsigned char prefix_len)
 {
        struct in6_rtmsg rt;
-       int sk, err;
+       int sk, err = 0;
 
        DBG("index %d host %s", index, host);
 
@@ -765,9 +868,10 @@ int connman_inet_del_ipv6_network_route(int index, const char *host,
 
        rt.rtmsg_dst_len = prefix_len;
 
-       err = inet_pton(AF_INET6, host, &rt.rtmsg_dst);
-       if (err < 0)
+       if (inet_pton(AF_INET6, host, &rt.rtmsg_dst) < 0) {
+               err = -errno;
                goto out;
+       }
 
        rt.rtmsg_flags = RTF_UP | RTF_HOST;
 
@@ -776,16 +880,19 @@ int connman_inet_del_ipv6_network_route(int index, const char *host,
 
        sk = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0);
        if (sk < 0) {
-               err = -1;
+               err = -errno;
                goto out;
        }
 
-       err = ioctl(sk, SIOCDELRT, &rt);
+       if (ioctl(sk, SIOCDELRT, &rt) < 0 && errno != ESRCH)
+               err = -errno;
+
        close(sk);
+
 out:
        if (err < 0)
                connman_error("Del IPv6 host route error (%s)",
-                                               strerror(errno));
+                                               strerror(-err));
 
        return err;
 }
@@ -800,7 +907,7 @@ int connman_inet_add_ipv6_network_route(int index, const char *host,
                                        unsigned char prefix_len)
 {
        struct in6_rtmsg rt;
-       int sk, err;
+       int sk, err = 0;
 
        DBG("index %d host %s gateway %s", index, host, gateway);
 
@@ -811,9 +918,10 @@ int connman_inet_add_ipv6_network_route(int index, const char *host,
 
        rt.rtmsg_dst_len = prefix_len;
 
-       err = inet_pton(AF_INET6, host, &rt.rtmsg_dst);
-       if (err < 0)
+       if (inet_pton(AF_INET6, host, &rt.rtmsg_dst) < 0) {
+               err = -errno;
                goto out;
+       }
 
        rt.rtmsg_flags = RTF_UP | RTF_HOST;
 
@@ -827,16 +935,19 @@ int connman_inet_add_ipv6_network_route(int index, const char *host,
 
        sk = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0);
        if (sk < 0) {
-               err = -1;
+               err = -errno;
                goto out;
        }
 
-       err = ioctl(sk, SIOCADDRT, &rt);
+       if (ioctl(sk, SIOCADDRT, &rt) < 0 && errno != EEXIST)
+               err = -errno;
+
        close(sk);
+
 out:
        if (err < 0)
                connman_error("Set IPv6 host route error (%s)",
-                                               strerror(errno));
+                                               strerror(-err));
 
        return err;
 }
@@ -850,18 +961,19 @@ int connman_inet_add_ipv6_host_route(int index, const char *host,
 int connman_inet_set_ipv6_gateway_address(int index, const char *gateway)
 {
        struct in6_rtmsg rt;
-       int sk, err;
+       int sk, err = 0;
 
-       DBG("index %d, gateway %s", index, gateway);
+       DBG("index %d gateway %s", index, gateway);
 
        if (gateway == NULL)
                return -EINVAL;
 
        memset(&rt, 0, sizeof(rt));
 
-       err = inet_pton(AF_INET6, gateway, &rt.rtmsg_gateway);
-       if (err < 0)
+       if (inet_pton(AF_INET6, gateway, &rt.rtmsg_gateway) < 0) {
+               err = -errno;
                goto out;
+       }
 
        rt.rtmsg_flags = RTF_UP | RTF_GATEWAY;
        rt.rtmsg_metric = 1;
@@ -870,16 +982,19 @@ int connman_inet_set_ipv6_gateway_address(int index, const char *gateway)
 
        sk = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0);
        if (sk < 0) {
-               err = -1;
+               err = -errno;
                goto out;
        }
 
-       err = ioctl(sk, SIOCADDRT, &rt);
+       if (ioctl(sk, SIOCADDRT, &rt) < 0 && errno != EEXIST)
+               err = -errno;
+
        close(sk);
+
 out:
        if (err < 0)
                connman_error("Set default IPv6 gateway error (%s)",
-                                               strerror(errno));
+                                               strerror(-err));
 
        return err;
 }
@@ -887,18 +1002,19 @@ out:
 int connman_inet_clear_ipv6_gateway_address(int index, const char *gateway)
 {
        struct in6_rtmsg rt;
-       int sk, err;
+       int sk, err = 0;
 
-       DBG("index %d, gateway %s", index, gateway);
+       DBG("index %d gateway %s", index, gateway);
 
        if (gateway == NULL)
                return -EINVAL;
 
        memset(&rt, 0, sizeof(rt));
 
-       err = inet_pton(AF_INET6, gateway, &rt.rtmsg_gateway);
-       if (err < 0)
+       if (inet_pton(AF_INET6, gateway, &rt.rtmsg_gateway) < 0) {
+               err = -errno;
                goto out;
+       }
 
        rt.rtmsg_flags = RTF_UP | RTF_GATEWAY;
        rt.rtmsg_metric = 1;
@@ -907,16 +1023,19 @@ int connman_inet_clear_ipv6_gateway_address(int index, const char *gateway)
 
        sk = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0);
        if (sk < 0) {
-               err = -1;
+               err = -errno;
                goto out;
        }
 
-       err = ioctl(sk, SIOCDELRT, &rt);
+       if (ioctl(sk, SIOCDELRT, &rt) < 0 && errno != ESRCH)
+               err = -errno;
+
        close(sk);
+
 out:
        if (err < 0)
                connman_error("Clear default IPv6 gateway error (%s)",
-                                               strerror(errno));
+                                               strerror(-err));
 
        return err;
 }
@@ -926,18 +1045,23 @@ int connman_inet_set_gateway_address(int index, const char *gateway)
        struct ifreq ifr;
        struct rtentry rt;
        struct sockaddr_in addr;
-       int sk, err;
+       int sk, err = 0;
+
+       DBG("index %d gateway %s", index, gateway);
 
        sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
-       if (sk < 0)
-               return -1;
+       if (sk < 0) {
+               err = -errno;
+               goto out;
+       }
 
        memset(&ifr, 0, sizeof(ifr));
        ifr.ifr_ifindex = index;
 
        if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
+               err = -errno;
                close(sk);
-               return -1;
+               goto out;
        }
 
        DBG("ifname %s", ifr.ifr_name);
@@ -960,13 +1084,16 @@ int connman_inet_set_gateway_address(int index, const char *gateway)
        addr.sin_addr.s_addr = INADDR_ANY;
        memcpy(&rt.rt_genmask, &addr, sizeof(rt.rt_genmask));
 
-       err = ioctl(sk, SIOCADDRT, &rt);
-       if (err < 0)
-               connman_error("Setting default gateway route failed (%s)",
-                                                       strerror(errno));
+       if (ioctl(sk, SIOCADDRT, &rt) < 0 && errno != EEXIST)
+               err = -errno;
 
        close(sk);
 
+out:
+       if (err < 0)
+               connman_error("Setting default gateway route failed (%s)",
+                                                       strerror(-err));
+
        return err;
 }
 
@@ -975,20 +1102,23 @@ int connman_inet_set_gateway_interface(int index)
        struct ifreq ifr;
        struct rtentry rt;
        struct sockaddr_in addr;
-       int sk, err;
+       int sk, err = 0;
 
-       DBG("");
+       DBG("index %d", index);
 
        sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
-       if (sk < 0)
-               return -1;
+       if (sk < 0) {
+               err = -errno;
+               goto out;
+       }
 
        memset(&ifr, 0, sizeof(ifr));
        ifr.ifr_ifindex = index;
 
        if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
+               err = -errno;
                close(sk);
-               return -1;
+               goto out;
        }
 
        DBG("ifname %s", ifr.ifr_name);
@@ -1006,11 +1136,15 @@ int connman_inet_set_gateway_interface(int index)
 
        rt.rt_dev = ifr.ifr_name;
 
-       err = ioctl(sk, SIOCADDRT, &rt);
+       if (ioctl(sk, SIOCADDRT, &rt) < 0 && errno != EEXIST)
+               err = -errno;
+
+       close(sk);
+
+out:
        if (err < 0)
                connman_error("Setting default interface route failed (%s)",
-                                                       strerror(errno));
-       close(sk);
+                                                       strerror(-err));
 
        return err;
 }
@@ -1021,20 +1155,23 @@ int connman_inet_set_ipv6_gateway_interface(int index)
        struct rtentry rt;
        struct sockaddr_in6 addr;
        const struct in6_addr any = IN6ADDR_ANY_INIT;
-       int sk, err;
+       int sk, err = 0;
 
-       DBG("");
+       DBG("index %d", index);
 
        sk = socket(PF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0);
-       if (sk < 0)
-               return -1;
+       if (sk < 0) {
+               err = -errno;
+               goto out;
+       }
 
        memset(&ifr, 0, sizeof(ifr));
        ifr.ifr_ifindex = index;
 
        if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
+               err = -errno;
                close(sk);
-               return -1;
+               goto out;
        }
 
        DBG("ifname %s", ifr.ifr_name);
@@ -1052,11 +1189,15 @@ int connman_inet_set_ipv6_gateway_interface(int index)
 
        rt.rt_dev = ifr.ifr_name;
 
-       err = ioctl(sk, SIOCADDRT, &rt);
+       if (ioctl(sk, SIOCADDRT, &rt) < 0 && errno != EEXIST)
+               err = -errno;
+
+       close(sk);
+
+out:
        if (err < 0)
                connman_error("Setting default interface route failed (%s)",
-                                                       strerror(errno));
-       close(sk);
+                                                       strerror(-err));
 
        return err;
 }
@@ -1066,20 +1207,23 @@ int connman_inet_clear_gateway_address(int index, const char *gateway)
        struct ifreq ifr;
        struct rtentry rt;
        struct sockaddr_in addr;
-       int sk, err;
+       int sk, err = 0;
 
-       DBG("");
+       DBG("index %d gateway %s", index, gateway);
 
        sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
-       if (sk < 0)
-               return -1;
+       if (sk < 0) {
+               err = -errno;
+               goto out;
+       }
 
        memset(&ifr, 0, sizeof(ifr));
        ifr.ifr_ifindex = index;
 
        if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
+               err = -errno;
                close(sk);
-               return -1;
+               goto out;
        }
 
        DBG("ifname %s", ifr.ifr_name);
@@ -1102,13 +1246,16 @@ int connman_inet_clear_gateway_address(int index, const char *gateway)
        addr.sin_addr.s_addr = INADDR_ANY;
        memcpy(&rt.rt_genmask, &addr, sizeof(rt.rt_genmask));
 
-       err = ioctl(sk, SIOCDELRT, &rt);
-       if (err < 0)
-               connman_error("Removing default gateway route failed (%s)",
-                                                       strerror(errno));
+       if (ioctl(sk, SIOCDELRT, &rt) < 0 && errno != ESRCH)
+               err = -errno;
 
        close(sk);
 
+out:
+       if (err < 0)
+               connman_error("Removing default gateway route failed (%s)",
+                                                       strerror(-err));
+
        return err;
 }
 
@@ -1117,20 +1264,23 @@ int connman_inet_clear_gateway_interface(int index)
        struct ifreq ifr;
        struct rtentry rt;
        struct sockaddr_in addr;
-       int sk, err;
+       int sk, err = 0;
 
-       DBG("");
+       DBG("index %d", index);
 
        sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
-       if (sk < 0)
-               return -1;
+       if (sk < 0) {
+               err = -errno;
+               goto out;
+       }
 
        memset(&ifr, 0, sizeof(ifr));
        ifr.ifr_ifindex = index;
 
        if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
+               err = -errno;
                close(sk);
-               return -1;
+               goto out;
        }
 
        DBG("ifname %s", ifr.ifr_name);
@@ -1148,11 +1298,15 @@ int connman_inet_clear_gateway_interface(int index)
 
        rt.rt_dev = ifr.ifr_name;
 
-       err = ioctl(sk, SIOCDELRT, &rt);
+       if (ioctl(sk, SIOCDELRT, &rt) < 0 && errno != ESRCH)
+               err = -errno;
+
+       close(sk);
+
+out:
        if (err < 0)
                connman_error("Removing default interface route failed (%s)",
-                                                       strerror(errno));
-       close(sk);
+                                                       strerror(-err));
 
        return err;
 }
@@ -1163,20 +1317,23 @@ int connman_inet_clear_ipv6_gateway_interface(int index)
        struct rtentry rt;
        struct sockaddr_in6 addr;
        const struct in6_addr any = IN6ADDR_ANY_INIT;
-       int sk, err;
+       int sk, err = 0;
 
-       DBG("");
+       DBG("index %d", index);
 
        sk = socket(PF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0);
-       if (sk < 0)
-               return -1;
+       if (sk < 0) {
+               err = -errno;
+               goto out;
+       }
 
        memset(&ifr, 0, sizeof(ifr));
        ifr.ifr_ifindex = index;
 
        if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
+               err = -errno;
                close(sk);
-               return -1;
+               goto out;
        }
 
        DBG("ifname %s", ifr.ifr_name);
@@ -1194,11 +1351,15 @@ int connman_inet_clear_ipv6_gateway_interface(int index)
 
        rt.rt_dev = ifr.ifr_name;
 
-       err = ioctl(sk, SIOCDELRT, &rt);
+       if (ioctl(sk, SIOCDELRT, &rt) < 0 && errno != ESRCH)
+               err = -errno;
+
+       close(sk);
+
+out:
        if (err < 0)
                connman_error("Removing default interface route failed (%s)",
-                                                       strerror(errno));
-       close(sk);
+                                                       strerror(-err));
 
        return err;
 }
@@ -1256,59 +1417,63 @@ connman_bool_t connman_inet_compare_subnet(int index, const char *host)
 int connman_inet_remove_from_bridge(int index, const char *bridge)
 {
        struct ifreq ifr;
-       int sk, err;
+       int sk, err = 0;
 
        if (bridge == NULL)
                return -EINVAL;
 
        sk = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
-       if (sk < 0)
-               return sk;
+       if (sk < 0) {
+               err = -errno;
+               goto out;
+       }
 
        memset(&ifr, 0, sizeof(ifr));
-       strncpy(ifr.ifr_name, bridge, IFNAMSIZ - 1);
+       strncpy(ifr.ifr_name, bridge, sizeof(ifr.ifr_name) - 1);
        ifr.ifr_ifindex = index;
 
-       err = ioctl(sk, SIOCBRDELIF, &ifr);
+       if (ioctl(sk, SIOCBRDELIF, &ifr) < 0)
+               err = -errno;
 
        close(sk);
 
-       if (err < 0) {
+out:
+       if (err < 0)
                connman_error("Remove interface from bridge error %s",
-                                                       strerror(errno));
-               return err;
-       }
+                                                       strerror(-err));
 
-       return 0;
+       return err;
 }
 
 int connman_inet_add_to_bridge(int index, const char *bridge)
 {
        struct ifreq ifr;
-       int sk, err;
+       int sk, err = 0;
 
        if (bridge == NULL)
                return -EINVAL;
 
        sk = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
-       if (sk < 0)
-               return sk;
+       if (sk < 0) {
+               err = -errno;
+               goto out;
+       }
 
        memset(&ifr, 0, sizeof(ifr));
-       strncpy(ifr.ifr_name, bridge, IFNAMSIZ - 1);
+       strncpy(ifr.ifr_name, bridge, sizeof(ifr.ifr_name) - 1);
        ifr.ifr_ifindex = index;
 
-       err = ioctl(sk, SIOCBRADDIF, &ifr);
+       if (ioctl(sk, SIOCBRADDIF, &ifr) < 0)
+               err = -errno;
 
        close(sk);
 
-       if (err < 0) {
+out:
+       if (err < 0)
                connman_error("Add interface to bridge error %s",
-                                                       strerror(errno));
-               return err;
-       }
+                                                       strerror(-err));
 
-       return 0;
+       return err;
 }
 
 int connman_inet_set_mtu(int index, int mtu)
@@ -1350,13 +1515,11 @@ int connman_inet_setup_tunnel(char *tunnel, int mtu)
        index = if_nametoindex(tunnel);
 
        err = connman_inet_set_mtu(index, mtu);
-       if (err < 0)
-               return err;
-       else if (err)
+       if (err != 0)
                goto done;
 
        memset(&ifr, 0, sizeof(ifr));
-       strncpy(ifr.ifr_name, tunnel, IFNAMSIZ);
+       strncpy(ifr.ifr_name, tunnel, sizeof(ifr.ifr_name) - 1);
        err = ioctl(sk, SIOCGIFFLAGS, &ifr);
        if (err)
                goto done;
@@ -1433,9 +1596,11 @@ static const struct in6_addr in6addr_all_routers_mc =
 
 static void rs_cleanup(struct rs_cb_data *data)
 {
-       g_io_channel_shutdown(data->channel, TRUE, NULL);
-       g_io_channel_unref(data->channel);
-       data->channel = 0;
+       if (data->channel != NULL) {
+               g_io_channel_shutdown(data->channel, TRUE, NULL);
+               g_io_channel_unref(data->channel);
+               data->channel = NULL;
+       }
 
        if (data->rs_timeout > 0)
                g_source_remove(data->rs_timeout);
@@ -1456,7 +1621,7 @@ static gboolean rs_timeout_cb(gpointer user_data)
                return FALSE;
 
        if (data->callback != NULL)
-               data->callback(NULL, data->user_data);
+               data->callback(NULL, 0, data->user_data);
 
        data->rs_timeout = 0;
        rs_cleanup(data);
@@ -1481,6 +1646,7 @@ static int icmpv6_recv(int fd, gpointer user_data)
 
        mhdr.msg_name = (void *)&saddr;
        mhdr.msg_namelen = sizeof(struct sockaddr_in6);
+       mhdr.msg_flags = 0;
        mhdr.msg_iov = &iov;
        mhdr.msg_iovlen = 1;
        mhdr.msg_control = (void *)chdr;
@@ -1488,16 +1654,18 @@ static int icmpv6_recv(int fd, gpointer user_data)
 
        len = recvmsg(fd, &mhdr, 0);
        if (len < 0) {
-               data->callback(NULL, data->user_data);
+               data->callback(NULL, 0, data->user_data);
                rs_cleanup(data);
                return -errno;
        }
 
        hdr = (struct nd_router_advert *)buf;
+       DBG("code %d len %zd hdr %zd", hdr->nd_ra_code, len,
+                               sizeof(struct nd_router_advert));
        if (hdr->nd_ra_code != 0)
                return 0;
 
-       data->callback(hdr, data->user_data);
+       data->callback(hdr, len, data->user_data);
        rs_cleanup(data);
 
        return len;
@@ -1720,3 +1888,634 @@ int __connman_inet_ipv6_send_rs(int index, int timeout,
 
        return 0;
 }
+
+GSList *__connman_inet_ipv6_get_prefixes(struct nd_router_advert *hdr,
+                                       unsigned int length)
+{
+       GSList *prefixes = NULL;
+       uint8_t *pos;
+       int len;
+
+       if (length <= sizeof(struct nd_router_advert))
+               return NULL;
+
+       len = length - sizeof(struct nd_router_advert);
+       pos = (uint8_t *)hdr + sizeof(struct nd_router_advert);
+
+       while (len > 0) {
+               struct nd_opt_prefix_info *pinfo;
+               char prefix_str[INET6_ADDRSTRLEN+1], *str;
+               const char *prefix;
+               int optlen;
+
+               if (len < 2)
+                       break;
+
+               optlen = pos[1] << 3;
+               if (optlen == 0 || optlen > len)
+                       break;
+
+               switch (pos[0]) {
+               case ND_OPT_PREFIX_INFORMATION:
+                       pinfo = (struct nd_opt_prefix_info *)pos;
+                       prefix = inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix,
+                                       prefix_str, INET6_ADDRSTRLEN);
+                       if (prefix == NULL)
+                               break;
+
+                       str = g_strdup_printf("%s/%d", prefix,
+                                               pinfo->nd_opt_pi_prefix_len);
+                       prefixes = g_slist_prepend(prefixes, str);
+
+                       DBG("prefix %s", str);
+
+                       break;
+               }
+
+               len -= optlen;
+               pos += optlen;
+       }
+
+       return prefixes;
+}
+
+static int get_dest_addr(int family, int index, char *buf, int len)
+{
+       struct ifreq ifr;
+       void *addr;
+       int sk;
+
+       sk = socket(family, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+       if (sk < 0)
+               return -errno;
+
+       memset(&ifr, 0, sizeof(ifr));
+       ifr.ifr_ifindex = index;
+
+       if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
+               DBG("SIOCGIFNAME (%d/%s)", errno, strerror(errno));
+               close(sk);
+               return -errno;
+       }
+
+       if (ioctl(sk, SIOCGIFFLAGS, &ifr) < 0) {
+               DBG("SIOCGIFFLAGS (%d/%s)", errno, strerror(errno));
+               close(sk);
+               return -errno;
+       }
+
+       if ((ifr.ifr_flags & IFF_POINTOPOINT) == 0) {
+               close(sk);
+               errno = EINVAL;
+               return -errno;
+       }
+
+       DBG("index %d %s", index, ifr.ifr_name);
+
+       if (ioctl(sk, SIOCGIFDSTADDR, &ifr) < 0) {
+               connman_error("Get destination address failed (%s)",
+                                                       strerror(errno));
+               close(sk);
+               return -errno;
+       }
+
+       close(sk);
+
+       switch (family) {
+       case AF_INET:
+               addr = &((struct sockaddr_in *)&ifr.ifr_dstaddr)->sin_addr;
+               break;
+       case AF_INET6:
+               addr = &((struct sockaddr_in6 *)&ifr.ifr_dstaddr)->sin6_addr;
+               break;
+       default:
+               errno = EINVAL;
+               return -errno;
+       }
+
+       if (inet_ntop(family, addr, buf, len) == NULL) {
+               DBG("error %d/%s", errno, strerror(errno));
+               return -errno;
+       }
+
+       return 0;
+}
+
+int connman_inet_get_dest_addr(int index, char **dest)
+{
+       char addr[INET_ADDRSTRLEN];
+       int ret;
+
+       ret = get_dest_addr(PF_INET, index, addr, INET_ADDRSTRLEN);
+       if (ret < 0)
+               return ret;
+
+       *dest = g_strdup(addr);
+
+       DBG("destination %s", *dest);
+
+       return 0;
+}
+
+int connman_inet_ipv6_get_dest_addr(int index, char **dest)
+{
+       char addr[INET6_ADDRSTRLEN];
+       int ret;
+
+       ret = get_dest_addr(PF_INET6, index, addr, INET6_ADDRSTRLEN);
+       if (ret < 0)
+               return ret;
+
+       *dest = g_strdup(addr);
+
+       DBG("destination %s", *dest);
+
+       return 0;
+}
+
+int __connman_inet_rtnl_open(struct __connman_inet_rtnl_handle *rth)
+{
+       int sndbuf = 1024;
+       int rcvbuf = 1024 * 4;
+
+       rth->fd = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE);
+       if (rth->fd < 0) {
+               connman_error("Can not open netlink socket: %s",
+                                               strerror(errno));
+               return -errno;
+       }
+
+       if (setsockopt(rth->fd, SOL_SOCKET, SO_SNDBUF, &sndbuf,
+                       sizeof(sndbuf)) < 0) {
+               connman_error("SO_SNDBUF: %s", strerror(errno));
+               return -errno;
+       }
+
+       if (setsockopt(rth->fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf,
+                       sizeof(rcvbuf)) < 0) {
+               connman_error("SO_RCVBUF: %s", strerror(errno));
+               return -errno;
+       }
+
+       memset(&rth->local, 0, sizeof(rth->local));
+       rth->local.nl_family = AF_NETLINK;
+       rth->local.nl_groups = 0;
+
+       if (bind(rth->fd, (struct sockaddr *)&rth->local,
+                                               sizeof(rth->local)) < 0) {
+               connman_error("Can not bind netlink socket: %s",
+                                                       strerror(errno));
+               return -errno;
+       }
+
+       rth->seq = time(NULL);
+
+       DBG("fd %d", rth->fd);
+
+       return 0;
+}
+
+struct inet_rtnl_cb_data {
+       GIOChannel *channel;
+       __connman_inet_rtnl_cb_t callback;
+       guint rtnl_timeout;
+       guint watch_id;
+       struct __connman_inet_rtnl_handle *rtnl;
+       void *user_data;
+};
+
+static void inet_rtnl_cleanup(struct inet_rtnl_cb_data *data)
+{
+       struct __connman_inet_rtnl_handle *rth = data->rtnl;
+
+       if (data->channel != NULL) {
+               g_io_channel_shutdown(data->channel, TRUE, NULL);
+               g_io_channel_unref(data->channel);
+               data->channel = NULL;
+       }
+
+       DBG("data %p", data);
+
+       if (data->rtnl_timeout > 0)
+               g_source_remove(data->rtnl_timeout);
+
+       if (data->watch_id > 0)
+               g_source_remove(data->watch_id);
+
+       if (rth != NULL) {
+               __connman_inet_rtnl_close(rth);
+               g_free(rth);
+       }
+
+       g_free(data);
+}
+
+static gboolean inet_rtnl_timeout_cb(gpointer user_data)
+{
+       struct inet_rtnl_cb_data *data = user_data;
+
+       DBG("user data %p", user_data);
+
+       if (data == NULL)
+               return FALSE;
+
+       if (data->callback != NULL)
+               data->callback(NULL, data->user_data);
+
+       data->rtnl_timeout = 0;
+       inet_rtnl_cleanup(data);
+       return FALSE;
+}
+
+static int inet_rtnl_recv(GIOChannel *chan, gpointer user_data)
+{
+       struct inet_rtnl_cb_data *rtnl_data = user_data;
+       struct __connman_inet_rtnl_handle *rth = rtnl_data->rtnl;
+       struct nlmsghdr *h = NULL;
+       struct sockaddr_nl nladdr;
+       socklen_t addr_len = sizeof(nladdr);
+       unsigned char buf[4096];
+       void *ptr = buf;
+       gsize len;
+       int status, fd;
+
+       memset(buf, 0, sizeof(buf));
+       memset(&nladdr, 0, sizeof(nladdr));
+
+       fd = g_io_channel_unix_get_fd(chan);
+
+       status = recvfrom(fd, buf, sizeof(buf), 0,
+                       (struct sockaddr *) &nladdr, &addr_len);
+       if (status < 0) {
+               if (errno == EINTR || errno == EAGAIN)
+                       return 0;
+
+               return -1;
+       }
+
+       if (status == 0)
+               return -1;
+
+       if (nladdr.nl_pid != 0) { /* not sent by kernel, ignore */
+               DBG("Received msg from %u, ignoring it", nladdr.nl_pid);
+               return 0;
+       }
+
+       len = status;
+
+       while (len > 0) {
+               struct nlmsgerr *err;
+
+               h = ptr;
+
+               if (!NLMSG_OK(h, len)) {
+                       return -1;
+#if !defined TIZEN_EXT
+                       /* Unnecessary 'break' statement */
+                       break;
+#endif
+               }
+
+               if (h->nlmsg_seq != rth->seq) {
+                       /* Skip this msg */
+                       DBG("skip %d/%d len %d", rth->seq,
+                               h->nlmsg_seq, h->nlmsg_len);
+
+                       len -= h->nlmsg_len;
+                       ptr += h->nlmsg_len;
+                       continue;
+               }
+
+               switch (h->nlmsg_type) {
+               case NLMSG_NOOP:
+               case NLMSG_OVERRUN:
+                       return -1;
+
+               case NLMSG_ERROR:
+                       err = (struct nlmsgerr *)NLMSG_DATA(h);
+                       connman_error("RTNETLINK answers %s (%d)",
+                               strerror(-err->error), -err->error);
+                       return err->error;
+               }
+
+               break;
+       }
+
+       if (h->nlmsg_seq == rth->seq) {
+               DBG("received %d seq %d", h->nlmsg_len, h->nlmsg_seq);
+
+               rtnl_data->callback(h, rtnl_data->user_data);
+
+               inet_rtnl_cleanup(rtnl_data);
+       }
+
+       return 0;
+}
+
+static gboolean inet_rtnl_event(GIOChannel *chan, GIOCondition cond,
+                                                       gpointer user_data)
+{
+       int ret;
+
+       DBG("");
+
+       if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR))
+               return FALSE;
+
+       ret = inet_rtnl_recv(chan, user_data);
+       if (ret != 0)
+               return TRUE;
+
+       return FALSE;
+}
+
+int __connman_inet_rtnl_talk(struct __connman_inet_rtnl_handle *rtnl,
+                       struct nlmsghdr *n, int timeout,
+                       __connman_inet_rtnl_cb_t callback, void *user_data)
+{
+       struct sockaddr_nl nladdr;
+       struct inet_rtnl_cb_data *data;
+       unsigned seq;
+       int err;
+
+       memset(&nladdr, 0, sizeof(nladdr));
+       nladdr.nl_family = AF_NETLINK;
+
+       n->nlmsg_seq = seq = ++rtnl->seq;
+
+       if (callback != NULL) {
+               data = g_try_malloc0(sizeof(struct inet_rtnl_cb_data));
+               if (data == NULL)
+                       return -ENOMEM;
+
+               data->callback = callback;
+               data->user_data = user_data;
+               data->rtnl = rtnl;
+               data->rtnl_timeout = g_timeout_add_seconds(timeout,
+                                               inet_rtnl_timeout_cb, data);
+
+               data->channel = g_io_channel_unix_new(rtnl->fd);
+               g_io_channel_set_close_on_unref(data->channel, TRUE);
+
+               g_io_channel_set_encoding(data->channel, NULL, NULL);
+               g_io_channel_set_buffered(data->channel, FALSE);
+
+               data->watch_id = g_io_add_watch(data->channel,
+                               G_IO_IN | G_IO_NVAL | G_IO_HUP | G_IO_ERR,
+                                               inet_rtnl_event, data);
+       } else
+               n->nlmsg_flags |= NLM_F_ACK;
+
+       err = sendto(rtnl->fd, &rtnl->req.n, rtnl->req.n.nlmsg_len, 0,
+               (struct sockaddr *) &nladdr, sizeof(nladdr));
+       DBG("handle %p len %d err %d", rtnl, rtnl->req.n.nlmsg_len, err);
+       if (err < 0) {
+               connman_error("Can not talk to rtnetlink");
+               return -errno;
+       }
+
+       if ((unsigned int)err != rtnl->req.n.nlmsg_len) {
+               connman_error("Sent %d bytes, msg truncated", err);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+void __connman_inet_rtnl_close(struct __connman_inet_rtnl_handle *rth)
+{
+       DBG("handle %p", rth);
+
+       if (rth->fd >= 0) {
+               close(rth->fd);
+               rth->fd = -1;
+       }
+}
+
+int __connman_inet_rtnl_addattr32(struct nlmsghdr *n, size_t maxlen, int type,
+                               __u32 data)
+{
+       int len = RTA_LENGTH(4);
+       struct rtattr *rta;
+
+       if (NLMSG_ALIGN(n->nlmsg_len) + len > maxlen) {
+               DBG("Error! max allowed bound %zd exceeded", maxlen);
+               return -1;
+       }
+       rta = NLMSG_TAIL(n);
+       rta->rta_type = type;
+       rta->rta_len = len;
+       memcpy(RTA_DATA(rta), &data, 4);
+       n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len;
+
+       return 0;
+}
+
+int connman_inet_check_ipaddress(const char *host)
+{
+       struct addrinfo hints;
+       struct addrinfo *addr;
+       int result;
+
+       memset(&hints, 0, sizeof(struct addrinfo));
+       hints.ai_flags = AI_NUMERICHOST;
+       addr = NULL;
+
+       result = getaddrinfo(host, NULL, &hints, &addr);
+       if (result == 0)
+               result = addr->ai_family;
+       freeaddrinfo(addr);
+
+       return result;
+}
+
+/* Check routine modified from ics-dhcp 4.2.3-P2 */
+connman_bool_t connman_inet_check_hostname(const char *ptr, size_t len)
+{
+       const char *p;
+
+       /*
+        * Not empty or complete length not over 255 characters.
+        */
+       if ((len == 0) || (len > 256))
+               return FALSE;
+
+       /*
+        * Consists of [[:alnum:]-]+ labels separated by [.]
+        * a [_] is against RFC but seems to be "widely used"
+        */
+       for (p = ptr; (*p != 0) && (len-- > 0); p++) {
+
+               if ((*p == '-') || (*p == '_')) {
+                       /*
+                        * Not allowed at begin or end of a label.
+                        */
+                       if (((p - ptr) == 0) || (len == 0) || (p[1] == '.'))
+                               return FALSE;
+
+               } else if (*p == '.') {
+                       /*
+                        * Each label has to be 1-63 characters;
+                        * we allow [.] at the end ('foo.bar.')
+                        */
+                       size_t d = p - ptr;
+
+                       if ((d <= 0) || (d >= 64))
+                               return FALSE;
+
+                       ptr = p + 1; /* Jump to the next label */
+
+               } else if (isalnum((unsigned char)*p) == 0) {
+                       /*
+                        * Also numbers at the begin are fine
+                        */
+                       return FALSE;
+               }
+       }
+
+       return TRUE;
+}
+
+char **__connman_inet_get_running_interfaces(void)
+{
+       char **result;
+       struct ifconf ifc;
+       struct ifreq *ifr = NULL;
+       int sk, i, numif, count = 0;
+
+       memset(&ifc, 0, sizeof(ifc));
+
+       sk = socket(AF_INET, SOCK_DGRAM, 0);
+       if (sk < 0)
+               return NULL;
+
+       if (ioctl(sk, SIOCGIFCONF, &ifc) < 0)
+               goto error;
+
+       /*
+        * Allocate some extra bytes just in case there will
+        * be new interfaces added between two SIOCGIFCONF
+        * calls.
+        */
+       ifr = g_try_malloc0(ifc.ifc_len * 2);
+       if (ifr == NULL)
+               goto error;
+
+       ifc.ifc_req = ifr;
+
+       if (ioctl(sk, SIOCGIFCONF, &ifc) < 0)
+               goto error;
+
+       numif = ifc.ifc_len / sizeof(struct ifreq);
+
+       result = g_try_malloc0((numif + 1) * sizeof(char *));
+       if (result == NULL)
+               goto error;
+
+       close(sk);
+
+       for (i = 0; i < numif; i++) {
+               struct ifreq *r = &ifr[i];
+               struct in6_addr *addr6;
+               in_addr_t addr4;
+
+               /*
+                * Note that we do not return loopback interfaces here as they
+                * are not needed for our purposes.
+                */
+               switch (r->ifr_addr.sa_family) {
+               case AF_INET:
+                       addr4 = ntohl(((struct sockaddr_in *)
+                                               &r->ifr_addr)->sin_addr.s_addr);
+                       if (((addr4 & 0xff000000) >> 24) == 127)
+                               continue;
+                       break;
+               case AF_INET6:
+                       addr6 = &((struct sockaddr_in6 *)
+                                               &r->ifr_addr)->sin6_addr;
+                       if (IN6_IS_ADDR_LINKLOCAL(addr6))
+                               continue;
+                       break;
+               }
+
+               result[count++] = g_strdup(r->ifr_name);
+       }
+
+       g_free(ifr);
+
+       if (count < numif)
+               result = g_try_realloc(result, (count + 1) * sizeof(char *));
+
+       return result;
+
+error:
+       close(sk);
+       g_free(ifr);
+       return NULL;
+}
+
+connman_bool_t connman_inet_is_ipv6_supported()
+{
+       int sk;
+
+       sk = socket(PF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+       if (sk < 0)
+               return FALSE;
+
+       close(sk);
+       return TRUE;
+}
+
+int __connman_inet_get_interface_address(int index, int family, void *address)
+{
+       struct ifaddrs *ifaddr, *ifa;
+       int err = -ENOENT;
+       char name[IF_NAMESIZE];
+
+       if (if_indextoname(index, name) == NULL)
+               return -EINVAL;
+
+       DBG("index %d interface %s", index, name);
+
+       if (getifaddrs(&ifaddr) < 0) {
+               err = -errno;
+               DBG("Cannot get addresses err %d/%s", err, strerror(-err));
+               return err;
+       }
+
+       for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
+               if (ifa->ifa_addr == NULL)
+                       continue;
+
+               if (strncmp(ifa->ifa_name, name, IF_NAMESIZE) == 0 &&
+                                       ifa->ifa_addr->sa_family == family) {
+                       if (family == AF_INET) {
+                               struct sockaddr_in *in4 = (struct sockaddr_in *)
+                                       ifa->ifa_addr;
+                               if (in4->sin_addr.s_addr == INADDR_ANY)
+                                       continue;
+                               memcpy(address, &in4->sin_addr,
+                                                       sizeof(struct in_addr));
+                       } else if (family == AF_INET6) {
+                               struct sockaddr_in6 *in6 =
+                                       (struct sockaddr_in6 *)ifa->ifa_addr;
+                               if (memcmp(&in6->sin6_addr, &in6addr_any,
+                                               sizeof(struct in6_addr)) == 0)
+                                       continue;
+                               memcpy(address, &in6->sin6_addr,
+                                               sizeof(struct in6_addr));
+
+                       } else {
+                               err = -EINVAL;
+                               goto out;
+                       }
+
+                       err = 0;
+                       break;
+               }
+       }
+
+out:
+       freeifaddrs(ifaddr);
+       return err;
+}
index e26f209..f459e50 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
@@ -49,6 +49,7 @@ struct connman_ipconfig {
        const struct connman_ipconfig_ops *ops;
        void *ops_data;
 
+       connman_bool_t enabled;
        enum connman_ipconfig_method method;
        struct connman_ipaddress *address;
        struct connman_ipaddress *system;
@@ -452,7 +453,7 @@ static void set_ipv6_privacy(gchar *ifname, int value)
        fclose(f);
 }
 
-static int get_rp_filter()
+static int get_rp_filter(void)
 {
        FILE *f;
        int value = -EINVAL, tmp;
@@ -506,6 +507,14 @@ void __connman_ipconfig_unset_rp_filter(int old_value)
        connman_info("rp_filter restored to %d", old_value);
 }
 
+gboolean __connman_ipconfig_ipv6_privacy_enabled(struct connman_ipconfig *ipconfig)
+{
+       if (ipconfig == NULL)
+               return FALSE;
+
+       return ipconfig->ipv6_privacy_config == 0 ? FALSE : TRUE;
+}
+
 static void free_ipdevice(gpointer data)
 {
        struct connman_ipdevice *ipdevice = data;
@@ -514,12 +523,12 @@ static void free_ipdevice(gpointer data)
                                                        ipdevice->index);
 
        if (ipdevice->config_ipv4 != NULL) {
-               connman_ipconfig_unref(ipdevice->config_ipv4);
+               __connman_ipconfig_unref(ipdevice->config_ipv4);
                ipdevice->config_ipv4 = NULL;
        }
 
        if (ipdevice->config_ipv6 != NULL) {
-               connman_ipconfig_unref(ipdevice->config_ipv6);
+               __connman_ipconfig_unref(ipdevice->config_ipv6);
                ipdevice->config_ipv6 = NULL;
        }
 
@@ -541,6 +550,15 @@ static void __connman_ipconfig_lower_up(struct connman_ipdevice *ipdevice)
 {
        DBG("ipconfig ipv4 %p ipv6 %p", ipdevice->config_ipv4,
                                        ipdevice->config_ipv6);
+
+       if (ipdevice->config_ipv6 != NULL &&
+                       ipdevice->config_ipv6->enabled == TRUE)
+               return;
+
+       if (__connman_device_isfiltered(ipdevice->ifname) == FALSE) {
+               ipdevice->ipv6_enabled = get_ipv6_state(ipdevice->ifname);
+               set_ipv6_state(ipdevice->ifname, FALSE);
+       }
 }
 
 static void __connman_ipconfig_lower_down(struct connman_ipdevice *ipdevice)
@@ -575,9 +593,9 @@ static void update_stats(struct connman_ipdevice *ipdevice,
                return;
 
        if (ipdevice->config_ipv4)
-               service = connman_ipconfig_get_data(ipdevice->config_ipv4);
+               service = __connman_ipconfig_get_data(ipdevice->config_ipv4);
        else if (ipdevice->config_ipv6)
-               service = connman_ipconfig_get_data(ipdevice->config_ipv6);
+               service = __connman_ipconfig_get_data(ipdevice->config_ipv6);
        else
                return;
 
@@ -639,6 +657,16 @@ void __connman_ipconfig_newlink(int index, unsigned short type,
                                                index, type, type2str(type));
 
 update:
+#if defined TIZEN_EXT
+       if (g_strcmp0(ipdevice->address, address) != 0) {
+               /* If an original address is built-in physical device,
+                * it's hardly get an address at a initial creation
+                */
+               g_free(ipdevice->address);
+               ipdevice->address = g_strdup(address);
+       }
+#endif
+
        ipdevice->mtu = mtu;
 
        update_stats(ipdevice, stats);
@@ -705,13 +733,6 @@ update:
                        ipconfig->ops->down(ipconfig);
        }
 
-#if defined TIZEN_EXT
-       if (g_strcmp0(ipdevice->address, address) != 0) {
-               g_free(ipdevice->address);
-               ipdevice->address = g_strdup(address);
-       }
-#endif
-
        if (lower_up)
                __connman_ipconfig_lower_up(ipdevice);
        if (lower_down)
@@ -799,12 +820,15 @@ void __connman_ipconfig_newaddr(int index, int family, const char *label,
        else
                return;
 
-       ipdevice->address_list = g_slist_append(ipdevice->address_list,
+       ipdevice->address_list = g_slist_prepend(ipdevice->address_list,
                                                                ipaddress);
 
        connman_info("%s {add} address %s/%u label %s family %d",
                ipdevice->ifname, address, prefixlen, label, family);
 
+       if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
+               __connman_ippool_newaddr(index, address, prefixlen);
+
        if (ipdevice->config_ipv4 != NULL && family == AF_INET)
                connman_ipaddress_copy(ipdevice->config_ipv4->system,
                                        ipaddress);
@@ -864,6 +888,9 @@ void __connman_ipconfig_deladdr(int index, int family, const char *label,
        ipdevice->address_list = g_slist_remove(ipdevice->address_list,
                                                                ipaddress);
 
+       if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
+               __connman_ippool_deladdr(index, address, prefixlen);
+
        connman_ipaddress_clear(ipaddress);
        g_free(ipaddress);
 
@@ -907,7 +934,6 @@ void __connman_ipconfig_newroute(int index, int family, unsigned char scope,
 
        if (scope == 0 && (g_strcmp0(dst, "0.0.0.0") == 0 ||
                                                g_strcmp0(dst, "::") == 0)) {
-               GSList *list;
                GList *config_list;
                enum connman_ipconfig_type type;
 
@@ -936,13 +962,6 @@ void __connman_ipconfig_newroute(int index, int family, unsigned char scope,
                } else
                        return;
 
-               for (list = ipdevice->address_list; list; list = list->next) {
-                       struct connman_ipaddress *ipaddress = list->data;
-
-                       g_free(ipaddress->gateway);
-                       ipaddress->gateway = g_strdup(gateway);
-               }
-
                for (config_list = g_list_first(ipconfig_list); config_list;
                                        config_list = g_list_next(config_list)) {
                        struct connman_ipconfig *ipconfig = config_list->data;
@@ -956,8 +975,8 @@ void __connman_ipconfig_newroute(int index, int family, unsigned char scope,
                        if (ipconfig->ops == NULL)
                                continue;
 
-                       if (ipconfig->ops->ip_bound)
-                               ipconfig->ops->ip_bound(ipconfig);
+                       if (ipconfig->ops->route_set)
+                               ipconfig->ops->route_set(ipconfig);
                }
        }
 
@@ -979,7 +998,6 @@ void __connman_ipconfig_delroute(int index, int family, unsigned char scope,
 
        if (scope == 0 && (g_strcmp0(dst, "0.0.0.0") == 0 ||
                                                g_strcmp0(dst, "::") == 0)) {
-               GSList *list;
                GList *config_list;
                enum connman_ipconfig_type type;
 
@@ -1006,13 +1024,6 @@ void __connman_ipconfig_delroute(int index, int family, unsigned char scope,
                } else
                        return;
 
-               for (list = ipdevice->address_list; list; list = list->next) {
-                       struct connman_ipaddress *ipaddress = list->data;
-
-                       g_free(ipaddress->gateway);
-                       ipaddress->gateway = NULL;
-               }
-
                for (config_list = g_list_first(ipconfig_list); config_list;
                                        config_list = g_list_next(config_list)) {
                        struct connman_ipconfig *ipconfig = config_list->data;
@@ -1026,8 +1037,8 @@ void __connman_ipconfig_delroute(int index, int family, unsigned char scope,
                        if (ipconfig->ops == NULL)
                                continue;
 
-                       if (ipconfig->ops->ip_release)
-                               ipconfig->ops->ip_release(ipconfig);
+                       if (ipconfig->ops->route_unset)
+                               ipconfig->ops->route_unset(ipconfig);
                }
        }
 
@@ -1082,7 +1093,8 @@ unsigned int __connman_ipconfig_get_flags_from_index(int index)
        return ipdevice->flags;
 }
 
-const char *__connman_ipconfig_get_gateway_from_index(int index)
+const char *__connman_ipconfig_get_gateway_from_index(int index,
+       enum connman_ipconfig_type type)
 {
        struct connman_ipdevice *ipdevice;
 
@@ -1090,19 +1102,23 @@ const char *__connman_ipconfig_get_gateway_from_index(int index)
        if (ipdevice == NULL)
                return NULL;
 
-       if (ipdevice->ipv4_gateway != NULL)
-               return ipdevice->ipv4_gateway;
+       if (type != CONNMAN_IPCONFIG_TYPE_IPV6) {
+               if (ipdevice->ipv4_gateway != NULL)
+                       return ipdevice->ipv4_gateway;
 
-       if (ipdevice->config_ipv4 != NULL &&
-                       ipdevice->config_ipv4->address != NULL)
-               return ipdevice->config_ipv4->address->gateway;
+               if (ipdevice->config_ipv4 != NULL &&
+                               ipdevice->config_ipv4->address != NULL)
+                       return ipdevice->config_ipv4->address->gateway;
+       }
 
-       if (ipdevice->ipv6_gateway != NULL)
-               return ipdevice->ipv6_gateway;
+       if (type != CONNMAN_IPCONFIG_TYPE_IPV4) {
+               if (ipdevice->ipv6_gateway != NULL)
+                       return ipdevice->ipv6_gateway;
 
-       if (ipdevice->config_ipv6 != NULL &&
-                       ipdevice->config_ipv6->address != NULL)
-               return ipdevice->config_ipv6->address->gateway;
+               if (ipdevice->config_ipv6 != NULL &&
+                               ipdevice->config_ipv6->address != NULL)
+                       return ipdevice->config_ipv6->address->gateway;
+       }
 
        return NULL;
 }
@@ -1182,9 +1198,6 @@ void __connman_ipconfig_set_gateway(struct connman_ipconfig *ipconfig, const cha
 }
 
 #if defined TIZEN_EXT
-/*
- * Description: __connman_service_lookup_from_index cannot find correct service
- */
 int __connman_ipconfig_gateway_add(struct connman_ipconfig *ipconfig, struct connman_service *service)
 #else
 int __connman_ipconfig_gateway_add(struct connman_ipconfig *ipconfig)
@@ -1250,6 +1263,7 @@ void __connman_ipconfig_set_prefixlen(struct connman_ipconfig *ipconfig, unsigne
 static struct connman_ipconfig *create_ipv6config(int index)
 {
        struct connman_ipconfig *ipv6config;
+       struct connman_ipdevice *ipdevice;
 
        DBG("index %d", index);
 
@@ -1260,9 +1274,13 @@ static struct connman_ipconfig *create_ipv6config(int index)
        ipv6config->refcount = 1;
 
        ipv6config->index = index;
+       ipv6config->enabled = FALSE;
        ipv6config->type = CONNMAN_IPCONFIG_TYPE_IPV6;
        ipv6config->method = CONNMAN_IPCONFIG_METHOD_AUTO;
-       ipv6config->ipv6_privacy_config = 0;
+
+       ipdevice = g_hash_table_lookup(ipdevice_hash, GINT_TO_POINTER(index));
+       if (ipdevice != NULL)
+               ipv6config->ipv6_privacy_config = ipdevice->ipv6_privacy;
 
        ipv6config->address = connman_ipaddress_alloc(AF_INET6);
        if (ipv6config->address == NULL) {
@@ -1284,7 +1302,7 @@ static struct connman_ipconfig *create_ipv6config(int index)
  *
  * Returns: a newly-allocated #connman_ipconfig structure
  */
-struct connman_ipconfig *connman_ipconfig_create(int index,
+struct connman_ipconfig *__connman_ipconfig_create(int index,
                                        enum connman_ipconfig_type type)
 {
        struct connman_ipconfig *ipconfig;
@@ -1301,6 +1319,7 @@ struct connman_ipconfig *connman_ipconfig_create(int index,
        ipconfig->refcount = 1;
 
        ipconfig->index = index;
+       ipconfig->enabled = FALSE;
        ipconfig->type = CONNMAN_IPCONFIG_TYPE_IPV4;
 
        ipconfig->address = connman_ipaddress_alloc(AF_INET);
@@ -1323,9 +1342,12 @@ struct connman_ipconfig *connman_ipconfig_create(int index,
  *
  * Increase reference counter of ipconfig
  */
-struct connman_ipconfig *connman_ipconfig_ref(struct connman_ipconfig *ipconfig)
+struct connman_ipconfig *
+__connman_ipconfig_ref_debug(struct connman_ipconfig *ipconfig,
+                               const char *file, int line, const char *caller)
 {
-       DBG("ipconfig %p refcount %d", ipconfig, ipconfig->refcount + 1);
+       DBG("%p ref %d by %s:%d:%s()", ipconfig, ipconfig->refcount + 1,
+               file, line, caller);
 
        __sync_fetch_and_add(&ipconfig->refcount, 1);
 
@@ -1338,12 +1360,14 @@ struct connman_ipconfig *connman_ipconfig_ref(struct connman_ipconfig *ipconfig)
  *
  * Decrease reference counter of ipconfig
  */
-void connman_ipconfig_unref(struct connman_ipconfig *ipconfig)
+void __connman_ipconfig_unref_debug(struct connman_ipconfig *ipconfig,
+                               const char *file, int line, const char *caller)
 {
        if (ipconfig == NULL)
                return;
 
-       DBG("ipconfig %p refcount %d", ipconfig, ipconfig->refcount - 1);
+       DBG("%p ref %d by %s:%d:%s()", ipconfig, ipconfig->refcount - 1,
+               file, line, caller);
 
        if (__sync_fetch_and_sub(&ipconfig->refcount, 1) != 1)
                return;
@@ -1351,10 +1375,10 @@ void connman_ipconfig_unref(struct connman_ipconfig *ipconfig)
        if (__connman_ipconfig_disable(ipconfig) < 0)
                ipconfig_list = g_list_remove(ipconfig_list, ipconfig);
 
-       connman_ipconfig_set_ops(ipconfig, NULL);
+       __connman_ipconfig_set_ops(ipconfig, NULL);
 
-       if (ipconfig->origin != NULL) {
-               connman_ipconfig_unref(ipconfig->origin);
+       if (ipconfig->origin != NULL && ipconfig->origin != ipconfig) {
+               __connman_ipconfig_unref(ipconfig->origin);
                ipconfig->origin = NULL;
        }
 
@@ -1370,7 +1394,7 @@ void connman_ipconfig_unref(struct connman_ipconfig *ipconfig)
  *
  * Get private data pointer
  */
-void *connman_ipconfig_get_data(struct connman_ipconfig *ipconfig)
+void *__connman_ipconfig_get_data(struct connman_ipconfig *ipconfig)
 {
        if (ipconfig == NULL)
                return NULL;
@@ -1385,7 +1409,7 @@ void *connman_ipconfig_get_data(struct connman_ipconfig *ipconfig)
  *
  * Set private data pointer
  */
-void connman_ipconfig_set_data(struct connman_ipconfig *ipconfig, void *data)
+void __connman_ipconfig_set_data(struct connman_ipconfig *ipconfig, void *data)
 {
        ipconfig->ops_data = data;
 }
@@ -1396,7 +1420,7 @@ void connman_ipconfig_set_data(struct connman_ipconfig *ipconfig, void *data)
  *
  * Get interface index
  */
-int connman_ipconfig_get_index(struct connman_ipconfig *ipconfig)
+int __connman_ipconfig_get_index(struct connman_ipconfig *ipconfig)
 {
        if (ipconfig == NULL)
                return -1;
@@ -1413,7 +1437,7 @@ int connman_ipconfig_get_index(struct connman_ipconfig *ipconfig)
  *
  * Get interface name
  */
-const char *connman_ipconfig_get_ifname(struct connman_ipconfig *ipconfig)
+const char *__connman_ipconfig_get_ifname(struct connman_ipconfig *ipconfig)
 {
        struct connman_ipdevice *ipdevice;
 
@@ -1438,7 +1462,7 @@ const char *connman_ipconfig_get_ifname(struct connman_ipconfig *ipconfig)
  *
  * Set the operation callbacks
  */
-void connman_ipconfig_set_ops(struct connman_ipconfig *ipconfig,
+void __connman_ipconfig_set_ops(struct connman_ipconfig *ipconfig,
                                const struct connman_ipconfig_ops *ops)
 {
        ipconfig->ops = ops;
@@ -1451,7 +1475,7 @@ void connman_ipconfig_set_ops(struct connman_ipconfig *ipconfig,
  *
  * Set the configuration method
  */
-int connman_ipconfig_set_method(struct connman_ipconfig *ipconfig,
+int __connman_ipconfig_set_method(struct connman_ipconfig *ipconfig,
                                        enum connman_ipconfig_method method)
 {
        ipconfig->method = method;
@@ -1474,8 +1498,8 @@ int __connman_ipconfig_address_add(struct connman_ipconfig *ipconfig)
        switch (ipconfig->method) {
        case CONNMAN_IPCONFIG_METHOD_UNKNOWN:
        case CONNMAN_IPCONFIG_METHOD_OFF:
-       case CONNMAN_IPCONFIG_METHOD_AUTO:
                break;
+       case CONNMAN_IPCONFIG_METHOD_AUTO:
        case CONNMAN_IPCONFIG_METHOD_FIXED:
        case CONNMAN_IPCONFIG_METHOD_DHCP:
        case CONNMAN_IPCONFIG_METHOD_MANUAL:
@@ -1494,7 +1518,9 @@ int __connman_ipconfig_address_remove(struct connman_ipconfig *ipconfig)
 {
        int err;
 
+#if !defined TIZEN_EXT
        DBG("");
+#endif
 
        if (ipconfig == NULL)
                return 0;
@@ -1504,8 +1530,8 @@ int __connman_ipconfig_address_remove(struct connman_ipconfig *ipconfig)
        switch (ipconfig->method) {
        case CONNMAN_IPCONFIG_METHOD_UNKNOWN:
        case CONNMAN_IPCONFIG_METHOD_OFF:
-       case CONNMAN_IPCONFIG_METHOD_AUTO:
                break;
+       case CONNMAN_IPCONFIG_METHOD_AUTO:
        case CONNMAN_IPCONFIG_METHOD_FIXED:
        case CONNMAN_IPCONFIG_METHOD_DHCP:
        case CONNMAN_IPCONFIG_METHOD_MANUAL:
@@ -1522,7 +1548,9 @@ int __connman_ipconfig_address_unset(struct connman_ipconfig *ipconfig)
 {
        int err;
 
+#if !defined TIZEN_EXT
        DBG("");
+#endif
 
        if (ipconfig == NULL)
                return 0;
@@ -1532,8 +1560,8 @@ int __connman_ipconfig_address_unset(struct connman_ipconfig *ipconfig)
        switch (ipconfig->method) {
        case CONNMAN_IPCONFIG_METHOD_UNKNOWN:
        case CONNMAN_IPCONFIG_METHOD_OFF:
-       case CONNMAN_IPCONFIG_METHOD_AUTO:
                break;
+       case CONNMAN_IPCONFIG_METHOD_AUTO:
        case CONNMAN_IPCONFIG_METHOD_FIXED:
        case CONNMAN_IPCONFIG_METHOD_DHCP:
        case CONNMAN_IPCONFIG_METHOD_MANUAL:
@@ -1687,6 +1715,8 @@ int __connman_ipconfig_enable(struct connman_ipconfig *ipconfig)
        } else
                return -EINVAL;
 
+       ipconfig->enabled = TRUE;
+
        if (type == CONNMAN_IPCONFIG_TYPE_IPV4 &&
                                        ipdevice->config_ipv4 != NULL) {
                ipconfig_list = g_list_remove(ipconfig_list,
@@ -1694,7 +1724,7 @@ int __connman_ipconfig_enable(struct connman_ipconfig *ipconfig)
 
                connman_ipaddress_clear(ipdevice->config_ipv4->system);
 
-               connman_ipconfig_unref(ipdevice->config_ipv4);
+               __connman_ipconfig_unref(ipdevice->config_ipv4);
        }
 
        if (type == CONNMAN_IPCONFIG_TYPE_IPV6 &&
@@ -1704,13 +1734,13 @@ int __connman_ipconfig_enable(struct connman_ipconfig *ipconfig)
 
                connman_ipaddress_clear(ipdevice->config_ipv6->system);
 
-               connman_ipconfig_unref(ipdevice->config_ipv6);
+               __connman_ipconfig_unref(ipdevice->config_ipv6);
        }
 
        if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
-               ipdevice->config_ipv4 = connman_ipconfig_ref(ipconfig);
+               ipdevice->config_ipv4 = __connman_ipconfig_ref(ipconfig);
        else if (type == CONNMAN_IPCONFIG_TYPE_IPV6)
-               ipdevice->config_ipv6 = connman_ipconfig_ref(ipconfig);
+               ipdevice->config_ipv6 = __connman_ipconfig_ref(ipconfig);
 
        ipconfig_list = g_list_append(ipconfig_list, ipconfig);
 
@@ -1755,11 +1785,13 @@ int __connman_ipconfig_disable(struct connman_ipconfig *ipconfig)
        if (ipdevice->config_ipv4 == NULL && ipdevice->config_ipv6 == NULL)
                return -EINVAL;
 
+       ipconfig->enabled = FALSE;
+
        if (ipdevice->config_ipv4 == ipconfig) {
                ipconfig_list = g_list_remove(ipconfig_list, ipconfig);
 
                connman_ipaddress_clear(ipdevice->config_ipv4->system);
-               connman_ipconfig_unref(ipdevice->config_ipv4);
+               __connman_ipconfig_unref(ipdevice->config_ipv4);
                ipdevice->config_ipv4 = NULL;
                return 0;
        }
@@ -1772,7 +1804,7 @@ int __connman_ipconfig_disable(struct connman_ipconfig *ipconfig)
                        disable_ipv6(ipdevice->config_ipv6);
 
                connman_ipaddress_clear(ipdevice->config_ipv6->system);
-               connman_ipconfig_unref(ipdevice->config_ipv6);
+               __connman_ipconfig_unref(ipdevice->config_ipv6);
                ipdevice->config_ipv6 = NULL;
                return 0;
        }
@@ -1822,10 +1854,8 @@ static const char *privacy2string(int privacy)
                return "disabled";
        else if (privacy == 1)
                return "enabled";
-       else if (privacy > 1)
+       else
                return "prefered";
-
-       return "disabled";
 }
 
 static int string2privacy(const char *privacy)
@@ -1834,6 +1864,8 @@ static int string2privacy(const char *privacy)
                return 0;
        else if (g_strcmp0(privacy, "enabled") == 0)
                return 1;
+       else if (g_strcmp0(privacy, "preferred") == 0)
+               return 2;
        else if (g_strcmp0(privacy, "prefered") == 0)
                return 2;
        else
@@ -1843,9 +1875,12 @@ static int string2privacy(const char *privacy)
 void __connman_ipconfig_append_ipv4(struct connman_ipconfig *ipconfig,
                                                        DBusMessageIter *iter)
 {
+       struct connman_ipaddress *append_addr = NULL;
        const char *str;
 
+#if !defined TIZEN_EXT
        DBG("");
+#endif
 
        if (ipconfig->type != CONNMAN_IPCONFIG_TYPE_IPV4)
                return;
@@ -1856,36 +1891,60 @@ void __connman_ipconfig_append_ipv4(struct connman_ipconfig *ipconfig,
 
        connman_dbus_dict_append_basic(iter, "Method", DBUS_TYPE_STRING, &str);
 
-       if (ipconfig->system == NULL)
+       switch (ipconfig->method) {
+       case CONNMAN_IPCONFIG_METHOD_UNKNOWN:
+       case CONNMAN_IPCONFIG_METHOD_OFF:
+               return;
+
+       case CONNMAN_IPCONFIG_METHOD_FIXED:
+               append_addr = ipconfig->address;
+               break;
+
+       case CONNMAN_IPCONFIG_METHOD_MANUAL:
+       case CONNMAN_IPCONFIG_METHOD_DHCP:
+       case CONNMAN_IPCONFIG_METHOD_AUTO:
+               append_addr = ipconfig->system;
+#if defined TIZEN_EXT
+               /* TIZEN enables get_properties before __connman_ipconfig_newaddr */
+               if (append_addr && append_addr->local == NULL)
+                       append_addr = ipconfig->address;
+#endif
+               break;
+       }
+
+       if (append_addr == NULL)
                return;
 
-       if (ipconfig->system->local != NULL) {
+       if (append_addr->local != NULL) {
                in_addr_t addr;
                struct in_addr netmask;
                char *mask;
 
                connman_dbus_dict_append_basic(iter, "Address",
-                               DBUS_TYPE_STRING, &ipconfig->system->local);
+                               DBUS_TYPE_STRING, &append_addr->local);
 
-               addr = 0xffffffff << (32 - ipconfig->system->prefixlen);
+               addr = 0xffffffff << (32 - append_addr->prefixlen);
                netmask.s_addr = htonl(addr);
                mask = inet_ntoa(netmask);
                connman_dbus_dict_append_basic(iter, "Netmask",
                                                DBUS_TYPE_STRING, &mask);
        }
 
-       if (ipconfig->system->gateway != NULL)
+       if (append_addr->gateway != NULL)
                connman_dbus_dict_append_basic(iter, "Gateway",
-                               DBUS_TYPE_STRING, &ipconfig->system->gateway);
+                               DBUS_TYPE_STRING, &append_addr->gateway);
 }
 
 void __connman_ipconfig_append_ipv6(struct connman_ipconfig *ipconfig,
                                        DBusMessageIter *iter,
                                        struct connman_ipconfig *ipconfig_ipv4)
 {
+       struct connman_ipaddress *append_addr = NULL;
        const char *str, *privacy;
 
+#if !defined TIZEN_EXT
        DBG("");
+#endif
 
        if (ipconfig->type != CONNMAN_IPCONFIG_TYPE_IPV6)
                return;
@@ -1902,20 +1961,41 @@ void __connman_ipconfig_append_ipv6(struct connman_ipconfig *ipconfig,
 
        connman_dbus_dict_append_basic(iter, "Method", DBUS_TYPE_STRING, &str);
 
-       if (ipconfig->system == NULL)
+       switch (ipconfig->method) {
+       case CONNMAN_IPCONFIG_METHOD_UNKNOWN:
+       case CONNMAN_IPCONFIG_METHOD_OFF:
+               return;
+
+       case CONNMAN_IPCONFIG_METHOD_FIXED:
+               append_addr = ipconfig->address;
+               break;
+
+       case CONNMAN_IPCONFIG_METHOD_MANUAL:
+       case CONNMAN_IPCONFIG_METHOD_DHCP:
+       case CONNMAN_IPCONFIG_METHOD_AUTO:
+               append_addr = ipconfig->system;
+#if defined TIZEN_EXT
+               /* TIZEN enables get_properties before __connman_ipconfig_newaddr */
+               if (append_addr && append_addr->local == NULL)
+                       append_addr = ipconfig->address;
+#endif
+               break;
+       }
+
+       if (append_addr == NULL)
                return;
 
-       if (ipconfig->system->local != NULL) {
+       if (append_addr->local != NULL) {
                connman_dbus_dict_append_basic(iter, "Address",
-                               DBUS_TYPE_STRING, &ipconfig->system->local);
+                               DBUS_TYPE_STRING, &append_addr->local);
                connman_dbus_dict_append_basic(iter, "PrefixLength",
                                                DBUS_TYPE_BYTE,
-                                               &ipconfig->system->prefixlen);
+                                               &append_addr->prefixlen);
        }
 
-       if (ipconfig->system->gateway != NULL)
+       if (append_addr->gateway != NULL)
                connman_dbus_dict_append_basic(iter, "Gateway",
-                               DBUS_TYPE_STRING, &ipconfig->system->gateway);
+                               DBUS_TYPE_STRING, &append_addr->gateway);
 
        privacy = privacy2string(ipconfig->ipv6_privacy_config);
        connman_dbus_dict_append_basic(iter, "Privacy",
@@ -1927,7 +2007,9 @@ void __connman_ipconfig_append_ipv6config(struct connman_ipconfig *ipconfig,
 {
        const char *str, *privacy;
 
+#if !defined TIZEN_EXT
        DBG("");
+#endif
 
        str = __connman_ipconfig_method2string(ipconfig->method);
        if (str == NULL)
@@ -1971,7 +2053,9 @@ void __connman_ipconfig_append_ipv4config(struct connman_ipconfig *ipconfig,
 {
        const char *str;
 
+#if !defined TIZEN_EXT
        DBG("");
+#endif
 
        str = __connman_ipconfig_method2string(ipconfig->method);
        if (str == NULL)
@@ -2030,7 +2114,7 @@ int __connman_ipconfig_set_config(struct connman_ipconfig *ipconfig,
        dbus_message_iter_recurse(array, &dict);
 
        while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
-               DBusMessageIter entry;
+               DBusMessageIter entry, value;
                const char *key;
                int type;
 
@@ -2042,7 +2126,12 @@ int __connman_ipconfig_set_config(struct connman_ipconfig *ipconfig,
                dbus_message_iter_get_basic(&entry, &key);
                dbus_message_iter_next(&entry);
 
-               type = dbus_message_iter_get_arg_type(&entry);
+               if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_VARIANT)
+                       return -EINVAL;
+
+               dbus_message_iter_recurse(&entry, &value);
+
+               type = dbus_message_iter_get_arg_type(&value);
 
                if (g_str_equal(key, "Method") == TRUE) {
                        const char *str;
@@ -2050,41 +2139,41 @@ int __connman_ipconfig_set_config(struct connman_ipconfig *ipconfig,
                        if (type != DBUS_TYPE_STRING)
                                return -EINVAL;
 
-                       dbus_message_iter_get_basic(&entry, &str);
+                       dbus_message_iter_get_basic(&value, &str);
                        method = __connman_ipconfig_string2method(str);
                } else if (g_str_equal(key, "Address") == TRUE) {
                        if (type != DBUS_TYPE_STRING)
                                return -EINVAL;
 
-                       dbus_message_iter_get_basic(&entry, &address);
+                       dbus_message_iter_get_basic(&value, &address);
                } else if (g_str_equal(key, "PrefixLength") == TRUE) {
                        if (type != DBUS_TYPE_STRING)
                                return -EINVAL;
 
-                       dbus_message_iter_get_basic(&entry,
+                       dbus_message_iter_get_basic(&value,
                                                        &prefix_length_string);
 
                        prefix_length = atoi(prefix_length_string);
                        if (prefix_length < 0 || prefix_length > 128)
                                return -EINVAL;
-
                } else if (g_str_equal(key, "Netmask") == TRUE) {
                        if (type != DBUS_TYPE_STRING)
                                return -EINVAL;
 
-                       dbus_message_iter_get_basic(&entry, &netmask);
+                       dbus_message_iter_get_basic(&value, &netmask);
                } else if (g_str_equal(key, "Gateway") == TRUE) {
                        if (type != DBUS_TYPE_STRING)
                                return -EINVAL;
 
-                       dbus_message_iter_get_basic(&entry, &gateway);
+                       dbus_message_iter_get_basic(&value, &gateway);
                } else if (g_str_equal(key, "Privacy") == TRUE) {
                        if (type != DBUS_TYPE_STRING)
                                return -EINVAL;
 
-                       dbus_message_iter_get_basic(&entry, &privacy_string);
+                       dbus_message_iter_get_basic(&value, &privacy_string);
                        privacy = string2privacy(privacy_string);
                }
+
                dbus_message_iter_next(&dict);
        }
 
@@ -2179,10 +2268,17 @@ int __connman_ipconfig_load(struct connman_ipconfig *ipconfig,
        key = g_strdup_printf("%smethod", prefix);
        method = g_key_file_get_string(keyfile, identifier, key, NULL);
        if (method == NULL) {
-               if (ipconfig->type == CONNMAN_IPCONFIG_TYPE_IPV4)
+               switch (ipconfig->type) {
+               case CONNMAN_IPCONFIG_TYPE_IPV4:
                        ipconfig->method = CONNMAN_IPCONFIG_METHOD_DHCP;
-               else
+                       break;
+               case CONNMAN_IPCONFIG_TYPE_IPV6:
+                       ipconfig->method = CONNMAN_IPCONFIG_METHOD_AUTO;
+                       break;
+               case CONNMAN_IPCONFIG_TYPE_UNKNOWN:
                        ipconfig->method = CONNMAN_IPCONFIG_METHOD_OFF;
+                       break;
+               }
        } else
                ipconfig->method = __connman_ipconfig_string2method(method);
 
@@ -2199,9 +2295,6 @@ int __connman_ipconfig_load(struct connman_ipconfig *ipconfig,
                        ipconfig->ipv6_privacy_config = string2privacy(privacy);
                        g_free(pprefix);
                        g_free(privacy);
-
-                       __connman_ipconfig_enable(ipconfig);
-                       enable_ipv6(ipconfig);
                }
        }
 
@@ -2214,21 +2307,25 @@ int __connman_ipconfig_load(struct connman_ipconfig *ipconfig,
        g_free(key);
 
        key = g_strdup_printf("%slocal_address", prefix);
+       g_free(ipconfig->address->local);
        ipconfig->address->local = g_key_file_get_string(
                        keyfile, identifier, key, NULL);
        g_free(key);
 
        key = g_strdup_printf("%speer_address", prefix);
+       g_free(ipconfig->address->peer);
        ipconfig->address->peer = g_key_file_get_string(
                                keyfile, identifier, key, NULL);
        g_free(key);
 
        key = g_strdup_printf("%sbroadcast_address", prefix);
+       g_free(ipconfig->address->broadcast);
        ipconfig->address->broadcast = g_key_file_get_string(
                                keyfile, identifier, key, NULL);
        g_free(key);
 
        key = g_strdup_printf("%sgateway", prefix);
+       g_free(ipconfig->address->gateway);
        ipconfig->address->gateway = g_key_file_get_string(
                                keyfile, identifier, key, NULL);
        g_free(key);
@@ -2287,8 +2384,9 @@ int __connman_ipconfig_save(struct connman_ipconfig *ipconfig,
        }
 
        key = g_strdup_printf("%snetmask_prefixlen", prefix);
-       g_key_file_set_integer(keyfile, identifier,
-                       key, ipconfig->address->prefixlen);
+       if (ipconfig->address->prefixlen != 0)
+               g_key_file_set_integer(keyfile, identifier,
+                               key, ipconfig->address->prefixlen);
        g_free(key);
 
        key = g_strdup_printf("%slocal_address", prefix);
diff --git a/src/ippool.c b/src/ippool.c
new file mode 100644 (file)
index 0000000..15f8fb9
--- /dev/null
@@ -0,0 +1,478 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2012  BMW Car IT GmbH. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <getopt.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/errno.h>
+#include <sys/socket.h>
+
+#include "connman.h"
+
+struct address_info {
+       int index;
+       uint32_t start;
+       uint32_t end;
+
+       unsigned int use_count;
+       struct connman_ippool *pool;
+};
+
+struct connman_ippool {
+       unsigned int refcount;
+
+       struct address_info *info;
+
+       char *gateway;
+       char *broadcast;
+       char *start_ip;
+       char *end_ip;
+       char *subnet_mask;
+
+       ippool_collision_cb_t collision_cb;
+       void *user_data;
+};
+
+GSList *allocated_blocks;
+GHashTable *pool_hash;
+
+static uint32_t last_block;
+static uint32_t block_16_bits;
+static uint32_t block_20_bits;
+static uint32_t block_24_bits;
+static uint32_t subnet_mask_24;
+
+struct connman_ippool *
+__connman_ippool_ref_debug(struct connman_ippool *pool,
+                               const char *file, int line, const char *caller)
+{
+       DBG("%p ref %d by %s:%d:%s()", pool, pool->refcount + 1,
+               file, line, caller);
+
+       __sync_fetch_and_add(&pool->refcount, 1);
+
+       return pool;
+}
+
+void __connman_ippool_unref_debug(struct connman_ippool *pool,
+                               const char *file, int line, const char *caller)
+{
+       if (pool == NULL)
+               return;
+
+       DBG("%p ref %d by %s:%d:%s()", pool, pool->refcount - 1,
+               file, line, caller);
+
+       if (__sync_fetch_and_sub(&pool->refcount, 1) != 1)
+               return;
+
+       g_hash_table_remove(pool_hash, pool);
+}
+
+static char *get_ip(uint32_t ip)
+{
+       struct in_addr addr;
+
+       addr.s_addr = htonl(ip);
+
+       return g_strdup(inet_ntoa(addr));
+}
+
+static uint32_t next_block(uint32_t block)
+{
+       uint32_t next;
+
+       /*
+        * Return the next IP block within the private IP range
+        *
+        * 16-bit block 192.168.0.0 – 192.168.255.255
+        * 20-bit block  172.16.0.0 –  172.31.255.255
+        * 24-bit block    10.0.0.0 –  10.255.255.255
+        */
+
+       next = (block & 0x0000ff00) >> 8;
+       next += 1;
+
+       if (next == 255) {
+               if ((block & 0xffff0000) == block_16_bits) {
+                       /*
+                        * Reached the end of the 16 bit block, switch
+                        * to the 20-bit block.
+                        */
+                       return block_20_bits;
+               }
+
+               if ((block & 0xffff0000) >= block_20_bits) {
+                       next = (block & 0x00ff0000) >> 16;
+                       if (next >= 16 && next < 32)
+                               next += 1;
+
+                       if (next == 32) {
+                               /*
+                                * Reached the end of the 20 bit
+                                * block, switch to the 24-bit block.
+                                */
+                               return block_24_bits;
+                       }
+
+                       return (block & 0xff000000) |
+                               ((next << 16) & 0x00ff0000);
+               }
+
+               if ((block & 0xff000000) == block_24_bits) {
+                       next = (block & 0x00ff0000) >> 16;
+                       if (next < 255)
+                               next += 1;
+
+                       if (next == 255) {
+                               /*
+                                * Reached the end of the 24 bit
+                                * block, switch to the 16-bit block.
+                                */
+                               return block_16_bits;
+                       }
+
+                       return (block & 0xff000000) |
+                               ((next << 16) & 0x00ff0000);
+               }
+       }
+
+       return (block & 0xffff0000) | ((next << 8) & 0x0000ff00);
+}
+
+static uint32_t get_free_block(unsigned int size)
+{
+       struct address_info *info;
+       uint32_t block;
+       GSList *list;
+       connman_bool_t collision;
+
+       /*
+        * Instead starting always from the 16 bit block, we start
+        * from the last assigned block. This is a simple optimimazion
+        * for the case where a lot of blocks have been assigned, e.g.
+        * the first half of the private IP pool is in use and a new
+        * we need to find a new block.
+        *
+        * To only thing we have to make sure is that we terminated if
+        * there is no block left.
+        */
+       if (last_block == 0)
+               block = block_16_bits;
+       else
+               block = next_block(last_block);
+
+       do {
+               collision = FALSE;
+               for (list = allocated_blocks; list != NULL; list = list->next) {
+                       info = list->data;
+
+                       if (info->start <= block && block <= info->end) {
+                               collision = TRUE;
+                               break;
+                       }
+               }
+
+               if (collision == FALSE)
+                       return block;
+
+               block = next_block(block);
+       } while (block != last_block);
+
+       return 0;
+}
+
+static struct address_info *lookup_info(int index, uint32_t start)
+{
+       GSList *list;
+
+       for (list = allocated_blocks; list != NULL; list = list->next) {
+               struct address_info *info = list->data;
+
+               if (info->index == index && info->start == start)
+                       return info;
+       }
+
+       return NULL;
+}
+
+static connman_bool_t is_private_address(uint32_t address)
+{
+       uint32_t val;
+
+       if ((address & 0xff000000) == block_24_bits)
+               return TRUE;
+
+       if ((address & 0xffff0000) == block_20_bits) {
+               val = (address & 0x00ff0000) >> 16;
+
+               if (val < 16 || val > 31)
+                       return FALSE;
+
+               return TRUE;
+       }
+
+       if ((address & 0xffffff00) == block_16_bits)
+               return TRUE;
+
+       return FALSE;
+}
+
+void __connman_ippool_newaddr(int index, const char *address,
+                               unsigned char prefixlen)
+{
+       struct address_info *info, *it;
+       struct in_addr inp;
+       uint32_t start, end, mask;
+       GSList *list;
+
+       if (inet_aton(address, &inp) == 0)
+               return;
+
+       start = ntohl(inp.s_addr);
+       if (is_private_address(start) == FALSE)
+               return;
+
+       if (prefixlen >= 32)
+               mask = 0xffffffff;
+       else
+               mask = ~(0xffffffff >> prefixlen);
+
+       start = start & mask;
+       end = start | ~mask;
+
+       info = lookup_info(index, start);
+       if (info != NULL)
+               goto update;
+
+       info = g_try_new0(struct address_info, 1);
+       if (info == NULL)
+               return;
+
+       info->index = index;
+       info->start = start;
+       info->end = end;
+
+       allocated_blocks = g_slist_prepend(allocated_blocks, info);
+
+update:
+       info->use_count = info->use_count + 1;
+
+       if (info->use_count > 1 || info->pool != NULL) {
+               /*
+                * We need only to check for the first IP in a block for
+                * collisions.
+                */
+               return;
+       }
+
+       for (list = allocated_blocks; list != NULL; list = list->next) {
+               it = list->data;
+
+               if (it == info)
+                       continue;
+
+               if (!(it->start <= info->start || info->start <= it->end))
+                       continue;
+
+               if (it->pool != NULL && it->pool->collision_cb != NULL)
+                       it->pool->collision_cb(it->pool, it->pool->user_data);
+
+               return;
+       }
+}
+
+void __connman_ippool_deladdr(int index, const char *address,
+                               unsigned char prefixlen)
+{
+       struct address_info *info;
+       struct in_addr inp;
+       uint32_t start, mask;
+
+       if (inet_aton(address, &inp) == 0)
+               return;
+
+       start = ntohl(inp.s_addr);
+       if (is_private_address(start) == FALSE)
+               return;
+
+       mask = ~(0xffffffff >> prefixlen);
+       start = start & mask;
+
+       info = lookup_info(index, start);
+       if (info == NULL) {
+               /* In theory this should never happen */
+               connman_error("Inconsistent IP pool management (start not found)");
+               return;
+       }
+
+       info->use_count = info->use_count - 1;
+       if (info->pool != NULL)
+               return;
+
+       if (info->use_count > 0)
+               return;
+
+       allocated_blocks = g_slist_remove(allocated_blocks, info);
+       g_free(info);
+}
+
+struct connman_ippool *__connman_ippool_create(int index,
+                                       unsigned int start,
+                                       unsigned int range,
+                                       ippool_collision_cb_t collision_cb,
+                                       void *user_data)
+{
+       struct connman_ippool *pool;
+       struct address_info *info;
+       uint32_t block;
+
+       DBG("");
+
+       /*
+        * The range is at max 255 and we don't support overlapping
+        * blocks.
+        */
+       if (start + range > 254) {
+               connman_error("IP pool does not support pool size larger than 254");
+               return NULL;
+       }
+
+       block = get_free_block(start + range);
+       if (block == 0) {
+               connman_warn("Could not find a free IP block");
+               return NULL;
+       }
+
+       pool = g_try_new0(struct connman_ippool, 1);
+       if (pool == NULL)
+               return NULL;
+
+       info = g_try_new0(struct address_info, 1);
+       if (info == NULL) {
+               g_free(pool);
+               return NULL;
+       }
+
+       last_block = block;
+
+       info->index = index;
+       info->start = block;
+       info->end = block + range;
+
+       pool->refcount = 1;
+       pool->info = info;
+       pool->collision_cb = collision_cb;
+       pool->user_data = user_data;
+
+       info->pool = pool;
+
+       if (range == 0)
+               range = 1;
+
+       pool->gateway = get_ip(info->start + 1);
+       pool->broadcast = get_ip(info->start + 255);
+       pool->subnet_mask = get_ip(subnet_mask_24);
+       pool->start_ip = get_ip(block + start);
+       pool->end_ip = get_ip(block + start + range);
+
+       allocated_blocks = g_slist_prepend(allocated_blocks, info);
+       g_hash_table_insert(pool_hash, pool, pool);
+
+       return pool;
+}
+
+const char *__connman_ippool_get_gateway(struct connman_ippool *pool)
+{
+       return pool->gateway;
+}
+
+const char *__connman_ippool_get_broadcast(struct connman_ippool *pool)
+{
+       return pool->broadcast;
+}
+
+const char *__connman_ippool_get_start_ip(struct connman_ippool *pool)
+{
+       return pool->start_ip;
+}
+
+const char *__connman_ippool_get_end_ip(struct connman_ippool *pool)
+{
+       return pool->end_ip;
+}
+
+const char *__connman_ippool_get_subnet_mask(struct connman_ippool *pool)
+{
+       return pool->subnet_mask;
+}
+
+static void pool_free(gpointer data)
+{
+       struct connman_ippool *pool = data;
+
+       if (pool->info != NULL) {
+               allocated_blocks = g_slist_remove(allocated_blocks, pool->info);
+               g_free(pool->info);
+       }
+
+       g_free(pool->gateway);
+       g_free(pool->broadcast);
+       g_free(pool->start_ip);
+       g_free(pool->end_ip);
+       g_free(pool->subnet_mask);
+
+       g_free(pool);
+}
+
+int __connman_ippool_init(void)
+{
+       DBG("");
+
+       block_16_bits = ntohl(inet_addr("192.168.0.0"));
+       block_20_bits = ntohl(inet_addr("172.16.0.0"));
+       block_24_bits = ntohl(inet_addr("10.0.0.0"));
+       subnet_mask_24 = ntohl(inet_addr("255.255.255.0"));
+
+       pool_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL,
+                                       pool_free);
+
+       return 0;
+}
+
+void __connman_ippool_cleanup(void)
+{
+       DBG("");
+
+       g_hash_table_destroy(pool_hash);
+       pool_hash = NULL;
+
+       g_slist_free_full(allocated_blocks, g_free);
+       last_block = 0;
+       allocated_blocks = NULL;
+}
index 9d6a9e7..fd6d9d7 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2011  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
@@ -141,29 +141,29 @@ static unsigned long entry_to_offset(struct connman_iptables *table,
        return (void *)entry - (void *)table->blob_entries->entrytable;
 }
 
-static int target_to_verdict(char *target_name)
+static int target_to_verdict(const char *target_name)
 {
-       if (!strcmp(target_name, LABEL_ACCEPT))
+       if (!g_strcmp0(target_name, LABEL_ACCEPT))
                return -NF_ACCEPT - 1;
 
-       if (!strcmp(target_name, LABEL_DROP))
+       if (!g_strcmp0(target_name, LABEL_DROP))
                return -NF_DROP - 1;
 
-       if (!strcmp(target_name, LABEL_QUEUE))
+       if (!g_strcmp0(target_name, LABEL_QUEUE))
                return -NF_QUEUE - 1;
 
-       if (!strcmp(target_name, LABEL_RETURN))
+       if (!g_strcmp0(target_name, LABEL_RETURN))
                return XT_RETURN;
 
        return 0;
 }
 
-static gboolean is_builtin_target(char *target_name)
+static gboolean is_builtin_target(const char *target_name)
 {
-       if (!strcmp(target_name, LABEL_ACCEPT) ||
-               !strcmp(target_name, LABEL_DROP) ||
-               !strcmp(target_name, LABEL_QUEUE) ||
-               !strcmp(target_name, LABEL_RETURN))
+       if (!g_strcmp0(target_name, LABEL_ACCEPT) ||
+               !g_strcmp0(target_name, LABEL_DROP) ||
+               !g_strcmp0(target_name, LABEL_QUEUE) ||
+               !g_strcmp0(target_name, LABEL_RETURN))
                return TRUE;
 
        return FALSE;
@@ -175,7 +175,7 @@ static gboolean is_jump(struct connman_iptables_entry *e)
 
        target = ipt_get_target(e->entry);
 
-       if (!strcmp(target->u.user.name, IPT_STANDARD_TARGET)) {
+       if (!g_strcmp0(target->u.user.name, IPT_STANDARD_TARGET)) {
                struct xt_standard_target *t;
 
                t = (struct xt_standard_target *)target;
@@ -207,14 +207,14 @@ static gboolean is_chain(struct connman_iptables *table,
                return TRUE;
 
        target = ipt_get_target(entry);
-       if (!strcmp(target->u.user.name, IPT_ERROR_TARGET))
+       if (!g_strcmp0(target->u.user.name, IPT_ERROR_TARGET))
                return TRUE;
 
        return FALSE;
 }
 
 static GList *find_chain_head(struct connman_iptables *table,
-                               char *chain_name)
+                               const char *chain_name)
 {
        GList *list;
        struct connman_iptables_entry *head;
@@ -228,13 +228,13 @@ static GList *find_chain_head(struct connman_iptables *table,
 
                /* Buit-in chain */
                builtin = head->builtin;
-               if (builtin >= 0 && !strcmp(hooknames[builtin], chain_name))
+               if (builtin >= 0 && !g_strcmp0(hooknames[builtin], chain_name))
                        break;
 
                /* User defined chain */
                target = ipt_get_target(entry);
-               if (!strcmp(target->u.user.name, IPT_ERROR_TARGET) &&
-                   !strcmp((char *)target->data, chain_name))
+               if (!g_strcmp0(target->u.user.name, IPT_ERROR_TARGET) &&
+                   !g_strcmp0((char *)target->data, chain_name))
                        break;
        }
 
@@ -242,7 +242,7 @@ static GList *find_chain_head(struct connman_iptables *table,
 }
 
 static GList *find_chain_tail(struct connman_iptables *table,
-                               char *chain_name)
+                               const char *chain_name)
 {
        struct connman_iptables_entry *tail;
        GList *chain_head, *list;
@@ -364,15 +364,16 @@ static int remove_table_entry(struct connman_iptables *table,
        table->size -= entry->entry->next_offset;
        removed = entry->entry->next_offset;
 
-       g_free(entry->entry);
-
        table->entries = g_list_remove(table->entries, entry);
 
+       g_free(entry->entry);
+       g_free(entry);
+
        return removed;
 }
 
 static int iptables_flush_chain(struct connman_iptables *table,
-                                               char *name)
+                                               const char *name)
 {
        GList *chain_head, *chain_tail, *list, *next;
        struct connman_iptables_entry *entry;
@@ -433,7 +434,7 @@ static int iptables_flush_chain(struct connman_iptables *table,
 }
 
 static int iptables_add_chain(struct connman_iptables *table,
-                                       char *name)
+                               const char *name)
 {
        GList *last;
        struct ipt_entry *entry_head;
@@ -466,9 +467,9 @@ static int iptables_add_chain(struct connman_iptables *table,
        entry_head->next_offset = entry_head_size;
 
        error = (struct error_target *) entry_head->elems;
-       strcpy(error->t.u.user.name, IPT_ERROR_TARGET);
+       g_stpcpy(error->t.u.user.name, IPT_ERROR_TARGET);
        error->t.u.user.target_size = ALIGN(sizeof(struct error_target));
-       strcpy(error->error, name);
+       g_stpcpy(error->error, name);
 
        if (iptables_add_entry(table, entry_head, last, -1) < 0)
                goto err_head;
@@ -503,7 +504,8 @@ err_head:
        return -ENOMEM;
 }
 
-static int iptables_delete_chain(struct connman_iptables *table, char *name)
+static int iptables_delete_chain(struct connman_iptables *table,
+                                       const char *name)
 {
        struct connman_iptables_entry *entry;
        GList *chain_head, *chain_tail;
@@ -537,7 +539,7 @@ static int iptables_delete_chain(struct connman_iptables *table, char *name)
 }
 
 static struct ipt_entry *new_rule(struct ipt_ip *ip,
-               char *target_name, struct xtables_target *xt_t,
+               const char *target_name, struct xtables_target *xt_t,
                struct xtables_rule_match *xt_rm)
 {
        struct xtables_rule_match *tmp_xt_rm;
@@ -613,8 +615,9 @@ static void update_hooks(struct connman_iptables *table, GList *chain_head,
 }
 
 static struct ipt_entry *prepare_rule_inclusion(struct connman_iptables *table,
-                               struct ipt_ip *ip, char *chain_name,
-                               char *target_name, struct xtables_target *xt_t,
+                               struct ipt_ip *ip, const char *chain_name,
+                               const char *target_name,
+                               struct xtables_target *xt_t,
                                int *builtin, struct xtables_rule_match *xt_rm)
 {
        GList *chain_tail, *chain_head;
@@ -679,8 +682,9 @@ static int iptables_append_rule(struct connman_iptables *table,
 }
 
 static int iptables_insert_rule(struct connman_iptables *table,
-                               struct ipt_ip *ip, char *chain_name,
-                               char *target_name, struct xtables_target *xt_t,
+                               struct ipt_ip *ip, const char *chain_name,
+                               const char *target_name,
+                               struct xtables_target *xt_t,
                                struct xtables_rule_match *xt_rm)
 {
        struct ipt_entry *new_entry;
@@ -696,7 +700,10 @@ static int iptables_insert_rule(struct connman_iptables *table,
        if (new_entry == NULL)
                return -EINVAL;
 
-       ret = iptables_add_entry(table, new_entry, chain_head->next, builtin);
+       if (builtin == -1)
+               chain_head = chain_head->next;
+
+       ret = iptables_add_entry(table, new_entry, chain_head, builtin);
        if (ret < 0)
                g_free(new_entry);
 
@@ -721,10 +728,16 @@ static gboolean is_same_ipt_entry(struct ipt_entry *i_e1,
 static gboolean is_same_target(struct xt_entry_target *xt_e_t1,
                                        struct xt_entry_target *xt_e_t2)
 {
+       unsigned int i;
+
        if (xt_e_t1 == NULL || xt_e_t2 == NULL)
                return FALSE;
 
-       if (strcmp(xt_e_t1->u.user.name, IPT_STANDARD_TARGET) == 0) {
+       if (g_strcmp0(xt_e_t1->u.user.name, "") == 0 &&
+                       g_strcmp0(xt_e_t2->u.user.name, "") == 0) {
+               /* fallthrough */
+               return TRUE;
+       } else if (g_strcmp0(xt_e_t1->u.user.name, IPT_STANDARD_TARGET) == 0) {
                struct xt_standard_target *xt_s_t1;
                struct xt_standard_target *xt_s_t2;
 
@@ -737,8 +750,14 @@ static gboolean is_same_target(struct xt_entry_target *xt_e_t1,
                if (xt_e_t1->u.target_size != xt_e_t2->u.target_size)
                        return FALSE;
 
-               if (strcmp(xt_e_t1->u.user.name, xt_e_t2->u.user.name) != 0)
+               if (g_strcmp0(xt_e_t1->u.user.name, xt_e_t2->u.user.name) != 0)
                        return FALSE;
+
+               for (i = 0; i < xt_e_t1->u.target_size -
+                               sizeof(struct xt_standard_target); i++) {
+                       if ((xt_e_t1->data[i] ^ xt_e_t2->data[i]) != 0)
+                               return FALSE;
+               }
        }
 
        return TRUE;
@@ -747,6 +766,8 @@ static gboolean is_same_target(struct xt_entry_target *xt_e_t1,
 static gboolean is_same_match(struct xt_entry_match *xt_e_m1,
                                struct xt_entry_match *xt_e_m2)
 {
+       unsigned int i;
+
        if (xt_e_m1 == NULL || xt_e_m2 == NULL)
                return FALSE;
 
@@ -756,15 +777,22 @@ static gboolean is_same_match(struct xt_entry_match *xt_e_m1,
        if (xt_e_m1->u.user.revision != xt_e_m2->u.user.revision)
                return FALSE;
 
-       if (strcmp(xt_e_m1->u.user.name, xt_e_m2->u.user.name) != 0)
+       if (g_strcmp0(xt_e_m1->u.user.name, xt_e_m2->u.user.name) != 0)
                return FALSE;
 
+       for (i = 0; i < xt_e_m1->u.match_size - sizeof(struct xt_entry_match);
+                       i++) {
+               if ((xt_e_m1->data[i] ^ xt_e_m2->data[i]) != 0)
+                       return FALSE;
+       }
+
        return TRUE;
 }
 
-static int iptables_delete_rule(struct connman_iptables *table,
-                               struct ipt_ip *ip, char *chain_name,
-                               char *target_name, struct xtables_target *xt_t,
+static GList *find_existing_rule(struct connman_iptables *table,
+                               struct ipt_ip *ip, const char *chain_name,
+                               const char *target_name,
+                               struct xtables_target *xt_t,
                                struct xtables_match *xt_m,
                                struct xtables_rule_match *xt_rm)
 {
@@ -773,24 +801,22 @@ static int iptables_delete_rule(struct connman_iptables *table,
        struct xt_entry_match *xt_e_m = NULL;
        struct connman_iptables_entry *entry;
        struct ipt_entry *entry_test;
-       int builtin, removed;
-
-       removed = 0;
+       int builtin;
 
        chain_head = find_chain_head(table, chain_name);
        if (chain_head == NULL)
-               return -EINVAL;
+               return NULL;
 
        chain_tail = find_chain_tail(table, chain_name);
        if (chain_tail == NULL)
-               return -EINVAL;
+               return NULL;
 
        if (!xt_t && !xt_m)
-               return -EINVAL;
+               return NULL;
 
        entry_test = new_rule(ip, target_name, xt_t, xt_rm);
        if (entry_test == NULL)
-               return -EINVAL;
+               return NULL;
 
        if (xt_t != NULL)
                xt_e_t = ipt_get_target(entry_test);
@@ -805,7 +831,7 @@ static int iptables_delete_rule(struct connman_iptables *table,
        else
                list = chain_head->next;
 
-       for (entry = NULL; list != chain_tail->prev; list = list->next) {
+       for (; list != chain_tail->prev; list = list->next) {
                struct connman_iptables_entry *tmp;
                struct ipt_entry *tmp_e;
 
@@ -833,14 +859,49 @@ static int iptables_delete_rule(struct connman_iptables *table,
                                continue;
                }
 
-               entry = tmp;
                break;
        }
 
-       if (entry == NULL) {
-               g_free(entry_test);
+       g_free(entry_test);
+
+       if (list != chain_tail->prev)
+               return list;
+
+       return NULL;
+}
+
+static int iptables_delete_rule(struct connman_iptables *table,
+                               struct ipt_ip *ip, const char *chain_name,
+                               const char *target_name,
+                               struct xtables_target *xt_t,
+                               struct xtables_match *xt_m,
+                               struct xtables_rule_match *xt_rm)
+{
+       struct connman_iptables_entry *entry;
+       GList *chain_head, *chain_tail, *list;
+       int builtin, removed;
+
+       removed = 0;
+
+       chain_head = find_chain_head(table, chain_name);
+       if (chain_head == NULL)
+               return -EINVAL;
+
+       chain_tail = find_chain_tail(table, chain_name);
+       if (chain_tail == NULL)
+               return -EINVAL;
+
+       list = find_existing_rule(table, ip, chain_name, target_name,
+                                                       xt_t, xt_m, xt_rm);
+       if (list == NULL)
+               return -EINVAL;
+
+       entry = chain_head->data;
+       builtin = entry->builtin;
+
+       entry = list->data;
+       if (entry == NULL)
                return -EINVAL;
-       }
 
        /* We have deleted a rule,
         * all references should be bumped accordingly */
@@ -875,8 +936,30 @@ static int iptables_delete_rule(struct connman_iptables *table,
        return 0;
 }
 
+static int iptables_compare_rule(struct connman_iptables *table,
+                               struct ipt_ip *ip, char *chain_name,
+                               char *target_name, struct xtables_target *xt_t,
+                               struct xtables_match *xt_m,
+                               struct xtables_rule_match *xt_rm)
+{
+       struct connman_iptables_entry *entry;
+       GList *found;
+
+       found = find_existing_rule(table, ip, chain_name, target_name,
+                                                       xt_t, xt_m, xt_rm);
+       if (found == NULL)
+               return -EINVAL;
+
+       entry = found->data;
+       if (entry == NULL)
+               return -EINVAL;
+
+       return 0;
+}
+
+
 static int iptables_change_policy(struct connman_iptables *table,
-                                       char *chain_name, char *policy)
+                               const char *chain_name, const char *policy)
 {
        GList *chain_head;
        struct connman_iptables_entry *entry;
@@ -924,7 +1007,7 @@ static struct ipt_replace *iptables_blob(struct connman_iptables *table)
                return NULL;
        }
 
-       strcpy(r->name, table->info->name);
+       g_stpcpy(r->name, table->info->name);
        r->num_entries = table->num_entries;
        r->size = table->size;
 
@@ -952,24 +1035,23 @@ static void dump_ip(struct ipt_entry *entry)
        char ip_mask[INET6_ADDRSTRLEN];
 
        if (strlen(ip->iniface))
-               connman_info("\tin %s", ip->iniface);
+               DBG("\tin %s", ip->iniface);
 
        if (strlen(ip->outiface))
-               connman_info("\tout %s", ip->outiface);
+               DBG("\tout %s", ip->outiface);
 
        if (inet_ntop(AF_INET, &ip->src, ip_string, INET6_ADDRSTRLEN) != NULL &&
                        inet_ntop(AF_INET, &ip->smsk,
                                        ip_mask, INET6_ADDRSTRLEN) != NULL)
-               connman_info("\tsrc %s/%s", ip_string, ip_mask);
+               DBG("\tsrc %s/%s", ip_string, ip_mask);
 
        if (inet_ntop(AF_INET, &ip->dst, ip_string, INET6_ADDRSTRLEN) != NULL &&
                        inet_ntop(AF_INET, &ip->dmsk,
                                        ip_mask, INET6_ADDRSTRLEN) != NULL)
-               connman_info("\tdst %s/%s", ip_string, ip_mask);
+               DBG("\tdst %s/%s", ip_string, ip_mask);
 }
 
-static void dump_target(struct connman_iptables *table,
-                               struct ipt_entry *entry)
+static void dump_target(struct ipt_entry *entry)
 
 {
        struct xtables_target *xt_t;
@@ -977,36 +1059,34 @@ static void dump_target(struct connman_iptables *table,
 
        target = ipt_get_target(entry);
 
-       if (!strcmp(target->u.user.name, IPT_STANDARD_TARGET)) {
+       if (!g_strcmp0(target->u.user.name, IPT_STANDARD_TARGET)) {
                struct xt_standard_target *t;
 
                t = (struct xt_standard_target *)target;
 
                switch (t->verdict) {
                case XT_RETURN:
-                       connman_info("\ttarget RETURN");
+                       DBG("\ttarget RETURN");
                        break;
 
                case -NF_ACCEPT - 1:
-                       connman_info("\ttarget ACCEPT");
+                       DBG("\ttarget ACCEPT");
                        break;
 
                case -NF_DROP - 1:
-                       connman_info("\ttarget DROP");
+                       DBG("\ttarget DROP");
                        break;
 
                case -NF_QUEUE - 1:
-                       connman_info("\ttarget QUEUE");
+                       DBG("\ttarget QUEUE");
                        break;
 
                case -NF_STOP - 1:
-                       connman_info("\ttarget STOP");
+                       DBG("\ttarget STOP");
                        break;
 
                default:
-                       connman_info("\tJUMP @%p (0x%x)",
-                               (char*)table->blob_entries->entrytable +
-                               t->verdict, t->verdict);
+                       DBG("\tJUMP %u", t->verdict);
                        break;
                }
 
@@ -1018,18 +1098,18 @@ static void dump_target(struct connman_iptables *table,
        } else {
                xt_t = xtables_find_target(target->u.user.name, XTF_TRY_LOAD);
                if (xt_t == NULL) {
-                       connman_info("\ttarget %s", target->u.user.name);
+                       DBG("\ttarget %s", target->u.user.name);
                        return;
                }
 
                if(xt_t->print != NULL) {
-                       connman_info("\ttarget ");
+                       DBG("\ttarget ");
                        xt_t->print(NULL, target, 1);
                }
        }
 }
 
-static void dump_match(struct connman_iptables *table, struct ipt_entry *entry)
+static void dump_match(struct ipt_entry *entry)
 {
        struct xtables_match *xt_m;
        struct xt_entry_match *match;
@@ -1047,14 +1127,14 @@ static void dump_match(struct connman_iptables *table, struct ipt_entry *entry)
                goto out;
 
        if(xt_m->print != NULL) {
-               connman_info("\tmatch ");
+               DBG("\tmatch ");
                xt_m->print(NULL, match, 1);
 
                return;
        }
 
 out:
-       connman_info("\tmatch %s", match->u.user.name);
+       DBG("\tmatch %s", match->u.user.name);
 
 }
 
@@ -1094,8 +1174,8 @@ static int dump_entry(struct ipt_entry *entry,
                                entry->next_offset);
        }
 
-       dump_match(table, entry);
-       dump_target(table, entry);
+       dump_match(entry);
+       dump_target(entry);
        dump_ip(entry);
 
        return 0;
@@ -1117,18 +1197,29 @@ static void iptables_dump(struct connman_iptables *table)
 static int iptables_get_entries(struct connman_iptables *table)
 {
        socklen_t entry_size;
+       int err;
 
        entry_size = sizeof(struct ipt_get_entries) + table->info->size;
 
-       return getsockopt(table->ipt_sock, IPPROTO_IP, IPT_SO_GET_ENTRIES,
+       err = getsockopt(table->ipt_sock, IPPROTO_IP, IPT_SO_GET_ENTRIES,
                                table->blob_entries, &entry_size);
+       if (err < 0)
+               return -errno;
+
+       return 0;
 }
 
 static int iptables_replace(struct connman_iptables *table,
                                        struct ipt_replace *r)
 {
-       return setsockopt(table->ipt_sock, IPPROTO_IP, IPT_SO_SET_REPLACE, r,
-                        sizeof(*r) + r->size);
+       int err;
+
+       err = setsockopt(table->ipt_sock, IPPROTO_IP, IPT_SO_SET_REPLACE, r,
+                       sizeof(*r) + r->size);
+       if (err < 0)
+               return -errno;
+
+       return 0;
 }
 
 static int add_entry(struct ipt_entry *entry, struct connman_iptables *table)
@@ -1152,7 +1243,11 @@ static void table_cleanup(struct connman_iptables *table)
        GList *list;
        struct connman_iptables_entry *entry;
 
-       close(table->ipt_sock);
+       if (table == NULL)
+               return;
+
+       if (table->ipt_sock >= 0)
+               close(table->ipt_sock);
 
        for (list = table->entries; list; list = list->next) {
                entry = list->data;
@@ -1167,26 +1262,28 @@ static void table_cleanup(struct connman_iptables *table)
        g_free(table);
 }
 
-static struct connman_iptables *iptables_init(char *table_name)
+static struct connman_iptables *iptables_init(const char *table_name)
 {
        struct connman_iptables *table = NULL;
        char *module = NULL;
        socklen_t s;
 
+       if (table_name == NULL)
+               table_name = "filter";
+
        DBG("%s", table_name);
 
        if (xtables_insmod("ip_tables", NULL, TRUE) != 0)
-               goto err;
+               DBG("ip_tables module loading gives error but trying anyway");
 
        module = g_strconcat("iptable_", table_name, NULL);
        if (module == NULL)
-               goto err;
+               return NULL;
 
        if (xtables_insmod(module, NULL, TRUE) != 0)
-               goto err;
+               DBG("%s module loading gives error but trying anyway", module);
 
        g_free(module);
-       module = NULL;
 
        table = g_hash_table_lookup(table_hash, table_name);
        if (table != NULL)
@@ -1207,8 +1304,11 @@ static struct connman_iptables *iptables_init(char *table_name)
        s = sizeof(*table->info);
        strcpy(table->info->name, table_name);
        if (getsockopt(table->ipt_sock, IPPROTO_IP, IPT_SO_GET_INFO,
-                                               table->info, &s) < 0)
+                                               table->info, &s) < 0) {
+               connman_error("iptables support missing error %d (%s)", errno,
+                       strerror(errno));
                goto err;
+       }
 
        table->blob_entries = g_try_malloc0(sizeof(struct ipt_get_entries) +
                                                table->info->size);
@@ -1239,8 +1339,6 @@ static struct connman_iptables *iptables_init(char *table_name)
        return table;
 
 err:
-       g_free(module);
-
        table_cleanup(table);
 
        return NULL;
@@ -1248,6 +1346,7 @@ err:
 
 static struct option iptables_opts[] = {
        {.name = "append",        .has_arg = 1, .val = 'A'},
+       {.name = "compare",       .has_arg = 1, .val = 'C'},
        {.name = "delete",        .has_arg = 1, .val = 'D'},
        {.name = "flush-chain",   .has_arg = 1, .val = 'F'},
        {.name = "insert",        .has_arg = 1, .val = 'I'},
@@ -1272,7 +1371,7 @@ struct xtables_globals iptables_globals = {
 };
 
 static struct xtables_target *prepare_target(struct connman_iptables *table,
-                                                       char *target_name)
+                                                       const char *target_name)
 {
        struct xtables_target *xt_t = NULL;
        gboolean is_builtin, is_user_defined;
@@ -1318,11 +1417,6 @@ static struct xtables_target *prepare_target(struct connman_iptables *table,
                else if (is_user_defined == TRUE) {
                        struct connman_iptables_entry *target_rule;
 
-                       if (chain_head == NULL) {
-                               g_free(xt_t->t);
-                               return NULL;
-                       }
-
                        target_rule = chain_head->next->data;
                        target->verdict = target_rule->offset;
                }
@@ -1361,7 +1455,8 @@ static struct xtables_target *prepare_target(struct connman_iptables *table,
 }
 
 static struct xtables_match *prepare_matches(struct connman_iptables *table,
-                       struct xtables_rule_match **xt_rm, char *match_name)
+                                       struct xtables_rule_match **xt_rm,
+                                       const char *match_name)
 {
        struct xtables_match *xt_m;
        size_t match_size;
@@ -1414,6 +1509,52 @@ done:
        return xt_m;
 }
 
+static int parse_ip_and_mask(const char *str, struct in_addr *ip, struct in_addr *mask)
+{
+       char **tokens;
+       uint32_t prefixlength;
+       uint32_t tmp;
+       int err;
+
+       tokens = g_strsplit(str, "/", 2);
+       if (tokens == NULL)
+               return -1;
+
+       if (!inet_pton(AF_INET, tokens[0], ip)) {
+               err = -1;
+               goto out;
+       }
+
+       if (tokens[1] != NULL) {
+               prefixlength = strtol(tokens[1], NULL, 10);
+               if (prefixlength > 31) {
+                       err = -1;
+                       goto out;
+               }
+
+               tmp = ~(0xffffffff >> prefixlength);
+       } else {
+               tmp = 0xffffffff;
+       }
+
+       mask->s_addr = htonl(tmp);
+       ip->s_addr = ip->s_addr & mask->s_addr;
+       err = 0;
+out:
+       g_strfreev(tokens);
+
+       return err;
+}
+
+static struct connman_iptables *pre_load_table(const char *table_name,
+                                       struct connman_iptables *table)
+{
+       if (table != NULL)
+               return table;
+
+       return iptables_init(table_name);
+}
+
 static int iptables_command(int argc, char *argv[])
 {
        struct connman_iptables *table;
@@ -1424,8 +1565,7 @@ static int iptables_command(int argc, char *argv[])
        char *table_name, *chain, *new_chain, *match_name, *target_name;
        char *flush_chain, *delete_chain, *policy;
        int c, ret, in_len, out_len;
-       gboolean dump, invert, insert, delete;
-       struct in_addr src, dst;
+       gboolean dump, invert, insert, delete, compare;
 
        if (argc == 0)
                return -EINVAL;
@@ -1434,33 +1574,45 @@ static int iptables_command(int argc, char *argv[])
        invert = FALSE;
        insert = FALSE;
        delete = FALSE;
-       table_name = chain = new_chain = match_name = target_name = NULL;
-       flush_chain = delete_chain = policy = NULL;
+       compare = FALSE;
+       chain = new_chain = match_name = target_name = NULL;
+       flush_chain = delete_chain = policy = table_name = NULL;
        memset(&ip, 0, sizeof(struct ipt_ip));
        table = NULL;
        xt_rm = NULL;
        xt_m = NULL;
        xt_t = NULL;
-       ret = 0;
+       /* Default code for options parsing */
+       ret = -EINVAL;
 
        /* extension's options will generate false-positives errors */
        opterr = 0;
 
        optind = 0;
 
-       while ((c = getopt_long(argc, argv, "-A:F:I:L::N:P:X:d:j:i:m:o:s:t:",
+       while ((c = getopt_long(argc, argv,
+                                       "-A:C:D:F:I:L::N:P:X:d:j:i:m:o:s:t:",
                                        iptables_globals.opts, NULL)) != -1) {
                switch (c) {
                case 'A':
-                       /* It is either -A, -D or -I at once */
+                       /* It is either -A, -C, -D or -I at once */
                        if (chain)
                                goto out;
 
                        chain = optarg;
                        break;
 
+               case 'C':
+                       /* It is either -A, -C, -D or -I at once */
+                       if (chain)
+                               goto out;
+
+                       chain = optarg;
+                       compare = TRUE;
+                       break;
+
                case 'D':
-                       /* It is either -A, -D or -I at once */
+                       /* It is either -A, -C, -D or -I at once */
                        if (chain)
                                goto out;
 
@@ -1473,7 +1625,7 @@ static int iptables_command(int argc, char *argv[])
                        break;
 
                case 'I':
-                       /* It is either -A, -D or -I at once */
+                       /* It is either -A, -C, -D or -I at once */
                        if (chain)
                                goto out;
 
@@ -1503,12 +1655,9 @@ static int iptables_command(int argc, char *argv[])
                        break;
 
                case 'd':
-                       if (!inet_pton(AF_INET, optarg, &dst))
+                       if (!parse_ip_and_mask(optarg, &ip.dst, &ip.dmsk))
                                break;
 
-                       ip.dst = dst;
-                       inet_pton(AF_INET, "255.255.255.255", &ip.dmsk);
-
                        if (invert)
                                ip.invflags |= IPT_INV_DSTIP;
 
@@ -1530,6 +1679,11 @@ static int iptables_command(int argc, char *argv[])
 
                case 'j':
                        target_name = optarg;
+
+                       table = pre_load_table(table_name, table);
+                       if (table == NULL)
+                               goto out;
+
                        xt_t = prepare_target(table, target_name);
                        if (xt_t == NULL)
                                goto out;
@@ -1538,6 +1692,11 @@ static int iptables_command(int argc, char *argv[])
 
                case 'm':
                        match_name = optarg;
+
+                       table = pre_load_table(table_name, table);
+                       if (table == NULL)
+                               goto out;
+
                        xt_m = prepare_matches(table, &xt_rm, match_name);
                        if (xt_m == NULL)
                                goto out;
@@ -1559,12 +1718,9 @@ static int iptables_command(int argc, char *argv[])
                        break;
 
                case 's':
-                       if (!inet_pton(AF_INET, optarg, &src))
+                       if (!parse_ip_and_mask(optarg, &ip.src, &ip.smsk))
                                break;
 
-                       ip.src = src;
-                       inet_pton(AF_INET, "255.255.255.255", &ip.smsk);
-
                        if (invert)
                                ip.invflags |= IPT_INV_SRCIP;
 
@@ -1573,11 +1729,9 @@ static int iptables_command(int argc, char *argv[])
                case 't':
                        table_name = optarg;
 
-                       table = iptables_init(table_name);
-                       if (table == NULL) {
-                               ret = -EINVAL;
+                       table = pre_load_table(table_name, table);
+                       if (table == NULL)
                                goto out;
-                       }
 
                        break;
 
@@ -1590,7 +1744,6 @@ static int iptables_command(int argc, char *argv[])
 
                        connman_error("Invalid option");
 
-                       ret = -EINVAL;
                        goto out;
 
                default:
@@ -1669,15 +1822,12 @@ static int iptables_command(int argc, char *argv[])
                xt_t->final_check(xt_t->tflags);
 #endif
 
-       if (table == NULL) {
-               table_name = "filter";
+       table = pre_load_table(table_name, table);
+       if (table == NULL)
+               goto out;
 
-               table = iptables_init(table_name);
-               if (table == NULL) {
-                       ret = -EINVAL;
-                       goto out;
-               }
-       }
+       /* Option parsing went fine, falling back to succes code */
+       ret = 0;
 
        if (delete_chain != NULL) {
                printf("Delete chain %s\n", delete_chain);
@@ -1690,7 +1840,6 @@ static int iptables_command(int argc, char *argv[])
        if (dump) {
                iptables_dump(table);
 
-               ret = 0;
                goto out;
        }
 
@@ -1726,6 +1875,12 @@ static int iptables_command(int argc, char *argv[])
                if (xt_t == NULL)
                        goto out;
 
+               if (compare == TRUE) {
+                       ret = iptables_compare_rule(table, &ip, chain,
+                                       target_name, xt_t, xt_m, xt_rm);
+                       goto out;
+               }
+
                if (delete == TRUE) {
                        DBG("Deleting %s to %s (match %s)\n",
                                        target_name, chain, match_name);
index 3d8829f..ee95696 100644 (file)
--- a/src/log.c
+++ b/src/log.c
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
@@ -41,9 +41,9 @@ static const char *program_path;
 #if defined TIZEN_EXT
 #include <sys/stat.h>
 
-#define LOG_FILE_PATH "/var/log/connman.log"
-#define MAX_LOG_SIZE   2 * 1024 * 1024
-#define MAX_LOG_COUNT  9
+#define LOG_FILE_PATH "/opt/usr/data/network/connman.log"
+#define MAX_LOG_SIZE   1 * 1024 * 1024
+#define MAX_LOG_COUNT  3
 
 #define openlog __connman_log_open
 #define closelog __connman_log_close
@@ -113,7 +113,7 @@ static void __connman_log_get_local_time(char *strtime, const int size)
        buf = time(NULL);
        local_ptm = localtime(&buf);
 
-       strftime(strtime, size, "%D %H:%M:%S", local_ptm);
+       strftime(strtime, size, "%m/%d %H:%M:%S", local_ptm);
 }
 
 void __connman_log(const int log_priority, const char *format, va_list ap)
@@ -304,7 +304,7 @@ static void print_backtrace(unsigned int offset)
                if (written < 0)
                        break;
 
-               len = read(infd[0], buf, sizeof(buf));
+               len = read(infd[0], buf, sizeof(buf) - 1);
                if (len < 0)
                        break;
 
@@ -367,32 +367,8 @@ static void signal_setup(sighandler_t handler)
 extern struct connman_debug_desc __start___debug[];
 extern struct connman_debug_desc __stop___debug[];
 
-void __connman_debug_list_available(DBusMessageIter *iter, void *user_data)
-{
-       struct connman_debug_desc *desc;
-
-       for (desc = __start___debug; desc < __stop___debug; desc++) {
-               if ((desc->flags & CONNMAN_DEBUG_FLAG_ALIAS) &&
-                                               desc->name != NULL)
-                       dbus_message_iter_append_basic(iter,
-                                       DBUS_TYPE_STRING, &desc->name);
-       }
-}
-
 static gchar **enabled = NULL;
 
-void __connman_debug_list_enabled(DBusMessageIter *iter, void *user_data)
-{
-       int i;
-
-       if (enabled == NULL)
-               return;
-
-       for (i = 0; enabled[i] != NULL; i++)
-               dbus_message_iter_append_basic(iter,
-                                       DBUS_TYPE_STRING, &enabled[i]);
-}
-
 static connman_bool_t is_enabled(struct connman_debug_desc *desc)
 {
        int i;
index 6cc1aa0..22435fc 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
 #include <getopt.h>
 #include <sys/stat.h>
 #include <net/if.h>
+#include <netdb.h>
 
 #include <gdbus.h>
 
-#ifdef HAVE_CAPNG
-#include <cap-ng.h>
-#endif
-
 #include "connman.h"
 
+#define DEFAULT_INPUT_REQUEST_TIMEOUT 120 * 1000
+#define DEFAULT_BROWSER_LAUNCH_TIMEOUT 300 * 1000
+
 static struct {
        connman_bool_t bg_scan;
-       connman_bool_t single_connection;
+       char **pref_timeservers;
+       unsigned int *auto_connect;
+       unsigned int *preferred_techs;
+       char **fallback_nameservers;
+       unsigned int timeout_inputreq;
+       unsigned int timeout_browserlaunch;
+       char **blacklisted_interfaces;
+       connman_bool_t single_tech;
+#if defined TIZEN_EXT
+       char **cellular_interfaces;
+       char **online_accessibility;
+       unsigned int *wifi_pairing_priority;
+       char **preferred_network_names;
+       connman_bool_t wifi_dhcp_release;
+#endif
 } connman_settings  = {
        .bg_scan = TRUE,
-       .single_connection = FALSE,
+       .pref_timeservers = NULL,
+       .auto_connect = NULL,
+       .preferred_techs = NULL,
+       .fallback_nameservers = NULL,
+       .timeout_inputreq = DEFAULT_INPUT_REQUEST_TIMEOUT,
+       .timeout_browserlaunch = DEFAULT_BROWSER_LAUNCH_TIMEOUT,
+       .blacklisted_interfaces = NULL,
+#if defined TIZEN_EXT
+       .single_tech = TRUE,
+       .online_accessibility = NULL,
+       .wifi_pairing_priority = NULL,
+       .preferred_network_names = NULL,
+       .wifi_dhcp_release = FALSE,
+#else
+       .single_tech = FALSE,
+#endif
 };
 
 static GKeyFile *load_config(const char *file)
@@ -75,13 +104,181 @@ static GKeyFile *load_config(const char *file)
        return keyfile;
 }
 
-static void parse_config(GKeyFile *config)
+static uint *parse_service_types(char **str_list, gsize len)
+{
+       unsigned int *type_list;
+       int i, j;
+       enum connman_service_type type;
+
+       type_list = g_try_new0(unsigned int, len + 1);
+       if (type_list == NULL)
+               return NULL;
+
+       i = 0;
+       j = 0;
+       while (str_list[i] != NULL)
+       {
+               type = __connman_service_string2type(str_list[i]);
+
+               if (type != CONNMAN_SERVICE_TYPE_UNKNOWN) {
+                       type_list[j] = type;
+                       j += 1;
+               }
+               i += 1;
+       }
+
+       return type_list;
+}
+
+static char **parse_fallback_nameservers(char **nameservers, gsize len)
+{
+       char **servers;
+       int i, j;
+
+       servers = g_try_new0(char *, len + 1);
+       if (servers == NULL)
+               return NULL;
+
+       i = 0;
+       j = 0;
+       while (nameservers[i] != NULL) {
+               if (connman_inet_check_ipaddress(nameservers[i]) > 0) {
+                       servers[j] = g_strdup(nameservers[i]);
+                       j += 1;
+               }
+               i += 1;
+       }
+
+       return servers;
+}
+
+#if defined TIZEN_EXT
+#define TIZEN_CUSTOM_SETTING "/opt/system/csc-default/usr"
+
+static enum connman_service_security convert_wifi_security(const char *security)
+{
+       if (security == NULL)
+               return CONNMAN_SERVICE_SECURITY_UNKNOWN;
+       else if (g_str_equal(security, "none") == TRUE)
+               return CONNMAN_SERVICE_SECURITY_NONE;
+       else if (g_str_equal(security, "wep") == TRUE)
+               return CONNMAN_SERVICE_SECURITY_WEP;
+       else if (g_str_equal(security, "psk") == TRUE)
+               return CONNMAN_SERVICE_SECURITY_PSK;
+       else if (g_str_equal(security, "ieee8021x") == TRUE)
+               return CONNMAN_SERVICE_SECURITY_8021X;
+       else
+               return CONNMAN_SERVICE_SECURITY_UNKNOWN;
+}
+
+static uint *parse_wifi_security_types(char **str_list, gsize len)
+{
+       unsigned int *type_list;
+       int i, j;
+       enum connman_service_security type;
+
+       type_list = g_try_new0(unsigned int, len + 1);
+       if (type_list == NULL)
+               return NULL;
+
+       i = 0;
+       j = 0;
+       while (str_list[i] != NULL)
+       {
+               type = convert_wifi_security(str_list[i]);
+
+               if (type != CONNMAN_SERVICE_SECURITY_UNKNOWN) {
+                       type_list[j] = type;
+                       j += 1;
+               }
+               i += 1;
+       }
+
+       return type_list;
+}
+
+static void check_Tizen_configuration(GKeyFile *config)
 {
        GError *error = NULL;
        gboolean boolean;
+       char **cellular_interfaces;
+       char **online_accessibility;
+       char **str_list;
+       gsize len;
 
-       if (config == NULL)
+       cellular_interfaces = g_key_file_get_string_list(config, "General",
+                       "NetworkCellularInterfaceList", &len, &error);
+
+       if (error == NULL)
+               connman_settings.cellular_interfaces = cellular_interfaces;
+
+       g_clear_error(&error);
+
+       online_accessibility = g_key_file_get_string_list(config, "General",
+                       "OnlineAccessibility", &len, &error);
+
+       if (error == NULL)
+               connman_settings.online_accessibility = online_accessibility;
+
+       g_clear_error(&error);
+
+       str_list = g_key_file_get_string_list(config, "General",
+                       "WifiPairingPriority", &len, &error);
+
+       if (error == NULL)
+               connman_settings.wifi_pairing_priority =
+                       parse_wifi_security_types(str_list, len);
+
+       g_strfreev(str_list);
+
+       g_clear_error(&error);
+
+       str_list = g_key_file_get_string_list(config, "General",
+                       "PreferredNetworkNames", &len, &error);
+
+       if (error == NULL)
+               connman_settings.preferred_network_names = str_list;
+
+       g_clear_error(&error);
+
+       boolean = g_key_file_get_boolean(config, "General",
+                       "WifiDhcpRelease", &error);
+
+       if (error == NULL)
+               connman_settings.wifi_dhcp_release = boolean;
+
+       g_clear_error(&error);
+}
+#endif
+
+static void parse_config(GKeyFile *config)
+{
+       GError *error = NULL;
+       gboolean boolean;
+       char **timeservers;
+       char **interfaces;
+       char **str_list;
+       gsize len;
+       static char *default_auto_connect[] = {
+               "wifi",
+               "ethernet",
+               "cellular",
+               NULL
+       };
+       static char *default_blacklist[] = {
+               "vmnet",
+               "vboxnet",
+               "virbr",
+               NULL
+       };
+       int timeout;
+
+       if (config == NULL) {
+               connman_settings.auto_connect =
+                       parse_service_types(default_auto_connect, 3);
+               connman_settings.blacklisted_interfaces = default_blacklist;
                return;
+       }
 
        DBG("parsing main.conf");
 
@@ -92,12 +289,83 @@ static void parse_config(GKeyFile *config)
 
        g_clear_error(&error);
 
+       timeservers = g_key_file_get_string_list(config, "General",
+                                               "FallbackTimeservers", NULL, &error);
+       if (error == NULL)
+               connman_settings.pref_timeservers = timeservers;
+
+       g_clear_error(&error);
+
+       str_list = g_key_file_get_string_list(config, "General",
+                       "DefaultAutoConnectTechnologies", &len, &error);
+
+       if (error == NULL)
+               connman_settings.auto_connect =
+                       parse_service_types(str_list, len);
+       else
+               connman_settings.auto_connect =
+                       parse_service_types(default_auto_connect, 3);
+
+       g_strfreev(str_list);
+
+       g_clear_error(&error);
+
+       str_list = g_key_file_get_string_list(config, "General",
+                       "PreferredTechnologies", &len, &error);
+
+       if (error == NULL)
+               connman_settings.preferred_techs =
+                       parse_service_types(str_list, len);
+
+       g_strfreev(str_list);
+
+       g_clear_error(&error);
+
+       str_list = g_key_file_get_string_list(config, "General",
+                       "FallbackNameservers", &len, &error);
+
+       if (error == NULL)
+               connman_settings.fallback_nameservers =
+                       parse_fallback_nameservers(str_list, len);
+
+       g_strfreev(str_list);
+
+       g_clear_error(&error);
+
+       timeout = g_key_file_get_integer(config, "General",
+                       "InputRequestTimeout", &error);
+       if (error == NULL && timeout >= 0)
+               connman_settings.timeout_inputreq = timeout * 1000;
+
+       g_clear_error(&error);
+
+       timeout = g_key_file_get_integer(config, "General",
+                       "BrowserLaunchTimeout", &error);
+       if (error == NULL && timeout >= 0)
+               connman_settings.timeout_browserlaunch = timeout * 1000;
+
+       g_clear_error(&error);
+
+       interfaces = g_key_file_get_string_list(config, "General",
+                       "NetworkInterfaceBlacklist", &len, &error);
+
+       if (error == NULL)
+               connman_settings.blacklisted_interfaces = interfaces;
+       else
+               connman_settings.blacklisted_interfaces = default_blacklist;
+
+       g_clear_error(&error);
+
        boolean = g_key_file_get_boolean(config, "General",
-                       "SingleConnection", &error);
+                       "SingleConnectedTechnology", &error);
        if (error == NULL)
-               connman_settings.single_connection = boolean;
+               connman_settings.single_tech = boolean;
 
        g_clear_error(&error);
+
+#if defined TIZEN_EXT
+       check_Tizen_configuration(config);
+#endif
 }
 
 static GMainLoop *main_loop = NULL;
@@ -247,12 +515,60 @@ connman_bool_t connman_setting_get_bool(const char *key)
        if (g_str_equal(key, "BackgroundScanning") == TRUE)
                return connman_settings.bg_scan;
 
-       if (g_str_equal(key, "SingleConnection") == TRUE)
-               return connman_settings.single_connection;
+       if (g_str_equal(key, "SingleConnectedTechnology") == TRUE)
+               return connman_settings.single_tech;
 
+#if defined TIZEN_EXT
+       if (g_str_equal(key, "WiFiDHCPRelease") == TRUE)
+               return connman_settings.wifi_dhcp_release;
+#endif
        return FALSE;
 }
 
+char **connman_setting_get_string_list(const char *key)
+{
+       if (g_str_equal(key, "FallbackTimeservers") == TRUE)
+               return connman_settings.pref_timeservers;
+
+       if (g_str_equal(key, "FallbackNameservers") == TRUE)
+               return connman_settings.fallback_nameservers;
+
+       if (g_str_equal(key, "NetworkInterfaceBlacklist") == TRUE)
+               return connman_settings.blacklisted_interfaces;
+
+#if defined TIZEN_EXT
+       if (g_str_equal(key, "NetworkCellularInterfaceList") == TRUE)
+               return connman_settings.cellular_interfaces;
+
+       if (g_str_equal(key, "PreferredNetworkNames") == TRUE)
+               return connman_settings.preferred_network_names;
+#endif
+       return NULL;
+}
+
+unsigned int *connman_setting_get_uint_list(const char *key)
+{
+       if (g_str_equal(key, "DefaultAutoConnectTechnologies") == TRUE)
+               return connman_settings.auto_connect;
+
+       if (g_str_equal(key, "PreferredTechnologies") == TRUE)
+               return connman_settings.preferred_techs;
+
+#if defined TIZEN_EXT
+       if (g_str_equal(key, "WiFiPairingPriority") == TRUE)
+               return connman_settings.wifi_pairing_priority;
+#endif
+       return NULL;
+}
+
+unsigned int connman_timeout_input_request(void) {
+       return connman_settings.timeout_inputreq;
+}
+
+unsigned int connman_timeout_browser_launch(void) {
+       return connman_settings.timeout_browserlaunch;
+}
+
 int main(int argc, char *argv[])
 {
        GOptionContext *context;
@@ -264,10 +580,6 @@ int main(int argc, char *argv[])
        guint signal;
 #endif
 
-#ifdef HAVE_CAPNG
-       /* Drop capabilities */
-#endif
-
 #ifdef NEED_THREADS
        if (g_thread_supported() == FALSE)
                g_thread_init(NULL);
@@ -344,11 +656,15 @@ int main(int argc, char *argv[])
 
        __connman_dbus_init(conn);
 
+#if defined TIZEN_EXT
+       config = load_config(TIZEN_CUSTOM_SETTING "/wifi/main.conf");
+       if (config == NULL)
+#endif
        config = load_config(CONFIGDIR "/main.conf");
-
        parse_config(config);
+       if (config != NULL)
+               g_key_file_free(config);
 
-       __connman_storage_migrate();
        __connman_technology_init();
        __connman_notifier_init();
        __connman_service_init();
@@ -357,7 +673,9 @@ int main(int argc, char *argv[])
        __connman_device_init(option_device, option_nodevice);
 
        __connman_agent_init();
+       __connman_ippool_init();
        __connman_iptables_init();
+       __connman_nat_init();
        __connman_tethering_init();
        __connman_counter_init();
        __connman_manager_init();
@@ -372,13 +690,19 @@ int main(int argc, char *argv[])
        __connman_proxy_init();
        __connman_detect_init();
        __connman_session_init();
+#if !defined TIZEN_EXT
        __connman_timeserver_init();
+#endif
        __connman_connection_init();
 
        __connman_plugin_init(option_plugin, option_noplugin);
 
        __connman_rtnl_start();
+#if defined TIZEN_EXT && defined TIZEN_RTC_TIMER
+       __connman_rtctimer_init();
+#endif
        __connman_dhcp_init();
+       __connman_dhcpv6_init();
        __connman_wpad_init();
        __connman_wispr_init();
        __connman_rfkill_init();
@@ -397,11 +721,17 @@ int main(int argc, char *argv[])
        __connman_rfkill_cleanup();
        __connman_wispr_cleanup();
        __connman_wpad_cleanup();
+       __connman_dhcpv6_cleanup();
        __connman_dhcp_cleanup();
-       __connman_provider_cleanup();
+#if defined TIZEN_EXT && defined TIZEN_RTC_TIMER
+       __connman_rtctimer_cleanup();
+#endif
        __connman_plugin_cleanup();
+       __connman_provider_cleanup();
        __connman_connection_cleanup();
+#if !defined TIZEN_EXT
        __connman_timeserver_cleanup();
+#endif
        __connman_session_cleanup();
        __connman_detect_cleanup();
        __connman_proxy_cleanup();
@@ -416,7 +746,9 @@ int main(int argc, char *argv[])
        __connman_counter_cleanup();
        __connman_agent_cleanup();
        __connman_tethering_cleanup();
+       __connman_nat_cleanup();
        __connman_iptables_cleanup();
+       __connman_ippool_cleanup();
        __connman_device_cleanup();
        __connman_network_cleanup();
        __connman_service_cleanup();
@@ -432,8 +764,12 @@ int main(int argc, char *argv[])
 
        g_main_loop_unref(main_loop);
 
-       if (config)
-               g_key_file_free(config);
+       if (connman_settings.pref_timeservers != NULL)
+               g_strfreev(connman_settings.pref_timeservers);
+
+       g_free(connman_settings.auto_connect);
+       g_free(connman_settings.preferred_techs);
+       g_strfreev(connman_settings.fallback_nameservers);
 
        g_free(option_debug);
 
index 89bca06..87ec2b4 100644 (file)
@@ -1,9 +1,75 @@
 [General]
 
+# Set input request timeout. Default is 120 seconds
+# The request for inputs like passphrase will timeout
+# after certain amount of time. Use this setting to
+# increase the value in case of different user
+# interface designs.
+# InputRequestTimeout = 120
+
+# Set browser launch timeout. Default is 300 seconds
+# The request for launching a browser for portal pages
+# will timeout after certain amount of time. Use this
+# setting to increase the value in case of different
+# user interface designs.
+# BrowserLaunchTimeout = 300
+
 # Enable background scanning. Default is true.
 # Background scanning will start every 5 minutes unless
 # the scan list is empty. In that case, a simple backoff
 # mechanism starting from 10s up to 5 minutes will run.
+BackgroundScanning = false
+
+# List of Fallback timeservers separated by ",".
+# These timeservers are used for NTP sync when there are
+# no timeserver set by the user or by the service.
+# These can contain mixed combination of fully qualified
+# domain names, IPv4 and IPv6 addresses.
+# FallbackTimeservers =
+
+# List of fallback nameservers separated by "," used if no
+# nameservers are otherwise provided by the service. The
+# nameserver entries must be in numeric format, host
+# names are ignored.
+# FallbackNameservers =
+
+# List of technologies that are marked autoconnectable
+# by default, separated by commas ",". The default value
+# for this entry when empty is ethernet,wifi,cellular.
+# Services that are automatically connected must have been
+# set up and saved to storage beforehand.
+# DefaultAutoConnectTechnologies =
+
+# List of preferred technologies from the most preferred
+# one to the least preferred one separated by commas ",".
+# Services of the listed technology type will be tried one
+# by one in the order given, until one of them gets connected
+# or they are all tried. A service of a preferred technology
+# type in state 'ready' will get the default route when
+# compared to another preferred type further down the list
+# with state 'ready' or with a non-preferred type; a service
+# of a preferred technology type in state 'online' will get
+# the default route when compared to either a non-preferred
+# type or a preferred type further down in the list.
+PreferredTechnologies = wifi
+
+# List of blacklisted network interfaces separated by ",".
+# Found interfaces will be compared to the list and will
+# not be handled by connman, if their first characters
+# match any of the list entries. Default value is
+# vmnet,vboxnet,virbr.
+NetworkInterfaceBlacklist = vmnet,vboxnet,virbr,usb,rndis,rmnet,rev_rmnet,dummy,seth_td,seth_w
+
+# Keep only a single connected technology at any time. When a new
+# service is connected by the user or a better one is found according
+# to PreferredTechnologies, the new service is kept connected and all
+# the other previously connected services are disconnected. With this
+# setting it does not matter whether the previously connected services
+# are in 'online' or 'ready' states, the newly connected service is
+# the only one that will be kept connected. A service connected by the
+# user will be used until going out of network coverage. With this
+# setting enabled applications will notice more network breaks than
+# normal. Default value is false.
+SingleConnectedTechnology = true
 
-BackgroundScanning = true
-SingleConnection = true
+NetworkCellularInterfaceList = pdp,rmnet,seth_td,seth_w
index 2b34ea6..0fa6f63 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
@@ -29,8 +29,8 @@
 
 #include "connman.h"
 
-connman_bool_t connman_state_idle;
-DBusMessage *session_mode_pending = NULL;
+static connman_bool_t connman_state_idle;
+static DBusMessage *session_mode_pending = NULL;
 
 static DBusMessage *get_properties(DBusConnection *conn,
                                        DBusMessage *msg, void *data)
@@ -50,11 +50,6 @@ static DBusMessage *get_properties(DBusConnection *conn,
 
        connman_dbus_dict_open(&array, &dict);
 
-       connman_dbus_dict_append_array(&dict, "Services",
-                       DBUS_TYPE_OBJECT_PATH, __connman_service_list, NULL);
-       connman_dbus_dict_append_array(&dict, "Technologies",
-                       DBUS_TYPE_OBJECT_PATH, __connman_technology_list, NULL);
-
        str = __connman_notifier_get_state();
        connman_dbus_dict_append_basic(&dict, "State",
                                                DBUS_TYPE_STRING, &str);
@@ -63,23 +58,6 @@ static DBusMessage *get_properties(DBusConnection *conn,
        connman_dbus_dict_append_basic(&dict, "OfflineMode",
                                        DBUS_TYPE_BOOLEAN, &offlinemode);
 
-       connman_dbus_dict_append_array(&dict, "AvailableTechnologies",
-               DBUS_TYPE_STRING, __connman_notifier_list_registered, NULL);
-       connman_dbus_dict_append_array(&dict, "EnabledTechnologies",
-               DBUS_TYPE_STRING, __connman_notifier_list_enabled, NULL);
-       connman_dbus_dict_append_array(&dict, "ConnectedTechnologies",
-               DBUS_TYPE_STRING, __connman_notifier_list_connected, NULL);
-
-       str = __connman_service_default();
-       if (str != NULL)
-               connman_dbus_dict_append_basic(&dict, "DefaultTechnology",
-                                               DBUS_TYPE_STRING, &str);
-
-       connman_dbus_dict_append_array(&dict, "AvailableDebugs",
-                       DBUS_TYPE_STRING, __connman_debug_list_available, NULL);
-       connman_dbus_dict_append_array(&dict, "EnabledDebugs",
-                       DBUS_TYPE_STRING, __connman_debug_list_enabled, NULL);
-
        sessionmode = __connman_session_mode();
        connman_dbus_dict_append_basic(&dict, "SessionMode",
                                        DBUS_TYPE_BOOLEAN,
@@ -102,8 +80,15 @@ static DBusMessage *set_property(DBusConnection *conn,
        if (dbus_message_iter_init(msg, &iter) == FALSE)
                return __connman_error_invalid_arguments(msg);
 
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+               return __connman_error_invalid_arguments(msg);
+
        dbus_message_iter_get_basic(&iter, &name);
        dbus_message_iter_next(&iter);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
+               return __connman_error_invalid_arguments(msg);
+
        dbus_message_iter_recurse(&iter, &value);
 
        type = dbus_message_iter_get_arg_type(&value);
@@ -141,17 +126,26 @@ static DBusMessage *set_property(DBusConnection *conn,
        return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
 }
 
-static DBusMessage *get_state(DBusConnection *conn,
-                                       DBusMessage *msg, void *data)
+static void append_technology_structs(DBusMessageIter *iter, void *user_data)
 {
-       const char *str;
+       __connman_technology_list_struct(iter);
+}
 
-       DBG("conn %p", conn);
+static DBusMessage *get_technologies(DBusConnection *conn,
+               DBusMessage *msg, void *data)
+{
+       DBusMessage *reply;
 
-       str = __connman_notifier_get_state();
+       DBG("");
+
+       reply = dbus_message_new_method_return(msg);
+       if (reply == NULL)
+               return NULL;
 
-       return g_dbus_create_reply(msg, DBUS_TYPE_STRING, &str,
-                                               DBUS_TYPE_INVALID);
+       __connman_dbus_append_objpath_dict_array(reply,
+                       append_technology_structs, NULL);
+
+       return reply;
 }
 
 static DBusMessage *remove_provider(DBusConnection *conn,
@@ -172,40 +166,6 @@ static DBusMessage *remove_provider(DBusConnection *conn,
        return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
 }
 
-static DBusMessage *request_scan(DBusConnection *conn,
-                                       DBusMessage *msg, void *data)
-{
-       enum connman_service_type type;
-       const char *str;
-       int err;
-
-       DBG("conn %p", conn);
-
-       dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &str,
-                                                       DBUS_TYPE_INVALID);
-
-       if (g_strcmp0(str, "") == 0)
-               type = CONNMAN_SERVICE_TYPE_UNKNOWN;
-       else if (g_strcmp0(str, "wifi") == 0)
-               type = CONNMAN_SERVICE_TYPE_WIFI;
-       else if (g_strcmp0(str, "wimax") == 0)
-               type = CONNMAN_SERVICE_TYPE_WIMAX;
-       else
-               return __connman_error_invalid_arguments(msg);
-
-       err = __connman_device_request_scan(type);
-       if (err < 0) {
-               if (err == -EINPROGRESS) {
-                       connman_error("Invalid return code from scan");
-                       err = -EINVAL;
-               }
-
-               return __connman_error_failed(msg, -err);
-       }
-
-       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
-}
-
 static DBusConnection *connection = NULL;
 
 static void session_mode_notify(void)
@@ -238,165 +198,29 @@ static struct connman_notifier technology_notifier = {
        .idle_state     = idle_state,
 };
 
-static DBusMessage *enable_technology(DBusConnection *conn,
-                                       DBusMessage *msg, void *data)
+static void append_service_structs(DBusMessageIter *iter, void *user_data)
 {
-       enum connman_service_type type;
-       const char *str;
-
-       DBG("conn %p", conn);
-
-       dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &str,
-                                                       DBUS_TYPE_INVALID);
-
-       if (g_strcmp0(str, "ethernet") == 0)
-               type = CONNMAN_SERVICE_TYPE_ETHERNET;
-       else if (g_strcmp0(str, "wifi") == 0)
-               type = CONNMAN_SERVICE_TYPE_WIFI;
-       else if (g_strcmp0(str, "wimax") == 0)
-               type = CONNMAN_SERVICE_TYPE_WIMAX;
-       else if (g_strcmp0(str, "bluetooth") == 0)
-               type = CONNMAN_SERVICE_TYPE_BLUETOOTH;
-       else if (g_strcmp0(str, "cellular") == 0)
-               type = CONNMAN_SERVICE_TYPE_CELLULAR;
-       else
-               return __connman_error_invalid_arguments(msg);
-
-       if (__connman_notifier_is_registered(type) == FALSE)
-               return __connman_error_not_registered(msg);
-
-       if (__connman_notifier_is_enabled(type) == TRUE)
-               return __connman_error_already_enabled(msg);
-
-        __connman_technology_enable(type, msg);
-
-       return NULL;
-}
-
-static DBusMessage *disable_technology(DBusConnection *conn,
-                                       DBusMessage *msg, void *data)
-{
-       enum connman_service_type type;
-       const char *str;
-
-       DBG("conn %p", conn);
-
-       dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &str,
-                                                       DBUS_TYPE_INVALID);
-
-       if (g_strcmp0(str, "ethernet") == 0)
-               type = CONNMAN_SERVICE_TYPE_ETHERNET;
-       else if (g_strcmp0(str, "wifi") == 0)
-               type = CONNMAN_SERVICE_TYPE_WIFI;
-       else if (g_strcmp0(str, "wimax") == 0)
-               type = CONNMAN_SERVICE_TYPE_WIMAX;
-       else if (g_strcmp0(str, "bluetooth") == 0)
-               type = CONNMAN_SERVICE_TYPE_BLUETOOTH;
-       else if (g_strcmp0(str, "cellular") == 0)
-               type = CONNMAN_SERVICE_TYPE_CELLULAR;
-       else
-               return __connman_error_invalid_arguments(msg);
-
-       if (__connman_notifier_is_registered(type) == FALSE)
-               return __connman_error_not_registered(msg);
-
-       if (__connman_notifier_is_enabled(type) == FALSE)
-               return __connman_error_already_disabled(msg);
-
-       __connman_technology_disable(type, msg);
-
-       return NULL;
+       __connman_service_list_struct(iter);
 }
 
 static DBusMessage *get_services(DBusConnection *conn,
                                        DBusMessage *msg, void *data)
 {
        DBusMessage *reply;
-       DBusMessageIter iter, array;
 
+#if defined TIZEN_EXT
+       DBG("conn %p", conn);
+#endif
        reply = dbus_message_new_method_return(msg);
        if (reply == NULL)
                return NULL;
 
-       dbus_message_iter_init_append(reply, &iter);
-
-       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
-                       DBUS_STRUCT_BEGIN_CHAR_AS_STRING
-                       DBUS_TYPE_OBJECT_PATH_AS_STRING
-                       DBUS_TYPE_ARRAY_AS_STRING
-                               DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
-                                       DBUS_TYPE_STRING_AS_STRING
-                                       DBUS_TYPE_VARIANT_AS_STRING
-                               DBUS_DICT_ENTRY_END_CHAR_AS_STRING
-                       DBUS_STRUCT_END_CHAR_AS_STRING, &array);
-
-       __connman_service_list_struct(&array);
-
-       dbus_message_iter_close_container(&iter, &array);
+       __connman_dbus_append_objpath_dict_array(reply,
+                       append_service_structs, NULL);
 
        return reply;
 }
 
-static DBusMessage *lookup_service(DBusConnection *conn,
-                                       DBusMessage *msg, void *data)
-{
-       const char *pattern, *path;
-       int err;
-
-       DBG("conn %p", conn);
-
-       dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &pattern,
-                                                       DBUS_TYPE_INVALID);
-
-       err = __connman_service_lookup(pattern, &path);
-       if (err < 0)
-               return __connman_error_failed(msg, -err);
-
-       return g_dbus_create_reply(msg, DBUS_TYPE_OBJECT_PATH, &path,
-                                                       DBUS_TYPE_INVALID);
-}
-
-static DBusMessage *connect_service(DBusConnection *conn,
-                                       DBusMessage *msg, void *data)
-{
-       int err;
-
-       DBG("conn %p", conn);
-
-       if (__connman_session_mode() == TRUE) {
-               connman_info("Session mode enabled: "
-                               "direct service connect disabled");
-
-               return __connman_error_failed(msg, -EINVAL);
-       }
-
-       err = __connman_service_create_and_connect(msg);
-       if (err < 0) {
-               if (err == -EINPROGRESS) {
-                       connman_error("Invalid return code from connect");
-                       err = -EINVAL;
-               }
-
-               return __connman_error_failed(msg, -err);
-       }
-
-       return NULL;
-}
-
-static DBusMessage *provision_service(DBusConnection *conn, DBusMessage *msg,
-                                       void *data)
-{
-       int err;
-
-       DBG("conn %p", conn);
-
-       err = __connman_service_provision(msg);
-       if (err < 0)
-               return __connman_error_failed(msg, -err);
-
-       return NULL;
-}
-
 static DBusMessage *connect_provider(DBusConnection *conn,
                                        DBusMessage *msg, void *data)
 {
@@ -408,18 +232,12 @@ static DBusMessage *connect_provider(DBusConnection *conn,
                connman_info("Session mode enabled: "
                                "direct provider connect disabled");
 
-               return __connman_error_failed(msg, -EINVAL);
+               return __connman_error_failed(msg, EINVAL);
        }
 
        err = __connman_provider_create_and_connect(msg);
-       if (err < 0) {
-               if (err == -EINPROGRESS) {
-                       connman_error("Invalid return code from connect");
-                       err = -EINVAL;
-               }
-
+       if (err < 0)
                return __connman_error_failed(msg, -err);
-       }
 
        return NULL;
 }
@@ -517,8 +335,12 @@ static DBusMessage *create_session(DBusConnection *conn,
        DBG("conn %p", conn);
 
        err = __connman_session_create(msg);
-       if (err < 0)
+       if (err < 0) {
+               if (err == -EINPROGRESS)
+                       return NULL;
+
                return __connman_error_failed(msg, -err);
+       }
 
        return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
 }
@@ -572,42 +394,69 @@ static DBusMessage *release_private_network(DBusConnection *conn,
        return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
 }
 
-static GDBusMethodTable manager_methods[] = {
-       { "GetProperties",     "",      "a{sv}", get_properties     },
-       { "SetProperty",       "sv",    "",      set_property,
-                                               G_DBUS_METHOD_FLAG_ASYNC },
-       { "GetState",          "",      "s",     get_state          },
-       { "RemoveProvider",    "o",     "",      remove_provider    },
-       { "RequestScan",       "s",     "",      request_scan       },
-       { "EnableTechnology",  "s",     "",      enable_technology,
-                                               G_DBUS_METHOD_FLAG_ASYNC },
-       { "DisableTechnology", "s",     "",      disable_technology,
-                                               G_DBUS_METHOD_FLAG_ASYNC },
-       { "GetServices",       "",      "a(oa{sv})", get_services   },
-       { "LookupService",     "s",     "o",     lookup_service,    },
-       { "ConnectService",    "a{sv}", "o",     connect_service,
-                                               G_DBUS_METHOD_FLAG_ASYNC },
-       { "ProvisionService",  "s",     "",      provision_service,
-                                               G_DBUS_METHOD_FLAG_ASYNC },
-       { "ConnectProvider",   "a{sv}", "o",     connect_provider,
-                                               G_DBUS_METHOD_FLAG_ASYNC },
-       { "RegisterAgent",     "o",     "",      register_agent     },
-       { "UnregisterAgent",   "o",     "",      unregister_agent   },
-       { "RegisterCounter",   "ouu",   "",      register_counter   },
-       { "UnregisterCounter", "o",     "",      unregister_counter },
-       { "CreateSession",     "a{sv}o", "o",    create_session     },
-       { "DestroySession",    "o",     "",      destroy_session    },
-       { "RequestPrivateNetwork",    "",     "oa{sv}h",
-                                               request_private_network,
-                                               G_DBUS_METHOD_FLAG_ASYNC },
-       { "ReleasePrivateNetwork",    "o",    "",
-                                               release_private_network },
+static const GDBusMethodTable manager_methods[] = {
+       { GDBUS_METHOD("GetProperties",
+                       NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
+                       get_properties) },
+       { GDBUS_ASYNC_METHOD("SetProperty",
+                       GDBUS_ARGS({ "name", "s" }, { "value", "v" }),
+                       NULL, set_property) },
+       { GDBUS_METHOD("GetTechnologies",
+                       NULL, GDBUS_ARGS({ "technologies", "a(oa{sv})" }),
+                       get_technologies) },
+       { GDBUS_METHOD("RemoveProvider",
+                       GDBUS_ARGS({ "provider", "o" }), NULL,
+                       remove_provider) },
+       { GDBUS_METHOD("GetServices",
+                       NULL, GDBUS_ARGS({ "services", "a(oa{sv})" }),
+                       get_services) },
+       { GDBUS_ASYNC_METHOD("ConnectProvider",
+                             GDBUS_ARGS({ "provider", "a{sv}" }),
+                             GDBUS_ARGS({ "path", "o" }),
+                             connect_provider) },
+       { GDBUS_METHOD("RegisterAgent",
+                       GDBUS_ARGS({ "path", "o" }), NULL,
+                       register_agent) },
+       { GDBUS_METHOD("UnregisterAgent",
+                       GDBUS_ARGS({ "path", "o" }), NULL,
+                       unregister_agent) },
+       { GDBUS_METHOD("RegisterCounter",
+                       GDBUS_ARGS({ "path", "o" }, { "accuracy", "u" },
+                                       { "period", "u" }),
+                       NULL, register_counter) },
+       { GDBUS_METHOD("UnregisterCounter",
+                       GDBUS_ARGS({ "path", "o" }), NULL,
+                       unregister_counter) },
+       { GDBUS_METHOD("CreateSession",
+                       GDBUS_ARGS({ "settings", "a{sv}" },
+                                               { "notifier", "o" }),
+                       GDBUS_ARGS({ "session", "o" }),
+                       create_session) },
+       { GDBUS_METHOD("DestroySession",
+                       GDBUS_ARGS({ "session", "o" }), NULL,
+                       destroy_session) },
+       { GDBUS_ASYNC_METHOD("RequestPrivateNetwork",
+                             NULL, GDBUS_ARGS({ "path", "o" },
+                                              { "settings", "a{sv}" },
+                                              { "socket", "h" }),
+                             request_private_network) },
+       { GDBUS_METHOD("ReleasePrivateNetwork",
+                       GDBUS_ARGS({ "path", "o" }), NULL,
+                       release_private_network) },
        { },
 };
 
-static GDBusSignalTable manager_signals[] = {
-       { "PropertyChanged", "sv" },
-       { "StateChanged",    "s"  },
+static const GDBusSignalTable manager_signals[] = {
+       { GDBUS_SIGNAL("PropertyChanged",
+                       GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
+       { GDBUS_SIGNAL("TechnologyAdded",
+                       GDBUS_ARGS({ "path", "o" },
+                                  { "properties", "a{sv}" })) },
+       { GDBUS_SIGNAL("TechnologyRemoved",
+                       GDBUS_ARGS({ "path", "o" })) },
+       { GDBUS_SIGNAL("ServicesChanged",
+                       GDBUS_ARGS({ "changed", "a(oa{sv})" },
+                                       { "removed", "ao" })) },
        { },
 };
 
diff --git a/src/nat.c b/src/nat.c
new file mode 100644 (file)
index 0000000..e17a3f8
--- /dev/null
+++ b/src/nat.c
@@ -0,0 +1,235 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2012  BMW Car IT GmbH. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdio.h>
+
+#include "connman.h"
+
+static char *default_interface;
+static GHashTable *nat_hash;
+
+struct connman_nat {
+       char *address;
+       unsigned char prefixlen;
+
+       char *interface;
+};
+
+static int enable_ip_forward(connman_bool_t enable)
+{
+       FILE *f;
+
+       f = fopen("/proc/sys/net/ipv4/ip_forward", "r+");
+       if (f == NULL)
+               return -errno;
+
+       if (enable == TRUE)
+               fprintf(f, "1");
+       else
+               fprintf(f, "0");
+
+       fclose(f);
+
+       return 0;
+}
+
+static void flush_nat(void)
+{
+       int err;
+
+       err = __connman_iptables_command("-t nat -F POSTROUTING");
+       if (err < 0) {
+               DBG("Flushing the nat table failed");
+
+               return;
+       }
+
+       __connman_iptables_commit("nat");
+}
+
+static int enable_nat(struct connman_nat *nat)
+{
+       int err;
+
+       g_free(nat->interface);
+       nat->interface = g_strdup(default_interface);
+
+       if (nat->interface == NULL)
+               return 0;
+
+       /* Enable masquerading */
+       err = __connman_iptables_command("-t nat -A POSTROUTING "
+                                       "-s %s/%d -o %s -j MASQUERADE",
+                                       nat->address,
+                                       nat->prefixlen,
+                                       nat->interface);
+       if (err < 0)
+               return err;
+
+       return __connman_iptables_commit("nat");
+}
+
+static void disable_nat(struct connman_nat *nat)
+{
+       int err;
+
+       if (nat->interface == NULL)
+               return;
+
+       /* Disable masquerading */
+       err = __connman_iptables_command("-t nat -D POSTROUTING "
+                                       "-s %s/%d -o %s -j MASQUERADE",
+                                       nat->address,
+                                       nat->prefixlen,
+                                       nat->interface);
+       if (err < 0)
+               return;
+
+       __connman_iptables_commit("nat");
+}
+
+int __connman_nat_enable(const char *name, const char *address,
+                               unsigned char prefixlen)
+{
+       struct connman_nat *nat;
+       int err;
+
+       if (g_hash_table_size(nat_hash) == 0) {
+               err = enable_ip_forward(TRUE);
+               if (err < 0)
+                       return err;
+       }
+
+       nat = g_try_new0(struct connman_nat, 1);
+       if (nat == NULL) {
+               if (g_hash_table_size(nat_hash) == 0)
+                       enable_ip_forward(FALSE);
+
+               return -ENOMEM;
+       }
+
+       nat->address = g_strdup(address);
+       nat->prefixlen = prefixlen;
+
+       g_hash_table_replace(nat_hash, g_strdup(name), nat);
+
+       return enable_nat(nat);
+}
+
+void __connman_nat_disable(const char *name)
+{
+       struct connman_nat *nat;
+
+       nat = g_hash_table_lookup(nat_hash, name);
+       if (nat == NULL)
+               return;
+
+       disable_nat(nat);
+
+       g_hash_table_remove(nat_hash, name);
+
+       if (g_hash_table_size(nat_hash) == 0)
+               enable_ip_forward(FALSE);
+}
+
+static void update_default_interface(struct connman_service *service)
+{
+       GHashTableIter iter;
+       gpointer key, value;
+       char *interface;
+       int err;
+
+       interface = connman_service_get_interface(service);
+
+       DBG("interface %s", interface);
+
+       g_free(default_interface);
+       default_interface = interface;
+
+       g_hash_table_iter_init(&iter, nat_hash);
+
+       while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
+               const char *name = key;
+               struct connman_nat *nat = value;
+
+               disable_nat(nat);
+               err = enable_nat(nat);
+               if (err < 0)
+                       DBG("Failed to enable nat for %s", name);
+       }
+}
+
+static void shutdown_nat(gpointer key, gpointer value, gpointer user_data)
+{
+       const char *name = key;
+
+       __connman_nat_disable(name);
+}
+
+static void cleanup_nat(gpointer data)
+{
+       struct connman_nat *nat = data;
+
+       g_free(nat->address);
+       g_free(nat->interface);
+}
+
+static struct connman_notifier nat_notifier = {
+       .name                   = "nat",
+       .default_changed        = update_default_interface,
+};
+
+int __connman_nat_init(void)
+{
+       int err;
+
+       DBG("");
+
+       err = connman_notifier_register(&nat_notifier);
+       if (err < 0)
+               return err;
+
+       nat_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                               g_free, cleanup_nat);
+
+       flush_nat();
+
+       return 0;
+}
+
+void __connman_nat_cleanup(void)
+{
+       DBG("");
+
+       g_hash_table_foreach(nat_hash, shutdown_nat, NULL);
+       g_hash_table_destroy(nat_hash);
+       nat_hash = NULL;
+
+       flush_nat();
+
+       connman_notifier_unregister(&nat_notifier);
+}
index 3b98372..61a9f8f 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
 
 #include "connman.h"
 
+/*
+ * How many times to send RS with the purpose of
+ * refreshing RDNSS entries before they actually expire.
+ * With a value of 1, one RS will be sent, with no retries.
+ */
+#define RS_REFRESH_COUNT       1
+
+/*
+ * Value in seconds to wait for RA after RS was sent.
+ * After this time elapsed, we can send another RS.
+ */
+#define RS_REFRESH_TIMEOUT     3
+
+#if defined TIZEN_EXT
+#define WIFI_ENCYPTION_MODE_LEN_MAX 6
+#define WIFI_BSSID_LEN_MAX 6
+#endif
+
 static GSList *network_list = NULL;
 static GSList *driver_list = NULL;
 
@@ -45,6 +63,8 @@ struct connman_network {
        char *group;
        char *path;
        int index;
+       int router_solicit_count;
+       int router_solicit_refresh_count;
 
        struct connman_network_driver *driver;
        void *driver_data;
@@ -74,16 +94,17 @@ struct connman_network {
                connman_bool_t use_wps;
                char *pin_wps;
 #if defined TIZEN_EXT
-               char encryption_mode[6];
-               unsigned char bssid[6];
+               char encryption_mode[WIFI_ENCYPTION_MODE_LEN_MAX];
+               unsigned char bssid[WIFI_BSSID_LEN_MAX];
                unsigned int maxrate;
+               unsigned int isHS20AP;
 #endif
        } wifi;
 
-       struct {
-               char *nsp_name;
-               int nsp_name_len;
-       } wimax;
+#if defined TIZEN_EXT
+       /* Multiple APN services and a default APN which a user selected */
+       connman_bool_t default_internet;
+#endif
 };
 
 static const char *type2string(enum connman_network_type type)
@@ -96,8 +117,6 @@ static const char *type2string(enum connman_network_type type)
                return "ethernet";
        case CONNMAN_NETWORK_TYPE_WIFI:
                return "wifi";
-       case CONNMAN_NETWORK_TYPE_WIMAX:
-               return "wimax";
        case CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN:
        case CONNMAN_NETWORK_TYPE_BLUETOOTH_DUN:
                return "bluetooth";
@@ -108,14 +127,6 @@ static const char *type2string(enum connman_network_type type)
        return NULL;
 }
 
-connman_bool_t __connman_network_has_driver(struct connman_network *network)
-{
-       if (network == NULL || network->driver == NULL)
-               return FALSE;
-
-       return TRUE;
-}
-
 static gboolean match_driver(struct connman_network *network,
                                        struct connman_network_driver *driver)
 {
@@ -126,1047 +137,1406 @@ static gboolean match_driver(struct connman_network *network,
        return FALSE;
 }
 
-static int network_probe(struct connman_network *network)
+static void set_configuration(struct connman_network *network,
+                       enum connman_ipconfig_type type)
 {
-       GSList *list;
-       struct connman_network_driver *driver = NULL;
-
-       DBG("network %p name %s", network, network->name);
-
-       if (network->driver != NULL)
-               return -EALREADY;
-
-       for (list = driver_list; list; list = list->next) {
-               driver = list->data;
-
-               if (match_driver(network, driver) == FALSE)
-                       continue;
-
-               DBG("driver %p name %s", driver, driver->name);
-
-               if (driver->probe(network) == 0)
-                       break;
+       struct connman_service *service;
 
-               driver = NULL;
-       }
+       DBG("network %p", network);
 
-       if (driver == NULL)
-               return -ENODEV;
+       if (network->device == NULL)
+               return;
 
-       if (network->group == NULL)
-               return -EINVAL;
+       __connman_device_set_network(network->device, network);
 
-       switch (network->type) {
-       case CONNMAN_NETWORK_TYPE_UNKNOWN:
-       case CONNMAN_NETWORK_TYPE_VENDOR:
-               return 0;
-       case CONNMAN_NETWORK_TYPE_ETHERNET:
-       case CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN:
-       case CONNMAN_NETWORK_TYPE_BLUETOOTH_DUN:
-       case CONNMAN_NETWORK_TYPE_CELLULAR:
-       case CONNMAN_NETWORK_TYPE_WIFI:
-       case CONNMAN_NETWORK_TYPE_WIMAX:
-               network->driver = driver;
-               if (__connman_service_create_from_network(network) == NULL) {
-                       network->driver = NULL;
-                       return -EINVAL;
-               }
-       }
+       connman_device_set_disconnected(network->device, FALSE);
 
-       return 0;
+       service = connman_service_lookup_from_network(network);
+       __connman_service_ipconfig_indicate_state(service,
+                                       CONNMAN_SERVICE_STATE_CONFIGURATION,
+                                       type);
 }
 
-static void network_remove(struct connman_network *network)
+static void dhcp_success(struct connman_network *network)
 {
-       DBG("network %p name %s", network, network->name);
+       struct connman_service *service;
+       struct connman_ipconfig *ipconfig_ipv4;
+       int err;
 
-       if (network->driver == NULL)
-               return;
+       service = connman_service_lookup_from_network(network);
+       if (service == NULL)
+               goto err;
 
-       connman_network_set_connected(network, FALSE);
+       connman_network_set_associating(network, FALSE);
 
-       switch (network->type) {
-       case CONNMAN_NETWORK_TYPE_UNKNOWN:
-       case CONNMAN_NETWORK_TYPE_VENDOR:
-               break;
-       case CONNMAN_NETWORK_TYPE_ETHERNET:
-       case CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN:
-       case CONNMAN_NETWORK_TYPE_BLUETOOTH_DUN:
-       case CONNMAN_NETWORK_TYPE_CELLULAR:
-       case CONNMAN_NETWORK_TYPE_WIFI:
-       case CONNMAN_NETWORK_TYPE_WIMAX:
-               if (network->group != NULL) {
-                       __connman_service_remove_from_network(network);
+       network->connecting = FALSE;
 
-                       g_free(network->group);
-                       network->group = NULL;
-               }
-               break;
-       }
+       ipconfig_ipv4 = __connman_service_get_ip4config(service);
+       err = __connman_ipconfig_address_add(ipconfig_ipv4);
+       if (err < 0)
+               goto err;
 
-       if (network->driver->remove)
-               network->driver->remove(network);
+#if defined TIZEN_EXT
+       err = __connman_ipconfig_gateway_add(ipconfig_ipv4, service);
+#else
+       err = __connman_ipconfig_gateway_add(ipconfig_ipv4);
+#endif
+       if (err < 0)
+               goto err;
 
-       network->driver = NULL;
+       return;
+
+err:
+       connman_network_set_error(network,
+                               CONNMAN_NETWORK_ERROR_CONFIGURE_FAIL);
 }
 
-static void network_change(struct connman_network *network)
+static void dhcp_failure(struct connman_network *network)
 {
-       DBG("network %p name %s", network, network->name);
+       struct connman_service *service;
 
-       if (network->connected == FALSE)
+       service = connman_service_lookup_from_network(network);
+       if (service == NULL)
                return;
 
-       connman_device_set_disconnected(network->device, TRUE);
+       __connman_service_ipconfig_indicate_state(service,
+                                       CONNMAN_SERVICE_STATE_IDLE,
+                                       CONNMAN_IPCONFIG_TYPE_IPV4);
+}
 
-       if (network->driver && network->driver->disconnect) {
-               network->driver->disconnect(network);
-               return;
-       }
+static void dhcp_callback(struct connman_network *network,
+                       connman_bool_t success)
+{
+       DBG("success %d", success);
 
-       network->connected = FALSE;
+       if (success == TRUE)
+               dhcp_success(network);
+       else
+               dhcp_failure(network);
 }
 
-static void probe_driver(struct connman_network_driver *driver)
+static int set_connected_fixed(struct connman_network *network)
 {
-       GSList *list;
+       struct connman_service *service;
+       struct connman_ipconfig *ipconfig_ipv4;
+       int err;
 
-       DBG("driver %p name %s", driver, driver->name);
+       DBG("");
 
-       for (list = network_list; list != NULL; list = list->next) {
-               struct connman_network *network = list->data;
+       service = connman_service_lookup_from_network(network);
 
-               if (network->driver != NULL)
-                       continue;
+       ipconfig_ipv4 = __connman_service_get_ip4config(service);
 
-               if (driver->type != network->type)
-                       continue;
+       set_configuration(network, CONNMAN_IPCONFIG_TYPE_IPV4);
 
-               if (driver->probe(network) < 0)
-                       continue;
+       network->connecting = FALSE;
 
-               network->driver = driver;
-       }
-}
+       connman_network_set_associating(network, FALSE);
 
-static void remove_driver(struct connman_network_driver *driver)
-{
-       GSList *list;
+       err = __connman_ipconfig_address_add(ipconfig_ipv4);
+       if (err < 0)
+               goto err;
 
-       DBG("driver %p name %s", driver, driver->name);
+#if defined TIZEN_EXT
+       err = __connman_ipconfig_gateway_add(ipconfig_ipv4, service);
+#else
+       err = __connman_ipconfig_gateway_add(ipconfig_ipv4);
+#endif
+       if (err < 0)
+               goto err;
 
-       for (list = network_list; list != NULL; list = list->next) {
-               struct connman_network *network = list->data;
+       return 0;
 
-               if (network->driver == driver)
-                       network_remove(network);
-       }
+err:
+       connman_network_set_error(network,
+                       CONNMAN_NETWORK_ERROR_CONFIGURE_FAIL);
+
+       return err;
 }
 
-static gint compare_priority(gconstpointer a, gconstpointer b)
+static void set_connected_manual(struct connman_network *network)
 {
-       const struct connman_network_driver *driver1 = a;
-       const struct connman_network_driver *driver2 = b;
+       struct connman_service *service;
+       struct connman_ipconfig *ipconfig;
+       int err;
 
-       return driver2->priority - driver1->priority;
-}
+       DBG("network %p", network);
 
-/**
- * connman_network_driver_register:
- * @driver: network driver definition
- *
- * Register a new network driver
- *
- * Returns: %0 on success
- */
-int connman_network_driver_register(struct connman_network_driver *driver)
-{
-       GSList *list;
+       service = connman_service_lookup_from_network(network);
 
-       DBG("driver %p name %s", driver, driver->name);
+       ipconfig = __connman_service_get_ip4config(service);
 
-       for (list = driver_list; list; list = list->next) {
-               struct connman_network_driver *tmp = list->data;
+       if (__connman_ipconfig_get_local(ipconfig) == NULL)
+               __connman_service_read_ip4config(service);
 
-               if (tmp->type == driver->type)
-                       return -EALREADY;
+       set_configuration(network, CONNMAN_IPCONFIG_TYPE_IPV4);
 
-       }
+       err = __connman_ipconfig_address_add(ipconfig);
+       if (err < 0)
+               goto err;
 
-       driver_list = g_slist_insert_sorted(driver_list, driver,
-                                                       compare_priority);
+#if defined TIZEN_EXT
+       err = __connman_ipconfig_gateway_add(ipconfig, service);
+#else
+       err = __connman_ipconfig_gateway_add(ipconfig);
+#endif
+       if (err < 0)
+               goto err;
 
-       probe_driver(driver);
+       network->connecting = FALSE;
 
-       return 0;
+       connman_network_set_associating(network, FALSE);
+
+       return;
+
+err:
+       connman_network_set_error(network,
+                                       CONNMAN_NETWORK_ERROR_CONFIGURE_FAIL);
+       return;
 }
 
-/**
- * connman_network_driver_unregister:
- * @driver: network driver definition
- *
- * Remove a previously registered network driver
- */
-void connman_network_driver_unregister(struct connman_network_driver *driver)
+static int set_connected_dhcp(struct connman_network *network)
 {
-       DBG("driver %p name %s", driver, driver->name);
+       int err;
 
-       driver_list = g_slist_remove(driver_list, driver);
+       DBG("network %p", network);
 
-       remove_driver(driver);
-}
+       set_configuration(network, CONNMAN_IPCONFIG_TYPE_IPV4);
 
-static void network_destruct(struct connman_network *network)
-{
-       DBG("network %p name %s", network, network->name);
+       err = __connman_dhcp_start(network, dhcp_callback);
+       if (err < 0) {
+               connman_error("Can not request DHCP lease");
+               return err;
+       }
 
-       g_free(network->wifi.ssid);
-       g_free(network->wifi.mode);
-       g_free(network->wifi.security);
-       g_free(network->wifi.passphrase);
-       g_free(network->wifi.agent_passphrase);
-       g_free(network->wifi.eap);
-       g_free(network->wifi.identity);
-       g_free(network->wifi.agent_identity);
-       g_free(network->wifi.ca_cert_path);
-       g_free(network->wifi.client_cert_path);
-       g_free(network->wifi.private_key_path);
-       g_free(network->wifi.private_key_passphrase);
-       g_free(network->wifi.phase2_auth);
-       g_free(network->wifi.pin_wps);
-
-       g_free(network->path);
-       g_free(network->group);
-       g_free(network->node);
-       g_free(network->name);
-       g_free(network->identifier);
-
-       network->device = NULL;
-
-       g_free(network);
+       return 0;
 }
 
-/**
- * connman_network_create:
- * @identifier: network identifier (for example an unqiue name)
- *
- * Allocate a new network and assign the #identifier to it.
- *
- * Returns: a newly-allocated #connman_network structure
- */
-struct connman_network *connman_network_create(const char *identifier,
-                                               enum connman_network_type type)
+static int manual_ipv6_set(struct connman_network *network,
+                               struct connman_ipconfig *ipconfig_ipv6)
 {
-       struct connman_network *network;
-       char *ident;
-
-       DBG("identifier %s type %d", identifier, type);
+       struct connman_service *service;
+       int err;
 
-       network = g_try_new0(struct connman_network, 1);
-       if (network == NULL)
-               return NULL;
+       DBG("network %p ipv6 %p", network, ipconfig_ipv6);
 
-       DBG("network %p", network);
+       service = connman_service_lookup_from_network(network);
+       if (service == NULL)
+               return -EINVAL;
 
-       network->refcount = 1;
+       if (__connman_ipconfig_get_local(ipconfig_ipv6) == NULL)
+               __connman_service_read_ip6config(service);
 
-       ident = g_strdup(identifier);
+       __connman_ipconfig_enable_ipv6(ipconfig_ipv6);
 
-       if (ident == NULL) {
-               g_free(network);
-               return NULL;
+       err = __connman_ipconfig_address_add(ipconfig_ipv6);
+       if (err < 0) {
+               connman_network_set_error(network,
+                       CONNMAN_NETWORK_ERROR_CONFIGURE_FAIL);
+               return err;
        }
 
-       network->type       = type;
-       network->identifier = ident;
+#if defined TIZEN_EXT
+       err = __connman_ipconfig_gateway_add(ipconfig_ipv6, service);
+#else
+       err = __connman_ipconfig_gateway_add(ipconfig_ipv6);
+#endif
+       if (err < 0)
+               return err;
 
-       network_list = g_slist_append(network_list, network);
+       __connman_connection_gateway_activate(service,
+                                               CONNMAN_IPCONFIG_TYPE_IPV6);
 
-       return network;
-}
+       __connman_device_set_network(network->device, network);
 
-/**
- * connman_network_ref:
- * @network: network structure
- *
- * Increase reference counter of  network
- */
-struct connman_network *connman_network_ref(struct connman_network *network)
-{
-       DBG("network %p name %s refcount %d", network, network->name,
-               network->refcount + 1);
+       connman_device_set_disconnected(network->device, FALSE);
 
-       __sync_fetch_and_add(&network->refcount, 1);
+       network->connecting = FALSE;
 
-       return network;
+       return 0;
 }
 
-/**
- * connman_network_unref:
- * @network: network structure
- *
- * Decrease reference counter of network
- */
-void connman_network_unref(struct connman_network *network)
+static void stop_dhcpv6(struct connman_network *network)
 {
-       DBG("network %p name %s refcount %d", network, network->name,
-               network->refcount - 1);
-
-       if (__sync_fetch_and_sub(&network->refcount, 1) != 1)
-               return;
-
-       network_list = g_slist_remove(network_list, network);
-
-       network_destruct(network);
+       __connman_dhcpv6_stop(network);
 }
 
-const char *__connman_network_get_type(struct connman_network *network)
+static void dhcpv6_release_callback(struct connman_network *network,
+                               connman_bool_t success)
 {
-       return type2string(network->type);
+       DBG("success %d", success);
+
+       stop_dhcpv6(network);
 }
 
-/**
- * connman_network_get_type:
- * @network: network structure
- *
- * Get type of network
- */
-enum connman_network_type connman_network_get_type(struct connman_network *network)
+static void release_dhcpv6(struct connman_network *network)
 {
-       return network->type;
+       __connman_dhcpv6_start_release(network, dhcpv6_release_callback);
+       stop_dhcpv6(network);
 }
 
-/**
- * connman_network_get_identifier:
- * @network: network structure
- *
- * Get identifier of network
- */
-const char *connman_network_get_identifier(struct connman_network *network)
+static void dhcpv6_info_callback(struct connman_network *network,
+                               connman_bool_t success)
 {
-       return network->identifier;
+       DBG("success %d", success);
+
+       stop_dhcpv6(network);
 }
 
-/**
- * connman_network_set_index:
- * @network: network structure
- * @index: index number
- *
- * Set index number of network
- */
-void connman_network_set_index(struct connman_network *network, int index)
+static gboolean dhcpv6_set_addresses(struct connman_network *network)
 {
        struct connman_service *service;
-       struct connman_ipconfig *ipconfig;
+       struct connman_ipconfig *ipconfig_ipv6;
+       int err = -EINVAL;
 
-       service = __connman_service_lookup_from_network(network);
+       service = connman_service_lookup_from_network(network);
        if (service == NULL)
-               goto done;
-
-       ipconfig = __connman_service_get_ip4config(service);
+               goto err;
 
-       DBG("index %d service %p ip4config %p", network->index,
-               service, ipconfig);
+       connman_network_set_associating(network, FALSE);
 
-       if (network->index < 0 && ipconfig == NULL) {
+       network->connecting = FALSE;
 
-               ipconfig = __connman_service_get_ip4config(service);
-               if (ipconfig == NULL)
-                       /*
-                        * This is needed for plugins that havent set their
-                        * ipconfig layer yet, due to not being able to get
-                        * a network index prior to creating a service.
-                        */
-                       __connman_service_create_ip4config(service, index);
-               else
-                       __connman_ipconfig_set_index(ipconfig, index);
+       ipconfig_ipv6 = __connman_service_get_ip6config(service);
+       err = __connman_ipconfig_address_add(ipconfig_ipv6);
+       if (err < 0)
+               goto err;
 
-       } else {
-               /* If index changed, the index of ipconfig must be reset. */
-               if (ipconfig == NULL)
-                       goto done;
+#if defined TIZEN_EXT
+       err = __connman_ipconfig_gateway_add(ipconfig_ipv6, service);
+#else
+       err = __connman_ipconfig_gateway_add(ipconfig_ipv6);
+#endif
+       if (err < 0)
+               goto err;
 
-               __connman_ipconfig_set_index(ipconfig, index);
-       }
+       return 0;
 
-done:
-       network->index = index;
+err:
+       connman_network_set_error(network,
+                               CONNMAN_NETWORK_ERROR_CONFIGURE_FAIL);
+       return err;
 }
 
-/**
- * connman_network_get_index:
- * @network: network structure
- *
- * Get index number of network
+static void autoconf_ipv6_set(struct connman_network *network);
+static void dhcpv6_callback(struct connman_network *network,
+                       connman_bool_t success);
+
+/*
+ * Have a separate callback for renew so that we do not do autoconf
+ * in wrong phase as the dhcpv6_callback() is also called when doing
+ * DHCPv6 solicitation.
  */
-int connman_network_get_index(struct connman_network *network)
+static void dhcpv6_renew_callback(struct connman_network *network,
+                                       connman_bool_t success)
 {
-       return network->index;
+       if (success == TRUE)
+               dhcpv6_callback(network, success);
+       else {
+               stop_dhcpv6(network);
+
+               /* restart and do solicit again. */
+               autoconf_ipv6_set(network);
+       }
 }
 
-/**
- * connman_network_set_group:
- * @network: network structure
- * @group: group name
- *
- * Set group name for automatic clustering
- */
-void connman_network_set_group(struct connman_network *network,
-                                                       const char *group)
+static void dhcpv6_callback(struct connman_network *network,
+                                       connman_bool_t success)
 {
-       switch (network->type) {
-       case CONNMAN_NETWORK_TYPE_UNKNOWN:
-       case CONNMAN_NETWORK_TYPE_VENDOR:
-               return;
-       case CONNMAN_NETWORK_TYPE_ETHERNET:
-       case CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN:
-       case CONNMAN_NETWORK_TYPE_BLUETOOTH_DUN:
-       case CONNMAN_NETWORK_TYPE_CELLULAR:
-       case CONNMAN_NETWORK_TYPE_WIFI:
-       case CONNMAN_NETWORK_TYPE_WIMAX:
-               break;
-       }
+       DBG("success %d", success);
 
-       if (g_strcmp0(network->group, group) == 0) {
-               if (group != NULL)
-                       __connman_service_update_from_network(network);
+       /* Start the renew process if necessary */
+       if (success == TRUE) {
+
+               if (dhcpv6_set_addresses(network) < 0) {
+                       stop_dhcpv6(network);
+                       return;
+               }
+
+               if (__connman_dhcpv6_start_renew(network,
+                                       dhcpv6_renew_callback) == -ETIMEDOUT)
+                       dhcpv6_renew_callback(network, FALSE);
+       } else
+               stop_dhcpv6(network);
+}
+
+static void check_dhcpv6(struct nd_router_advert *reply,
+                       unsigned int length, void *user_data)
+{
+       struct connman_network *network = user_data;
+       GSList *prefixes;
+
+       DBG("reply %p", reply);
+
+       if (reply == NULL) {
+               /*
+                * Router solicitation message seem to get lost easily so
+                * try to send it again.
+                */
+               if (network->router_solicit_count > 0) {
+                       DBG("re-send router solicitation %d",
+                                               network->router_solicit_count);
+                       network->router_solicit_count--;
+                       __connman_inet_ipv6_send_rs(network->index, 1,
+                                               check_dhcpv6, network);
+                       return;
+               }
+               connman_network_unref(network);
                return;
        }
 
-       if (network->group != NULL) {
-               __connman_service_remove_from_network(network);
+       network->router_solicit_count = 0;
 
-               g_free(network->group);
+       /*
+        * If we were disconnected while waiting router advertisement,
+        * we just quit and do not start DHCPv6
+        */
+       if (network->connected == FALSE) {
+               connman_network_unref(network);
+               return;
        }
 
-       network->group = g_strdup(group);
+       prefixes = __connman_inet_ipv6_get_prefixes(reply, length);
 
-       if (network->group != NULL)
-               network_probe(network);
-}
+       /*
+        * We do stateful/stateless DHCPv6 if router advertisement says so.
+        */
+       if (reply->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED)
+               __connman_dhcpv6_start(network, prefixes, dhcpv6_callback);
+       else if (reply->nd_ra_flags_reserved & ND_RA_FLAG_OTHER)
+               __connman_dhcpv6_start_info(network, dhcpv6_info_callback);
 
-/**
- * connman_network_get_group:
- * @network: network structure
- *
- * Get group name for automatic clustering
- */
-const char *connman_network_get_group(struct connman_network *network)
-{
-       return network->group;
+       connman_network_unref(network);
 }
 
-const char *__connman_network_get_ident(struct connman_network *network)
+static void receive_refresh_rs_reply(struct nd_router_advert *reply,
+               unsigned int length, void *user_data)
 {
-       if (network->device == NULL)
-               return NULL;
+       struct connman_network *network = user_data;
 
-       return connman_device_get_ident(network->device);
-}
+       DBG("reply %p", reply);
 
-connman_bool_t __connman_network_get_weakness(struct connman_network *network)
-{
-       switch (network->type) {
-       case CONNMAN_NETWORK_TYPE_UNKNOWN:
-       case CONNMAN_NETWORK_TYPE_VENDOR:
-       case CONNMAN_NETWORK_TYPE_ETHERNET:
-       case CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN:
-       case CONNMAN_NETWORK_TYPE_BLUETOOTH_DUN:
-       case CONNMAN_NETWORK_TYPE_CELLULAR:
-       case CONNMAN_NETWORK_TYPE_WIMAX:
-               break;
-       case CONNMAN_NETWORK_TYPE_WIFI:
-               if (g_strcmp0(network->wifi.mode, "adhoc") == 0)
-                       return TRUE;
-               if (network->strength > 0 && network->strength < 20)
-                       return TRUE;
-               break;
+       if (reply == NULL) {
+               /*
+                * Router solicitation message seem to get lost easily so
+                * try to send it again.
+                */
+               if (network->router_solicit_refresh_count > 1) {
+                       network->router_solicit_refresh_count--;
+                       DBG("re-send router solicitation %d",
+                                       network->router_solicit_refresh_count);
+                       __connman_inet_ipv6_send_rs(network->index,
+                                       RS_REFRESH_TIMEOUT,
+                                       receive_refresh_rs_reply,
+                                       network);
+                       return;
+               }
        }
 
-       return FALSE;
+       /* RS refresh not in progress anymore */
+       network->router_solicit_refresh_count = 0;
+
+       connman_network_unref(network);
+       return;
 }
 
-connman_bool_t connman_network_get_connecting(struct connman_network *network)
+int __connman_refresh_rs_ipv6(struct connman_network *network, int index)
 {
-       return network->connecting;
-}
+       int ret = 0;
 
-/**
- * connman_network_set_available:
- * @network: network structure
- * @available: availability state
- *
- * Change availability state of network (in range)
- */
-int connman_network_set_available(struct connman_network *network,
-                                               connman_bool_t available)
-{
-       DBG("network %p available %d", network, available);
+       DBG("network %p index %d", network, index);
 
-       if (network->available == available)
-               return -EALREADY;
+       /* Send only one RS for all RDNSS entries which are about to expire */
+       if (network->router_solicit_refresh_count > 0) {
+               DBG("RS refresh already started");
+               return 0;
+       }
 
-       network->available = available;
+       network->router_solicit_refresh_count = RS_REFRESH_COUNT;
 
-       return 0;
-}
+       connman_network_ref(network);
 
-/**
- * connman_network_get_available:
- * @network: network structure
- *
- * Get network available setting
- */
-connman_bool_t connman_network_get_available(struct connman_network *network)
-{
-       return network->available;
+       ret = __connman_inet_ipv6_send_rs(index, RS_REFRESH_TIMEOUT,
+                       receive_refresh_rs_reply, network);
+       return ret;
 }
 
-/**
- * connman_network_set_associating:
- * @network: network structure
- * @associating: associating state
- *
- * Change associating state of network
- */
-int connman_network_set_associating(struct connman_network *network,
-                                               connman_bool_t associating)
+static void autoconf_ipv6_set(struct connman_network *network)
 {
-       DBG("network %p associating %d", network, associating);
+       struct connman_service *service;
+       struct connman_ipconfig *ipconfig;
+       int index;
 
-       if (network->associating == associating)
-               return -EALREADY;
+       DBG("network %p", network);
 
-       network->associating = associating;
+       if (network->router_solicit_count > 0) {
+               /*
+                * The autoconfiguration is already pending and we have sent
+                * router solicitation messages and are now waiting answers.
+                * There is no need to continue any further.
+                */
+               DBG("autoconfiguration already started");
+               return;
+       }
 
-       if (associating == TRUE) {
-               struct connman_service *service;
+       __connman_device_set_network(network->device, network);
 
-               service = __connman_service_lookup_from_network(network);
-               __connman_service_ipconfig_indicate_state(service,
-                                       CONNMAN_SERVICE_STATE_ASSOCIATION,
-                                       CONNMAN_IPCONFIG_TYPE_IPV4);
-               __connman_service_ipconfig_indicate_state(service,
-                                       CONNMAN_SERVICE_STATE_ASSOCIATION,
-                                       CONNMAN_IPCONFIG_TYPE_IPV6);
-       }
+       connman_device_set_disconnected(network->device, FALSE);
 
-       return 0;
-}
+       network->connecting = FALSE;
 
-static void set_associate_error(struct connman_network *network)
-{
-       struct connman_service *service;
+#if defined TIZEN_EXT
+       if(network->type == CONNMAN_NETWORK_TYPE_CELLULAR)
+               return;
+#endif
 
-       service = __connman_service_lookup_from_network(network);
+       service = connman_service_lookup_from_network(network);
+       if (service == NULL)
+               return;
 
-       __connman_service_indicate_error(service,
-                                       CONNMAN_SERVICE_ERROR_CONNECT_FAILED);
-}
+       ipconfig = __connman_service_get_ip6config(service);
+       if (ipconfig == NULL)
+               return;
 
-static void set_configure_error(struct connman_network *network)
-{
-       struct connman_service *service;
+       index = __connman_ipconfig_get_index(ipconfig);
 
-       service = __connman_service_lookup_from_network(network);
+       connman_network_ref(network);
 
-       __connman_service_indicate_error(service,
-                                       CONNMAN_SERVICE_ERROR_CONNECT_FAILED);
+       /* Try to get stateless DHCPv6 information, RFC 3736 */
+       network->router_solicit_count = 3;
+       __connman_inet_ipv6_send_rs(index, 1, check_dhcpv6, network);
 }
 
-static void set_invalid_key_error(struct connman_network *network)
+static void set_connected(struct connman_network *network)
 {
+       struct connman_ipconfig *ipconfig_ipv4, *ipconfig_ipv6;
+       enum connman_ipconfig_method ipv4_method, ipv6_method;
        struct connman_service *service;
+       int ret;
 
-       service = __connman_service_lookup_from_network(network);
+       if (network->connected == TRUE)
+               return;
 
-       __connman_service_indicate_error(service,
-                                       CONNMAN_SERVICE_ERROR_INVALID_KEY);
-}
+       network->connected = TRUE;
 
-static void set_connect_error(struct connman_network *network)
-{
+       service = connman_service_lookup_from_network(network);
+
+       ipconfig_ipv4 = __connman_service_get_ip4config(service);
+       ipconfig_ipv6 = __connman_service_get_ip6config(service);
 
+       DBG("service %p ipv4 %p ipv6 %p", service, ipconfig_ipv4,
+               ipconfig_ipv6);
+
+       ipv4_method = __connman_ipconfig_get_method(ipconfig_ipv4);
+       ipv6_method = __connman_ipconfig_get_method(ipconfig_ipv6);
+
+       DBG("method ipv4 %d ipv6 %d", ipv4_method, ipv6_method);
+
+       switch (ipv6_method) {
+       case CONNMAN_IPCONFIG_METHOD_UNKNOWN:
+       case CONNMAN_IPCONFIG_METHOD_OFF:
+               break;
+       case CONNMAN_IPCONFIG_METHOD_DHCP:
+       case CONNMAN_IPCONFIG_METHOD_AUTO:
 #if defined TIZEN_EXT
-       if (network->associating != FALSE)
-               network->associating = FALSE;
+               if(network->type == CONNMAN_NETWORK_TYPE_CELLULAR)
+                       __connman_service_ipconfig_indicate_state(service,
+                               CONNMAN_SERVICE_STATE_CONFIGURATION,
+                                       CONNMAN_IPCONFIG_TYPE_IPV6);
 #endif
+               autoconf_ipv6_set(network);
+               break;
+       case CONNMAN_IPCONFIG_METHOD_FIXED:
+       case CONNMAN_IPCONFIG_METHOD_MANUAL:
+               ret = manual_ipv6_set(network, ipconfig_ipv6);
+               if (ret != 0) {
+                       connman_network_set_error(network,
+                                       CONNMAN_NETWORK_ERROR_ASSOCIATE_FAIL);
+                       return;
+               }
+               break;
+       }
 
-       struct connman_service *service;
+       switch (ipv4_method) {
+       case CONNMAN_IPCONFIG_METHOD_UNKNOWN:
+       case CONNMAN_IPCONFIG_METHOD_OFF:
+       case CONNMAN_IPCONFIG_METHOD_AUTO:
+               return;
+       case CONNMAN_IPCONFIG_METHOD_FIXED:
+               if (set_connected_fixed(network) < 0) {
+                       connman_network_set_error(network,
+                                       CONNMAN_NETWORK_ERROR_ASSOCIATE_FAIL);
+                       return;
+               }
+               return;
+       case CONNMAN_IPCONFIG_METHOD_MANUAL:
+               set_connected_manual(network);
+               return;
+       case CONNMAN_IPCONFIG_METHOD_DHCP:
+               if (set_connected_dhcp(network) < 0) {
+                       connman_network_set_error(network,
+                                       CONNMAN_NETWORK_ERROR_ASSOCIATE_FAIL);
+                       return;
+               }
+       }
 
-       service = __connman_service_lookup_from_network(network);
+       network->connecting = FALSE;
 
-       __connman_service_indicate_error(service,
-                                       CONNMAN_SERVICE_ERROR_CONNECT_FAILED);
+       connman_network_set_associating(network, FALSE);
 }
 
-void connman_network_set_ipv4_method(struct connman_network *network,
-                                       enum connman_ipconfig_method method)
+static void set_disconnected(struct connman_network *network)
 {
+       struct connman_ipconfig *ipconfig_ipv4, *ipconfig_ipv6;
+       enum connman_ipconfig_method ipv4_method, ipv6_method;
+       enum connman_service_state state;
        struct connman_service *service;
-       struct connman_ipconfig *ipconfig;
 
-       service = __connman_service_lookup_from_network(network);
-       if (service == NULL)
+       if (network->connected == FALSE)
                return;
 
-       ipconfig = __connman_service_get_ip4config(service);
-       if (ipconfig == NULL)
-               return;
+       network->connected = FALSE;
 
-       connman_ipconfig_set_method(ipconfig, method);
-}
+       service = connman_service_lookup_from_network(network);
 
-void connman_network_set_ipv6_method(struct connman_network *network,
-                                       enum connman_ipconfig_method method)
-{
-       struct connman_service *service;
-       struct connman_ipconfig *ipconfig;
+       ipconfig_ipv4 = __connman_service_get_ip4config(service);
+       ipconfig_ipv6 = __connman_service_get_ip6config(service);
 
-       service = __connman_service_lookup_from_network(network);
-       if (service == NULL)
-               return;
+       DBG("service %p ipv4 %p ipv6 %p", service, ipconfig_ipv4,
+               ipconfig_ipv6);
 
-       ipconfig = __connman_service_get_ip6config(service);
-       if (ipconfig == NULL)
-               return;
+       ipv4_method = __connman_ipconfig_get_method(ipconfig_ipv4);
+       ipv6_method = __connman_ipconfig_get_method(ipconfig_ipv6);
 
-       connman_ipconfig_set_method(ipconfig, method);
-}
+       DBG("method ipv4 %d ipv6 %d", ipv4_method, ipv6_method);
 
-void connman_network_set_error(struct connman_network *network,
-                                       enum connman_network_error error)
-{
-       DBG("nework %p, error %d", network, error);
+       /*
+        * Resetting solicit count here will prevent the RS resend loop
+        * from sending packets in check_dhcpv6()
+        */
+       network->router_solicit_count = 0;
 
-       network->connecting = FALSE;
-       network->associating = FALSE;
+       __connman_device_set_network(network->device, NULL);
 
-       switch (error) {
-       case CONNMAN_NETWORK_ERROR_UNKNOWN:
-               return;
-       case CONNMAN_NETWORK_ERROR_ASSOCIATE_FAIL:
-               set_associate_error(network);
+       switch (ipv6_method) {
+       case CONNMAN_IPCONFIG_METHOD_UNKNOWN:
+       case CONNMAN_IPCONFIG_METHOD_OFF:
+       case CONNMAN_IPCONFIG_METHOD_FIXED:
+       case CONNMAN_IPCONFIG_METHOD_MANUAL:
                break;
-       case CONNMAN_NETWORK_ERROR_CONFIGURE_FAIL:
-               set_configure_error(network);
+       case CONNMAN_IPCONFIG_METHOD_DHCP:
+       case CONNMAN_IPCONFIG_METHOD_AUTO:
+               release_dhcpv6(network);
                break;
-       case CONNMAN_NETWORK_ERROR_INVALID_KEY:
-               set_invalid_key_error(network);
+       }
+
+       switch (ipv4_method) {
+       case CONNMAN_IPCONFIG_METHOD_UNKNOWN:
+       case CONNMAN_IPCONFIG_METHOD_OFF:
+       case CONNMAN_IPCONFIG_METHOD_AUTO:
+       case CONNMAN_IPCONFIG_METHOD_FIXED:
+       case CONNMAN_IPCONFIG_METHOD_MANUAL:
                break;
-       case CONNMAN_NETWORK_ERROR_CONNECT_FAIL:
-               set_connect_error(network);
+       case CONNMAN_IPCONFIG_METHOD_DHCP:
+               __connman_dhcp_stop(network);
                break;
        }
 
-       network_change(network);
-}
-
-void connman_network_clear_error(struct connman_network *network)
-{
-       struct connman_service *service;
+       /*
+        * We only set the disconnect state if we were not in idle
+        * or in failure. It does not make sense to go to disconnect
+        * state if we were not connected.
+        */
+       state = __connman_service_ipconfig_get_state(service,
+                                               CONNMAN_IPCONFIG_TYPE_IPV4);
+       if (state != CONNMAN_SERVICE_STATE_IDLE &&
+                       state != CONNMAN_SERVICE_STATE_FAILURE)
+               __connman_service_ipconfig_indicate_state(service,
+                                       CONNMAN_SERVICE_STATE_DISCONNECT,
+                                       CONNMAN_IPCONFIG_TYPE_IPV4);
 
-       DBG("network %p", network);
+       state = __connman_service_ipconfig_get_state(service,
+                                               CONNMAN_IPCONFIG_TYPE_IPV6);
+       if (state != CONNMAN_SERVICE_STATE_IDLE &&
+                               state != CONNMAN_SERVICE_STATE_FAILURE)
+               __connman_service_ipconfig_indicate_state(service,
+                                       CONNMAN_SERVICE_STATE_DISCONNECT,
+                                       CONNMAN_IPCONFIG_TYPE_IPV6);
 
-       if (network == NULL)
-               return;
+       __connman_connection_gateway_remove(service,
+                                       CONNMAN_IPCONFIG_TYPE_ALL);
 
-       if (network->connecting == TRUE || network->associating == TRUE)
-               return;
+#if defined TIZEN_EXT
+       /**
+         * Skip the functions if there is any connected profiles
+         * that use same interface
+         */
+       if (connman_service_get_type(service) != CONNMAN_SERVICE_TYPE_CELLULAR
+               || __connman_service_get_connected_count_of_iface(service)
+                                                                <= 0) {
+#endif
+       __connman_ipconfig_address_unset(ipconfig_ipv4);
+       __connman_ipconfig_address_unset(ipconfig_ipv6);
+#if defined TIZEN_EXT
+       }
+#endif
 
-       service = __connman_service_lookup_from_network(network);
-       __connman_service_clear_error(service);
-}
+       /*
+        * Special handling for IPv6 autoconfigured address.
+        * The simplest way to remove autoconfigured routes is to
+        * disable IPv6 temporarily so that kernel will do the cleanup
+        * automagically.
+        */
+       if (ipv6_method == CONNMAN_IPCONFIG_METHOD_AUTO) {
+               __connman_ipconfig_disable_ipv6(ipconfig_ipv6);
+               __connman_ipconfig_enable_ipv6(ipconfig_ipv6);
+       }
 
-static void set_configuration(struct connman_network *network)
-{
-       struct connman_service *service;
+       __connman_service_ipconfig_indicate_state(service,
+                                               CONNMAN_SERVICE_STATE_IDLE,
+                                               CONNMAN_IPCONFIG_TYPE_IPV4);
 
-       DBG("network %p", network);
+       __connman_service_ipconfig_indicate_state(service,
+                                               CONNMAN_SERVICE_STATE_IDLE,
+                                               CONNMAN_IPCONFIG_TYPE_IPV6);
 
-       if (network->device == NULL)
-               return;
+       network->connecting = FALSE;
 
-       __connman_device_set_network(network->device, network);
+       connman_network_set_associating(network, FALSE);
+}
 
-       connman_device_set_disconnected(network->device, FALSE);
 
-       service = __connman_service_lookup_from_network(network);
-       __connman_service_ipconfig_indicate_state(service,
-                                       CONNMAN_SERVICE_STATE_CONFIGURATION,
-                                       CONNMAN_IPCONFIG_TYPE_IPV4);
-}
 
-static void dhcp_success(struct connman_network *network)
+static int network_probe(struct connman_network *network)
 {
-       struct connman_service *service;
-       struct connman_ipconfig *ipconfig_ipv4;
-       int err;
+       GSList *list;
+       struct connman_network_driver *driver = NULL;
 
-       service = __connman_service_lookup_from_network(network);
-       if (service == NULL)
-               goto err;
+       DBG("network %p name %s", network, network->name);
 
-       connman_network_set_associating(network, FALSE);
+       if (network->driver != NULL)
+               return -EALREADY;
+
+       for (list = driver_list; list; list = list->next) {
+               driver = list->data;
+
+               if (match_driver(network, driver) == FALSE) {
+                       driver = NULL;
+                       continue;
+               }
+
+               DBG("driver %p name %s", driver, driver->name);
+
+               if (driver->probe(network) == 0)
+                       break;
+
+               driver = NULL;
+       }
+
+       if (driver == NULL)
+               return -ENODEV;
+
+       if (network->group == NULL)
+               return -EINVAL;
+
+       switch (network->type) {
+       case CONNMAN_NETWORK_TYPE_UNKNOWN:
+       case CONNMAN_NETWORK_TYPE_VENDOR:
+               return 0;
+       case CONNMAN_NETWORK_TYPE_ETHERNET:
+       case CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN:
+       case CONNMAN_NETWORK_TYPE_BLUETOOTH_DUN:
+       case CONNMAN_NETWORK_TYPE_CELLULAR:
+       case CONNMAN_NETWORK_TYPE_WIFI:
+               network->driver = driver;
+               if (__connman_service_create_from_network(network) == NULL) {
+                       network->driver = NULL;
+                       return -EINVAL;
+               }
+       }
+
+       return 0;
+}
+
+static void network_remove(struct connman_network *network)
+{
+       DBG("network %p name %s", network, network->name);
+
+       if (network->driver == NULL)
+               return;
+
+       if (network->connected == TRUE)
+               set_disconnected(network);
+
+       switch (network->type) {
+       case CONNMAN_NETWORK_TYPE_UNKNOWN:
+       case CONNMAN_NETWORK_TYPE_VENDOR:
+               break;
+       case CONNMAN_NETWORK_TYPE_ETHERNET:
+       case CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN:
+       case CONNMAN_NETWORK_TYPE_BLUETOOTH_DUN:
+       case CONNMAN_NETWORK_TYPE_CELLULAR:
+       case CONNMAN_NETWORK_TYPE_WIFI:
+               if (network->group != NULL) {
+                       __connman_service_remove_from_network(network);
+
+                       g_free(network->group);
+                       network->group = NULL;
+               }
+               break;
+       }
+
+       if (network->driver->remove)
+               network->driver->remove(network);
+
+       network->driver = NULL;
+}
+
+static void network_change(struct connman_network *network)
+{
+       DBG("network %p name %s", network, network->name);
+
+       if (network->connected == FALSE)
+               return;
+
+       connman_device_set_disconnected(network->device, TRUE);
+
+       if (network->driver && network->driver->disconnect) {
+               network->driver->disconnect(network);
+               return;
+       }
+
+       network->connected = FALSE;
+}
+
+static void probe_driver(struct connman_network_driver *driver)
+{
+       GSList *list;
+
+       DBG("driver %p name %s", driver, driver->name);
+
+       for (list = network_list; list != NULL; list = list->next) {
+               struct connman_network *network = list->data;
+
+               if (network->driver != NULL)
+                       continue;
+
+               if (driver->type != network->type)
+                       continue;
+
+               if (driver->probe(network) < 0)
+                       continue;
+
+               network->driver = driver;
+       }
+}
+
+static void remove_driver(struct connman_network_driver *driver)
+{
+       GSList *list;
+
+       DBG("driver %p name %s", driver, driver->name);
+
+       for (list = network_list; list != NULL; list = list->next) {
+               struct connman_network *network = list->data;
+
+               if (network->driver == driver)
+                       network_remove(network);
+       }
+}
+
+static gint compare_priority(gconstpointer a, gconstpointer b)
+{
+       const struct connman_network_driver *driver1 = a;
+       const struct connman_network_driver *driver2 = b;
+
+       return driver2->priority - driver1->priority;
+}
+
+/**
+ * connman_network_driver_register:
+ * @driver: network driver definition
+ *
+ * Register a new network driver
+ *
+ * Returns: %0 on success
+ */
+int connman_network_driver_register(struct connman_network_driver *driver)
+{
+       DBG("driver %p name %s", driver, driver->name);
+
+       driver_list = g_slist_insert_sorted(driver_list, driver,
+                                                       compare_priority);
+
+       probe_driver(driver);
+
+       return 0;
+}
+
+/**
+ * connman_network_driver_unregister:
+ * @driver: network driver definition
+ *
+ * Remove a previously registered network driver
+ */
+void connman_network_driver_unregister(struct connman_network_driver *driver)
+{
+       DBG("driver %p name %s", driver, driver->name);
+
+       driver_list = g_slist_remove(driver_list, driver);
+
+       remove_driver(driver);
+}
+
+static void network_destruct(struct connman_network *network)
+{
+       DBG("network %p name %s", network, network->name);
+
+       g_free(network->wifi.ssid);
+       g_free(network->wifi.mode);
+       g_free(network->wifi.security);
+       g_free(network->wifi.passphrase);
+       g_free(network->wifi.agent_passphrase);
+       g_free(network->wifi.eap);
+       g_free(network->wifi.identity);
+       g_free(network->wifi.agent_identity);
+       g_free(network->wifi.ca_cert_path);
+       g_free(network->wifi.client_cert_path);
+       g_free(network->wifi.private_key_path);
+       g_free(network->wifi.private_key_passphrase);
+       g_free(network->wifi.phase2_auth);
+       g_free(network->wifi.pin_wps);
+
+       g_free(network->path);
+       g_free(network->group);
+       g_free(network->node);
+       g_free(network->name);
+       g_free(network->identifier);
+
+       network->device = NULL;
+
+       g_free(network);
+}
+
+/**
+ * connman_network_create:
+ * @identifier: network identifier (for example an unqiue name)
+ *
+ * Allocate a new network and assign the #identifier to it.
+ *
+ * Returns: a newly-allocated #connman_network structure
+ */
+struct connman_network *connman_network_create(const char *identifier,
+                                               enum connman_network_type type)
+{
+       struct connman_network *network;
+       char *ident;
+
+       DBG("identifier %s type %d", identifier, type);
+
+       network = g_try_new0(struct connman_network, 1);
+       if (network == NULL)
+               return NULL;
+
+       DBG("network %p", network);
+
+       network->refcount = 1;
+
+       ident = g_strdup(identifier);
+
+       if (ident == NULL) {
+               g_free(network);
+               return NULL;
+       }
+
+       network->type       = type;
+       network->identifier = ident;
+
+       network_list = g_slist_prepend(network_list, network);
+
+       return network;
+}
+
+/**
+ * connman_network_ref:
+ * @network: network structure
+ *
+ * Increase reference counter of  network
+ */
+struct connman_network *
+connman_network_ref_debug(struct connman_network *network,
+                       const char *file, int line, const char *caller)
+{
+       DBG("%p name %s ref %d by %s:%d:%s()", network, network->name,
+               network->refcount + 1, file, line, caller);
+
+       __sync_fetch_and_add(&network->refcount, 1);
+
+       return network;
+}
+
+/**
+ * connman_network_unref:
+ * @network: network structure
+ *
+ * Decrease reference counter of network
+ */
+void connman_network_unref_debug(struct connman_network *network,
+                               const char *file, int line, const char *caller)
+{
+       DBG("%p name %s ref %d by %s:%d:%s()", network, network->name,
+               network->refcount - 1, file, line, caller);
+
+       if (__sync_fetch_and_sub(&network->refcount, 1) != 1)
+               return;
+
+       network_list = g_slist_remove(network_list, network);
+
+       network_destruct(network);
+}
+
+const char *__connman_network_get_type(struct connman_network *network)
+{
+       return type2string(network->type);
+}
+
+/**
+ * connman_network_get_type:
+ * @network: network structure
+ *
+ * Get type of network
+ */
+enum connman_network_type connman_network_get_type(struct connman_network *network)
+{
+       return network->type;
+}
+
+/**
+ * connman_network_get_identifier:
+ * @network: network structure
+ *
+ * Get identifier of network
+ */
+const char *connman_network_get_identifier(struct connman_network *network)
+{
+       return network->identifier;
+}
+
+/**
+ * connman_network_set_index:
+ * @network: network structure
+ * @index: index number
+ *
+ * Set index number of network
+ */
+void connman_network_set_index(struct connman_network *network, int index)
+{
+       struct connman_service *service;
+       struct connman_ipconfig *ipconfig;
+
+       service = connman_service_lookup_from_network(network);
+       if (service == NULL)
+               goto done;
+
+       ipconfig = __connman_service_get_ip4config(service);
+       if (ipconfig == NULL)
+               goto done;
+
+       /* If index changed, the index of ipconfig must be reset. */
+       __connman_ipconfig_set_index(ipconfig, index);
+
+       DBG("index %d service %p ip4config %p", network->index,
+               service, ipconfig);
+done:
+       network->index = index;
+}
+
+/**
+ * connman_network_get_index:
+ * @network: network structure
+ *
+ * Get index number of network
+ */
+int connman_network_get_index(struct connman_network *network)
+{
+       return network->index;
+}
+
+/**
+ * connman_network_set_group:
+ * @network: network structure
+ * @group: group name
+ *
+ * Set group name for automatic clustering
+ */
+void connman_network_set_group(struct connman_network *network,
+                                                       const char *group)
+{
+       switch (network->type) {
+       case CONNMAN_NETWORK_TYPE_UNKNOWN:
+       case CONNMAN_NETWORK_TYPE_VENDOR:
+               return;
+       case CONNMAN_NETWORK_TYPE_ETHERNET:
+       case CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN:
+       case CONNMAN_NETWORK_TYPE_BLUETOOTH_DUN:
+       case CONNMAN_NETWORK_TYPE_CELLULAR:
+       case CONNMAN_NETWORK_TYPE_WIFI:
+               break;
+       }
 
-       network->connecting = FALSE;
+       if (g_strcmp0(network->group, group) == 0) {
+               if (group != NULL)
+                       __connman_service_update_from_network(network);
+               return;
+       }
 
-       ipconfig_ipv4 = __connman_service_get_ip4config(service);
-       err = __connman_ipconfig_address_add(ipconfig_ipv4);
-       if (err < 0)
-               goto err;
+       if (network->group != NULL) {
+               __connman_service_remove_from_network(network);
 
-#if defined TIZEN_EXT
-/*
- * Description: __connman_service_lookup_from_index cannot find correct service
- */
-       err = __connman_ipconfig_gateway_add(ipconfig_ipv4, service);
-#else
-       err = __connman_ipconfig_gateway_add(ipconfig_ipv4);
-#endif
-       if (err < 0)
-               goto err;
+               g_free(network->group);
+       }
 
-       return;
+       network->group = g_strdup(group);
 
-err:
-       connman_network_set_error(network,
-                               CONNMAN_NETWORK_ERROR_CONFIGURE_FAIL);
+       if (network->group != NULL)
+               network_probe(network);
 }
 
-static void dhcp_failure(struct connman_network *network)
+/**
+ * connman_network_get_group:
+ * @network: network structure
+ *
+ * Get group name for automatic clustering
+ */
+const char *connman_network_get_group(struct connman_network *network)
 {
-       struct connman_service *service;
-
-       service = __connman_service_lookup_from_network(network);
-       if (service == NULL)
-               return;
-
-       __connman_service_ipconfig_indicate_state(service,
-                                       CONNMAN_SERVICE_STATE_IDLE,
-                                       CONNMAN_IPCONFIG_TYPE_IPV4);
+       return network->group;
 }
 
-static void dhcp_callback(struct connman_network *network,
-                       connman_bool_t success)
+const char *__connman_network_get_ident(struct connman_network *network)
 {
-       DBG("success %d", success);
+       if (network->device == NULL)
+               return NULL;
 
-       if (success == TRUE)
-               dhcp_success(network);
-       else
-               dhcp_failure(network);
+       return connman_device_get_ident(network->device);
 }
 
-static int set_connected_fixed(struct connman_network *network)
+connman_bool_t __connman_network_get_weakness(struct connman_network *network)
 {
-       struct connman_service *service;
-       struct connman_ipconfig *ipconfig_ipv4;
-       int err;
-
-       DBG("");
-
-       service = __connman_service_lookup_from_network(network);
-
-       ipconfig_ipv4 = __connman_service_get_ip4config(service);
+       switch (network->type) {
+       case CONNMAN_NETWORK_TYPE_UNKNOWN:
+       case CONNMAN_NETWORK_TYPE_VENDOR:
+       case CONNMAN_NETWORK_TYPE_ETHERNET:
+       case CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN:
+       case CONNMAN_NETWORK_TYPE_BLUETOOTH_DUN:
+       case CONNMAN_NETWORK_TYPE_CELLULAR:
+               break;
+       case CONNMAN_NETWORK_TYPE_WIFI:
+               if (g_strcmp0(network->wifi.mode, "adhoc") == 0)
+                       return TRUE;
+               if (network->strength > 0 && network->strength < 20)
+                       return TRUE;
+               break;
+       }
 
-       set_configuration(network);
+       return FALSE;
+}
 
-       network->connecting = FALSE;
+connman_bool_t connman_network_get_connecting(struct connman_network *network)
+{
+       return network->connecting;
+}
 
-       connman_network_set_associating(network, FALSE);
+/**
+ * connman_network_set_available:
+ * @network: network structure
+ * @available: availability state
+ *
+ * Change availability state of network (in range)
+ */
+int connman_network_set_available(struct connman_network *network,
+                                               connman_bool_t available)
+{
+       DBG("network %p available %d", network, available);
 
-       err = __connman_ipconfig_address_add(ipconfig_ipv4);
-       if (err < 0)
-               goto err;
+       if (network->available == available)
+               return -EALREADY;
 
-#if defined TIZEN_EXT
-/*
- * Description: __connman_service_lookup_from_index cannot find correct service
- */
-       err = __connman_ipconfig_gateway_add(ipconfig_ipv4, service);
-#else
-       err = __connman_ipconfig_gateway_add(ipconfig_ipv4);
-#endif
-       if (err < 0)
-               goto err;
+       network->available = available;
 
        return 0;
+}
 
-err:
-       connman_network_set_error(network,
-                       CONNMAN_NETWORK_ERROR_CONFIGURE_FAIL);
-
-       return err;
+/**
+ * connman_network_get_available:
+ * @network: network structure
+ *
+ * Get network available setting
+ */
+connman_bool_t connman_network_get_available(struct connman_network *network)
+{
+       return network->available;
 }
 
-static void set_connected_manual(struct connman_network *network)
+#if defined TIZEN_EXT
+void connman_network_clear_associating(struct connman_network *network)
 {
        struct connman_service *service;
-       struct connman_ipconfig *ipconfig;
-       int err;
+       enum connman_service_state state;
 
        DBG("network %p", network);
 
-       service = __connman_service_lookup_from_network(network);
+       network->connecting = FALSE;
+       network->associating = FALSE;
 
-       ipconfig = __connman_service_get_ip4config(service);
+       service = connman_service_lookup_from_network(network);
+       if (!service)
+               return;
 
-       if (__connman_ipconfig_get_local(ipconfig) == NULL)
-               __connman_service_read_ip4config(service);
+       state = __connman_service_ipconfig_get_state(service,
+                                               CONNMAN_IPCONFIG_TYPE_IPV4);
+       if (state != CONNMAN_SERVICE_STATE_IDLE &&
+                       state != CONNMAN_SERVICE_STATE_FAILURE)
+               __connman_service_ipconfig_indicate_state(service,
+                                       CONNMAN_SERVICE_STATE_DISCONNECT,
+                                       CONNMAN_IPCONFIG_TYPE_IPV4);
 
-       set_configuration(network);
+       state = __connman_service_ipconfig_get_state(service,
+                                               CONNMAN_IPCONFIG_TYPE_IPV6);
+       if (state != CONNMAN_SERVICE_STATE_IDLE &&
+                               state != CONNMAN_SERVICE_STATE_FAILURE)
+               __connman_service_ipconfig_indicate_state(service,
+                                       CONNMAN_SERVICE_STATE_DISCONNECT,
+                                       CONNMAN_IPCONFIG_TYPE_IPV6);
 
-       err = __connman_ipconfig_address_add(ipconfig);
-       if (err < 0)
-               goto err;
+       __connman_service_ipconfig_indicate_state(service,
+                                               CONNMAN_SERVICE_STATE_IDLE,
+                                               CONNMAN_IPCONFIG_TYPE_IPV4);
 
-#if defined TIZEN_EXT
-/*
- * Description: __connman_service_lookup_from_index cannot find correct service
- */
-       err = __connman_ipconfig_gateway_add(ipconfig, service);
-#else
-       err = __connman_ipconfig_gateway_add(ipconfig);
-#endif
-       if (err < 0)
-               goto err;
+       __connman_service_ipconfig_indicate_state(service,
+                                               CONNMAN_SERVICE_STATE_IDLE,
+                                               CONNMAN_IPCONFIG_TYPE_IPV6);
+}
 
-       network->connecting = FALSE;
+static gboolean __connman_network_clear_associating_delayed(gpointer user_data)
+{
+       GSList *list;
+       gboolean found = FALSE;
+       enum connman_service_state state_ipv4;
+       enum connman_service_state state_ipv6;
+       struct connman_service *service;
+       struct connman_network *network = (struct connman_network *)user_data;
 
-       connman_network_set_associating(network, FALSE);
+       for (list = network_list; list != NULL; list = list->next) {
+               struct connman_network *item = list->data;
 
-       return;
+               if (item == network) {
+                       found = TRUE;
+                       break;
+               }
+       }
 
-err:
-       connman_network_set_error(network,
-                                       CONNMAN_NETWORK_ERROR_CONFIGURE_FAIL);
-       return;
-}
+       if (found != TRUE)
+               return FALSE;
 
-static int set_connected_dhcp(struct connman_network *network)
-{
-       int err;
+       DBG("network %p name %s", network, network->name);
+       service = connman_service_lookup_from_network(network);
 
-       DBG("network %p", network);
+       state_ipv4 = __connman_service_ipconfig_get_state(service,
+                                               CONNMAN_IPCONFIG_TYPE_IPV4);
+       state_ipv6 = __connman_service_ipconfig_get_state(service,
+                                               CONNMAN_IPCONFIG_TYPE_IPV6);
 
-       set_configuration(network);
+       DBG("service %p state %d/%d", service, state_ipv4, state_ipv6);
 
-       err = __connman_dhcp_start(network, dhcp_callback);
-       if (err < 0) {
-               connman_error("Can not request DHCP lease");
-               return err;
-       }
+       if (network->associating == FALSE &&
+                       state_ipv4 == CONNMAN_SERVICE_STATE_ASSOCIATION &&
+                       state_ipv6 == CONNMAN_SERVICE_STATE_ASSOCIATION)
+               connman_network_clear_associating(network);
 
-       return 0;
+       return FALSE;
 }
+#endif
 
-static int manual_ipv6_set(struct connman_network *network,
-                               struct connman_ipconfig *ipconfig_ipv6)
+/**
+ * connman_network_set_associating:
+ * @network: network structure
+ * @associating: associating state
+ *
+ * Change associating state of network
+ */
+int connman_network_set_associating(struct connman_network *network,
+                                               connman_bool_t associating)
 {
-       struct connman_service *service;
-       int err;
+       DBG("network %p associating %d", network, associating);
 
-       DBG("network %p ipv6 %p", network, ipconfig_ipv6);
+       if (network->associating == associating)
+               return -EALREADY;
 
-       service = __connman_service_lookup_from_network(network);
-       if (service == NULL)
-               return -EINVAL;
+       network->associating = associating;
 
-       if (__connman_ipconfig_get_local(ipconfig_ipv6) == NULL)
-               __connman_service_read_ip6config(service);
+       if (associating == TRUE) {
+               struct connman_service *service;
 
-       err = __connman_ipconfig_address_add(ipconfig_ipv6);
-       if (err < 0) {
-               connman_network_set_error(network,
-                       CONNMAN_NETWORK_ERROR_CONFIGURE_FAIL);
-               return err;
+               service = connman_service_lookup_from_network(network);
+               __connman_service_ipconfig_indicate_state(service,
+                                       CONNMAN_SERVICE_STATE_ASSOCIATION,
+                                       CONNMAN_IPCONFIG_TYPE_IPV4);
+               __connman_service_ipconfig_indicate_state(service,
+                                       CONNMAN_SERVICE_STATE_ASSOCIATION,
+                                       CONNMAN_IPCONFIG_TYPE_IPV6);
        }
 
 #if defined TIZEN_EXT
-/*
- * Description: __connman_service_lookup_from_index cannot find correct service
- */
-       err = __connman_ipconfig_gateway_add(ipconfig_ipv6, service);
-#else
-       err = __connman_ipconfig_gateway_add(ipconfig_ipv6);
+       if (associating == FALSE &&
+                       connman_network_get_bool(network, "WiFi.UseWPS") == FALSE)
+               g_timeout_add_seconds(1,
+                               __connman_network_clear_associating_delayed, network);
 #endif
-       if (err < 0)
-               return err;
-
-       __connman_connection_gateway_activate(service,
-                                               CONNMAN_IPCONFIG_TYPE_IPV6);
 
-       __connman_device_set_network(network->device, network);
+       return 0;
+}
 
-       connman_device_set_disconnected(network->device, FALSE);
+static void set_associate_error(struct connman_network *network)
+{
+       struct connman_service *service;
 
-       network->connecting = FALSE;
+       service = connman_service_lookup_from_network(network);
 
-       return 0;
+       __connman_service_indicate_error(service,
+                                       CONNMAN_SERVICE_ERROR_CONNECT_FAILED);
 }
 
-static void autoconf_ipv6_set(struct connman_network *network)
+static void set_configure_error(struct connman_network *network)
 {
-       DBG("network %p", network);
+       struct connman_service *service;
 
-       __connman_device_set_network(network->device, network);
+       service = connman_service_lookup_from_network(network);
 
-       connman_device_set_disconnected(network->device, FALSE);
+       __connman_service_indicate_error(service,
+                                       CONNMAN_SERVICE_ERROR_CONNECT_FAILED);
+}
+
+static void set_invalid_key_error(struct connman_network *network)
+{
+       struct connman_service *service;
 
-       /* XXX: Append IPv6 nameservers here */
+       service = connman_service_lookup_from_network(network);
 
-       network->connecting = FALSE;
+       __connman_service_indicate_error(service,
+                                       CONNMAN_SERVICE_ERROR_INVALID_KEY);
 }
 
-static gboolean set_connected(gpointer user_data)
+static void set_connect_error(struct connman_network *network)
 {
-       struct connman_network *network = user_data;
        struct connman_service *service;
-       struct connman_ipconfig *ipconfig_ipv4, *ipconfig_ipv6;
-       enum connman_ipconfig_method ipv4_method, ipv6_method;
-
-       service = __connman_service_lookup_from_network(network);
 
-       ipconfig_ipv4 = __connman_service_get_ip4config(service);
-       ipconfig_ipv6 = __connman_service_get_ip6config(service);
+#if defined TIZEN_EXT
+       if (network->associating != FALSE)
+               network->associating = FALSE;
+#endif
+       service = connman_service_lookup_from_network(network);
 
-       DBG("service %p ipv4 %p ipv6 %p", service, ipconfig_ipv4,
-               ipconfig_ipv6);
+       __connman_service_indicate_error(service,
+                                       CONNMAN_SERVICE_ERROR_CONNECT_FAILED);
+}
 
-       ipv4_method = __connman_ipconfig_get_method(ipconfig_ipv4);
-       ipv6_method = __connman_ipconfig_get_method(ipconfig_ipv6);
+#if defined TIZEN_EXT
+static void set_dhcp_error(struct connman_network *network)
+{
+       struct connman_service *service;
 
-       DBG("method ipv4 %d ipv6 %d", ipv4_method, ipv6_method);
-       DBG("network connected %d", network->connected);
+       if (network->associating != FALSE)
+               network->associating = FALSE;
 
-       if (network->connected == TRUE) {
-               int ret;
+       service = connman_service_lookup_from_network(network);
 
-               switch (ipv6_method) {
-               case CONNMAN_IPCONFIG_METHOD_UNKNOWN:
-               case CONNMAN_IPCONFIG_METHOD_OFF:
-                       break;
-               case CONNMAN_IPCONFIG_METHOD_AUTO:
-                       autoconf_ipv6_set(network);
-                       break;
-               case CONNMAN_IPCONFIG_METHOD_FIXED:
-               case CONNMAN_IPCONFIG_METHOD_MANUAL:
-                       ret = manual_ipv6_set(network, ipconfig_ipv6);
-                       if (ret != 0) {
-                               connman_network_set_error(network,
-                                       CONNMAN_NETWORK_ERROR_ASSOCIATE_FAIL);
-                               return FALSE;
-                       }
-                       break;
-               case CONNMAN_IPCONFIG_METHOD_DHCP:
-                       break;
-               }
+       __connman_service_indicate_error(service,
+                                       CONNMAN_SERVICE_ERROR_DHCP_FAILED);
+}
+#endif
 
-               switch (ipv4_method) {
-               case CONNMAN_IPCONFIG_METHOD_UNKNOWN:
-               case CONNMAN_IPCONFIG_METHOD_OFF:
-               case CONNMAN_IPCONFIG_METHOD_AUTO:
-                       return FALSE;
-               case CONNMAN_IPCONFIG_METHOD_FIXED:
-                       if (set_connected_fixed(network) < 0) {
-                               connman_network_set_error(network,
-                                       CONNMAN_NETWORK_ERROR_ASSOCIATE_FAIL);
-                               return FALSE;
-                       }
-                       return TRUE;
-               case CONNMAN_IPCONFIG_METHOD_MANUAL:
-                       set_connected_manual(network);
-                       return TRUE;
-               case CONNMAN_IPCONFIG_METHOD_DHCP:
-                       if (set_connected_dhcp(network) < 0) {
-                               connman_network_set_error(network,
-                                       CONNMAN_NETWORK_ERROR_ASSOCIATE_FAIL);
-                               return FALSE;
-                       }
-               }
+void connman_network_set_ipv4_method(struct connman_network *network,
+                                       enum connman_ipconfig_method method)
+{
+       struct connman_service *service;
+       struct connman_ipconfig *ipconfig;
 
-       } else {
-               enum connman_service_state state;
+       service = connman_service_lookup_from_network(network);
+       if (service == NULL)
+               return;
 
-               __connman_device_set_network(network->device, NULL);
+       ipconfig = __connman_service_get_ip4config(service);
+       if (ipconfig == NULL)
+               return;
 
-               switch (ipv4_method) {
-               case CONNMAN_IPCONFIG_METHOD_UNKNOWN:
-               case CONNMAN_IPCONFIG_METHOD_OFF:
-               case CONNMAN_IPCONFIG_METHOD_AUTO:
-               case CONNMAN_IPCONFIG_METHOD_FIXED:
-               case CONNMAN_IPCONFIG_METHOD_MANUAL:
-                       break;
-               case CONNMAN_IPCONFIG_METHOD_DHCP:
-                       __connman_dhcp_stop(network);
-                       break;
-               }
+       __connman_ipconfig_set_method(ipconfig, method);
+}
 
-               /*
-                * We only set the disconnect state if we were not in idle
-                * or in failure. It does not make sense to go to disconnect
-                * state if we were not connected.
-                */
-               state = __connman_service_ipconfig_get_state(service,
-                                               CONNMAN_IPCONFIG_TYPE_IPV4);
-               if (state != CONNMAN_SERVICE_STATE_IDLE &&
-                                       state != CONNMAN_SERVICE_STATE_FAILURE)
-                       __connman_service_ipconfig_indicate_state(service,
-                                       CONNMAN_SERVICE_STATE_DISCONNECT,
-                                       CONNMAN_IPCONFIG_TYPE_IPV4);
+void connman_network_set_ipv6_method(struct connman_network *network,
+                                       enum connman_ipconfig_method method)
+{
+       struct connman_service *service;
+       struct connman_ipconfig *ipconfig;
 
-               state = __connman_service_ipconfig_get_state(service,
-                                               CONNMAN_IPCONFIG_TYPE_IPV6);
-               if (state != CONNMAN_SERVICE_STATE_IDLE &&
-                                       state != CONNMAN_SERVICE_STATE_FAILURE)
-                       __connman_service_ipconfig_indicate_state(service,
-                                       CONNMAN_SERVICE_STATE_DISCONNECT,
-                                       CONNMAN_IPCONFIG_TYPE_IPV6);
+       service = connman_service_lookup_from_network(network);
+       if (service == NULL)
+               return;
 
-               __connman_connection_gateway_remove(service,
-                                               CONNMAN_IPCONFIG_TYPE_ALL);
+       ipconfig = __connman_service_get_ip6config(service);
+       if (ipconfig == NULL)
+               return;
 
-               __connman_ipconfig_address_unset(ipconfig_ipv4);
-               __connman_ipconfig_address_unset(ipconfig_ipv6);
+       __connman_ipconfig_set_method(ipconfig, method);
+}
 
-               /*
-                * Special handling for IPv6 autoconfigured address.
-                * The simplest way to remove autoconfigured routes is to
-                * disable IPv6 temporarily so that kernel will do the cleanup
-                * automagically.
-                */
-               if (ipv6_method == CONNMAN_IPCONFIG_METHOD_AUTO) {
-                       __connman_ipconfig_disable_ipv6(ipconfig_ipv6);
-                       __connman_ipconfig_enable_ipv6(ipconfig_ipv6);
-               }
+void connman_network_set_error(struct connman_network *network,
+                                       enum connman_network_error error)
+{
+       DBG("network %p error %d", network, error);
 
-#if defined TIZEN_EXT
-               if (connman_service_get_type(service) ==
-                               CONNMAN_SERVICE_TYPE_CELLULAR) {
-                       network->connecting = FALSE;
-                       connman_network_set_associating(network, FALSE);
-               }
-#endif
-               __connman_service_ipconfig_indicate_state(service,
-                                       CONNMAN_SERVICE_STATE_IDLE,
-                                       CONNMAN_IPCONFIG_TYPE_IPV4);
+       network->connecting = FALSE;
+       network->associating = FALSE;
 
-               __connman_service_ipconfig_indicate_state(service,
-                                       CONNMAN_SERVICE_STATE_IDLE,
-                                       CONNMAN_IPCONFIG_TYPE_IPV6);
+       switch (error) {
+       case CONNMAN_NETWORK_ERROR_UNKNOWN:
+               return;
+       case CONNMAN_NETWORK_ERROR_ASSOCIATE_FAIL:
+               set_associate_error(network);
+               break;
+       case CONNMAN_NETWORK_ERROR_CONFIGURE_FAIL:
+               set_configure_error(network);
+               break;
+       case CONNMAN_NETWORK_ERROR_INVALID_KEY:
+               set_invalid_key_error(network);
+               break;
+       case CONNMAN_NETWORK_ERROR_CONNECT_FAIL:
+               set_connect_error(network);
+               break;
 #if defined TIZEN_EXT
-               if (connman_service_get_type(service) ==
-                               CONNMAN_SERVICE_TYPE_CELLULAR)
-                       return FALSE;
+       case CONNMAN_NETWORK_ERROR_DHCP_FAIL:
+               set_dhcp_error(network);
+               break;
 #endif
        }
 
-       network->connecting = FALSE;
+       network_change(network);
+}
 
-       connman_network_set_associating(network, FALSE);
+void connman_network_clear_error(struct connman_network *network)
+{
+       struct connman_service *service;
 
-       return FALSE;
+       DBG("network %p", network);
+
+       if (network == NULL)
+               return;
+
+       if (network->connecting == TRUE || network->associating == TRUE)
+               return;
+
+       service = connman_service_lookup_from_network(network);
+       __connman_service_clear_error(service);
 }
 
 /**
@@ -1179,21 +1549,25 @@ static gboolean set_connected(gpointer user_data)
 int connman_network_set_connected(struct connman_network *network,
                                                connman_bool_t connected)
 {
-       DBG("network %p connected %d", network, connected);
+       DBG("network %p connected %d/%d connecting %d associating %d",
+               network, network->connected, connected, network->connecting,
+               network->associating);
 
        if ((network->connecting == TRUE || network->associating == TRUE) &&
                                                        connected == FALSE) {
                connman_network_set_error(network,
                                        CONNMAN_NETWORK_ERROR_CONNECT_FAIL);
-               __connman_network_disconnect(network);
+               if (__connman_network_disconnect(network) == 0)
+                       return 0;
        }
 
        if (network->connected == connected)
                return -EALREADY;
 
-       network->connected = connected;
-
-       set_connected(network);
+       if (connected == FALSE)
+               set_disconnected(network);
+       else
+               set_connected(network);
 
        return 0;
 }
@@ -1220,6 +1594,57 @@ connman_bool_t connman_network_get_associating(struct connman_network *network)
        return network->associating;
 }
 
+void connman_network_clear_hidden(void *user_data)
+{
+       if (user_data == NULL)
+               return;
+
+       DBG("user_data %p", user_data);
+
+       /*
+        * Hidden service does not have a connect timeout so
+        * we do not need to remove it. We can just return
+        * error to the caller telling that we could not find
+        * any network that we could connect to.
+        */
+       __connman_service_reply_dbus_pending(user_data, EIO);
+}
+
+int connman_network_connect_hidden(struct connman_network *network,
+                       char *identity, char* passphrase, void *user_data)
+{
+       int err = 0;
+       struct connman_service *service;
+
+       service = connman_service_lookup_from_network(network);
+
+       DBG("network %p service %p user_data %p", network, service, user_data);
+
+       if (service == NULL)
+               return -EINVAL;
+
+       if (identity != NULL)
+               __connman_service_set_agent_identity(service, identity);
+
+       if (passphrase != NULL)
+               err = __connman_service_add_passphrase(service, passphrase);
+
+       if (err == -ENOKEY) {
+               __connman_service_indicate_error(service,
+                                       CONNMAN_SERVICE_ERROR_INVALID_KEY);
+               goto out;
+       } else {
+               __connman_service_set_hidden(service);
+               __connman_service_set_userconnect(service, TRUE);
+               __connman_service_set_hidden_data(service, user_data);
+               return __connman_service_connect(service);
+       }
+
+out:
+       __connman_service_return_error(service, -err, user_data);
+       return err;
+}
+
 /**
  * __connman_network_connect:
  * @network: network structure
@@ -1265,7 +1690,6 @@ int __connman_network_connect(struct connman_network *network)
                return err;
        }
 
-       network->connected = TRUE;
        set_connected(network);
 
        return err;
@@ -1296,10 +1720,8 @@ int __connman_network_disconnect(struct connman_network *network)
        network->connecting = FALSE;
 
        err = network->driver->disconnect(network);
-       if (err == 0) {
-               connman_network_set_connected(network, FALSE);
-               set_connected(network);
-       }
+       if (err == 0)
+               set_disconnected(network);
 
        return err;
 }
@@ -1310,7 +1732,7 @@ static int manual_ipv4_set(struct connman_network *network,
        struct connman_service *service;
        int err;
 
-       service = __connman_service_lookup_from_network(network);
+       service = connman_service_lookup_from_network(network);
        if (service == NULL)
                return -EINVAL;
 
@@ -1322,9 +1744,6 @@ static int manual_ipv4_set(struct connman_network *network,
        }
 
 #if defined TIZEN_EXT
-/*
- * Description: __connman_service_lookup_from_index cannot find correct service
- */
        return __connman_ipconfig_gateway_add(ipconfig, service);
 #else
        return __connman_ipconfig_gateway_add(ipconfig);
@@ -1338,7 +1757,7 @@ int __connman_network_clear_ipconfig(struct connman_network *network,
        enum connman_ipconfig_method method;
        enum connman_ipconfig_type type;
 
-       service = __connman_service_lookup_from_network(network);
+       service = connman_service_lookup_from_network(network);
        if (service == NULL)
                return -EINVAL;
 
@@ -1349,8 +1768,10 @@ int __connman_network_clear_ipconfig(struct connman_network *network,
        case CONNMAN_IPCONFIG_METHOD_UNKNOWN:
        case CONNMAN_IPCONFIG_METHOD_OFF:
        case CONNMAN_IPCONFIG_METHOD_FIXED:
-       case CONNMAN_IPCONFIG_METHOD_AUTO:
                return -EINVAL;
+       case CONNMAN_IPCONFIG_METHOD_AUTO:
+               release_dhcpv6(network);
+               break;
        case CONNMAN_IPCONFIG_METHOD_MANUAL:
                __connman_ipconfig_address_remove(ipconfig);
                break;
@@ -1432,7 +1853,7 @@ int connman_network_set_ipaddress(struct connman_network *network,
 
        DBG("network %p", network);
 
-       service = __connman_service_lookup_from_network(network);
+       service = connman_service_lookup_from_network(network);
        if (service == NULL)
                return -EINVAL;
 
@@ -1451,41 +1872,20 @@ int connman_network_set_ipaddress(struct connman_network *network,
 
 #if defined TIZEN_EXT
 /*
- * Description: Telephony plug-in requires manual PROXY setting function
- */
-int connman_network_set_proxy(struct connman_network *network,
-                               const char *proxies)
-{
-       struct connman_service *service;
-
-       DBG("network %p proxies %s", network, proxies);
-
-       service = __connman_service_lookup_from_network(network);
-       if (service == NULL)
-               return -EINVAL;
-
-       __connman_service_set_proxy(service, proxies);
-
-       connman_service_set_proxy_method(service, CONNMAN_SERVICE_PROXY_METHOD_MANUAL);
-
-       return 0;
-}
-
-/*
- * Description: Network client requires additional wifi specific info
+ * Description: Network client requires additional Wi-Fi specific info
  */
 int connman_network_set_bssid(struct connman_network *network,
                                const unsigned char *bssid)
 {
+       int i = 0;
+
        if (bssid == NULL)
                return -EINVAL;
 
-       DBG("network %p bssid %02x:%02x:%02x:%02x:%02x:%02x", network,
-                       bssid[0], bssid[1], bssid[2],
-                       bssid[3], bssid[4], bssid[5]);
+       DBG("network %p bssid %02x:%02x:%02x", network,
+                       bssid[0], bssid[4], bssid[5]);
 
-       int i = 0;
-       for (;i < 6;i++)
+       for (;i < WIFI_BSSID_LEN_MAX;i++)
                network->wifi.bssid[i] = bssid[i];
 
        return 0;
@@ -1499,8 +1899,6 @@ unsigned char *connman_network_get_bssid(struct connman_network *network)
 int connman_network_set_maxrate(struct connman_network *network,
                                unsigned int maxrate)
 {
-       DBG("network %p maxrate %d", network, maxrate);
-
        network->wifi.maxrate = maxrate;
 
        return 0;
@@ -1517,9 +1915,8 @@ int connman_network_set_enc_mode(struct connman_network *network,
        if (encryption_mode == NULL)
                return -EINVAL;
 
-       DBG("network %p encryption mode %s", network, encryption_mode);
-
-       g_strlcpy(network->wifi.encryption_mode, encryption_mode, 6);
+       g_strlcpy(network->wifi.encryption_mode, encryption_mode,
+                                       WIFI_ENCYPTION_MODE_LEN_MAX);
 
        return 0;
 }
@@ -1529,24 +1926,23 @@ const char *connman_network_get_enc_mode(struct connman_network *network)
        return (const char *)network->wifi.encryption_mode;
 }
 
-const char *connman_network_get_ifname(struct connman_network *network)
+int connman_network_set_is_hs20AP(struct connman_network *network,
+                               unsigned int isHS20AP)
 {
-       struct connman_service *service;
-       struct connman_ipconfig *ipconfig;
-       const char *ifname = NULL;
+       if (network == NULL)
+               return 0;
 
-       service = __connman_service_lookup_from_network(network);
-       if (service == NULL)
-               return NULL;
+       network->wifi.isHS20AP = isHS20AP;
 
-       ipconfig = __connman_service_get_ip4config(service);
+       return 0;
+}
 
-       if (ipconfig != NULL)
-               ifname = connman_ipconfig_get_ifname(ipconfig);
+unsigned int connman_network_get_is_hs20AP(struct connman_network *network)
+{
+       if (network == NULL)
+               return 0;
 
-       DBG("index %d, service %p ip4config %p ifname %s",
-                       network->index, service, ipconfig, ifname);
-       return ifname;
+       return network->wifi.isHS20AP;
 }
 #endif
 
@@ -1559,7 +1955,7 @@ int connman_network_set_nameservers(struct connman_network *network,
 
        DBG("network %p nameservers %s", network, nameservers);
 
-       service = __connman_service_lookup_from_network(network);
+       service = connman_service_lookup_from_network(network);
        if (service == NULL)
                return -EINVAL;
 
@@ -1587,7 +1983,7 @@ int connman_network_set_domain(struct connman_network *network,
 
        DBG("network %p domain %s", network, domain);
 
-       service = __connman_service_lookup_from_network(network);
+       service = connman_service_lookup_from_network(network);
        if (service == NULL)
                return -EINVAL;
 
@@ -1606,7 +2002,9 @@ int connman_network_set_domain(struct connman_network *network,
 int connman_network_set_name(struct connman_network *network,
                                                        const char *name)
 {
+#if !defined TIZEN_EXT
        DBG("network %p name %s", network, name);
+#endif
 
        g_free(network->name);
        network->name = g_strdup(name);
@@ -1640,7 +2038,9 @@ connman_uint8_t connman_network_get_strength(struct connman_network *network)
 int connman_network_set_frequency(struct connman_network *network,
                                                connman_uint16_t frequency)
 {
+#if !defined TIZEN_EXT
        DBG("network %p frequency %d", network, frequency);
+#endif
 
        network->frequency = frequency;
 
@@ -1668,23 +2068,6 @@ connman_uint16_t connman_network_get_wifi_channel(struct connman_network *networ
 }
 
 /**
- * connman_network_set_roaming:
- * @network: network structure
- * @roaming: roaming state
- *
- * Set roaming state for network
- */
-int connman_network_set_roaming(struct connman_network *network,
-                                               connman_bool_t roaming)
-{
-       DBG("network %p roaming %d", network, roaming);
-
-       network->roaming = roaming;
-
-       return 0;
-}
-
-/**
  * connman_network_set_string:
  * @network: network structure
  * @key: unique identifier
@@ -1695,7 +2078,9 @@ int connman_network_set_roaming(struct connman_network *network,
 int connman_network_set_string(struct connman_network *network,
                                        const char *key, const char *value)
 {
+#if !defined TIZEN_EXT
        DBG("network %p key %s value %s", network, key, value);
+#endif
 
        if (g_strcmp0(key, "Name") == 0)
                return connman_network_set_name(network, value);
@@ -1762,7 +2147,9 @@ int connman_network_set_string(struct connman_network *network,
 const char *connman_network_get_string(struct connman_network *network,
                                                        const char *key)
 {
+#if !defined TIZEN_EXT
        DBG("network %p key %s", network, key);
+#endif
 
        if (g_str_equal(key, "Path") == TRUE)
                return network->path;
@@ -1814,11 +2201,15 @@ int connman_network_set_bool(struct connman_network *network,
        DBG("network %p key %s value %d", network, key, value);
 
        if (g_strcmp0(key, "Roaming") == 0)
-               return connman_network_set_roaming(network, value);
+               network->roaming = value;
        else if (g_strcmp0(key, "WiFi.WPS") == 0)
                network->wifi.wps = value;
        else if (g_strcmp0(key, "WiFi.UseWPS") == 0)
                network->wifi.use_wps = value;
+#if defined TIZEN_EXT
+       else if (g_strcmp0(key, "DefaultInternet") == 0)
+               network->default_internet = value;
+#endif
 
        return -EINVAL;
 }
@@ -1833,7 +2224,9 @@ int connman_network_set_bool(struct connman_network *network,
 connman_bool_t connman_network_get_bool(struct connman_network *network,
                                                        const char *key)
 {
+#if !defined TIZEN_EXT
        DBG("network %p key %s", network, key);
+#endif
 
        if (g_str_equal(key, "Roaming") == TRUE)
                return network->roaming;
@@ -1841,6 +2234,10 @@ connman_bool_t connman_network_get_bool(struct connman_network *network,
                return network->wifi.wps;
        else if (g_str_equal(key, "WiFi.UseWPS") == TRUE)
                return network->wifi.use_wps;
+#if defined TIZEN_EXT
+       else if (g_strcmp0(key, "DefaultInternet") == 0)
+               return network->default_internet;
+#endif
 
        return FALSE;
 }
@@ -1857,7 +2254,9 @@ connman_bool_t connman_network_get_bool(struct connman_network *network,
 int connman_network_set_blob(struct connman_network *network,
                        const char *key, const void *data, unsigned int size)
 {
+#if !defined TIZEN_EXT
        DBG("network %p key %s size %d", network, key, size);
+#endif
 
        if (g_str_equal(key, "WiFi.SSID") == TRUE) {
                g_free(network->wifi.ssid);
@@ -1885,7 +2284,9 @@ int connman_network_set_blob(struct connman_network *network,
 const void *connman_network_get_blob(struct connman_network *network,
                                        const char *key, unsigned int *size)
 {
+#if !defined TIZEN_EXT
        DBG("network %p key %s", network, key);
+#endif
 
        if (g_str_equal(key, "WiFi.SSID") == TRUE) {
                if (size != NULL)
@@ -1956,7 +2357,6 @@ void connman_network_update(struct connman_network *network)
        case CONNMAN_NETWORK_TYPE_BLUETOOTH_DUN:
        case CONNMAN_NETWORK_TYPE_CELLULAR:
        case CONNMAN_NETWORK_TYPE_WIFI:
-       case CONNMAN_NETWORK_TYPE_WIMAX:
                break;
        }
 
index 5769b66..7f4205f 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
@@ -32,6 +32,8 @@ static DBusConnection *connection = NULL;
 static GSList *notifier_list = NULL;
 static GHashTable *service_hash = NULL;
 
+static const char *notifier_state;
+
 static gint compare_priority(gconstpointer a, gconstpointer b)
 {
        const struct connman_notifier *notifier1 = a;
@@ -73,142 +75,68 @@ void connman_notifier_unregister(struct connman_notifier *notifier)
 
 #define MAX_TECHNOLOGIES 10
 
-static volatile int registered[MAX_TECHNOLOGIES];
-static volatile int enabled[MAX_TECHNOLOGIES];
-static volatile int connected[MAX_TECHNOLOGIES];
+static int connected[MAX_TECHNOLOGIES];
+static int online[MAX_TECHNOLOGIES];
 
-void __connman_notifier_list_registered(DBusMessageIter *iter, void *user_data)
+static connman_bool_t notifier_is_online(void)
 {
-       int i;
+       unsigned int i;
 
        __sync_synchronize();
        for (i = 0; i < MAX_TECHNOLOGIES; i++) {
-               const char *type = __connman_service_type2string(i);
-
-               if (type == NULL)
-                       continue;
-
-               if (registered[i] > 0)
-                       dbus_message_iter_append_basic(iter,
-                                               DBUS_TYPE_STRING, &type);
+               if (online[i] > 0)
+                       return TRUE;
        }
-}
-
-void __connman_notifier_list_enabled(DBusMessageIter *iter, void *user_data)
-{
-       int i;
 
-       __sync_synchronize();
-       for (i = 0; i < MAX_TECHNOLOGIES; i++) {
-               const char *type = __connman_service_type2string(i);
-
-               if (type == NULL)
-                       continue;
-
-               if (enabled[i] > 0)
-                       dbus_message_iter_append_basic(iter,
-                                               DBUS_TYPE_STRING, &type);
-       }
+       return FALSE;
 }
 
-void __connman_notifier_list_connected(DBusMessageIter *iter, void *user_data)
+connman_bool_t __connman_notifier_is_connected(void)
 {
-       int i;
+       unsigned int i;
 
        __sync_synchronize();
        for (i = 0; i < MAX_TECHNOLOGIES; i++) {
-               const char *type = __connman_service_type2string(i);
-
-               if (type == NULL)
-                       continue;
-
                if (connected[i] > 0)
-                       dbus_message_iter_append_basic(iter,
-                                               DBUS_TYPE_STRING, &type);
+                       return TRUE;
        }
-}
 
-static void technology_registered(enum connman_service_type type,
-                                               connman_bool_t registered)
-{
-       DBG("type %d registered %d", type, registered);
-
-       connman_dbus_property_changed_array(CONNMAN_MANAGER_PATH,
-               CONNMAN_MANAGER_INTERFACE, "AvailableTechnologies",
-               DBUS_TYPE_STRING, __connman_notifier_list_registered, NULL);
+       return FALSE;
 }
 
-static void technology_enabled(enum connman_service_type type,
-                                               connman_bool_t enabled)
+static const char *evaluate_notifier_state(void)
 {
-       GSList *list;
-
-       DBG("type %d enabled %d", type, enabled);
-
-       connman_dbus_property_changed_array(CONNMAN_MANAGER_PATH,
-               CONNMAN_MANAGER_INTERFACE, "EnabledTechnologies",
-               DBUS_TYPE_STRING, __connman_notifier_list_enabled, NULL);
-
-       for (list = notifier_list; list; list = list->next) {
-               struct connman_notifier *notifier = list->data;
-
-               if (notifier->service_enabled)
-                       notifier->service_enabled(type, enabled);
-       }
-}
+       if (notifier_is_online() == TRUE)
+               return "online";
 
-unsigned int __connman_notifier_count_connected(void)
-{
-       unsigned int i, count = 0;
+       if (__connman_notifier_is_connected() == TRUE)
+               return "ready";
 
-       __sync_synchronize();
-       for (i = 0; i < MAX_TECHNOLOGIES; i++) {
-               if (connected[i] > 0)
-                       count++;
-       }
+       if ( __connman_technology_get_offlinemode() == TRUE)
+               return "offline";
 
-       return count;
+       return "idle";
 }
 
 const char *__connman_notifier_get_state(void)
 {
-       unsigned int count = __connman_notifier_count_connected();
-
-       if (count > 0)
-               return "online";
-
-       return "offline";
+       return notifier_state;
 }
 
-static void state_changed(connman_bool_t connected)
+static void state_changed(void)
 {
-       unsigned int count = __connman_notifier_count_connected();
-       char *state = "offline";
-       DBusMessage *signal;
+       const char *state;
 
-       if (count > 1)
-               return;
+       state = evaluate_notifier_state();
 
-       if (count == 1) {
-               if (connected == FALSE)
-                       return;
+       if (g_strcmp0(state, notifier_state) == 0)
+               return;
 
-               state = "online";
-       }
+       notifier_state = state;
 
        connman_dbus_property_changed_basic(CONNMAN_MANAGER_PATH,
                                CONNMAN_MANAGER_INTERFACE, "State",
-                                               DBUS_TYPE_STRING, &state);
-
-       signal = dbus_message_new_signal(CONNMAN_MANAGER_PATH,
-                               CONNMAN_MANAGER_INTERFACE, "StateChanged");
-       if (signal == NULL)
-               return;
-
-       dbus_message_append_args(signal, DBUS_TYPE_STRING, &state,
-                                                       DBUS_TYPE_INVALID);
-
-       g_dbus_send_message(connection, signal);
+                                       DBUS_TYPE_STRING, &notifier_state);
 }
 
 static void technology_connected(enum connman_service_type type,
@@ -216,68 +144,11 @@ static void technology_connected(enum connman_service_type type,
 {
        DBG("type %d connected %d", type, connected);
 
-       connman_dbus_property_changed_array(CONNMAN_MANAGER_PATH,
-               CONNMAN_MANAGER_INTERFACE, "ConnectedTechnologies",
-               DBUS_TYPE_STRING, __connman_notifier_list_connected, NULL);
-
-       state_changed(connected);
-}
-
-void __connman_notifier_register(enum connman_service_type type)
-{
-       DBG("type %d", type);
-
-       switch (type) {
-       case CONNMAN_SERVICE_TYPE_UNKNOWN:
-       case CONNMAN_SERVICE_TYPE_SYSTEM:
-       case CONNMAN_SERVICE_TYPE_GPS:
-       case CONNMAN_SERVICE_TYPE_VPN:
-       case CONNMAN_SERVICE_TYPE_GADGET:
-               return;
-       case CONNMAN_SERVICE_TYPE_ETHERNET:
-       case CONNMAN_SERVICE_TYPE_WIFI:
-       case CONNMAN_SERVICE_TYPE_WIMAX:
-       case CONNMAN_SERVICE_TYPE_BLUETOOTH:
-       case CONNMAN_SERVICE_TYPE_CELLULAR:
-               break;
-       }
-
-       if (__sync_fetch_and_add(&registered[type], 1) == 0)
-               technology_registered(type, TRUE);
+       __connman_technology_set_connected(type, connected);
+       state_changed();
 }
 
-void __connman_notifier_unregister(enum connman_service_type type)
-{
-       DBG("type %d", type);
-
-       __sync_synchronize();
-       if (registered[type] == 0) {
-               connman_error("notifier unregister underflow");
-               return;
-       }
-
-       switch (type) {
-       case CONNMAN_SERVICE_TYPE_UNKNOWN:
-       case CONNMAN_SERVICE_TYPE_SYSTEM:
-       case CONNMAN_SERVICE_TYPE_GPS:
-       case CONNMAN_SERVICE_TYPE_VPN:
-       case CONNMAN_SERVICE_TYPE_GADGET:
-               return;
-       case CONNMAN_SERVICE_TYPE_ETHERNET:
-       case CONNMAN_SERVICE_TYPE_WIFI:
-       case CONNMAN_SERVICE_TYPE_WIMAX:
-       case CONNMAN_SERVICE_TYPE_BLUETOOTH:
-       case CONNMAN_SERVICE_TYPE_CELLULAR:
-               break;
-       }
-
-       if (__sync_fetch_and_sub(&registered[type], 1) != 1)
-               return;
-
-       technology_registered(type, FALSE);
-}
-
-void __connman_notifier_enable(enum connman_service_type type)
+void __connman_notifier_connect(enum connman_service_type type)
 {
        DBG("type %d", type);
 
@@ -290,68 +161,29 @@ void __connman_notifier_enable(enum connman_service_type type)
                return;
        case CONNMAN_SERVICE_TYPE_ETHERNET:
        case CONNMAN_SERVICE_TYPE_WIFI:
-       case CONNMAN_SERVICE_TYPE_WIMAX:
        case CONNMAN_SERVICE_TYPE_BLUETOOTH:
        case CONNMAN_SERVICE_TYPE_CELLULAR:
                break;
        }
 
-       if (__sync_fetch_and_add(&enabled[type], 1) == 0)
-               technology_enabled(type, TRUE);
+       if (__sync_fetch_and_add(&connected[type], 1) == 0)
+               technology_connected(type, TRUE);
 }
 
-void __connman_notifier_disable(enum connman_service_type type)
+void __connman_notifier_enter_online(enum connman_service_type type)
 {
        DBG("type %d", type);
 
-       __sync_synchronize();
-       if (enabled[type] == 0) {
-               connman_error("notifier disable underflow");
-               return;
-       }
-
-       switch (type) {
-       case CONNMAN_SERVICE_TYPE_UNKNOWN:
-       case CONNMAN_SERVICE_TYPE_SYSTEM:
-       case CONNMAN_SERVICE_TYPE_GPS:
-       case CONNMAN_SERVICE_TYPE_VPN:
-       case CONNMAN_SERVICE_TYPE_GADGET:
-               return;
-       case CONNMAN_SERVICE_TYPE_ETHERNET:
-       case CONNMAN_SERVICE_TYPE_WIFI:
-       case CONNMAN_SERVICE_TYPE_WIMAX:
-       case CONNMAN_SERVICE_TYPE_BLUETOOTH:
-       case CONNMAN_SERVICE_TYPE_CELLULAR:
-               break;
-       }
-
-       if (__sync_fetch_and_sub(&enabled[type], 1) != 1)
-               return;
-
-       technology_enabled(type, FALSE);
+       if (__sync_fetch_and_add(&online[type], 1) == 0)
+               state_changed();
 }
 
-void __connman_notifier_connect(enum connman_service_type type)
+void __connman_notifier_leave_online(enum connman_service_type type)
 {
        DBG("type %d", type);
 
-       switch (type) {
-       case CONNMAN_SERVICE_TYPE_UNKNOWN:
-       case CONNMAN_SERVICE_TYPE_SYSTEM:
-       case CONNMAN_SERVICE_TYPE_GPS:
-       case CONNMAN_SERVICE_TYPE_VPN:
-       case CONNMAN_SERVICE_TYPE_GADGET:
-               return;
-       case CONNMAN_SERVICE_TYPE_ETHERNET:
-       case CONNMAN_SERVICE_TYPE_WIFI:
-       case CONNMAN_SERVICE_TYPE_WIMAX:
-       case CONNMAN_SERVICE_TYPE_BLUETOOTH:
-       case CONNMAN_SERVICE_TYPE_CELLULAR:
-               break;
-       }
-
-       if (__sync_fetch_and_add(&connected[type], 1) == 0)
-               technology_connected(type, TRUE);
+       if (__sync_fetch_and_sub(&online[type], 1) == 1)
+               state_changed();
 }
 
 void __connman_notifier_disconnect(enum connman_service_type type)
@@ -373,7 +205,6 @@ void __connman_notifier_disconnect(enum connman_service_type type)
                return;
        case CONNMAN_SERVICE_TYPE_ETHERNET:
        case CONNMAN_SERVICE_TYPE_WIFI:
-       case CONNMAN_SERVICE_TYPE_WIMAX:
        case CONNMAN_SERVICE_TYPE_BLUETOOTH:
        case CONNMAN_SERVICE_TYPE_CELLULAR:
                break;
@@ -385,36 +216,10 @@ void __connman_notifier_disconnect(enum connman_service_type type)
        technology_connected(type, FALSE);
 }
 
-static void technology_default(enum connman_service_type type)
-{
-       const char *str;
-
-       str = __connman_service_type2string(type);
-       if (str == NULL)
-               str = "";
-
-       connman_dbus_property_changed_basic(CONNMAN_MANAGER_PATH,
-                       CONNMAN_MANAGER_INTERFACE, "DefaultTechnology",
-                                               DBUS_TYPE_STRING, &str);
-}
-
 void __connman_notifier_default_changed(struct connman_service *service)
 {
-       enum connman_service_type type = connman_service_get_type(service);
-       char *interface;
        GSList *list;
 
-       technology_default(type);
-
-       interface = connman_service_get_interface(service);
-#if !defined TIZEN_EXT
-/*
- * Description: Tethering service is provided by external module in TIZEN
- */
-       __connman_tethering_update_interface(interface);
-#endif
-       g_free(interface);
-
        for (list = notifier_list; list; list = list->next) {
                struct connman_notifier *notifier = list->data;
 
@@ -487,6 +292,7 @@ void __connman_notifier_offlinemode(connman_bool_t enabled)
        DBG("enabled %d", enabled);
 
        offlinemode_changed(enabled);
+       state_changed();
 
        for (list = notifier_list; list; list = list->next) {
                struct connman_notifier *notifier = list->data;
@@ -510,36 +316,28 @@ static void notify_idle_state(connman_bool_t idle)
        }
 }
 
-#if defined TIZEN_EXT
-/*
- * August 22nd, 2011. TIZEN
- *
- * This part is added to send a DBus signal which means scan is completed
- * because scan UX of a Wi-Fi setting application has an active scan procedure
- * and it needs scan complete signal whether success or not
- */
-
-void __connman_notifier_scan_completed(connman_bool_t success)
-{
-       DBG("scan completed : %s", success == TRUE ? "success" : "failed");
-
-       connman_dbus_scan_completed_basic(CONNMAN_MANAGER_PATH,
-                               CONNMAN_MANAGER_INTERFACE, DBUS_TYPE_BOOLEAN, &success);
-}
-#endif
-
 void __connman_notifier_service_state_changed(struct connman_service *service,
                                        enum connman_service_state state)
 {
        GSList *list;
        unsigned int old_size;
        connman_bool_t found;
+#if defined TIZEN_EXT
+       GSList *next_list;
 
+       for (list = notifier_list; list;) {
+               next_list = list->next;
+#else
        for (list = notifier_list; list; list = list->next) {
+#endif
                struct connman_notifier *notifier = list->data;
 
                if (notifier->service_state_changed)
                        notifier->service_state_changed(service, state);
+
+#if defined TIZEN_EXT
+               list = next_list;
+#endif
        }
 
        old_size = g_hash_table_size(service_hash);
@@ -586,54 +384,6 @@ void __connman_notifier_ipconfig_changed(struct connman_service *service,
        }
 }
 
-static connman_bool_t technology_supported(enum connman_service_type type)
-{
-       switch (type) {
-       case CONNMAN_SERVICE_TYPE_UNKNOWN:
-       case CONNMAN_SERVICE_TYPE_SYSTEM:
-       case CONNMAN_SERVICE_TYPE_GPS:
-       case CONNMAN_SERVICE_TYPE_VPN:
-       case CONNMAN_SERVICE_TYPE_GADGET:
-               return FALSE;
-       case CONNMAN_SERVICE_TYPE_ETHERNET:
-       case CONNMAN_SERVICE_TYPE_WIFI:
-       case CONNMAN_SERVICE_TYPE_WIMAX:
-       case CONNMAN_SERVICE_TYPE_BLUETOOTH:
-       case CONNMAN_SERVICE_TYPE_CELLULAR:
-               break;
-       }
-
-       return TRUE;
-}
-
-connman_bool_t __connman_notifier_is_registered(enum connman_service_type type)
-{
-       DBG("type %d", type);
-
-       if (technology_supported(type) == FALSE)
-               return FALSE;
-
-       __sync_synchronize();
-       if (registered[type] > 0)
-               return TRUE;
-
-       return FALSE;
-}
-
-connman_bool_t __connman_notifier_is_enabled(enum connman_service_type type)
-{
-       DBG("type %d", type);
-
-       if (technology_supported(type) == FALSE)
-               return FALSE;
-
-       __sync_synchronize();
-       if (enabled[type] > 0)
-               return TRUE;
-
-       return FALSE;
-}
-
 int __connman_notifier_init(void)
 {
        DBG("");
@@ -643,6 +393,7 @@ int __connman_notifier_init(void)
        service_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal,
                                                NULL, NULL);
 
+       notifier_state = evaluate_notifier_state();
 
        return 0;
 }
index fa4f624..91a8832 100644 (file)
--- a/src/ntp.c
+++ b/src/ntp.c
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
@@ -37,8 +37,6 @@
 
 #include <glib.h>
 
-#include <gweb/gresolv.h>
-
 #include "connman.h"
 
 struct ntp_short {
@@ -71,10 +69,13 @@ struct ntp_msg {
 
 #define LOGTOD(a)  ((a) < 0 ? 1. / (1L << -(a)) : 1L << (int)(a))
 
+static guint channel_watch = 0;
 static struct timeval transmit_timeval;
-static char *transmit_server;
-static int transmit_fd;
-static guint transmit_delay = 16;
+static int transmit_fd = 0;
+
+static char *timeserver = NULL;
+static gint poll_id = 0;
+static gint timeout_id = 0;
 
 static void send_packet(int fd, const char *server)
 {
@@ -109,11 +110,24 @@ static void send_packet(int fd, const char *server)
        }
 }
 
-static gboolean next_request(gpointer user_data)
+static gboolean next_server(gpointer user_data)
+{
+       if (timeserver != NULL) {
+               g_free(timeserver);
+               timeserver = NULL;
+       }
+
+       __connman_timeserver_sync_next();
+
+       return FALSE;
+}
+
+static gboolean next_poll(gpointer user_data)
 {
-       DBG("server %s", transmit_server);
+       if (timeserver == NULL || transmit_fd == 0)
+               return FALSE;
 
-       send_packet(transmit_fd, transmit_server);
+       send_packet(transmit_fd, timeserver);
 
        return FALSE;
 }
@@ -123,6 +137,7 @@ static void decode_msg(void *base, size_t len, struct timeval *tv)
        struct ntp_msg *msg = base;
        double org, rec, xmt, dst;
        double delay, offset;
+       static guint transmit_delay;
 
        if (len < sizeof(*msg)) {
                connman_error("Invalid response from time server");
@@ -166,6 +181,23 @@ static void decode_msg(void *base, size_t len, struct timeval *tv)
 
        DBG("offset=%f delay=%f", offset, delay);
 
+       /* Remove the timeout, as timeserver has responded */
+       if (timeout_id > 0)
+               g_source_remove(timeout_id);
+
+       /*
+        * Now poll the server every transmit_delay seconds
+        * for time correction.
+        */
+       if (poll_id > 0)
+               g_source_remove(poll_id);
+
+       DBG("Timeserver %s, next sync in %d seconds", timeserver, transmit_delay);
+
+       poll_id = g_timeout_add_seconds(transmit_delay, next_poll, NULL);
+
+       connman_info("ntp: time slew %+.6f s", offset);
+
        if (offset < STEPTIME_MIN_OFFSET && offset > -STEPTIME_MIN_OFFSET) {
                struct timeval adj;
 
@@ -200,8 +232,6 @@ static void decode_msg(void *base, size_t len, struct timeval *tv)
        }
 }
 
-static guint channel_watch = 0;
-
 static gboolean received_data(GIOChannel *channel, GIOCondition condition,
                                                        gpointer user_data)
 {
@@ -250,24 +280,25 @@ static gboolean received_data(GIOChannel *channel, GIOCondition condition,
 
        decode_msg(iov.iov_base, iov.iov_len, tv);
 
-       g_timeout_add_seconds(transmit_delay, next_request, NULL);
-
        return TRUE;
 }
 
-static void start_ntp(const char *server)
+static void start_ntp(char *server)
 {
        GIOChannel *channel;
        struct sockaddr_in addr;
-       int fd, tos = IPTOS_LOWDELAY, timestamp = 1;
+       int tos = IPTOS_LOWDELAY, timestamp = 1;
+
+       if (server == NULL)
+               return;
 
        DBG("server %s", server);
 
        if (channel_watch > 0)
-               return;
+               goto send;
 
-       fd = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
-       if (fd < 0) {
+       transmit_fd = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+       if (transmit_fd < 0) {
                connman_error("Failed to open time server socket");
                return;
        }
@@ -275,28 +306,28 @@ static void start_ntp(const char *server)
        memset(&addr, 0, sizeof(addr));
        addr.sin_family = AF_INET;
 
-       if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+       if (bind(transmit_fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
                connman_error("Failed to bind time server socket");
-               close(fd);
+               close(transmit_fd);
                return;
        }
 
-       if (setsockopt(fd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) < 0) {
+       if (setsockopt(transmit_fd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) < 0) {
                connman_error("Failed to set type of service option");
-               close(fd);
+               close(transmit_fd);
                return;
        }
 
-       if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMP, &timestamp,
+       if (setsockopt(transmit_fd, SOL_SOCKET, SO_TIMESTAMP, &timestamp,
                                                sizeof(timestamp)) < 0) {
                connman_error("Failed to enable timestamp support");
-               close(fd);
+               close(transmit_fd);
                return;
        }
 
-       channel = g_io_channel_unix_new(fd);
+       channel = g_io_channel_unix_new(transmit_fd);
        if (channel == NULL) {
-               close(fd);
+               close(transmit_fd);
                return;
        }
 
@@ -311,72 +342,52 @@ static void start_ntp(const char *server)
 
        g_io_channel_unref(channel);
 
-       transmit_fd = fd;
-       transmit_server = g_strdup(server);
-
-       send_packet(fd, server);
+send:
+       send_packet(transmit_fd, server);
 }
 
-static void resolv_debug(const char *str, void *data)
+int __connman_ntp_start(char *server)
 {
-       connman_info("%s: %s\n", (const char *) data, str);
-}
+       DBG("%s", server);
 
-static GResolv *resolv = NULL;
-static guint resolv_lookup = 0;
+       if (server == NULL)
+               return -EINVAL;
 
-static void resolv_result(GResolvResultStatus status,
-                                       char **results, gpointer user_data)
-{
-       int i;
+       if (timeserver != NULL)
+               g_free(timeserver);
 
-       resolv_lookup = 0;
+       timeserver = g_strdup(server);
 
-       if (results != NULL) {
-               for (i = 0; results[i]; i++)
-                       DBG("result: %s", results[i]);
+       start_ntp(timeserver);
 
-               if (results[0] != NULL)
-                       start_ntp(results[0]);
-       }
-}
+       /*
+        * Add a fallback timeout , preferably short, 5 sec here,
+        * to fallback on the next server.
+        */
 
-int __connman_ntp_start(const char *interface, const char *resolver,
-                                                       const char *server)
-{
-       DBG("interface %s server %s", interface, server);
-
-       resolv = g_resolv_new(0);
-       if (resolv == NULL)
-               return -ENOMEM;
-
-       if (getenv("CONNMAN_RESOLV_DEBUG"))
-               g_resolv_set_debug(resolv, resolv_debug, "RESOLV");
-
-       if (resolver != NULL)
-               g_resolv_add_nameserver(resolv, resolver, 53, 0);
-
-       g_resolv_lookup_hostname(resolv, server, resolv_result, NULL);
+       timeout_id = g_timeout_add_seconds(5, next_server, NULL);
 
        return 0;
 }
 
-void __connman_ntp_stop(const char *interface)
+void __connman_ntp_stop()
 {
-       DBG("interface %s", interface);
+       DBG("");
 
-       if (resolv == NULL)
-               return;
+       if (poll_id > 0)
+               g_source_remove(poll_id);
 
-       if (resolv_lookup > 0) {
-               g_resolv_cancel_lookup(resolv, resolv_lookup);
-               resolv_lookup = 0;
-       }
+       if (timeout_id > 0)
+               g_source_remove(timeout_id);
 
        if (channel_watch > 0) {
                g_source_remove(channel_watch);
                channel_watch = 0;
+               transmit_fd = 0;
        }
 
-       g_resolv_unref(resolv);
+       if (timeserver != NULL) {
+               g_free(timeserver);
+               timeserver = NULL;
+       }
 }
index afd97c0..adf8525 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
@@ -114,7 +114,7 @@ static gboolean check_plugin(struct connman_plugin_desc *desc,
        return TRUE;
 }
 
-#include "builtin.h"
+#include <builtin.h>
 
 int __connman_plugin_init(const char *pattern, const char *exclude)
 {
index 0d4291b..d862260 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
@@ -58,6 +58,9 @@ struct connman_provider {
        struct connman_provider_driver *driver;
        void *driver_data;
        GHashTable *setting_strings;
+       GHashTable *user_routes;
+       gchar **user_networks;
+       gsize num_user_networks;
 };
 
 void __connman_provider_append_properties(struct connman_provider *provider,
@@ -76,20 +79,101 @@ void __connman_provider_append_properties(struct connman_provider *provider,
                                                 &provider->type);
 }
 
-static int connman_provider_load(struct connman_provider *provider)
+int __connman_provider_append_user_route(struct connman_provider *provider,
+                       int family, const char *network, const char *netmask)
+{
+       struct connman_route *route;
+       char *key = g_strdup_printf("%d/%s/%s", family, network, netmask);
+
+       DBG("family %d network %s netmask %s", family, network, netmask);
+
+       route = g_hash_table_lookup(provider->user_routes, key);
+       if (route == NULL) {
+               route = g_try_new0(struct connman_route, 1);
+               if (route == NULL) {
+                       connman_error("out of memory");
+                       return -ENOMEM;
+               }
+
+               route->family = family;
+               route->host = g_strdup(network);
+               route->netmask = g_strdup(netmask);
+
+               g_hash_table_replace(provider->user_routes, key, route);
+       } else
+               g_free(key);
+
+       return 0;
+}
+
+static void set_user_networks(struct connman_provider *provider,
+                                                       char **networks)
+{
+       int i = 0;
+
+       while (networks[i] != NULL) {
+               char **elems = g_strsplit(networks[i], "/", 0);
+               char *network, *netmask;
+               int family = PF_UNSPEC, ret;
+
+               if (elems == NULL)
+                       break;
+
+               network = elems[0];
+               if (network == NULL || *network == '\0') {
+                       DBG("no network/netmask set");
+                       g_strfreev(elems);
+                       break;
+               }
+
+               netmask = elems[1];
+               if (netmask != NULL && *netmask == '\0') {
+                       DBG("no netmask set");
+                       g_strfreev(elems);
+                       break;
+               }
+
+               if (g_strrstr(network, ":") != NULL)
+                       family = AF_INET6;
+               else if (g_strrstr(network, ".") != NULL) {
+                       family = AF_INET;
+
+                       if (g_strrstr(netmask, ".") == NULL) {
+                               /* We have netmask length */
+                               in_addr_t addr;
+                               struct in_addr netmask_in;
+                               unsigned char prefix_len = 32;
+
+                               if (netmask != NULL)
+                                       prefix_len = atoi(netmask);
+
+                               addr = 0xffffffff << (32 - prefix_len);
+                               netmask_in.s_addr = htonl(addr);
+                               netmask = inet_ntoa(netmask_in);
+
+                               DBG("network %s netmask %s", network, netmask);
+                       }
+               }
+
+               ret = __connman_provider_append_user_route(provider,
+                                               family, network, netmask);
+               g_strfreev(elems);
+
+               if (ret != 0)
+                       break;
+
+               i++;
+       }
+}
+
+static int provider_load_from_keyfile(struct connman_provider *provider,
+               GKeyFile *keyfile)
 {
        gsize idx = 0;
-       GKeyFile *keyfile;
        gchar **settings;
        gchar *key, *value;
        gsize length;
 
-       DBG("provider %p", provider);
-
-       keyfile = __connman_storage_load_provider(provider->identifier);
-       if (keyfile == NULL)
-               return -ENOENT;
-
        settings = g_key_file_get_keys(keyfile, provider->identifier, &length,
                                NULL);
        if (settings == NULL) {
@@ -99,18 +183,46 @@ static int connman_provider_load(struct connman_provider *provider)
 
        while (idx < length) {
                key = settings[idx];
-               DBG("found key %s", key);
                if (key != NULL) {
-                       value = g_key_file_get_string(keyfile,
+                       if (g_str_equal(key, "Networks") == TRUE) {
+                               g_strfreev(provider->user_networks);
+                               provider->user_networks =
+                                       g_key_file_get_string_list(keyfile,
                                                provider->identifier,
-                                               key, NULL);
-                       connman_provider_set_string(provider, key, value);
-                       g_free(value);
+                                               key,
+                                               &provider->num_user_networks,
+                                               NULL);
+                       } else {
+                               value = g_key_file_get_string(keyfile,
+                                                       provider->identifier,
+                                                       key, NULL);
+                               connman_provider_set_string(provider, key,
+                                                       value);
+                               g_free(value);
+                       }
                }
                idx += 1;
        }
        g_strfreev(settings);
 
+       if (provider->user_networks != NULL)
+               set_user_networks(provider, provider->user_networks);
+
+       return 0;
+}
+
+static int connman_provider_load(struct connman_provider *provider)
+{
+       GKeyFile *keyfile;
+
+       DBG("provider %p", provider);
+
+       keyfile = __connman_storage_load_provider(provider->identifier);
+       if (keyfile == NULL)
+               return -ENOENT;
+
+       provider_load_from_keyfile(provider, keyfile);
+
        g_key_file_free(keyfile);
        return 0;
 }
@@ -133,6 +245,11 @@ static int connman_provider_save(struct connman_provider *provider)
                        "Host", provider->host);
        g_key_file_set_string(keyfile, provider->identifier,
                        "VPN.Domain", provider->domain);
+       if (provider->user_networks != NULL)
+               g_key_file_set_string_list(keyfile, provider->identifier,
+                               "Networks",
+                               (const gchar **)provider->user_networks,
+                               provider->num_user_networks);
 
        if (provider->driver != NULL && provider->driver->save != NULL)
                provider->driver->save(provider, keyfile);
@@ -200,7 +317,6 @@ static void provider_remove(struct connman_provider *provider)
 
 static int provider_register(struct connman_provider *provider)
 {
-       connman_provider_load(provider);
        return provider_probe(provider);
 }
 
@@ -209,9 +325,12 @@ static void provider_unregister(struct connman_provider *provider)
        provider_remove(provider);
 }
 
-struct connman_provider *connman_provider_ref(struct connman_provider *provider)
+struct connman_provider *
+connman_provider_ref_debug(struct connman_provider *provider,
+                       const char *file, int line, const char *caller)
 {
-       DBG("provider %p refcount %d", provider, provider->refcount + 1);
+       DBG("%p ref %d by %s:%d:%s()", provider, provider->refcount + 1,
+               file, line, caller);
 
        __sync_fetch_and_add(&provider->refcount, 1);
 
@@ -227,13 +346,18 @@ static void provider_destruct(struct connman_provider *provider)
        g_free(provider->host);
        g_free(provider->domain);
        g_free(provider->identifier);
+       g_strfreev(provider->user_networks);
        g_hash_table_destroy(provider->routes);
+       g_hash_table_destroy(provider->user_routes);
        g_hash_table_destroy(provider->setting_strings);
+       g_free(provider);
 }
 
-void connman_provider_unref(struct connman_provider *provider)
+void connman_provider_unref_debug(struct connman_provider *provider,
+                               const char *file, int line, const char *caller)
 {
-       DBG("provider %p refcount %d", provider, provider->refcount - 1);
+       DBG("%p ref %d by %s:%d:%s()", provider, provider->refcount - 1,
+               file, line, caller);
 
        if (__sync_fetch_and_sub(&provider->refcount, 1) != 1)
                return;
@@ -255,7 +379,7 @@ static int provider_indicate_state(struct connman_provider *provider,
                                        state, CONNMAN_IPCONFIG_TYPE_IPV6);
 }
 
-int __connman_provider_disconnect(struct connman_provider *provider)
+int connman_provider_disconnect(struct connman_provider *provider)
 {
        int err;
 
@@ -375,9 +499,6 @@ static int set_connected(struct connman_provider *provider,
 
                __connman_ipconfig_address_add(ipconfig);
 #if defined TIZEN_EXT
-/*
- * Description: __connman_service_lookup_from_index cannot find correct service
- */
                __connman_ipconfig_gateway_add(ipconfig, service);
 #else
                __connman_ipconfig_gateway_add(ipconfig);
@@ -389,6 +510,9 @@ static int set_connected(struct connman_provider *provider,
                g_hash_table_foreach(provider->routes, provider_append_routes,
                                        provider);
 
+               g_hash_table_foreach(provider->user_routes, provider_append_routes,
+                                       provider);
+
        } else {
                if (ipconfig != NULL) {
                        provider_indicate_state(provider,
@@ -434,8 +558,6 @@ int connman_provider_indicate_error(struct connman_provider *provider,
                                        enum connman_provider_error error)
 {
        enum connman_service_error service_error;
-       const char *path;
-       int ret;
 
        switch (error) {
        case CONNMAN_PROVIDER_ERROR_LOGIN_FAILED:
@@ -452,23 +574,20 @@ int connman_provider_indicate_error(struct connman_provider *provider,
                break;
        }
 
-       ret = __connman_service_indicate_error(provider->vpn_service,
+       return __connman_service_indicate_error(provider->vpn_service,
                                                        service_error);
-       path = __connman_service_get_path(provider->vpn_service);
-       __connman_provider_remove(path);
-
-       return ret;
 }
 
 static void unregister_provider(gpointer data)
 {
        struct connman_provider *provider = data;
-       struct connman_service *service = provider->vpn_service;
 
-       DBG("provider %p", provider);
+       DBG("provider %p service %p", provider, provider->vpn_service);
 
-       provider->vpn_service = NULL;
-       __connman_service_put(service);
+       if (provider->vpn_service != NULL) {
+               connman_service_unref(provider->vpn_service);
+               provider->vpn_service = NULL;
+       }
 
        connman_provider_unref(provider);
 }
@@ -492,8 +611,11 @@ static void provider_initialize(struct connman_provider *provider)
        provider->type = NULL;
        provider->domain = NULL;
        provider->identifier = NULL;
+       provider->user_networks = NULL;
        provider->routes = g_hash_table_new_full(g_direct_hash, g_direct_equal,
                                        NULL, destroy_route);
+       provider->user_routes = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                       g_free, destroy_route);
        provider->setting_strings = g_hash_table_new_full(g_str_hash, g_str_equal,
                                                        g_free, g_free);
 }
@@ -532,8 +654,6 @@ static struct connman_provider *connman_provider_get(const char *identifier)
 
        g_hash_table_insert(provider_hash, provider->identifier, provider);
 
-       provider->name = g_strdup(identifier);
-
        return provider;
 }
 
@@ -552,14 +672,145 @@ static void provider_dbus_ident(char *ident)
        }
 }
 
+static struct connman_provider *provider_create_from_keyfile(GKeyFile *keyfile,
+               const char *ident)
+{
+       struct connman_provider *provider;
+
+       if (keyfile == NULL || ident == NULL)
+               return NULL;
+
+       provider = connman_provider_lookup(ident);
+       if (provider == NULL) {
+               provider = connman_provider_get(ident);
+               if (provider == NULL) {
+                       DBG("can not create provider");
+                       return NULL;
+               }
+
+               provider_load_from_keyfile(provider, keyfile);
+
+               if (provider->name == NULL || provider->host == NULL ||
+                               provider->domain == NULL) {
+                       DBG("cannot get name, host or domain");
+                       connman_provider_unref(provider);
+                       return NULL;
+               }
+
+               provider_register(provider);
+       }
+       return provider;
+}
+
+static int provider_create_service(struct connman_provider *provider)
+{
+       if (provider->vpn_service != NULL)
+               return -EALREADY;
+
+       provider->vpn_service =
+               __connman_service_create_from_provider(provider);
+
+       if (provider->vpn_service == NULL)
+               return -EOPNOTSUPP;
+
+       return 0;
+}
+
+static void provider_create_all_from_type(const char *provider_type)
+{
+       unsigned int i;
+       char **providers;
+       char *id, *type;
+       GKeyFile *keyfile;
+       struct connman_provider *provider;
+
+       DBG("provider type %s", provider_type);
+
+       providers = __connman_storage_get_providers();
+       if (providers == NULL)
+               return;
+
+       for (i = 0; providers[i] != NULL; i+=1) {
+
+               if (strncmp(providers[i], "provider_", 9) != 0)
+                       continue;
+
+               id = providers[i] + 9;
+               keyfile = __connman_storage_load_provider(id);
+
+               if (keyfile == NULL)
+                       continue;
+
+               type = g_key_file_get_string(keyfile, id, "Type", NULL);
+
+               DBG("keyfile %p id %s type %s", keyfile, id, type);
+
+               if (strcmp(provider_type, type) != 0) {
+                       g_free(type);
+                       g_key_file_free(keyfile);
+                       continue;
+               }
+
+               provider = provider_create_from_keyfile(keyfile, id);
+               if (provider != NULL) {
+                       if (provider_create_service(provider) == -EOPNOTSUPP) {
+                               DBG("could not create service");
+                               connman_provider_unref(provider);
+                       }
+               }
+
+               g_free(type);
+               g_key_file_free(keyfile);
+       }
+       g_strfreev(providers);
+}
+
+static char **get_user_networks(DBusMessageIter *array, int *count)
+{
+       DBusMessageIter entry;
+       char **networks = NULL;
+       GSList *list = NULL, *l;
+       int len;
+
+       dbus_message_iter_recurse(array, &entry);
+
+       while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING) {
+               const char *val;
+               dbus_message_iter_get_basic(&entry, &val);
+
+               list = g_slist_prepend(list, g_strdup(val));
+               dbus_message_iter_next(&entry);
+       }
+
+       len = g_slist_length(list);
+       if (len == 0)
+               goto out;
+
+       networks = g_try_new(char *, len + 1);
+       if (networks == NULL)
+               goto out;
+
+       *count = len;
+       networks[len] = 0;
+
+       for (l = list; l != NULL; l = g_slist_next(l))
+               networks[--len] = l->data;
+
+out:
+       g_slist_free(list);
+
+       return networks;
+}
+
 int __connman_provider_create_and_connect(DBusMessage *msg)
 {
        struct connman_provider *provider;
        DBusMessageIter iter, array;
        const char *type = NULL, *name = NULL, *service_path;
        const char *host = NULL, *domain = NULL;
+       char **networks = NULL;
        char *ident;
-       int err;
+       int err, count = 0;
 
        dbus_message_iter_init(msg, &iter);
        dbus_message_iter_recurse(&iter, &array);
@@ -585,6 +836,10 @@ int __connman_provider_create_and_connect(DBusMessage *msg)
                        else if (g_str_equal(key, "VPN.Domain") == TRUE)
                                dbus_message_iter_get_basic(&value, &domain);
                        break;
+               case DBUS_TYPE_ARRAY:
+                       if (g_str_equal(key, "Networks") == TRUE)
+                               networks = get_user_networks(&value, &count);
+                       break;
                }
 
                dbus_message_iter_next(&array);
@@ -593,7 +848,7 @@ int __connman_provider_create_and_connect(DBusMessage *msg)
        if (host == NULL || domain == NULL)
                return -EINVAL;
 
-       DBG("Type %s name %s", type, name);
+       DBG("Type %s name %s networks %p", type, name, networks);
 
        if (type == NULL || name == NULL)
                return -EOPNOTSUPP;
@@ -614,11 +869,18 @@ int __connman_provider_create_and_connect(DBusMessage *msg)
 
                provider->host = g_strdup(host);
                provider->domain = g_strdup(domain);
-               g_free(provider->name);
                provider->name = g_strdup(name);
                provider->type = g_strdup(type);
 
-               provider_register(provider);
+               if (provider_register(provider) == 0)
+                       connman_provider_load(provider);
+       }
+
+       if (networks != NULL) {
+               g_strfreev(provider->user_networks);
+               provider->user_networks = networks;
+               provider->num_user_networks = count;
+               set_user_networks(provider, provider->user_networks);
        }
 
        dbus_message_iter_init(msg, &iter);
@@ -646,19 +908,19 @@ int __connman_provider_create_and_connect(DBusMessage *msg)
 
        g_free(ident);
 
-       if (provider->vpn_service == NULL) {
-               provider->vpn_service =
-                       __connman_service_create_from_provider(provider);
-               if (provider->vpn_service == NULL) {
-                       err = -EOPNOTSUPP;
+       err = provider_create_service(provider);
+       if (err == -EALREADY) {
+               DBG("provider already connected");
+       } else {
+               if (err == -EOPNOTSUPP) {
                        goto unref;
-               }
+               } else {
+                       err = __connman_service_connect(provider->vpn_service);
 
-               err = __connman_service_connect(provider->vpn_service);
-               if (err < 0 && err != -EINPROGRESS)
-                       goto failed;
-       } else
-               DBG("provider already connected");
+                       if (err < 0 && err != -EINPROGRESS)
+                               goto failed;
+               }
+       }
 
        connman_provider_save(provider);
        service_path = __connman_service_get_path(provider->vpn_service);
@@ -668,13 +930,13 @@ int __connman_provider_create_and_connect(DBusMessage *msg)
        return 0;
 
 failed:
-       __connman_service_put(provider->vpn_service);
+       connman_service_unref(provider->vpn_service);
        provider->vpn_service = NULL;
 
 unref:
        DBG("can not connect, delete provider");
 
-       connman_provider_unref(provider);
+       g_hash_table_remove(provider_hash, provider->identifier);
 
        return err;
 }
@@ -727,6 +989,23 @@ const char *connman_provider_get_string(struct connman_provider *provider,
        return g_hash_table_lookup(provider->setting_strings, key);
 }
 
+connman_bool_t
+__connman_provider_check_routes(struct connman_provider *provider)
+{
+       if (provider == NULL)
+               return FALSE;
+
+       if (provider->user_routes != NULL &&
+                       g_hash_table_size(provider->user_routes) > 0)
+               return TRUE;
+
+       if (provider->routes != NULL &&
+                       g_hash_table_size(provider->routes) > 0)
+               return TRUE;
+
+       return FALSE;
+}
+
 void *connman_provider_get_data(struct connman_provider *provider)
 {
        return provider->driver_data;
@@ -759,7 +1038,7 @@ void connman_provider_set_index(struct connman_provider *provider, int index)
                }
        }
 
-       connman_ipconfig_set_method(ipconfig, CONNMAN_IPCONFIG_METHOD_FIXED);
+       __connman_ipconfig_set_method(ipconfig, CONNMAN_IPCONFIG_METHOD_FIXED);
        __connman_ipconfig_set_index(ipconfig, index);
 
 
@@ -775,7 +1054,7 @@ void connman_provider_set_index(struct connman_provider *provider, int index)
                }
        }
 
-       connman_ipconfig_set_method(ipconfig, CONNMAN_IPCONFIG_METHOD_OFF);
+       __connman_ipconfig_set_method(ipconfig, CONNMAN_IPCONFIG_METHOD_OFF);
        __connman_ipconfig_set_index(ipconfig, index);
 
 done:
@@ -993,6 +1272,7 @@ int connman_provider_driver_register(struct connman_provider_driver *driver)
 
        driver_list = g_slist_insert_sorted(driver_list, driver,
                                                        compare_priority);
+       provider_create_all_from_type(driver->name);
        return 0;
 }
 
@@ -1020,9 +1300,71 @@ static void provider_offline_mode(connman_bool_t enabled)
 
 }
 
+static struct connman_provider *provider_get(int index)
+{
+       GHashTableIter iter;
+       gpointer value, key;
+
+       g_hash_table_iter_init(&iter, provider_hash);
+
+       while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
+               struct connman_provider *provider = value;
+
+               if (provider->index == index)
+                       return provider;
+       }
+
+       return NULL;
+}
+
+static void provider_service_changed(struct connman_service *service,
+                               enum connman_service_state state)
+{
+       struct connman_provider *provider;
+       int vpn_index, service_index;
+
+       if (service == NULL)
+               return;
+
+       switch (state) {
+       case CONNMAN_SERVICE_STATE_UNKNOWN:
+       case CONNMAN_SERVICE_STATE_IDLE:
+       case CONNMAN_SERVICE_STATE_ASSOCIATION:
+       case CONNMAN_SERVICE_STATE_CONFIGURATION:
+       case CONNMAN_SERVICE_STATE_READY:
+       case CONNMAN_SERVICE_STATE_ONLINE:
+               return;
+       case CONNMAN_SERVICE_STATE_DISCONNECT:
+       case CONNMAN_SERVICE_STATE_FAILURE:
+               break;
+       }
+
+       service_index = __connman_service_get_index(service);
+
+       vpn_index = __connman_connection_get_vpn_index(service_index);
+
+       DBG("service %p %s state %d index %d/%d", service,
+               __connman_service_get_ident(service),
+               state, service_index, vpn_index);
+
+       if (vpn_index < 0)
+               return;
+
+       provider = provider_get(vpn_index);
+       if (provider == NULL)
+               return;
+
+       DBG("disconnect %p index %d", provider, vpn_index);
+
+       connman_provider_disconnect(provider);
+
+       return;
+}
+
 static struct connman_notifier provider_notifier = {
        .name                   = "provider",
        .offline_mode           = provider_offline_mode,
+       .service_state_changed  = provider_service_changed,
 };
 
 int __connman_provider_init(void)
index a83837b..b6775b6 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
@@ -46,6 +46,7 @@ static void remove_lookup(struct proxy_lookup *lookup)
 {
        lookup_list = g_slist_remove(lookup_list, lookup);
 
+       connman_service_unref(lookup->service);
        g_free(lookup->url);
        g_free(lookup);
 }
@@ -117,7 +118,7 @@ unsigned int connman_proxy_lookup(const char *interface, const char *url,
        lookup->cb = cb;
        lookup->user_data = user_data;
        lookup->url = g_strdup(url);
-       lookup->service = service;
+       lookup->service = connman_service_ref(service);
 
        lookup->watch = g_timeout_add_seconds(0, lookup_callback, lookup);
        if (lookup->watch == 0) {
@@ -127,7 +128,7 @@ unsigned int connman_proxy_lookup(const char *interface, const char *url,
        }
 
        DBG("token %u", lookup->token);
-       lookup_list = g_slist_append(lookup_list, lookup);
+       lookup_list = g_slist_prepend(lookup_list, lookup);
 
        return lookup->token;
 }
@@ -144,6 +145,8 @@ void connman_proxy_lookup_cancel(unsigned int token)
 
                if (lookup->token == token)
                        break;
+
+               lookup = NULL;
        }
 
        if (lookup != NULL) {
@@ -178,7 +181,7 @@ void connman_proxy_driver_lookup_notify(struct connman_service *service,
                        if (lookup->cb)
                                lookup->cb(result, lookup->user_data);
 
-                       matches = g_slist_append(matches, lookup);
+                       matches = g_slist_prepend(matches, lookup);
                }
        }
 
index 4d836e1..b965778 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
 #include <string.h>
 #include <sys/stat.h>
 #include <resolv.h>
+#include <netdb.h>
 
 #include "connman.h"
 
 #define RESOLVER_FLAG_PUBLIC (1 << 0)
 
+/*
+ * Threshold for RDNSS lifetime. Will be used to trigger RS
+ * before RDNSS entries actually expire
+ */
+#define RESOLVER_LIFETIME_REFRESH_THRESHOLD 0.8
+
 struct entry_data {
-       char *interface;
+       int index;
        char *domain;
        char *server;
+       int family;
        unsigned int flags;
+       unsigned int lifetime;
        guint timeout;
 };
 
@@ -48,7 +57,7 @@ static GSList *entry_list = NULL;
 static connman_bool_t dnsproxy_enabled = FALSE;
 
 struct resolvfile_entry {
-       char *interface;
+       int index;
        char *domain;
        char *server;
 };
@@ -66,7 +75,6 @@ static void resolvfile_remove_entries(GList *entries)
 
                g_free(entry->server);
                g_free(entry->domain);
-               g_free(entry->interface);
                g_free(entry);
        }
 
@@ -122,17 +130,8 @@ static int resolvfile_export(void)
 
        old_umask = umask(022);
 
-#if defined TIZEN_EXT
-/*
- * Description: /etc path is read-only in SLP
- *              /opt/etc/resolv.conf rather than /etc/resolv.conf
- */
-       fd = open("/opt/etc/resolv.conf", O_RDWR | O_CREAT | O_CLOEXEC,
-                                       S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
-#else
        fd = open("/etc/resolv.conf", O_RDWR | O_CREAT | O_CLOEXEC,
                                        S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
-#endif
        if (fd < 0) {
                err = -errno;
                goto done;
@@ -158,21 +157,21 @@ done:
        return err;
 }
 
-int __connman_resolvfile_append(const char *interface, const char *domain,
+int __connman_resolvfile_append(int index, const char *domain,
                                                        const char *server)
 {
        struct resolvfile_entry *entry;
 
-       DBG("interface %s server %s", interface, server);
+       DBG("index %d server %s", index, server);
 
-       if (interface == NULL)
+       if (index < 0)
                return -ENOENT;
 
        entry = g_try_new0(struct resolvfile_entry, 1);
        if (entry == NULL)
                return -ENOMEM;
 
-       entry->interface = g_strdup(interface);
+       entry->index = index;
        entry->domain = g_strdup(domain);
        entry->server = g_strdup(server);
 
@@ -181,18 +180,17 @@ int __connman_resolvfile_append(const char *interface, const char *domain,
        return resolvfile_export();
 }
 
-int __connman_resolvfile_remove(const char *interface, const char *domain,
+int __connman_resolvfile_remove(int index, const char *domain,
                                                        const char *server)
 {
        GList *list, *matches = NULL;
 
-       DBG("interface %s server %s", interface, server);
+       DBG("index %d server %s", index, server);
 
        for (list = resolvfile_list; list; list = g_list_next(list)) {
                struct resolvfile_entry *entry = list->data;
 
-               if (interface != NULL &&
-                               g_strcmp0(entry->interface, interface) != 0)
+               if (index >= 0 && entry->index != index)
                        continue;
 
                if (domain != NULL && g_strcmp0(entry->domain, domain) != 0)
@@ -219,10 +217,10 @@ static void remove_entries(GSList *entries)
                entry_list = g_slist_remove(entry_list, entry);
 
                if (dnsproxy_enabled == TRUE) {
-                       __connman_dnsproxy_remove(entry->interface, entry->domain,
+                       __connman_dnsproxy_remove(entry->index, entry->domain,
                                                        entry->server);
                } else {
-                       __connman_resolvfile_remove(entry->interface, entry->domain,
+                       __connman_resolvfile_remove(entry->index, entry->domain,
                                                        entry->server);
                }
 
@@ -230,7 +228,6 @@ static void remove_entries(GSList *entries)
                        g_source_remove(entry->timeout);
                g_free(entry->server);
                g_free(entry->domain);
-               g_free(entry->interface);
                g_free(entry);
        }
 
@@ -241,17 +238,15 @@ static gboolean resolver_expire_cb(gpointer user_data)
 {
        struct entry_data *entry = user_data;
        GSList *list;
-       int index;
 
-       DBG("interface %s domain %s server %s",
-                       entry->interface, entry->domain, entry->server);
+       DBG("index %d domain %s server %s",
+                       entry->index, entry->domain, entry->server);
 
-       list = g_slist_append(NULL, entry);
+       list = g_slist_prepend(NULL, entry);
 
-       index = connman_inet_ifindex(entry->interface);
-       if (index >= 0) {
+       if (entry->index >= 0) {
                struct connman_service *service;
-               service = __connman_service_lookup_from_index(index);
+               service = __connman_service_lookup_from_index(entry->index);
                if (service != NULL)
                        __connman_service_nameserver_remove(service,
                                                        entry->server, TRUE);
@@ -262,14 +257,48 @@ static gboolean resolver_expire_cb(gpointer user_data)
        return FALSE;
 }
 
-static int append_resolver(const char *interface, const char *domain,
+static gboolean resolver_refresh_cb(gpointer user_data)
+{
+       struct entry_data *entry = user_data;
+       unsigned int interval;
+       struct connman_service *service = NULL;
+
+       /* Round up what we have left from lifetime */
+       interval = entry->lifetime *
+               (1 - RESOLVER_LIFETIME_REFRESH_THRESHOLD) + 1.0;
+
+       DBG("RDNSS start index %d domain %s "
+                       "server %s remaining lifetime %d",
+                       entry->index, entry->domain,
+                       entry->server, interval);
+
+       entry->timeout = g_timeout_add_seconds(interval,
+                       resolver_expire_cb, entry);
+
+       if (entry->index >= 0) {
+               service = __connman_service_lookup_from_index(entry->index);
+               if (service != NULL) {
+                       /*
+                        * Send Router Solicitation to refresh RDNSS entries
+                        * before their lifetime expires
+                        */
+                       __connman_refresh_rs_ipv6(
+                                       __connman_service_get_network(service),
+                                       entry->index);
+               }
+       }
+       return FALSE;
+}
+
+static int append_resolver(int index, const char *domain,
                                const char *server, unsigned int lifetime,
                                                        unsigned int flags)
 {
        struct entry_data *entry;
+       unsigned int interval;
 
-       DBG("interface %s domain %s server %s lifetime %d flags %d",
-                               interface, domain, server, lifetime, flags);
+       DBG("index %d domain %s server %s lifetime %d flags %d",
+                               index, domain, server, lifetime, flags);
 
        if (server == NULL && domain == NULL)
                return -EINVAL;
@@ -278,23 +307,32 @@ static int append_resolver(const char *interface, const char *domain,
        if (entry == NULL)
                return -ENOMEM;
 
-       entry->interface = g_strdup(interface);
+       entry->index = index;
        entry->domain = g_strdup(domain);
        entry->server = g_strdup(server);
        entry->flags = flags;
+       entry->lifetime = lifetime;
+
+       if (server != NULL)
+               entry->family = connman_inet_check_ipaddress(server);
+
        if (lifetime) {
-               int index;
-               entry->timeout = g_timeout_add_seconds(lifetime,
-                                               resolver_expire_cb, entry);
+               interval = lifetime * RESOLVER_LIFETIME_REFRESH_THRESHOLD;
+
+               DBG("RDNSS start index %d domain %s "
+                               "server %s lifetime threshold %d",
+                               index, domain, server, interval);
+
+               entry->timeout = g_timeout_add_seconds(interval,
+                               resolver_refresh_cb, entry);
 
                /*
                 * We update the service only for those nameservers
                 * that are automagically added via netlink (lifetime > 0)
                 */
-               index = connman_inet_ifindex(interface);
-               if (index >= 0) {
+               if (server != NULL && entry->index >= 0) {
                        struct connman_service *service;
-                       service = __connman_service_lookup_from_index(index);
+                       service = __connman_service_lookup_from_index(entry->index);
                        if (service != NULL)
                                __connman_service_nameserver_append(service,
                                                                server, TRUE);
@@ -303,27 +341,27 @@ static int append_resolver(const char *interface, const char *domain,
        entry_list = g_slist_append(entry_list, entry);
 
        if (dnsproxy_enabled == TRUE)
-               __connman_dnsproxy_append(interface, domain, server);
+               __connman_dnsproxy_append(entry->index, domain, server);
        else
-               __connman_resolvfile_append(interface, domain, server);
+               __connman_resolvfile_append(entry->index, domain, server);
 
        return 0;
 }
 
 /**
  * connman_resolver_append:
- * @interface: network interface
+ * @index: network interface index
  * @domain: domain limitation
  * @server: server address
  *
  * Append resolver server address to current list
  */
-int connman_resolver_append(const char *interface, const char *domain,
+int connman_resolver_append(int index, const char *domain,
                                                const char *server)
 {
-       GSList *list, *matches = NULL;
+       GSList *list;
 
-       DBG("interface %s domain %s server %s", interface, domain, server);
+       DBG("index %d domain %s server %s", index, domain, server);
 
        if (server == NULL && domain == NULL)
                return -EINVAL;
@@ -331,48 +369,46 @@ int connman_resolver_append(const char *interface, const char *domain,
        for (list = entry_list; list; list = list->next) {
                struct entry_data *entry = list->data;
 
-               if (entry->timeout > 0 ||
-                               g_strcmp0(entry->interface, interface) != 0 ||
-                               g_strcmp0(entry->domain, domain) != 0 ||
-                               g_strcmp0(entry->server, server) != 0)
+               if (entry->timeout > 0)
                        continue;
 
-               matches = g_slist_append(matches, entry);
+               if (entry->index == index &&
+                               g_strcmp0(entry->domain, domain) == 0 &&
+                               g_strcmp0(entry->server, server) == 0)
+                       return -EEXIST;
        }
 
-       if (matches != NULL)
-               remove_entries(matches);
-
-       return append_resolver(interface, domain, server, 0, 0);
+       return append_resolver(index, domain, server, 0, 0);
 }
 
 /**
  * connman_resolver_append_lifetime:
- * @interface: network interface
+ * @index: network interface index
  * @domain: domain limitation
  * @server: server address
  * @timeout: server lifetime in seconds
  *
  * Append resolver server address to current list
  */
-int connman_resolver_append_lifetime(const char *interface, const char *domain,
+int connman_resolver_append_lifetime(int index, const char *domain,
                                const char *server, unsigned int lifetime)
 {
        GSList *list;
+       unsigned int interval;
 
-       DBG("interface %s domain %s server %s lifetime %d",
-                               interface, domain, server, lifetime);
+       DBG("index %d domain %s server %s lifetime %d",
+                               index, domain, server, lifetime);
 
-       if (server == NULL)
+       if (server == NULL && domain == NULL)
                return -EINVAL;
 
        for (list = entry_list; list; list = list->next) {
                struct entry_data *entry = list->data;
 
-               if (!entry->timeout ||
-                               g_strcmp0(entry->interface, interface) ||
-                               g_strcmp0(entry->domain, domain) ||
-                               g_strcmp0(entry->server, server))
+               if (entry->timeout == 0 ||
+                               entry->index != index ||
+                               g_strcmp0(entry->domain, domain) != 0 ||
+                               g_strcmp0(entry->server, server) != 0)
                        continue;
 
                g_source_remove(entry->timeout);
@@ -382,46 +418,48 @@ int connman_resolver_append_lifetime(const char *interface, const char *domain,
                        return 0;
                }
 
-               entry->timeout = g_timeout_add_seconds(lifetime,
-                                               resolver_expire_cb, entry);
+               interval = lifetime * RESOLVER_LIFETIME_REFRESH_THRESHOLD;
+
+               DBG("RDNSS start index %d domain %s "
+                               "server %s lifetime threshold %d",
+                               index, domain, server, interval);
+
+               entry->timeout = g_timeout_add_seconds(interval,
+                               resolver_refresh_cb, entry);
                return 0;
        }
 
-       return append_resolver(interface, domain, server, lifetime, 0);
+       return append_resolver(index, domain, server, lifetime, 0);
 }
 
 /**
  * connman_resolver_remove:
- * @interface: network interface
+ * @index: network interface index
  * @domain: domain limitation
  * @server: server address
  *
  * Remover resolver server address from current list
  */
-int connman_resolver_remove(const char *interface, const char *domain,
-                                                       const char *server)
+int connman_resolver_remove(int index, const char *domain, const char *server)
 {
        GSList *list, *matches = NULL;
 
-       DBG("interface %s domain %s server %s", interface, domain, server);
-
-       if (server == NULL)
-               return -EINVAL;
+       DBG("index %d domain %s server %s", index, domain, server);
 
        for (list = entry_list; list; list = list->next) {
                struct entry_data *entry = list->data;
 
-               if (interface != NULL &&
-                               g_strcmp0(entry->interface, interface) != 0)
+               if (entry->index != index)
                        continue;
 
-               if (domain != NULL && g_strcmp0(entry->domain, domain) != 0)
+               if (g_strcmp0(entry->domain, domain) != 0)
                        continue;
 
                if (g_strcmp0(entry->server, server) != 0)
                        continue;
 
-               matches = g_slist_append(matches, entry);
+               matches = g_slist_prepend(matches, entry);
+               break;
        }
 
        if (matches == NULL)
@@ -434,26 +472,26 @@ int connman_resolver_remove(const char *interface, const char *domain,
 
 /**
  * connman_resolver_remove_all:
- * @interface: network interface
+ * @index: network interface index
  *
- * Remove all resolver server address for the specified interface
+ * Remove all resolver server address for the specified interface index
  */
-int connman_resolver_remove_all(const char *interface)
+int connman_resolver_remove_all(int index)
 {
        GSList *list, *matches = NULL;
 
-       DBG("interface %s", interface);
+       DBG("index %d", index);
 
-       if (interface == NULL)
+       if (index < 0)
                return -EINVAL;
 
        for (list = entry_list; list; list = list->next) {
                struct entry_data *entry = list->data;
 
-               if (g_strcmp0(entry->interface, interface) != 0)
+               if (entry->index != index)
                        continue;
 
-               matches = g_slist_append(matches, entry);
+               matches = g_slist_prepend(matches, entry);
        }
 
        if (matches == NULL)
@@ -465,46 +503,86 @@ int connman_resolver_remove_all(const char *interface)
 }
 
 /**
- * connman_resolver_append_public_server:
- * @server: server address
+ * connman_resolver_flush:
  *
- * Append public resolver server address to current list
+ * Flush pending resolver requests
  */
-int connman_resolver_append_public_server(const char *server)
+void connman_resolver_flush(void)
 {
-       DBG("server %s", server);
+       if (dnsproxy_enabled == TRUE)
+               __connman_dnsproxy_flush();
 
-       return append_resolver(NULL, NULL, server, 0, RESOLVER_FLAG_PUBLIC);
+       return;
 }
 
-/**
- * connman_resolver_remove_public_server:
- * @server: server address
- *
- * Remove public resolver server address to current list
- */
-int connman_resolver_remove_public_server(const char *server)
+int __connman_resolver_redo_servers(int index)
 {
-       DBG("server %s", server);
+       GSList *list;
+
+       if (dnsproxy_enabled == FALSE)
+               return 0;
+
+       DBG("index %d", index);
+
+       if (index < 0)
+               return -EINVAL;
+
+       for (list = entry_list; list; list = list->next) {
+               struct entry_data *entry = list->data;
+
+               if (entry->timeout == 0 || entry->index != index)
+                       continue;
+
+               /*
+                * This function must only check IPv6 server addresses so
+                * do not remove IPv4 name servers unnecessarily.
+                */
+               if (entry->family != AF_INET6)
+                       continue;
+
+               /*
+                * We remove the server, and then re-create so that it will
+                * use proper source addresses when sending DNS queries.
+                */
+               __connman_dnsproxy_remove(entry->index, entry->domain,
+                                       entry->server);
+               /*
+                * Remove also the resolver timer for the old server entry.
+                * A new timer will be set for the new server entry
+                * when the next Router Advertisement message arrives
+                * with RDNSS/DNSSL settings.
+                */
+               g_source_remove(entry->timeout);
+               entry->timeout = 0;
 
-       return connman_resolver_remove(NULL, NULL, server);
+               __connman_dnsproxy_append(entry->index, entry->domain,
+                                       entry->server);
+       }
+
+       return 0;
 }
 
-/**
- * connman_resolver_flush:
- *
- * Flush pending resolver requests
- */
-void connman_resolver_flush(void)
+static void free_entry(gpointer data)
 {
-       if (dnsproxy_enabled == TRUE)
-               __connman_dnsproxy_flush();
+       struct entry_data *entry = data;
+       g_free(entry->domain);
+       g_free(entry->server);
+       g_free(entry);
+}
 
-       return;
+static void free_resolvfile(gpointer data)
+{
+       struct resolvfile_entry *entry = data;
+       g_free(entry->domain);
+       g_free(entry->server);
+       g_free(entry);
 }
 
 int __connman_resolver_init(connman_bool_t dnsproxy)
 {
+       int i;
+       char **ns;
+
        DBG("dnsproxy %d", dnsproxy);
 
        if (dnsproxy == FALSE)
@@ -517,6 +595,12 @@ int __connman_resolver_init(connman_bool_t dnsproxy)
 
        dnsproxy_enabled = TRUE;
 
+       ns = connman_setting_get_string_list("FallbackNameservers");
+       for (i = 0; ns != NULL && ns[i] != NULL; i += 1) {
+               DBG("server %s", ns[i]);
+               append_resolver(-1, NULL, ns[i], 0, RESOLVER_FLAG_PUBLIC);
+       }
+
        return 0;
 }
 
@@ -526,4 +610,18 @@ void __connman_resolver_cleanup(void)
 
        if (dnsproxy_enabled == TRUE)
                __connman_dnsproxy_cleanup();
+       else {
+               GList *list;
+               GSList *slist;
+
+               for (list = resolvfile_list; list; list = g_list_next(list))
+                       free_resolvfile(list->data);
+               g_list_free(resolvfile_list);
+               resolvfile_list = NULL;
+
+               for (slist = entry_list; slist; slist = g_slist_next(slist))
+                       free_entry(slist->data);
+               g_slist_free(entry_list);
+               entry_list = NULL;
+       }
 }
index 3387ed0..9532ddf 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
@@ -38,7 +38,6 @@ enum rfkill_type {
        RFKILL_TYPE_WLAN,
        RFKILL_TYPE_BLUETOOTH,
        RFKILL_TYPE_UWB,
-       RFKILL_TYPE_WIMAX,
        RFKILL_TYPE_WWAN,
        RFKILL_TYPE_GPS,
        RFKILL_TYPE_FM,
@@ -67,8 +66,6 @@ static enum connman_service_type convert_type(uint8_t type)
                return CONNMAN_SERVICE_TYPE_WIFI;
        case RFKILL_TYPE_BLUETOOTH:
                return CONNMAN_SERVICE_TYPE_BLUETOOTH;
-       case RFKILL_TYPE_WIMAX:
-               return CONNMAN_SERVICE_TYPE_WIMAX;
        case RFKILL_TYPE_WWAN:
                return CONNMAN_SERVICE_TYPE_CELLULAR;
        }
@@ -76,6 +73,7 @@ static enum connman_service_type convert_type(uint8_t type)
        return CONNMAN_SERVICE_TYPE_UNKNOWN;
 }
 
+#if !defined TIZEN_EXT
 static enum rfkill_type convert_service_type(enum connman_service_type type)
 {
        switch (type) {
@@ -83,8 +81,6 @@ static enum rfkill_type convert_service_type(enum connman_service_type type)
                return RFKILL_TYPE_WLAN;
        case CONNMAN_SERVICE_TYPE_BLUETOOTH:
                return RFKILL_TYPE_BLUETOOTH;
-       case CONNMAN_SERVICE_TYPE_WIMAX:
-               return RFKILL_TYPE_WIMAX;
        case CONNMAN_SERVICE_TYPE_CELLULAR:
                return RFKILL_TYPE_WWAN;
        case CONNMAN_SERVICE_TYPE_GPS:
@@ -99,6 +95,7 @@ static enum rfkill_type convert_service_type(enum connman_service_type type)
 
        return NUM_RFKILL_TYPES;
 }
+#endif
 
 static GIOStatus rfkill_process(GIOChannel *chan)
 {
@@ -162,19 +159,21 @@ static GIOChannel *channel = NULL;
 
 int __connman_rfkill_block(enum connman_service_type type, connman_bool_t block)
 {
+#if !defined TIZEN_EXT
        uint8_t rfkill_type;
        struct rfkill_event event;
        ssize_t len;
-       int fd;
+       int fd, err;
+#endif
 
        DBG("type %d block %d", type, block);
 
-       rfkill_type = convert_service_type(type);
 #if defined TIZEN_EXT
-       DBG("try to set rfkill block %d and type %d, but it's not permitted",
-                                       block, rfkill_type);
+       DBG("try to set rfkill block %d, but it's not permitted", block);
+
        return 0;
-#endif
+#else
+       rfkill_type = convert_service_type(type);
        if (rfkill_type == NUM_RFKILL_TYPES)
                return -EINVAL;
 
@@ -188,12 +187,16 @@ int __connman_rfkill_block(enum connman_service_type type, connman_bool_t block)
        event.soft = block;
 
        len = write(fd, &event, sizeof(event));
-       if (len < 0)
+       if (len < 0) {
                connman_error("Failed to change RFKILL state");
+               err = len;
+       } else
+               err = 0;
 
        close(fd);
 
-       return 0;
+       return err;
+#endif
 }
 
 int __connman_rfkill_init(void)
index 2311051..07ea357 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
 #define ARPHDR_PHONET_PIPE (821)
 #endif
 
+#if defined TIZEN_EXT
+#ifndef ARPHDR_RMNET
+#define ARPHDR_RMNET (530)
+#endif
+#endif
+
 #define print(arg...) do { if (0) connman_info(arg); } while (0)
 //#define print(arg...) connman_info(arg)
 
@@ -90,21 +96,13 @@ static connman_bool_t ether_blacklisted(const char *name)
        if (name == NULL)
                return TRUE;
 
-       /* virtual interface from VMware */
-       if (g_str_has_prefix(name, "vmnet") == TRUE)
-               return TRUE;
-
-       /* virtual interface from VirtualBox */
-       if (g_str_has_prefix(name, "vboxnet") == TRUE)
-               return TRUE;
-
-       /* virtual interface from Virtual Machine Manager */
-       if (g_str_has_prefix(name, "virbr") == TRUE)
+       if (__connman_device_isfiltered(name) == TRUE)
                return TRUE;
 
        return FALSE;
 }
 
+#if !defined TIZEN_EXT
 static connman_bool_t wext_interface(char *ifname)
 {
        struct iwreq wrq;
@@ -115,7 +113,7 @@ static connman_bool_t wext_interface(char *ifname)
                return FALSE;
 
        memset(&wrq, 0, sizeof(wrq));
-       strncpy(wrq.ifr_name, ifname, IFNAMSIZ);
+       strncpy(wrq.ifr_name, ifname, sizeof(wrq.ifr_name) - 1);
 
        err = ioctl(fd, SIOCGIWNAME, &wrq);
 
@@ -126,6 +124,29 @@ static connman_bool_t wext_interface(char *ifname)
 
        return TRUE;
 }
+#endif
+
+#if defined TIZEN_EXT
+static connman_bool_t __connman_rtnl_is_cellular_device(const char *name)
+{
+       char **pattern;
+       char **cellular_interfaces;
+
+       cellular_interfaces =
+                       connman_setting_get_string_list("NetworkCellularInterfaceList");
+       if (cellular_interfaces == NULL)
+               return FALSE;
+
+       for (pattern = cellular_interfaces; *pattern; pattern++) {
+               if (g_str_has_prefix(name, *pattern) == TRUE) {
+                       DBG("Cellular interface: %s", name);
+                       return TRUE;
+               }
+       }
+
+       return FALSE;
+}
+#endif
 
 static void read_uevent(struct interface_data *interface)
 {
@@ -133,6 +154,14 @@ static void read_uevent(struct interface_data *interface)
        connman_bool_t found_devtype;
        FILE *f;
 
+#if defined TIZEN_EXT
+       if (__connman_rtnl_is_cellular_device(interface->name) == TRUE) {
+               interface->service_type = CONNMAN_SERVICE_TYPE_CELLULAR;
+               interface->device_type = CONNMAN_DEVICE_TYPE_CELLULAR;
+               return;
+       }
+#endif
+
        if (ether_blacklisted(interface->name) == TRUE) {
                interface->service_type = CONNMAN_SERVICE_TYPE_UNKNOWN;
                interface->device_type = CONNMAN_DEVICE_TYPE_UNKNOWN;
@@ -174,9 +203,6 @@ static void read_uevent(struct interface_data *interface)
                } else if (strcmp(line + 8, "bluetooth") == 0) {
                        interface->service_type = CONNMAN_SERVICE_TYPE_BLUETOOTH;
                        interface->device_type = CONNMAN_DEVICE_TYPE_BLUETOOTH;
-               } else if (strcmp(line + 8, "wimax") == 0) {
-                       interface->service_type = CONNMAN_SERVICE_TYPE_WIMAX;
-                       interface->device_type = CONNMAN_DEVICE_TYPE_WIMAX;
                } else if (strcmp(line + 8, "gadget") == 0) {
                        interface->service_type = CONNMAN_SERVICE_TYPE_GADGET;
                        interface->device_type = CONNMAN_DEVICE_TYPE_GADGET;
@@ -192,6 +218,8 @@ static void read_uevent(struct interface_data *interface)
        if (found_devtype)
                return;
 
+#if !defined TIZEN_EXT
+       /* TIZEN does not use old wext interface */
        /* We haven't got a DEVTYPE, let's check if it's a wireless device */
        if (wext_interface(interface->name)) {
                interface->service_type = CONNMAN_SERVICE_TYPE_WIFI;
@@ -200,6 +228,7 @@ static void read_uevent(struct interface_data *interface)
                connman_error("%s runs an unsupported 802.11 driver",
                                interface->name);
        }
+#endif
 }
 
 enum connman_device_type __connman_rtnl_get_device_type(int index)
@@ -291,7 +320,9 @@ static void trigger_rtnl(int index, void *user_data)
        }
 
        if (rtnl->newgateway) {
-               const char *gateway = __connman_ipconfig_get_gateway_from_index(index);
+               const char *gateway =
+                       __connman_ipconfig_get_gateway_from_index(index,
+                                       CONNMAN_IPCONFIG_TYPE_ALL);
 
                if (gateway != NULL)
                        rtnl->newgateway(index, gateway);
@@ -363,10 +394,10 @@ static const char *operstate2str(unsigned char operstate)
        return "";
 }
 
-static void extract_link(struct ifinfomsg *msg, int bytes,
+static connman_bool_t extract_link(struct ifinfomsg *msg, int bytes,
                                struct ether_addr *address, const char **ifname,
                                unsigned int *mtu, unsigned char *operstate,
-                                               struct rtnl_link_stats *stats)
+                               struct rtnl_link_stats *stats)
 {
        struct rtattr *attr;
 
@@ -396,8 +427,12 @@ static void extract_link(struct ifinfomsg *msg, int bytes,
                        break;
                case IFLA_LINKMODE:
                        break;
+               case IFLA_WIRELESS:
+                       return FALSE;
                }
        }
+
+       return TRUE;
 }
 
 static void process_newlink(unsigned short type, int index, unsigned flags,
@@ -414,7 +449,14 @@ static void process_newlink(unsigned short type, int index, unsigned flags,
        GSList *list;
 
        memset(&stats, 0, sizeof(stats));
-       extract_link(msg, bytes, &address, &ifname, &mtu, &operstate, &stats);
+       if (extract_link(msg, bytes, &address, &ifname, &mtu, &operstate,
+                                       &stats) == FALSE)
+               return;
+#if defined TIZEN_EXT
+       /* Do not accept Wi-Fi P2P interface */
+       if (g_strrstr(ifname, "p2p") != NULL)
+               return;
+#endif
 
        snprintf(ident, 13, "%02x%02x%02x%02x%02x%02x",
                                                address.ether_addr_octet[0],
@@ -436,12 +478,13 @@ static void process_newlink(unsigned short type, int index, unsigned flags,
        case ARPHRD_ETHER:
        case ARPHRD_LOOPBACK:
        case ARPHDR_PHONET_PIPE:
+       case ARPHRD_PPP:
        case ARPHRD_NONE:
 #if defined TIZEN_EXT
 /*
- * Description: SLP requires ARPHRD_PPP PPP type device
+ * Description: ARPHDR_RMNET for QC modem using QMI
  */
-       case ARPHRD_PPP:
+       case ARPHDR_RMNET:
 #endif
                __connman_ipconfig_newlink(index, type, flags,
                                                        str, mtu, &stats);
@@ -471,15 +514,28 @@ static void process_newlink(unsigned short type, int index, unsigned flags,
                        read_uevent(interface);
 
 #if defined TIZEN_EXT
-               if (type == ARPHRD_PPP) {
-                       interface->service_type = CONNMAN_SERVICE_TYPE_CELLULAR;
-                       interface->device_type = CONNMAN_DEVICE_TYPE_CELLULAR;
-               }
+               if (type == ARPHRD_PPP || type == ARPHDR_RMNET)
+                       read_uevent(interface);
 #endif
 
                __connman_technology_add_interface(interface->service_type,
                        interface->index, interface->name, interface->ident);
        }
+#if defined TIZEN_EXT
+       else if (g_strcmp0(interface->ident, ident) != 0) {
+               /* If an original address is built-in physical device,
+                * it's hardly get an address at a initial creation
+                */
+               __connman_technology_remove_interface(interface->service_type,
+                               interface->index, interface->name, interface->ident);
+
+               g_free(interface->ident);
+               interface->ident = g_strdup(ident);
+
+               __connman_technology_add_interface(interface->service_type,
+                               interface->index, interface->name, interface->ident);
+       }
+#endif
 
        for (list = rtnl_list; list; list = list->next) {
                struct connman_rtnl *rtnl = list->data;
@@ -508,7 +564,15 @@ static void process_dellink(unsigned short type, int index, unsigned flags,
        GSList *list;
 
        memset(&stats, 0, sizeof(stats));
-       extract_link(msg, bytes, NULL, &ifname, NULL, &operstate, &stats);
+       if (extract_link(msg, bytes, NULL, &ifname, NULL, &operstate,
+                                       &stats) == FALSE)
+               return;
+
+#if defined TIZEN_EXT
+       /* Do not accept Wi-Fi P2P interface */
+       if (g_strrstr(ifname, "p2p") != NULL)
+               return;
+#endif
 
        if (operstate != 0xff)
                connman_info("%s {dellink} index %d operstate %u <%s>",
@@ -527,11 +591,12 @@ static void process_dellink(unsigned short type, int index, unsigned flags,
        case ARPHRD_LOOPBACK:
        case ARPHRD_NONE:
 #if defined TIZEN_EXT
-/*
- * Description: SLP requires ARPHRD_PPP PPP type device
- */
+       /*
+        * Description: SLP requires ARPHRD_PPP for PPP type device
+        *              ARPHDR_RMNET for QC modem using QMI
+        */
        case ARPHRD_PPP:
-               DBG("interface index(%d)", index);
+       case ARPHDR_RMNET:
 #endif
                __connman_ipconfig_dellink(index, &stats);
                break;
@@ -595,18 +660,17 @@ static void extract_ipv6_addr(struct ifaddrmsg *msg, int bytes,
 static void process_newaddr(unsigned char family, unsigned char prefixlen,
                                int index, struct ifaddrmsg *msg, int bytes)
 {
+       struct in_addr ipv4_addr = { INADDR_ANY };
+       struct in6_addr ipv6_address, ipv6_local;
        const char *label = NULL;
        void *src;
        char ip_string[INET6_ADDRSTRLEN];
 
        if (family == AF_INET) {
-               struct in_addr ipv4_addr = { INADDR_ANY };
 
                extract_ipv4_addr(msg, bytes, &label, &ipv4_addr, NULL, NULL);
                src = &ipv4_addr;
        } else if (family == AF_INET6) {
-               struct in6_addr ipv6_address, ipv6_local;
-
                extract_ipv6_addr(msg, bytes, &ipv6_address, &ipv6_local);
                if (IN6_IS_ADDR_LINKLOCAL(&ipv6_address))
                        return;
@@ -621,23 +685,31 @@ static void process_newaddr(unsigned char family, unsigned char prefixlen,
 
        __connman_ipconfig_newaddr(index, family, label,
                                        prefixlen, ip_string);
+
+       if (family == AF_INET6) {
+               /*
+                * Re-create RDNSS configured servers if there are any
+                * for this interface. This is done because we might
+                * have now properly configured interface with proper
+                * autoconfigured address.
+                */
+               __connman_resolver_redo_servers(index);
+       }
 }
 
 static void process_deladdr(unsigned char family, unsigned char prefixlen,
                                int index, struct ifaddrmsg *msg, int bytes)
 {
+       struct in_addr ipv4_addr = { INADDR_ANY };
+       struct in6_addr ipv6_address, ipv6_local;
        const char *label = NULL;
        void *src;
        char ip_string[INET6_ADDRSTRLEN];
 
        if (family == AF_INET) {
-               struct in_addr ipv4_addr = { INADDR_ANY };
-
                extract_ipv4_addr(msg, bytes, &label, &ipv4_addr, NULL, NULL);
                src = &ipv4_addr;
        } else if (family == AF_INET6) {
-               struct in6_addr ipv6_address, ipv6_local;
-
                extract_ipv6_addr(msg, bytes, &ipv6_address, &ipv6_local);
                if (IN6_IS_ADDR_LINKLOCAL(&ipv6_address))
                        return;
@@ -1193,65 +1265,75 @@ static const char **rtnl_nd_opt_dnssl(struct nd_opt_hdr *opt, guint32 *lifetime)
 static void rtnl_newnduseropt(struct nlmsghdr *hdr)
 {
        struct nduseroptmsg *msg = (struct nduseroptmsg *) NLMSG_DATA(hdr);
-       struct nd_opt_hdr *opt = (void *)&msg[1];
+       struct nd_opt_hdr *opt;
        guint32 lifetime = -1;
        const char **domains = NULL;
        struct in6_addr *servers = NULL;
-       int nr_servers = 0;
+       int i, nr_servers = 0;
        int msglen = msg->nduseropt_opts_len;
+       int index;
+#if defined TIZEN_EXT
        char *interface;
+#endif
 
-       DBG("family %02x index %x len %04x type %02x code %02x",
-           msg->nduseropt_family, msg->nduseropt_ifindex,
-           msg->nduseropt_opts_len, msg->nduseropt_icmp_type,
-           msg->nduseropt_icmp_code);
+       DBG("family %d index %d len %d type %d code %d",
+               msg->nduseropt_family, msg->nduseropt_ifindex,
+               msg->nduseropt_opts_len, msg->nduseropt_icmp_type,
+               msg->nduseropt_icmp_code);
 
        if (msg->nduseropt_family != AF_INET6 ||
-           msg->nduseropt_icmp_type != ND_ROUTER_ADVERT ||
-           msg->nduseropt_icmp_code != 0)
+                       msg->nduseropt_icmp_type != ND_ROUTER_ADVERT ||
+                       msg->nduseropt_icmp_code != 0)
                return;
 
-       interface = connman_inet_ifname(msg->nduseropt_ifindex);
-       if (!interface)
+       index = msg->nduseropt_ifindex;
+       if (index < 0)
                return;
 
-       for (opt = (void *)&msg[1];
-            msglen >= 2 && msglen >= opt->nd_opt_len && opt->nd_opt_len;
-            msglen -= opt->nd_opt_len,
-                    opt = ((void *)opt) + opt->nd_opt_len*8) {
-
-               DBG("nd opt type %d len %d\n",
-                   opt->nd_opt_type, opt->nd_opt_len);
+#if defined TIZEN_EXT
+       /* Do not accept Wi-Fi P2P interface */
+       interface = connman_inet_ifname(index);
 
-               if (opt->nd_opt_type == 25)
-                       servers = rtnl_nd_opt_rdnss(opt, &lifetime,
-                                                   &nr_servers);
-               else if (opt->nd_opt_type == 31)
-                       domains = rtnl_nd_opt_dnssl(opt, &lifetime);
+       if (g_strrstr(interface, "p2p") != NULL) {
+               g_free(interface);
+               return;
        }
 
-       if (nr_servers) {
-               int i, j;
-               char buf[40];
+       g_free(interface);
+#endif
+       for (opt = (void *)&msg[1];
+                       msglen > 0;
+                       msglen -= opt->nd_opt_len * 8,
+                       opt = ((void *)opt) + opt->nd_opt_len*8) {
+
+               DBG("remaining %d nd opt type %d len %d\n",
+                       msglen, opt->nd_opt_type, opt->nd_opt_len);
 
-               for (i = 0; i < nr_servers; i++) {
-                       if (!inet_ntop(AF_INET6, servers + i, buf, sizeof(buf)))
-                               continue;
+               if (opt->nd_opt_type == 25) { /* ND_OPT_RDNSS */
+                       char buf[40];
 
-                       if (domains == NULL || domains[0] == NULL) {
-                               connman_resolver_append_lifetime(interface,
+                       servers = rtnl_nd_opt_rdnss(opt, &lifetime,
+                                                               &nr_servers);
+                       for (i = 0; i < nr_servers; i++) {
+                               if (!inet_ntop(AF_INET6, servers + i, buf,
+                                                               sizeof(buf)))
+                                       continue;
+
+                               connman_resolver_append_lifetime(index,
                                                        NULL, buf, lifetime);
-                               continue;
                        }
 
-                       for (j = 0; domains[j]; j++)
-                               connman_resolver_append_lifetime(interface,
-                                                               domains[j],
-                                                               buf, lifetime);
+               } else if (opt->nd_opt_type == 31) { /* ND_OPT_DNSSL */
+                       g_free(domains);
+
+                       domains = rtnl_nd_opt_dnssl(opt, &lifetime);
+                       for (i = 0; domains != NULL && domains[i] != NULL; i++)
+                               connman_resolver_append_lifetime(index,
+                                               domains[i], NULL, lifetime);
                }
        }
+
        g_free(domains);
-       g_free(interface);
 }
 
 static const char *type2string(uint16_t type)
@@ -1271,6 +1353,8 @@ static const char *type2string(uint16_t type)
                return "NEWLINK";
        case RTM_DELLINK:
                return "DELLINK";
+       case RTM_GETADDR:
+               return "GETADDR";
        case RTM_NEWADDR:
                return "NEWADDR";
        case RTM_DELADDR:
@@ -1372,10 +1456,11 @@ static void rtnl_message(void *buf, size_t len)
                if (!NLMSG_OK(hdr, len))
                        break;
 
-               DBG("%s len %d type %d flags 0x%04x seq %d",
+               DBG("%s len %d type %d flags 0x%04x seq %d pid %d",
                                        type2string(hdr->nlmsg_type),
                                        hdr->nlmsg_len, hdr->nlmsg_type,
-                                       hdr->nlmsg_flags, hdr->nlmsg_seq);
+                                       hdr->nlmsg_flags, hdr->nlmsg_seq,
+                                       hdr->nlmsg_pid);
 
                switch (hdr->nlmsg_type) {
                case NLMSG_NOOP:
@@ -1421,27 +1506,37 @@ static gboolean netlink_event(GIOChannel *chan,
                                GIOCondition cond, gpointer data)
 {
        unsigned char buf[4096];
-       gsize len;
-       GIOStatus status;
+       struct sockaddr_nl nladdr;
+       socklen_t addr_len = sizeof(nladdr);
+       ssize_t status;
+       int fd;
 
        if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR))
                return FALSE;
 
        memset(buf, 0, sizeof(buf));
+       memset(&nladdr, 0, sizeof(nladdr));
 
-       status = g_io_channel_read_chars(chan, (gchar *) buf,
-                                               sizeof(buf), &len, NULL);
+       fd = g_io_channel_unix_get_fd(chan);
+
+       status = recvfrom(fd, buf, sizeof(buf), 0,
+                       (struct sockaddr *) &nladdr, &addr_len);
+       if (status < 0) {
+               if (errno == EINTR || errno == EAGAIN)
+                       return TRUE;
 
-       switch (status) {
-       case G_IO_STATUS_NORMAL:
-               break;
-       case G_IO_STATUS_AGAIN:
-               return TRUE;
-       default:
                return FALSE;
        }
 
-       rtnl_message(buf, len);
+       if (status == 0)
+               return FALSE;
+
+       if (nladdr.nl_pid != 0) { /* not sent by kernel, ignore */
+               DBG("Received msg from %u, ignoring it", nladdr.nl_pid);
+               return TRUE;
+       }
+
+       rtnl_message(buf, status);
 
        return TRUE;
 }
index da6fd28..940a036 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
 #include <string.h>
 #include <netdb.h>
 #include <gdbus.h>
+#include <ctype.h>
 
 #include <connman/storage.h>
+#include <connman/setting.h>
 
 #include "connman.h"
 
 #define CONNECT_TIMEOUT                120
 
+#if defined TIZEN_EXT
+#define WIFI_BSSID_STR_LEN     18
+#endif
 static DBusConnection *connection = NULL;
 
 static GSequence *service_list = NULL;
 static GHashTable *service_hash = NULL;
 static GSList *counter_list = NULL;
+static unsigned int autoconnect_timeout = 0;
+static struct connman_service *current_default = NULL;
+static connman_bool_t services_dirty = FALSE;
 
 struct connman_stats {
        connman_bool_t valid;
@@ -79,8 +87,6 @@ struct connman_service {
        char *passphrase;
        char *agent_passphrase;
        connman_bool_t roaming;
-       connman_bool_t login_required;
-       connman_bool_t network_created;
        struct connman_ipconfig *ipconfig_ipv4;
        struct connman_ipconfig *ipconfig_ipv6;
        struct connman_network *network;
@@ -91,6 +97,7 @@ struct connman_service {
        char **domains;
        char *domainname;
        char **timeservers;
+       char **timeservers_config;
        /* 802.1x settings from the config files */
        char *eap;
        char *identity;
@@ -111,74 +118,74 @@ struct connman_service {
        char **excludes;
        char *pac;
        connman_bool_t wps;
+       int online_check_count;
+       connman_bool_t do_split_routing;
+       connman_bool_t new_service;
+       connman_bool_t hidden_service;
+       char *config_file;
+       char *config_entry;
 #if defined TIZEN_EXT
        /*
         * Description: TIZEN implements system global connection management.
-        *              It's only for PDP (cellular) bearer. Wi-Fi is managed by ConnMan automatically.
-        *              Reference count can help to manage open/close connection requests by each application.
+        *              It's only for PDP (cellular) bearer. Wi-Fi is managed
+        *              by ConnMan automatically. Reference count can help to
+        *              manage open/close connection requests by each application.
         */
-       gint user_initiated_pdn_connection_refcount;
+       int user_pdn_connection_refcount;
+       connman_bool_t localnetwork;
 #endif
 };
 
+static connman_bool_t allow_property_changed(struct connman_service *service);
+
+struct find_data {
+       const char *path;
+       struct connman_service *service;
+};
+
 #if defined TIZEN_EXT
 /*
- * Public APIs to use user_initiated_pdn_connection_refcount
+ * Public APIs to use user_pdn_connection_refcount
  */
-void connman_service_user_initiated_pdn_connection_ref(
-               struct connman_service *service)
+void connman_service_user_pdn_connection_ref(struct connman_service *service)
 {
-       g_atomic_int_inc(&service->user_initiated_pdn_connection_refcount);
-       DBG("User try to make a PDP connection with already referenced count: %d",
-                       service->user_initiated_pdn_connection_refcount);
+       __sync_fetch_and_add(&service->user_pdn_connection_refcount, 1);
+
+       DBG("User made PDN connection referenced: %d",
+                               service->user_pdn_connection_refcount);
 }
 
-connman_bool_t connman_service_user_initiated_pdn_connection_unref_and_test(
-               struct connman_service *service)
+connman_bool_t connman_service_user_pdn_connection_unref_and_test(
+                                       struct connman_service *service)
 {
-       connman_bool_t test_zero = TRUE;
+       __sync_synchronize();
 
-       if (service->user_initiated_pdn_connection_refcount > 0)
-               test_zero = g_atomic_int_dec_and_test(&service->user_initiated_pdn_connection_refcount);
+       DBG("User made PDN connection referenced: %d, which will be decreased",
+                               service->user_pdn_connection_refcount);
 
-       DBG("User try to disconnect existing PDP connection with already unreferenced count: %d",
-                       service->user_initiated_pdn_connection_refcount);
+       if (service->user_pdn_connection_refcount < 1)
+               return TRUE;
 
-       return test_zero;
-}
+       if (__sync_sub_and_fetch(&service->user_pdn_connection_refcount, 1) == 0)
+               return TRUE;
 
-connman_bool_t connman_service_is_no_ref_user_initiated_pdn_connection(
-               struct connman_service *service)
-{
-       return g_atomic_int_compare_and_exchange(&service->user_initiated_pdn_connection_refcount,
-                       0, 0);
+       return FALSE;
 }
-#endif
 
-static void append_path(gpointer value, gpointer user_data)
+connman_bool_t connman_service_is_no_ref_user_pdn_connection(
+                                       struct connman_service *cellular)
 {
-       struct connman_service *service = value;
-       DBusMessageIter *iter = user_data;
-
-       if (service->path == NULL || service->hidden == TRUE)
-               return;
-
-       dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH,
-                                                       &service->path);
-}
+       if (cellular == NULL)
+               return TRUE;
 
-void __connman_service_list(DBusMessageIter *iter, void *user_data)
-{
-       if (service_list == NULL)
-               return;
+       __sync_synchronize();
+       if (cellular->type == CONNMAN_SERVICE_TYPE_CELLULAR &&
+                       cellular->user_pdn_connection_refcount == 0)
+               return TRUE;
 
-       g_sequence_foreach(service_list, append_path, iter);
+       return FALSE;
 }
-
-struct find_data {
-       const char *path;
-       struct connman_service *service;
-};
+#endif
 
 static void compare_path(gpointer value, gpointer user_data)
 {
@@ -214,8 +221,6 @@ const char *__connman_service_type2string(enum connman_service_type type)
                return "ethernet";
        case CONNMAN_SERVICE_TYPE_WIFI:
                return "wifi";
-       case CONNMAN_SERVICE_TYPE_WIMAX:
-               return "wimax";
        case CONNMAN_SERVICE_TYPE_BLUETOOTH:
                return "bluetooth";
        case CONNMAN_SERVICE_TYPE_CELLULAR:
@@ -231,6 +236,31 @@ const char *__connman_service_type2string(enum connman_service_type type)
        return NULL;
 }
 
+enum connman_service_type __connman_service_string2type(const char *str)
+{
+       if (str == NULL)
+               return CONNMAN_SERVICE_TYPE_UNKNOWN;
+
+       if (strcmp(str, "ethernet") == 0)
+               return CONNMAN_SERVICE_TYPE_ETHERNET;
+       if (strcmp(str, "gadget") == 0)
+               return CONNMAN_SERVICE_TYPE_GADGET;
+       if (strcmp(str, "wifi") == 0)
+               return CONNMAN_SERVICE_TYPE_WIFI;
+       if (strcmp(str, "cellular") == 0)
+               return CONNMAN_SERVICE_TYPE_CELLULAR;
+       if (strcmp(str, "bluetooth") == 0)
+               return CONNMAN_SERVICE_TYPE_BLUETOOTH;
+       if (strcmp(str, "vpn") == 0)
+               return CONNMAN_SERVICE_TYPE_VPN;
+       if (strcmp(str, "gps") == 0)
+               return CONNMAN_SERVICE_TYPE_GPS;
+       if (strcmp(str, "system") == 0)
+               return CONNMAN_SERVICE_TYPE_SYSTEM;
+
+       return CONNMAN_SERVICE_TYPE_UNKNOWN;
+}
+
 static const char *security2string(enum connman_service_security security)
 {
        switch (security) {
@@ -241,13 +271,11 @@ static const char *security2string(enum connman_service_security security)
        case CONNMAN_SERVICE_SECURITY_WEP:
                return "wep";
        case CONNMAN_SERVICE_SECURITY_PSK:
+       case CONNMAN_SERVICE_SECURITY_WPA:
+       case CONNMAN_SERVICE_SECURITY_RSN:
                return "psk";
        case CONNMAN_SERVICE_SECURITY_8021X:
                return "ieee8021x";
-       case CONNMAN_SERVICE_SECURITY_WPA:
-               return "wpa";
-       case CONNMAN_SERVICE_SECURITY_RSN:
-               return "rsn";
        }
 
        return NULL;
@@ -354,20 +382,25 @@ static int service_load(struct connman_service *service)
        DBG("service %p", service);
 
        keyfile = connman_storage_load_service(service->identifier);
-       if (keyfile == NULL)
+       if (keyfile == NULL) {
+               service->new_service = TRUE;
                return -EIO;
+       } else
+               service->new_service = FALSE;
 
        switch (service->type) {
        case CONNMAN_SERVICE_TYPE_UNKNOWN:
        case CONNMAN_SERVICE_TYPE_SYSTEM:
-       case CONNMAN_SERVICE_TYPE_ETHERNET:
        case CONNMAN_SERVICE_TYPE_GPS:
-       case CONNMAN_SERVICE_TYPE_VPN:
        case CONNMAN_SERVICE_TYPE_GADGET:
 #if defined TIZEN_EXT
        case CONNMAN_SERVICE_TYPE_CELLULAR:
 #endif
                break;
+       case CONNMAN_SERVICE_TYPE_VPN:
+               service->do_split_routing = g_key_file_get_boolean(keyfile,
+                               service->identifier, "SplitRouting", NULL);
+               break;
        case CONNMAN_SERVICE_TYPE_WIFI:
                if (service->name == NULL) {
                        gchar *name;
@@ -418,7 +451,6 @@ static int service_load(struct connman_service *service)
                }
                /* fall through */
 
-       case CONNMAN_SERVICE_TYPE_WIMAX:
        case CONNMAN_SERVICE_TYPE_BLUETOOTH:
 #if !defined TIZEN_EXT
        case CONNMAN_SERVICE_TYPE_CELLULAR:
@@ -426,12 +458,6 @@ static int service_load(struct connman_service *service)
                service->favorite = g_key_file_get_boolean(keyfile,
                                service->identifier, "Favorite", NULL);
 
-               autoconnect = g_key_file_get_boolean(keyfile,
-                               service->identifier, "AutoConnect", &error);
-               if (error == NULL)
-                       service->autoconnect = autoconnect;
-               g_clear_error(&error);
-
                str = g_key_file_get_string(keyfile,
                                service->identifier, "Failure", NULL);
                if (str != NULL) {
@@ -441,6 +467,14 @@ static int service_load(struct connman_service *service)
                        service->error = string2error(str);
                        g_free(str);
                }
+               /* fall through */
+
+       case CONNMAN_SERVICE_TYPE_ETHERNET:
+               autoconnect = g_key_file_get_boolean(keyfile,
+                               service->identifier, "AutoConnect", &error);
+               if (error == NULL)
+                       service->autoconnect = autoconnect;
+               g_clear_error(&error);
                break;
        }
 
@@ -473,6 +507,13 @@ static int service_load(struct connman_service *service)
                service->nameservers_config = NULL;
        }
 
+       service->timeservers_config = g_key_file_get_string_list(keyfile,
+                       service->identifier, "Timeservers", &length, NULL);
+       if (service->timeservers_config != NULL && length == 0) {
+               g_strfreev(service->timeservers_config);
+               service->timeservers_config = NULL;
+       }
+
        service->domains = g_key_file_get_string_list(keyfile,
                        service->identifier, "Domains", &length, NULL);
        if (service->domains != NULL && length == 0) {
@@ -508,6 +549,9 @@ static int service_load(struct connman_service *service)
                service->pac = str;
        }
 
+       service->hidden_service = g_key_file_get_boolean(keyfile,
+                                       service->identifier, "Hidden", NULL);
+
 #if defined TIZEN_EXT
        if (service->type == CONNMAN_SERVICE_TYPE_WIFI &&
                        service->security == CONNMAN_SERVICE_SECURITY_8021X) {
@@ -575,7 +619,10 @@ static int service_save(struct connman_service *service)
        const char *cst_str = NULL;
        int err = 0;
 
-       DBG("service %p", service);
+       DBG("service %p new %d", service, service->new_service);
+
+       if (service->new_service == TRUE)
+               return -ESRCH;
 
        keyfile = __connman_storage_open_service(service->identifier);
        if (keyfile == NULL)
@@ -588,11 +635,13 @@ static int service_save(struct connman_service *service)
        switch (service->type) {
        case CONNMAN_SERVICE_TYPE_UNKNOWN:
        case CONNMAN_SERVICE_TYPE_SYSTEM:
-       case CONNMAN_SERVICE_TYPE_ETHERNET:
        case CONNMAN_SERVICE_TYPE_GPS:
-       case CONNMAN_SERVICE_TYPE_VPN:
        case CONNMAN_SERVICE_TYPE_GADGET:
                break;
+       case CONNMAN_SERVICE_TYPE_VPN:
+               g_key_file_set_boolean(keyfile, service->identifier,
+                               "SplitRouting", service->do_split_routing);
+               break;
        case CONNMAN_SERVICE_TYPE_WIFI:
                if (service->network) {
                        const unsigned char *ssid;
@@ -603,23 +652,23 @@ static int service_save(struct connman_service *service)
 
                        if (ssid != NULL && ssid_len > 0 && ssid[0] != '\0') {
                                char *identifier = service->identifier;
-                               GString *str;
+                               GString *ssid_str;
                                unsigned int i;
 
-                               str = g_string_sized_new(ssid_len * 2);
-                               if (str == NULL) {
+                               ssid_str = g_string_sized_new(ssid_len * 2);
+                               if (ssid_str == NULL) {
                                        err = -ENOMEM;
                                        goto done;
                                }
 
                                for (i = 0; i < ssid_len; i++)
-                                       g_string_append_printf(str,
+                                       g_string_append_printf(ssid_str,
                                                        "%02x", ssid[i]);
 
                                g_key_file_set_string(keyfile, identifier,
-                                                       "SSID", str->str);
+                                                       "SSID", ssid_str->str);
 
-                               g_string_free(str, TRUE);
+                               g_string_free(ssid_str, TRUE);
                        }
 
                        freq = connman_network_get_frequency(service->network);
@@ -628,16 +677,11 @@ static int service_save(struct connman_service *service)
                }
                /* fall through */
 
-       case CONNMAN_SERVICE_TYPE_WIMAX:
        case CONNMAN_SERVICE_TYPE_BLUETOOTH:
        case CONNMAN_SERVICE_TYPE_CELLULAR:
                g_key_file_set_boolean(keyfile, service->identifier,
                                        "Favorite", service->favorite);
 
-               if (service->favorite == TRUE)
-                       g_key_file_set_boolean(keyfile, service->identifier,
-                                       "AutoConnect", service->autoconnect);
-
                if (service->state_ipv4 == CONNMAN_SERVICE_STATE_FAILURE ||
                        service->state_ipv6 == CONNMAN_SERVICE_STATE_FAILURE) {
                        const char *failure = error2string(service->error);
@@ -649,6 +693,12 @@ static int service_save(struct connman_service *service)
                        g_key_file_remove_key(keyfile, service->identifier,
                                                        "Failure", NULL);
                }
+               /* fall through */
+
+       case CONNMAN_SERVICE_TYPE_ETHERNET:
+               if (service->favorite == TRUE)
+                       g_key_file_set_boolean(keyfile, service->identifier,
+                                       "AutoConnect", service->autoconnect);
                break;
        }
 
@@ -684,6 +734,16 @@ static int service_save(struct connman_service *service)
        g_key_file_remove_key(keyfile, service->identifier,
                                                        "Nameservers", NULL);
 
+       if (service->timeservers_config != NULL) {
+               guint len = g_strv_length(service->timeservers_config);
+
+               g_key_file_set_string_list(keyfile, service->identifier,
+                                                               "Timeservers",
+                               (const gchar **) service->timeservers_config, len);
+       } else
+               g_key_file_remove_key(keyfile, service->identifier,
+                                                       "Timeservers", NULL);
+
        if (service->domains != NULL) {
                guint len = g_strv_length(service->domains);
 
@@ -726,6 +786,19 @@ static int service_save(struct connman_service *service)
                g_key_file_remove_key(keyfile, service->identifier,
                                                        "Proxy.URL", NULL);
 
+       if (service->hidden_service == TRUE)
+               g_key_file_set_boolean(keyfile, service->identifier, "Hidden",
+                                                                       TRUE);
+
+       if (service->config_file != NULL && strlen(service->config_file) > 0)
+               g_key_file_set_string(keyfile, service->identifier,
+                               "Config.file", service->config_file);
+
+       if (service->config_entry != NULL &&
+                                       strlen(service->config_entry) > 0)
+               g_key_file_set_string(keyfile, service->identifier,
+                               "Config.ident", service->config_entry);
+
 #if defined TIZEN_EXT
        if (service->type == CONNMAN_SERVICE_TYPE_WIFI &&
                        service->security == CONNMAN_SERVICE_SECURITY_8021X) {
@@ -787,41 +860,9 @@ done:
        return err;
 }
 
-static guint changed_timeout = 0;
-
-static gboolean notify_services_changed(gpointer user_data)
-{
-       changed_timeout = 0;
-
-       connman_dbus_property_changed_array(CONNMAN_MANAGER_PATH,
-                               CONNMAN_MANAGER_INTERFACE, "Services",
-                               DBUS_TYPE_OBJECT_PATH, __connman_service_list,
-                               NULL);
-
-       return FALSE;
-}
-
-static void services_changed(gboolean delayed)
+void __connman_service_save(struct connman_service *service)
 {
-       DBG("");
-
-       if (changed_timeout > 0) {
-               g_source_remove(changed_timeout);
-               changed_timeout = 0;
-       }
-
-       if (__connman_connection_update_gateway() == TRUE) {
-               notify_services_changed(NULL);
-               return;
-       }
-
-       if (delayed == FALSE) {
-               notify_services_changed(NULL);
-               return;
-       }
-
-       changed_timeout = g_timeout_add_seconds(1, notify_services_changed,
-                                                                NULL);
+       service_save(service);
 }
 
 static enum connman_service_state combine_state(
@@ -951,6 +992,25 @@ static connman_bool_t is_connected_state(const struct connman_service *service,
        return FALSE;
 }
 
+static connman_bool_t is_idle_state(const struct connman_service *service,
+                               enum connman_service_state state)
+{
+       switch (state) {
+       case CONNMAN_SERVICE_STATE_UNKNOWN:
+       case CONNMAN_SERVICE_STATE_ASSOCIATION:
+       case CONNMAN_SERVICE_STATE_CONFIGURATION:
+       case CONNMAN_SERVICE_STATE_READY:
+       case CONNMAN_SERVICE_STATE_ONLINE:
+       case CONNMAN_SERVICE_STATE_DISCONNECT:
+       case CONNMAN_SERVICE_STATE_FAILURE:
+               break;
+       case CONNMAN_SERVICE_STATE_IDLE:
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
 static connman_bool_t is_connecting(struct connman_service *service)
 {
        return is_connecting_state(service, service->state);
@@ -961,18 +1021,73 @@ static connman_bool_t is_connected(struct connman_service *service)
        return is_connected_state(service, service->state);
 }
 
-static void update_nameservers(struct connman_service *service)
+static int nameserver_get_index(struct connman_service *service)
 {
-       const char *ifname;
+       int index;
 
-       if (service->ipconfig_ipv4)
-               ifname = connman_ipconfig_get_ifname(service->ipconfig_ipv4);
-       else if (service->ipconfig_ipv6)
-               ifname = connman_ipconfig_get_ifname(service->ipconfig_ipv6);
-       else
-               ifname = NULL;
+       index = __connman_service_get_index(service);
+
+       if (index < 0)
+               return -1;
+
+       switch (combine_state(service->state_ipv4, service->state_ipv6)) {
+       case CONNMAN_SERVICE_STATE_UNKNOWN:
+       case CONNMAN_SERVICE_STATE_IDLE:
+       case CONNMAN_SERVICE_STATE_ASSOCIATION:
+       case CONNMAN_SERVICE_STATE_CONFIGURATION:
+       case CONNMAN_SERVICE_STATE_FAILURE:
+       case CONNMAN_SERVICE_STATE_DISCONNECT:
+               return -1;
+       case CONNMAN_SERVICE_STATE_READY:
+       case CONNMAN_SERVICE_STATE_ONLINE:
+               break;
+       }
+
+       return index;
+}
+
+static void remove_nameservers(struct connman_service *service,
+               int index, char **ns)
+{
+       int i;
+
+       if (ns == NULL)
+               return;
+
+       if (index < 0)
+               index = nameserver_get_index(service);
+
+       if (index < 0)
+                       return;
+
+       for (i = 0; ns[i] != NULL; i++)
+               connman_resolver_remove(index, NULL, ns[i]);
+}
+
+static void remove_searchdomains(struct connman_service *service,
+               int index, char **sd)
+{
+       int i;
+
+       if (sd == NULL)
+               return;
 
-       if (ifname == NULL)
+       if (index < 0)
+               index = nameserver_get_index(service);
+
+       if (index < 0)
+               return;
+
+       for (i = 0; sd[i] != NULL; i++)
+               connman_resolver_remove(index, sd[i], NULL);
+}
+
+static void update_nameservers(struct connman_service *service)
+{
+       int index;
+
+       index = __connman_service_get_index(service);
+       if (index < 0)
                return;
 
        switch (combine_state(service->state_ipv4, service->state_ipv6)) {
@@ -983,7 +1098,7 @@ static void update_nameservers(struct connman_service *service)
                return;
        case CONNMAN_SERVICE_STATE_FAILURE:
        case CONNMAN_SERVICE_STATE_DISCONNECT:
-               connman_resolver_remove_all(ifname);
+               connman_resolver_remove_all(index);
                return;
        case CONNMAN_SERVICE_STATE_READY:
        case CONNMAN_SERVICE_STATE_ONLINE:
@@ -993,27 +1108,40 @@ static void update_nameservers(struct connman_service *service)
        if (service->nameservers_config != NULL) {
                int i;
 
-               for (i = 0; service->nameservers_config[i] != NULL; i++) {
-                       connman_resolver_append(ifname, NULL,
-                                               service->nameservers_config[i]);
+               remove_nameservers(service, index, service->nameservers);
+
+               i = g_strv_length(service->nameservers_config);
+               while (i != 0) {
+                       i--;
+                       connman_resolver_append(index, NULL,
+                                       service->nameservers_config[i]);
                }
        } else if (service->nameservers != NULL) {
                int i;
 
-               for (i = 0; service->nameservers[i] != NULL; i++) {
-                       connman_resolver_append(ifname, NULL,
-                                               service->nameservers[i]);
+               i = g_strv_length(service->nameservers);
+               while (i != 0) {
+                       i--;
+                       connman_resolver_append(index, NULL,
+                                       service->nameservers[i]);
                }
        }
 
        if (service->domains != NULL) {
+               char *searchdomains[2] = {NULL, NULL};
                int i;
 
-               for (i = 0; service->domains[i]; i++)
-                       connman_resolver_append(ifname, service->domains[i],
+               searchdomains[0] = service->domainname;
+               remove_searchdomains(service, index, searchdomains);
+
+               i = g_strv_length(service->domains);
+               while (i != 0) {
+                       i--;
+                       connman_resolver_append(index, service->domains[i],
                                                NULL);
+               }
        } else if (service->domainname != NULL)
-               connman_resolver_append(ifname, service->domainname, NULL);
+               connman_resolver_append(index, service->domainname, NULL);
 
        connman_resolver_flush();
 }
@@ -1118,8 +1246,16 @@ int __connman_service_nameserver_remove(struct connman_service *service,
        for (i = 0, j = 0; i < len; i++) {
                if (g_strcmp0(nameservers[i], nameserver) != 0) {
                        servers[j] = g_strdup(nameservers[i]);
+#if !defined TIZEN_EXT
                        if (servers[j] == NULL)
                                return -ENOMEM;
+#else
+                       /* Free the previously allocated array of strings before return */
+                       if (servers[j] == NULL) {
+                               g_strfreev(servers);
+                               return -ENOMEM;
+                       }
+#endif
                        j++;
                }
        }
@@ -1146,65 +1282,64 @@ void __connman_service_nameserver_clear(struct connman_service *service)
        update_nameservers(service);
 }
 
+static void add_nameserver_route(int family, int index, char *nameserver,
+                               const char *gw)
+{
+       switch (family) {
+       case AF_INET:
+               if (connman_inet_compare_subnet(index, nameserver) == TRUE)
+                       break;
+
+               if (connman_inet_add_host_route(index, nameserver, gw) < 0)
+                       /* For P-t-P link the above route add will fail */
+                       connman_inet_add_host_route(index, nameserver, NULL);
+               break;
+
+       case AF_INET6:
+               if (connman_inet_add_ipv6_host_route(index, nameserver,
+                                                               gw) < 0)
+                       connman_inet_add_ipv6_host_route(index, nameserver,
+                                                       NULL);
+               break;
+       }
+}
+
 static void nameserver_add_routes(int index, char **nameservers,
                                        const char *gw)
 {
-       int i, ret, family;
-       struct addrinfo hints;
-       struct addrinfo *addr;
+       int i, family;
 
        for (i = 0; nameservers[i] != NULL; i++) {
-               memset(&hints, 0, sizeof(struct addrinfo));
-               hints.ai_flags = AI_NUMERICHOST;
-               addr = NULL;
-
-               ret = getaddrinfo(nameservers[i], NULL, &hints, &addr);
-               if (ret == EAI_NONAME)
-                       family = AF_INET; /* use the IPv4 as a default */
-               else if (ret != 0)
+               family = connman_inet_check_ipaddress(nameservers[i]);
+               if (family < 0)
                        continue;
-               else
-                       family = addr->ai_family;
 
-               if (family == AF_INET) {
-                       if (connman_inet_compare_subnet(index,
-                                               nameservers[i]) != TRUE)
-                               connman_inet_add_host_route(index,
-                                                       nameservers[i], gw);
-               } else if (family == AF_INET6)
-                       connman_inet_add_ipv6_host_route(index,
-                                                       nameservers[i], gw);
-
-               freeaddrinfo(addr);
+               add_nameserver_route(family, index, nameservers[i], gw);
        }
 }
 
-static void nameserver_del_routes(int index, char **nameservers)
+static void nameserver_del_routes(int index, char **nameservers,
+                               enum connman_ipconfig_type type)
 {
-       int i, ret, family;
-       struct addrinfo hints;
-       struct addrinfo *addr;
+       int i, family;
 
        for (i = 0; nameservers[i] != NULL; i++) {
-               memset(&hints, 0, sizeof(struct addrinfo));
-               hints.ai_flags = AI_NUMERICHOST;
-               addr = NULL;
-
-               ret = getaddrinfo(nameservers[i], NULL, &hints, &addr);
-               if (ret == EAI_NONAME)
-                       family = AF_INET; /* use the IPv4 as a default */
-               else if (ret != 0)
+               family = connman_inet_check_ipaddress(nameservers[i]);
+               if (family < 0)
                        continue;
-               else
-                       family = addr->ai_family;
 
-               if (family == AF_INET)
-                       connman_inet_del_host_route(index, nameservers[i]);
-               else if (family == AF_INET6)
-                       connman_inet_del_ipv6_host_route(index,
+               switch (family) {
+               case AF_INET:
+                       if (type != CONNMAN_IPCONFIG_TYPE_IPV6)
+                               connman_inet_del_host_route(index,
                                                        nameservers[i]);
-
-               freeaddrinfo(addr);
+                       break;
+               case AF_INET6:
+                       if (type != CONNMAN_IPCONFIG_TYPE_IPV4)
+                               connman_inet_del_ipv6_host_route(index,
+                                                       nameservers[i]);
+                       break;
+               }
        }
 }
 
@@ -1239,7 +1374,8 @@ void __connman_service_nameserver_add_routes(struct connman_service *service,
        }
 }
 
-void __connman_service_nameserver_del_routes(struct connman_service *service)
+void __connman_service_nameserver_del_routes(struct connman_service *service,
+                                       enum connman_ipconfig_type type)
 {
        int index = -1;
 
@@ -1252,9 +1388,10 @@ void __connman_service_nameserver_del_routes(struct connman_service *service)
                index = connman_provider_get_index(service->provider);
 
        if (service->nameservers_config != NULL)
-               nameserver_del_routes(index, service->nameservers_config);
+               nameserver_del_routes(index, service->nameservers_config,
+                                       type);
        else if (service->nameservers != NULL)
-               nameserver_del_routes(index, service->nameservers);
+               nameserver_del_routes(index, service->nameservers, type);
 }
 
 static struct connman_stats *stats_get(struct connman_service *service)
@@ -1345,61 +1482,148 @@ static void reset_stats(struct connman_service *service)
        g_timer_reset(service->stats_roaming.timer);
 }
 
-static struct connman_service *get_default(void)
+#if defined TIZEN_EXT
+static connman_bool_t __connman_service_is_internet_profile(
+               struct connman_service *cellular)
 {
-       struct connman_service *service;
-       GSequenceIter *iter;
-
-       iter = g_sequence_get_begin_iter(service_list);
-
-       if (g_sequence_iter_is_end(iter) == TRUE)
-               return NULL;
+       const char internet_suffix[] = "_1";
 
-       service = g_sequence_get(iter);
+       DBG("Service path: %s", cellular->path);
 
-       if (is_connected(service) == FALSE)
-               return NULL;
+       if (g_str_has_suffix(cellular->path, internet_suffix) == TRUE)
+               return TRUE;
 
-       return service;
+       return FALSE;
 }
 
-static void default_changed(void)
+static connman_bool_t __connman_service_is_tethering_profile(
+               struct connman_service *cellular)
 {
-       struct connman_service *service = get_default();
+       const char tethering_suffix[] = "_5";
 
-       __connman_notifier_default_changed(service);
-}
-
-const char *__connman_service_default(void)
-{
-       struct connman_service *service;
+       DBG("Service path: %s", cellular->path);
 
-       service = get_default();
-       if (service == NULL)
-               return "";
+       if (g_str_has_suffix(cellular->path, tethering_suffix) == TRUE)
+               return TRUE;
 
-       return __connman_service_type2string(service->type);
+       return FALSE;
 }
 
-static void state_changed(struct connman_service *service)
+struct connman_service *connman_service_get_default_connection(void)
 {
-       const char *str;
+       GSequenceIter *iter;
+       struct connman_service *default_service = NULL;
 
-       __connman_notifier_service_state_changed(service, service->state);
+       iter = g_sequence_get_begin_iter(service_list);
 
-       str = state2string(service->state);
-       if (str == NULL)
-               return;
+       while (g_sequence_iter_is_end(iter) == FALSE) {
+               struct connman_service *service = g_sequence_get(iter);
 
-#if defined TIZEN_EXT
-       const char *str_err = error2string(service->error);
-       if ((service->state == CONNMAN_SERVICE_STATE_FAILURE) && (str_err != NULL))
-               connman_dbus_service_property_changed_with_error_cause(service->path,
-                               CONNMAN_SERVICE_INTERFACE, "State", DBUS_TYPE_STRING, &str,
-                               "Error", DBUS_TYPE_STRING, &str_err);
-       else
-#endif
-       connman_dbus_property_changed_basic(service->path,
+               DBG("service: %p %s %s %s", service, service->name,
+                               state2string(service->state),
+                               __connman_service_type2string(service->type));
+
+               if (service->localnetwork == TRUE) {
+                       iter = g_sequence_iter_next(iter);
+                       continue;
+               }
+
+               if (service->type == CONNMAN_SERVICE_TYPE_WIFI &&
+                               is_connected(service) == TRUE) {
+                       return service;
+               } else if (service->type == CONNMAN_SERVICE_TYPE_CELLULAR &&
+                               __connman_service_is_internet_profile(service) == TRUE) {
+                       if (default_service == NULL)
+                               default_service = service;
+                       else if (is_connected(service) == TRUE &&
+                                       is_connected(default_service) == FALSE)
+                               default_service = service;
+               } else if (service->type == CONNMAN_SERVICE_TYPE_ETHERNET &&
+                               is_connected(service) == TRUE) {
+                       if (default_service == NULL)
+                               default_service = service;
+               } else if (service->type == CONNMAN_SERVICE_TYPE_BLUETOOTH &&
+                               is_connected(service) == TRUE) {
+                       if (default_service == NULL)
+                               default_service = service;
+               }
+
+               iter = g_sequence_iter_next(iter);
+       }
+
+       return default_service;
+}
+#endif
+
+struct connman_service *__connman_service_get_default(void)
+{
+       struct connman_service *service;
+       GSequenceIter *iter;
+
+       iter = g_sequence_get_begin_iter(service_list);
+
+       if (g_sequence_iter_is_end(iter) == TRUE)
+               return NULL;
+
+       service = g_sequence_get(iter);
+
+       if (is_connected(service) == FALSE)
+               return NULL;
+
+       return service;
+}
+
+connman_bool_t __connman_service_index_is_default(int index)
+{
+       struct connman_service *service;
+
+       if (index < 0)
+               return FALSE;
+
+       service = __connman_service_get_default();
+
+       if (__connman_service_get_index(service) == index)
+               return TRUE;
+
+       return FALSE;
+}
+
+static void default_changed(void)
+{
+       struct connman_service *service = __connman_service_get_default();
+
+       if (service == current_default)
+               return;
+
+#if defined TIZEN_EXT
+       current_default = service;
+
+       __connman_service_timeserver_changed(service, NULL);
+#else
+       __connman_service_timeserver_changed(current_default, NULL);
+
+       current_default = service;
+#endif
+
+       __connman_notifier_default_changed(service);
+}
+
+static void state_changed(struct connman_service *service)
+{
+       const char *str;
+
+       __connman_notifier_service_state_changed(service, service->state);
+
+       str = state2string(service->state);
+       if (str == NULL)
+               return;
+
+#if !defined TIZEN_EXT
+       if (allow_property_changed(service) == FALSE)
+               return;
+#endif
+
+       connman_dbus_property_changed_basic(service->path,
                                CONNMAN_SERVICE_INTERFACE, "State",
                                                DBUS_TYPE_STRING, &str);
 }
@@ -1409,6 +1633,9 @@ static void strength_changed(struct connman_service *service)
        if (service->strength == 0)
                return;
 
+       if (allow_property_changed(service) == FALSE)
+               return;
+
        connman_dbus_property_changed_basic(service->path,
                                CONNMAN_SERVICE_INTERFACE, "Strength",
                                        DBUS_TYPE_BYTE, &service->strength);
@@ -1419,6 +1646,9 @@ static void favorite_changed(struct connman_service *service)
        if (service->path == NULL)
                return;
 
+       if (allow_property_changed(service) == FALSE)
+               return;
+
        connman_dbus_property_changed_basic(service->path,
                                CONNMAN_SERVICE_INTERFACE, "Favorite",
                                        DBUS_TYPE_BOOLEAN, &service->favorite);
@@ -1429,6 +1659,9 @@ static void immutable_changed(struct connman_service *service)
        if (service->path == NULL)
                return;
 
+       if (allow_property_changed(service) == FALSE)
+               return;
+
        connman_dbus_property_changed_basic(service->path,
                                CONNMAN_SERVICE_INTERFACE, "Immutable",
                                        DBUS_TYPE_BOOLEAN, &service->immutable);
@@ -1439,6 +1672,9 @@ static void roaming_changed(struct connman_service *service)
        if (service->path == NULL)
                return;
 
+       if (allow_property_changed(service) == FALSE)
+               return;
+
        connman_dbus_property_changed_basic(service->path,
                                CONNMAN_SERVICE_INTERFACE, "Roaming",
                                        DBUS_TYPE_BOOLEAN, &service->roaming);
@@ -1449,61 +1685,12 @@ static void autoconnect_changed(struct connman_service *service)
        if (service->path == NULL)
                return;
 
-       connman_dbus_property_changed_basic(service->path,
-                               CONNMAN_SERVICE_INTERFACE, "AutoConnect",
-                               DBUS_TYPE_BOOLEAN, &service->autoconnect);
-}
-
-static void passphrase_changed(struct connman_service *service)
-{
-       dbus_bool_t required;
-
-       switch (service->type) {
-       case CONNMAN_SERVICE_TYPE_UNKNOWN:
-       case CONNMAN_SERVICE_TYPE_SYSTEM:
-       case CONNMAN_SERVICE_TYPE_ETHERNET:
-       case CONNMAN_SERVICE_TYPE_WIMAX:
-       case CONNMAN_SERVICE_TYPE_BLUETOOTH:
-       case CONNMAN_SERVICE_TYPE_CELLULAR:
-       case CONNMAN_SERVICE_TYPE_GPS:
-       case CONNMAN_SERVICE_TYPE_VPN:
-       case CONNMAN_SERVICE_TYPE_GADGET:
-               return;
-       case CONNMAN_SERVICE_TYPE_WIFI:
-               required = FALSE;
-
-               switch (service->security) {
-               case CONNMAN_SERVICE_SECURITY_UNKNOWN:
-               case CONNMAN_SERVICE_SECURITY_NONE:
-                       break;
-               case CONNMAN_SERVICE_SECURITY_WEP:
-               case CONNMAN_SERVICE_SECURITY_PSK:
-               case CONNMAN_SERVICE_SECURITY_WPA:
-               case CONNMAN_SERVICE_SECURITY_RSN:
-                       if (service->passphrase == NULL)
-                               required = TRUE;
-                       break;
-               case CONNMAN_SERVICE_SECURITY_8021X:
-                       break;
-               }
-               break;
-       }
-
-       connman_dbus_property_changed_basic(service->path,
-                               CONNMAN_SERVICE_INTERFACE, "PassphraseRequired",
-                                               DBUS_TYPE_BOOLEAN, &required);
-}
-
-static void login_changed(struct connman_service *service)
-{
-       dbus_bool_t required = service->login_required;
-
-       if (service->path == NULL)
+       if (allow_property_changed(service) == FALSE)
                return;
 
        connman_dbus_property_changed_basic(service->path,
-                               CONNMAN_SERVICE_INTERFACE, "LoginRequired",
-                                               DBUS_TYPE_BOOLEAN, &required);
+                               CONNMAN_SERVICE_INTERFACE, "AutoConnect",
+                               DBUS_TYPE_BOOLEAN, &service->autoconnect);
 }
 
 static void append_security(DBusMessageIter *iter, void *user_data)
@@ -1555,6 +1742,9 @@ static void append_ipv4(DBusMessageIter *iter, void *user_data)
 {
        struct connman_service *service = user_data;
 
+#if defined TIZEN_EXT
+       if (service->state_ipv4 != CONNMAN_SERVICE_STATE_IDLE)
+#endif
        DBG("ipv4 %p state %s", service->ipconfig_ipv4,
                                state2string(service->state_ipv4));
 
@@ -1569,6 +1759,9 @@ static void append_ipv6(DBusMessageIter *iter, void *user_data)
 {
        struct connman_service *service = user_data;
 
+#if defined TIZEN_EXT
+       if (service->state_ipv6 != CONNMAN_SERVICE_STATE_IDLE)
+#endif
        DBG("ipv6 %p state %s", service->ipconfig_ipv6,
                                state2string(service->state_ipv6));
 
@@ -1598,14 +1791,16 @@ static void append_ipv6config(DBusMessageIter *iter, void *user_data)
                                                        iter);
 }
 
-static void append_nameserver(DBusMessageIter *iter, char ***nameservers)
+static void append_nameservers(DBusMessageIter *iter, char **servers)
 {
-       char **servers;
        int i;
 
-       servers = *nameservers;
+#if !defined TIZEN_EXT
+       DBG("%p", servers);
+#endif
 
        for (i = 0; servers[i] != NULL; i++) {
+               DBG("servers[%d] %s", i, servers[i]);
                dbus_message_iter_append_basic(iter,
                                        DBUS_TYPE_STRING, &servers[i]);
        }
@@ -1619,45 +1814,55 @@ static void append_dns(DBusMessageIter *iter, void *user_data)
                return;
 
        if (service->nameservers_config != NULL) {
-               append_nameserver(iter, &service->nameservers_config);
+               append_nameservers(iter, service->nameservers_config);
                return;
        } else {
                if (service->nameservers != NULL)
-                       append_nameserver(iter, &service->nameservers);
+                       append_nameservers(iter, service->nameservers);
 
                if (service->nameservers_auto != NULL)
-                       append_nameserver(iter, &service->nameservers_auto);
+                       append_nameservers(iter, service->nameservers_auto);
        }
 }
 
 static void append_dnsconfig(DBusMessageIter *iter, void *user_data)
 {
        struct connman_service *service = user_data;
-       int i;
 
        if (service->nameservers_config == NULL)
                return;
 
-       for (i = 0; service->nameservers_config[i]; i++) {
-               dbus_message_iter_append_basic(iter,
-                               DBUS_TYPE_STRING,
-                               &service->nameservers_config[i]);
+       append_nameservers(iter, service->nameservers_config);
+}
+
+static void append_ts(DBusMessageIter *iter, void *user_data)
+{
+       GSList *list = user_data;
+
+       while (list != NULL) {
+               char *timeserver = list->data;
+
+               if (timeserver != NULL)
+                       dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
+                                       &timeserver);
+
+               list = g_slist_next(list);
        }
 }
 
-static void append_domain(DBusMessageIter *iter, void *user_data)
+static void append_tsconfig(DBusMessageIter *iter, void *user_data)
 {
        struct connman_service *service = user_data;
+       int i;
 
-       if (is_connected(service) == FALSE &&
-                               is_connecting(service) == FALSE)
-               return;
-
-       if (service->domainname == NULL)
+       if (service->timeservers_config == NULL)
                return;
 
-       dbus_message_iter_append_basic(iter,
-                               DBUS_TYPE_STRING, &service->domainname);
+       for (i = 0; service->timeservers_config[i]; i++) {
+               dbus_message_iter_append_basic(iter,
+                               DBUS_TYPE_STRING,
+                               &service->timeservers_config[i]);
+       }
 }
 
 static void append_domainconfig(DBusMessageIter *iter, void *user_data)
@@ -1673,6 +1878,21 @@ static void append_domainconfig(DBusMessageIter *iter, void *user_data)
                                DBUS_TYPE_STRING, &service->domains[i]);
 }
 
+static void append_domain(DBusMessageIter *iter, void *user_data)
+{
+       struct connman_service *service = user_data;
+
+       if (is_connected(service) == FALSE &&
+                               is_connecting(service) == FALSE)
+               return;
+
+       if (service->domains != NULL)
+               append_domainconfig(iter, user_data);
+       else if (service->domainname != NULL)
+               dbus_message_iter_append_basic(iter,
+                               DBUS_TYPE_STRING, &service->domainname);
+}
+
 static void append_proxies(DBusMessageIter *iter, void *user_data)
 {
        struct connman_service *service = user_data;
@@ -1707,7 +1927,9 @@ static void append_proxy(DBusMessageIter *iter, void *user_data)
        const char *method = proxymethod2string(
                CONNMAN_SERVICE_PROXY_METHOD_DIRECT);
 
+#if !defined TIZEN_EXT
        DBG("");
+#endif
 
        if (is_connected(service) == FALSE)
                return;
@@ -1796,7 +2018,9 @@ static void append_provider(DBusMessageIter *iter, void *user_data)
 {
        struct connman_service *service = user_data;
 
+#if !defined TIZEN_EXT
        DBG("%p %p", service, service->provider);
+#endif
 
        if (is_connected(service) == FALSE)
                return;
@@ -1809,6 +2033,9 @@ static void append_provider(DBusMessageIter *iter, void *user_data)
 static void settings_changed(struct connman_service *service,
                                struct connman_ipconfig *ipconfig)
 {
+       if (allow_property_changed(service) == FALSE)
+               return;
+
        connman_dbus_property_changed_dict(service->path,
                                        CONNMAN_SERVICE_INTERFACE, "IPv4",
                                                        append_ipv4, service);
@@ -1822,6 +2049,9 @@ static void settings_changed(struct connman_service *service,
 
 static void ipv4_configuration_changed(struct connman_service *service)
 {
+       if (allow_property_changed(service) == FALSE)
+               return;
+
        connman_dbus_property_changed_dict(service->path,
                                        CONNMAN_SERVICE_INTERFACE,
                                                        "IPv4.Configuration",
@@ -1831,6 +2061,9 @@ static void ipv4_configuration_changed(struct connman_service *service)
 
 static void ipv6_configuration_changed(struct connman_service *service)
 {
+       if (allow_property_changed(service) == FALSE)
+               return;
+
        connman_dbus_property_changed_dict(service->path,
                                        CONNMAN_SERVICE_INTERFACE,
                                                        "IPv6.Configuration",
@@ -1840,6 +2073,9 @@ static void ipv6_configuration_changed(struct connman_service *service)
 
 static void dns_changed(struct connman_service *service)
 {
+       if (allow_property_changed(service) == FALSE)
+               return;
+
        connman_dbus_property_changed_array(service->path,
                                CONNMAN_SERVICE_INTERFACE, "Nameservers",
                                        DBUS_TYPE_STRING, append_dns, service);
@@ -1847,6 +2083,9 @@ static void dns_changed(struct connman_service *service)
 
 static void dns_configuration_changed(struct connman_service *service)
 {
+       if (allow_property_changed(service) == FALSE)
+               return;
+
        connman_dbus_property_changed_array(service->path,
                                CONNMAN_SERVICE_INTERFACE,
                                "Nameservers.Configuration",
@@ -1857,6 +2096,9 @@ static void dns_configuration_changed(struct connman_service *service)
 
 static void domain_changed(struct connman_service *service)
 {
+       if (allow_property_changed(service) == FALSE)
+               return;
+
        connman_dbus_property_changed_array(service->path,
                                CONNMAN_SERVICE_INTERFACE, "Domains",
                                DBUS_TYPE_STRING, append_domain, service);
@@ -1864,216 +2106,63 @@ static void domain_changed(struct connman_service *service)
 
 static void domain_configuration_changed(struct connman_service *service)
 {
+       if (allow_property_changed(service) == FALSE)
+               return;
+
        connman_dbus_property_changed_array(service->path,
                                CONNMAN_SERVICE_INTERFACE,
                                "Domains.Configuration",
                                DBUS_TYPE_STRING, append_domainconfig, service);
 }
 
-#if defined TIZEN_EXT
-static int __connman_service_dbus_update_default_info(int type, ...)
+static void proxy_changed(struct connman_service *service)
 {
-#define NETCONFIG_SERVICE                                      "net.netconfig"
-#define NETCONFIG_NETWORK_STATE_INTERFACE      NETCONFIG_SERVICE ".network"
-#define NETCONFIG_NETWORK_STATE_PATH           "/net/netconfig/network"
-       DBusMessage *message;
-       DBusPendingCall *call;
-       dbus_bool_t ok;
-       va_list va;
-
-       message = dbus_message_new_method_call(NETCONFIG_SERVICE,
-                       NETCONFIG_NETWORK_STATE_PATH,
-                       NETCONFIG_NETWORK_STATE_INTERFACE, "UpdateDefaultConnectionInfo");
-
-       if (message == NULL)
-               return -ENOMEM;
-
-       va_start(va, type);
-       ok = dbus_message_append_args_valist(message, type, va);
-       va_end(va);
-
-       if (!ok)
-               return -ENOMEM;
-
-       if (dbus_connection_send_with_reply(connection, message, &call, 40000) == FALSE) {
-               connman_error("Failed to call %s.%s", NETCONFIG_NETWORK_STATE_INTERFACE,
-                               "UpdateDefaultConnectionInfo");
-               dbus_message_unref(message);
-               return -EINVAL;
-       }
-
-       if (call == NULL) {
-               connman_error("D-Bus connection not available");
-               dbus_message_unref(message);
-               return -EINVAL;
-       }
-
-       dbus_message_unref(message);
+       if (allow_property_changed(service) == FALSE)
+               return;
 
-       return -EINPROGRESS;
+       connman_dbus_property_changed_dict(service->path,
+                                       CONNMAN_SERVICE_INTERFACE, "Proxy",
+                                                       append_proxy, service);
 }
 
-static connman_bool_t __connman_service_is_internet_profile(
-               struct connman_service *cellular)
+static void proxy_configuration_changed(struct connman_service *service)
 {
-#define CONNMAN_CELLULAR_SERVICE_PROFILE_PREFIX        CONNMAN_PATH "/service/cellular_"
-       DBG("Service path: %s", cellular->path);
-
-       const char internet_suffix[] = "_1";
-       char *suffix = NULL;
+       if (allow_property_changed(service) == FALSE)
+               return;
 
-       if (g_str_has_prefix(cellular->path, CONNMAN_CELLULAR_SERVICE_PROFILE_PREFIX) == TRUE) {
-               suffix = strrchr(cellular->path, '_');
-               if (strcmp(suffix, internet_suffix) == 0)
-                       return TRUE;
-       }
+       connman_dbus_property_changed_dict(service->path,
+                       CONNMAN_SERVICE_INTERFACE, "Proxy.Configuration",
+                                               append_proxyconfig, service);
 
-       DBG("Not Internet profile.");
-       return FALSE;
+       proxy_changed(service);
 }
 
-static struct connman_service *__connman_service_get_default(void)
+static void timeservers_configuration_changed(struct connman_service *service)
 {
-       GSequenceIter *iter;
-       struct connman_service *default_service = NULL;
-
-       iter = g_sequence_get_begin_iter(service_list);
-
-       while (g_sequence_iter_is_end(iter) == FALSE) {
-               struct connman_service *service = g_sequence_get(iter);
-
-               DBG("service: %p %s %s %s", service, service->name,
-                               state2string(service->state),
-                               __connman_service_type2string(service->type));
-
-               if (service->type == CONNMAN_SERVICE_TYPE_WIFI &&
-                               is_connected(service) == TRUE)
-                       return service;
-
-               if (service->type == CONNMAN_SERVICE_TYPE_CELLULAR)
-                       if (__connman_service_is_internet_profile(service) == TRUE) {
-                               if (default_service == NULL)
-                                       default_service = service;
-                               else if (is_connected(default_service) == FALSE &&
-                                               is_connected(service) == TRUE)
-                                       default_service = service;
-                       }
-
-               iter = g_sequence_iter_next(iter);
-       }
+       if (allow_property_changed(service) == FALSE)
+               return;
 
-       return default_service;
+       connman_dbus_property_changed_array(service->path,
+                       CONNMAN_SERVICE_INTERFACE,
+                       "Timeservers.Configuration",
+                       DBUS_TYPE_STRING,
+                       append_tsconfig, service);
 }
 
-static void __connman_service_update_default_info(struct connman_service *service)
+static void link_changed(struct connman_service *service)
 {
-       struct connman_ipconfig *ip_config = NULL;
-       gchar *connection_type = NULL, *connection_state = NULL;
-       gchar *ip_addr = NULL, *proxy_addr = NULL;
-       gchar **proxy_list = NULL;
-
-       ip_config = __connman_service_get_ip4config(service);
-       if (ip_config == NULL)
+       if (allow_property_changed(service) == FALSE)
                return;
 
-       DBG("Update state %p %s %s", service, service->path, state2string(service->state));
-
-       ip_addr = g_strdup(__connman_ipconfig_get_local(ip_config));
-       if (ip_addr == NULL)
-               ip_addr = g_strdup("");
-
-       connection_type = g_strdup(
-                       __connman_service_type2string(connman_service_get_type(service)));
-
-       connection_state = g_strdup(state2string(service->state));
-
-       proxy_list = connman_service_get_proxy_servers(service);
-       if (proxy_list != NULL)
-               proxy_addr = g_strdup(proxy_list[0]);
-       if (proxy_addr == NULL)
-               proxy_addr = g_strdup("");
-
-       __connman_service_dbus_update_default_info(
-                       DBUS_TYPE_STRING, &connection_type, DBUS_TYPE_STRING, &connection_state,
-                       DBUS_TYPE_STRING, &ip_addr, DBUS_TYPE_STRING, &proxy_addr, DBUS_TYPE_INVALID);
-
-       g_free(connection_type);
-       g_free(connection_state);
-       g_free(ip_addr);
-       g_free(proxy_addr);
-       g_strfreev(proxy_list);
+       connman_dbus_property_changed_dict(service->path,
+                                       CONNMAN_SERVICE_INTERFACE, "Ethernet",
+                                               append_ethernet, service);
 }
 
-static void __connman_service_update_default(struct connman_service *service)
-{
-       struct connman_service *default_service = NULL;
-
-       default_service = __connman_service_get_default();
-
-       DBG("service %p %s %s", service, service->path, state2string(service->state));
-       if (default_service != NULL)
-               DBG("default service %p %s %s", default_service, default_service->path,
-                               state2string(default_service->state));
-       else
-               DBG("default service is NULL");
-
-       if (is_connected(service) == TRUE) {
-               if (service == default_service)
-                       __connman_service_update_default_info(service);
-
-               return;
-       }
-
-       if (default_service == NULL) {
-               __connman_service_update_default_info(service);
-               return;
-       }
-
-       if (service != default_service) {
-               if (default_service->type == CONNMAN_SERVICE_TYPE_WIFI)
-                       return;
-
-               if (is_connected(default_service) == TRUE)
-                       __connman_service_update_default_info(default_service);
-               else
-                       __connman_service_update_default_info(service);
-       } else
-               __connman_service_update_default_info(service);
-}
-#endif
-
-static void proxy_changed(struct connman_service *service)
-{
-       connman_dbus_property_changed_dict(service->path,
-                                       CONNMAN_SERVICE_INTERFACE, "Proxy",
-                                                       append_proxy, service);
-#if defined TIZEN_EXT
-       DBG("");
-       if (is_connected(service) == TRUE)
-               __connman_service_update_default(service);
-#endif
-}
-
-static void proxy_configuration_changed(struct connman_service *service)
-{
-       connman_dbus_property_changed_dict(service->path,
-                       CONNMAN_SERVICE_INTERFACE, "Proxy.Configuration",
-                                               append_proxyconfig, service);
-
-       proxy_changed(service);
-}
-
-static void link_changed(struct connman_service *service)
-{
-       connman_dbus_property_changed_dict(service->path,
-                                       CONNMAN_SERVICE_INTERFACE, "Ethernet",
-                                               append_ethernet, service);
-}
-
-static void stats_append_counters(DBusMessageIter *dict,
-                       struct connman_stats_data *stats,
-                       struct connman_stats_data *counters,
-                       connman_bool_t append_all)
+static void stats_append_counters(DBusMessageIter *dict,
+                       struct connman_stats_data *stats,
+                       struct connman_stats_data *counters,
+                       connman_bool_t append_all)
 {
        if (counters->rx_packets != stats->rx_packets || append_all) {
                counters->rx_packets = stats->rx_packets;
@@ -2215,28 +2304,6 @@ static void stats_update(struct connman_service *service,
        stats->data.time = stats->data_last.time + seconds;
 }
 
-static char *wifi_build_group_name(const unsigned char *ssid,
-                                               unsigned int ssid_len,
-                                                       const char *mode,
-                                                       const char *security)
-{
-       GString *str;
-       unsigned int i;
-
-       /* the last 3 is for the 2 '_' and '\0' */
-       str = g_string_sized_new((ssid_len * 2) + strlen(mode)
-                                       + strlen(security) + 3);
-       if (str == NULL)
-               return NULL;
-
-       for (i = 0; i < ssid_len; i++)
-               g_string_append_printf(str, "%02x", ssid[i]);
-
-       g_string_append_printf(str, "_%s_%s", mode, security);
-
-       return g_string_free(str, FALSE);
-}
-
 void __connman_service_notify(struct connman_service *service,
                        unsigned int rx_packets, unsigned int tx_packets,
                        unsigned int rx_bytes, unsigned int tx_bytes,
@@ -2286,7 +2353,7 @@ int __connman_service_counter_register(const char *counter)
 
        DBG("counter %s", counter);
 
-       counter_list = g_slist_append(counter_list, (gpointer)counter);
+       counter_list = g_slist_prepend(counter_list, (gpointer)counter);
 
        iter = g_sequence_get_begin_iter(service_list);
 
@@ -2385,20 +2452,22 @@ connman_bool_t __connman_service_session_dec(struct connman_service *service)
 static void append_wifi_ext_info(DBusMessageIter *dict,
                                        struct connman_network *network)
 {
-       unsigned char bssid_buff[18] = {0,};
-       unsigned char *bssid_str = bssid_buff;
+       char bssid_buff[WIFI_BSSID_STR_LEN] = { 0, };
+       char *bssid_str = bssid_buff;
        unsigned char *bssid;
        unsigned int maxrate;
        connman_uint16_t frequency;
        const char *enc_mode;
        const char *str;
+       gboolean passpoint;
 
        bssid = connman_network_get_bssid(network);
        maxrate = connman_network_get_maxrate(network);
        frequency = connman_network_get_frequency(network);
        enc_mode = connman_network_get_enc_mode(network);
+       passpoint = connman_network_get_is_hs20AP(network);
 
-       snprintf((char *)bssid_str, 18, "%02x:%02x:%02x:%02x:%02x:%02x",
+       snprintf(bssid_str, WIFI_BSSID_STR_LEN, "%02x:%02x:%02x:%02x:%02x:%02x",
                                bssid[0], bssid[1], bssid[2],
                                bssid[3], bssid[4], bssid[5]);
 
@@ -2410,6 +2479,8 @@ static void append_wifi_ext_info(DBusMessageIter *dict,
                                        DBUS_TYPE_UINT16, &frequency);
        connman_dbus_dict_append_basic(dict, "EncryptionMode",
                                        DBUS_TYPE_STRING, &enc_mode);
+       connman_dbus_dict_append_basic(dict, "Passpoint",
+                                       DBUS_TYPE_BOOLEAN, &passpoint);
 
        str = connman_network_get_string(network, "WiFi.Security");
 
@@ -2429,30 +2500,22 @@ static void append_wifi_ext_info(DBusMessageIter *dict,
                        connman_dbus_dict_append_basic(dict, "Identity",
                                        DBUS_TYPE_STRING, &str);
 
-               str = connman_network_get_string(network, "WiFi.Passphrase");
-               if (str != NULL)
-                       connman_dbus_dict_append_basic(dict, "Password",
-                                       DBUS_TYPE_STRING, &str);
-
                str = connman_network_get_string(network, "WiFi.CACertFile");
                if (str != NULL)
                        connman_dbus_dict_append_basic(dict, "CACertFile",
                                        DBUS_TYPE_STRING, &str);
 
-               str = connman_network_get_string(network, "WiFi.ClientCertFile");
+               str = connman_network_get_string(network,
+                                                       "WiFi.ClientCertFile");
                if (str != NULL)
                        connman_dbus_dict_append_basic(dict, "ClientCertFile",
                                        DBUS_TYPE_STRING, &str);
 
-               str = connman_network_get_string(network, "WiFi.PrivateKeyFile");
+               str = connman_network_get_string(network,
+                                                       "WiFi.PrivateKeyFile");
                if (str != NULL)
                        connman_dbus_dict_append_basic(dict, "PrivateKeyFile",
                                        DBUS_TYPE_STRING, &str);
-
-               str = connman_network_get_string(network, "WiFi.PrivateKeyPassphrase");
-               if (str != NULL)
-                       connman_dbus_dict_append_basic(dict, "PrivateKeyPassphrase",
-                                       DBUS_TYPE_STRING, &str);
        }
 }
 #endif
@@ -2460,8 +2523,8 @@ static void append_wifi_ext_info(DBusMessageIter *dict,
 static void append_properties(DBusMessageIter *dict, dbus_bool_t limited,
                                        struct connman_service *service)
 {
-       dbus_bool_t required;
        const char *str;
+       GSList *list;
 
        str = __connman_service_type2string(service->type);
        if (str != NULL)
@@ -2497,14 +2560,15 @@ static void append_properties(DBusMessageIter *dict, dbus_bool_t limited,
        else
                connman_dbus_dict_append_basic(dict, "AutoConnect",
                                        DBUS_TYPE_BOOLEAN, &service->favorite);
+#if defined TIZEN_EXT
+       connman_dbus_dict_append_basic(dict, "LocalNetwork",
+                                       DBUS_TYPE_BOOLEAN, &service->localnetwork);
+#endif
 
        if (service->name != NULL)
                connman_dbus_dict_append_basic(dict, "Name",
                                        DBUS_TYPE_STRING, &service->name);
 
-       connman_dbus_dict_append_basic(dict, "LoginRequired",
-                               DBUS_TYPE_BOOLEAN, &service->login_required);
-
        switch (service->type) {
        case CONNMAN_SERVICE_TYPE_UNKNOWN:
        case CONNMAN_SERVICE_TYPE_SYSTEM:
@@ -2520,38 +2584,15 @@ static void append_properties(DBusMessageIter *dict, dbus_bool_t limited,
                                                append_ethernet, service);
                break;
        case CONNMAN_SERVICE_TYPE_WIFI:
-               if (service->passphrase != NULL && limited == FALSE)
-                       connman_dbus_dict_append_basic(dict, "Passphrase",
-                               DBUS_TYPE_STRING, &service->passphrase);
-
-               required = FALSE;
-
-               switch (service->security) {
-               case CONNMAN_SERVICE_SECURITY_UNKNOWN:
-               case CONNMAN_SERVICE_SECURITY_NONE:
-                       break;
-               case CONNMAN_SERVICE_SECURITY_WEP:
-               case CONNMAN_SERVICE_SECURITY_PSK:
-               case CONNMAN_SERVICE_SECURITY_WPA:
-               case CONNMAN_SERVICE_SECURITY_RSN:
-                       if (service->passphrase == NULL)
-                               required = TRUE;
-                       break;
-               case CONNMAN_SERVICE_SECURITY_8021X:
-                       break;
-               }
-
-               connman_dbus_dict_append_basic(dict, "PassphraseRequired",
-                                               DBUS_TYPE_BOOLEAN, &required);
-
 #if defined TIZEN_EXT
                if (service->network != NULL)
                        append_wifi_ext_info(dict, service->network);
-#endif
 
-               /* fall through */
+               connman_dbus_dict_append_dict(dict, "Ethernet",
+                                               append_ethernet, service);
+               break;
+#endif
        case CONNMAN_SERVICE_TYPE_ETHERNET:
-       case CONNMAN_SERVICE_TYPE_WIMAX:
        case CONNMAN_SERVICE_TYPE_BLUETOOTH:
                connman_dbus_dict_append_dict(dict, "Ethernet",
                                                append_ethernet, service);
@@ -2574,6 +2615,20 @@ static void append_properties(DBusMessageIter *dict, dbus_bool_t limited,
        connman_dbus_dict_append_array(dict, "Nameservers.Configuration",
                                DBUS_TYPE_STRING, append_dnsconfig, service);
 
+       if (service->state == CONNMAN_SERVICE_STATE_READY ||
+                       service->state == CONNMAN_SERVICE_STATE_ONLINE)
+               list = __connman_timeserver_get_all(service);
+       else
+               list = NULL;
+
+       connman_dbus_dict_append_array(dict, "Timeservers",
+                               DBUS_TYPE_STRING, append_ts, list);
+
+       g_slist_free_full(list, g_free);
+
+       connman_dbus_dict_append_array(dict, "Timeservers.Configuration",
+                               DBUS_TYPE_STRING, append_tsconfig, service);
+
        connman_dbus_dict_append_array(dict, "Domains",
                                DBUS_TYPE_STRING, append_domain, service);
 
@@ -2589,32 +2644,76 @@ static void append_properties(DBusMessageIter *dict, dbus_bool_t limited,
                                                append_provider, service);
 }
 
-static void append_struct(gpointer value, gpointer user_data)
+static void append_struct_service(DBusMessageIter *iter,
+               connman_dbus_append_cb_t function,
+               struct connman_service *service)
 {
-       struct connman_service *service = value;
-       DBusMessageIter *iter = user_data;
        DBusMessageIter entry, dict;
 
-       if (service->path == NULL || service->hidden == TRUE)
-               return;
-
        dbus_message_iter_open_container(iter, DBUS_TYPE_STRUCT, NULL, &entry);
 
        dbus_message_iter_append_basic(&entry, DBUS_TYPE_OBJECT_PATH,
                                                        &service->path);
 
+#if defined TIZEN_EXT
+       DBG("%s", service->path);
+#endif
        connman_dbus_dict_open(&entry, &dict);
-       append_properties(&dict, TRUE, service);
+       if (function != NULL)
+               function(&dict, service);
        connman_dbus_dict_close(&entry, &dict);
 
        dbus_message_iter_close_container(iter, &entry);
 }
 
+static void append_dict_properties(DBusMessageIter *dict, void *user_data)
+{
+       struct connman_service *service = user_data;
+
+       append_properties(dict, TRUE, service);
+}
+
+static void append_struct(gpointer value, gpointer user_data)
+{
+       struct connman_service *service = value;
+       DBusMessageIter *iter = user_data;
+
+       if (service->path == NULL)
+               return;
+
+       append_struct_service(iter, append_dict_properties, service);
+}
+
 void __connman_service_list_struct(DBusMessageIter *iter)
 {
        g_sequence_foreach(service_list, append_struct, iter);
 }
 
+connman_bool_t __connman_service_is_hidden(struct connman_service *service)
+{
+       return service->hidden;
+}
+
+connman_bool_t
+__connman_service_is_split_routing(struct connman_service *service)
+{
+       return service->do_split_routing;
+}
+
+connman_bool_t __connman_service_index_is_split_routing(int index)
+{
+       struct connman_service *service;
+
+       if (index < 0)
+               return FALSE;
+
+       service = __connman_service_lookup_from_index(index);
+       if (!service)
+               return FALSE;
+
+       return __connman_service_is_split_routing(service);
+}
+
 int __connman_service_get_index(struct connman_service *service)
 {
        if (service == NULL)
@@ -2628,10 +2727,18 @@ int __connman_service_get_index(struct connman_service *service)
        return -1;
 }
 
+void __connman_service_set_hidden(struct connman_service *service)
+{
+       if (service == NULL || service->hidden == TRUE)
+               return;
+
+       service->hidden_service = TRUE;
+}
+
 void __connman_service_set_domainname(struct connman_service *service,
                                                const char *domainname)
 {
-       if (service == NULL)
+       if (service == NULL || service->hidden == TRUE)
                return;
 
        g_free(service->domainname);
@@ -2657,17 +2764,102 @@ char **connman_service_get_nameservers(struct connman_service *service)
                return NULL;
 
        if (service->nameservers_config != NULL)
-               return service->nameservers_config;
-       else if (service->nameservers != NULL)
-               return service->nameservers;
+               return g_strdupv(service->nameservers_config);
+       else if (service->nameservers != NULL ||
+                                       service->nameservers_auto != NULL) {
+               int len = 0, len_auto = 0, i;
+               char **nameservers;
+
+               if (service->nameservers != NULL)
+                       len = g_strv_length(service->nameservers);
+               if (service->nameservers_auto != NULL)
+                       len_auto = g_strv_length(service->nameservers_auto);
+
+               nameservers = g_try_new0(char *, len + len_auto + 1);
+               if (nameservers == NULL)
+                       return NULL;
+
+               for (i = 0; i < len; i++)
+                       nameservers[i] = g_strdup(service->nameservers[i]);
+
+               for (i = 0; i < len_auto; i++)
+                       nameservers[i + len] =
+                               g_strdup(service->nameservers_auto[i]);
+
+               return nameservers;
+       }
 
        return NULL;
 }
 
+char **connman_service_get_timeservers_config(struct connman_service *service)
+{
+       if (service == NULL)
+               return NULL;
+
+       return service->timeservers_config;
+}
+
+char **connman_service_get_timeservers(struct connman_service *service)
+{
+       if (service == NULL)
+               return NULL;
+
+       return service->timeservers;
+}
+
+#if defined TIZEN_EXT
+/*
+ * Description: Telephony plug-in requires manual PROXY setting function
+ */
+int connman_service_set_proxy(struct connman_service *service,
+                                       const char *proxy, gboolean active)
+{
+       char **proxies_array = NULL;
+
+       if (service == NULL)
+               return -EINVAL;
+
+       switch (service->type) {
+       case CONNMAN_SERVICE_TYPE_CELLULAR:
+       case CONNMAN_SERVICE_TYPE_ETHERNET:
+       case CONNMAN_SERVICE_TYPE_WIFI:
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       g_strfreev(service->proxies);
+       service->proxies = NULL;
+
+       if (proxy != NULL)
+               proxies_array = g_strsplit(proxy, " ", 0);
+
+       service->proxies = proxies_array;
+
+       if (proxy == NULL) {
+               service->proxy_config = CONNMAN_SERVICE_PROXY_METHOD_DIRECT;
+               DBG("proxy changed (%d)", active);
+       } else {
+               service->proxy_config = CONNMAN_SERVICE_PROXY_METHOD_MANUAL;
+               DBG("proxy chagned %s (%d)", proxy, active);
+       }
+
+       if (active == TRUE) {
+               proxy_changed(service);
+
+               __connman_notifier_proxy_changed(service);
+       }
+
+       return 0;
+}
+#endif
+
 void connman_service_set_proxy_method(struct connman_service *service,
                                        enum connman_service_proxy_method method)
 {
-       if (service == NULL)
+       if (service == NULL || service->hidden == TRUE)
                return;
 
        service->proxy = method;
@@ -2716,7 +2908,7 @@ const char *connman_service_get_proxy_url(struct connman_service *service)
 void __connman_service_set_proxy_autoconfig(struct connman_service *service,
                                                        const char *url)
 {
-       if (service == NULL)
+       if (service == NULL || service->hidden == TRUE)
                return;
 
        service->proxy = CONNMAN_SERVICE_PROXY_METHOD_AUTO;
@@ -2751,33 +2943,6 @@ const char *connman_service_get_proxy_autoconfig(struct connman_service *service
        return NULL;
 }
 
-static void update_timeservers(struct connman_service *service)
-{
-       int i;
-
-       if (service->timeservers == NULL)
-               return;
-
-       switch (service->state) {
-       case CONNMAN_SERVICE_STATE_UNKNOWN:
-       case CONNMAN_SERVICE_STATE_IDLE:
-       case CONNMAN_SERVICE_STATE_ASSOCIATION:
-       case CONNMAN_SERVICE_STATE_CONFIGURATION:
-               return;
-       case CONNMAN_SERVICE_STATE_FAILURE:
-       case CONNMAN_SERVICE_STATE_DISCONNECT:
-               for (i = 0; service->timeservers[i] != NULL; i++)
-                       connman_timeserver_remove(service->timeservers[i]);
-               return;
-       case CONNMAN_SERVICE_STATE_READY:
-       case CONNMAN_SERVICE_STATE_ONLINE:
-               break;
-       }
-
-       for (i = 0; service->timeservers[i] != NULL; i++)
-               connman_timeserver_append(service->timeservers[i]);
-}
-
 int __connman_service_timeserver_append(struct connman_service *service,
                                                const char *timeserver)
 {
@@ -2809,8 +2974,6 @@ int __connman_service_timeserver_append(struct connman_service *service,
        service->timeservers[len] = g_strdup(timeserver);
        service->timeservers[len + 1] = NULL;
 
-       update_timeservers(service);
-
        return 0;
 }
 
@@ -2818,7 +2981,7 @@ int __connman_service_timeserver_remove(struct connman_service *service,
                                                const char *timeserver)
 {
        char **servers;
-       int len, i, j;
+       int len, i, j, found = 0;
 
        DBG("service %p timeserver %s", service, timeserver);
 
@@ -2828,70 +2991,75 @@ int __connman_service_timeserver_remove(struct connman_service *service,
        if (service->timeservers == NULL)
                return 0;
 
+       for (i = 0; service->timeservers != NULL &&
+                                       service->timeservers[i] != NULL; i++)
+               if (g_strcmp0(service->timeservers[i], timeserver) == 0) {
+                       found = 1;
+                       break;
+               }
+
+       if (found == 0)
+               return 0;
+
        len = g_strv_length(service->timeservers);
-       if (len == 1) {
-               if (g_strcmp0(service->timeservers[0], timeserver) != 0)
-                       return 0;
 
+       if (len == 1) {
                g_strfreev(service->timeservers);
                service->timeservers = NULL;
 
                return 0;
        }
 
-       servers = g_try_new0(char *, len - 1);
+       servers = g_try_new0(char *, len);
        if (servers == NULL)
                return -ENOMEM;
 
        for (i = 0, j = 0; i < len; i++) {
                if (g_strcmp0(service->timeservers[i], timeserver) != 0) {
                        servers[j] = g_strdup(service->timeservers[i]);
+                       if (servers[j] == NULL)
+                               return -ENOMEM;
                        j++;
                }
        }
-       servers[len - 2] = NULL;
+       servers[len - 1] = NULL;
 
        g_strfreev(service->timeservers);
        service->timeservers = servers;
 
-       update_timeservers(service);
-
        return 0;
 }
 
+void __connman_service_timeserver_changed(struct connman_service *service,
+               GSList *ts_list)
+{
+       if (service == NULL)
+               return;
+
+       if (allow_property_changed(service) == FALSE)
+               return;
+
+       connman_dbus_property_changed_array(service->path,
+                       CONNMAN_SERVICE_INTERFACE, "Timeservers",
+                       DBUS_TYPE_STRING, append_ts, ts_list);
+}
+
 void __connman_service_set_pac(struct connman_service *service,
                                        const char *pac)
 {
+       if (service->hidden == TRUE)
+               return;
        g_free(service->pac);
        service->pac = g_strdup(pac);
 
        proxy_changed(service);
 }
 
-#if defined TIZEN_EXT
-/*
- * Description: Telephony plug-in requires manual PROXY setting function
- */
-void __connman_service_set_proxy(struct connman_service *service,
-                                       const char *proxies)
+void __connman_service_set_identity(struct connman_service *service,
+                                       const char *identity)
 {
-       char **proxies_array = NULL;
-
-       g_strfreev(service->proxies);
-       service->proxies = NULL;
-
-       if (proxies != NULL)
-               proxies_array = g_strsplit(proxies, " ", 0);
-
-       service->proxies = proxies_array;
-}
-#endif
-
-void __connman_service_set_identity(struct connman_service *service,
-                                       const char *identity)
-{
-       if (service->immutable)
-               return;
+       if (service->immutable || service->hidden == TRUE)
+               return;
 
        g_free(service->identity);
        service->identity = g_strdup(identity);
@@ -2905,6 +3073,8 @@ void __connman_service_set_identity(struct connman_service *service,
 void __connman_service_set_agent_identity(struct connman_service *service,
                                                const char *agent_identity)
 {
+       if (service->hidden == TRUE)
+               return;
        g_free(service->agent_identity);
        service->agent_identity = g_strdup(agent_identity);
 
@@ -2914,28 +3084,100 @@ void __connman_service_set_agent_identity(struct connman_service *service,
                                        service->agent_identity);
 }
 
-void __connman_service_set_passphrase(struct connman_service *service,
-                                       const char* passphrase)
+static int check_passphrase(struct connman_service *service,
+                               enum connman_service_security security,
+                               const char *passphrase)
 {
-       if (service->immutable == TRUE)
-               return;
+       guint i;
+       gsize length;
 
-       g_free(service->passphrase);
-       service->passphrase = g_strdup(passphrase);
+       if (passphrase == NULL) {
+               /*
+                * This will prevent __connman_service_set_passphrase() to
+                * wipe the passphrase out in case of -ENOKEY error for a
+                * favorite service. */
+               if (service->favorite == TRUE)
+                       return 1;
+               else
+                       return 0;
+       }
 
-       passphrase_changed(service);
+       length = strlen(passphrase);
 
-       if (service->network != NULL)
-               connman_network_set_string(service->network,
-                                       "WiFi.Passphrase",
-                                       service->passphrase);
+       switch (security) {
+       case CONNMAN_SERVICE_SECURITY_PSK:
+       case CONNMAN_SERVICE_SECURITY_WPA:
+       case CONNMAN_SERVICE_SECURITY_RSN:
+               /* A raw key is always 64 bytes length,
+                * its content is in hex representation.
+                * A PSK key must be between [8..63].
+                */
+               if (length == 64) {
+                       for (i = 0; i < 64; i++)
+                               if (!isxdigit((unsigned char)
+                                             passphrase[i]))
+                                       return -ENOKEY;
+               } else if (length < 8 || length > 63)
+                       return -ENOKEY;
+               break;
+       case CONNMAN_SERVICE_SECURITY_WEP:
+               /* length of WEP key is 10 or 26
+                * length of WEP passphrase is 5 or 13
+                */
+               if (length == 10 || length == 26) {
+                       for (i = 0; i < length; i++)
+                               if (!isxdigit((unsigned char)
+                                             passphrase[i]))
+                                       return -ENOKEY;
+               } else if (length != 5 && length != 13)
+                       return -ENOKEY;
+               break;
+       case CONNMAN_SERVICE_SECURITY_UNKNOWN:
+       case CONNMAN_SERVICE_SECURITY_NONE:
+       case CONNMAN_SERVICE_SECURITY_8021X:
+               break;
+       }
 
-       service_save(service);
+       return 0;
+}
+
+int __connman_service_set_passphrase(struct connman_service *service,
+                                       const char *passphrase)
+{
+       int err = 0;
+
+       if (service->immutable == TRUE || service->hidden == TRUE)
+               return -EINVAL;
+
+       err = check_passphrase(service, service->security, passphrase);
+
+       if (err == 0) {
+               g_free(service->passphrase);
+               service->passphrase = g_strdup(passphrase);
+
+               if (service->network != NULL)
+                       connman_network_set_string(service->network,
+                                                       "WiFi.Passphrase",
+                                                       service->passphrase);
+               service_save(service);
+       }
+
+       return err;
+}
+
+const char *__connman_service_get_passphrase(struct connman_service *service)
+{
+       if (service == NULL)
+               return NULL;
+
+       return service->passphrase;
 }
 
 void __connman_service_set_agent_passphrase(struct connman_service *service,
                                                const char *agent_passphrase)
 {
+       if (service->hidden == TRUE)
+               return;
        g_free(service->agent_passphrase);
        service->agent_passphrase = g_strdup(agent_passphrase);
 
@@ -2952,7 +3194,11 @@ static DBusMessage *get_properties(DBusConnection *conn,
        DBusMessage *reply;
        DBusMessageIter array, dict;
 
+#if defined TIZEN_EXT
+       DBG("service %p %s", service, service == NULL ? "(nil)" : service->name);
+#else
        DBG("service %p", service);
+#endif
 
        reply = dbus_message_new_method_return(msg);
        if (reply == NULL)
@@ -2993,11 +3239,11 @@ static int update_proxy_configuration(struct connman_service *service,
                dbus_message_iter_get_basic(&entry, &key);
                dbus_message_iter_next(&entry);
 
-               if (dbus_message_iter_get_arg_type(&entry) !=
-                                                       DBUS_TYPE_VARIANT)
+               if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_VARIANT)
                        goto error;
 
                dbus_message_iter_recurse(&entry, &variant);
+
                type = dbus_message_iter_get_arg_type(&variant);
 
                if (g_str_equal(key, "Method") == TRUE) {
@@ -3167,9 +3413,9 @@ static int set_ipconfig(struct connman_service *service,
        if (type == CONNMAN_IPCONFIG_TYPE_IPV4) {
                if (err == 0 && old_method == CONNMAN_IPCONFIG_METHOD_OFF &&
                                method == CONNMAN_IPCONFIG_METHOD_DHCP) {
-                       *new_state = service->state_ipv4 =
-                               CONNMAN_SERVICE_STATE_CONFIGURATION;
+                       *new_state = service->state_ipv4;
                        __connman_ipconfig_enable(ipconfig);
+                       __connman_service_auto_connect();
                }
 
        } else if (type == CONNMAN_IPCONFIG_TYPE_IPV6) {
@@ -3177,15 +3423,21 @@ static int set_ipconfig(struct connman_service *service,
                                method == CONNMAN_IPCONFIG_METHOD_AUTO) {
                        *new_state = service->state_ipv6;
                        __connman_ipconfig_enable(ipconfig);
+                       __connman_service_auto_connect();
                }
        }
 
        DBG("err %d ipconfig %p type %d method %d state %s", err, ipconfig,
-               type, method, state2string(*new_state));
+               type, method, new_state == NULL ? "-" : state2string(*new_state));
 
        return err;
 }
 
+#if defined TIZEN_EXT
+static gint service_compare(gconstpointer a, gconstpointer b,
+                                                       gpointer user_data);
+#endif
+
 static DBusMessage *set_property(DBusConnection *conn,
                                        DBusMessage *msg, void *user_data)
 {
@@ -3199,8 +3451,15 @@ static DBusMessage *set_property(DBusConnection *conn,
        if (dbus_message_iter_init(msg, &iter) == FALSE)
                return __connman_error_invalid_arguments(msg);
 
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+               return __connman_error_invalid_arguments(msg);
+
        dbus_message_iter_get_basic(&iter, &name);
        dbus_message_iter_next(&iter);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
+               return __connman_error_invalid_arguments(msg);
+
        dbus_message_iter_recurse(&iter, &value);
 
        type = dbus_message_iter_get_arg_type(&value);
@@ -3211,8 +3470,10 @@ static DBusMessage *set_property(DBusConnection *conn,
                if (type != DBUS_TYPE_BOOLEAN)
                        return __connman_error_invalid_arguments(msg);
 
+#if !defined TIZEN_EXT
                if (service->favorite == FALSE)
                        return __connman_error_invalid_service(msg);
+#endif
 
                dbus_message_iter_get_basic(&value, &autoconnect);
 
@@ -3224,18 +3485,6 @@ static DBusMessage *set_property(DBusConnection *conn,
                autoconnect_changed(service);
 
                service_save(service);
-       } else if (g_str_equal(name, "Passphrase") == TRUE) {
-               const char *passphrase;
-
-               if (type != DBUS_TYPE_STRING)
-                       return __connman_error_invalid_arguments(msg);
-
-               if (service->immutable == TRUE)
-                       return __connman_error_not_supported(msg);
-
-               dbus_message_iter_get_basic(&value, &passphrase);
-
-               __connman_service_set_passphrase(service, passphrase);
        } else if (g_str_equal(name, "Nameservers.Configuration") == TRUE) {
                DBusMessageIter entry;
                GString *str;
@@ -3250,10 +3499,12 @@ static DBusMessage *set_property(DBusConnection *conn,
                        return __connman_error_invalid_arguments(msg);
 
                index = connman_network_get_index(service->network);
-               gw = __connman_ipconfig_get_gateway_from_index(index);
+               gw = __connman_ipconfig_get_gateway_from_index(index,
+                       CONNMAN_IPCONFIG_TYPE_ALL);
 
                if (gw && strlen(gw))
-                       __connman_service_nameserver_del_routes(service);
+                       __connman_service_nameserver_del_routes(service,
+                                               CONNMAN_IPCONFIG_TYPE_ALL);
 
                dbus_message_iter_recurse(&value, &entry);
 
@@ -3261,12 +3512,15 @@ static DBusMessage *set_property(DBusConnection *conn,
                        const char *val;
                        dbus_message_iter_get_basic(&entry, &val);
                        dbus_message_iter_next(&entry);
-                       if (str->len > 0)
-                               g_string_append_printf(str, " %s", val);
-                       else
-                               g_string_append(str, val);
+                       if (connman_inet_check_ipaddress(val) > 0) {
+                               if (str->len > 0)
+                                       g_string_append_printf(str, " %s", val);
+                               else
+                                       g_string_append(str, val);
+                       }
                }
 
+               remove_nameservers(service, -1, service->nameservers_config);
                g_strfreev(service->nameservers_config);
 
                if (str->len > 0) {
@@ -3285,6 +3539,48 @@ static DBusMessage *set_property(DBusConnection *conn,
                dns_configuration_changed(service);
 
                service_save(service);
+       } else if (g_str_equal(name, "Timeservers.Configuration") == TRUE) {
+               DBusMessageIter entry;
+               GSList *list = NULL;
+               int count = 0;
+
+               if (type != DBUS_TYPE_ARRAY)
+                       return __connman_error_invalid_arguments(msg);
+
+               dbus_message_iter_recurse(&value, &entry);
+
+               while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING) {
+                       const char *val;
+                       GSList *new_head;
+
+                       dbus_message_iter_get_basic(&entry, &val);
+
+                       new_head = __connman_timeserver_add_list(list, val);
+                       if (list != new_head) {
+                               count++;
+                               list = new_head;
+                       }
+
+                       dbus_message_iter_next(&entry);
+               }
+
+               g_strfreev(service->timeservers_config);
+               service->timeservers_config = NULL;
+
+               if (list != NULL) {
+                       service->timeservers_config = g_new0(char *, count+1);
+
+                       while (list != NULL) {
+                               count--;
+                               service->timeservers_config[count] = list->data;
+                               list = g_slist_delete_link(list, list);
+                       };
+               }
+
+               service_save(service);
+               timeservers_configuration_changed(service);
+
+               __connman_timeserver_sync(service);
        } else if (g_str_equal(name, "Domains.Configuration") == TRUE) {
                DBusMessageIter entry;
                GString *str;
@@ -3308,6 +3604,7 @@ static DBusMessage *set_property(DBusConnection *conn,
                                g_string_append(str, val);
                }
 
+               remove_searchdomains(service, -1, service->domains);
                g_strfreev(service->domains);
 
                if (str->len > 0)
@@ -3380,17 +3677,94 @@ static DBusMessage *set_property(DBusConnection *conn,
                                                        ipv4, ipv6);
 
                service_save(service);
+#if defined TIZEN_EXT
+       } else if (g_str_equal(name, "LocalNetwork") == TRUE) {
+               connman_bool_t localnetwork;
+
+               if (type != DBUS_TYPE_BOOLEAN)
+                       return __connman_error_invalid_arguments(msg);
+
+               dbus_message_iter_get_basic(&value, &localnetwork);
+
+               if (service->type == CONNMAN_SERVICE_TYPE_CELLULAR)
+                       return __connman_error_invalid_arguments(msg);
+
+               if (service->localnetwork == localnetwork)
+                       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+
+               service->localnetwork = localnetwork;
+               if (service->favorite == TRUE) {
+                       GSequenceIter *iter;
+
+                       service->order = __connman_service_get_order(service);
+
+                       iter = g_hash_table_lookup(service_hash, service->identifier);
+                       if (iter != NULL && g_sequence_get_length(service_list) > 1)
+                               g_sequence_sort_changed(iter, service_compare, NULL);
+               }
+
+               if (is_connected(service) == TRUE) {
+                       struct connman_service *default_service;
+
+                       if (service->state_ipv4 == CONNMAN_SERVICE_STATE_ONLINE)
+                               __connman_service_ipconfig_indicate_state(service,
+                                                               CONNMAN_SERVICE_STATE_READY,
+                                                               CONNMAN_IPCONFIG_TYPE_IPV4);
+
+                       if (service->state_ipv6 == CONNMAN_SERVICE_STATE_ONLINE)
+                               __connman_service_ipconfig_indicate_state(service,
+                                                               CONNMAN_SERVICE_STATE_READY,
+                                                               CONNMAN_IPCONFIG_TYPE_IPV6);
+
+                       default_service = connman_service_get_default_connection();
+
+                       if (default_service != NULL) {
+                               if (is_connected(default_service) == TRUE)
+                                       __connman_connection_update_gateway();
+                               else
+                                       __connman_service_connect(default_service);
+                       }
+               }
+
+               service_save(service);
+#endif
        } else
                return __connman_error_invalid_property(msg);
 
        return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
 }
 
+static void set_error(struct connman_service *service,
+                                       enum connman_service_error error)
+{
+       const char *str;
+
+       if (service->error == error)
+               return;
+
+       service->error = error;
+
+       if (service->path == NULL)
+               return;
+
+       str = error2string(service->error);
+
+       if (str == NULL)
+               str = "";
+
+       if (allow_property_changed(service) == FALSE)
+               return;
+
+       connman_dbus_property_changed_basic(service->path,
+                               CONNMAN_SERVICE_INTERFACE, "Error",
+                               DBUS_TYPE_STRING, &str);
+}
+
 static void set_idle(struct connman_service *service)
 {
        service->state = service->state_ipv4 = service->state_ipv6 =
                                                CONNMAN_SERVICE_STATE_IDLE;
-       service->error = CONNMAN_SERVICE_ERROR_UNKNOWN;
+       set_error(service, CONNMAN_SERVICE_ERROR_UNKNOWN);
        state_changed(service);
 }
 
@@ -3410,16 +3784,6 @@ static DBusMessage *clear_property(DBusConnection *conn,
 
                g_get_current_time(&service->modified);
                service_save(service);
-       } else if (g_str_equal(name, "Passphrase") == TRUE) {
-               if (service->immutable == TRUE)
-                       return __connman_error_not_supported(msg);
-
-               g_free(service->passphrase);
-               service->passphrase = NULL;
-
-               passphrase_changed(service);
-
-               service_save(service);
        } else
                return __connman_error_invalid_property(msg);
 
@@ -3443,105 +3807,306 @@ static connman_bool_t is_ignore(struct connman_service *service)
        return FALSE;
 }
 
-void __connman_service_auto_connect(void)
+struct preferred_tech_data {
+       GSequence *preferred_list;
+       enum connman_service_type type;
+#if defined TIZEN_EXT
+       char **preferred_names;
+       GSequenceIter *preferred_before;
+#endif
+};
+
+#if defined TIZEN_EXT
+static gboolean is_preferred_network_by_name(char **preferred_names, char *name)
 {
-       struct connman_service *service = NULL;
-       GSequenceIter *iter;
+       char **pattern;
 
-       DBG("");
+       if (preferred_names == NULL || name == NULL)
+               return FALSE;
 
-       if (__connman_session_mode() == TRUE) {
-               DBG("Session mode enabled: auto connect disabled");
-               return;
+       for (pattern = preferred_names; *pattern; pattern++)
+               if (g_str_equal(*pattern, name) == TRUE)
+                       return TRUE;
+
+       return FALSE;
+}
+#endif
+
+static void preferred_tech_add_by_type(gpointer data, gpointer user_data)
+{
+       struct connman_service *service = data;
+       struct preferred_tech_data *tech_data = user_data;
+#if defined TIZEN_EXT
+       struct connman_service *service_before = NULL;
+#endif
+
+       if (service->type == tech_data->type) {
+#if defined TIZEN_EXT
+               if (tech_data->preferred_before == NULL) {
+                       tech_data->preferred_before =
+                                       g_sequence_append(tech_data->preferred_list, service);
+               } else {
+                       if (tech_data->preferred_before)
+                               service_before = g_sequence_get(tech_data->preferred_before);
+
+                       if (service_before && service_before->type == service->type &&
+                                       service_before->security == service->security) {
+                               if (is_preferred_network_by_name(tech_data->preferred_names,
+                                                                                       service->name) == TRUE)
+                                       g_sequence_insert_before(tech_data->preferred_before,
+                                                                                               service);
+                               else
+                                       g_sequence_append(tech_data->preferred_list, service);
+                       } else {
+                               tech_data->preferred_before =
+                                               g_sequence_append(tech_data->preferred_list, service);
+                       }
+               }
+#else
+               g_sequence_append(tech_data->preferred_list, service);
+#endif
+
+               DBG("type %d service %p %s", tech_data->type, service,
+                               service->name);
        }
+}
 
-       iter = g_sequence_get_begin_iter(service_list);
+static GSequence* preferred_tech_list_get(GSequence *list)
+{
+       unsigned int *tech_array;
+       struct preferred_tech_data tech_data;
+       int i;
+
+       tech_array = connman_setting_get_uint_list("PreferredTechnologies");
+       if (tech_array == NULL)
+               return NULL;
+
+       if (connman_setting_get_bool("SingleConnectedTechnology") == TRUE) {
+               GSequenceIter *iter = g_sequence_get_begin_iter(service_list);
+               while (g_sequence_iter_is_end(iter) == FALSE) {
+                       struct connman_service *service;
+
+                       service = g_sequence_get(iter);
+
+                       if (is_connected(service) == FALSE)
+                               break;
+
+                       if (service->userconnect == TRUE) {
+                               DBG("service %p name %s is user connected",
+                                               service, service->name);
+#if defined TIZEN_EXT
+                               /* We can connect to a favorite service like
+                                * wifi even we have a userconnect for cellular
+                                * because we have refount for cellular service
+                                */
+                               if (service->type == CONNMAN_SERVICE_TYPE_CELLULAR)
+                                       break;
+#endif
+                               return NULL;
+                       }
+
+                       iter = g_sequence_iter_next(iter);
+               }
+       }
+
+       tech_data.preferred_list = g_sequence_new(NULL);
+#if defined TIZEN_EXT
+       tech_data.preferred_names =
+                       connman_setting_get_string_list("PreferredNetworkNames");
+       tech_data.preferred_before = NULL;
+#endif
+
+       for (i = 0; tech_array[i] != 0; i += 1) {
+               tech_data.type = tech_array[i];
+               g_sequence_foreach(service_list, preferred_tech_add_by_type,
+                               &tech_data);
+       }
+
+       return tech_data.preferred_list;
+}
+
+static connman_bool_t auto_connect_service(GSequenceIter* iter,
+               connman_bool_t preferred)
+{
+       struct connman_service *service = NULL;
 
        while (g_sequence_iter_is_end(iter) == FALSE) {
                service = g_sequence_get(iter);
 
 #if defined TIZEN_EXT
-               DBG("service: %p %s %s %s, favorite(%d), ignore(%d)", service, service->name,
+               DBG("service %p %s %s %s, favorite(%d), ignore(%d), hidden(%d, %d)",
+                               service, service->name,
                                state2string(service->state),
                                __connman_service_type2string(service->type),
-                               service->favorite, is_ignore(service));
+                               service->favorite, is_ignore(service),
+                               service->hidden, service->hidden_service);
 
                /* Tizen takes Wi-Fi as the highest priority into consideration. */
                if (service->type != CONNMAN_SERVICE_TYPE_WIFI)
-                       if (is_connecting(service) == TRUE || is_connected(service) == TRUE) {
-                               service = NULL;
-                               iter = g_sequence_iter_next(iter);
-                               continue;
-                       }
+                       if (is_connecting(service) == TRUE || is_connected(service) == TRUE)
+                               goto next_service;
 #endif
+
                if (service->pending != NULL)
-                       return;
+                       return TRUE;
 
                if (is_connecting(service) == TRUE)
-                       return;
+                       return TRUE;
 
-               if (service->favorite == FALSE)
-                       return;
+               if (service->favorite == FALSE) {
+                       if (preferred == TRUE)
+                               goto next_service;
+                       return FALSE;
+               }
 
                if (is_connected(service) == TRUE)
-                       return;
+                       return TRUE;
 
                if (is_ignore(service) == FALSE && service->state ==
-                                               CONNMAN_SERVICE_STATE_IDLE)
+                               CONNMAN_SERVICE_STATE_IDLE)
                        break;
 
+       next_service:
                service = NULL;
 
                iter = g_sequence_iter_next(iter);
        }
 
        if (service != NULL) {
+
+               DBG("service %p %s %s", service, service->name,
+                               (preferred == TRUE)? "preferred": "auto");
+
                service->userconnect = FALSE;
                __connman_service_connect(service);
+               return TRUE;
        }
+       return FALSE;
 }
 
-static void remove_timeout(struct connman_service *service)
+static gboolean run_auto_connect(gpointer data)
 {
-       if (service->timeout > 0) {
-               g_source_remove(service->timeout);
-               service->timeout = 0;
-       }
-}
+       GSequenceIter *iter = NULL;
+       GSequence *preferred_tech;
 
-static void reply_pending(struct connman_service *service, int error)
-{
-       remove_timeout(service);
+       autoconnect_timeout = 0;
 
-       if (service->pending != NULL) {
+       DBG("");
+
+       preferred_tech = preferred_tech_list_get(service_list);
+       if (preferred_tech != NULL)
+               iter = g_sequence_get_begin_iter(preferred_tech);
+
+       if (iter == NULL || auto_connect_service(iter, TRUE) == FALSE)
+               iter = g_sequence_get_begin_iter(service_list);
+
+       if (iter != NULL)
+               auto_connect_service(iter, FALSE);
+
+       if (preferred_tech != NULL)
+               g_sequence_free(preferred_tech);
+
+       return FALSE;
+}
+
+void __connman_service_auto_connect(void)
+{
+       DBG("");
+
+       if (__connman_session_mode() == TRUE) {
+               DBG("Session mode enabled: auto connect disabled");
+               return;
+       }
+
+       if (autoconnect_timeout != 0)
+               return;
+
+       autoconnect_timeout = g_timeout_add_seconds(0, run_auto_connect, NULL);
+}
+
+static void remove_timeout(struct connman_service *service)
+{
+       if (service->timeout > 0) {
+               g_source_remove(service->timeout);
+               service->timeout = 0;
+       }
+}
+
+void __connman_service_reply_dbus_pending(DBusMessage *pending, int error)
+{
+       if (pending != NULL) {
                if (error > 0) {
                        DBusMessage *reply;
 
-                       reply = __connman_error_failed(service->pending,
-                                                               error);
+                       reply = __connman_error_failed(pending, error);
                        if (reply != NULL)
                                g_dbus_send_message(connection, reply);
                } else {
-                       const char *sender;
+                       const char *sender, *path;
 
-                       sender = dbus_message_get_interface(service->pending);
+                       sender = dbus_message_get_interface(pending);
+                       path = dbus_message_get_path(pending);
 
-                       DBG("sender %s", sender);
+                       DBG("sender %s path %s", sender, path);
 
                        if (g_strcmp0(sender, CONNMAN_MANAGER_INTERFACE) == 0)
-                               g_dbus_send_reply(connection, service->pending,
-                                       DBUS_TYPE_OBJECT_PATH, &service->path,
+                               g_dbus_send_reply(connection, pending,
+                                       DBUS_TYPE_OBJECT_PATH, &path,
                                                        DBUS_TYPE_INVALID);
                        else
-                               g_dbus_send_reply(connection, service->pending,
+                               g_dbus_send_reply(connection, pending,
                                                        DBUS_TYPE_INVALID);
                }
 
-               dbus_message_unref(service->pending);
+               dbus_message_unref(pending);
+       }
+}
+
+static void reply_pending(struct connman_service *service, int error)
+{
+       remove_timeout(service);
+
+       if (service->pending != NULL) {
+               __connman_service_reply_dbus_pending(service->pending, error);
                service->pending = NULL;
        }
 }
 
+static void check_pending_msg(struct connman_service *service)
+{
+       if (service->pending == NULL)
+               return;
+
+       DBG("service %p pending msg %p already exists", service,
+                                               service->pending);
+       dbus_message_unref(service->pending);
+}
+
+void __connman_service_set_hidden_data(struct connman_service *service,
+                                                       gpointer user_data)
+{
+       DBusMessage *pending = user_data;
+
+       DBG("service %p pending %p", service, pending);
+
+       if (service == NULL || pending == NULL)
+               return;
+
+       check_pending_msg(service);
+
+       service->pending = pending;
+}
+
+void __connman_service_return_error(struct connman_service *service,
+                               int error, gpointer user_data)
+{
+       DBG("service %p error %d user_data %p", service, error, user_data);
+
+       __connman_service_set_hidden_data(service, user_data);
+
+       reply_pending(service, error);
+}
+
 static gboolean connect_timeout(gpointer user_data)
 {
        struct connman_service *service = user_data;
@@ -3553,6 +4118,8 @@ static gboolean connect_timeout(gpointer user_data)
 
        if (service->network != NULL)
                __connman_network_disconnect(service->network);
+       else if (service->provider != NULL)
+               connman_provider_disconnect(service->provider);
 
        __connman_ipconfig_disable(service->ipconfig_ipv4);
        __connman_ipconfig_disable(service->ipconfig_ipv6);
@@ -3613,6 +4180,29 @@ static connman_bool_t get_reconnect_state(struct connman_service *service)
        return __connman_device_get_reconnect(device);
 }
 
+static connman_bool_t is_interface_available(struct connman_service *service,
+                                       struct connman_service *other_service)
+{
+       unsigned int index = 0, other_index = 0;
+
+       if (service->ipconfig_ipv4 != NULL)
+               index = __connman_ipconfig_get_index(service->ipconfig_ipv4);
+       else if (service->ipconfig_ipv6 != NULL)
+               index = __connman_ipconfig_get_index(service->ipconfig_ipv6);
+
+       if (other_service->ipconfig_ipv4 != NULL)
+               other_index = __connman_ipconfig_get_index(
+                                               other_service->ipconfig_ipv4);
+       else if (other_service->ipconfig_ipv6 != NULL)
+               other_index = __connman_ipconfig_get_index(
+                                               other_service->ipconfig_ipv6);
+
+       if (index > 0 && other_index != index)
+               return TRUE;
+
+       return FALSE;
+}
+
 static DBusMessage *connect_service(DBusConnection *conn,
                                        DBusMessage *msg, void *user_data)
 {
@@ -3627,9 +4217,8 @@ static DBusMessage *connect_service(DBusConnection *conn,
         * Description: TIZEN implements system global connection management.
         */
        if (service->type == CONNMAN_SERVICE_TYPE_CELLULAR)
-               connman_service_user_initiated_pdn_connection_ref(service);
+               connman_service_user_pdn_connection_ref(service);
 #endif
-
        if (service->pending != NULL)
                return __connman_error_in_progress(msg);
 
@@ -3638,8 +4227,28 @@ static DBusMessage *connect_service(DBusConnection *conn,
        while (g_sequence_iter_is_end(iter) == FALSE) {
                struct connman_service *temp = g_sequence_get(iter);
 
-               if (service->type == temp->type && is_connecting(temp) == TRUE)
-                       return __connman_error_in_progress(msg);
+               /*
+                * We should allow connection if there are available
+                * interfaces for a given technology type (like having
+                * more than one wifi card).
+                */
+               if (service->type == temp->type &&
+                               is_connecting(temp) == TRUE &&
+                               is_interface_available(service,
+                                                       temp) == FALSE) {
+                       if (temp->pending != NULL)
+                               __connman_service_return_error(temp,
+                                                       ECONNABORTED,
+                                                       NULL);
+
+                       err = __connman_service_disconnect(temp);
+                       if (err < 0 && err != -EINPROGRESS)
+                               return __connman_error_in_progress(msg);
+                       else {
+                               set_idle(temp);
+                               break;
+                       }
+               }
 
                iter = g_sequence_iter_next(iter);
        }
@@ -3683,15 +4292,14 @@ static DBusMessage *disconnect_service(DBusConnection *conn,
         * Description: TIZEN implements system global connection management.
         */
        if (service->type == CONNMAN_SERVICE_TYPE_CELLULAR) {
-               if (connman_service_user_initiated_pdn_connection_unref_and_test(service) != TRUE)
+               if (connman_service_user_pdn_connection_unref_and_test(service) != TRUE)
                        return __connman_error_failed(msg, EISCONN);
 
                if (is_connected(service) == TRUE &&
-                               service == __connman_service_get_default())
+                               service == connman_service_get_default_connection())
                        return __connman_error_failed(msg, EISCONN);
        }
 #endif
-
        reply_pending(service, ECONNABORTED);
 
        service->ignore = TRUE;
@@ -3713,22 +4321,35 @@ static DBusMessage *disconnect_service(DBusConnection *conn,
        return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
 }
 
-static DBusMessage *remove_service(DBusConnection *conn,
-                                       DBusMessage *msg, void *user_data)
+#if defined TIZEN_EXT
+static void __connman_service_cleanup_network_8021x(struct connman_service *service)
 {
-       struct connman_service *service = user_data;
+       if (service == NULL)
+               return;
 
-       DBG("service %p", service);
+       DBG("service %p ", service);
 
+       connman_network_set_string(service->network, "WiFi.EAP", NULL);
+       connman_network_set_string(service->network, "WiFi.Identity", NULL);
+       connman_network_set_string(service->network, "WiFi.CACertFile", NULL);
+       connman_network_set_string(service->network, "WiFi.ClientCertFile", NULL);
+       connman_network_set_string(service->network, "WiFi.PrivateKeyFile", NULL);
+       connman_network_set_string(service->network, "WiFi.PrivateKeyPassphrase", NULL);
+       connman_network_set_string(service->network, "WiFi.Phase2", NULL);
+}
+#endif
+
+gboolean __connman_service_remove(struct connman_service *service)
+{
        if (service->type == CONNMAN_SERVICE_TYPE_ETHERNET)
-               return __connman_error_not_supported(msg);
+               return FALSE;
 
-       if (service->immutable == TRUE)
-               return __connman_error_not_supported(msg);
+       if (service->immutable == TRUE || service->hidden == TRUE)
+               return FALSE;
 
        if (service->favorite == FALSE && service->state !=
                                                CONNMAN_SERVICE_STATE_FAILURE)
-               return __connman_error_not_supported(msg);
+               return FALSE;
 
        set_reconnect_state(service, FALSE);
 
@@ -3737,12 +4358,74 @@ static DBusMessage *remove_service(DBusConnection *conn,
        g_free(service->passphrase);
        service->passphrase = NULL;
 
-       passphrase_changed(service);
+       g_free(service->agent_passphrase);
+       service->agent_passphrase = NULL;
+
+       g_free(service->identity);
+       service->identity = NULL;
+
+       g_free(service->agent_identity);
+       service->agent_identity = NULL;
+
+       g_free(service->eap);
+       service->eap = NULL;
+
+#if defined TIZEN_EXT
+       g_free(service->ca_cert_file);
+       service->ca_cert_file = NULL;
+
+       g_free(service->client_cert_file);
+       service->client_cert_file = NULL;
+
+       g_free(service->private_key_file);
+       service->private_key_file = NULL;
+
+       g_free(service->private_key_passphrase);
+       service->private_key_passphrase = NULL;
+
+       g_free(service->phase2);
+       service->phase2 = NULL;
+
+       __connman_service_cleanup_network_8021x(service);
+
+       __connman_ipconfig_set_method(service->ipconfig_ipv4, CONNMAN_IPCONFIG_METHOD_DHCP);
+       __connman_ipconfig_set_method(service->ipconfig_ipv6, CONNMAN_IPCONFIG_METHOD_AUTO);
+       connman_service_set_proxy(service, NULL, FALSE);
+
+       __connman_service_nameserver_clear(service);
+
+       remove_nameservers(service, -1, service->nameservers_config);
+       g_strfreev(service->nameservers_config);
+       service->nameservers_config = NULL;
+
+       service->localnetwork = FALSE;
+#endif
 
+#if defined TIZEN_EXT
+       if (service->security != CONNMAN_SERVICE_SECURITY_8021X)
+#endif
        set_idle(service);
 
        __connman_service_set_favorite(service, FALSE);
+
+#if defined TIZEN_EXT
+       __connman_storage_remove_service(service->identifier);
+#else
        service_save(service);
+#endif
+
+       return TRUE;
+}
+
+static DBusMessage *remove_service(DBusConnection *conn,
+                                       DBusMessage *msg, void *user_data)
+{
+       struct connman_service *service = user_data;
+
+       DBG("service %p", service);
+
+       if (__connman_service_remove(service) == FALSE)
+               return __connman_error_not_supported(msg);
 
        return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
 }
@@ -3786,7 +4469,7 @@ static void apply_relevant_default_downgrade(struct connman_service *service)
 {
        struct connman_service *def_service;
 
-       def_service = get_default();
+       def_service = __connman_service_get_default();
        if (def_service == NULL)
                return;
 
@@ -3795,6 +4478,18 @@ static void apply_relevant_default_downgrade(struct connman_service *service)
                def_service->state = CONNMAN_SERVICE_STATE_READY;
 }
 
+static void switch_default_service(struct connman_service *default_service,
+               struct connman_service *downgrade_service)
+{
+       GSequenceIter *src, *dst;
+
+       apply_relevant_default_downgrade(default_service);
+       src = g_hash_table_lookup(service_hash, downgrade_service->identifier);
+       dst = g_hash_table_lookup(service_hash, default_service->identifier);
+       g_sequence_move(src, dst);
+       downgrade_state(downgrade_service);
+}
+
 static DBusMessage *move_service(DBusConnection *conn,
                                        DBusMessage *msg, void *user_data,
                                                                gboolean before)
@@ -3802,7 +4497,6 @@ static DBusMessage *move_service(DBusConnection *conn,
        struct connman_service *service = user_data;
        struct connman_service *target;
        const char *path;
-       GSequenceIter *src, *dst;
        enum connman_ipconfig_method target4, target6;
        enum connman_ipconfig_method service4, service6;
 
@@ -3815,18 +4509,36 @@ static DBusMessage *move_service(DBusConnection *conn,
                return __connman_error_not_supported(msg);
 
        target = find_service(path);
-       if (target == NULL || target->favorite == FALSE || target == service ||
-                               target->type == CONNMAN_SERVICE_TYPE_VPN)
+       if (target == NULL || target->favorite == FALSE || target == service)
                return __connman_error_invalid_service(msg);
 
+       if (target->type == CONNMAN_SERVICE_TYPE_VPN) {
+               /*
+                * We only allow VPN route splitting if there are
+                * routes defined for a given VPN.
+                */
+               if (__connman_provider_check_routes(target->provider)
+                                                               == FALSE) {
+                       connman_info("Cannot move service. "
+                               "No routes defined for provider %s",
+                               __connman_provider_get_ident(target->provider));
+                       return __connman_error_invalid_service(msg);
+               }
+
+               target->do_split_routing = TRUE;
+       } else
+               target->do_split_routing = FALSE;
+
+       service->do_split_routing = FALSE;
+
        target4 = __connman_ipconfig_get_method(target->ipconfig_ipv4);
        target6 = __connman_ipconfig_get_method(target->ipconfig_ipv6);
        service4 = __connman_ipconfig_get_method(service->ipconfig_ipv4);
        service6 = __connman_ipconfig_get_method(service->ipconfig_ipv6);
 
-       DBG("target %s method %d/%d state %d/%d", target->identifier,
-                               target4, target6,
-                               target->state_ipv4, target->state_ipv6);
+       DBG("target %s method %d/%d state %d/%d split %d", target->identifier,
+               target4, target6, target->state_ipv4, target->state_ipv6,
+               target->do_split_routing);
 
        DBG("service %s method %d/%d state %d/%d", service->identifier,
                                service4, service6,
@@ -3870,9 +4582,7 @@ static DBusMessage *move_service(DBusConnection *conn,
 
        g_get_current_time(&service->modified);
        service_save(service);
-
-       src = g_hash_table_lookup(service_hash, service->identifier);
-       dst = g_hash_table_lookup(service_hash, target->identifier);
+       service_save(target);
 
        /*
         * If the service which goes down is the default service and is
@@ -3880,17 +4590,12 @@ static DBusMessage *move_service(DBusConnection *conn,
         * the service which goes up, needs to recompute its state which
         * is triggered via downgrading it - if relevant - to state ready.
         */
-       if (before == TRUE) {
-               apply_relevant_default_downgrade(target);
-               g_sequence_move(src, dst);
-               downgrade_state(service);
-       } else {
-               apply_relevant_default_downgrade(service);
-               g_sequence_move(dst, src);
-               downgrade_state(target);
-       }
+       if (before == TRUE)
+               switch_default_service(target, service);
+       else
+               switch_default_service(service, target);
 
-       services_changed(FALSE);
+       __connman_connection_update_gateway();
 
        return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
 }
@@ -3917,22 +4622,161 @@ static DBusMessage *reset_counters(DBusConnection *conn,
        return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
 }
 
-static GDBusMethodTable service_methods[] = {
-       { "GetProperties", "",   "a{sv}", get_properties     },
-       { "SetProperty",   "sv", "",      set_property       },
-       { "ClearProperty", "s",  "",      clear_property     },
-       { "Connect",       "",   "",      connect_service,
-                                               G_DBUS_METHOD_FLAG_ASYNC },
-       { "Disconnect",    "",   "",      disconnect_service },
-       { "Remove",        "",   "",      remove_service     },
-       { "MoveBefore",    "o",  "",      move_before        },
-       { "MoveAfter",     "o",  "",      move_after         },
-       { "ResetCounters", "",   "",      reset_counters     },
+static struct _services_notify {
+       int id;
+       GHashTable *add;
+       GHashTable *remove;
+} *services_notify;
+
+static void service_append_added_foreach(gpointer data, gpointer user_data)
+{
+       struct connman_service *service = data;
+       DBusMessageIter *iter = user_data;
+
+       if (service == NULL || service->path == NULL) {
+               DBG("service %p or path is NULL", service);
+               return;
+       }
+
+       if (g_hash_table_lookup(services_notify->add, service->path) != NULL) {
+#if !defined TIZEN_EXT
+               DBG("new %s", service->path);
+#endif
+
+               append_struct(service, iter);
+               g_hash_table_remove(services_notify->add, service->path);
+       } else {
+#if !defined TIZEN_EXT
+               DBG("changed %s", service->path);
+#endif
+
+               append_struct_service(iter, NULL, service);
+       }
+}
+
+static void service_append_ordered(DBusMessageIter *iter, void *user_data)
+{
+       if (service_list != NULL)
+               g_sequence_foreach(service_list,
+                                       service_append_added_foreach, iter);
+}
+
+static void append_removed(gpointer key, gpointer value, gpointer user_data)
+{
+       char *objpath = key;
+       DBusMessageIter *iter = user_data;
+
+       DBG("removed %s", objpath);
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &objpath);
+}
+
+static gboolean service_send_changed(gpointer data)
+{
+       DBusMessage *signal;
+       DBusMessageIter iter, array;
+
+       DBG("");
+
+       services_notify->id = 0;
+
+       signal = dbus_message_new_signal(CONNMAN_MANAGER_PATH,
+                       CONNMAN_MANAGER_INTERFACE, "ServicesChanged");
+       if (signal == NULL)
+               return FALSE;
+
+       __connman_dbus_append_objpath_dict_array(signal,
+                       service_append_ordered, NULL);
+
+       dbus_message_iter_init_append(signal, &iter);
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                       DBUS_TYPE_OBJECT_PATH_AS_STRING, &array);
+
+       g_hash_table_foreach(services_notify->remove, append_removed, &array);
+
+       dbus_message_iter_close_container(&iter, &array);
+
+       dbus_connection_send(connection, signal, NULL);
+       dbus_message_unref(signal);
+
+       g_hash_table_remove_all(services_notify->remove);
+       g_hash_table_remove_all(services_notify->add);
+
+       return FALSE;
+}
+
+static void service_schedule_changed(void)
+{
+       if (services_notify->id != 0)
+               return;
+
+       services_notify->id = g_timeout_add(100, service_send_changed, NULL);
+}
+
+static void service_schedule_added(struct connman_service *service)
+{
+       DBG("service %p", service);
+
+       g_hash_table_remove(services_notify->remove, service->path);
+       g_hash_table_replace(services_notify->add, service->path, service);
+
+       service_schedule_changed();
+}
+
+static void service_schedule_removed(struct connman_service *service)
+{
+       DBG("service %p %s", service, service->path);
+
+       if (service == NULL || service->path == NULL) {
+               DBG("service %p or path is NULL", service);
+               return;
+       }
+
+       g_hash_table_remove(services_notify->add, service->path);
+       g_hash_table_replace(services_notify->remove, g_strdup(service->path),
+                       NULL);
+
+       service_schedule_changed();
+}
+
+static connman_bool_t allow_property_changed(struct connman_service *service)
+{
+       if (g_hash_table_lookup_extended(services_notify->add, service->path,
+                                       NULL, NULL) == TRUE) {
+               DBG("no property updates for service %p", service);
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+static const GDBusMethodTable service_methods[] = {
+       { GDBUS_DEPRECATED_METHOD("GetProperties",
+                       NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
+                       get_properties) },
+       { GDBUS_METHOD("SetProperty",
+                       GDBUS_ARGS({ "name", "s" }, { "value", "v" }),
+                       NULL, set_property) },
+       { GDBUS_METHOD("ClearProperty",
+                       GDBUS_ARGS({ "name", "s" }), NULL,
+                       clear_property) },
+       { GDBUS_ASYNC_METHOD("Connect", NULL, NULL,
+                             connect_service) },
+       { GDBUS_METHOD("Disconnect", NULL, NULL,
+                       disconnect_service) },
+       { GDBUS_METHOD("Remove", NULL, NULL, remove_service) },
+       { GDBUS_METHOD("MoveBefore",
+                       GDBUS_ARGS({ "service", "o" }), NULL,
+                       move_before) },
+       { GDBUS_METHOD("MoveAfter",
+                       GDBUS_ARGS({ "service", "o" }), NULL,
+                       move_after) },
+       { GDBUS_METHOD("ResetCounters", NULL, NULL, reset_counters) },
        { },
 };
 
-static GDBusSignalTable service_signals[] = {
-       { "PropertyChanged", "sv" },
+static const GDBusSignalTable service_signals[] = {
+       { GDBUS_SIGNAL("PropertyChanged",
+                       GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
        { },
 };
 
@@ -3948,13 +4792,15 @@ static void service_free(gpointer user_data)
        g_hash_table_remove(service_hash, service->identifier);
 
        __connman_notifier_service_remove(service);
+       service_schedule_removed(service);
 
+       __connman_wispr_stop(service);
        stats_stop(service);
 
        service->path = NULL;
 
        if (path != NULL) {
-               services_changed(FALSE);
+               __connman_connection_update_gateway();
 
                g_dbus_unregister_interface(connection, path,
                                                CONNMAN_SERVICE_INTERFACE);
@@ -3964,27 +4810,30 @@ static void service_free(gpointer user_data)
        g_hash_table_destroy(service->counter_table);
 
        if (service->network != NULL) {
-               if (service->network_created == TRUE)
-                       connman_network_unref(service->network);
+               __connman_network_disconnect(service->network);
+               connman_network_unref(service->network);
+               service->network = NULL;
        }
 
        if (service->provider != NULL)
                connman_provider_unref(service->provider);
 
        if (service->ipconfig_ipv4 != NULL) {
-               connman_ipconfig_set_ops(service->ipconfig_ipv4, NULL);
-               connman_ipconfig_set_data(service->ipconfig_ipv4, NULL);
-               connman_ipconfig_unref(service->ipconfig_ipv4);
+               __connman_ipconfig_set_ops(service->ipconfig_ipv4, NULL);
+               __connman_ipconfig_set_data(service->ipconfig_ipv4, NULL);
+               __connman_ipconfig_unref(service->ipconfig_ipv4);
                service->ipconfig_ipv4 = NULL;
        }
 
        if (service->ipconfig_ipv6 != NULL) {
-               connman_ipconfig_set_ops(service->ipconfig_ipv6, NULL);
-               connman_ipconfig_set_data(service->ipconfig_ipv6, NULL);
-               connman_ipconfig_unref(service->ipconfig_ipv6);
+               __connman_ipconfig_set_ops(service->ipconfig_ipv6, NULL);
+               __connman_ipconfig_set_data(service->ipconfig_ipv6, NULL);
+               __connman_ipconfig_unref(service->ipconfig_ipv6);
                service->ipconfig_ipv6 = NULL;
        }
 
+       g_strfreev(service->timeservers);
+       g_strfreev(service->timeservers_config);
        g_strfreev(service->nameservers);
        g_strfreev(service->nameservers_config);
        g_strfreev(service->nameservers_auto);
@@ -4006,6 +4855,8 @@ static void service_free(gpointer user_data)
        g_free(service->private_key_file);
        g_free(service->private_key_passphrase);
        g_free(service->phase2);
+       g_free(service->config_file);
+       g_free(service->config_entry);
 
        if (service->stats.timer != NULL)
                g_timer_destroy(service->stats.timer);
@@ -4015,33 +4866,6 @@ static void service_free(gpointer user_data)
        g_free(service);
 }
 
-/**
- * __connman_service_put:
- * @service: service structure
- *
- * Release service if no longer needed
- */
-void __connman_service_put(struct connman_service *service)
-{
-       GSequenceIter *iter;
-
-       DBG("service %p", service);
-
-       if (__sync_fetch_and_sub(&service->refcount, 1) != 1)
-               return;
-
-       iter = g_hash_table_lookup(service_hash, service->identifier);
-       if (iter != NULL) {
-               reply_pending(service, ECONNABORTED);
-
-               __connman_service_disconnect(service);
-
-               g_sequence_remove(iter);
-       } else {
-               service_free(service);
-       }
-}
-
 static void stats_init(struct connman_service *service)
 {
        /* home */
@@ -4062,7 +4886,7 @@ static void service_initialize(struct connman_service *service)
        service->refcount = 1;
        service->session_usage_count = 0;
 
-       service->network_created = FALSE;
+       service->error = CONNMAN_SERVICE_ERROR_UNKNOWN;
 
        service->type     = CONNMAN_SERVICE_TYPE_UNKNOWN;
        service->security = CONNMAN_SERVICE_SECURITY_UNKNOWN;
@@ -4086,12 +4910,13 @@ static void service_initialize(struct connman_service *service)
        service->provider = NULL;
 
        service->wps = FALSE;
-
 #if defined TIZEN_EXT
+       service->localnetwork = FALSE;
        /*
         * Description: TIZEN implements system global connection management.
         */
-       g_atomic_int_set(&service->user_initiated_pdn_connection_refcount, 0);
+       service->user_pdn_connection_refcount = 0;
+       __sync_synchronize();
 #endif
 }
 
@@ -4146,9 +4971,12 @@ struct connman_service *connman_service_create(void)
  *
  * Increase reference counter of service
  */
-struct connman_service *connman_service_ref(struct connman_service *service)
+struct connman_service *
+connman_service_ref_debug(struct connman_service *service,
+                       const char *file, int line, const char *caller)
 {
-       DBG("%p", service);
+       DBG("%p ref %d by %s:%d:%s()", service, service->refcount + 1,
+               file, line, caller);
 
        __sync_fetch_and_add(&service->refcount, 1);
 
@@ -4159,12 +4987,52 @@ struct connman_service *connman_service_ref(struct connman_service *service)
  * connman_service_unref:
  * @service: service structure
  *
- * Decrease reference counter of service
+ * Decrease reference counter of service and release service if no
+ * longer needed.
  */
-void connman_service_unref(struct connman_service *service)
+void connman_service_unref_debug(struct connman_service *service,
+                       const char *file, int line, const char *caller)
 {
-       __connman_service_put(service);
+       GSequenceIter *iter;
+
+       DBG("%p ref %d by %s:%d:%s()", service, service->refcount - 1,
+               file, line, caller);
+
+       if (__sync_fetch_and_sub(&service->refcount, 1) != 1)
+               return;
+
+       iter = g_hash_table_lookup(service_hash, service->identifier);
+       if (iter != NULL) {
+               reply_pending(service, ECONNABORTED);
+
+               __connman_service_disconnect(service);
+
+               g_sequence_remove(iter);
+       } else {
+#if defined TIZEN_EXT
+               if (service == current_default)
+                       current_default = NULL;
+#endif
+               service_free(service);
+       }
+}
+
+#if defined TIZEN_EXT
+static int __check_wifi_security_priority(unsigned int *wifi_priority,
+               enum connman_service_security security)
+{
+       int i = 0;
+
+       while (wifi_priority[i] != 0) {
+               if (wifi_priority[i] == security)
+                       return i;
+
+               i++;
+       }
+
+       return i;
 }
+#endif
 
 static gint service_compare(gconstpointer a, gconstpointer b,
                                                        gpointer user_data)
@@ -4172,6 +5040,11 @@ static gint service_compare(gconstpointer a, gconstpointer b,
        struct connman_service *service_a = (void *) a;
        struct connman_service *service_b = (void *) b;
        enum connman_service_state state_a, state_b;
+#if defined TIZEN_EXT
+       unsigned int *wifi_priority;
+       int service_a_priority;
+       int service_b_priority;
+#endif
 
        state_a = service_a->state;
        state_b = service_b->state;
@@ -4182,20 +5055,15 @@ static gint service_compare(gconstpointer a, gconstpointer b,
 
                if (a_connected == TRUE && b_connected == TRUE) {
 #if defined TIZEN_EXT
-                       /* We prefer wifi on connected state */
-                       if (service_a->type != service_b->type) {
-                               if (service_a->type == CONNMAN_SERVICE_TYPE_WIFI)
-                                       return -1;
-                               if (service_b->type == CONNMAN_SERVICE_TYPE_WIFI)
-                                       return 1;
-                       }
-#endif
+                       goto compare;
+#else
                        /* We prefer online over ready state */
                        if (state_a == CONNMAN_SERVICE_STATE_ONLINE)
                                return -1;
 
                        if (state_b == CONNMAN_SERVICE_STATE_ONLINE)
                                return 1;
+#endif
                }
 
                if (a_connected == TRUE)
@@ -4209,6 +5077,9 @@ static gint service_compare(gconstpointer a, gconstpointer b,
                        return 1;
        }
 
+#if defined TIZEN_EXT
+compare:
+#endif
        if (service_a->order > service_b->order)
                return -1;
 
@@ -4233,14 +5104,12 @@ static gint service_compare(gconstpointer a, gconstpointer b,
 #if defined TIZEN_EXT
                case CONNMAN_SERVICE_TYPE_WIFI:
                        return -1;
-               case CONNMAN_SERVICE_TYPE_WIMAX:
                case CONNMAN_SERVICE_TYPE_BLUETOOTH:
                case CONNMAN_SERVICE_TYPE_CELLULAR:
                        return 1;
 #else
                case CONNMAN_SERVICE_TYPE_WIFI:
                        return 1;
-               case CONNMAN_SERVICE_TYPE_WIMAX:
                case CONNMAN_SERVICE_TYPE_BLUETOOTH:
                case CONNMAN_SERVICE_TYPE_CELLULAR:
                        return -1;
@@ -4248,6 +5117,25 @@ static gint service_compare(gconstpointer a, gconstpointer b,
                }
        }
 
+#if defined TIZEN_EXT
+       wifi_priority = connman_setting_get_uint_list("WiFiPairingPriority");
+
+       if (wifi_priority != NULL &&
+                       service_a->type == CONNMAN_SERVICE_TYPE_WIFI &&
+                       service_b->type == CONNMAN_SERVICE_TYPE_WIFI) {
+               service_a_priority = __check_wifi_security_priority(wifi_priority,
+                               service_a->security);
+               service_b_priority = __check_wifi_security_priority(wifi_priority,
+                               service_b->security);
+
+               if (service_a_priority < service_b_priority)
+                       return -1;
+
+               if (service_a_priority > service_b_priority)
+                       return 1;
+       }
+#endif
+
        return (gint) service_b->strength - (gint) service_a->strength;
 }
 
@@ -4280,10 +5168,10 @@ char *connman_service_get_interface(struct connman_service *service)
 
        if (service->type == CONNMAN_SERVICE_TYPE_VPN) {
                if (service->ipconfig_ipv4)
-                       index = connman_ipconfig_get_index(
+                       index = __connman_ipconfig_get_index(
                                                service->ipconfig_ipv4);
                else if (service->ipconfig_ipv6)
-                       index = connman_ipconfig_get_index(
+                       index = __connman_ipconfig_get_index(
                                                service->ipconfig_ipv6);
                else
                        return NULL;
@@ -4344,6 +5232,23 @@ __connman_service_get_ipconfig(struct connman_service *service, int family)
 
 }
 
+connman_bool_t __connman_service_is_connected_state(struct connman_service *service,
+                                       enum connman_ipconfig_type type)
+{
+       if (service == NULL)
+               return FALSE;
+
+       switch (type) {
+       case CONNMAN_IPCONFIG_TYPE_UNKNOWN:
+               break;
+       case CONNMAN_IPCONFIG_TYPE_IPV4:
+               return is_connected_state(service, service->state_ipv4);
+       case CONNMAN_IPCONFIG_TYPE_IPV6:
+               return is_connected_state(service, service->state_ipv6);
+       }
+
+       return FALSE;
+}
 enum connman_service_security __connman_service_get_security(struct connman_service *service)
 {
        if (service == NULL)
@@ -4368,22 +5273,88 @@ connman_bool_t __connman_service_wps_enabled(struct connman_service *service)
        return service->wps;
 }
 
+void __connman_service_mark_dirty(void)
+ {
+       services_dirty = TRUE;
+ }
+
+#if defined TIZEN_EXT
 /**
- * __connman_service_set_favorite:
+  * Returns profile count if there is any connected profiles
+  * that use same interface
+  */
+int __connman_service_get_connected_count_of_iface(
+                                       struct connman_service *service)
+{
+       GHashTableIter iter;
+       gpointer key, value;
+       const char *ifname1;
+       const char *ifname2;
+       int count = 0;
+
+       DBG("");
+
+       if (service->ipconfig_ipv4)
+               ifname1 = __connman_ipconfig_get_ifname(service->ipconfig_ipv4);
+       else if (service->ipconfig_ipv6)
+               ifname1 = __connman_ipconfig_get_ifname(service->ipconfig_ipv6);
+       else
+               ifname1 = NULL;
+
+       if (ifname1 == NULL)
+               return 0;
+
+       g_hash_table_iter_init(&iter, service_hash);
+
+       while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
+               GSequenceIter *iter = value;
+               struct connman_service *iter_service = g_sequence_get(iter);
+
+               if (service == iter_service)
+                       continue;
+
+               if (iter_service->ipconfig_ipv4)
+                       ifname2 = __connman_ipconfig_get_ifname(
+                                       iter_service->ipconfig_ipv4);
+               else if (iter_service->ipconfig_ipv6)
+                       ifname2 = __connman_ipconfig_get_ifname(
+                                       iter_service->ipconfig_ipv6);
+               else
+                       ifname2 = NULL;
+
+               if (is_connected(iter_service) && ifname2 != NULL &&
+                               g_strcmp0(ifname1, ifname2) == 0)
+                       count++;
+       }
+
+       DBG("Interface %s, count %d", ifname1, count);
+
+       return count;
+}
+#endif
+
+/**
+ * __connman_service_set_favorite_delayed:
  * @service: service structure
  * @favorite: favorite value
+ * @delay_ordering: do not order service sequence
  *
  * Change the favorite setting of service
  */
-int __connman_service_set_favorite(struct connman_service *service,
-                                               connman_bool_t favorite)
+int __connman_service_set_favorite_delayed(struct connman_service *service,
+                                       connman_bool_t favorite,
+                                       gboolean delay_ordering)
 {
        GSequenceIter *iter;
 
 #if defined TIZEN_EXT
+       struct connman_device *device;
+
        if (service->type == CONNMAN_SERVICE_TYPE_CELLULAR)
                return -EIO;
 #endif
+       if (service->hidden == TRUE)
+               return -EOPNOTSUPP;
        iter = g_hash_table_lookup(service_hash, service->identifier);
        if (iter == NULL)
                return -ENOENT;
@@ -4392,34 +5363,64 @@ int __connman_service_set_favorite(struct connman_service *service,
                return -EALREADY;
 
        service->favorite = favorite;
-       service->order = __connman_service_get_order(service);
+
+       if (delay_ordering == FALSE)
+               service->order = __connman_service_get_order(service);
 
        favorite_changed(service);
 
-       g_sequence_sort_changed(iter, service_compare, NULL);
+       if (delay_ordering == FALSE) {
 
-       services_changed(FALSE);
+               if (g_sequence_get_length(service_list) > 1) {
+                       g_sequence_sort_changed(iter, service_compare, NULL);
+                       service_schedule_changed();
+               }
+
+               __connman_connection_update_gateway();
+       }
 
 #if defined TIZEN_EXT
-       {
-               struct connman_device *device = connman_network_get_device(service->network);
+       device = connman_network_get_device(service->network);
 
-               if (connman_device_get_type(device) == CONNMAN_DEVICE_TYPE_WIFI) {
-                       if (service->favorite == TRUE)
-                               connman_device_significant_wifi_profile_ref(device);
-                       else
-                               connman_device_significant_wifi_profile_unref_and_test(device);
-               }
+       if (device == NULL)
+               return 0;
 
-               connman_device_save_significant_wifi_profile_refcount_to_storage(device);
+       if (connman_device_get_type(device) == CONNMAN_DEVICE_TYPE_WIFI) {
+               if (service->favorite == TRUE)
+                       connman_device_sig_wifi_profile_ref(device);
+               else
+                       connman_device_sig_wifi_profile_unref_and_test(device);
        }
+
+       connman_device_save_sig_wifi_profile_refcount2storage(device);
 #endif
        return 0;
 }
 
+/**
+ * __connman_service_set_favorite:
+ * @service: service structure
+ * @favorite: favorite value
+ *
+ * Change the favorite setting of service
+ */
+int __connman_service_set_favorite(struct connman_service *service,
+                                               connman_bool_t favorite)
+{
+       return __connman_service_set_favorite_delayed(service, favorite,
+                                                       FALSE);
+}
+
+connman_bool_t connman_service_get_favorite(struct connman_service *service)
+{
+       return service->favorite;
+}
+
 int __connman_service_set_immutable(struct connman_service *service,
                                                connman_bool_t immutable)
 {
+       if (service->hidden == TRUE)
+               return -EOPNOTSUPP;
        service->immutable = immutable;
 
        immutable_changed(service);
@@ -4430,6 +5431,8 @@ int __connman_service_set_immutable(struct connman_service *service,
 void __connman_service_set_string(struct connman_service *service,
                                  const char *key, const char *value)
 {
+       if (service->hidden == TRUE)
+               return;
        if (g_str_equal(key, "EAP") == TRUE) {
                g_free(service->eap);
                service->eap = g_strdup(value);
@@ -4457,6 +5460,13 @@ void __connman_service_set_string(struct connman_service *service,
        }
 }
 
+void __connman_service_set_userconnect(struct connman_service *service,
+                                               connman_bool_t userconnect)
+{
+       if (service != NULL)
+               service->userconnect = userconnect;
+}
+
 static void service_complete(struct connman_service *service)
 {
        reply_pending(service, EIO);
@@ -4474,53 +5484,203 @@ static void report_error_cb(struct connman_service *service,
        if (retry == TRUE)
                __connman_service_connect(service);
        else {
+               /* It is not relevant to stay on Failure state
+                * when failing is due to wrong user input */
+               service->state = CONNMAN_SERVICE_STATE_IDLE;
+
                service_complete(service);
-               services_changed(FALSE);
-               __connman_device_request_scan(CONNMAN_DEVICE_TYPE_UNKNOWN);
+               __connman_connection_update_gateway();
+       }
+}
+
+int __connman_service_add_passphrase(struct connman_service *service,
+                               const gchar *passphrase)
+{
+       int err = 0;
+
+       switch (service->security) {
+       case CONNMAN_SERVICE_SECURITY_WEP:
+       case CONNMAN_SERVICE_SECURITY_PSK:
+               err = __connman_service_set_passphrase(service, passphrase);
+               break;
+       case CONNMAN_SERVICE_SECURITY_8021X:
+               __connman_service_set_agent_passphrase(service,
+                                               passphrase);
+               break;
+       case CONNMAN_SERVICE_SECURITY_UNKNOWN:
+       case CONNMAN_SERVICE_SECURITY_NONE:
+       case CONNMAN_SERVICE_SECURITY_WPA:
+       case CONNMAN_SERVICE_SECURITY_RSN:
+               DBG("service security '%s' (%d) not handled",
+                               security2string(service->security),
+                               service->security);
+               break;
+       }
+
+       return err;
+}
+
+static int check_wpspin(struct connman_service *service, const char *wpspin)
+{
+       int length;
+       guint i;
+
+       if (wpspin == NULL)
+               return 0;
+
+       length = strlen(wpspin);
+
+       /* If 0, it will mean user wants to use PBC method */
+       if (length == 0) {
+               connman_network_set_string(service->network,
+                                                       "WiFi.PinWPS", NULL);
+               return 0;
+       }
+
+       /* A WPS PIN is always 8 chars length,
+        * its content is in digit representation.
+        */
+       if (length != 8)
+               return -ENOKEY;
+
+       for (i = 0; i < 8; i++)
+               if (!isdigit((unsigned char) wpspin[i]))
+                       return -ENOKEY;
+
+       connman_network_set_string(service->network, "WiFi.PinWPS", wpspin);
+
+       return 0;
+}
+
+#if defined TIZEN_EXT
+static int __connman_service_connect_hidden(struct connman_service *service,
+                       const char *name, int name_len,
+                       const char *identity, const char *passphrase, void *user_data)
+{
+       GSequenceIter *iter = g_sequence_get_begin_iter(service_list);
+
+       while (g_sequence_iter_is_end(iter) == FALSE) {
+               struct connman_service *target = g_sequence_get(iter);
+               const char *target_ssid = NULL;
+               unsigned int target_ssid_len = 0;
+
+               if (service->network != NULL &&
+                                       service->security == target->security) {
+                       target_ssid = connman_network_get_blob(service->network,
+                                                       "WiFi.SSID", &target_ssid_len);
+                       if (target_ssid_len == name_len &&
+                                                       memcmp(target_ssid, name, name_len) == 0) {
+                               return connman_network_connect_hidden(service->network,
+                                                       (char *)identity, (char *)passphrase, user_data);
+                       }
+               }
+
+               iter = g_sequence_iter_next(iter);
        }
+
+       return -ENOENT;
 }
+#endif
 
 static void request_input_cb (struct connman_service *service,
+                       connman_bool_t values_received,
+                       const char *name, int name_len,
                        const char *identity, const char *passphrase,
-                       void *user_data)
+                       gboolean wps, const char *wpspin,
+                       const char *error, void *user_data)
 {
+       struct connman_device *device;
+       int err = 0;
+
        DBG ("RequestInput return, %p", service);
 
-       if (identity == NULL && passphrase == NULL && service->wps == FALSE) {
-               service_complete(service);
-               services_changed(FALSE);
-               __connman_device_request_scan(CONNMAN_DEVICE_TYPE_UNKNOWN);
-               return;
+       if (error != NULL) {
+               DBG("error: %s", error);
+
+               if (g_strcmp0(error,
+                               "net.connman.Agent.Error.Canceled") == 0) {
+                       err = -EINVAL;
+
+                       if (service->hidden == TRUE)
+                               __connman_service_return_error(service,
+                                                       ECANCELED, user_data);
+                       goto done;
+               } else {
+                       if (service->hidden == TRUE)
+                               __connman_service_return_error(service,
+                                                       ETIMEDOUT, user_data);
+               }
+       }
+
+       if (service->hidden == TRUE && name_len > 0 && name_len <= 32) {
+#if defined TIZEN_EXT
+               /* TIZEN already has Wi-Fi hidden scan before this hidden connection */
+               err = __connman_service_connect_hidden(service, name, name_len,
+                                               identity, passphrase, user_data);
+               if (err == 0 || err == -EALREADY || err == -EINPROGRESS)
+                       return;
+#endif
+               device = connman_network_get_device(service->network);
+               err = __connman_device_request_hidden_scan(device,
+                                               name, name_len,
+                                               identity, passphrase,
+                                               user_data);
+               if (err < 0)
+                       __connman_service_return_error(service, -err,
+                                                       user_data);
+       }
+
+       if (values_received == FALSE || service->hidden == TRUE) {
+               err = -EINVAL;
+               goto done;
+       }
+
+       if (wps == TRUE && service->network != NULL) {
+               err = check_wpspin(service, wpspin);
+               if (err < 0)
+                       goto done;
+
+               connman_network_set_bool(service->network, "WiFi.UseWPS", wps);
        }
 
        if (identity != NULL)
                __connman_service_set_agent_identity(service, identity);
 
-       if (passphrase != NULL) {
-               switch (service->security) {
-               case CONNMAN_SERVICE_SECURITY_WEP:
-               case CONNMAN_SERVICE_SECURITY_PSK:
-                       __connman_service_set_passphrase(service, passphrase);
-                       break;
-               case CONNMAN_SERVICE_SECURITY_8021X:
-                       __connman_service_set_agent_passphrase(service,
-                                                       passphrase);
-                       break;
-               case CONNMAN_SERVICE_SECURITY_UNKNOWN:
-               case CONNMAN_SERVICE_SECURITY_NONE:
-               case CONNMAN_SERVICE_SECURITY_WPA:
-               case CONNMAN_SERVICE_SECURITY_RSN:
-                       DBG("service security '%s' not handled",
-                               security2string(service->security));
-                       break;
-               }
-       }
+       if (passphrase != NULL)
+               err = __connman_service_add_passphrase(service, passphrase);
 
-       __connman_service_connect(service);
+ done:
+       if (err >= 0) {
+               /* We forget any previous error. */
+               set_error(service, CONNMAN_SERVICE_ERROR_UNKNOWN);
 
-       /* Never cache agent provided credentials */
-       __connman_service_set_agent_identity(service, NULL);
-       __connman_service_set_agent_passphrase(service, NULL);
+               __connman_service_connect(service);
+
+               /* Never cache agent provided credentials */
+               __connman_service_set_agent_identity(service, NULL);
+               __connman_service_set_agent_passphrase(service, NULL);
+       } else if (err == -ENOKEY) {
+               __connman_service_indicate_error(service,
+                                       CONNMAN_SERVICE_ERROR_INVALID_KEY);
+       } else {
+               /* It is not relevant to stay on Failure state
+                * when failing is due to wrong user input */
+               service->state = CONNMAN_SERVICE_STATE_IDLE;
+
+               if (service->hidden == FALSE) {
+                       /*
+                        * If there was a real error when requesting
+                        * hidden scan, then that error is returned already
+                        * to the user somewhere above so do not try to
+                        * do this again.
+                        */
+                       __connman_service_return_error(service, -err,
+                                                       user_data);
+               }
+
+               service_complete(service);
+               __connman_connection_update_gateway();
+       }
 }
 
 static void downgrade_connected_services(void)
@@ -4546,84 +5706,191 @@ static void downgrade_connected_services(void)
        }
 }
 
+static int service_update_preferred_order(struct connman_service *default_service,
+               struct connman_service *new_service,
+               enum connman_service_state new_state)
+{
+       unsigned int *tech_array;
+       int i;
+
+       if (default_service == NULL || default_service == new_service ||
+                       default_service->state != new_state )
+               return 0;
+
+       tech_array = connman_setting_get_uint_list("PreferredTechnologies");
+       if (tech_array != NULL) {
+
+               for (i = 0; tech_array[i] != 0; i += 1) {
+                       if (default_service->type == tech_array[i])
+                               return -EALREADY;
+
+                       if (new_service->type == tech_array[i]) {
+                               switch_default_service(default_service,
+                                               new_service);
+                               __connman_connection_update_gateway();
+                               return 0;
+                       }
+               }
+       }
+
+       return -EALREADY;
+}
+
 #if defined TIZEN_EXT
-static connman_bool_t __connman_service_can_drop_cellular(
-               struct connman_service *cellular)
+static connman_bool_t __connman_service_can_drop(struct connman_service *service)
 {
-       if (cellular->type == CONNMAN_SERVICE_TYPE_CELLULAR &&
-                               is_connected(cellular) == TRUE)
-               if (connman_service_is_no_ref_user_initiated_pdn_connection(cellular) == TRUE)
+       if (is_connected(service) == TRUE || is_connecting(service) == TRUE) {
+               if (service->type != CONNMAN_SERVICE_TYPE_CELLULAR)
                        return TRUE;
+               else if (connman_service_is_no_ref_user_pdn_connection(service) == TRUE)
+                       return TRUE;
+       }
+
        return FALSE;
 }
 
-static void __connman_service_connect_default(void)
+static struct connman_device *default_connecting_device = NULL;
+
+static void __connman_service_disconnect_default(struct connman_service *service)
 {
-       static struct connman_device *connecting_device = NULL;
-       struct connman_service *default_service = NULL;
        struct connman_device *default_device = NULL;
 
-       default_service = __connman_service_get_default();
+       if (default_connecting_device == NULL)
+               return;
 
-       if (default_service != NULL) {
-               if (is_connecting(default_service) == FALSE &&
-                               is_connected(default_service) == FALSE) {
+       default_device = connman_network_get_device(
+                       __connman_service_get_network(service));
 
-                       default_device = connman_network_get_device(
-                                       __connman_service_get_network(default_service));
+       DBG("Disconnecting service %p %s", service, service->path);
+       DBG("Disconnecting device %p %p %s",
+                       default_connecting_device,
+                       default_device,
+                       connman_device_get_string(default_device, "Name"));
 
-                       DBG("connecting_device %p", connecting_device);
+       if (default_connecting_device == default_device)
+               default_connecting_device = NULL;
+}
 
-                       if (connecting_device == default_device)
+static void __connman_service_connect_default(struct connman_service *current)
+{
+       int err;
+       GSequenceIter *iter;
+       connman_bool_t default_internet;
+       struct connman_service *default_service = NULL;
+       struct connman_device *default_device = NULL;
+
+       if (current->type == CONNMAN_SERVICE_TYPE_CELLULAR) {
+               if (default_connecting_device != NULL && is_connected(current) == TRUE
+                               && __connman_service_is_internet_profile(current) == TRUE) {
+                       if (current->network == NULL)
                                return;
 
-                       connecting_device = default_device;
-                       default_service->userconnect = FALSE;
+                       default_device = connman_network_get_device(current->network);
 
-                       DBG("Connecting default service %p %s",
-                                       default_service, default_service->path);
-                       DBG("Connecting device %p %s", connecting_device,
-                                       connman_device_get_string(connecting_device, "Name"));
+                       DBG("Cellular service connected %p %s", current, current->path);
+                       DBG("Cellular device connected %p %p %s",
+                                       default_connecting_device,
+                                       default_device,
+                                       connman_device_get_string(default_device, "Name"));
 
-                       __connman_service_connect(default_service);
-               } else if (is_connected(default_service) == TRUE) {
-                       DBG("default service connected %s", default_service->path);
-                       connecting_device = NULL;
+                       if (default_connecting_device == default_device)
+                               default_connecting_device = NULL;
+               }
+
+               return;
+       } else if (current->localnetwork != TRUE &&
+                       (is_connected(current) == TRUE || is_connecting(current) == TRUE))
+               return;
+
+       /* Always-on: keep default cellular connection as possible */
+       iter = g_sequence_get_begin_iter(service_list);
+
+       while (g_sequence_iter_is_end(iter) == FALSE) {
+               struct connman_service *service = g_sequence_get(iter);
+
+               if (service->type != CONNMAN_SERVICE_TYPE_CELLULAR ||
+                       __connman_service_is_internet_profile(service) != TRUE ||
+                                                                               service->network == NULL) {
+                       iter = g_sequence_iter_next(iter);
+                       continue;
+               }
+
+               default_internet =
+                               connman_network_get_bool(service->network, "DefaultInternet");
+
+               DBG("service: %p %s %s %s (default: %d)", service, service->name,
+                               __connman_service_type2string(service->type),
+                               state2string(service->state), default_internet);
+
+               if (default_internet == TRUE) {
+                       default_service = service;
+                       break;
                }
+
+               iter = g_sequence_iter_next(iter);
+       }
+
+       if (default_service != NULL &&
+                       is_connected(default_service) == FALSE &&
+                       is_connecting(default_service) == FALSE) {
+               default_device = connman_network_get_device(default_service->network);
+
+               DBG("connecting_device %p", default_connecting_device);
+
+               if (default_connecting_device == default_device)
+                       return;
+
+               default_connecting_device = default_device;
+               default_service->userconnect = FALSE;
+
+               DBG("Connecting default service %p %s",
+                               default_service, default_service->path);
+               DBG("Connecting device %p %s", default_connecting_device,
+                               connman_device_get_string(default_connecting_device, "Name"));
+
+               err = __connman_network_connect(default_service->network);
+               if (err != EINPROGRESS)
+                       default_connecting_device = NULL;
        }
 }
 #endif
 
-static void service_single_connection(struct connman_service *allowed)
+static void single_connected_tech(struct connman_service *allowed)
 {
+       GSList *services = NULL;
        GSequenceIter *iter;
-       GSList *services = NULL, *list;
+       GSList *list;
 
 #if defined TIZEN_EXT
-       if (allowed->type == CONNMAN_SERVICE_TYPE_CELLULAR)
+       if (allowed->localnetwork == TRUE ||
+                       allowed->type == CONNMAN_SERVICE_TYPE_CELLULAR)
                return;
 #endif
-
        iter = g_sequence_get_begin_iter(service_list);
 
        while (g_sequence_iter_is_end(iter) == FALSE) {
                struct connman_service *service = g_sequence_get(iter);
 
-               if (service != allowed && is_connected(service))
 #if defined TIZEN_EXT
-                       if (__connman_service_can_drop_cellular(service) == TRUE)
+               if (service != allowed && service->type != allowed->type &&
+                       __connman_service_can_drop(service) == TRUE)
+#else
+               if (service != allowed && is_connected(service))
 #endif
                        services = g_slist_prepend(services, service);
 
                iter = g_sequence_iter_next(iter);
        }
 
-       DBG("Allowed %p %s", allowed, allowed->path);
+       DBG("keeping %p %s", allowed, allowed->path);
 
        for (list = services; list != NULL; list = list->next) {
                struct connman_service *service = list->data;
 
-               DBG("Disconnecting %p %s", service, service->path);
+               DBG("disconnecting %p %s", service, service->path);
+#if defined TIZEN_EXT
+               __connman_service_disconnect_default(service);
+#endif
                __connman_service_disconnect(service);
        }
 
@@ -4634,6 +5901,7 @@ static int service_indicate_state(struct connman_service *service)
 {
        enum connman_service_state old_state, new_state;
        struct connman_service *def_service;
+       int result;
        GSequenceIter *iter;
 
        if (service == NULL)
@@ -4652,14 +5920,18 @@ static int service_indicate_state(struct connman_service *service)
        if (old_state == new_state)
                return -EALREADY;
 
-       def_service = get_default();
+       def_service = __connman_service_get_default();
 
        if (new_state == CONNMAN_SERVICE_STATE_ONLINE) {
-               if (def_service != NULL && def_service != service &&
-                       def_service->state == CONNMAN_SERVICE_STATE_ONLINE)
-                       return -EALREADY;
+               result = service_update_preferred_order(def_service,
+                               service, new_state);
+               if (result == -EALREADY)
+                       return result;
        }
 
+       if (old_state == CONNMAN_SERVICE_STATE_ONLINE)
+               __connman_notifier_leave_online(service->type);
+
        service->state = new_state;
        state_changed(service);
 
@@ -4667,9 +5939,11 @@ static int service_indicate_state(struct connman_service *service)
                        old_state != CONNMAN_SERVICE_STATE_DISCONNECT) {
 #if !defined TIZEN_EXT
                /*
-                * Description: 'service->pending' should be cleared whenever connection is finished regardless success or failure.
-                *              If the service is disconnected in configuration state by dhcp failure or by the other part of connman,
-                *              new state is 'idle' but old state is 'disconnect'. So it's not cleared.
+                * Description: 'service->pending' should be cleared whenever
+                * connection is finished regardless success or failure.
+                * If the service is disconnected in configuration state by
+                * dhcp failure or by the other part of connman, new state is
+                * 'idle' but old state is 'disconnect'. So it's not cleared.
                 */
                reply_pending(service, ECONNABORTED);
 #endif
@@ -4678,7 +5952,12 @@ static int service_indicate_state(struct connman_service *service)
        }
 
        if (new_state == CONNMAN_SERVICE_STATE_CONFIGURATION) {
-               if (__connman_stats_service_register(service) == 0) {
+               if (service->new_service == FALSE &&
+                               __connman_stats_service_register(service) == 0) {
+                       /*
+                        * For new services the statistics are updated after
+                        * we have successfully connected.
+                        */
                        __connman_stats_get(service, FALSE,
                                                &service->stats.data);
                        __connman_stats_get(service, TRUE,
@@ -4686,40 +5965,45 @@ static int service_indicate_state(struct connman_service *service)
                }
        }
 
-       if (new_state == CONNMAN_SERVICE_STATE_ONLINE) {
-               if (service->login_required == TRUE) {
-                       service->login_required = FALSE;
-                       login_changed(service);
-               }
-
-               connman_timeserver_sync();
-       }
-
        if (new_state == CONNMAN_SERVICE_STATE_IDLE) {
-#if defined TIZEN_EXT
-               if (service->type == CONNMAN_SERVICE_TYPE_WIFI &&
-                       connman_network_get_bool(service->network,
-                                               "WiFi.UseWPS") == TRUE) {
-                       connman_network_set_bool(service->network,
-                                                       "WiFi.UseWPS", FALSE);
-               }
+               connman_bool_t reconnect;
 
+#if defined TIZEN_EXT
                reply_pending(service, ECONNABORTED);
-               __connman_device_request_scan(service->type);
-
-               __connman_service_update_default(service);
 #endif
-               connman_bool_t reconnect;
-
                reconnect = get_reconnect_state(service);
                if (reconnect == TRUE)
                        __connman_service_auto_connect();
-
-               __connman_device_request_scan(CONNMAN_DEVICE_TYPE_UNKNOWN);
        }
 
        if (new_state == CONNMAN_SERVICE_STATE_READY) {
                enum connman_ipconfig_method method;
+#if defined TIZEN_EXT
+               struct connman_network *network;
+#endif
+
+               if (service->new_service == TRUE &&
+                               __connman_stats_service_register(service) == 0) {
+                       /*
+                        * This is normally done after configuring state
+                        * but for new service do this after we have connected
+                        * successfully.
+                        */
+                       __connman_stats_get(service, FALSE,
+                                               &service->stats.data);
+                       __connman_stats_get(service, TRUE,
+                                               &service->stats_roaming.data);
+               }
+
+               service->new_service = FALSE;
+
+               service_update_preferred_order(def_service, service, new_state);
+
+#if defined TIZEN_EXT
+               network = __connman_service_get_network(service);
+               if (connman_network_get_is_hs20AP(network))
+                       service->autoconnect = FALSE;
+#endif
 
                set_reconnect_state(service, TRUE);
 
@@ -4727,8 +6011,6 @@ static int service_indicate_state(struct connman_service *service)
 
                reply_pending(service, 0);
 
-               service->userconnect = FALSE;
-
                g_get_current_time(&service->modified);
                service_save(service);
 
@@ -4736,7 +6018,8 @@ static int service_indicate_state(struct connman_service *service)
                dns_changed(service);
                domain_changed(service);
 
-               __connman_notifier_connect(service->type);
+               if (old_state != CONNMAN_SERVICE_STATE_ONLINE)
+                       __connman_notifier_connect(service->type);
 
                if (service->type == CONNMAN_SERVICE_TYPE_WIFI &&
                        connman_network_get_bool(service->network,
@@ -4759,19 +6042,17 @@ static int service_indicate_state(struct connman_service *service)
                        __connman_ipconfig_disable_ipv6(
                                                service->ipconfig_ipv6);
 
-#if defined TIZEN_EXT
-               __connman_service_update_default(service);
-#endif
-               if (connman_setting_get_bool("SingleConnection") == TRUE)
-                       service_single_connection(service);
+               if (connman_setting_get_bool("SingleConnectedTechnology")
+                               == TRUE)
+                       single_connected_tech(service);
 
        } else if (new_state == CONNMAN_SERVICE_STATE_DISCONNECT) {
-               def_service = get_default();
+               def_service = __connman_service_get_default();
 
-               if (__connman_notifier_count_connected() == 0 &&
+               if (__connman_notifier_is_connected() == FALSE &&
                        def_service != NULL &&
                                def_service->provider != NULL)
-                       __connman_provider_disconnect(def_service->provider);
+                       connman_provider_disconnect(def_service->provider);
 
                default_changed();
 
@@ -4779,11 +6060,21 @@ static int service_indicate_state(struct connman_service *service)
 
                __connman_wpad_stop(service);
 
+#if defined TIZEN_EXT
+               /**
+                 * Skip the functions if there is any connected profiles
+                 * that use same interface
+                 */
+               if (service->type != CONNMAN_SERVICE_TYPE_CELLULAR ||
+                       __connman_service_get_connected_count_of_iface(
+                                                       service) <= 0) {
+#endif
                update_nameservers(service);
                dns_changed(service);
                domain_changed(service);
-
-               __connman_notifier_disconnect(service->type);
+#if defined TIZEN_EXT
+               }
+#endif
 
                /*
                 * Previous services which are connected and which states
@@ -4791,31 +6082,49 @@ static int service_indicate_state(struct connman_service *service)
                 * to ready so wispr/portal will be rerun on those
                 */
                downgrade_connected_services();
+
+               __connman_service_auto_connect();
        }
 
        if (new_state == CONNMAN_SERVICE_STATE_FAILURE) {
                if (service->userconnect == TRUE &&
                        __connman_agent_report_error(service,
                                        error2string(service->error),
-                                       report_error_cb, NULL) == -EIO)
+                                       report_error_cb, NULL) == -EINPROGRESS)
                        return 0;
                service_complete(service);
-
-               __connman_device_request_scan(CONNMAN_DEVICE_TYPE_UNKNOWN);
+#if defined TIZEN_EXT
+       } else if (service->error == CONNMAN_SERVICE_ERROR_INVALID_KEY &&
+                       service->state_ipv4 != CONNMAN_SERVICE_STATE_FAILURE &&
+                       service->state_ipv6 != CONNMAN_SERVICE_STATE_FAILURE)
+#else
        } else
-               service->error = CONNMAN_SERVICE_ERROR_UNKNOWN;
+#endif
+               set_error(service, CONNMAN_SERVICE_ERROR_UNKNOWN);
 
 #if defined TIZEN_EXT
-       __connman_service_connect_default();
+       __connman_service_connect_default(service);
 #endif
+
+       if ((old_state == CONNMAN_SERVICE_STATE_ONLINE &&
+                       new_state != CONNMAN_SERVICE_STATE_READY) ||
+               (old_state == CONNMAN_SERVICE_STATE_READY &&
+                       new_state != CONNMAN_SERVICE_STATE_ONLINE)) {
+               __connman_notifier_disconnect(service->type);
+       }
+
        iter = g_hash_table_lookup(service_hash, service->identifier);
-       if (iter != NULL)
+       if (iter != NULL && g_sequence_get_length(service_list) > 1) {
                g_sequence_sort_changed(iter, service_compare, NULL);
+               service_schedule_changed();
+       }
 
-       services_changed(FALSE);
+       __connman_connection_update_gateway();
 
-       if (new_state == CONNMAN_SERVICE_STATE_ONLINE)
+       if (new_state == CONNMAN_SERVICE_STATE_ONLINE) {
+               __connman_notifier_enter_online(service->type);
                default_changed();
+       }
 
        return 0;
 }
@@ -4828,11 +6137,12 @@ int __connman_service_indicate_error(struct connman_service *service,
        if (service == NULL)
                return -EINVAL;
 
-       service->error = error;
+       set_error(service, error);
 
 #if defined TIZEN_EXT
-       if (service->error == CONNMAN_SERVICE_ERROR_INVALID_KEY &&
-           service->favorite == FALSE)
+       if (service->favorite == FALSE &&
+                       (service->error == CONNMAN_SERVICE_ERROR_INVALID_KEY ||
+                       service->error == CONNMAN_SERVICE_ERROR_CONNECT_FAILED))
 #else
        if (service->error == CONNMAN_SERVICE_ERROR_INVALID_KEY)
 #endif
@@ -4859,7 +6169,7 @@ int __connman_service_clear_error(struct connman_service *service)
 
        service->state_ipv4 = service->state_ipv6 =
                                                CONNMAN_SERVICE_STATE_UNKNOWN;
-       service->error = CONNMAN_SERVICE_ERROR_UNKNOWN;;
+       set_error(service, CONNMAN_SERVICE_ERROR_UNKNOWN);
 
        if (service->favorite == TRUE)
                set_reconnect_state(service, TRUE);
@@ -4987,6 +6297,43 @@ static void service_rp_filter(struct connman_service *service,
                connected_networks_count, original_rp_filter);
 }
 
+static gboolean redo_wispr(gpointer user_data)
+{
+       struct connman_service *service = user_data;
+
+       DBG("");
+
+       __connman_wispr_start(service, CONNMAN_IPCONFIG_TYPE_IPV6);
+
+       return FALSE;
+}
+
+int __connman_service_online_check_failed(struct connman_service *service,
+                                       enum connman_ipconfig_type type)
+{
+       DBG("service %p type %d count %d", service, type,
+                                               service->online_check_count);
+
+       /* currently we only retry IPv6 stuff */
+       if (type == CONNMAN_IPCONFIG_TYPE_IPV4 ||
+                       service->online_check_count != 1) {
+               connman_warn("Online check failed for %p %s", service,
+                       service->name);
+               return 0;
+       }
+
+       service->online_check_count = 0;
+
+       /*
+        * We set the timeout to 1 sec so that we have a chance to get
+        * necessary IPv6 router advertisement messages that might have
+        * DNS data etc.
+        */
+       g_timeout_add_seconds(1, redo_wispr, service);
+
+       return EAGAIN;
+}
+
 int __connman_service_ipconfig_indicate_state(struct connman_service *service,
                                        enum connman_service_state new_state,
                                        enum connman_ipconfig_type type)
@@ -5009,15 +6356,22 @@ int __connman_service_ipconfig_indicate_state(struct connman_service *service,
        if (ipconfig == NULL)
                return -EINVAL;
 
-#if defined TIZEN_EXT
-       if (new_state == CONNMAN_SERVICE_STATE_FAILURE &&
-           service->type == CONNMAN_SERVICE_TYPE_CELLULAR)
-               g_atomic_int_set(&service->user_initiated_pdn_connection_refcount, 0);
-#endif
        /* Any change? */
        if (old_state == new_state)
                return -EALREADY;
 
+#if defined TIZEN_EXT
+       __sync_synchronize();
+       if (service->user_pdn_connection_refcount > 0 &&
+                       service->type == CONNMAN_SERVICE_TYPE_CELLULAR)
+               if (new_state == CONNMAN_SERVICE_STATE_FAILURE ||
+                               new_state == CONNMAN_SERVICE_STATE_DISCONNECT ||
+                               new_state == CONNMAN_SERVICE_STATE_IDLE) {
+                       service->user_pdn_connection_refcount = 0;
+                       __sync_synchronize();
+               }
+#endif
+
        DBG("service %p (%s) state %d (%s) type %d (%s)",
                service, service ? service->identifier : NULL,
                new_state, state2string(new_state),
@@ -5037,11 +6391,22 @@ int __connman_service_ipconfig_indicate_state(struct connman_service *service,
        case CONNMAN_SERVICE_STATE_READY:
                update_nameservers(service);
 
+#if defined TIZEN_EXT
+               if (service->type == CONNMAN_SERVICE_TYPE_CELLULAR &&
+                               __connman_service_is_internet_profile(service) != TRUE) {
+                       if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
+                               service_rp_filter(service, TRUE);
+
+                       break;
+               }
+#endif
                if (type == CONNMAN_IPCONFIG_TYPE_IPV4) {
                        check_proxy_setup(service);
                        service_rp_filter(service, TRUE);
-               } else
+               } else {
+                       service->online_check_count = 1;
                        __connman_wispr_start(service, type);
+               }
                break;
        case CONNMAN_SERVICE_STATE_ONLINE:
                break;
@@ -5091,19 +6456,6 @@ int __connman_service_ipconfig_indicate_state(struct connman_service *service,
        return ret;
 }
 
-int __connman_service_request_login(struct connman_service *service)
-{
-       DBG("service %p", service);
-
-       if (service == NULL)
-               return -EINVAL;
-
-       service->login_required = TRUE;
-       login_changed(service);
-
-       return 0;
-}
-
 static connman_bool_t prepare_network(struct connman_service *service)
 {
        enum connman_network_type type;
@@ -5125,7 +6477,6 @@ static connman_bool_t prepare_network(struct connman_service *service)
                                "WiFi.Passphrase", service->passphrase);
                break;
        case CONNMAN_NETWORK_TYPE_ETHERNET:
-       case CONNMAN_NETWORK_TYPE_WIMAX:
        case CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN:
        case CONNMAN_NETWORK_TYPE_BLUETOOTH_DUN:
        case CONNMAN_NETWORK_TYPE_CELLULAR:
@@ -5173,6 +6524,9 @@ static int service_connect(struct connman_service *service)
 {
        int err;
 
+       if (service->hidden == TRUE)
+               return -EPERM;
+
        switch (service->type) {
        case CONNMAN_SERVICE_TYPE_UNKNOWN:
        case CONNMAN_SERVICE_TYPE_SYSTEM:
@@ -5180,7 +6534,6 @@ static int service_connect(struct connman_service *service)
        case CONNMAN_SERVICE_TYPE_GADGET:
                return -EINVAL;
        case CONNMAN_SERVICE_TYPE_ETHERNET:
-       case CONNMAN_SERVICE_TYPE_WIMAX:
        case CONNMAN_SERVICE_TYPE_BLUETOOTH:
        case CONNMAN_SERVICE_TYPE_CELLULAR:
        case CONNMAN_SERVICE_TYPE_VPN:
@@ -5198,20 +6551,19 @@ static int service_connect(struct connman_service *service)
                                if (service->network == NULL)
                                        return -EOPNOTSUPP;
 
-#if defined TIZEN_EXT
-                               if (service->wps == TRUE) {
-                                       connman_network_set_bool(service->network, "WiFi.UseWPS", TRUE);
-                                       break;
-                               } else
-                                       return -ENOKEY;
-#else
                                if (service->wps == FALSE ||
                                        connman_network_get_bool(
                                                        service->network,
                                                        "WiFi.UseWPS") == FALSE)
                                        return -ENOKEY;
+                       } else if (service->error ==
+#if defined TIZEN_EXT
+                                       CONNMAN_SERVICE_ERROR_INVALID_KEY &&
+                                       service->favorite == FALSE)
+#else
+                                       CONNMAN_SERVICE_ERROR_INVALID_KEY)
 #endif
-                       }
+                               return -ENOKEY;
                        break;
                case CONNMAN_SERVICE_SECURITY_8021X:
                        if (service->eap == NULL)
@@ -5219,12 +6571,13 @@ static int service_connect(struct connman_service *service)
 
 #if defined TIZEN_EXT
                        /*
-                        * never request credentials if using EAP-TLS, EAP-SIM or EAP-AKA
-                        * (EAP-TLS, EAP-SIM and EAP-AKA networks need to be fully provisioned)
+                        * never request credentials if using EAP-TLS, EAP-SIM
+                        * or EAP-AKA (EAP-TLS, EAP-SIM and EAP-AKA networks
+                        * need to be fully provisioned)
                         */
                        if (g_str_equal(service->eap, "tls") == TRUE ||
-                                       g_str_equal(service->eap, "sim") == TRUE ||
-                                       g_str_equal(service->eap, "aka") == TRUE)
+                               g_str_equal(service->eap, "sim") == TRUE ||
+                               g_str_equal(service->eap, "aka") == TRUE)
                                break;
 #else
                        /*
@@ -5321,518 +6674,210 @@ int __connman_service_connect(struct connman_service *service)
                err = service_connect(service);
        }
 
-       if (err >= 0)
+       if (err >= 0) {
+               set_error(service, CONNMAN_SERVICE_ERROR_UNKNOWN);
                return 0;
+       }
 
        if (err == -EINPROGRESS) {
                if (service->timeout == 0)
-                       service->timeout = g_timeout_add_seconds(
-                               CONNECT_TIMEOUT, connect_timeout, service);
-
-               return -EINPROGRESS;
-       }
-
-       __connman_service_ipconfig_indicate_state(service,
-                                       CONNMAN_SERVICE_STATE_FAILURE,
-                                       CONNMAN_IPCONFIG_TYPE_IPV4);
-       __connman_service_ipconfig_indicate_state(service,
-                                       CONNMAN_SERVICE_STATE_FAILURE,
-                                       CONNMAN_IPCONFIG_TYPE_IPV6);
-
-       if (service->network != NULL)
-               __connman_network_disconnect(service->network);
-       else if (service->type == CONNMAN_SERVICE_TYPE_VPN &&
-                               service->provider != NULL)
-                       __connman_provider_disconnect(service->provider);
-
-       if (service->userconnect == TRUE) {
-               if (err == -ENOKEY) {
-                       if (__connman_agent_request_passphrase_input(service,
-                                                       request_input_cb,
-                                                       NULL) == -EIO)
-                               return -EINPROGRESS;
-               }
-#if defined TIZEN_EXT
-               reply_pending(service, -err);
-#else
-               reply_pending(service, err);
-#endif
-       }
-
-       return err;
-}
-
-int __connman_service_disconnect(struct connman_service *service)
-{
-       int err;
-
-       DBG("service %p", service);
-
-       if (service->network != NULL) {
-               err = __connman_network_disconnect(service->network);
-       } else if (service->type == CONNMAN_SERVICE_TYPE_VPN &&
-                                       service->provider != NULL)
-               err = __connman_provider_disconnect(service->provider);
-       else
-               return -EOPNOTSUPP;
-
-       if (err < 0 && err != -EINPROGRESS)
-               return err;
-
-       __connman_6to4_remove(service->ipconfig_ipv4);
-
-       if (service->ipconfig_ipv4)
-               __connman_ipconfig_set_proxy_autoconfig(service->ipconfig_ipv4,
-                                                       NULL);
-       else
-               __connman_ipconfig_set_proxy_autoconfig(service->ipconfig_ipv6,
-                                                       NULL);
-
-       __connman_ipconfig_address_remove(service->ipconfig_ipv4);
-       __connman_ipconfig_address_remove(service->ipconfig_ipv6);
-
-       __connman_ipconfig_disable(service->ipconfig_ipv4);
-       __connman_ipconfig_disable(service->ipconfig_ipv6);
-
-       __connman_stats_service_unregister(service);
-
-       return err;
-}
-
-int __connman_service_disconnect_all(void)
-{
-       GSequenceIter *iter;
-       GSList *services = NULL, *list;
-
-       DBG("");
-
-       iter = g_sequence_get_begin_iter(service_list);
-
-       while (g_sequence_iter_is_end(iter) == FALSE) {
-               struct connman_service *service = g_sequence_get(iter);
-
-               services = g_slist_prepend(services, service);
-
-               iter = g_sequence_iter_next(iter);
-       }
-
-       for (list = services; list != NULL; list = list->next) {
-               struct connman_service *service = list->data;
-
-               service->ignore = TRUE;
-
-               set_reconnect_state(service, FALSE);
-
-               __connman_service_disconnect(service);
-       }
-
-       g_slist_free(list);
-
-       return 0;
-
-}
-
-/**
- * __connman_service_lookup:
- * @pattern: search pattern
- * @path: return object path
- *
- * Look up a service path from a search pattern
- */
-int __connman_service_lookup(const char *pattern, const char **path)
-{
-       GHashTableIter iter;
-       gpointer key, value;
-       struct connman_device *device;
-       const char *ifname;
-
-       g_hash_table_iter_init(&iter, service_hash);
-
-       while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
-               GSequenceIter *iter = value;
-               struct connman_service *service = g_sequence_get(iter);
-
-               if (g_strcmp0(service->identifier, pattern) == 0 ||
-                               g_strcmp0(service->name, pattern) == 0) {
-                       *path = (const char *) service->path;
-                       return 0;
-               }
-
-               if (service->network == NULL)
-                       continue;
-
-               device = connman_network_get_device(service->network);
-               if (device == NULL)
-                       continue;
-
-               ifname = connman_device_get_string(device, "Interface");
-               if (ifname != NULL && g_strcmp0(ifname, pattern) == 0) {
-                       *path = (const char *) service->path;
-                       return 0;
-               }
-
-       }
-
-       return -ENXIO;
-}
-
-/**
- * lookup_by_identifier:
- * @identifier: service identifier
- *
- * Look up a service by identifier (reference count will not be increased)
- */
-static struct connman_service *lookup_by_identifier(const char *identifier)
-{
-       GSequenceIter *iter;
-
-       iter = g_hash_table_lookup(service_hash, identifier);
-       if (iter != NULL)
-               return g_sequence_get(iter);
-
-       return NULL;
-}
-
-static struct connman_network *create_hidden_wifi(struct connman_device *device,
-               const char *ssid, const char *mode, const char *security,
-               const char *group)
-{
-       struct connman_network *network;
-       char *name;
-       int index;
-       unsigned int i, ssid_len;
-
-       ssid_len = strlen(ssid);
-       if (ssid_len < 1)
-               return NULL;
-
-       network = connman_network_create(group, CONNMAN_NETWORK_TYPE_WIFI);
-       if (network == NULL)
-               return NULL;
-
-       connman_network_set_blob(network, "WiFi.SSID",
-                                       (unsigned char *) ssid, ssid_len);
-
-       connman_network_set_string(network, "WiFi.Mode", mode);
-       connman_network_set_string(network, "WiFi.Security", security);
-
-       name = g_try_malloc0(ssid_len + 1);
-       if (name == NULL) {
-               connman_network_unref(network);
-               return NULL;
-       }
-
-       for (i = 0; i < ssid_len; i++) {
-               if (g_ascii_isprint(ssid[i]))
-                       name[i] = ssid[i];
-               else
-                       name[i] = ' ';
-       }
-
-       connman_network_set_name(network, name);
-
-       g_free(name);
-
-       index = connman_device_get_index(device);
-       connman_network_set_index(network, index);
-
-       if (connman_device_add_network(device, network) < 0) {
-               connman_network_unref(network);
-               return NULL;
-       }
-
-       connman_network_set_available(network, TRUE);
-
-       return network;
-}
-
-int __connman_service_create_and_connect(DBusMessage *msg)
-{
-       struct connman_service *service;
-       struct connman_network *network;
-       struct connman_device *device;
-       DBusMessageIter iter, array;
-       const char *mode = "managed", *security = "none", *group_security;
-       const char *type = NULL, *ssid = NULL, *passphrase = NULL;
-#if defined TIZEN_EXT
-       const char *eap_type = NULL, *eap_auth = NULL, *identity = NULL, *password = NULL;
-       const char *ca_cert_file = NULL, *client_cert_file = NULL;
-       const char *private_key_file = NULL, *private_key_password = NULL;
-#endif
-       connman_bool_t network_created = FALSE;
-       unsigned int ssid_len = 0;
-       const char *ident;
-       char *name, *group;
-       int err;
+                       service->timeout = g_timeout_add_seconds(
+                               CONNECT_TIMEOUT, connect_timeout, service);
+               set_error(service, CONNMAN_SERVICE_ERROR_UNKNOWN);
+               return -EINPROGRESS;
+       }
 
-       dbus_message_iter_init(msg, &iter);
-       dbus_message_iter_recurse(&iter, &array);
+       if (service->network != NULL)
+               __connman_network_disconnect(service->network);
+       else if (service->type == CONNMAN_SERVICE_TYPE_VPN &&
+                               service->provider != NULL)
+                       connman_provider_disconnect(service->provider);
 
-       while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_DICT_ENTRY) {
-               DBusMessageIter entry, value;
-               const char *key;
+       if (service->userconnect == TRUE) {
+               if (err == -ENOKEY || err == -EPERM) {
+                       DBusMessage *pending = NULL;
 
-               dbus_message_iter_recurse(&array, &entry);
-               dbus_message_iter_get_basic(&entry, &key);
+                       /*
+                        * We steal the reply here. The idea is that the
+                        * connecting client will see the connection status
+                        * after the real hidden network is connected or
+                        * connection failed.
+                        */
+                       if (service->hidden == TRUE) {
+                               pending = service->pending;
+                               service->pending = NULL;
+                       }
 
-               dbus_message_iter_next(&entry);
-               dbus_message_iter_recurse(&entry, &value);
-
-               switch (dbus_message_iter_get_arg_type(&value)) {
-               case DBUS_TYPE_STRING:
-                       if (g_str_equal(key, "Type") == TRUE)
-                               dbus_message_iter_get_basic(&value, &type);
-                       else if (g_str_equal(key, "WiFi.Mode") == TRUE ||
-                                       g_str_equal(key, "Mode") == TRUE)
-                               dbus_message_iter_get_basic(&value, &mode);
-                       else if (g_str_equal(key, "WiFi.Security") == TRUE ||
-                                       g_str_equal(key, "Security") == TRUE)
-                               dbus_message_iter_get_basic(&value, &security);
-                       else if (g_str_equal(key, "WiFi.Passphrase") == TRUE ||
-                                       g_str_equal(key, "Passphrase") == TRUE)
-                               dbus_message_iter_get_basic(&value, &passphrase);
-                       else if (g_str_equal(key, "WiFi.SSID") == TRUE ||
-                                       g_str_equal(key, "SSID") == TRUE)
-                               dbus_message_iter_get_basic(&value, &ssid);
-#if defined TIZEN_EXT
-                       else if (g_str_equal(key, "WiFi.EAPType") == TRUE ||
-                                       g_str_equal(key, "EAPType") == TRUE)
-                               dbus_message_iter_get_basic(&value, &eap_type);
-                       else if (g_str_equal(key, "WiFi.EAPAuth") == TRUE ||
-                                       g_str_equal(key, "EAPAuth") == TRUE)
-                               dbus_message_iter_get_basic(&value, &eap_auth);
-                       else if (g_str_equal(key, "WiFi.Identity") == TRUE ||
-                                       g_str_equal(key, "Identity") == TRUE)
-                               dbus_message_iter_get_basic(&value, &identity);
-                       else if (g_str_equal(key, "WiFi.Password") == TRUE ||
-                                       g_str_equal(key, "Password") == TRUE)
-                               dbus_message_iter_get_basic(&value, &password);
-                       else if (g_str_equal(key, "WiFi.CACert") == TRUE ||
-                                       g_str_equal(key, "CACert") == TRUE)
-                               dbus_message_iter_get_basic(&value, &ca_cert_file);
-                       else if (g_str_equal(key, "WiFi.ClientCert") == TRUE ||
-                                       g_str_equal(key, "ClientCert") == TRUE)
-                               dbus_message_iter_get_basic(&value, &client_cert_file);
-                       else if (g_str_equal(key, "WiFi.PrivateKeyFile") == TRUE ||
-                                       g_str_equal(key, "PrivateKeyFile") == TRUE)
-                               dbus_message_iter_get_basic(&value, &private_key_file);
-                       else if (g_str_equal(key, "WiFi.PrivateKeyPassword") == TRUE ||
-                                       g_str_equal(key, "PrivateKeyPassword") == TRUE)
-                               dbus_message_iter_get_basic(&value, &private_key_password);
-                       break;
-#endif
-               }
+                       err = __connman_agent_request_passphrase_input(service,
+                                       request_input_cb, pending);
+                       if (service->hidden == TRUE && err != -EINPROGRESS)
+                               service->pending = pending;
 
-               dbus_message_iter_next(&array);
+                       return err;
+               }
+               reply_pending(service, -err);
        }
 
-       if (type == NULL)
-               return -EINVAL;
-
-       if (g_strcmp0(type, "wifi") != 0 || g_strcmp0(mode, "managed") != 0)
-               return -EOPNOTSUPP;
+       return err;
+}
 
-       if (ssid == NULL)
-               return -EINVAL;
+int __connman_service_disconnect(struct connman_service *service)
+{
+       int err;
 
-       ssid_len = strlen(ssid);
-       if (ssid_len < 1)
-               return -EINVAL;
+       DBG("service %p", service);
 
-       if (g_strcmp0(security, "none") != 0 &&
-                               g_strcmp0(security, "wep") != 0 &&
-                               g_strcmp0(security, "psk") != 0 &&
-                               g_strcmp0(security, "wpa") != 0 &&
-                               g_strcmp0(security, "rsn") != 0 &&
-                               g_strcmp0(security, "ieee8021x") != 0)
-               return -EINVAL;
+       service->userconnect = FALSE;
+       service->proxy = CONNMAN_SERVICE_PROXY_METHOD_UNKNOWN;
 
-       device = __connman_device_find_device(CONNMAN_SERVICE_TYPE_WIFI);
-       if (device == NULL)
+       if (service->network != NULL) {
+               err = __connman_network_disconnect(service->network);
+       } else if (service->type == CONNMAN_SERVICE_TYPE_VPN &&
+                                       service->provider != NULL)
+               err = connman_provider_disconnect(service->provider);
+       else
                return -EOPNOTSUPP;
 
-       ident = connman_device_get_ident(device);
-       if (ident == NULL)
-               return -EOPNOTSUPP;
+       if (err < 0 && err != -EINPROGRESS)
+               return err;
 
+       __connman_6to4_remove(service->ipconfig_ipv4);
 
-       if (!g_strcmp0(security, "wpa") ||
-               !g_strcmp0(security, "rsn"))
-               group_security = "psk";
+       if (service->ipconfig_ipv4)
+               __connman_ipconfig_set_proxy_autoconfig(service->ipconfig_ipv4,
+                                                       NULL);
        else
-               group_security = security;
-
-       group = wifi_build_group_name((unsigned char *) ssid,
-                                               ssid_len, mode, group_security);
-       if (group == NULL)
-               return -EINVAL;
-
-       name = g_strdup_printf("%s_%s_%s", type, ident, group);
-
-       service = lookup_by_identifier(name);
+               __connman_ipconfig_set_proxy_autoconfig(service->ipconfig_ipv6,
+                                                       NULL);
 
-       if (service == NULL) {
-               network = create_hidden_wifi(device, ssid,
-                                               mode, security, group);
-               if (network != NULL) {
-                       connman_network_set_group(network, group);
-                       network_created = TRUE;
-               }
+#if defined TIZEN_EXT
+       /**
+         * Skip the functions If there is any connected profiles
+         * that use same interface
+         */
+       if (service->type != CONNMAN_SERVICE_TYPE_CELLULAR ||
+               __connman_service_get_connected_count_of_iface(service) <= 0) {
+#endif
+       __connman_ipconfig_address_remove(service->ipconfig_ipv4);
+       __connman_ipconfig_address_remove(service->ipconfig_ipv6);
 
-               service = lookup_by_identifier(name);
+       __connman_ipconfig_disable(service->ipconfig_ipv4);
+       __connman_ipconfig_disable(service->ipconfig_ipv6);
+#if defined TIZEN_EXT
        }
+#endif
 
-       g_free(name);
-       g_free(group);
+       __connman_stats_service_unregister(service);
 
-       if (service == NULL) {
-               err = -EOPNOTSUPP;
-               goto failed;
-       }
+       return err;
+}
 
-       service->network_created = network_created;
+int __connman_service_disconnect_all(void)
+{
+       GSequenceIter *iter;
+       GSList *services = NULL, *list;
 
-       if (is_connected(service) == TRUE) {
-               err = -EISCONN;
-               goto failed;
-       }
+       DBG("");
 
-       if (is_connecting(service) == TRUE) {
-               err = -EALREADY;
-               goto failed;
-       }
+       iter = g_sequence_get_begin_iter(service_list);
 
-       set_reconnect_state(service, FALSE);
+       while (g_sequence_iter_is_end(iter) == FALSE) {
+               struct connman_service *service = g_sequence_get(iter);
 
-       __connman_device_disconnect(device);
+               services = g_slist_prepend(services, service);
 
-       if (passphrase != NULL) {
-               g_free(service->passphrase);
-               service->passphrase = g_strdup(passphrase);
+               iter = g_sequence_iter_next(iter);
        }
 
-#if defined TIZEN_EXT
-       if (g_strcmp0(security, "ieee8021x") == 0) {
-               if (eap_type != NULL)
-                       __connman_service_set_string(service, "EAP", eap_type);
-
-               if (eap_auth != NULL)
-                       __connman_service_set_string(service, "Phase2", eap_auth);
-
-               if (identity != NULL)
-                       __connman_service_set_string(service, "Identity", identity);
-
-               if (password != NULL)
-                       __connman_service_set_string(service, "Passphrase", password);
-
-               if (ca_cert_file != NULL)
-                       __connman_service_set_string(service, "CACertFile", ca_cert_file);
+       for (list = services; list != NULL; list = list->next) {
+               struct connman_service *service = list->data;
 
-               if (client_cert_file != NULL)
-                       __connman_service_set_string(service, "ClientCertFile", client_cert_file);
+               service->ignore = TRUE;
 
-               if (private_key_file != NULL)
-                       __connman_service_set_string(service, "PrivateKeyFile", private_key_file);
+               set_reconnect_state(service, FALSE);
 
-               if (private_key_password != NULL)
-                       __connman_service_set_string(service, "PrivateKeyPassphrase", private_key_password);
+               __connman_service_disconnect(service);
        }
-#endif
 
-       service->userconnect = TRUE;
-
-       err = __connman_service_connect(service);
-       if (err < 0 && err != -EINPROGRESS)
-               goto failed;
-
-       service->pending = dbus_message_ref(msg);
+       g_slist_free(list);
 
        return 0;
 
-failed:
-       if (service != NULL && service->network_created == TRUE) {
-               struct connman_network *network = service->network;
+}
 
-               if (network != NULL) {
-                       connman_network_set_available(network, FALSE);
-                       __connman_device_cleanup_networks(device);
-               } else
-                       __connman_service_put(service);
-       }
+/**
+ * lookup_by_identifier:
+ * @identifier: service identifier
+ *
+ * Look up a service by identifier (reference count will not be increased)
+ */
+static struct connman_service *lookup_by_identifier(const char *identifier)
+{
+       GSequenceIter *iter;
 
-       return err;
+       iter = g_hash_table_lookup(service_hash, identifier);
+       if (iter != NULL)
+               return g_sequence_get(iter);
+
+       return NULL;
 }
 
+struct provision_user_data {
+       const char *ident;
+       int ret;
+};
+
 static void provision_changed(gpointer value, gpointer user_data)
 {
        struct connman_service *service = value;
-       char *path = user_data;
-
-       __connman_config_provision_service_ident(service, path);
-}
+       struct provision_user_data *data = user_data;
+       const char *path = data->ident;
+       int ret;
 
-void __connman_service_provision_changed(const char *ident)
-{
-       g_sequence_foreach(service_list, provision_changed, (void *)ident);
+       ret = __connman_config_provision_service_ident(service, path,
+                       service->config_file, service->config_entry);
+       if (ret > 0)
+               data->ret = ret;
 }
 
-int __connman_service_provision(DBusMessage *msg)
+int __connman_service_provision_changed(const char *ident)
 {
-       GKeyFile *keyfile = NULL;
-       const char *config_str = NULL;
-       char *group = NULL, *ident = NULL;
-       int err = 0;
-       struct connman_service *service;
-
-       DBG("");
-
-       dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &config_str,
-                                                       DBUS_TYPE_INVALID);
-
-       if (config_str == NULL || strlen(config_str) == 0)
-               return -EINVAL;
+       struct provision_user_data data = {
+               .ident = ident,
+               .ret = 0
+       };
 
-       keyfile = g_key_file_new();
-
-       /* populate GKeyFile with config_str */
-       if (g_key_file_load_from_data(keyfile, config_str,
-                                       strlen(config_str), 0, NULL) == FALSE) {
-               err = -EINVAL;
-               goto done;
-       }
+       g_sequence_foreach(service_list, provision_changed, (void *)&data);
 
        /*
-        * read only one group of settings (only one service supported, no
-        * global settings)
+        * Because the provision_changed() might have set some services
+        * as favorite, we must sort the sequence now.
         */
-       group = g_key_file_get_start_group(keyfile);
-
-       if (group == NULL || g_str_has_prefix(group, "service_") == FALSE) {
-               err = -EINVAL;
-               goto done;
-       }
+       if (services_dirty == TRUE) {
+               services_dirty = FALSE;
 
-       err = __connman_config_load_service(keyfile, group, TRUE);
-       if (err < 0)
-               goto done;
-
-       ident = group + strlen("service_");
+               if (g_sequence_get_length(service_list) > 1) {
+                       g_sequence_sort(service_list, service_compare, NULL);
+                       service_schedule_changed();
+               }
 
-       /* trigger service provisioning if service exists */
-       service = lookup_by_identifier(ident);
-       if (service != NULL)
-               __connman_config_provision_service(service);
+               __connman_connection_update_gateway();
+       }
 
-       g_dbus_send_reply(connection, msg, DBUS_TYPE_INVALID);
+       return data.ret;
+}
 
-done:
-       if (group != NULL)
-               g_free(group);
+void __connman_service_set_config(struct connman_service *service,
+                               const char *file_id, const char *entry)
+{
+       if (service == NULL)
+               return;
 
-       if (keyfile != NULL)
-               g_key_file_free(keyfile);
+       g_free(service->config_file);
+       service->config_file = g_strdup(file_id);
 
-       return err;
+       g_free(service->config_entry);
+       service->config_entry = g_strdup(entry);
 }
 
 /**
@@ -5884,9 +6929,15 @@ static int service_register(struct connman_service *service)
 
        DBG("path %s", service->path);
 
+#if defined TIZEN_EXT
+       service_load(service);
+
+       __connman_config_provision_service(service);
+#else
        __connman_config_provision_service(service);
 
        service_load(service);
+#endif
 
        g_dbus_register_interface(connection, service->path,
                                        CONNMAN_SERVICE_INTERFACE,
@@ -5894,19 +6945,21 @@ static int service_register(struct connman_service *service)
                                                        NULL, service, NULL);
 
        iter = g_hash_table_lookup(service_hash, service->identifier);
-       if (iter != NULL)
+       if (iter != NULL && g_sequence_get_length(service_list) > 1) {
                g_sequence_sort_changed(iter, service_compare, NULL);
+               service_schedule_changed();
+       }
 
-       services_changed(TRUE);
+       __connman_connection_update_gateway();
 
        return 0;
 }
 
 static void service_up(struct connman_ipconfig *ipconfig)
 {
-       struct connman_service *service = connman_ipconfig_get_data(ipconfig);
+       struct connman_service *service = __connman_ipconfig_get_data(ipconfig);
 
-       DBG("%s up", connman_ipconfig_get_ifname(ipconfig));
+       DBG("%s up", __connman_ipconfig_get_ifname(ipconfig));
 
        link_changed(service);
 
@@ -5916,23 +6969,29 @@ static void service_up(struct connman_ipconfig *ipconfig)
 
 static void service_down(struct connman_ipconfig *ipconfig)
 {
-       DBG("%s down", connman_ipconfig_get_ifname(ipconfig));
+       DBG("%s down", __connman_ipconfig_get_ifname(ipconfig));
 }
 
 static void service_lower_up(struct connman_ipconfig *ipconfig)
 {
-       struct connman_service *service = connman_ipconfig_get_data(ipconfig);
+       struct connman_service *service = __connman_ipconfig_get_data(ipconfig);
 
-       DBG("%s lower up", connman_ipconfig_get_ifname(ipconfig));
+       DBG("%s lower up", __connman_ipconfig_get_ifname(ipconfig));
 
        stats_start(service);
 }
 
 static void service_lower_down(struct connman_ipconfig *ipconfig)
 {
-       struct connman_service *service = connman_ipconfig_get_data(ipconfig);
+       struct connman_service *service = __connman_ipconfig_get_data(ipconfig);
 
-       DBG("%s lower down", connman_ipconfig_get_ifname(ipconfig));
+       DBG("%s lower down", __connman_ipconfig_get_ifname(ipconfig));
+
+       if (is_idle_state(service, service->state_ipv4) == FALSE)
+               __connman_ipconfig_disable(service->ipconfig_ipv4);
+
+       if (is_idle_state(service, service->state_ipv6) == FALSE)
+               __connman_ipconfig_disable(service->ipconfig_ipv6);
 
        stats_stop(service);
        service_save(service);
@@ -5940,11 +6999,11 @@ static void service_lower_down(struct connman_ipconfig *ipconfig)
 
 static void service_ip_bound(struct connman_ipconfig *ipconfig)
 {
-       struct connman_service *service = connman_ipconfig_get_data(ipconfig);
+       struct connman_service *service = __connman_ipconfig_get_data(ipconfig);
        enum connman_ipconfig_method method = CONNMAN_IPCONFIG_METHOD_UNKNOWN;
        enum connman_ipconfig_type type = CONNMAN_IPCONFIG_TYPE_UNKNOWN;
 
-       DBG("%s ip bound", connman_ipconfig_get_ifname(ipconfig));
+       DBG("%s ip bound", __connman_ipconfig_get_ifname(ipconfig));
 
        type = __connman_ipconfig_get_config_type(ipconfig);
        method = __connman_ipconfig_get_method(ipconfig);
@@ -5963,11 +7022,11 @@ static void service_ip_bound(struct connman_ipconfig *ipconfig)
 
 static void service_ip_release(struct connman_ipconfig *ipconfig)
 {
-       struct connman_service *service = connman_ipconfig_get_data(ipconfig);
+       struct connman_service *service = __connman_ipconfig_get_data(ipconfig);
        enum connman_ipconfig_method method = CONNMAN_IPCONFIG_METHOD_UNKNOWN;
        enum connman_ipconfig_type type = CONNMAN_IPCONFIG_TYPE_UNKNOWN;
 
-       DBG("%s ip release", connman_ipconfig_get_ifname(ipconfig));
+       DBG("%s ip release", __connman_ipconfig_get_ifname(ipconfig));
 
        type = __connman_ipconfig_get_config_type(ipconfig);
        method = __connman_ipconfig_get_method(ipconfig);
@@ -6002,28 +7061,28 @@ static const struct connman_ipconfig_ops service_ops = {
 static void setup_ip4config(struct connman_service *service, int index,
                        enum connman_ipconfig_method method)
 {
-       service->ipconfig_ipv4 = connman_ipconfig_create(index,
+       service->ipconfig_ipv4 = __connman_ipconfig_create(index,
                                                CONNMAN_IPCONFIG_TYPE_IPV4);
        if (service->ipconfig_ipv4 == NULL)
                return;
 
-       connman_ipconfig_set_method(service->ipconfig_ipv4, method);
+       __connman_ipconfig_set_method(service->ipconfig_ipv4, method);
 
-       connman_ipconfig_set_data(service->ipconfig_ipv4, service);
+       __connman_ipconfig_set_data(service->ipconfig_ipv4, service);
 
-       connman_ipconfig_set_ops(service->ipconfig_ipv4, &service_ops);
+       __connman_ipconfig_set_ops(service->ipconfig_ipv4, &service_ops);
 }
 
 static void setup_ip6config(struct connman_service *service, int index)
 {
-       service->ipconfig_ipv6 = connman_ipconfig_create(index,
+       service->ipconfig_ipv6 = __connman_ipconfig_create(index,
                                                CONNMAN_IPCONFIG_TYPE_IPV6);
        if (service->ipconfig_ipv6 == NULL)
                return;
 
-       connman_ipconfig_set_data(service->ipconfig_ipv6, service);
+       __connman_ipconfig_set_data(service->ipconfig_ipv6, service);
 
-       connman_ipconfig_set_ops(service->ipconfig_ipv6, &service_ops);
+       __connman_ipconfig_set_ops(service->ipconfig_ipv6, &service_ops);
 }
 
 void __connman_service_read_ip4config(struct connman_service *service)
@@ -6086,18 +7145,20 @@ void __connman_service_create_ip6config(struct connman_service *service,
 }
 
 /**
- * __connman_service_lookup_from_network:
+ * connman_service_lookup_from_network:
  * @network: network structure
  *
  * Look up a service by network (reference count will not be increased)
  */
-struct connman_service *__connman_service_lookup_from_network(struct connman_network *network)
+struct connman_service *connman_service_lookup_from_network(struct connman_network *network)
 {
        struct connman_service *service;
        const char *ident, *group;
        char *name;
 
+#if !defined TIZEN_EXT
        DBG("network %p", network);
+#endif
 
        if (network == NULL)
                return NULL;
@@ -6128,11 +7189,11 @@ struct connman_service *__connman_service_lookup_from_index(int index)
        while (g_sequence_iter_is_end(iter) == FALSE) {
                service = g_sequence_get(iter);
 
-               if (connman_ipconfig_get_index(service->ipconfig_ipv4)
+               if (__connman_ipconfig_get_index(service->ipconfig_ipv4)
                                                        == index)
                        return service;
 
-               if (connman_ipconfig_get_index(service->ipconfig_ipv6)
+               if (__connman_ipconfig_get_index(service->ipconfig_ipv6)
                                                        == index)
                        return service;
 
@@ -6142,6 +7203,11 @@ struct connman_service *__connman_service_lookup_from_index(int index)
        return NULL;
 }
 
+struct connman_service *__connman_service_lookup_from_ident(const char *identifier)
+{
+       return lookup_by_identifier(identifier);
+}
+
 const char *__connman_service_get_ident(struct connman_service *service)
 {
        return service->identifier;
@@ -6154,34 +7220,72 @@ const char *__connman_service_get_path(struct connman_service *service)
 
 unsigned int __connman_service_get_order(struct connman_service *service)
 {
+#if !defined TIZEN_EXT
        GSequenceIter *iter;
+#endif
 
        if (service == NULL)
                return 0;
 
+#if defined TIZEN_EXT
+       if (service->type != CONNMAN_SERVICE_TYPE_CELLULAR)
+#endif
        if (service->favorite == FALSE) {
                service->order = 0;
                goto done;
        }
 
+#if defined TIZEN_EXT
+       if (service->localnetwork == TRUE)
+               service->order = 0;
+       else if (service->type == CONNMAN_SERVICE_TYPE_VPN &&
+                       service->do_split_routing == FALSE)
+               service->order = 10;
+       else if (service->type == CONNMAN_SERVICE_TYPE_WIFI)
+               service->order = 5;
+       else if (service->type == CONNMAN_SERVICE_TYPE_ETHERNET)
+               service->order = 4;
+       else if (service->type == CONNMAN_SERVICE_TYPE_BLUETOOTH)
+               service->order = 3;
+       else if (service->type == CONNMAN_SERVICE_TYPE_CELLULAR &&
+                       __connman_service_is_internet_profile(service) == TRUE)
+               service->order = 1;
+       else if (service->type == CONNMAN_SERVICE_TYPE_CELLULAR &&
+                       __connman_service_is_tethering_profile(service) == TRUE)
+               service->order = 0;
+       else if (service->type == CONNMAN_SERVICE_TYPE_CELLULAR)
+               service->order = 0;
+       else
+               service->order = 2;
+#else
        iter = g_hash_table_lookup(service_hash, service->identifier);
        if (iter != NULL) {
                if (g_sequence_iter_get_position(iter) == 0)
                        service->order = 1;
-               else if (service->type == CONNMAN_SERVICE_TYPE_VPN)
+               else if (service->type == CONNMAN_SERVICE_TYPE_VPN &&
+                               service->do_split_routing == FALSE)
                        service->order = 10;
-#if defined TIZEN_EXT
-               else if (service->type == CONNMAN_SERVICE_TYPE_WIFI)
-                       service->order = 2;
-#endif
                else
                        service->order = 0;
        }
+#endif
+
+       DBG("service %p name %s order %d split %d", service, service->name,
+               service->order, service->do_split_routing);
 
 done:
        return service->order;
 }
 
+void __connman_service_update_ordering(void)
+{
+       GSequenceIter *iter;
+
+       iter = g_sequence_get_begin_iter(service_list);
+       if (iter != NULL && g_sequence_get_length(service_list) > 1)
+               g_sequence_sort_changed(iter, service_compare, NULL);
+}
+
 static enum connman_service_type convert_network_type(struct connman_network *network)
 {
        enum connman_network_type type = connman_network_get_type(network);
@@ -6194,8 +7298,6 @@ static enum connman_service_type convert_network_type(struct connman_network *ne
                return CONNMAN_SERVICE_TYPE_ETHERNET;
        case CONNMAN_NETWORK_TYPE_WIFI:
                return CONNMAN_SERVICE_TYPE_WIFI;
-       case CONNMAN_NETWORK_TYPE_WIMAX:
-               return CONNMAN_SERVICE_TYPE_WIMAX;
        case CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN:
        case CONNMAN_NETWORK_TYPE_BLUETOOTH_DUN:
                return CONNMAN_SERVICE_TYPE_BLUETOOTH;
@@ -6270,17 +7372,20 @@ static void update_from_network(struct connman_service *service,
                service->wps = connman_network_get_bool(network, "WiFi.WPS");
 
        if (service->strength > strength && service->network != NULL) {
-               service->network = network;
+               connman_network_unref(service->network);
+               service->network = connman_network_ref(network);
 
                strength_changed(service);
        }
 
        if (service->network == NULL)
-               service->network = network;
+               service->network = connman_network_ref(network);
 
        iter = g_hash_table_lookup(service_hash, service->identifier);
-       if (iter != NULL)
+       if (iter != NULL && g_sequence_get_length(service_list) > 1) {
                g_sequence_sort_changed(iter, service_compare, NULL);
+               service_schedule_changed();
+       }
 }
 
 /**
@@ -6295,7 +7400,8 @@ struct connman_service * __connman_service_create_from_network(struct connman_ne
        struct connman_device *device;
        const char *ident, *group;
        char *name;
-       int index;
+       unsigned int *auto_connect_types;
+       int i, index;
 
        DBG("network %p", network);
 
@@ -6323,26 +7429,34 @@ struct connman_service * __connman_service_create_from_network(struct connman_ne
 
        if (service->path != NULL) {
                update_from_network(service, network);
-               services_changed(TRUE);
+               __connman_connection_update_gateway();
                return service;
        }
 
        service->type = convert_network_type(network);
 
+       auto_connect_types = connman_setting_get_uint_list("DefaultAutoConnectTechnologies");
+       service->autoconnect = FALSE;
+       for (i = 0; auto_connect_types != NULL &&
+                    auto_connect_types[i] != 0; i++) {
+               if (service->type == auto_connect_types[i]) {
+                       service->autoconnect = TRUE;
+                       break;
+               }
+       }
+
        switch (service->type) {
        case CONNMAN_SERVICE_TYPE_UNKNOWN:
        case CONNMAN_SERVICE_TYPE_SYSTEM:
-       case CONNMAN_SERVICE_TYPE_ETHERNET:
-       case CONNMAN_SERVICE_TYPE_WIMAX:
        case CONNMAN_SERVICE_TYPE_BLUETOOTH:
        case CONNMAN_SERVICE_TYPE_GPS:
        case CONNMAN_SERVICE_TYPE_VPN:
        case CONNMAN_SERVICE_TYPE_GADGET:
-               service->autoconnect = FALSE;
-               break;
        case CONNMAN_SERVICE_TYPE_WIFI:
        case CONNMAN_SERVICE_TYPE_CELLULAR:
-               service->autoconnect = TRUE;
+               break;
+       case CONNMAN_SERVICE_TYPE_ETHERNET:
+               service->favorite = TRUE;
                break;
        }
 
@@ -6365,9 +7479,23 @@ struct connman_service * __connman_service_create_from_network(struct connman_ne
                device = connman_network_get_device(service->network);
                if (device && connman_device_get_scanning(device) == FALSE)
                        __connman_service_auto_connect();
+
+#if defined TIZEN_EXT
+               /* TIZEN synchronizes below information when the service creates */
+               if (service->eap != NULL)
+                       connman_network_set_string(service->network, "WiFi.EAP",
+                                                               service->eap);
+               if (service->identity != NULL)
+                       connman_network_set_string(service->network, "WiFi.Identity",
+                                                               service->identity);
+               if (service->phase2 != NULL)
+                       connman_network_set_string(service->network, "WiFi.Phase2",
+                                                               service->phase2);
+#endif
        }
 
        __connman_notifier_service_add(service, service->name);
+       service_schedule_added(service);
 
        return service;
 }
@@ -6384,7 +7512,7 @@ void __connman_service_update_from_network(struct connman_network *network)
 
        DBG("network %p", network);
 
-       service = __connman_service_lookup_from_network(network);
+       service = connman_service_lookup_from_network(network);
        if (service == NULL)
                return;
 
@@ -6395,9 +7523,11 @@ void __connman_service_update_from_network(struct connman_network *network)
        if (g_strcmp0(service->name, name) != 0) {
                g_free(service->name);
                service->name = g_strdup(name);
-               connman_dbus_property_changed_basic(service->path,
-                               CONNMAN_SERVICE_INTERFACE, "Name",
-                               DBUS_TYPE_STRING, &service->name);
+
+               if (allow_property_changed(service) == TRUE)
+                       connman_dbus_property_changed_basic(service->path,
+                                       CONNMAN_SERVICE_INTERFACE, "Name",
+                                       DBUS_TYPE_STRING, &service->name);
        }
 
        if (service->type == CONNMAN_SERVICE_TYPE_WIFI)
@@ -6407,6 +7537,18 @@ void __connman_service_update_from_network(struct connman_network *network)
        if (strength == service->strength)
                goto roaming;
 
+#if defined TIZEN_EXT
+       /* Enable auto connect, when the signal strength be excellent */
+       if (service->type == CONNMAN_SERVICE_TYPE_WIFI &&
+                       strength >= 57 && service->strength < 57 &&
+                       service->favorite == TRUE &&
+                       service->state == CONNMAN_SERVICE_STATE_FAILURE) {
+               DBG("Signal strength changed excellent and recover state");
+
+               service->state = CONNMAN_SERVICE_STATE_IDLE;
+       }
+#endif
+
        service->strength = strength;
        need_sort = TRUE;
 
@@ -6432,8 +7574,10 @@ roaming:
 sorting:
        if (need_sort == TRUE) {
                iter = g_hash_table_lookup(service_hash, service->identifier);
-               if (iter != NULL)
+               if (iter != NULL && g_sequence_get_length(service_list) > 1) {
                        g_sequence_sort_changed(iter, service_compare, NULL);
+                       service_schedule_changed();
+               }
        }
 }
 
@@ -6443,23 +7587,16 @@ void __connman_service_remove_from_network(struct connman_network *network)
 
        DBG("network %p", network);
 
-       service = __connman_service_lookup_from_network(network);
+       service = connman_service_lookup_from_network(network);
        if (service == NULL)
                return;
 
+       service->ignore = TRUE;
+
        __connman_connection_gateway_remove(service,
                                        CONNMAN_IPCONFIG_TYPE_ALL);
-#if defined TIZEN_EXT
-       __connman_service_ipconfig_indicate_state(service,
-                                       CONNMAN_SERVICE_STATE_DISCONNECT,
-                                       CONNMAN_IPCONFIG_TYPE_IPV4);
 
-       __connman_service_ipconfig_indicate_state(service,
-                               CONNMAN_SERVICE_STATE_DISCONNECT,
-                               CONNMAN_IPCONFIG_TYPE_IPV6);
-#endif
-
-       __connman_service_put(service);
+       connman_service_unref(service);
 }
 
 /**
@@ -6495,6 +7632,7 @@ __connman_service_create_from_provider(struct connman_provider *provider)
        service->userconnect = TRUE;
 
        service->state_ipv4 = service->state_ipv6 = CONNMAN_SERVICE_STATE_IDLE;
+       service->state = combine_state(service->state_ipv4, service->state_ipv6);
 
        str = connman_provider_get_string(provider, "Name");
        if (str != NULL) {
@@ -6518,10 +7656,71 @@ __connman_service_create_from_provider(struct connman_provider *provider)
        service_register(service);
 
        __connman_notifier_service_add(service, service->name);
+       service_schedule_added(service);
 
        return service;
 }
 
+static void remove_unprovisioned_services(void)
+{
+       gchar **services;
+       GKeyFile *keyfile, *configkeyfile;
+       char *file, *section;
+       int i = 0;
+
+       services = connman_storage_get_services();
+       if (services == NULL)
+               return;
+
+       for (;services[i] != NULL; i++) {
+               file = section = NULL;
+               keyfile = configkeyfile = NULL;
+
+               keyfile = connman_storage_load_service(services[i]);
+               if (keyfile == NULL)
+                       continue;
+
+               file = g_key_file_get_string(keyfile, services[i],
+                                       "Config.file", NULL);
+               if (file == NULL)
+                       goto next;
+
+               section = g_key_file_get_string(keyfile, services[i],
+                                       "Config.ident", NULL);
+               if (section == NULL)
+                       goto next;
+
+               configkeyfile = __connman_storage_load_config(file);
+               if (configkeyfile == NULL) {
+                       /*
+                        * Config file is missing, remove the provisioned
+                        * service.
+                        */
+                       __connman_storage_remove_service(services[i]);
+                       goto next;
+               }
+
+               if (g_key_file_has_group(configkeyfile, section) == FALSE)
+                       /*
+                        * Config section is missing, remove the provisioned
+                        * service.
+                        */
+                       __connman_storage_remove_service(services[i]);
+
+       next:
+               if (keyfile != NULL)
+                       g_key_file_free(keyfile);
+
+               if (configkeyfile != NULL)
+                       g_key_file_free(configkeyfile);
+
+               g_free(section);
+               g_free(file);
+       }
+
+       g_strfreev(services);
+}
+
 int __connman_service_init(void)
 {
        DBG("");
@@ -6533,6 +7732,13 @@ int __connman_service_init(void)
 
        service_list = g_sequence_new(service_free);
 
+       services_notify = g_new0(struct _services_notify, 1);
+       services_notify->remove = g_hash_table_new_full(g_str_hash,
+                       g_str_equal, g_free, NULL);
+       services_notify->add = g_hash_table_new(g_str_hash, g_str_equal);
+
+       remove_unprovisioned_services();
+
        return 0;
 }
 
@@ -6542,6 +7748,11 @@ void __connman_service_cleanup(void)
 
        DBG("");
 
+       if (autoconnect_timeout != 0) {
+               g_source_remove(autoconnect_timeout);
+               autoconnect_timeout = 0;
+       }
+
        list = service_list;
        service_list = NULL;
        g_sequence_free(list);
@@ -6552,5 +7763,13 @@ void __connman_service_cleanup(void)
        g_slist_free(counter_list);
        counter_list = NULL;
 
+       if (services_notify->id != 0) {
+               g_source_remove(services_notify->id);
+               service_send_changed(NULL);
+               g_hash_table_destroy(services_notify->remove);
+               g_hash_table_destroy(services_notify->add);
+       }
+       g_free(services_notify);
+
        dbus_connection_unref(connection);
 }
index 83cdaf0..6d8dda7 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *  Copyright (C) 2011  BWM CarIT GmbH. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
@@ -53,6 +53,18 @@ enum connman_session_reason {
        CONNMAN_SESSION_REASON_PERIODIC         = 4,
 };
 
+enum connman_session_state {
+       CONNMAN_SESSION_STATE_DISCONNECTED   = 0,
+       CONNMAN_SESSION_STATE_CONNECTED      = 1,
+       CONNMAN_SESSION_STATE_ONLINE         = 2,
+};
+
+enum connman_session_type {
+       CONNMAN_SESSION_TYPE_ANY      = 0,
+       CONNMAN_SESSION_TYPE_LOCAL    = 1,
+       CONNMAN_SESSION_TYPE_INTERNET = 2,
+};
+
 enum connman_session_roaming_policy {
        CONNMAN_SESSION_ROAMING_POLICY_UNKNOWN          = 0,
        CONNMAN_SESSION_ROAMING_POLICY_DEFAULT          = 1,
@@ -74,7 +86,8 @@ struct service_entry {
 };
 
 struct session_info {
-       connman_bool_t online;
+       enum connman_session_state state;
+       enum connman_session_type type;
        connman_bool_t priority;
        GSList *allowed_bearers;
        connman_bool_t avoid_handover;
@@ -96,7 +109,6 @@ struct connman_session {
        guint notify_watch;
 
        connman_bool_t append_all;
-       connman_bool_t info_dirty;
        struct session_info *info;
        struct session_info *info_last;
 
@@ -150,6 +162,44 @@ static const char *reason2string(enum connman_session_reason reason)
        return NULL;
 }
 
+static const char *state2string(enum connman_session_state state)
+{
+       switch (state) {
+       case CONNMAN_SESSION_STATE_DISCONNECTED:
+               return "disconnected";
+       case CONNMAN_SESSION_STATE_CONNECTED:
+               return "connected";
+       case CONNMAN_SESSION_STATE_ONLINE:
+               return "online";
+       }
+
+       return NULL;
+}
+
+static const char *type2string(enum connman_session_type type)
+{
+       switch (type) {
+       case CONNMAN_SESSION_TYPE_ANY:
+               return "";
+       case CONNMAN_SESSION_TYPE_LOCAL:
+               return "local";
+       case CONNMAN_SESSION_TYPE_INTERNET:
+               return "internet";
+       }
+
+       return NULL;
+}
+
+static enum connman_session_type string2type(const char *type)
+{
+       if (g_strcmp0(type, "local") == 0)
+               return CONNMAN_SESSION_TYPE_LOCAL;
+       else if (g_strcmp0(type, "internet") == 0)
+               return CONNMAN_SESSION_TYPE_INTERNET;
+
+       return CONNMAN_SESSION_TYPE_ANY;
+}
+
 static const char *roamingpolicy2string(enum connman_session_roaming_policy policy)
 {
        switch (policy) {
@@ -195,8 +245,6 @@ static enum connman_service_type bearer2service(const char *bearer)
                return CONNMAN_SERVICE_TYPE_ETHERNET;
        else if (g_strcmp0(bearer, "wifi") == 0)
                return CONNMAN_SERVICE_TYPE_WIFI;
-       else if (g_strcmp0(bearer, "wimax") == 0)
-               return CONNMAN_SERVICE_TYPE_WIMAX;
        else if (g_strcmp0(bearer, "bluetooth") == 0)
                return CONNMAN_SERVICE_TYPE_BLUETOOTH;
        else if (g_strcmp0(bearer, "cellular") == 0)
@@ -214,8 +262,6 @@ static char *service2bearer(enum connman_service_type type)
                return "ethernet";
        case CONNMAN_SERVICE_TYPE_WIFI:
                return "wifi";
-       case CONNMAN_SERVICE_TYPE_WIMAX:
-               return "wimax";
        case CONNMAN_SERVICE_TYPE_BLUETOOTH:
                return "bluetooth";
        case CONNMAN_SERVICE_TYPE_CELLULAR:
@@ -271,7 +317,7 @@ static GSList *session_parse_allowed_bearers(DBusMessageIter *iter)
                        info->match_all = FALSE;
                }
 
-               list = g_slist_append(list, info);
+               list = g_slist_prepend(list, info);
 
                dbus_message_iter_next(&array);
        }
@@ -322,6 +368,11 @@ static void append_ipconfig_ipv4(DBusMessageIter *iter, void *user_data)
        if (service == NULL)
                return;
 
+       if (__connman_service_is_connected_state(service,
+                               CONNMAN_IPCONFIG_TYPE_IPV4) == FALSE) {
+               return;
+       }
+
        ipconfig_ipv4 = __connman_service_get_ip4config(service);
        if (ipconfig_ipv4 == NULL)
                return;
@@ -337,6 +388,11 @@ static void append_ipconfig_ipv6(DBusMessageIter *iter, void *user_data)
        if (service == NULL)
                return;
 
+       if (__connman_service_is_connected_state(service,
+                               CONNMAN_IPCONFIG_TYPE_IPV6) == FALSE) {
+               return;
+       }
+
        ipconfig_ipv4 = __connman_service_get_ip4config(service);
        ipconfig_ipv6 = __connman_service_get_ip6config(service);
        if (ipconfig_ipv6 == NULL)
@@ -355,11 +411,13 @@ static void append_notify(DBusMessageIter *dict,
        const char *name, *ifname, *bearer;
 
        if (session->append_all == TRUE ||
-                       info->online != info_last->online) {
-               connman_dbus_dict_append_basic(dict, "Online",
-                                               DBUS_TYPE_BOOLEAN,
-                                               &info->online);
-               info_last->online = info->online;
+                       info->state != info_last->state) {
+               const char *state = state2string(info->state);
+
+               connman_dbus_dict_append_basic(dict, "State",
+                                               DBUS_TYPE_STRING,
+                                               &state);
+               info_last->state = info->state;
        }
 
        if (session->append_all == TRUE ||
@@ -399,6 +457,14 @@ static void append_notify(DBusMessageIter *dict,
                info_last->entry = info->entry;
        }
 
+       if (session->append_all == TRUE || info->type != info_last->type) {
+               const char *type = type2string(info->type);
+
+               connman_dbus_dict_append_basic(dict, "ConnectionType",
+                                               DBUS_TYPE_STRING,
+                                               &type);
+               info_last->type = info->type;
+       }
 
        if (session->append_all == TRUE ||
                        info->priority != info_last->priority) {
@@ -475,7 +541,58 @@ static void append_notify(DBusMessageIter *dict,
        }
 
        session->append_all = FALSE;
-       session->info_dirty = FALSE;
+}
+
+static connman_bool_t is_type_matching_state(enum connman_session_state *state,
+                                               enum connman_session_type type)
+{
+       switch (type) {
+       case CONNMAN_SESSION_TYPE_ANY:
+               return TRUE;
+       case CONNMAN_SESSION_TYPE_LOCAL:
+               if (*state >= CONNMAN_SESSION_STATE_CONNECTED) {
+                       *state = CONNMAN_SESSION_STATE_CONNECTED;
+                       return TRUE;
+               }
+
+               break;
+       case CONNMAN_SESSION_TYPE_INTERNET:
+               if (*state == CONNMAN_SESSION_STATE_ONLINE)
+                       return TRUE;
+               break;
+       }
+
+       return FALSE;
+}
+
+static connman_bool_t compute_notifiable_changes(struct connman_session *session)
+{
+       struct session_info *info_last = session->info_last;
+       struct session_info *info = session->info;
+
+       if (session->append_all == TRUE)
+               return TRUE;
+
+       if (info->state != info_last->state)
+               return TRUE;
+
+       if (info->entry != info_last->entry &&
+                       info->state >= CONNMAN_SESSION_STATE_CONNECTED)
+               return TRUE;
+
+       if (info->periodic_connect != info_last->periodic_connect ||
+                       info->allowed_bearers != info_last->allowed_bearers ||
+                       info->avoid_handover != info_last->avoid_handover ||
+                       info->stay_connected != info_last->stay_connected ||
+                       info->roaming_policy != info_last->roaming_policy ||
+                       info->idle_timeout != info_last->idle_timeout ||
+                       info->priority != info_last->priority ||
+                       info->marker != info_last->marker ||
+                       info->ecall != info_last->ecall ||
+                       info->type != info_last->type)
+               return TRUE;
+
+       return FALSE;
 }
 
 static gboolean session_notify(gpointer user_data)
@@ -484,8 +601,8 @@ static gboolean session_notify(gpointer user_data)
        DBusMessage *msg;
        DBusMessageIter array, dict;
 
-       if (session->info_dirty == FALSE)
-               return 0;
+       if (compute_notifiable_changes(session) == FALSE)
+               return FALSE;
 
        DBG("session %p owner %s notify_path %s", session,
                session->owner, session->notify_path);
@@ -566,7 +683,7 @@ static int service_type_weight(enum connman_service_type type)
         *
         * 1. Ethernet
         * 2. Bluetooth
-        * 3. WiFi/WiMAX
+        * 3. WiFi
         * 4. Cellular
         */
 
@@ -576,7 +693,6 @@ static int service_type_weight(enum connman_service_type type)
        case CONNMAN_SERVICE_TYPE_BLUETOOTH:
                return 3;
        case CONNMAN_SERVICE_TYPE_WIFI:
-       case CONNMAN_SERVICE_TYPE_WIMAX:
                return 2;
        case CONNMAN_SERVICE_TYPE_CELLULAR:
                return 1;
@@ -678,7 +794,7 @@ static void cleanup_session(gpointer user_data)
        g_free(session);
 }
 
-static connman_bool_t is_online(enum connman_service_state state)
+static enum connman_session_state service_to_session_state(enum connman_service_state state)
 {
        switch (state) {
        case CONNMAN_SERVICE_STATE_UNKNOWN:
@@ -687,8 +803,27 @@ static connman_bool_t is_online(enum connman_service_state state)
        case CONNMAN_SERVICE_STATE_CONFIGURATION:
        case CONNMAN_SERVICE_STATE_DISCONNECT:
        case CONNMAN_SERVICE_STATE_FAILURE:
+               break;
        case CONNMAN_SERVICE_STATE_READY:
+               return CONNMAN_SESSION_STATE_CONNECTED;
+       case CONNMAN_SERVICE_STATE_ONLINE:
+               return CONNMAN_SESSION_STATE_ONLINE;
+       }
+
+       return CONNMAN_SESSION_STATE_DISCONNECTED;
+}
+
+static connman_bool_t is_connected(enum connman_service_state state)
+{
+       switch (state) {
+       case CONNMAN_SERVICE_STATE_UNKNOWN:
+       case CONNMAN_SERVICE_STATE_IDLE:
+       case CONNMAN_SERVICE_STATE_ASSOCIATION:
+       case CONNMAN_SERVICE_STATE_CONFIGURATION:
+       case CONNMAN_SERVICE_STATE_DISCONNECT:
+       case CONNMAN_SERVICE_STATE_FAILURE:
                break;
+       case CONNMAN_SERVICE_STATE_READY:
        case CONNMAN_SERVICE_STATE_ONLINE:
                return TRUE;
        }
@@ -704,10 +839,10 @@ static connman_bool_t is_connecting(enum connman_service_state state)
                break;
        case CONNMAN_SERVICE_STATE_ASSOCIATION:
        case CONNMAN_SERVICE_STATE_CONFIGURATION:
-       case CONNMAN_SERVICE_STATE_READY:
                return TRUE;
        case CONNMAN_SERVICE_STATE_DISCONNECT:
        case CONNMAN_SERVICE_STATE_FAILURE:
+       case CONNMAN_SERVICE_STATE_READY:
        case CONNMAN_SERVICE_STATE_ONLINE:
                break;
        }
@@ -847,33 +982,31 @@ static gboolean call_connect(gpointer user_data)
        return FALSE;
 }
 
-static connman_bool_t deselect_service(struct session_info *info)
+static void deselect_service(struct session_info *info)
 {
        struct service_entry *entry;
-       connman_bool_t disconnect, online;
+       connman_bool_t disconnect, connected;
 
        DBG("");
 
        if (info->entry == NULL)
-               return FALSE;
+               return;
 
        disconnect = explicit_disconnect(info);
 
-       online = is_connecting(info->entry->state) == TRUE ||
-                       is_online(info->entry->state) == TRUE;
+       connected = is_connecting(info->entry->state) == TRUE ||
+                       is_connected(info->entry->state) == TRUE;
 
-       info->online = FALSE;
+       info->state = CONNMAN_SESSION_STATE_DISCONNECTED;
        info->entry->reason = CONNMAN_SESSION_REASON_UNKNOWN;
 
        entry = info->entry;
        info->entry = NULL;
 
-       DBG("disconnect %d online %d", disconnect, online);
+       DBG("disconnect %d connected %d", disconnect, connected);
 
-       if (disconnect == TRUE && online == TRUE)
+       if (disconnect == TRUE && connected == TRUE)
                pending_timeout_add(0, call_disconnect, entry);
-
-       return TRUE;
 }
 
 static void deselect_and_disconnect(struct connman_session *session,
@@ -881,53 +1014,55 @@ static void deselect_and_disconnect(struct connman_session *session,
 {
        struct session_info *info = session->info;
 
-       session->info_dirty |= deselect_service(info);
+       deselect_service(info);
 
        info->reason = reason;
 }
 
-static connman_bool_t select_online_service(struct session_info *info,
+static void select_connected_service(struct session_info *info,
                                        struct service_entry *entry)
 {
-       info->online = TRUE;
+       enum connman_session_state state;
+
+       state = service_to_session_state(entry->state);
+       if (is_type_matching_state(&state, info->type) == FALSE)
+               return;
+
+       info->state = state;
 
        info->entry = entry;
        info->entry->reason = info->reason;
 
        if (explicit_connect(info->reason) == FALSE)
-               return TRUE;
+               return;
 
        __connman_service_session_inc(info->entry->service);
-
-       return TRUE;
 }
 
-static connman_bool_t select_offline_service(struct session_info *info,
+static void select_offline_service(struct session_info *info,
                                        struct service_entry *entry)
 {
        if (explicit_connect(info->reason) == FALSE)
-               return FALSE;
+               return;
 
-       info->online = FALSE;
+       info->state = service_to_session_state(entry->state);
 
        info->entry = entry;
        info->entry->reason = info->reason;
 
        __connman_service_session_inc(info->entry->service);
        pending_timeout_add(0, call_connect, entry);
-
-       return TRUE;
 }
 
-static connman_bool_t select_service(struct session_info *info,
+static void select_service(struct session_info *info,
                                struct service_entry *entry)
 {
        DBG("service %p", entry->service);
 
-       if (is_online(entry->state) == TRUE)
-               return select_online_service(info, entry);
+       if (is_connected(entry->state) == TRUE)
+               select_connected_service(info, entry);
        else
-               return select_offline_service(info, entry);
+               select_offline_service(info, entry);
 }
 
 static void select_and_connect(struct connman_session *session,
@@ -953,8 +1088,7 @@ static void select_and_connect(struct connman_session *session,
                case CONNMAN_SERVICE_STATE_ONLINE:
                case CONNMAN_SERVICE_STATE_IDLE:
                case CONNMAN_SERVICE_STATE_DISCONNECT:
-                       session->info_dirty |=
-                               select_service(info, entry);
+                       select_service(info, entry);
                        return;
                case CONNMAN_SERVICE_STATE_UNKNOWN:
                case CONNMAN_SERVICE_STATE_FAILURE:
@@ -1055,9 +1189,12 @@ static void session_changed(struct connman_session *session,
                                                reason2string(info->reason));
 
        if (info->entry != NULL) {
-               info->online = is_online(info->entry->state);
-               if (info_last->online != info->online)
-                       session->info_dirty = TRUE;
+               enum connman_session_state state;
+
+               state = service_to_session_state(info->entry->state);
+
+               if (is_type_matching_state(&state, info->type) == TRUE)
+                       info->state = state;
        }
 
        switch (trigger) {
@@ -1093,14 +1230,21 @@ static void session_changed(struct connman_session *session,
                        g_sequence_free(service_list_last);
                }
 
-               if (info->online == FALSE) {
+               if (info->type != info_last->type) {
+                       if (info->state >= CONNMAN_SESSION_STATE_CONNECTED &&
+                                       is_type_matching_state(&info->state,
+                                                       info->type) == FALSE)
+                               deselect_and_disconnect(session, info->reason);
+               }
+
+               if (info->state == CONNMAN_SESSION_STATE_DISCONNECTED) {
                        select_and_connect(session,
                                        CONNMAN_SESSION_REASON_FREE_RIDE);
                }
 
                break;
        case CONNMAN_SESSION_TRIGGER_CONNECT:
-               if (info->online == TRUE) {
+               if (info->state >= CONNMAN_SESSION_STATE_CONNECTED) {
                        if (info->entry->reason == CONNMAN_SESSION_REASON_CONNECT)
                                break;
                        info->entry->reason = CONNMAN_SESSION_REASON_CONNECT;
@@ -1123,7 +1267,7 @@ static void session_changed(struct connman_session *session,
 
                break;
        case CONNMAN_SESSION_TRIGGER_PERIODIC:
-               if (info->online == TRUE) {
+               if (info->state >= CONNMAN_SESSION_STATE_CONNECTED) {
                        info->entry->reason = CONNMAN_SESSION_REASON_PERIODIC;
                        __connman_service_session_inc(info->entry->service);
                        break;
@@ -1135,8 +1279,8 @@ static void session_changed(struct connman_session *session,
                break;
        case CONNMAN_SESSION_TRIGGER_SERVICE:
                if (info->entry != NULL &&
-                               (is_connecting(info->entry->state) == TRUE ||
-                                       is_online(info->entry->state) == TRUE)) {
+                       (is_connecting(info->entry->state) == TRUE ||
+                               is_connected(info->entry->state) == TRUE)) {
                        break;
                }
 
@@ -1149,7 +1293,8 @@ static void session_changed(struct connman_session *session,
 
                break;
        case CONNMAN_SESSION_TRIGGER_ECALL:
-               if (info->online == FALSE && info->entry != NULL &&
+               if (info->state == CONNMAN_SESSION_STATE_DISCONNECTED &&
+                               info->entry != NULL &&
                                info->entry->service != NULL) {
                        deselect_and_disconnect(session, info->reason);
                }
@@ -1208,7 +1353,6 @@ static void update_ecall_sessions(struct connman_session *session)
                        continue;
 
                session_iter->info->ecall = info->ecall;
-               session_iter->info_dirty = TRUE;
 
                session_changed(session_iter, CONNMAN_SESSION_TRIGGER_ECALL);
        }
@@ -1238,7 +1382,6 @@ static void update_ecall(struct connman_session *session)
 
        update_ecall_sessions(session);
 
-       session->info_dirty = TRUE;
        return;
 
 err:
@@ -1251,9 +1394,9 @@ static DBusMessage *change_session(DBusConnection *conn,
 {
        struct connman_session *session = user_data;
        struct session_info *info = session->info;
-       struct session_info *info_last = session->info_last;
        DBusMessageIter iter, value;
        const char *name;
+       const char *val;
        GSList *allowed_bearers;
 
        DBG("session %p", session);
@@ -1281,8 +1424,6 @@ static DBusMessage *change_session(DBusConnection *conn,
                        }
 
                        info->allowed_bearers = allowed_bearers;
-
-                       session->info_dirty = TRUE;
                } else {
                        goto err;
                }
@@ -1291,21 +1432,12 @@ static DBusMessage *change_session(DBusConnection *conn,
                if (g_str_equal(name, "Priority") == TRUE) {
                        dbus_message_iter_get_basic(&value,
                                        &info->priority);
-
-                       if (info_last->priority != info->priority)
-                               session->info_dirty = TRUE;
                } else if (g_str_equal(name, "AvoidHandover") == TRUE) {
                        dbus_message_iter_get_basic(&value,
                                        &info->avoid_handover);
-
-                       if (info_last->avoid_handover != info->avoid_handover)
-                               session->info_dirty = TRUE;
                } else if (g_str_equal(name, "StayConnected") == TRUE) {
                        dbus_message_iter_get_basic(&value,
                                        &info->stay_connected);
-
-                       if (info_last->stay_connected != info->stay_connected)
-                               session->info_dirty = TRUE;
                } else if (g_str_equal(name, "EmergencyCall") == TRUE) {
                        dbus_message_iter_get_basic(&value,
                                        &info->ecall);
@@ -1319,28 +1451,21 @@ static DBusMessage *change_session(DBusConnection *conn,
                if (g_str_equal(name, "PeriodicConnect") == TRUE) {
                        dbus_message_iter_get_basic(&value,
                                        &info->periodic_connect);
-
-                       if (info_last->periodic_connect != info->periodic_connect)
-                               session->info_dirty = TRUE;
                } else if (g_str_equal(name, "IdleTimeout") == TRUE) {
                        dbus_message_iter_get_basic(&value,
                                        &info->idle_timeout);
-
-                       if (info_last->idle_timeout != info->idle_timeout)
-                               session->info_dirty = TRUE;
                } else {
                        goto err;
                }
                break;
        case DBUS_TYPE_STRING:
-               if (g_str_equal(name, "RoamingPolicy") == TRUE) {
-                       const char *val;
+               if (g_str_equal(name, "ConnectionType") == TRUE) {
+                       dbus_message_iter_get_basic(&value, &val);
+                       info->type = string2type(val);
+               } else if (g_str_equal(name, "RoamingPolicy") == TRUE) {
                        dbus_message_iter_get_basic(&value, &val);
                        info->roaming_policy =
                                        string2roamingpolicy(val);
-
-                       if (info_last->roaming_policy != info->roaming_policy)
-                               session->info_dirty = TRUE;
                } else {
                        goto err;
                }
@@ -1349,8 +1474,7 @@ static DBusMessage *change_session(DBusConnection *conn,
                goto err;
        }
 
-       if (session->info_dirty == TRUE)
-               session_changed(session, CONNMAN_SESSION_TRIGGER_SETTING);
+       session_changed(session, CONNMAN_SESSION_TRIGGER_SETTING);
 
        return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
 
@@ -1426,11 +1550,14 @@ static DBusMessage *destroy_session(DBusConnection *conn,
        return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
 }
 
-static GDBusMethodTable session_methods[] = {
-       { "Destroy",    "",   "", destroy_session    },
-       { "Connect",    "",   "", connect_session    },
-       { "Disconnect", "",   "", disconnect_session },
-       { "Change",     "sv", "", change_session     },
+static const GDBusMethodTable session_methods[] = {
+       { GDBUS_METHOD("Destroy", NULL, NULL, destroy_session) },
+       { GDBUS_METHOD("Connect", NULL, NULL, connect_session) },
+       { GDBUS_METHOD("Disconnect", NULL, NULL,
+                       disconnect_session ) },
+       { GDBUS_METHOD("Change",
+                       GDBUS_ARGS({ "name", "s" }, { "value", "v" }),
+                       NULL, change_session) },
        { },
 };
 
@@ -1442,6 +1569,7 @@ int __connman_session_create(DBusMessage *msg)
        struct connman_session *session = NULL;
        struct session_info *info, *info_last;
 
+       enum connman_session_type type = CONNMAN_SESSION_TYPE_ANY;
        connman_bool_t priority = FALSE, avoid_handover = FALSE;
        connman_bool_t stay_connected = FALSE, ecall = FALSE;
        enum connman_session_roaming_policy roaming_policy =
@@ -1516,7 +1644,10 @@ int __connman_session_create(DBusMessage *msg)
                        }
                        break;
                case DBUS_TYPE_STRING:
-                       if (g_str_equal(key, "RoamingPolicy") == TRUE) {
+                       if (g_str_equal(key, "ConnectionType") == TRUE) {
+                               dbus_message_iter_get_basic(&value, &val);
+                               type = string2type(val);
+                       } else if (g_str_equal(key, "RoamingPolicy") == TRUE) {
                                dbus_message_iter_get_basic(&value, &val);
                                roaming_policy = string2roamingpolicy(val);
                        } else {
@@ -1575,7 +1706,8 @@ int __connman_session_create(DBusMessage *msg)
                g_dbus_add_disconnect_watch(connection, session->owner,
                                        owner_disconnect, session, NULL);
 
-       info->online = FALSE;
+       info->state = CONNMAN_SESSION_STATE_DISCONNECTED;
+       info->type = type;
        info->priority = priority;
        info->avoid_handover = avoid_handover;
        info->stay_connected = stay_connected;
@@ -1625,7 +1757,7 @@ int __connman_session_create(DBusMessage *msg)
                update_ecall_sessions(session);
        }
 
-       info_last->online = info->online;
+       info_last->state = info->state;
        info_last->priority = info->priority;
        info_last->avoid_handover = info->avoid_handover;
        info_last->stay_connected = info->stay_connected;
@@ -1637,7 +1769,6 @@ int __connman_session_create(DBusMessage *msg)
        info_last->marker = info->marker;
        info_last->allowed_bearers = info->allowed_bearers;
 
-       session->info_dirty = TRUE;
        session->append_all = TRUE;
 
        session_changed(session, CONNMAN_SESSION_TRIGGER_SETTING);
@@ -1698,7 +1829,13 @@ void __connman_session_set_mode(connman_bool_t enable)
 {
        DBG("enable %d", enable);
 
-       sessionmode = enable;
+       if (sessionmode != enable) {
+               sessionmode = enable;
+
+               connman_dbus_property_changed_basic(CONNMAN_MANAGER_PATH,
+                               CONNMAN_MANAGER_INTERFACE, "SessionMode",
+                               DBUS_TYPE_BOOLEAN, &sessionmode);
+       }
 
        if (sessionmode == TRUE)
                __connman_service_disconnect_all();
@@ -1815,6 +1952,9 @@ static void ipconfig_changed(struct connman_service *service,
                session = value;
                info = session->info;
 
+               if (info->state == CONNMAN_SESSION_STATE_DISCONNECTED)
+                       continue;
+
                if (info->entry != NULL && info->entry->service == service) {
                        if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
                                ipconfig_ipv4_changed(session);
index 828c1ef..0393f40 100644 (file)
@@ -125,7 +125,7 @@ struct stats_iter {
        struct stats_record *it;
 };
 
-GHashTable *stats_hash = NULL;
+static GHashTable *stats_hash = NULL;
 
 static struct stats_file_header *get_hdr(struct stats_file *file)
 {
@@ -692,7 +692,7 @@ int __connman_stats_service_register(struct connman_service *service)
 
        /* If the dir doesn't exist, create it */
        if (!g_file_test(dir, G_FILE_TEST_IS_DIR)) {
-               if(mkdir(dir, MODE) < 0) {
+               if (mkdir(dir, MODE) < 0) {
                        if (errno != EEXIST) {
                                g_free(dir);
 
index 51404c8..b4babee 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
@@ -58,20 +58,24 @@ static GKeyFile *storage_load(const char *pathname)
        return keyfile;
 }
 
-static void storage_save(GKeyFile *keyfile, char *pathname)
+static int storage_save(GKeyFile *keyfile, char *pathname)
 {
        gchar *data = NULL;
        gsize length = 0;
        GError *error = NULL;
+       int ret = 0;
 
        data = g_key_file_to_data(keyfile, &length, NULL);
 
        if (!g_file_set_contents(pathname, data, length, &error)) {
                DBG("Failed to store information: %s", error->message);
-               g_free(error);
+               g_error_free(error);
+               ret = -EIO;
        }
 
        g_free(data);
+
+       return ret;
 }
 
 static void storage_delete(const char *pathname)
@@ -82,7 +86,7 @@ static void storage_delete(const char *pathname)
                connman_error("Failed to remove %s", pathname);
 }
 
-GKeyFile *__connman_storage_load_global()
+GKeyFile *__connman_storage_load_global(void)
 {
        gchar *pathname;
        GKeyFile *keyfile = NULL;
@@ -98,20 +102,23 @@ GKeyFile *__connman_storage_load_global()
        return keyfile;
 }
 
-void __connman_storage_save_global(GKeyFile *keyfile)
+int __connman_storage_save_global(GKeyFile *keyfile)
 {
        gchar *pathname;
+       int ret;
 
        pathname = g_strdup_printf("%s/%s", STORAGEDIR, SETTINGS);
        if(pathname == NULL)
-               return;
+               return -ENOMEM;
 
-       storage_save(keyfile, pathname);
+       ret = storage_save(keyfile, pathname);
 
        g_free(pathname);
+
+       return ret;
 }
 
-void __connman_storage_delete_global()
+void __connman_storage_delete_global(void)
 {
        gchar *pathname;
 
@@ -140,30 +147,6 @@ GKeyFile *__connman_storage_load_config(const char *ident)
        return keyfile;
 }
 
-void __connman_storage_save_config(GKeyFile *keyfile, const char *ident)
-{
-       gchar *pathname;
-
-       pathname = g_strdup_printf("%s/%s.config", STORAGEDIR, ident);
-       if(pathname == NULL)
-               return;
-
-       storage_save(keyfile, pathname);
-}
-
-void __connman_storage_delete_config(const char *ident)
-{
-       gchar *pathname;
-
-       pathname = g_strdup_printf("%s/%s.config", STORAGEDIR, ident);
-       if(pathname == NULL)
-               return;
-
-       storage_delete(pathname);
-
-       g_free(pathname);
-}
-
 GKeyFile *__connman_storage_open_service(const char *service_id)
 {
        gchar *pathname;
@@ -186,7 +169,7 @@ GKeyFile *__connman_storage_open_service(const char *service_id)
        return keyfile;
 }
 
-gchar **connman_storage_get_services()
+gchar **connman_storage_get_services(void)
 {
        struct dirent *d;
        gchar *str;
@@ -210,6 +193,7 @@ gchar **connman_storage_get_services()
 
                switch (d->d_type) {
                case DT_DIR:
+               case DT_UNKNOWN:
                        /*
                         * If the settings file is not found, then
                         * assume this directory is not a services dir.
@@ -229,7 +213,11 @@ gchar **connman_storage_get_services()
        closedir(dir);
 
        str = g_string_free(result, FALSE);
-       if (str) {
+       if (str && str[0] != '\0') {
+               /*
+                * Remove the trailing separator so that services doesn't end up
+                * with an empty element.
+                */
                str[strlen(str) - 1] = '\0';
                services = g_strsplit(str, "/", -1);
        }
@@ -249,34 +237,25 @@ GKeyFile *connman_storage_load_service(const char *service_id)
 
        keyfile =  storage_load(pathname);
        g_free(pathname);
-       if (keyfile)
-               return keyfile;
-
-       pathname = g_strdup_printf("%s/%s", STORAGEDIR, DEFAULT);
-       if(pathname == NULL)
-               return NULL;
-
-       keyfile =  storage_load(pathname);
-
-       g_free(pathname);
 
        return keyfile;
 }
 
-void __connman_storage_save_service(GKeyFile *keyfile, const char *service_id)
+int __connman_storage_save_service(GKeyFile *keyfile, const char *service_id)
 {
+       int ret = 0;
        gchar *pathname, *dirname;
 
        dirname = g_strdup_printf("%s/%s", STORAGEDIR, service_id);
        if(dirname == NULL)
-               return;
+               return -ENOMEM;
 
        /* If the dir doesn't exist, create it */
        if (!g_file_test(dirname, G_FILE_TEST_IS_DIR)) {
                if(mkdir(dirname, MODE) < 0) {
                        if (errno != EEXIST) {
                                g_free(dirname);
-                               return;
+                               return -errno;
                        }
                }
        }
@@ -285,9 +264,74 @@ void __connman_storage_save_service(GKeyFile *keyfile, const char *service_id)
 
        g_free(dirname);
 
-       storage_save(keyfile, pathname);
+       ret = storage_save(keyfile, pathname);
+
+       g_free(pathname);
+
+       return ret;
+}
+
+static gboolean remove_file(const char *service_id, const char *file)
+{
+       gchar *pathname;
+       gboolean ret = FALSE;
+
+       pathname = g_strdup_printf("%s/%s/%s", STORAGEDIR, service_id, file);
+       if(pathname == NULL)
+               return FALSE;
+
+       if (g_file_test(pathname, G_FILE_TEST_EXISTS) == FALSE) {
+               ret = TRUE;
+       } else if (g_file_test(pathname, G_FILE_TEST_IS_REGULAR) == TRUE) {
+               unlink(pathname);
+               ret = TRUE;
+       }
 
        g_free(pathname);
+       return ret;
+}
+
+static gboolean remove_dir(const char *service_id)
+{
+       gchar *pathname;
+       gboolean ret = FALSE;
+
+       pathname = g_strdup_printf("%s/%s", STORAGEDIR, service_id);
+       if(pathname == NULL)
+               return FALSE;
+
+       if (g_file_test(pathname, G_FILE_TEST_EXISTS) == FALSE) {
+               ret = TRUE;
+       } else if (g_file_test(pathname, G_FILE_TEST_IS_DIR) == TRUE) {
+               rmdir(pathname);
+               ret = TRUE;
+       }
+
+       g_free(pathname);
+       return ret;
+}
+
+gboolean __connman_storage_remove_service(const char *service_id)
+{
+       gboolean removed;
+
+       /* Remove service configuration file */
+       removed = remove_file(service_id, SETTINGS);
+       if (removed == FALSE)
+               return FALSE;
+
+       /* Remove the statistics file also */
+       removed = remove_file(service_id, "data");
+       if (removed == FALSE)
+               return FALSE;
+
+       removed = remove_dir(service_id);
+       if (removed == FALSE)
+               return FALSE;
+
+       DBG("Removed service dir %s/%s", STORAGEDIR, service_id);
+
+       return TRUE;
 }
 
 GKeyFile *__connman_storage_load_provider(const char *identifier)
@@ -328,129 +372,51 @@ void __connman_storage_save_provider(GKeyFile *keyfile, const char *identifier)
        g_free(pathname);
 }
 
-/*
- * This function migrates keys from default.profile to settings file.
- * This can be removed once the migration is over.
-*/
-void __connman_storage_migrate()
+gchar **__connman_storage_get_providers(void)
 {
-       gchar *pathname;
-       GKeyFile *keyfile_def = NULL;
-       GKeyFile *keyfile = NULL;
-       GError *error = NULL;
-       connman_bool_t val;
-
-       /* If setting file exists, migration has been done. */
-       keyfile = __connman_storage_load_global();
-       if (keyfile) {
-               g_key_file_free(keyfile);
-               return;
-       }
-
-       pathname = g_strdup_printf("%s/%s", STORAGEDIR, DEFAULT);
-       if(pathname == NULL)
-               return;
-
-       /* Copy global settings from default.profile to settings. */
-       keyfile = g_key_file_new();
-
-       /* If default.profile doesn't exists, create settings with defaults. */
-       keyfile_def = storage_load(pathname);
-       if (keyfile_def == NULL) {
-               g_key_file_set_boolean(keyfile, "global",
-                                       "OfflineMode", FALSE);
-
-               g_key_file_set_boolean(keyfile, "WiFi",
-                                       "Enable", FALSE);
-
-               g_key_file_set_boolean(keyfile, "Bluetooth",
-                                       "Enable", FALSE);
-
-               g_key_file_set_boolean(keyfile, "Wired",
-                                       "Enable", FALSE);
-
-               g_key_file_set_boolean(keyfile, "3G",
-                                       "Enable", FALSE);
-
-               g_key_file_set_boolean(keyfile, "WiMAX",
-                                       "Enable", FALSE);
-
-               goto done;
-       }
-
-       /* offline mode */
-       val = g_key_file_get_boolean(keyfile_def, "global",
-                                       "OfflineMode", &error);
-       if (error != NULL) {
-               g_clear_error(&error);
-               val = FALSE;
-       }
-
-       g_key_file_set_boolean(keyfile, "global",
-                                       "OfflineMode", val);
-
-       /* wifi */
-       val = g_key_file_get_boolean(keyfile_def, "WiFi",
-                                       "Enable", &error);
-       if (error != NULL) {
-               g_clear_error(&error);
-               val = FALSE;
-       }
-
-       g_key_file_set_boolean(keyfile, "WiFi",
-                                       "Enable", val);
-
-       /* bluetooth */
-       val = g_key_file_get_boolean(keyfile_def, "Bluetooth",
-                                       "Enable", &error);
-       if (error != NULL) {
-               g_clear_error(&error);
-               val = FALSE;
-       }
-
-       g_key_file_set_boolean(keyfile, "Bluetooth",
-                                       "Enable", val);
+       GSList *list = NULL;
+       int num = 0, i = 0;
+       struct dirent *d;
+       gchar *str;
+       DIR *dir;
+       struct stat buf;
+       int ret;
+       char **providers;
+       GSList *iter;
 
-       /* wired */
-       val = g_key_file_get_boolean(keyfile_def, "Wired",
-                                       "Enable", &error);
-       if (error != NULL) {
-               g_clear_error(&error);
-               val = FALSE;
-       }
+       dir = opendir(STORAGEDIR);
+       if (dir == NULL)
+               return NULL;
 
-       g_key_file_set_boolean(keyfile, "Wired",
-                                       "Enable", val);
+       while ((d = readdir(dir))) {
+               if (strcmp(d->d_name, ".") == 0 ||
+                               strcmp(d->d_name, "..") == 0 ||
+                               strncmp(d->d_name, "provider_", 9) != 0)
+                       continue;
 
-       /* 3G */
-       val = g_key_file_get_boolean(keyfile_def, "3G",
-                                       "Enable", &error);
-       if (error != NULL) {
-               g_clear_error(&error);
-               val = FALSE;
+               if (d->d_type == DT_DIR) {
+                       str = g_strdup_printf("%s/%s/settings", STORAGEDIR,
+                                       d->d_name);
+                       ret = stat(str, &buf);
+                       g_free(str);
+                       if (ret < 0)
+                               continue;
+                       list = g_slist_prepend(list, g_strdup(d->d_name));
+                       num += 1;
+               }
        }
 
-       g_key_file_set_boolean(keyfile, "3G",
-                                       "Enable", val);
+       closedir(dir);
 
-       /* WiMAX */
-       val = g_key_file_get_boolean(keyfile_def, "WiMAX",
-                                       "Enable", &error);
-       if (error != NULL) {
-               g_clear_error(&error);
-               val = FALSE;
+       providers = g_try_new0(char *, num + 1);
+       for (iter = list; iter != NULL; iter = g_slist_next(iter)) {
+               if (providers != NULL)
+                       providers[i] = iter->data;
+               else
+                       g_free(iter->data);
+               i += 1;
        }
+       g_slist_free(list);
 
-       g_key_file_set_boolean(keyfile, "WiMAX",
-                                       "Enable", val);
-
-done:
-       __connman_storage_save_global(keyfile);
-
-       g_key_file_free(keyfile);
-
-       if (keyfile_def)
-               g_key_file_free(keyfile_def);
-
-       g_free(pathname);
+       return providers;
 }
index 8be3b25..60e336a 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
@@ -240,7 +240,7 @@ int connman_task_set_notify(struct connman_task *task, const char *member,
        notify->func = function;
        notify->data = user_data;
 
-       g_hash_table_insert(task->notify, g_strdup(member), notify);
+       g_hash_table_replace(task->notify, g_strdup(member), notify);
 
        return 0;
 }
@@ -408,7 +408,7 @@ int connman_task_stop(struct connman_task *task)
        return 0;
 }
 
-static DBusHandlerResult task_filter(DBusConnection *connection,
+static DBusHandlerResult task_filter(DBusConnection *conn,
                                        DBusMessage *message, void *user_data)
 {
        struct connman_task *task;
@@ -452,7 +452,7 @@ send_reply:
        }
 
        if (reply != NULL) {
-               dbus_connection_send(connection, reply, NULL);
+               dbus_connection_send(conn, reply, NULL);
 
                dbus_message_unref(reply);
        }
index 406423b..aa98e08 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
@@ -34,6 +34,13 @@ static DBusConnection *connection;
 
 static GSList *technology_list = NULL;
 
+/*
+ * List of devices with no technology associated with them either because of
+ * no compiled in support or the driver is not yet loaded.
+*/
+static GSList *techless_device_list = NULL;
+static GHashTable *rfkill_list;
+
 static connman_bool_t global_offlinemode;
 
 struct connman_rfkill {
@@ -43,22 +50,14 @@ struct connman_rfkill {
        connman_bool_t hardblock;
 };
 
-enum connman_technology_state {
-       CONNMAN_TECHNOLOGY_STATE_UNKNOWN   = 0,
-       CONNMAN_TECHNOLOGY_STATE_OFFLINE   = 1,
-       CONNMAN_TECHNOLOGY_STATE_ENABLED   = 2,
-       CONNMAN_TECHNOLOGY_STATE_CONNECTED = 3,
-};
-
 struct connman_technology {
        int refcount;
        enum connman_service_type type;
-       enum connman_technology_state state;
        char *path;
-       GHashTable *rfkill_list;
        GSList *device_list;
        int enabled;
        char *regdom;
+       connman_bool_t connected;
 
        connman_bool_t tethering;
        char *tethering_ident;
@@ -71,6 +70,8 @@ struct connman_technology {
 
        DBusMessage *pending_reply;
        guint pending_timeout;
+
+       GSList *scan_pending;
 };
 
 static GSList *driver_list = NULL;
@@ -83,6 +84,17 @@ static gint compare_priority(gconstpointer a, gconstpointer b)
        return driver2->priority - driver1->priority;
 }
 
+static void rfkill_check(gpointer key, gpointer value, gpointer user_data)
+{
+       struct connman_rfkill *rfkill = value;
+       enum connman_service_type type = GPOINTER_TO_INT(user_data);
+
+       /* Calling _technology_rfkill_add will update the tech. */
+       if (rfkill->type == type)
+               __connman_technology_add_rfkill(rfkill->index, type,
+                               rfkill->softblock, rfkill->hardblock);
+}
+
 /**
  * connman_technology_driver_register:
  * @driver: Technology driver definition
@@ -94,23 +106,35 @@ static gint compare_priority(gconstpointer a, gconstpointer b)
 int connman_technology_driver_register(struct connman_technology_driver *driver)
 {
        GSList *list;
-       struct connman_technology *technology;
+       struct connman_device *device;
+       enum connman_service_type type;
 
-       DBG("driver %p name %s", driver, driver->name);
+       DBG("Registering %s driver", driver->name);
 
        driver_list = g_slist_insert_sorted(driver_list, driver,
                                                        compare_priority);
 
-       for (list = technology_list; list; list = list->next) {
-               technology = list->data;
-
-               if (technology->driver != NULL)
+       /*
+        * Check for technology less devices if this driver
+        * can service any of them.
+       */
+       for (list = techless_device_list; list != NULL; list = list->next) {
+               device = list->data;
+
+               type = __connman_device_get_service_type(device);
+               if (type != driver->type)
                        continue;
 
-               if (technology->type == driver->type)
-                       technology->driver = driver;
+               techless_device_list = g_slist_remove(techless_device_list,
+                                                               device);
+
+               __connman_technology_add_device(device);
        }
 
+       /* Check for orphaned rfkill switches. */
+       g_hash_table_foreach(rfkill_list, rfkill_check,
+                                       GINT_TO_POINTER(driver->type));
+
        return 0;
 }
 
@@ -125,7 +149,7 @@ void connman_technology_driver_unregister(struct connman_technology_driver *driv
        GSList *list;
        struct connman_technology *technology;
 
-       DBG("driver %p name %s", driver, driver->name);
+       DBG("Unregistering driver %p name %s", driver, driver->name);
 
        for (list = technology_list; list; list = list->next) {
                technology = list->data;
@@ -240,57 +264,6 @@ static void free_rfkill(gpointer data)
        g_free(rfkill);
 }
 
-void __connman_technology_list(DBusMessageIter *iter, void *user_data)
-{
-       GSList *list;
-
-       for (list = technology_list; list; list = list->next) {
-               struct connman_technology *technology = list->data;
-
-               if (technology->path == NULL)
-                       continue;
-
-               dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH,
-                                                       &technology->path);
-       }
-}
-
-static void technologies_changed(void)
-{
-       connman_dbus_property_changed_array(CONNMAN_MANAGER_PATH,
-                       CONNMAN_MANAGER_INTERFACE, "Technologies",
-                       DBUS_TYPE_OBJECT_PATH, __connman_technology_list, NULL);
-}
-
-static const char *state2string(enum connman_technology_state state)
-{
-       switch (state) {
-       case CONNMAN_TECHNOLOGY_STATE_UNKNOWN:
-               break;
-       case CONNMAN_TECHNOLOGY_STATE_OFFLINE:
-               return "offline";
-       case CONNMAN_TECHNOLOGY_STATE_ENABLED:
-               return "enabled";
-       case CONNMAN_TECHNOLOGY_STATE_CONNECTED:
-               return "connected";
-       }
-
-       return NULL;
-}
-
-static void state_changed(struct connman_technology *technology)
-{
-       const char *str;
-
-       str = state2string(technology->state);
-       if (str == NULL)
-               return;
-
-       connman_dbus_property_changed_basic(technology->path,
-                               CONNMAN_TECHNOLOGY_INTERFACE, "State",
-                                               DBUS_TYPE_STRING, &str);
-}
-
 static const char *get_name(enum connman_service_type type)
 {
        switch (type) {
@@ -304,75 +277,82 @@ static const char *get_name(enum connman_service_type type)
                return "Wired";
        case CONNMAN_SERVICE_TYPE_WIFI:
                return "WiFi";
-       case CONNMAN_SERVICE_TYPE_WIMAX:
-               return "WiMAX";
        case CONNMAN_SERVICE_TYPE_BLUETOOTH:
                return "Bluetooth";
        case CONNMAN_SERVICE_TYPE_CELLULAR:
-               return "3G";
+               return "Cellular";
        }
 
        return NULL;
 }
 
-static void load_state(struct connman_technology *technology)
+static void save_state(struct connman_technology *technology)
 {
        GKeyFile *keyfile;
        gchar *identifier;
-       GError *error = NULL;
-       connman_bool_t enable;
 
        DBG("technology %p", technology);
 
        keyfile = __connman_storage_load_global();
-       /* Fallback on disabling technology if file not found. */
-       if (keyfile == NULL) {
-               technology->enable_persistent = FALSE;
-               return;
-       }
+       if (keyfile == NULL)
+               keyfile = g_key_file_new();
 
        identifier = g_strdup_printf("%s", get_name(technology->type));
        if (identifier == NULL)
                goto done;
 
-       enable = g_key_file_get_boolean(keyfile, identifier, "Enable", &error);
-       if (error == NULL)
-               technology->enable_persistent = enable;
-       else {
-               technology->enable_persistent = FALSE;
-               g_clear_error(&error);
-       }
+       g_key_file_set_boolean(keyfile, identifier, "Enable",
+                               technology->enable_persistent);
+
 done:
        g_free(identifier);
 
+       __connman_storage_save_global(keyfile);
+
        g_key_file_free(keyfile);
 
        return;
 }
 
-static void save_state(struct connman_technology *technology)
+static void load_state(struct connman_technology *technology)
 {
        GKeyFile *keyfile;
        gchar *identifier;
+       GError *error = NULL;
+       connman_bool_t enable;
 
        DBG("technology %p", technology);
 
        keyfile = __connman_storage_load_global();
-       if (keyfile == NULL)
-               keyfile = g_key_file_new();
+       /* Fallback on disabling technology if file not found. */
+       if (keyfile == NULL) {
+               if (technology->type == CONNMAN_SERVICE_TYPE_ETHERNET)
+                       /* We enable ethernet by default */
+                       technology->enable_persistent = TRUE;
+               else
+                       technology->enable_persistent = FALSE;
+               return;
+       }
 
        identifier = g_strdup_printf("%s", get_name(technology->type));
        if (identifier == NULL)
                goto done;
 
-       g_key_file_set_boolean(keyfile, identifier, "Enable",
-                               technology->enable_persistent);
+       enable = g_key_file_get_boolean(keyfile, identifier, "Enable", &error);
+       if (error == NULL)
+               technology->enable_persistent = enable;
+       else {
+               if (technology->type == CONNMAN_SERVICE_TYPE_ETHERNET)
+                       technology->enable_persistent = TRUE;
+               else
+                       technology->enable_persistent = FALSE;
 
+               save_state(technology);
+               g_clear_error(&error);
+       }
 done:
        g_free(identifier);
 
-       __connman_storage_save_global(keyfile);
-
        g_key_file_free(keyfile);
 
        return;
@@ -383,7 +363,7 @@ connman_bool_t __connman_technology_get_offlinemode(void)
        return global_offlinemode;
 }
 
-static void connman_technology_save_offlinemode()
+static void connman_technology_save_offlinemode(void)
 {
        GKeyFile *keyfile;
 
@@ -401,7 +381,7 @@ static void connman_technology_save_offlinemode()
        return;
 }
 
-static connman_bool_t connman_technology_load_offlinemode()
+static connman_bool_t connman_technology_load_offlinemode(void)
 {
        GKeyFile *keyfile;
        GError *error = NULL;
@@ -410,12 +390,12 @@ static connman_bool_t connman_technology_load_offlinemode()
        /* If there is a error, we enable offlinemode */
        keyfile = __connman_storage_load_global();
        if (keyfile == NULL)
-               return TRUE;
+               return FALSE;
 
        offlinemode = g_key_file_get_boolean(keyfile, "global",
                                                "OfflineMode", &error);
        if (error != NULL) {
-               offlinemode = TRUE;
+               offlinemode = FALSE;
                g_clear_error(&error);
        }
 
@@ -424,26 +404,14 @@ static connman_bool_t connman_technology_load_offlinemode()
        return offlinemode;
 }
 
-static DBusMessage *get_properties(DBusConnection *conn,
-                                       DBusMessage *message, void *user_data)
+static void append_properties(DBusMessageIter *iter,
+               struct connman_technology *technology)
 {
-       struct connman_technology *technology = user_data;
-       DBusMessage *reply;
-       DBusMessageIter array, dict;
+       DBusMessageIter dict;
        const char *str;
+       connman_bool_t powered;
 
-       reply = dbus_message_new_method_return(message);
-       if (reply == NULL)
-               return NULL;
-
-       dbus_message_iter_init_append(reply, &array);
-
-       connman_dbus_dict_open(&array, &dict);
-
-       str = state2string(technology->state);
-       if (str != NULL)
-               connman_dbus_dict_append_basic(&dict, "State",
-                                               DBUS_TYPE_STRING, &str);
+       connman_dbus_dict_open(iter, &dict);
 
        str = get_name(technology->type);
        if (str != NULL)
@@ -455,6 +423,18 @@ static DBusMessage *get_properties(DBusConnection *conn,
                connman_dbus_dict_append_basic(&dict, "Type",
                                                DBUS_TYPE_STRING, &str);
 
+       __sync_synchronize();
+       if (technology->enabled > 0)
+               powered = TRUE;
+       else
+               powered = FALSE;
+       connman_dbus_dict_append_basic(&dict, "Powered",
+                                       DBUS_TYPE_BOOLEAN, &powered);
+
+       connman_dbus_dict_append_basic(&dict, "Connected",
+                                       DBUS_TYPE_BOOLEAN,
+                                       &technology->connected);
+
        connman_dbus_dict_append_basic(&dict, "Tethering",
                                        DBUS_TYPE_BOOLEAN,
                                        &technology->tethering);
@@ -469,11 +449,236 @@ static DBusMessage *get_properties(DBusConnection *conn,
                                                DBUS_TYPE_STRING,
                                                &technology->tethering_passphrase);
 
-       connman_dbus_dict_close(&array, &dict);
+       connman_dbus_dict_close(iter, &dict);
+}
+
+static void technology_added_signal(struct connman_technology *technology)
+{
+       DBusMessage *signal;
+       DBusMessageIter iter;
+
+       signal = dbus_message_new_signal(CONNMAN_MANAGER_PATH,
+                       CONNMAN_MANAGER_INTERFACE, "TechnologyAdded");
+       if (signal == NULL)
+               return;
+
+       dbus_message_iter_init_append(signal, &iter);
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
+                                                       &technology->path);
+       append_properties(&iter, technology);
+
+       dbus_connection_send(connection, signal, NULL);
+       dbus_message_unref(signal);
+}
+
+static void technology_removed_signal(struct connman_technology *technology)
+{
+       g_dbus_emit_signal(connection, CONNMAN_MANAGER_PATH,
+                       CONNMAN_MANAGER_INTERFACE, "TechnologyRemoved",
+                       DBUS_TYPE_OBJECT_PATH, &technology->path,
+                       DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *get_properties(DBusConnection *conn,
+                                       DBusMessage *message, void *user_data)
+{
+       struct connman_technology *technology = user_data;
+       DBusMessage *reply;
+       DBusMessageIter iter;
+
+       reply = dbus_message_new_method_return(message);
+       if (reply == NULL)
+               return NULL;
+
+       dbus_message_iter_init_append(reply, &iter);
+       append_properties(&iter, technology);
 
        return reply;
 }
 
+void __connman_technology_list_struct(DBusMessageIter *array)
+{
+       GSList *list;
+       DBusMessageIter entry;
+
+       for (list = technology_list; list; list = list->next) {
+               struct connman_technology *technology = list->data;
+
+               if (technology->path == NULL)
+                       continue;
+
+               dbus_message_iter_open_container(array, DBUS_TYPE_STRUCT,
+                               NULL, &entry);
+               dbus_message_iter_append_basic(&entry, DBUS_TYPE_OBJECT_PATH,
+                               &technology->path);
+               append_properties(&entry, technology);
+               dbus_message_iter_close_container(array, &entry);
+       }
+}
+
+static gboolean technology_pending_reply(gpointer user_data)
+{
+       struct connman_technology *technology = user_data;
+       DBusMessage *reply;
+
+       /* Power request timedout, send ETIMEDOUT. */
+       if (technology->pending_reply != NULL) {
+               reply = __connman_error_failed(technology->pending_reply, ETIMEDOUT);
+               if (reply != NULL)
+                       g_dbus_send_message(connection, reply);
+
+               dbus_message_unref(technology->pending_reply);
+               technology->pending_reply = NULL;
+               technology->pending_timeout = 0;
+       }
+
+       return FALSE;
+}
+
+static int technology_enable(struct connman_technology *technology,
+               DBusMessage *msg)
+{
+       GSList *list;
+       int err = 0;
+       int ret = -ENODEV;
+       DBusMessage *reply;
+
+       DBG("technology %p enable", technology);
+
+       __sync_synchronize();
+       if (technology->enabled > 0) {
+               err = -EALREADY;
+               goto done;
+       }
+
+       if (technology->pending_reply != NULL) {
+               err = -EBUSY;
+               goto done;
+       }
+
+       if (msg != NULL) {
+               /*
+                * This is a bit of a trick. When msg is not NULL it means
+                * thats technology_enable was invoked from the manager API.
+                * Hence we save the state here.
+                */
+               technology->enable_persistent = TRUE;
+               save_state(technology);
+       }
+
+       __connman_rfkill_block(technology->type, FALSE);
+
+       /*
+        * An empty device list means that devices in the technology
+        * were rfkill blocked. The unblock above will enable the devs.
+        */
+       if (technology->device_list == NULL) {
+               ret = 0;
+               goto done;
+       }
+
+       for (list = technology->device_list; list; list = list->next) {
+               struct connman_device *device = list->data;
+
+               err = __connman_device_enable(device);
+               /*
+                * err = 0 : Device was enabled right away.
+                * If atleast one device gets enabled, we consider
+                * the technology to be enabled.
+                */
+               if (err == 0)
+                       ret = 0;
+       }
+
+done:
+       if (ret == 0) {
+               if (msg != NULL)
+                       g_dbus_send_reply(connection, msg, DBUS_TYPE_INVALID);
+               return ret;
+       }
+
+       if (msg != NULL) {
+               if (err == -EINPROGRESS) {
+                       technology->pending_reply = dbus_message_ref(msg);
+                       technology->pending_timeout = g_timeout_add_seconds(10,
+                                       technology_pending_reply, technology);
+               } else {
+                       if (err == -EALREADY)
+                               reply = __connman_error_already_enabled(msg);
+                       else
+                               reply = __connman_error_failed(msg, -err);
+                       if (reply != NULL)
+                               g_dbus_send_message(connection, reply);
+               }
+       }
+
+       return err;
+}
+
+static int technology_disable(struct connman_technology *technology,
+               DBusMessage *msg)
+{
+       GSList *list;
+       int err = 0;
+       int ret = -ENODEV;
+       DBusMessage *reply;
+
+       DBG("technology %p disable", technology);
+
+       __sync_synchronize();
+       if (technology->enabled == 0) {
+               err = -EALREADY;
+               goto done;
+       }
+
+       if (technology->pending_reply != NULL) {
+               err = -EBUSY;
+               goto done;
+       }
+
+       if (technology->tethering == TRUE)
+               set_tethering(technology, FALSE);
+
+       if (msg != NULL) {
+               technology->enable_persistent = FALSE;
+               save_state(technology);
+       }
+
+       __connman_rfkill_block(technology->type, TRUE);
+
+       for (list = technology->device_list; list; list = list->next) {
+               struct connman_device *device = list->data;
+
+               err = __connman_device_disable(device);
+               if (err == 0)
+                       ret = 0;
+       }
+
+done:
+       if (ret == 0) {
+               if (msg != NULL)
+                       g_dbus_send_reply(connection, msg, DBUS_TYPE_INVALID);
+               return ret;
+       }
+
+       if (msg != NULL) {
+               if (err == -EINPROGRESS) {
+                       technology->pending_reply = dbus_message_ref(msg);
+                       technology->pending_timeout = g_timeout_add_seconds(10,
+                                       technology_pending_reply, technology);
+               } else {
+                       if (err == -EALREADY)
+                               reply = __connman_error_already_disabled(msg);
+                       else
+                               reply = __connman_error_failed(msg, -err);
+                       if (reply != NULL)
+                               g_dbus_send_message(connection, reply);
+               }
+       }
+
+       return err;
+}
+
 static DBusMessage *set_property(DBusConnection *conn,
                                        DBusMessage *msg, void *data)
 {
@@ -487,8 +692,15 @@ static DBusMessage *set_property(DBusConnection *conn,
        if (dbus_message_iter_init(msg, &iter) == FALSE)
                return __connman_error_invalid_arguments(msg);
 
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+               return __connman_error_invalid_arguments(msg);
+
        dbus_message_iter_get_basic(&iter, &name);
        dbus_message_iter_next(&iter);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
+               return __connman_error_invalid_arguments(msg);
+
        dbus_message_iter_recurse(&iter, &value);
 
        type = dbus_message_iter_get_arg_type(&value);
@@ -519,52 +731,150 @@ static DBusMessage *set_property(DBusConnection *conn,
                if (technology->type != CONNMAN_SERVICE_TYPE_WIFI)
                        return __connman_error_not_supported(msg);
 
-               technology->tethering_ident = g_strdup(str);
-       } else if (g_str_equal(name, "TetheringPassphrase") == TRUE) {
-               const char *str;
+               technology->tethering_ident = g_strdup(str);
+       } else if (g_str_equal(name, "TetheringPassphrase") == TRUE) {
+               const char *str;
+
+               dbus_message_iter_get_basic(&value, &str);
+
+               if (technology->type != CONNMAN_SERVICE_TYPE_WIFI)
+                       return __connman_error_not_supported(msg);
+
+               if (strlen(str) < 8)
+                       return __connman_error_invalid_arguments(msg);
+
+               technology->tethering_passphrase = g_strdup(str);
+       } else if (g_str_equal(name, "Powered") == TRUE) {
+               connman_bool_t enable;
+
+               if (type != DBUS_TYPE_BOOLEAN)
+                       return __connman_error_invalid_arguments(msg);
+
+               dbus_message_iter_get_basic(&value, &enable);
+               if (enable == TRUE)
+                       technology_enable(technology, msg);
+               else
+                       technology_disable(technology, msg);
+
+       } else
+               return __connman_error_invalid_property(msg);
+
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static struct connman_technology *technology_find(enum connman_service_type type)
+{
+       GSList *list;
+
+       DBG("type %d", type);
+
+       for (list = technology_list; list; list = list->next) {
+               struct connman_technology *technology = list->data;
+
+               if (technology->type == type)
+                       return technology;
+       }
+
+       return NULL;
+}
+
+static void reply_scan_pending(struct connman_technology *technology, int err)
+{
+       DBusMessage *reply;
+
+       DBG("technology %p err %d", technology, err);
+
+       while (technology->scan_pending != NULL) {
+               DBusMessage *msg = technology->scan_pending->data;
+
+               DBG("reply to %s", dbus_message_get_sender(msg));
+
+               if (err == 0)
+                       reply = g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+               else
+                       reply = __connman_error_failed(msg, -err);
+               g_dbus_send_message(connection, reply);
+               dbus_message_unref(msg);
+
+               technology->scan_pending =
+                       g_slist_delete_link(technology->scan_pending,
+                                       technology->scan_pending);
+       }
+}
+
+void __connman_technology_scan_started(struct connman_device *device)
+{
+       DBG("device %p", device);
+}
+
+void __connman_technology_scan_stopped(struct connman_device *device)
+{
+       int count = 0;
+       struct connman_technology *technology;
+       enum connman_service_type type;
+       GSList *list;
+
+       type = __connman_device_get_service_type(device);
+       technology = technology_find(type);
 
-               dbus_message_iter_get_basic(&value, &str);
+       DBG("technology %p device %p", technology, device);
 
-               if (technology->type != CONNMAN_SERVICE_TYPE_WIFI)
-                       return __connman_error_not_supported(msg);
+       if (technology == NULL)
+               return;
 
-               if (strlen(str) < 8)
-                       return __connman_error_invalid_arguments(msg);
+       for (list = technology->device_list; list != NULL; list = list->next) {
+               struct connman_device *other_device = list->data;
 
-               technology->tethering_passphrase = g_strdup(str);
-       } else
-               return __connman_error_invalid_property(msg);
+               if (device == other_device)
+                       continue;
 
-       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
-}
+               if (__connman_device_get_service_type(other_device) != type)
+                       continue;
 
-static GDBusMethodTable technology_methods[] = {
-       { "GetProperties", "",   "a{sv}", get_properties },
-       { "SetProperty",   "sv", "",      set_property   },
-       { },
-};
+               if (connman_device_get_scanning(other_device) == TRUE)
+                       count += 1;
+       }
 
-static GDBusSignalTable technology_signals[] = {
-       { "PropertyChanged", "sv" },
-       { },
-};
+       if (count == 0)
+               reply_scan_pending(technology, 0);
+}
 
-static struct connman_technology *technology_find(enum connman_service_type type)
+static DBusMessage *scan(DBusConnection *conn, DBusMessage *msg, void *data)
 {
-       GSList *list;
+       struct connman_technology *technology = data;
+       int err;
 
-       DBG("type %d", type);
+       DBG ("technology %p request from %s", technology,
+                       dbus_message_get_sender(msg));
 
-       for (list = technology_list; list; list = list->next) {
-               struct connman_technology *technology = list->data;
+       dbus_message_ref(msg);
+       technology->scan_pending =
+               g_slist_prepend(technology->scan_pending, msg);
 
-               if (technology->type == type)
-                       return technology;
-       }
+       err = __connman_device_request_scan(technology->type);
+       if (err < 0)
+               reply_scan_pending(technology, err);
 
        return NULL;
 }
 
+static const GDBusMethodTable technology_methods[] = {
+       { GDBUS_DEPRECATED_METHOD("GetProperties",
+                       NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
+                       get_properties) },
+       { GDBUS_METHOD("SetProperty",
+                       GDBUS_ARGS({ "name", "s" }, { "value", "v" }),
+                       NULL, set_property) },
+       { GDBUS_ASYNC_METHOD("Scan", NULL, NULL, scan) },
+       { },
+};
+
+static const GDBusSignalTable technology_signals[] = {
+       { GDBUS_SIGNAL("PropertyChanged",
+                       GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
+       { },
+};
+
 static struct connman_technology *technology_get(enum connman_service_type type)
 {
        struct connman_technology *technology;
@@ -580,8 +890,10 @@ static struct connman_technology *technology_get(enum connman_service_type type)
                return NULL;
 
        technology = technology_find(type);
-       if (technology != NULL)
+       if (technology != NULL) {
+               __sync_fetch_and_add(&technology->refcount, 1);
                return technology;
+       }
 
        /* First check if we have a driver for this technology type */
        for (list = driver_list; list; list = list->next) {
@@ -609,12 +921,9 @@ static struct connman_technology *technology_get(enum connman_service_type type)
        technology->path = g_strdup_printf("%s/technology/%s",
                                                        CONNMAN_PATH, str);
 
-       technology->rfkill_list = g_hash_table_new_full(g_int_hash, g_int_equal,
-                                                       NULL, free_rfkill);
        technology->device_list = NULL;
 
        technology->pending_reply = NULL;
-       technology->state = CONNMAN_TECHNOLOGY_STATE_OFFLINE;
 
        load_state(technology);
 
@@ -627,9 +936,9 @@ static struct connman_technology *technology_get(enum connman_service_type type)
                return NULL;
        }
 
-       technology_list = g_slist_append(technology_list, technology);
+       technology_list = g_slist_prepend(technology_list, technology);
 
-       technologies_changed();
+       technology_added_signal(technology);
 
        technology->driver = driver;
        err = driver->probe(technology);
@@ -645,9 +954,11 @@ static void technology_put(struct connman_technology *technology)
 {
        DBG("technology %p", technology);
 
-       if (__sync_fetch_and_sub(&technology->refcount, 1) != 1)
+       if (__sync_sub_and_fetch(&technology->refcount, 1) > 0)
                return;
 
+       reply_scan_pending(technology, -EINTR);
+
        if (technology->driver) {
                technology->driver->remove(technology);
                technology->driver = NULL;
@@ -655,13 +966,12 @@ static void technology_put(struct connman_technology *technology)
 
        technology_list = g_slist_remove(technology_list, technology);
 
-       technologies_changed();
+       technology_removed_signal(technology);
 
        g_dbus_unregister_interface(connection, technology->path,
                                                CONNMAN_TECHNOLOGY_INTERFACE);
 
        g_slist_free(technology->device_list);
-       g_hash_table_destroy(technology->rfkill_list);
 
        g_free(technology->path);
        g_free(technology->regdom);
@@ -679,7 +989,6 @@ void __connman_technology_add_interface(enum connman_service_type type,
                return;
        case CONNMAN_SERVICE_TYPE_ETHERNET:
        case CONNMAN_SERVICE_TYPE_WIFI:
-       case CONNMAN_SERVICE_TYPE_WIMAX:
        case CONNMAN_SERVICE_TYPE_BLUETOOTH:
        case CONNMAN_SERVICE_TYPE_CELLULAR:
        case CONNMAN_SERVICE_TYPE_GPS:
@@ -688,10 +997,10 @@ void __connman_technology_add_interface(enum connman_service_type type,
                break;
        }
 
-       connman_info("Create interface %s [ %s ]", name,
+       connman_info("Adding interface %s [ %s ]", name,
                                __connman_service_type2string(type));
 
-       technology = technology_get(type);
+       technology = technology_find(type);
 
        if (technology == NULL || technology->driver == NULL
                        || technology->driver->add_interface == NULL)
@@ -712,7 +1021,6 @@ void __connman_technology_remove_interface(enum connman_service_type type,
                return;
        case CONNMAN_SERVICE_TYPE_ETHERNET:
        case CONNMAN_SERVICE_TYPE_WIFI:
-       case CONNMAN_SERVICE_TYPE_WIMAX:
        case CONNMAN_SERVICE_TYPE_BLUETOOTH:
        case CONNMAN_SERVICE_TYPE_CELLULAR:
        case CONNMAN_SERVICE_TYPE_GPS:
@@ -731,8 +1039,6 @@ void __connman_technology_remove_interface(enum connman_service_type type,
 
        if (technology->driver->remove_interface)
                technology->driver->remove_interface(technology, index);
-
-       technology_put(technology);
 }
 
 int __connman_technology_add_device(struct connman_device *device)
@@ -743,19 +1049,35 @@ int __connman_technology_add_device(struct connman_device *device)
        DBG("device %p", device);
 
        type = __connman_device_get_service_type(device);
-       __connman_notifier_register(type);
 
        technology = technology_get(type);
-       if (technology == NULL)
+       if (technology == NULL) {
+               /*
+                * Since no driver can be found for this device at the moment we
+                * add it to the techless device list.
+               */
+               techless_device_list = g_slist_prepend(techless_device_list,
+                                                               device);
+
                return -ENXIO;
+       }
 
-       if (technology->enable_persistent && !global_offlinemode)
-               __connman_device_enable(device);
+       if (technology->enable_persistent && !global_offlinemode) {
+               int err = __connman_device_enable(device);
+               /*
+                * connman_technology_add_device() calls __connman_device_enable()
+                * but since the device is already enabled, the calls does not
+                * propagate through to connman_technology_enabled via
+                * connman_device_set_powered.
+                */
+               if (err == -EALREADY)
+                       __connman_technology_enabled(type);
+       }
        /* if technology persistent state is offline */
        if (!technology->enable_persistent)
                __connman_device_disable(device);
 
-       technology->device_list = g_slist_append(technology->device_list,
+       technology->device_list = g_slist_prepend(technology->device_list,
                                                                device);
 
        return 0;
@@ -769,39 +1091,34 @@ int __connman_technology_remove_device(struct connman_device *device)
        DBG("device %p", device);
 
        type = __connman_device_get_service_type(device);
-       __connman_notifier_unregister(type);
 
        technology = technology_find(type);
-       if (technology == NULL)
+       if (technology == NULL) {
+               techless_device_list = g_slist_remove(techless_device_list,
+                                                               device);
                return -ENXIO;
+       }
 
        technology->device_list = g_slist_remove(technology->device_list,
                                                                device);
-       if (technology->device_list == NULL) {
-               technology->state = CONNMAN_TECHNOLOGY_STATE_OFFLINE;
-               state_changed(technology);
-       }
+       technology_put(technology);
 
        return 0;
 }
 
-static gboolean technology_pending_reply(gpointer user_data)
+static void powered_changed(struct connman_technology *technology)
 {
-       struct connman_technology *technology = user_data;
-       DBusMessage *reply;
-
-       /* Power request timedout, send ETIMEDOUT. */
-       if (technology->pending_reply != NULL) {
-               reply = __connman_error_failed(technology->pending_reply, ETIMEDOUT);
-               if (reply != NULL)
-                       g_dbus_send_message(connection, reply);
+       connman_bool_t powered;
 
-               dbus_message_unref(technology->pending_reply);
-               technology->pending_reply = NULL;
-               technology->pending_timeout = 0;
-       }
+       __sync_synchronize();
+       if (technology->enabled >0)
+               powered = TRUE;
+       else
+               powered = FALSE;
 
-       return FALSE;
+       connman_dbus_property_changed_basic(technology->path,
+                       CONNMAN_TECHNOLOGY_INTERFACE, "Powered",
+                       DBUS_TYPE_BOOLEAN, &powered);
 }
 
 int __connman_technology_enabled(enum connman_service_type type)
@@ -812,11 +1129,10 @@ int __connman_technology_enabled(enum connman_service_type type)
        if (technology == NULL)
                return -ENXIO;
 
-       if (__sync_fetch_and_add(&technology->enabled, 1) == 0) {
-               __connman_notifier_enable(type);
-               technology->state = CONNMAN_TECHNOLOGY_STATE_ENABLED;
-               state_changed(technology);
-       }
+       if (__sync_fetch_and_add(&technology->enabled, 1) != 0)
+               return -EALREADY;
+
+       powered_changed(technology);
 
        if (technology->pending_reply != NULL) {
                g_dbus_send_reply(connection, technology->pending_reply, DBUS_TYPE_INVALID);
@@ -829,81 +1145,6 @@ int __connman_technology_enabled(enum connman_service_type type)
        return 0;
 }
 
-int __connman_technology_enable(enum connman_service_type type, DBusMessage *msg)
-{
-       struct connman_technology *technology;
-       GSList *list;
-       int err = 0;
-       int ret = -ENODEV;
-       DBusMessage *reply;
-
-       DBG("type %d enable", type);
-
-       technology = technology_find(type);
-       if (technology == NULL) {
-               err = -ENXIO;
-               goto done;
-       }
-
-       if (technology->pending_reply != NULL) {
-               err = -EBUSY;
-               goto done;
-       }
-
-       if (msg != NULL) {
-               /*
-                * This is a bit of a trick. When msg is not NULL it means
-                * thats technology_enable was invoked from the manager API. Hence we save
-                * the state here.
-                */
-               technology->enable_persistent = TRUE;
-               save_state(technology);
-       }
-
-       __connman_rfkill_block(technology->type, FALSE);
-
-       /*
-        * An empty device list means that devices in the technology
-        * were rfkill blocked. The unblock above will enable the devs.
-        */
-       if (technology->device_list == NULL)
-               return 0;
-
-       for (list = technology->device_list; list; list = list->next) {
-               struct connman_device *device = list->data;
-
-               err = __connman_device_enable(device);
-               /*
-                * err = 0 : Device was enabled right away.
-                * If atleast one device gets enabled, we consider
-                * the technology to be enabled.
-                */
-               if (err == 0)
-                       ret = 0;
-       }
-
-done:
-       if (ret == 0) {
-               if (msg != NULL)
-                       g_dbus_send_reply(connection, msg, DBUS_TYPE_INVALID);
-               return ret;
-       }
-
-       if (msg != NULL) {
-               if (err == -EINPROGRESS) {
-                       technology->pending_reply = dbus_message_ref(msg);
-                       technology->pending_timeout = g_timeout_add_seconds(10,
-                                       technology_pending_reply, technology);
-               } else {
-                       reply = __connman_error_failed(msg, -err);
-                       if (reply != NULL)
-                               g_dbus_send_message(connection, reply);
-               }
-       }
-
-       return err;
-}
-
 int __connman_technology_disabled(enum connman_service_type type)
 {
        struct connman_technology *technology;
@@ -912,6 +1153,9 @@ int __connman_technology_disabled(enum connman_service_type type)
        if (technology == NULL)
                return -ENXIO;
 
+       if (__sync_fetch_and_sub(&technology->enabled, 1) != 1)
+               return -EINPROGRESS;
+
        if (technology->pending_reply != NULL) {
                g_dbus_send_reply(connection, technology->pending_reply, DBUS_TYPE_INVALID);
                dbus_message_unref(technology->pending_reply);
@@ -920,77 +1164,11 @@ int __connman_technology_disabled(enum connman_service_type type)
                technology->pending_timeout = 0;
        }
 
-       if (__sync_fetch_and_sub(&technology->enabled, 1) != 1)
-               return 0;
-
-       __connman_notifier_disable(type);
-       technology->state = CONNMAN_TECHNOLOGY_STATE_OFFLINE;
-       state_changed(technology);
+       powered_changed(technology);
 
        return 0;
 }
 
-int __connman_technology_disable(enum connman_service_type type, DBusMessage *msg)
-{
-       struct connman_technology *technology;
-       GSList *list;
-       int err = 0;
-       int ret = -ENODEV;
-       DBusMessage *reply;
-
-       DBG("type %d disable", type);
-
-       technology = technology_find(type);
-       if (technology == NULL) {
-               err = -ENXIO;
-               goto done;
-       }
-
-       if (technology->pending_reply != NULL) {
-               err = -EBUSY;
-               goto done;
-       }
-
-       if (technology->tethering == TRUE)
-               set_tethering(technology, FALSE);
-
-       if (msg != NULL) {
-               technology->enable_persistent = FALSE;
-               save_state(technology);
-       }
-
-       __connman_rfkill_block(technology->type, TRUE);
-
-       for (list = technology->device_list; list; list = list->next) {
-               struct connman_device *device = list->data;
-
-               err = __connman_device_disable(device);
-               if (err == 0)
-                       ret = 0;
-       }
-
-done:
-       if (ret == 0) {
-               if (msg != NULL)
-                       g_dbus_send_reply(connection, msg, DBUS_TYPE_INVALID);
-               return ret;
-       }
-
-       if (msg != NULL) {
-               if (err == -EINPROGRESS) {
-                       technology->pending_reply = dbus_message_ref(msg);
-                       technology->pending_timeout = g_timeout_add_seconds(10,
-                                       technology_pending_reply, technology);
-               } else {
-                       reply = __connman_error_failed(msg, -err);
-                       if (reply != NULL)
-                               g_dbus_send_message(connection, reply);
-               }
-       }
-
-       return err;
-}
-
 int __connman_technology_set_offlinemode(connman_bool_t offlinemode)
 {
        GSList *list;
@@ -1017,10 +1195,10 @@ int __connman_technology_set_offlinemode(connman_bool_t offlinemode)
                struct connman_technology *technology = list->data;
 
                if (offlinemode)
-                       err = __connman_technology_disable(technology->type, NULL);
+                       err = technology_disable(technology, NULL);
 
                if (!offlinemode && technology->enable_persistent)
-                       err = __connman_technology_enable(technology->type, NULL);
+                       err = technology_enable(technology, NULL);
        }
 
        if (err == 0 || err == -EINPROGRESS || err == -EALREADY) {
@@ -1032,6 +1210,24 @@ int __connman_technology_set_offlinemode(connman_bool_t offlinemode)
        return err;
 }
 
+void __connman_technology_set_connected(enum connman_service_type type,
+               connman_bool_t connected)
+{
+       struct connman_technology *technology;
+
+       technology = technology_find(type);
+       if (technology == NULL)
+               return;
+
+       DBG("technology %p connected %d", technology, connected);
+
+       technology->connected = connected;
+
+       connman_dbus_property_changed_basic(technology->path,
+                       CONNMAN_TECHNOLOGY_INTERFACE, "Connected",
+                       DBUS_TYPE_BOOLEAN, &connected);
+}
+
 int __connman_technology_add_rfkill(unsigned int index,
                                        enum connman_service_type type,
                                                connman_bool_t softblock,
@@ -1043,22 +1239,26 @@ int __connman_technology_add_rfkill(unsigned int index,
        DBG("index %u type %d soft %u hard %u", index, type,
                                                        softblock, hardblock);
 
-       technology = technology_get(type);
-       if (technology == NULL)
-               return -ENXIO;
+       rfkill = g_hash_table_lookup(rfkill_list, GINT_TO_POINTER(index));
+       if (rfkill != NULL)
+               goto done;
 
        rfkill = g_try_new0(struct connman_rfkill, 1);
        if (rfkill == NULL)
                return -ENOMEM;
 
-       __connman_notifier_register(type);
-
        rfkill->index = index;
        rfkill->type = type;
        rfkill->softblock = softblock;
        rfkill->hardblock = hardblock;
 
-       g_hash_table_replace(technology->rfkill_list, &rfkill->index, rfkill);
+       g_hash_table_insert(rfkill_list, GINT_TO_POINTER(index), rfkill);
+
+done:
+       technology = technology_get(type);
+       /* If there is no driver for this type, ignore it. */
+       if (technology == NULL)
+               return -ENXIO;
 
        if (hardblock) {
                DBG("%s is switched off.", get_name(type));
@@ -1093,16 +1293,12 @@ int __connman_technology_update_rfkill(unsigned int index,
 
        DBG("index %u soft %u hard %u", index, softblock, hardblock);
 
-       technology = technology_find(type);
-       if (technology == NULL)
-               return -ENXIO;
-
-       rfkill = g_hash_table_lookup(technology->rfkill_list, &index);
+       rfkill = g_hash_table_lookup(rfkill_list, GINT_TO_POINTER(index));
        if (rfkill == NULL)
                return -ENXIO;
 
        if (rfkill->softblock == softblock &&
-               rfkill->hardblock == hardblock)
+                               rfkill->hardblock == hardblock)
                return 0;
 
        rfkill->softblock = softblock;
@@ -1113,6 +1309,11 @@ int __connman_technology_update_rfkill(unsigned int index,
                return 0;
        }
 
+       technology = technology_find(type);
+       /* If there is no driver for this type, ignore it. */
+       if (technology == NULL)
+               return -ENXIO;
+
        if (!global_offlinemode) {
                if (technology->enable_persistent && softblock)
                        return __connman_rfkill_block(type, FALSE);
@@ -1131,15 +1332,15 @@ int __connman_technology_remove_rfkill(unsigned int index,
 
        DBG("index %u", index);
 
-       technology = technology_find(type);
-       if (technology == NULL)
-               return -ENXIO;
-
-       rfkill = g_hash_table_lookup(technology->rfkill_list, &index);
+       rfkill = g_hash_table_lookup(rfkill_list, GINT_TO_POINTER(index));
        if (rfkill == NULL)
                return -ENXIO;
 
-       g_hash_table_remove(technology->rfkill_list, &index);
+       g_hash_table_remove(rfkill_list, GINT_TO_POINTER(index));
+
+       technology = technology_find(type);
+       if (technology == NULL)
+               return -ENXIO;
 
        technology_put(technology);
 
@@ -1152,8 +1353,14 @@ int __connman_technology_init(void)
 
        connection = connman_dbus_get_connection();
 
+       rfkill_list = g_hash_table_new_full(g_direct_hash, g_direct_equal,
+                                                       NULL, free_rfkill);
+
        global_offlinemode = connman_technology_load_offlinemode();
 
+       /* This will create settings file if it is missing */
+       connman_technology_save_offlinemode();
+
        return 0;
 }
 
@@ -1161,5 +1368,7 @@ void __connman_technology_cleanup(void)
 {
        DBG("");
 
+       g_hash_table_destroy(rfkill_list);
+
        dbus_connection_unref(connection);
 }
index 0164695..e70243b 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *  Copyright (C) 2011 ProFUSION embedded systems
  *
  *  This program is free software; you can redistribute it and/or modify
@@ -35,6 +35,8 @@
 #include <string.h>
 #include <fcntl.h>
 #include <linux/if_tun.h>
+#include <netinet/in.h>
+#include <linux/if_bridge.h>
 
 #include "connman.h"
 
 #define DBUS_TYPE_UNIX_FD -1
 #endif
 
-#define BRIDGE_PROC_DIR "/proc/sys/net/bridge"
-
 #define BRIDGE_NAME "tether"
-#define BRIDGE_IP "192.168.218.1"
-#define BRIDGE_BCAST "192.168.218.255"
-#define BRIDGE_SUBNET "255.255.255.0"
-#define BRIDGE_IP_START "192.168.218.100"
-#define BRIDGE_IP_END "192.168.218.200"
 #define BRIDGE_DNS "8.8.8.8"
 
 #define DEFAULT_MTU    1500
 
-#define PRIVATE_NETWORK_IP "192.168.219.1"
-#define PRIVATE_NETWORK_PEER_IP "192.168.219.2"
-#define PRIVATE_NETWORK_NETMASK "255.255.255.0"
 #define PRIVATE_NETWORK_PRIMARY_DNS BRIDGE_DNS
 #define PRIVATE_NETWORK_SECONDARY_DNS "8.8.4.4"
 
-static char *default_interface = NULL;
 static volatile int tethering_enabled;
 static GDHCPServer *tethering_dhcp_server = NULL;
+static struct connman_ippool *dhcp_ippool = NULL;
 static DBusConnection *connection;
 static GHashTable *pn_hash;
 
@@ -80,17 +72,25 @@ struct connman_private_network {
        char *interface;
        int index;
        guint iface_watch;
-       const char *server_ip;
-       const char *peer_ip;
+       struct connman_ippool *pool;
        const char *primary_dns;
        const char *secondary_dns;
 };
 
 const char *__connman_tethering_get_bridge(void)
 {
-       struct stat st;
+       int sk, err;
+       unsigned long args[3];
 
-       if (stat(BRIDGE_PROC_DIR, &st) < 0) {
+       sk = socket(AF_INET, SOCK_STREAM, 0);
+       if (sk < 0)
+               return NULL;
+
+       args[0] = BRCTL_GET_VERSION;
+       args[1] = args[2] = 0;
+       err = ioctl(sk, SIOCGIFBR, &args);
+       close(sk);
+       if (err == -1) {
                connman_error("Missing support for 802.1d ethernet bridging");
                return NULL;
        }
@@ -175,250 +175,110 @@ static void dhcp_server_stop(GDHCPServer *server)
        g_dhcp_server_unref(server);
 }
 
-static int set_forward_delay(const char *name, unsigned int delay)
-{
-       FILE *f;
-       char *forward_delay_path;
-
-       forward_delay_path =
-               g_strdup_printf("/sys/class/net/%s/bridge/forward_delay", name);
-
-       if (forward_delay_path == NULL)
-               return -ENOMEM;
-
-       f = fopen(forward_delay_path, "r+");
-
-       g_free(forward_delay_path);
-
-       if (f == NULL)
-               return -errno;
-
-       fprintf(f, "%d", delay);
-
-       fclose(f);
-
-       return 0;
-}
-
-static int create_bridge(const char *name)
-{
-       int sk, err;
-
-       DBG("name %s", name);
-
-       sk = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
-       if (sk < 0)
-               return -EOPNOTSUPP;
-
-       if (ioctl(sk, SIOCBRADDBR, name) == -1) {
-               err = -errno;
-               if (err != -EEXIST)
-                       return -EOPNOTSUPP;
-       }
-
-       err = set_forward_delay(name, 0);
-
-       if (err < 0)
-               ioctl(sk, SIOCBRDELBR, name);
-
-       close(sk);
-
-       return err;
-}
-
-static int remove_bridge(const char *name)
-{
-       int sk, err;
-
-       DBG("name %s", name);
-
-       sk = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
-       if (sk < 0)
-               return -EOPNOTSUPP;
-
-       err = ioctl(sk, SIOCBRDELBR, name);
-
-       close(sk);
-
-       if (err < 0)
-               return -EOPNOTSUPP;
-
-       return 0;
-}
-
-static int enable_bridge(const char *name)
-{
-       int err, index;
-
-       index = connman_inet_ifindex(name);
-       if (index < 0)
-               return index;
-
-       err = __connman_inet_modify_address(RTM_NEWADDR,
-                       NLM_F_REPLACE | NLM_F_ACK, index, AF_INET,
-                                       BRIDGE_IP, NULL, 24, BRIDGE_BCAST);
-       if (err < 0)
-               return err;
-
-       return connman_inet_ifup(index);
-}
-
-static int disable_bridge(const char *name)
-{
-       int index;
-
-       index = connman_inet_ifindex(name);
-       if (index < 0)
-               return index;
-
-       return connman_inet_ifdown(index);
-}
-
-static int enable_ip_forward(connman_bool_t enable)
-{
-
-       FILE *f;
-
-       f = fopen("/proc/sys/net/ipv4/ip_forward", "r+");
-       if (f == NULL)
-               return -errno;
-
-       if (enable == TRUE)
-               fprintf(f, "1");
-       else
-               fprintf(f, "0");
-
-       fclose(f);
-
-       return 0;
-}
-
-static int enable_nat(const char *interface)
+static void tethering_restart(struct connman_ippool *pool, void *user_data)
 {
-       int err;
-
-       if (interface == NULL)
-               return 0;
-
-       /* Enable IPv4 forwarding */
-       err = enable_ip_forward(TRUE);
-       if (err < 0)
-               return err;
-
-       /* POSTROUTING flush */
-       err = __connman_iptables_command("-t nat -F POSTROUTING");
-       if (err < 0)
-               return err;
-
-       /* Enable masquerading */
-       err = __connman_iptables_command("-t nat -A POSTROUTING "
-                                       "-o %s -j MASQUERADE", interface);
-       if (err < 0)
-               return err;
-
-       return __connman_iptables_commit("nat");
-}
-
-static void disable_nat(const char *interface)
-{
-       int err;
-
-       /* Disable IPv4 forwarding */
-       enable_ip_forward(FALSE);
-
-       /* POSTROUTING flush */
-       err = __connman_iptables_command("-t nat -F POSTROUTING");
-       if (err < 0)
-               return;
-
-       __connman_iptables_commit("nat");
+       __connman_tethering_set_disabled();
+       __connman_tethering_set_enabled();
 }
 
 void __connman_tethering_set_enabled(void)
 {
+       int index;
        int err;
+       const char *gateway;
+       const char *broadcast;
+       const char *subnet_mask;
+       const char *start_ip;
+       const char *end_ip;
        const char *dns;
+       unsigned char prefixlen;
 
        DBG("enabled %d", tethering_enabled + 1);
 
        if (__sync_fetch_and_add(&tethering_enabled, 1) != 0)
                return;
 
-       err = create_bridge(BRIDGE_NAME);
-       if (err < 0)
+       err = __connman_bridge_create(BRIDGE_NAME);
+       if (err < 0) {
+               __sync_fetch_and_sub(&tethering_enabled, 1);
                return;
+       }
+
+       index = connman_inet_ifindex(BRIDGE_NAME);
+       dhcp_ippool = __connman_ippool_create(index, 2, 252,
+                                               tethering_restart, NULL);
+       if (dhcp_ippool == NULL) {
+               connman_error("Fail to create IP pool");
+               __connman_bridge_remove(BRIDGE_NAME);
+               __sync_fetch_and_sub(&tethering_enabled, 1);
+               return;
+       }
 
-       err = enable_bridge(BRIDGE_NAME);
+       gateway = __connman_ippool_get_gateway(dhcp_ippool);
+       broadcast = __connman_ippool_get_broadcast(dhcp_ippool);
+       subnet_mask = __connman_ippool_get_subnet_mask(dhcp_ippool);
+       start_ip = __connman_ippool_get_start_ip(dhcp_ippool);
+       end_ip = __connman_ippool_get_end_ip(dhcp_ippool);
+
+       err = __connman_bridge_enable(BRIDGE_NAME, gateway, broadcast);
        if (err < 0 && err != -EALREADY) {
-               remove_bridge(BRIDGE_NAME);
+               __connman_ippool_unref(dhcp_ippool);
+               __connman_bridge_remove(BRIDGE_NAME);
+               __sync_fetch_and_sub(&tethering_enabled, 1);
                return;
        }
 
-       dns = BRIDGE_IP;
-       if (__connman_dnsproxy_add_listener(BRIDGE_NAME) < 0) {
+       dns = gateway;
+       if (__connman_dnsproxy_add_listener(index) < 0) {
                connman_error("Can't add listener %s to DNS proxy",
                                                                BRIDGE_NAME);
                dns = BRIDGE_DNS;
        }
 
-       tethering_dhcp_server =
-               dhcp_server_start(BRIDGE_NAME,
-                                       BRIDGE_IP, BRIDGE_SUBNET,
-                                       BRIDGE_IP_START, BRIDGE_IP_END,
-                                       24 * 3600, dns);
+       tethering_dhcp_server = dhcp_server_start(BRIDGE_NAME,
+                                               gateway, subnet_mask,
+                                               start_ip, end_ip,
+                                               24 * 3600, dns);
        if (tethering_dhcp_server == NULL) {
-               disable_bridge(BRIDGE_NAME);
-               remove_bridge(BRIDGE_NAME);
+               __connman_bridge_disable(BRIDGE_NAME);
+               __connman_ippool_unref(dhcp_ippool);
+               __connman_bridge_remove(BRIDGE_NAME);
+               __sync_fetch_and_sub(&tethering_enabled, 1);
                return;
        }
 
-       enable_nat(default_interface);
+       prefixlen =
+               __connman_ipconfig_netmask_prefix_len(subnet_mask);
+       __connman_nat_enable(BRIDGE_NAME, start_ip, prefixlen);
 
        DBG("tethering started");
 }
 
 void __connman_tethering_set_disabled(void)
 {
+       int index;
+
        DBG("enabled %d", tethering_enabled - 1);
 
-       __connman_dnsproxy_remove_listener(BRIDGE_NAME);
+       index = connman_inet_ifindex(BRIDGE_NAME);
+       __connman_dnsproxy_remove_listener(index);
 
        if (__sync_fetch_and_sub(&tethering_enabled, 1) != 1)
                return;
 
-       disable_nat(default_interface);
+       __connman_nat_disable(BRIDGE_NAME);
 
        dhcp_server_stop(tethering_dhcp_server);
 
        tethering_dhcp_server = NULL;
 
-       disable_bridge(BRIDGE_NAME);
-
-       remove_bridge(BRIDGE_NAME);
-
-       DBG("tethering stopped");
-}
-
-void __connman_tethering_update_interface(const char *interface)
-{
-       DBG("interface %s", interface);
-
-       g_free(default_interface);
-
-       if (interface == NULL) {
-               disable_nat(interface);
-               default_interface = NULL;
-
-               return;
-       }
+       __connman_bridge_disable(BRIDGE_NAME);
 
-       default_interface = g_strdup(interface);
+       __connman_ippool_unref(dhcp_ippool);
 
-       __sync_synchronize();
-       if (tethering_enabled == 0)
-               return;
+       __connman_bridge_remove(BRIDGE_NAME);
 
-       enable_nat(interface);
+       DBG("tethering stopped");
 }
 
 static void setup_tun_interface(unsigned int flags, unsigned change,
@@ -427,6 +287,9 @@ static void setup_tun_interface(unsigned int flags, unsigned change,
        struct connman_private_network *pn = data;
        unsigned char prefixlen;
        DBusMessageIter array, dict;
+       const char *server_ip;
+       const char *peer_ip;
+       const char *subnet_mask;
        int err;
 
        DBG("index %d flags %d change %d", pn->index,  flags, change);
@@ -434,22 +297,24 @@ static void setup_tun_interface(unsigned int flags, unsigned change,
        if (flags & IFF_UP)
                return;
 
+       subnet_mask = __connman_ippool_get_subnet_mask(pn->pool);
+       server_ip = __connman_ippool_get_start_ip(pn->pool);
+       peer_ip = __connman_ippool_get_end_ip(pn->pool);
        prefixlen =
-               __connman_ipconfig_netmask_prefix_len(PRIVATE_NETWORK_NETMASK);
+               __connman_ipconfig_netmask_prefix_len(subnet_mask);
 
        if ((__connman_inet_modify_address(RTM_NEWADDR,
                                NLM_F_REPLACE | NLM_F_ACK, pn->index, AF_INET,
-                               pn->server_ip, pn->peer_ip,
-                               prefixlen, NULL)) < 0) {
+                               server_ip, peer_ip, prefixlen, NULL)) < 0) {
                DBG("address setting failed");
                return;
        }
 
        connman_inet_ifup(pn->index);
 
-       err = enable_nat(default_interface);
+       err = __connman_nat_enable(BRIDGE_NAME, server_ip, prefixlen);
        if (err < 0) {
-               connman_error("failed to enable NAT on %s", default_interface);
+               connman_error("failed to enable NAT");
                goto error;
        }
 
@@ -461,9 +326,9 @@ static void setup_tun_interface(unsigned int flags, unsigned change,
        connman_dbus_dict_open(&array, &dict);
 
        connman_dbus_dict_append_basic(&dict, "ServerIPv4",
-                                       DBUS_TYPE_STRING, &pn->server_ip);
+                                       DBUS_TYPE_STRING, &server_ip);
        connman_dbus_dict_append_basic(&dict, "PeerIPv4",
-                                       DBUS_TYPE_STRING, &pn->peer_ip);
+                                       DBUS_TYPE_STRING, &peer_ip);
        connman_dbus_dict_append_basic(&dict, "PrimaryDNS",
                                        DBUS_TYPE_STRING, &pn->primary_dns);
        connman_dbus_dict_append_basic(&dict, "SecondaryDNS",
@@ -488,8 +353,9 @@ static void remove_private_network(gpointer user_data)
 {
        struct connman_private_network *pn = user_data;
 
-       disable_nat(default_interface);
+       __connman_nat_disable(BRIDGE_NAME);
        connman_rtnl_remove_watch(pn->iface_watch);
+       __connman_ippool_unref(pn->pool);
 
        if (pn->watch > 0) {
                g_dbus_remove_watch(connection, pn->watch);
@@ -504,7 +370,7 @@ static void remove_private_network(gpointer user_data)
        g_free(pn);
 }
 
-static void owner_disconnect(DBusConnection *connection, void *user_data)
+static void owner_disconnect(DBusConnection *conn, void *user_data)
 {
        struct connman_private_network *pn = user_data;
 
@@ -515,6 +381,15 @@ static void owner_disconnect(DBusConnection *connection, void *user_data)
        g_hash_table_remove(pn_hash, pn->path);
 }
 
+static void ippool_disconnect(struct connman_ippool *pool, void *user_data)
+{
+       struct connman_private_network *pn = user_data;
+
+       DBG("block used externally");
+
+       g_hash_table_remove(pn_hash, pn->path);
+}
+
 int __connman_private_network_request(DBusMessage *msg, const char *owner)
 {
        struct connman_private_network *pn;
@@ -566,8 +441,12 @@ int __connman_private_network_request(DBusMessage *msg, const char *owner)
        pn->fd = fd;
        pn->interface = iface;
        pn->index = index;
-       pn->server_ip = PRIVATE_NETWORK_IP;
-       pn->peer_ip = PRIVATE_NETWORK_PEER_IP;
+       pn->pool = __connman_ippool_create(pn->index, 1, 1, ippool_disconnect, pn);
+       if (pn->pool == NULL) {
+               errno = -ENOMEM;
+               goto error;
+       }
+
        pn->primary_dns = PRIVATE_NETWORK_PRIMARY_DNS;
        pn->secondary_dns = PRIVATE_NETWORK_SECONDARY_DNS;
 
@@ -622,8 +501,9 @@ void __connman_tethering_cleanup(void)
        if (tethering_enabled == 0) {
                if (tethering_dhcp_server)
                        dhcp_server_stop(tethering_dhcp_server);
-               disable_bridge(BRIDGE_NAME);
-               remove_bridge(BRIDGE_NAME);
+               __connman_bridge_disable(BRIDGE_NAME);
+               __connman_bridge_remove(BRIDGE_NAME);
+               __connman_nat_disable(BRIDGE_NAME);
        }
 
        if (connection == NULL)
index 038d44b..1c67aaa 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
 #include <errno.h>
 
 #include <glib.h>
+#include <stdlib.h>
+#include <gweb/gresolv.h>
+#include <netdb.h>
 
 #include "connman.h"
 
-static GSList *driver_list = NULL;
-static GHashTable *server_hash = NULL;
+static GSList *ts_list = NULL;
 
-static gint compare_priority(gconstpointer a, gconstpointer b)
+static GResolv *resolv = NULL;
+static int resolv_id = 0;
+
+static void resolv_debug(const char *str, void *data)
+{
+       connman_info("%s: %s\n", (const char *) data, str);
+}
+static void save_timeservers(char **servers)
 {
-       const struct connman_timeserver_driver *driver1 = a;
-       const struct connman_timeserver_driver *driver2 = b;
+       GKeyFile *keyfile;
+       int cnt;
+
+       keyfile = __connman_storage_load_global();
+       if (keyfile == NULL)
+               keyfile = g_key_file_new();
+
+       for (cnt = 0; servers != NULL && servers[cnt] != NULL; cnt++);
+
+       g_key_file_set_string_list(keyfile, "global", "Timeservers",
+                          (const gchar **)servers, cnt);
 
-       return driver2->priority - driver1->priority;
+       __connman_storage_save_global(keyfile);
+
+       g_key_file_free(keyfile);
+
+       return;
 }
 
-/**
- * connman_timeserver_driver_register:
- * @driver: timeserver driver definition
- *
- * Register a new timeserver driver
- *
- * Returns: %0 on success
+static char **load_timeservers(void)
+{
+       GKeyFile *keyfile;
+       char **servers = NULL;
+
+       keyfile = __connman_storage_load_global();
+       if (keyfile == NULL)
+               return NULL;
+
+       servers = g_key_file_get_string_list(keyfile, "global",
+                                               "Timeservers", NULL, NULL);
+
+       g_key_file_free(keyfile);
+
+       return servers;
+}
+
+static void resolv_result(GResolvResultStatus status, char **results, gpointer user_data)
+{
+       int i;
+
+       DBG("status %d", status);
+
+       if (status == G_RESOLV_RESULT_STATUS_SUCCESS) {
+               if (results != NULL) {
+                       for (i = 0; results[i]; i++)
+                               DBG("result: %s", results[i]);
+
+                       __connman_ntp_start(results[0]);
+
+                       return;
+               }
+       }
+
+       /* If resolving fails, move to the next server */
+       __connman_timeserver_sync_next();
+}
+
+/*
+ * Once the timeserver list (ts_list) is created, we start querying the
+ * servers one by one. If resolving fails on one of them, we move to the
+ * next one. The user can enter either an IP address or a URL for the
+ * timeserver. We only resolve the urls. Once we have a IP for the NTP
+ * server, we start querying it for time corrections.
  */
-int connman_timeserver_driver_register(struct connman_timeserver_driver *driver)
+void __connman_timeserver_sync_next()
 {
-       DBG("driver %p name %s", driver, driver->name);
+       char *server;
 
-       driver_list = g_slist_insert_sorted(driver_list, driver,
-                                                       compare_priority);
+       __connman_ntp_stop();
 
-       return 0;
+       /* Get the 1st server in the list */
+       if (ts_list == NULL)
+               return;
+
+       server = ts_list->data;
+
+       ts_list = g_slist_delete_link(ts_list, ts_list);
+
+       /* if its a IP , directly query it. */
+       if (connman_inet_check_ipaddress(server) > 0) {
+               DBG("Using timeserver %s", server);
+
+               __connman_ntp_start(server);
+
+               g_free(server);
+               return;
+       }
+
+       DBG("Resolving server %s", server);
+
+       resolv_id = g_resolv_lookup_hostname(resolv, server,
+                                               resolv_result, NULL);
+
+       g_free(server);
+
+       return;
 }
 
-/**
- * connman_timeserver_driver_unregister:
- * @driver: timeserver driver definition
- *
- * Remove a previously registered timeserver driver
+GSList *__connman_timeserver_add_list(GSList *server_list,
+               const char *timeserver)
+{
+       GSList *list = server_list;
+
+       if (timeserver == NULL)
+               return server_list;
+
+       while (list != NULL) {
+               char *existing_server = list->data;
+               if (strcmp(timeserver, existing_server) == 0)
+                       return server_list;
+               list = g_slist_next(list);
+       }
+       return g_slist_prepend(server_list, g_strdup(timeserver));
+}
+
+/*
+ * __connman_timeserver_get_all function creates the timeserver
+ * list which will be used to determine NTP server for time corrections.
+ * The service settings take priority over the global timeservers.
  */
-void connman_timeserver_driver_unregister(struct connman_timeserver_driver *driver)
+GSList *__connman_timeserver_get_all(struct connman_service *service)
 {
-       DBG("driver %p name %s", driver, driver->name);
+       GSList *list = NULL;
+       struct connman_network *network;
+       char **timeservers;
+       char **service_ts;
+       char **service_ts_config;
+       const char *service_gw;
+       char **fallback_ts;
+       int index, i;
+
+       service_ts_config = connman_service_get_timeservers_config(service);
+
+       /* First add Service Timeservers.Configuration to the list */
+       for (i = 0; service_ts_config != NULL && service_ts_config[i] != NULL;
+                       i++)
+               list = __connman_timeserver_add_list(list,
+                               service_ts_config[i]);
+
+       service_ts = connman_service_get_timeservers(service);
+
+       /* First add Service Timeservers via DHCP to the list */
+       for (i = 0; service_ts != NULL && service_ts[i] != NULL; i++)
+               list = __connman_timeserver_add_list(list, service_ts[i]);
+
+       network = __connman_service_get_network(service);
+       if (network != NULL) {
+               index = connman_network_get_index(network);
+               service_gw = __connman_ipconfig_get_gateway_from_index(index,
+                       CONNMAN_IPCONFIG_TYPE_ALL);
+
+               /* Then add Service Gateway to the list */
+               if (service_gw != NULL)
+                       list = __connman_timeserver_add_list(list, service_gw);
+       }
+
+       /* Then add Global Timeservers to the list */
+       timeservers = load_timeservers();
+
+       for (i = 0; timeservers != NULL && timeservers[i] != NULL; i++)
+               list = __connman_timeserver_add_list(list, timeservers[i]);
+
+       g_strfreev(timeservers);
 
-       driver_list = g_slist_remove(driver_list, driver);
+       fallback_ts = connman_setting_get_string_list("FallbackTimeservers");
+
+       /* Lastly add the fallback servers */
+       for (i = 0; fallback_ts != NULL && fallback_ts[i] != NULL; i++)
+               list = __connman_timeserver_add_list(list, fallback_ts[i]);
+
+       return g_slist_reverse(list);
 }
 
-/**
- * connman_timeserver_append:
- * @server: server address
- *
- * Append time server server address to current list
+/*
+ * This function must be called everytime the default service changes, the
+ * service timeserver(s) or gatway changes or the global timeserver(s) changes.
  */
-int connman_timeserver_append(const char *server)
+int __connman_timeserver_sync(struct connman_service *default_service)
 {
-       GSList *list;
+#if defined TIZEN_EXT
+       /* Tizen updates time (ntp) by system service */
+
+       return 0;
+#else
+       struct connman_service *service;
 
-       DBG("server %s", server);
+       if (default_service != NULL)
+               service = default_service;
+       else
+               service = __connman_service_get_default();
 
-       if (server == NULL)
+       if (service == NULL)
                return -EINVAL;
 
-       /* This server is already handled by a driver */
-       if (g_hash_table_lookup(server_hash, server))
+       if (resolv == NULL)
                return 0;
+       /*
+        * Before we start creating the new timeserver list we must stop
+        * any ongoing ntp query and server resolution.
+        */
 
-       for (list = driver_list; list; list = list->next) {
-               struct connman_timeserver_driver *driver = list->data;
-               char *new_server;
+       __connman_ntp_stop();
 
-               if (driver->append == NULL)
-                       continue;
+       if (resolv_id > 0)
+               g_resolv_cancel_lookup(resolv, resolv_id);
 
-               new_server = g_strdup(server);
-               if (new_server == NULL)
-                       return -ENOMEM;
+       g_slist_free_full(ts_list, g_free);
 
-               if (driver->append(server) == 0) {
-                       g_hash_table_insert(server_hash, new_server, driver);
-                       return 0;
-               } else {
-                       g_free(new_server);
-               }
+       ts_list = __connman_timeserver_get_all(service);
+
+       __connman_service_timeserver_changed(service, ts_list);
+
+       if (ts_list == NULL) {
+               DBG("No timeservers set.");
+               return 0;
        }
 
-       return -ENOENT;
+        __connman_timeserver_sync_next();
+
+       return 0;
+#endif
 }
 
-/**
- * connman_timeserver_remove:
- * @server: server address
- *
- * Remover time server server address from current list
- */
-int connman_timeserver_remove(const char *server)
+static int timeserver_start(struct connman_service *service)
 {
-       struct connman_timeserver_driver *driver;
+       char **nameservers;
+       int i;
 
-       DBG("server %s", server);
+       DBG("service %p", service);
 
-       if (server == NULL)
+       i = __connman_service_get_index(service);
+       if (i < 0)
                return -EINVAL;
 
-       driver = g_hash_table_lookup(server_hash, server);
-       if (driver == NULL)
+       nameservers = connman_service_get_nameservers(service);
+       if (nameservers == NULL)
                return -EINVAL;
 
-       g_hash_table_remove(server_hash, server);
+       /* Stop an already ongoing resolution, if there is one */
+       if (resolv != NULL && resolv_id > 0)
+               g_resolv_cancel_lookup(resolv, resolv_id);
+
+       /* get rid of the old resolver */
+       if (resolv != NULL) {
+               g_resolv_unref(resolv);
+               resolv = NULL;
+       }
+
+       resolv = g_resolv_new(i);
+       if (resolv == NULL) {
+               g_strfreev(nameservers);
+               return -ENOMEM;
+       }
 
-       if (driver->remove == NULL)
-               return -ENOENT;
+       if (getenv("CONNMAN_RESOLV_DEBUG"))
+               g_resolv_set_debug(resolv, resolv_debug, "RESOLV");
 
-       return driver->remove(server);
+       for (i = 0; nameservers[i] != NULL; i++)
+               g_resolv_add_nameserver(resolv, nameservers[i], 53, 0);
+
+       g_strfreev(nameservers);
+
+       return __connman_timeserver_sync(service);
 }
 
-void connman_timeserver_sync(void)
+static void timeserver_stop(void)
 {
-       GSList *list;
+       DBG(" ");
 
-       DBG("");
+       if (resolv != NULL) {
+               g_resolv_unref(resolv);
+               resolv = NULL;
+       }
 
-       for (list = driver_list; list; list = list->next) {
-               struct connman_timeserver_driver *driver = list->data;
+       g_slist_free_full(ts_list, g_free);
 
-               if (driver->sync == NULL)
-                       continue;
+       ts_list = NULL;
 
-               driver->sync();
-       }
+       __connman_ntp_stop();
+}
+
+int __connman_timeserver_system_set(char **servers)
+{
+       save_timeservers(servers);
+
+       __connman_timeserver_sync(NULL);
+
+       return 0;
 }
 
+char **__connman_timeserver_system_get()
+{
+       char **servers;
+
+       servers = load_timeservers();
+       return servers;
+}
+
+static void default_changed(struct connman_service *default_service)
+{
+       if (default_service != NULL)
+               timeserver_start(default_service);
+       else
+               timeserver_stop();
+}
+
+static struct connman_notifier timeserver_notifier = {
+       .name                   = "timeserver",
+       .default_changed        = default_changed,
+};
+
 int __connman_timeserver_init(void)
 {
        DBG("");
 
-       server_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
-                                               g_free, NULL);
+       connman_notifier_register(&timeserver_notifier);
 
        return 0;
 }
@@ -169,5 +367,5 @@ void __connman_timeserver_cleanup(void)
 {
        DBG("");
 
-       g_hash_table_destroy(server_hash);
+       connman_notifier_unregister(&timeserver_notifier);
 }
index 37d71a0..e0cec0f 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
@@ -157,9 +157,11 @@ static char *find_origin(void *src_map, struct stat *src_st,
        DIR *dir;
        struct dirent *d;
        char *str, pathname[PATH_MAX];
+       struct stat buf;
+       int ret;
 
        if (subpath == NULL)
-               strncpy(pathname, basepath, sizeof(pathname));
+               strncpy(pathname, basepath, sizeof(pathname) - 1);
        else
                snprintf(pathname, sizeof(pathname),
                                        "%s/%s", basepath, subpath);
@@ -192,6 +194,17 @@ static char *find_origin(void *src_map, struct stat *src_st,
                                return str;
                        }
                        break;
+               case DT_UNKNOWN:
+                       /*
+                        * If there is no d_type support use fstatat()
+                        * to check if d_name is directory
+                        */
+                       ret = fstatat(dirfd(dir), d->d_name, &buf, 0);
+                       if (ret < 0)
+                               continue;
+                       if ((buf.st_mode & S_IFDIR) == 0)
+                               continue;
+                       /* fall through */
                case DT_DIR:
                        if (subpath == NULL)
                                strncpy(pathname, d->d_name, sizeof(pathname));
index aec8d78..1451c74 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
index 64b9070..54432bb 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
@@ -53,9 +53,15 @@ enum connman_wispr_result {
        CONNMAN_WISPR_RESULT_FAILED  = 3,
 };
 
+struct wispr_route {
+       char *address;
+       int if_index;
+};
+
 struct connman_wispr_portal_context {
        struct connman_service *service;
        enum connman_ipconfig_type type;
+       struct connman_wispr_portal *wispr_portal;
 
        /* Portal/WISPr common */
        GWeb *web;
@@ -64,6 +70,8 @@ struct connman_wispr_portal_context {
 
        const char *status_url;
 
+       char *redirect_url;
+
        /* WISPr specific */
        GWebParser *wispr_parser;
        struct connman_wispr_message wispr_msg;
@@ -73,6 +81,10 @@ struct connman_wispr_portal_context {
        char *wispr_formdata;
 
        enum connman_wispr_result wispr_result;
+
+       GSList *route_list;
+
+       guint timeout;
 };
 
 struct connman_wispr_portal {
@@ -83,6 +95,9 @@ struct connman_wispr_portal {
 static gboolean wispr_portal_web_result(GWebResult *result, gpointer user_data);
 
 static GHashTable *wispr_portal_list = NULL;
+#if defined TIZEN_EXT
+static struct connman_wispr_portal_context *browser_request_wp_context = NULL;
+#endif
 
 static void connman_wispr_message_init(struct connman_wispr_message *msg)
 {
@@ -113,14 +128,50 @@ static void connman_wispr_message_init(struct connman_wispr_message *msg)
        msg->location_name = NULL;
 }
 
+static void free_wispr_routes(struct connman_wispr_portal_context *wp_context)
+{
+       while (wp_context->route_list != NULL) {
+               struct wispr_route *route = wp_context->route_list->data;
+
+               DBG("free route to %s if %d type %d", route->address,
+                               route->if_index, wp_context->type);
+
+               switch(wp_context->type) {
+               case CONNMAN_IPCONFIG_TYPE_IPV4:
+                       connman_inet_del_host_route(route->if_index,
+                                       route->address);
+                       break;
+               case CONNMAN_IPCONFIG_TYPE_IPV6:
+                       connman_inet_del_ipv6_host_route(route->if_index,
+                                       route->address);
+                       break;
+               case CONNMAN_IPCONFIG_TYPE_UNKNOWN:
+                       break;
+               }
+
+               g_free(route->address);
+               g_free(route);
+
+               wp_context->route_list =
+                       g_slist_delete_link(wp_context->route_list,
+                                       wp_context->route_list);
+       }
+}
+
 static void free_connman_wispr_portal_context(struct connman_wispr_portal_context *wp_context)
 {
-       DBG("");
+       DBG("context %p", wp_context);
 
        if (wp_context == NULL)
                return;
 
-       connman_service_unref(wp_context->service);
+       if (wp_context->wispr_portal != NULL) {
+               if (wp_context->wispr_portal->ipv4_context == wp_context)
+                       wp_context->wispr_portal->ipv4_context = NULL;
+
+               if (wp_context->wispr_portal->ipv6_context == wp_context)
+                       wp_context->wispr_portal->ipv6_context = NULL;
+       }
 
        if (wp_context->token > 0)
                connman_proxy_lookup_cancel(wp_context->token);
@@ -128,18 +179,38 @@ static void free_connman_wispr_portal_context(struct connman_wispr_portal_contex
        if (wp_context->request_id > 0)
                g_web_cancel_request(wp_context->web, wp_context->request_id);
 
-       g_web_unref(wp_context->web);
+       if (wp_context->timeout > 0)
+               g_source_remove(wp_context->timeout);
+
+       if (wp_context->web != NULL)
+               g_web_unref(wp_context->web);
+
+       g_free(wp_context->redirect_url);
+
+       if (wp_context->wispr_parser != NULL)
+               g_web_parser_unref(wp_context->wispr_parser);
 
-       g_web_parser_unref(wp_context->wispr_parser);
        connman_wispr_message_init(&wp_context->wispr_msg);
 
        g_free(wp_context->wispr_username);
        g_free(wp_context->wispr_password);
        g_free(wp_context->wispr_formdata);
 
+       free_wispr_routes(wp_context);
+
+#if defined TIZEN_EXT
+       if (browser_request_wp_context == wp_context) {
+               browser_request_wp_context = NULL;
+       }
+#endif
        g_free(wp_context);
 }
 
+static struct connman_wispr_portal_context *create_wispr_portal_context(void)
+{
+       return g_try_new0(struct connman_wispr_portal_context, 1);
+}
+
 static void free_connman_wispr_portal(gpointer data)
 {
        struct connman_wispr_portal *wispr_portal = data;
@@ -343,7 +414,7 @@ static void xml_wispr_parser_callback(const char *str, gpointer user_data)
        result = g_markup_parse_context_parse(parser_context,
                                        str, strlen(str), NULL);
        if (result == TRUE)
-               result = g_markup_parse_context_end_parse(parser_context, NULL);
+               g_markup_parse_context_end_parse(parser_context, NULL);
 
        g_markup_parse_context_free(parser_context);
 }
@@ -363,6 +434,8 @@ static void wispr_portal_error(struct connman_wispr_portal_context *wp_context)
 static void portal_manage_status(GWebResult *result,
                        struct connman_wispr_portal_context *wp_context)
 {
+       struct connman_service *service = wp_context->service;
+       enum connman_ipconfig_type type = wp_context->type;
        const char *str = NULL;
 
        DBG("");
@@ -380,9 +453,57 @@ static void portal_manage_status(GWebResult *result,
                                &str) == TRUE)
                connman_info("Client-Region: %s", str);
 
-       __connman_service_ipconfig_indicate_state(wp_context->service,
-                                               CONNMAN_SERVICE_STATE_ONLINE,
-                                               wp_context->type);
+       free_connman_wispr_portal_context(wp_context);
+
+       __connman_service_ipconfig_indicate_state(service,
+                                       CONNMAN_SERVICE_STATE_ONLINE, type);
+}
+
+static gboolean wispr_route_request(const char *address, int ai_family,
+               int if_index, gpointer user_data)
+{
+       int result = -1;
+       struct connman_wispr_portal_context *wp_context = user_data;
+       const char *gateway;
+       struct wispr_route *route;
+
+       gateway = __connman_ipconfig_get_gateway_from_index(if_index,
+               wp_context->type);
+
+       DBG("address %s if %d gw %s", address, if_index, gateway);
+
+       if (gateway == NULL)
+               return FALSE;
+
+       route = g_try_new0(struct wispr_route, 1);
+       if (route == 0) {
+               DBG("could not create struct");
+               return FALSE;
+       }
+
+       switch(wp_context->type) {
+       case CONNMAN_IPCONFIG_TYPE_IPV4:
+               result = connman_inet_add_host_route(if_index, address,
+                               gateway);
+               break;
+       case CONNMAN_IPCONFIG_TYPE_IPV6:
+               result = connman_inet_add_ipv6_host_route(if_index, address,
+                               gateway);
+               break;
+       case CONNMAN_IPCONFIG_TYPE_UNKNOWN:
+               break;
+       }
+
+       if (result < 0) {
+               g_free(route);
+               return FALSE;
+       }
+
+       route->address = g_strdup(address);
+       route->if_index = if_index;
+       wp_context->route_list = g_slist_prepend(wp_context->route_list, route);
+
+       return TRUE;
 }
 
 static void wispr_portal_request_portal(struct connman_wispr_portal_context *wp_context)
@@ -391,7 +512,9 @@ static void wispr_portal_request_portal(struct connman_wispr_portal_context *wp_
 
        wp_context->request_id = g_web_request_get(wp_context->web,
                                        wp_context->status_url,
-                                       wispr_portal_web_result, wp_context);
+                                       wispr_portal_web_result,
+                                       wispr_route_request,
+                                       wp_context);
 
        if (wp_context->request_id == 0)
                wispr_portal_error(wp_context);
@@ -428,14 +551,58 @@ static gboolean wispr_input(const guint8 **data, gsize *length,
        return FALSE;
 }
 
+static void wispr_portal_browser_reply_cb(struct connman_service *service,
+                                       connman_bool_t authentication_done,
+                                       const char *error, void *user_data)
+{
+       struct connman_wispr_portal_context *wp_context = user_data;
+
+       DBG("");
+
+       if (service == NULL || wp_context == NULL)
+               return;
+
+       if (authentication_done == FALSE) {
+#if defined TIZEN_EXT
+               if (wp_context != browser_request_wp_context) {
+                       wp_context = NULL;
+                       return;
+               }
+#endif
+               wispr_portal_error(wp_context);
+               free_wispr_routes(wp_context);
+               return;
+       }
+
+       /* Restarting the test */
+       __connman_wispr_start(service, wp_context->type);
+}
+
 static void wispr_portal_request_wispr_login(struct connman_service *service,
+                               connman_bool_t success,
+                               const char *ssid, int ssid_len,
                                const char *username, const char *password,
-                               void *user_data)
+                               gboolean wps, const char *wpspin,
+                               const char *error, void *user_data)
 {
        struct connman_wispr_portal_context *wp_context = user_data;
 
        DBG("");
 
+       if (error != NULL) {
+               if (g_strcmp0(error,
+                       "net.connman.Agent.Error.LaunchBrowser") == 0) {
+                       if (__connman_agent_request_browser(service,
+                                       wispr_portal_browser_reply_cb,
+                                       wp_context->redirect_url,
+                                       wp_context) == -EINPROGRESS)
+                               return;
+               }
+
+               free_connman_wispr_portal_context(wp_context);
+               return;
+       }
+
        g_free(wp_context->wispr_username);
        wp_context->wispr_username = g_strdup(username);
 
@@ -483,12 +650,19 @@ static gboolean wispr_manage_message(GWebResult *result,
                DBG("Login required");
 
                wp_context->wispr_result = CONNMAN_WISPR_RESULT_LOGIN;
-
-               __connman_service_request_login(wp_context->service);
-
+#if defined TIZEN_EXT
+               if (__connman_agent_request_browser(wp_context->service,
+                               wispr_portal_browser_reply_cb,
+                               wp_context->wispr_msg.login_url,
+                               wp_context) != -EINPROGRESS)
+                       wispr_portal_error(wp_context);
+               else
+                       browser_request_wp_context = wp_context;
+               break;
+#endif
                if (__connman_agent_request_login_input(wp_context->service,
                                        wispr_portal_request_wispr_login,
-                                       wp_context) != -EIO)
+                                       wp_context) != -EINPROGRESS)
                        wispr_portal_error(wp_context);
 
                break;
@@ -520,6 +694,11 @@ static gboolean wispr_manage_message(GWebResult *result,
        return FALSE;
 }
 
+#if defined TIZEN_EXT
+static void internet_unreachable_cb(struct connman_service *service,
+               gboolean retry, void *user_data) {}
+#endif
+
 static gboolean wispr_portal_web_result(GWebResult *result, gpointer user_data)
 {
        struct connman_wispr_portal_context *wp_context = user_data;
@@ -531,9 +710,6 @@ static gboolean wispr_portal_web_result(GWebResult *result, gpointer user_data)
 
        DBG("");
 
-       if (wp_context->request_id == 0)
-               return FALSE;
-
        if (wp_context->wispr_result != CONNMAN_WISPR_RESULT_ONLINE) {
                g_web_result_get_chunk(result, &chunk, &length);
 
@@ -561,31 +737,61 @@ static gboolean wispr_portal_web_result(GWebResult *result, gpointer user_data)
                        break;
 
                if (g_web_result_get_header(result, "X-ConnMan-Status",
-                                                               &str) == TRUE)
+                                               &str) == TRUE) {
                        portal_manage_status(result, wp_context);
+                       return FALSE;
+               }
                else
-                       __connman_service_request_login(wp_context->service);
+                       __connman_agent_request_browser(wp_context->service,
+                                       wispr_portal_browser_reply_cb,
+                                       wp_context->redirect_url, wp_context);
+#if defined TIZEN_EXT
+                       browser_request_wp_context = wp_context;
+#endif
 
                break;
        case 302:
-               if (g_web_result_get_header(result, "Location",
-                                               &redirect) == FALSE)
+               if (g_web_supports_tls() == FALSE ||
+                               g_web_result_get_header(result, "Location",
+                                                       &redirect) == FALSE) {
+
+                       __connman_agent_request_browser(wp_context->service,
+                                       wispr_portal_browser_reply_cb,
+                                       wp_context->status_url, wp_context);
+#if defined TIZEN_EXT
+                       browser_request_wp_context = wp_context;
+#endif
                        break;
+               }
 
                DBG("Redirect URL: %s", redirect);
 
+               wp_context->redirect_url = g_strdup(redirect);
+
                wp_context->request_id = g_web_request_get(wp_context->web,
-                               redirect, wispr_portal_web_result, wp_context);
+                               redirect, wispr_portal_web_result,
+                               wispr_route_request, wp_context);
 
                goto done;
+       case 400:
        case 404:
-               wispr_portal_error(wp_context);
+               if (__connman_service_online_check_failed(wp_context->service,
+                                               wp_context->type) == 0) {
+#if defined TIZEN_EXT
+                       __connman_agent_report_error(wp_context->service,
+                                       "internet-unreachable", internet_unreachable_cb, NULL);
+#endif
+                       wispr_portal_error(wp_context);
+                       free_connman_wispr_portal_context(wp_context);
+                       return FALSE;
+               }
 
                break;
        default:
                break;
        }
 
+       free_wispr_routes(wp_context);
        wp_context->request_id = 0;
 done:
        wp_context->wispr_msg.message_type = -1;
@@ -598,16 +804,18 @@ static void proxy_callback(const char *proxy, void *user_data)
 
        DBG("proxy %s", proxy);
 
-       wp_context->token = 0;
-
-       if (proxy == NULL)
-               proxy = getenv("http_proxy");
+       if (wp_context == NULL)
+               return;
 
-       if (getenv("CONNMAN_WEB_DEBUG"))
-               g_web_set_debug(wp_context->web, web_debug, "WEB");
+       wp_context->token = 0;
 
-       if (proxy != NULL && g_strcmp0(proxy, "DIRECT") != 0)
+       if (proxy != NULL && g_strcmp0(proxy, "DIRECT") != 0) {
+               if (g_str_has_prefix(proxy, "PROXY")) {
+                       proxy += 5;
+                       for (; *proxy == ' ' && *proxy != '\0'; proxy++);
+               }
                g_web_set_proxy(wp_context->web, proxy);
+       }
 
        g_web_set_accept(wp_context->web, NULL);
        g_web_set_user_agent(wp_context->web, "ConnMan/%s wispr", VERSION);
@@ -623,12 +831,26 @@ static void proxy_callback(const char *proxy, void *user_data)
        wispr_portal_request_portal(wp_context);
 }
 
+static gboolean no_proxy_callback(gpointer user_data)
+{
+       struct connman_wispr_portal_context *wp_context = user_data;
+
+       wp_context->timeout = 0;
+
+       proxy_callback("DIRECT", wp_context);
+
+       return FALSE;
+}
+
 static int wispr_portal_detect(struct connman_wispr_portal_context *wp_context)
 {
+       enum connman_service_proxy_method proxy_method;
        enum connman_service_type service_type;
        char *interface = NULL;
+       char **nameservers = NULL;
        int if_index;
        int err = 0;
+       int i;
 
        DBG("wispr/portal context %p", wp_context);
        DBG("service %p", wp_context->service);
@@ -638,7 +860,6 @@ static int wispr_portal_detect(struct connman_wispr_portal_context *wp_context)
        switch (service_type) {
        case CONNMAN_SERVICE_TYPE_ETHERNET:
        case CONNMAN_SERVICE_TYPE_WIFI:
-       case CONNMAN_SERVICE_TYPE_WIMAX:
        case CONNMAN_SERVICE_TYPE_BLUETOOTH:
        case CONNMAN_SERVICE_TYPE_CELLULAR:
                break;
@@ -657,15 +878,29 @@ static int wispr_portal_detect(struct connman_wispr_portal_context *wp_context)
        DBG("interface %s", interface);
 
        if_index = connman_inet_ifindex(interface);
-       if (if_index < 0)
-               return -EINVAL;
+       if (if_index < 0) {
+               DBG("Could not get ifindex");
+               err = -EINVAL;
+               goto done;
+       }
+
+       nameservers = connman_service_get_nameservers(wp_context->service);
+       if (nameservers == NULL) {
+               DBG("Could not get nameservers");
+               err = -EINVAL;
+               goto done;
+       }
 
        wp_context->web = g_web_new(if_index);
        if (wp_context->web == NULL) {
+               DBG("Could not set up GWeb");
                err = -ENOMEM;
                goto done;
        }
 
+       if (getenv("CONNMAN_WEB_DEBUG"))
+               g_web_set_debug(wp_context->web, web_debug, "WEB");
+
        if (wp_context->type == CONNMAN_IPCONFIG_TYPE_IPV4) {
                g_web_set_address_family(wp_context->web, AF_INET);
                wp_context->status_url = STATUS_URL_IPV4;
@@ -674,14 +909,29 @@ static int wispr_portal_detect(struct connman_wispr_portal_context *wp_context)
                wp_context->status_url = STATUS_URL_IPV6;
        }
 
-       wp_context->token = connman_proxy_lookup(interface,
-                                       wp_context->status_url,
-                                       wp_context->service,
-                                       proxy_callback, wp_context);
-       if (wp_context->token == 0)
-               err = -EINVAL;
+       for (i = 0; nameservers[i] != NULL; i++)
+               g_web_add_nameserver(wp_context->web, nameservers[i]);
+
+       proxy_method = connman_service_get_proxy_method(wp_context->service);
+
+       if (proxy_method != CONNMAN_SERVICE_PROXY_METHOD_DIRECT) {
+               wp_context->token = connman_proxy_lookup(interface,
+                                               wp_context->status_url,
+                                               wp_context->service,
+                                               proxy_callback, wp_context);
+
+               if (wp_context->token == 0) {
+                       err = -EINVAL;
+                       free_connman_wispr_portal_context(wp_context);
+               }
+       } else if (wp_context->timeout == 0) {
+               wp_context->timeout =
+                       g_timeout_add_seconds(0, no_proxy_callback, wp_context);
+       }
 
 done:
+       g_strfreev(nameservers);
+
        g_free(interface);
        return err;
 }
@@ -695,6 +945,11 @@ int __connman_wispr_start(struct connman_service *service,
 
        DBG("service %p", service);
 
+#if defined TIZEN_EXT
+       if (connman_service_get_type(service) == CONNMAN_SERVICE_TYPE_CELLULAR)
+               return -EPERM;
+#endif
+
        if (wispr_portal_list == NULL)
                return -EINVAL;
 
@@ -724,14 +979,13 @@ int __connman_wispr_start(struct connman_service *service,
        if (wp_context != NULL)
                free_connman_wispr_portal_context(wp_context);
 
-       wp_context = g_try_new0(struct connman_wispr_portal_context, 1);
+       wp_context = create_wispr_portal_context();
        if (wp_context == NULL)
                return -ENOMEM;
 
-       connman_service_ref(service);
-
        wp_context->service = service;
        wp_context->type = type;
+       wp_context->wispr_portal = wispr_portal;
 
        if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
                wispr_portal->ipv4_context = wp_context;
index dd25c7c..4e5834e 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
@@ -147,12 +147,15 @@ int __connman_wpad_start(struct connman_service *service)
                return -EINVAL;
 
        wpad = g_try_new0(struct connman_wpad, 1);
-       if (wpad == NULL)
+       if (wpad == NULL) {
+               g_strfreev(nameservers);
                return -ENOMEM;
+       }
 
        wpad->service = service;
        wpad->resolv = g_resolv_new(index);
        if (wpad->resolv == NULL) {
+               g_strfreev(nameservers);
                g_free(wpad);
                return -ENOMEM;
        }
@@ -163,6 +166,8 @@ int __connman_wpad_start(struct connman_service *service)
        for (i = 0; nameservers[i] != NULL; i++)
                g_resolv_add_nameserver(wpad->resolv, nameservers[i], 53, 0);
 
+       g_strfreev(nameservers);
+
        wpad->hostname = g_strdup_printf("wpad.%s", domainname);
 
        DBG("hostname %s", wpad->hostname);
diff --git a/test/connect-service b/test/connect-service
deleted file mode 100755 (executable)
index 8621602..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-#!/usr/bin/python
-
-import sys
-import dbus
-
-if (len(sys.argv) < 2):
-       print "Usage: %s <ssid> [passphrase] [security]" % (sys.argv[0])
-       sys.exit(1)
-
-bus = dbus.SystemBus()
-
-manager = dbus.Interface(bus.get_object("net.connman", "/"),
-                                       "net.connman.Manager")
-
-print "Attempting to connect service %s" % (sys.argv[1])
-
-if len(sys.argv) > 2:
-       if len(sys.argv) > 3:
-               security = sys.argv[3]
-       else:
-               security = "rsn"
-       passphrase = sys.argv[2]
-else:
-       security = "none"
-       passphrase = ""
-
-path = manager.ConnectService(({ "Type": "wifi", "Mode": "managed",
-                                       "SSID": sys.argv[1],
-                                       "Security": security,
-                                       "Passphrase": passphrase }),
-                                               timeout=60000);
-print "Service path is %s" %(path)
index 8743523..d9c56a0 100755 (executable)
@@ -5,6 +5,7 @@ import dbus
 
 if (len(sys.argv) < 2):
        print "Usage: %s type" % (sys.argv[0])
+       sys.exit(1)
 
 bus = dbus.SystemBus()
 
@@ -27,14 +28,13 @@ def technology_disable_tethering(path, tech_type):
                        else:
                                return None
 
-properties = manager.GetProperties()
+technologies = manager.GetTechnologies()
+tech = None
 
-for key in properties.keys():
-       if key in ["Technologies"]:
-               for path in properties[key]:
-                       tech = technology_disable_tethering(path, sys.argv[1])
-                       if tech != None:
-                               break;
+for path,_ in technologies:
+       tech = technology_disable_tethering(path, sys.argv[1])
+       if tech != None:
+               break;
 
 if tech == None:
        print "Failed to disable %s tethering" % (sys.argv[1])
index cdb037f..c9668a1 100755 (executable)
@@ -3,11 +3,12 @@
 import sys
 import dbus
 
-if (len(sys.argv) < 4 and sys.argv[1] == "wifi"):
+if (len(sys.argv) >= 2 and len(sys.argv) < 4 and sys.argv[1] == "wifi"):
        print "Usage: %s wifi [SSID] [passphrase]" % (sys.argv[0])
        sys.exit(1)
 elif (len(sys.argv) < 2):
        print "Usage: %s type" % (sys.argv[0])
+       sys.exit(1)
 
 bus = dbus.SystemBus()
 
@@ -36,20 +37,18 @@ def technology_enable_tethering(path, tech_type, ssid, psk):
                        else:
                                return None
 
-properties = manager.GetProperties()
+technologies = manager.GetTechnologies()
+tech = None
 
-for key in properties.keys():
-       if key in ["Technologies"]:
-               for path in properties[key]:
-                       if (len(sys.argv) == 4):
-                               tech = technology_enable_tethering(path,
+for path,_ in technologies:
+       if (len(sys.argv) == 4):
+               tech = technology_enable_tethering(path,
                                        sys.argv[1], sys.argv[2], sys.argv[3])
-                       else:
-                               tech = technology_enable_tethering(path,
-                                                       sys.argv[1], "", "")
+       else:
+               tech = technology_enable_tethering(path, sys.argv[1], "", "")
 
-                       if tech != None:
-                               break;
+       if tech != None:
+               break;
 
 if tech == None:
        print "Failed to enable %s tethering" % (sys.argv[1])
diff --git a/test/find-service b/test/find-service
deleted file mode 100755 (executable)
index c53e6eb..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/usr/bin/python
-
-import sys
-import dbus
-
-if (len(sys.argv) < 2):
-       print "Usage: %s <pattern>" % (sys.argv[0])
-       sys.exit(1)
-
-bus = dbus.SystemBus()
-
-manager = dbus.Interface(bus.get_object('net.connman', '/'),
-                                       'net.connman.Manager')
-
-path = manager.LookupService(sys.argv[1])
-
-print "Service is %s" % (path)
diff --git a/test/list-profiles b/test/list-profiles
deleted file mode 100755 (executable)
index b9b9103..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-#!/usr/bin/python
-
-import dbus
-
-bus = dbus.SystemBus()
-
-manager = dbus.Interface(bus.get_object("net.connman", "/"),
-                                       "net.connman.Manager")
-
-properties = manager.GetProperties()
-
-active = properties["ActiveProfile"]
-
-for path in properties["Profiles"]:
-       if (active == path):
-               print "[ %s ]  <== active" % (path)
-       else:
-               print "[ %s ]" % (path)
-
-       profile = dbus.Interface(bus.get_object("net.connman", path),
-                                               "net.connman.Profile")
-
-       properties = profile.GetProperties()
-       for key in properties.keys():
-               if key in ["Services"]:
-                       list = ""
-                       for path in properties["Services"]:
-                               val = str(path)
-                               list = list + val[val.rfind("/") + 1:] + " "
-                       print "    Services = [ %s]" % (list)
-               else:
-                       print "    %s = %s" % (key, properties[key])
-
-       print
index c403f85..6be4945 100755 (executable)
@@ -28,15 +28,11 @@ bus = dbus.SystemBus()
 manager = dbus.Interface(bus.get_object("net.connman", "/"),
                                        "net.connman.Manager")
 
-properties = manager.GetProperties()
-
-for path in properties["Services"]:
+for path, properties in manager.GetServices():
        service = dbus.Interface(bus.get_object("net.connman", path),
                                                "net.connman.Service")
-
-       properties = service.GetProperties()
-
-       print "[ %s ]" % (path)
+       identifier = path[path.rfind("/") + 1:]
+       print "[ %s ]" % (identifier)
 
        for key in properties.keys():
                if key in ["IPv4", "IPv4.Configuration",
@@ -45,8 +41,9 @@ for path in properties["Services"]:
                                                "Ethernet", "Provider"]:
                        val = extract_values(properties[key])
                elif key in ["Nameservers", "Nameservers.Configuration",
-                                       "Domains", "Domains.Configuration",
-                                               "Security"]:
+                            "Domains", "Domains.Configuration",
+                            "Timeservers", "Timeservers.Configuration",
+                            "Security"]:
                        val = extract_list(properties[key])
                elif key in ["Favorite", "Immutable", "AutoConnect",
                                        "LoginRequired", "PassphraseRequired"]:
index 4d44519..1b3b84c 100755 (executable)
@@ -42,9 +42,8 @@ def extract(name, value):
                val = int(value)
        elif name in ["IPv4", "IPv6", "Ethernet", "Proxy" ]:
                val = extract_values(value)
-       elif name in ["Nameservers", "Domains", "Services",
-                     "Update", "Technologies", "AvailableTechnologies",
-                     "EnabledTechnologies", "ConnectedTechnologies" ]:
+       elif name in ["Services", "Technologies",
+                       "Nameservers", "Domains", "Timeservers"]:
                val = extract_list(value)
        else:
                val = str(value)
diff --git a/test/monitor-manager b/test/monitor-manager
deleted file mode 100755 (executable)
index 05e1efd..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-#!/usr/bin/python
-
-import gobject
-
-import dbus
-import dbus.mainloop.glib
-
-def property_changed(name, value):
-       if name in ["Profiles", "Services", "Technologies"]:
-               val = "["
-               for i in value:
-                       val = val + " " + i[i.rfind("/") + 1:]
-               val = val + " ]"
-       elif name in ["AvailableTechnologies", "EnabledTechnologies",
-                                               "ConnectedTechnologies",
-                               "AvailableDebugs", "EnabledDebugs"]:
-               val = "["
-               for i in value:
-                       val = val + " " + i
-               val = val + " ]"
-       elif name in ["Strength", "Priority"]:
-               val = int(value)
-       else:
-               val = str(value)
-       print "%s = %s" % (name, val)
-
-if __name__ == '__main__':
-       dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
-
-       bus = dbus.SystemBus()
-
-       bus.add_signal_receiver(property_changed,
-                               bus_name="net.connman",
-                               dbus_interface="net.connman.Manager",
-                                       signal_name = "PropertyChanged")
-
-       mainloop = gobject.MainLoop()
-       mainloop.run()
index cbae377..9476bf8 100755 (executable)
@@ -25,18 +25,18 @@ def extract_list(list):
 
 def property_changed(name, value, path):
        service = path[path.rfind("/") + 1:]
-       if name in ["Profiles", "Services", "Technologies"]:
+       if name in ["Services"]:
                val = "["
                for i in value:
                        val = val + " " + i[i.rfind("/") + 1:]
                val = val + " ]"
        elif name in ["IPv4", "IPv4.Configuration",
                        "IPv6", "IPv6.Configuration",
-                       "Proxy", "Proxy.Configuration", "Ethernet"]:
+                       "Proxy", "Proxy.Configuration", "Ethernet", "Provider"]:
                val = extract_values(value)
        elif name in ["Nameservers", "Nameservers.Configuration",
-                                       "Domains", "Domains.Configuration"
-                                               "Security"]:
+                       "Domains", "Domains.Configuration",
+                       "Timeservers", "Timeservers.Configuration", "Security"]:
                val = extract_list(value)
        elif name in ["Strength", "Priority"]:
                val = int(value)
@@ -44,6 +44,26 @@ def property_changed(name, value, path):
                val = str(value)
        print "[%s] %s = %s" % (service, name, val)
 
+def services_changed(services, removed):
+       for i in services:
+               service = i[0][i[0].rfind("/") + 1:]
+               print "[%s] changed" % (service)
+               for n in i[1].keys():
+                       property_changed(n, i[1][n], i[0])
+       for i in removed:
+               service = i[i.rfind("/") + 1:]
+               print "[%s] removed" % (service)
+
+def technology_added(path, properties):
+       technology = path[path.rfind("/") + 1:]
+       print "[%s] added" % (technology)
+       for n in properties.keys():
+               property_changed(n, properties[n], technology)
+
+def technology_removed(path):
+       technology = path[path.rfind("/") + 1:]
+       print "[%s] removed" % (technology)
+
 if __name__ == '__main__':
        dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
 
@@ -51,8 +71,35 @@ if __name__ == '__main__':
 
        bus.add_signal_receiver(property_changed,
                                bus_name="net.connman",
+                               dbus_interface="net.connman.Manager",
+                               signal_name="PropertyChanged",
+                               path_keyword="path")
+
+       bus.add_signal_receiver(services_changed,
+                               bus_name="net.connman",
+                               dbus_interface="net.connman.Manager",
+                               signal_name="ServicesChanged")
+
+       bus.add_signal_receiver(property_changed,
+                               bus_name="net.connman",
                                dbus_interface="net.connman.Service",
-                               signal_name = "PropertyChanged",
+                               signal_name="PropertyChanged",
+                               path_keyword="path")
+
+       bus.add_signal_receiver(technology_added,
+                               bus_name="net.connman",
+                               dbus_interface="net.connman.Manager",
+                               signal_name="TechnologyAdded")
+
+       bus.add_signal_receiver(technology_removed,
+                               bus_name="net.connman",
+                               dbus_interface="net.connman.Manager",
+                               signal_name="TechnologyRemoved")
+
+       bus.add_signal_receiver(property_changed,
+                               bus_name="net.connman",
+                               dbus_interface="net.connman.Technology",
+                               signal_name="PropertyChanged",
                                path_keyword="path")
 
        mainloop = gobject.MainLoop()
diff --git a/test/provision-service b/test/provision-service
deleted file mode 100755 (executable)
index a44b862..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-#!/usr/bin/python
-
-import sys
-import dbus
-
-if (len(sys.argv) < 3):
-       print "Usage: %s <ssid> [key=value] ..." % (sys.argv[0])
-       sys.exit(1)
-
-bus = dbus.SystemBus()
-
-manager = dbus.Interface(bus.get_object("net.connman", "/"),
-                                       "net.connman.Manager")
-
-config = unicode("", "utf-8")
-config += "[service_%s]\n" %(sys.argv[1])
-
-for arg in sys.argv:
-       keyval = arg.split("=", 1)
-       if (len(keyval) >= 2):
-               config += arg
-               config += '\n'
-
-config = config.rstrip()
-
-manager.ProvisionService(config);
-
-print "Sent configuration:\n%s" %(config)
diff --git a/test/set-address b/test/set-address
deleted file mode 100755 (executable)
index 9201678..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-#!/usr/bin/python
-
-import sys
-import dbus
-
-if (len(sys.argv) < 3):
-       print "Usage: %s <address> <netmask | prefix length> <gateway> [version]" % (sys.argv[0])
-       sys.exit(1)
-
-bus = dbus.SystemBus()
-
-manager = dbus.Interface(bus.get_object('net.connman', "/"),
-                                       'net.connman.Manager')
-
-properties = manager.GetProperties()
-
-for path in properties["Services"]:
-       service = dbus.Interface(bus.get_object('net.connman', path),
-                                               'net.connman.Service')
-
-       properties = service.GetProperties()
-
-
-
-       if (len(sys.argv) == 5 and sys.argv[4] == "ipv6"):
-               ipv = 6
-       else:
-               ipv = 4
-
-       print "Setting IPv%d address %s for %s" % (ipv, sys.argv[1], path)
-
-       if (ipv == 4):
-               service.SetProperty("IPv4.Configuration",
-                                       { "Method": "manual",
-                                               "Address": sys.argv[1],
-                                               "Netmask": sys.argv[2],
-                                                       "Gateway": sys.argv[3]})
-       else:
-               service.SetProperty("IPv6.Configuration",
-                                       { "Method": "manual",
-                                               "Address": sys.argv[1],
-                                               "PrefixLength": sys.argv[2],
-                                                       "Gateway": sys.argv[3]})
-
-       print
index 820b24b..235113f 100755 (executable)
@@ -3,6 +3,9 @@
 import sys
 import dbus
 
+def make_variant(string):
+       return dbus.String(string, variant_level=1)
+
 def print_usage():
        print "Usage: %s <service> [off|dhcp|manual <address> [netmask] [gateway]]" % (sys.argv[0])
 
@@ -20,13 +23,13 @@ properties = service.GetProperties()
 
 print "Setting method %s for %s" % (sys.argv[2], sys.argv[1])
 
-ipv4_configuration = { "Method": sys.argv[2] }
+ipv4_configuration = { "Method": make_variant(sys.argv[2]) }
 if (len(sys.argv) > 3):
-       ipv4_configuration["Address"] = sys.argv[3]
+       ipv4_configuration["Address"] = make_variant(sys.argv[3])
 if (len(sys.argv) > 4):
-       ipv4_configuration["Netmask"] = sys.argv[4]
+       ipv4_configuration["Netmask"] = make_variant(sys.argv[4])
 if (len(sys.argv) > 5):
-        ipv4_configuration["Gateway"] = sys.argv[5]
+        ipv4_configuration["Gateway"] = make_variant(sys.argv[5])
 
 service.SetProperty("IPv4.Configuration", ipv4_configuration)
 print "New IPv4.Configuration: ", ipv4_configuration
index 7f60b88..4f2de09 100755 (executable)
@@ -3,6 +3,9 @@
 import sys
 import dbus
 
+def make_variant(string):
+       return dbus.String(string, variant_level=1)
+
 def print_usage():
        print "Usage: %s <service> off|manual|auto [<address> [prefixlen] [gateway]] [<privacy>]" % (sys.argv[0])
 
@@ -19,17 +22,17 @@ properties = service.GetProperties()
 
 print "Setting method %s for %s" % (sys.argv[2], sys.argv[1])
 
-ipv6_configuration = { "Method": sys.argv[2] }
+ipv6_configuration = { "Method": make_variant(sys.argv[2])}
 if sys.argv[2] == "auto":
        if (len(sys.argv) > 3):
-               ipv6_configuration["Privacy"] = sys.argv[3]
+               ipv6_configuration["Privacy"] = make_variant(sys.argv[3])
 else:
        if (len(sys.argv) > 3):
-               ipv6_configuration["Address"] = sys.argv[3]
+               ipv6_configuration["Address"] = make_variant(sys.argv[3])
        if (len(sys.argv) > 4):
-               ipv6_configuration["PrefixLength"] = sys.argv[4]
+               ipv6_configuration["PrefixLength"] = make_variant(sys.argv[4])
        if (len(sys.argv) > 5):
-               ipv6_configuration["Gateway"] = sys.argv[5]
+               ipv6_configuration["Gateway"] = make_variant(sys.argv[5])
 
 service.SetProperty("IPv6.Configuration", ipv6_configuration)
 print "New IPv6.Configuration: ", ipv6_configuration
diff --git a/test/set-timeservers b/test/set-timeservers
new file mode 100755 (executable)
index 0000000..19cc938
--- /dev/null
@@ -0,0 +1,20 @@
+#!/usr/bin/python
+
+import sys
+import dbus
+
+if (len(sys.argv) < 2):
+       print "Usage: %s <service> [timeserver*]" % (sys.argv[0])
+       sys.exit(1)
+
+bus = dbus.SystemBus()
+path = "/net/connman/service/" + sys.argv[1]
+service = dbus.Interface(bus.get_object('net.connman', path),
+                                       'net.connman.Service')
+
+properties = service.GetProperties()
+
+print "Setting timeserver to %s" % (sys.argv[2:])
+
+service.SetProperty("Timeservers.Configuration",
+       dbus.Array(sys.argv[2:], signature=dbus.Signature('s')))
index 20e8a50..4b6450f 100755 (executable)
@@ -12,9 +12,9 @@ print object.Introspect()
 manager = dbus.Interface(bus.get_object("net.connman", "/"),
                                        "net.connman.Manager")
 
-properties = manager.GetProperties()
+technologies = manager.GetTechnologies()
 
-for path in properties["Technologies"]:
+for path, properties in technologies:
        object = dbus.Interface(bus.get_object("net.connman", path),
                                        "org.freedesktop.DBus.Introspectable")
 
index d3bb315..45437df 100755 (executable)
@@ -10,7 +10,12 @@ import sys
 class Canceled(dbus.DBusException):
        _dbus_error_name = "net.connman.Error.Canceled"
 
+class LaunchBrowser(dbus.DBusException):
+       _dbus_error_name = "net.connman.Agent.Error.LaunchBrowser"
+
 class Agent(dbus.service.Object):
+       name = None
+       ssid = None
        identity = None
        passphrase = None
        wpspin = None
@@ -27,9 +32,12 @@ class Agent(dbus.service.Object):
                response = {}
 
                if not self.identity and not self.passphrase and not self.wpspin:
+                       print "Service credentials requested, type cancel to cancel"
                        args = raw_input('Answer: ')
 
                        for arg in args.split():
+                               if arg.startswith("cancel"):
+                                       response["Error"] = arg
                                if arg.startswith("Identity="):
                                        identity = arg.replace("Identity=", "", 1)
                                        response["Identity"] = identity
@@ -54,9 +62,13 @@ class Agent(dbus.service.Object):
                response = {}
 
                if not self.username and not self.password:
+                       print "User login requested, type cancel to cancel"
+                       print "or browser to login through the browser by yourself."
                        args = raw_input('Answer: ')
 
                        for arg in args.split():
+                               if arg.startswith("cancel") or arg.startswith("browser"):
+                                       response["Error"] = arg
                                if arg.startswith("Username="):
                                        username = arg.replace("Username=", "", 1)
                                        response["Username"] = username
@@ -71,20 +83,51 @@ class Agent(dbus.service.Object):
 
                return response
 
+       def input_hidden(self):
+               response = {}
+
+               if not self.name and not self.ssid:
+                       args = raw_input('Answer ')
+
+                       for arg in args.split():
+                               if arg.startswith("Name="):
+                                       name = arg.replace("Name=", "", 1)
+                                       response["Name"] = name
+                                       break
+                               if arg.startswith("SSID="):
+                                       ssid = arg.replace("SSID", "", 1)
+                                       response["SSID"] = ssid
+                                       break
+               else:
+                       if self.name:
+                               response["Name"] = self.name
+                       if self.ssid:
+                               response["SSID"] = self.ssid
+
+               return response
+
        @dbus.service.method("net.connman.Agent",
                                        in_signature='oa{sv}',
                                        out_signature='a{sv}')
        def RequestInput(self, path, fields):
                print "RequestInput (%s,%s)" % (path, fields)
 
-               response = None
+               response = {}
 
+               if fields.has_key("Name"):
+                       response.update(self.input_hidden())
                if fields.has_key("Passphrase"):
-                       response = self.input_passphrase()
-               elif fields.has_key("Username"):
-                       response = self.input_username()
-               else:
-                       print "No method to answer the input request"
+                       response.update(self.input_passphrase())
+               if fields.has_key("Username"):
+                       response.update(self.input_username())
+
+               if response.has_key("Error"):
+                       if response["Error"] == "cancel":
+                               raise Canceled("canceled")
+                               return
+                       if response["Error"] == "browser":
+                               raise LaunchBrowser("launch browser")
+                               return
 
                print "returning (%s)" % (response)
 
@@ -93,6 +136,22 @@ class Agent(dbus.service.Object):
        @dbus.service.method("net.connman.Agent",
                                        in_signature='os',
                                        out_signature='')
+       def RequestBrowser(self, path, url):
+               print "RequestBrowser (%s,%s)" % (path, url)
+
+               print "Please login through the given url in a browser"
+               print "Then press enter to accept or some text to cancel"
+
+               args = raw_input('> ')
+
+               if len(args) > 0:
+                       raise Canceled("canceled")
+
+               return
+
+       @dbus.service.method("net.connman.Agent",
+                                       in_signature='os',
+                                       out_signature='')
        def ReportError(self, path, error):
                print "ReportError %s, %s" % (path, error)
                retry = raw_input("Retry service (yes/no): ")
@@ -112,6 +171,8 @@ class Agent(dbus.service.Object):
 
 def print_usage():
        print "Usage:"
+       print "For hidden service:"
+       print "%s Name=<hidden service name> [SSID=<hidden ssid>]" % (sys.argv[0])
        print "For EAP/WPA input:"
        print "%s Identity=<identity> Passphrase=<passphrase> WPS=<wpspin>" % (sys.argv[0])
        print "For WISPr login input:"
@@ -134,7 +195,11 @@ if __name__ == '__main__':
 
        if len(sys.argv) >= 2:
                for arg in sys.argv[1:]:
-                       if arg.startswith("Identity="):
+                       if arg.startswith("Name="):
+                               object.name = arg.replace("Name=", "", 1)
+                       elif arg.startswith("SSID="):
+                               object.ssid = arg.replace("SSID=", "", 1)
+                       elif arg.startswith("Identity="):
                                object.identity = arg.replace("Identity=", "", 1)
                        elif arg.startswith("Passphrase="):
                                object.passphrase = arg.replace("Passphrase=", "", 1)
index 2d9223d..67b0c85 100755 (executable)
@@ -13,29 +13,35 @@ if len(sys.argv) < 2:
        print ""
        print "  state"
        print "  services"
-       print "  passphrase <service> [passphrase]"
        print "  autoconnect <service> [autoconnect]"
        print "  connect <service>"
        print "  disconnect <service>"
        print "  remove <service>"
        print ""
-       print "  scan [type]"
+       print "  scan <type>"
        print "  enable <type>"
        print "  disable <type>"
        print "  offlinemode [on|off]"
        sys.exit(1)
 
 def print_services(services):
-       for path in services:
-               service = dbus.Interface(bus.get_object("net.connman", path),
-                                               "net.connman.Service")
-
-               properties = service.GetProperties()
-
+       for path, properties in services:
                identifier = path[path.rfind("/") + 1:]
+               state = " "
+               autoconnect = "  "
 
                if properties["Favorite"] == dbus.Boolean(1):
                        favorite = "*"
+
+                       if properties["AutoConnect"] == dbus.Boolean(1):
+                               autoconnect = " A"
+                       else:
+                               autoconnect = "  "
+
+                       if properties["State"] == "ready":
+                               state = "R"
+                       elif properties["State"] == "online":
+                               state = "O"
                else:
                        favorite = " "
 
@@ -44,7 +50,8 @@ def print_services(services):
                else:
                        name = "{" + properties["Type"] + "}"
 
-               print "%s %-26s { %s }" % (favorite, name, identifier)
+               print "%s%s%s %-26s { %s }" % (favorite, autoconnect, state,
+                                              name, identifier)
 
 if sys.argv[1] == "state":
        properties = manager.GetProperties()
@@ -52,40 +59,7 @@ if sys.argv[1] == "state":
        print "System is %s" % (properties["State"])
 
 elif sys.argv[1] in ["services", "list", "show"]:
-       properties = manager.GetProperties()
-
-       print_services(properties["Services"])
-
-elif sys.argv[1] in ["passphrase", "pass"]:
-       if (len(sys.argv) < 3):
-               print "Need at least service parameter"
-               sys.exit(1)
-
-       path = "/net/connman/service/" + sys.argv[2]
-
-       service = dbus.Interface(bus.get_object("net.connman", path),
-                                               "net.connman.Service")
-
-       if (len(sys.argv) > 3):
-               passphrase = sys.argv[3]
-
-               service.SetProperty("Passphrase", passphrase);
-
-               print "Passphrase %s set for %s" % (passphrase, sys.argv[2])
-       else:
-               properties = service.GetProperties()
-
-               if "Name" in properties.keys():
-                       name = properties["Name"]
-               else:
-                       name = "{" + properties["Type"] + "}"
-
-               if "Passphrase" in properties.keys():
-                       passphrase = properties["Passphrase"]
-               else:
-                       passphrase = "not set"
-
-               print "Passphrase for %s is %s" % (name, passphrase)
+       print_services(manager.GetServices())
 
 elif sys.argv[1] in ["autoconnect", "autoconn"]:
        if (len(sys.argv) < 3):
@@ -172,21 +146,24 @@ elif sys.argv[1] in ["remove"]:
 
 elif sys.argv[1] == "scan":
        if len(sys.argv) > 2:
-               manager.RequestScan(sys.argv[2])
-       else:
-               manager.RequestScan("")
+               path = "/net/connman/technology/" + sys.argv[2]
+               technology = dbus.Interface(bus.get_object("net.connman", path),
+                                               "net.connman.Technology")
+               technology.Scan()
 
 elif sys.argv[1] == "enable":
        if len(sys.argv) > 2:
-               manager.EnableTechnology(sys.argv[2])
-       else:
-               manager.EnableTechnology("")
+               path = "/net/connman/technology/" + sys.argv[2]
+               technology = dbus.Interface(bus.get_object("net.connman", path),
+                                           "net.connman.Technology")
+               technology.SetProperty("Powered", True)
 
 elif sys.argv[1] == "disable":
        if len(sys.argv) > 2:
-               manager.DisableTechnology(sys.argv[2])
-       else:
-               manager.DisableTechnology("")
+               path = "/net/connman/technology/" + sys.argv[2]
+               technology = dbus.Interface(bus.get_object("net.connman", path),
+                                           "net.connman.Technology")
+               technology.SetProperty("Powered", False)
 
 elif sys.argv[1] in ["offlinemode", "flightmode"]:
        if len(sys.argv) > 2:
index 8bbcd7f..2b4493c 100755 (executable)
@@ -30,93 +30,64 @@ manager = dbus.Interface(bus.get_object("net.connman", "/"),
 
 properties = manager.GetProperties()
 
-def print_properties(key, value):
-       if key == "Profiles":
-               interface = "net.connman.Profile"
-       elif key == "Services":
-               interface = "net.connman.Service"
-       elif key == "Technologies":
-               interface = "net.connman.Technology"
-       else:
-               return
-
-       print "%s" % (key)
-       for path in value:
-               print "    %s" % (path)
-               obj = dbus.Interface(bus.get_object("net.connman", path),
-                                                               interface)
-
-               properties = obj.GetProperties()
-
-               for key in properties.keys():
-                       if key in ["Services", "Technologies"]:
-                               continue
-
-                       elif key in ["Powered", "Scanning", "Connected",
-                                       "Available", "Remember", "Default",
-                                       "Favorite", "Immutable", "AutoConnect",
-                                               "LoginRequired",
-                                               "PassphraseRequired"]:
-                               if properties[key] == dbus.Boolean(1):
-                                       val = "true"
-                               else:
-                                       val = "false"
-
-                       elif key in ["IPv4", "IPv4.Configuration",
-                                       "IPv6", "IPv6.Configuration",
-                                               "Proxy", "Proxy.Configuration",
-                                                       "Ethernet", "Provider"]:
-                               val = extract_values(properties[key])
-
-                       elif key in ["Nameservers", "Nameservers.Configuration",
-                                       "Domains", "Domains.Configuration",
-                                               "Security"]:
-                               val = extract_list(properties[key])
-
-                       elif key in ["Strength", "Priority"]:
-                               val = int(properties[key])
-
-                       elif key in ["Tethering"]:
-                               if properties[key] == dbus.Boolean(1):
-                                       val = "true"
-                               else:
-                                       val = "false"
-                       else:
-                               val = str(properties[key])
-
-                       print "        %s = %s" % (key, val)
-
-               if "Services" in properties.keys():
-                       list = ""
-                       for path in properties["Services"]:
-                               val = str(path)
-                               list = list + val[val.rfind("/") + 1:] + " "
-                       print "        Services = [ %s]" % (list)
-
-
 for key in properties.keys():
-       if key in ["Profiles", "Services", "Technologies"]:
-               print_properties(key, properties[key])
-       elif key in ["AvailableTechnologies", "EnabledTechnologies",
-                                       "ConnectedTechnologies",
-                               "AvailableDebugs", "EnabledDebugs"]:
-               print "%s" % (key)
-               list = ""
-               for val in properties[key]:
-                       list = list + val + " "
-               print "    [ %s]" % (list)
-       elif key in ["OfflineMode", "Tethering"]:
+       if key in ["OfflineMode", "SessionMode"]:
                print "%s" % (key)
                if properties[key] == dbus.Boolean(1):
                        print "    true"
                else:
                        print "    false"
-       elif key in ["DefaultTechnology"]:
-               print "%s" % (key)
-               if properties[key] == "":
-                       print "    <none>"
-               else:
-                       print "    %s" % (properties[key])
        else:
                print "%s" % (key)
                print "    %s" % (properties[key])
+
+print ("Services")
+services = manager.GetServices()
+
+for (path, properties) in services:
+       print "    %s" % (path)
+       for key in properties.keys():
+               if key in ["Available", "Remember", "Default",
+                               "Favorite", "Immutable", "AutoConnect",
+                                       "LoginRequired",
+                                       "PassphraseRequired"]:
+                       if properties[key] == dbus.Boolean(1):
+                               val = "true"
+                       else:
+                               val = "false"
+
+               elif key in ["IPv4", "IPv4.Configuration",
+                               "IPv6", "IPv6.Configuration",
+                                       "Proxy", "Proxy.Configuration",
+                                               "Ethernet", "Provider"]:
+                       val = extract_values(properties[key])
+
+               elif key in ["Nameservers", "Nameservers.Configuration",
+                               "Domains", "Domains.Configuration",
+                                       "Security"]:
+                       val = extract_list(properties[key])
+
+               elif key in ["Strength", "Priority"]:
+                       val = int(properties[key])
+
+               else:
+                       val = str(properties[key])
+
+               print "        %s = %s" % (key, val)
+
+print ("Technologies")
+technologies = manager.GetTechnologies()
+
+for (path, properties) in technologies:
+       print "    %s" % (path)
+       for key in properties.keys():
+
+               if key in ["Connected", "Powered", "Tethering"]:
+                       if properties[key] == dbus.Boolean(1):
+                               val = "true"
+                       else:
+                               val = "false"
+               else:
+                       val = properties[key]
+
+               print "        %s = %s" % (key, val)
index 6c7fa5c..2d82fb6 100755 (executable)
@@ -76,7 +76,6 @@ class SessionApplication(dbus.service.Object):
                        bus.watch_name_owner('net.connman', self.connman_name_owner_changed)
                except dbus.DBusException:
                        traceback.print_exc()
-                       exit(1)
 
        def connman_name_owner_changed(self, proxy):
                try:
@@ -97,7 +96,6 @@ class SessionApplication(dbus.service.Object):
 
                except dbus.DBusException:
                        traceback.print_exc()
-                       exit(1)
 
        def release(self, session_name):
                s = self.find_session(session_name)
@@ -112,8 +110,11 @@ class SessionApplication(dbus.service.Object):
                del self.sessions[session_name]
 
        def type_convert(self, key, value):
-               if key in [ "AllowedBearers", "RoamingPolicy" ]:
+               if key in [ "AllowedBearers" ]:
                        return value
+               elif key in [ "RoamingPolicy", "ConnectionType" ]:
+                       if len(value) > 0:
+                               return value[0]
                elif key in [ "Priority", "AvoidHandover",
                              "StayConnected", "EmergencyCall" ]:
                        flag = str(value[0]).strip().lower()
@@ -162,7 +163,6 @@ class SessionApplication(dbus.service.Object):
                                print e.get_dbus_message()
                                return
                        traceback.print_exc()
-                       exit(1)
 
        @dbus.service.method("com.example.TestSession",
                                in_signature='', out_signature='')
@@ -178,7 +178,6 @@ class SessionApplication(dbus.service.Object):
                        self.release(session_name)
                except dbus.DBusException:
                        traceback.print_exc()
-                       exit(1)
 
        @dbus.service.method("com.example.TestSession",
                                in_signature='', out_signature='')
@@ -197,7 +196,6 @@ class SessionApplication(dbus.service.Object):
                                print e.get_dbus_message()
                                return
                        traceback.print_exc()
-                       exit(1)
 
        @dbus.service.method("com.example.TestSession",
                                in_signature='', out_signature='')
@@ -216,7 +214,6 @@ class SessionApplication(dbus.service.Object):
                                print e.get_dbus_message()
                                return
                        traceback.print_exc()
-                       exit(1)
 
        @dbus.service.method("com.example.TestSession",
                                in_signature='', out_signature='')
@@ -236,7 +233,6 @@ class SessionApplication(dbus.service.Object):
                                print e.get_dbus_message()
                                return
                        traceback.print_exc()
-                       exit(1)
 
        @dbus.service.method("com.example.TestSession",
                                in_signature='', out_signature='')
diff --git a/tizen/rtctimer.c b/tizen/rtctimer.c
new file mode 100644 (file)
index 0000000..e8f6dbc
--- /dev/null
@@ -0,0 +1,355 @@
+/*
+ *
+ *  Tizen RTC (hardware-based) timer library
+ *
+ *  Copyright (C) 2012-2013  Danny Jeongseok Seo <S.Seo@samsung.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <unistd.h>
+
+#include <gdbus.h>
+
+#include <connman/log.h>
+#include <connman/dbus.h>
+
+#include "rtctimer.h"
+
+#define TIZEN_RTC_TIMER_SERVICE                "com.samsung.alarm.manager"
+#define TIZEN_RTC_TIMER_INTERFACE      "com.samsung.alarm.manager"
+#define TIZEN_RTC_TIMER_PATH           "/com/samsung/alarm/manager"
+
+#define TIZEN_RTC_TIMER_CLIENT_INTERFACE       "com.samsung.alarm.client"
+#define TIZEN_RTC_TIMER_CLIENT_PATH                    "/com/samsung/alarm/client"
+
+static DBusConnection *connection = NULL;
+static GHashTable *timer_list = NULL;
+
+struct RTCTimer {
+       gpointer data;
+       GSourceFunc func;
+       GDestroyNotify destroy_func;
+};
+
+struct GDestroyNotifyIdle {
+       gpointer data;
+       GDestroyNotify destroy_func;
+};
+
+static int __rtc_timeout_create(struct tm duetime)
+{
+       DBusMessage *message, *reply;
+       DBusMessageIter iter;
+       DBusError error;
+       int pid = 0, value = 0;
+       int timer = 0, return_code = 0;
+       const char *connman_service = CONNMAN_SERVICE;
+       const char *cookie = "";
+
+       message = dbus_message_new_method_call(TIZEN_RTC_TIMER_SERVICE,
+                                       TIZEN_RTC_TIMER_PATH, TIZEN_RTC_TIMER_INTERFACE,
+                                       "alarm_create");
+       if (message == NULL)
+               return -ENOMEM;
+
+       dbus_message_iter_init_append(message, &iter);
+
+       pid = getpid();
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &pid);
+
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &connman_service);
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &connman_service);
+
+       value = duetime.tm_year + 1900;
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &value);
+
+       value = duetime.tm_mon + 1;
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &value);
+
+       value = duetime.tm_mday;
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &value);
+
+       value = duetime.tm_hour;
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &value);
+
+       value = duetime.tm_min;
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &value);
+
+       value = duetime.tm_sec;
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &value);
+
+       value = 0;
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &value);
+       value = 0;
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &value);
+       value = 0;
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &value);
+
+       /* interval 0, only once */
+       value = 0;
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &value);
+       /* ALARM_REPEAT_MODE_ONCE, only once */
+       value = 0;
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &value);
+
+       /* ALARM_TYPE_VOLATILE 0x02 | ALARM_TYPE_RELATIVE 0x80000000*/
+       value = 0x02 | 0x80000000;
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &value);
+       /* reserved */
+       value = 0;
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &value);
+
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &connman_service);
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &connman_service);
+
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &cookie);
+
+       dbus_error_init(&error);
+       reply = dbus_connection_send_with_reply_and_block(connection,
+                       message, 10 * 1000, &error);
+       if (reply == NULL) {
+               if (dbus_error_is_set(&error) == TRUE) {
+                       DBG("%s", error.message);
+                       dbus_error_free(&error);
+               } else {
+                       DBG("Failed to request RTC timer");
+               }
+               dbus_message_unref(message);
+               return 0;
+       }
+       dbus_message_unref(message);
+
+       if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
+               DBG("Failed to request RTC timer");
+               return 0;
+       }
+
+       dbus_message_iter_init(reply, &iter);
+       dbus_message_iter_get_basic(&iter, &timer);
+       dbus_message_iter_get_basic(&iter, &return_code);
+       DBG("RTC timer id(%d), return(%d)", timer, return_code);
+
+       dbus_message_unref(reply);
+
+       return timer;
+}
+
+static gboolean __rtc_timeout_remove(int timer)
+{
+       DBusMessage *message, *reply;
+       DBusMessageIter iter;
+       DBusError error;
+       int pid = 0, return_code = 0;
+       const char *cookie = "";
+
+       message = dbus_message_new_method_call(TIZEN_RTC_TIMER_SERVICE,
+                                       TIZEN_RTC_TIMER_PATH, TIZEN_RTC_TIMER_INTERFACE,
+                                       "alarm_delete");
+       if (message == NULL)
+               return -ENOMEM;
+
+       dbus_message_iter_init_append(message, &iter);
+
+       pid = getpid();
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &pid);
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &timer);
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &cookie);
+
+       dbus_error_init(&error);
+       reply = dbus_connection_send_with_reply_and_block(connection,
+                       message, 10 * 1000, &error);
+       if (reply == NULL) {
+               if (dbus_error_is_set(&error) == TRUE) {
+                       DBG("%s", error.message);
+                       dbus_error_free(&error);
+               } else {
+                       DBG("Failed to remove RTC timer");
+               }
+               dbus_message_unref(message);
+               return FALSE;
+       }
+       dbus_message_unref(message);
+
+       if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
+               DBG("Failed to remove RTC timer");
+               return FALSE;
+       }
+
+       dbus_message_iter_init(reply, &iter);
+       dbus_message_iter_get_basic(&iter, &return_code);
+       DBG("RTC timer id(%d), return(%d)", timer, return_code);
+
+       dbus_message_unref(reply);
+
+       return TRUE;
+}
+
+int rtc_timeout_add_seconds(guint interval, GSourceFunc function,
+                                                       gpointer data, GDestroyNotify notify)
+{
+       int timer;
+       time_t current_time;
+       struct tm duetime;
+       struct RTCTimer *timer_data;
+
+       time(&current_time);
+       current_time += interval;
+       localtime_r(&current_time, &duetime);
+
+       timer = __rtc_timeout_create(duetime);
+       if (timer <= 0)
+               return timer;
+
+       timer_data = g_try_new0(struct RTCTimer, 1);
+       if (timer_data == NULL)
+               return 0;
+
+       DBG("RTC timer %p", timer_data);
+
+       timer_data->data = data;
+       timer_data->func = function;
+       timer_data->destroy_func = notify;
+
+       g_hash_table_replace(timer_list, GINT_TO_POINTER(timer),
+                                               (gpointer)timer_data);
+
+       return timer;
+}
+
+gboolean rtc_timeout_remove(int timer)
+{
+       if (g_hash_table_lookup(timer_list, GINT_TO_POINTER(timer)) == NULL)
+               return FALSE;
+
+       if (__rtc_timeout_remove(timer) == FALSE)
+               return FALSE;
+
+       return g_hash_table_remove(timer_list, GINT_TO_POINTER(timer));
+}
+
+static DBusMessage *timer_expired(DBusConnection *conn,
+                                                               DBusMessage *msg, void *user_data)
+{
+       int timer = 0;
+       DBusMessageIter iter;
+       gpointer data;
+       GSourceFunc func;
+       struct RTCTimer *timer_data;
+
+       DBG("conn %p", conn);
+
+       dbus_message_iter_init(msg, &iter);
+       dbus_message_iter_get_basic(&iter, &timer);
+
+       timer_data = (struct RTCTimer *)g_hash_table_lookup(timer_list,
+                                                                               GINT_TO_POINTER(timer));
+       if (timer_data == NULL)
+               return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+
+       DBG("RTC timer %p", timer_data);
+
+       func = timer_data->func;
+       data = timer_data->data;
+       if (func != NULL)
+               g_idle_add(func, data);
+
+       g_hash_table_remove(timer_list, GINT_TO_POINTER(timer));
+
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static const GDBusMethodTable timer_methods[] = {
+       { GDBUS_METHOD("alarm_expired",
+                       GDBUS_ARGS({ "alarm_id", "i" }),
+                       NULL, timer_expired) },
+       { },
+};
+
+static gboolean remove_timer_idle(gpointer data)
+{
+       struct GDestroyNotifyIdle *destroy = (struct GDestroyNotifyIdle *)data;
+
+       DBG("");
+
+       if (destroy == NULL)
+               return FALSE;
+
+       if (destroy->destroy_func != NULL)
+               destroy->destroy_func(destroy->data);
+
+       g_free(destroy);
+
+       return FALSE;
+}
+
+static void remove_timer(gpointer data)
+{
+       struct GDestroyNotifyIdle *destroy;
+       struct RTCTimer *timer_data = (struct RTCTimer *)data;
+
+       DBG("");
+
+       if (timer_data == NULL)
+               return;
+
+       if (timer_data->destroy_func == NULL)
+               return;
+
+       destroy = g_try_new0(struct GDestroyNotifyIdle, 1);
+       if (destroy == NULL)
+               return;
+
+       destroy->data = timer_data->data;
+       destroy->destroy_func = timer_data->destroy_func;
+
+       g_idle_add(remove_timer_idle, destroy);
+
+       g_free(timer_data);
+}
+
+int __connman_rtctimer_init(void)
+{
+       DBG("");
+
+       connection = connman_dbus_get_connection();
+       if (connection == NULL)
+               return -1;
+
+       timer_list = g_hash_table_new_full(g_direct_hash, g_direct_equal,
+                                       NULL, remove_timer);
+
+       g_dbus_register_interface(connection, TIZEN_RTC_TIMER_CLIENT_PATH,
+                                       TIZEN_RTC_TIMER_CLIENT_INTERFACE,
+                                       timer_methods,
+                                       NULL, NULL, NULL, NULL);
+
+       return 0;
+}
+
+void __connman_rtctimer_cleanup(void)
+{
+       DBG("");
+
+       g_hash_table_destroy(timer_list);
+       timer_list = NULL;
+
+       dbus_connection_unref(connection);
+}
similarity index 55%
rename from plugins/meego.c
rename to tizen/rtctimer.h
index 717b220..36e17bb 100644 (file)
@@ -1,8 +1,8 @@
 /*
  *
- *  Connection Manager
+ *  Tizen RTC (hardware-based) timer library
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2012-2013  Danny Jeongseok Seo <S.Seo@samsung.com>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
  *
  */
 
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
+#ifndef __RTCTIMER_H
+#define __RTCTIMER_H
 
-#define CONNMAN_API_SUBJECT_TO_CHANGE
-#include <connman/timeserver.h>
-#include <connman/plugin.h>
+#include <glib.h>
 
-#define MEEGO_NTP_SERVER "ntp.meego.com"
+#ifdef __cplusplus
+extern "C" {
+#endif
 
-static int meego_init(void)
-{
-       return connman_timeserver_append(MEEGO_NTP_SERVER);
-}
+int rtc_timeout_add_seconds(guint interval, GSourceFunc function,
+                                                       gpointer data, GDestroyNotify notify);
+gboolean rtc_timeout_remove(int timer);
 
-static void meego_exit(void)
-{
-       connman_timeserver_remove(MEEGO_NTP_SERVER);
+#ifdef __cplusplus
 }
+#endif
 
-CONNMAN_PLUGIN_DEFINE(meego, "MeeGo features plugin", VERSION,
-                       CONNMAN_PLUGIN_PRIORITY_LOW, meego_init, meego_exit)
+#endif /* __RTCTIMER_H */
index 14d7311..273ab02 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
diff --git a/tools/alg-test.c b/tools/alg-test.c
deleted file mode 100644 (file)
index a7404b7..0000000
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- *
- *  Connection Manager
- *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License version 2 as
- *  published by the Free Software Foundation.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#define _GNU_SOURCE
-#include <stdio.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/sendfile.h>
-#include <sys/socket.h>
-#include <linux/if_alg.h>
-
-static void build_hash(int sk, int fd, size_t size, const char *pathname)
-{
-       unsigned char hash[20];
-       ssize_t written, length;
-       int i;
-
-       written = sendfile(sk, fd, NULL, size);
-       if (written < 0)
-               perror("Failed to write data");
-
-       printf("send %zd bytes\n", written);
-
-       length = recv(sk, hash, sizeof(hash), 0);
-       if (length < 0)
-               perror("Failed to read data");
-
-       printf("recv %zd bytes\n", length);
-
-       for (i = 0; i < length; i++)
-               printf("%02x", hash[i]);
-       printf("  %s\n", pathname);
-}
-
-static int create_hash(int sk, const char *pathname)
-{
-       struct stat st;
-       int fd;
-
-       fd = open(pathname, O_RDONLY | O_CLOEXEC);
-       if (fd < 0)
-               return -1;
-
-       if (fstat(fd, &st) < 0) {
-               close(fd);
-               return -1;
-       }
-
-       build_hash(sk, fd, st.st_size, pathname);
-
-       close(fd);
-
-       return 0;
-}
-
-static int create_socket(void)
-{
-       struct sockaddr_alg salg = {
-               .salg_family = AF_ALG,
-               .salg_type = "hash",
-               .salg_name = "sha1",
-       };
-       int sk, nsk;
-
-       sk = socket(PF_ALG, SOCK_SEQPACKET | SOCK_CLOEXEC, 0);
-       if (sk < 0) {
-               perror("Failed to create socket");
-               return -1;
-       }
-
-       if (bind(sk, (struct sockaddr *) &salg, sizeof(salg)) < 0) {
-               perror("Failed to bind socket");
-               close(sk);
-               return -1;
-       }
-
-       nsk = accept(sk, NULL, 0);
-       if (nsk < 0) {
-               perror("Failed to accept socket");
-               close(sk);
-               return -1;
-       }
-
-       close(sk);
-
-       return nsk;
-}
-
-int main(int argc, char *argv[])
-{
-       int sk;
-
-       if (argc < 2) {
-               fprintf(stderr, "Missing argument\n");
-               return 1;
-       }
-
-       sk = create_socket();
-       if (sk < 0)
-               return 1;
-
-       create_hash(sk, argv[1]);
-
-       close(sk);
-
-       return 0;
-}
index f9f03fe..94d94f0 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
index 4d460e7..4252e62 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
index 18527c8..284656b 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
index b3f1ce1..cc227e8 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *  Connection Manager
  *
- *  Copyright (C) 2007-2011  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
@@ -689,7 +689,10 @@ static int connman_iptables_insert_rule(struct connman_iptables *table,
        if (new_entry == NULL)
                return -EINVAL;
 
-       ret = connman_add_entry(table, new_entry, chain_head->next, builtin);
+       if (builtin == -1)
+               chain_head = chain_head->next;
+
+       ret = connman_add_entry(table, new_entry, chain_head, builtin);
        if (ret < 0)
                g_free(new_entry);
 
@@ -755,7 +758,7 @@ static gboolean is_same_match(struct xt_entry_match *xt_e_m1,
        return TRUE;
 }
 
-static int connman_iptables_delete_rule(struct connman_iptables *table,
+static GList *find_existing_rule(struct connman_iptables *table,
                                struct ipt_ip *ip, char *chain_name,
                                char *target_name, struct xtables_target *xt_t,
                                struct xtables_match *xt_m,
@@ -766,24 +769,22 @@ static int connman_iptables_delete_rule(struct connman_iptables *table,
        struct xt_entry_match *xt_e_m = NULL;
        struct connman_iptables_entry *entry;
        struct ipt_entry *entry_test;
-       int builtin, removed;
-
-       removed = 0;
+       int builtin;
 
        chain_head = find_chain_head(table, chain_name);
        if (chain_head == NULL)
-               return -EINVAL;
+               return NULL;
 
        chain_tail = find_chain_tail(table, chain_name);
        if (chain_tail == NULL)
-               return -EINVAL;
+               return NULL;
 
        if (!xt_t && !xt_m)
-               return -EINVAL;
+               return NULL;
 
        entry_test = new_rule(ip, target_name, xt_t, xt_rm);
        if (entry_test == NULL)
-               return -EINVAL;
+               return NULL;
 
        if (xt_t != NULL)
                xt_e_t = ipt_get_target(entry_test);
@@ -798,7 +799,7 @@ static int connman_iptables_delete_rule(struct connman_iptables *table,
        else
                list = chain_head->next;
 
-       for (entry = NULL; list != chain_tail->prev; list = list->next) {
+       for (; list != chain_tail->prev; list = list->next) {
                struct connman_iptables_entry *tmp;
                struct ipt_entry *tmp_e;
 
@@ -826,14 +827,44 @@ static int connman_iptables_delete_rule(struct connman_iptables *table,
                                continue;
                }
 
-               entry = tmp;
                break;
        }
 
-       if (entry == NULL) {
-               g_free(entry_test);
+       g_free(entry_test);
+
+       if (list != chain_tail->prev)
+               return list;
+
+       return NULL;
+}
+
+static int connman_iptables_delete_rule(struct connman_iptables *table,
+                               struct ipt_ip *ip, char *chain_name,
+                               char *target_name, struct xtables_target *xt_t,
+                               struct xtables_match *xt_m,
+                               struct xtables_rule_match *xt_rm)
+{
+       struct connman_iptables_entry *entry;
+       GList *chain_tail, *list;
+       int builtin, removed;
+
+       removed = 0;
+
+       chain_tail = find_chain_tail(table, chain_name);
+       if (chain_tail == NULL)
+               return -EINVAL;
+
+       list = find_existing_rule(table, ip, chain_name, target_name,
+                                                       xt_t, xt_m, xt_rm);
+       if (list == NULL)
                return -EINVAL;
-       }
+
+       entry = list->data;
+
+       if (entry == NULL)
+               return -EINVAL;
+
+       builtin = entry->builtin;
 
        /* We have deleted a rule,
         * all references should be bumped accordingly */
@@ -868,6 +899,28 @@ static int connman_iptables_delete_rule(struct connman_iptables *table,
        return 0;
 }
 
+static int connman_iptables_compare_rule(struct connman_iptables *table,
+                               struct ipt_ip *ip, char *chain_name,
+                               char *target_name, struct xtables_target *xt_t,
+                               struct xtables_match *xt_m,
+                               struct xtables_rule_match *xt_rm)
+{
+       struct connman_iptables_entry *entry;
+       GList *found;
+
+       found = find_existing_rule(table, ip, chain_name, target_name,
+                                                       xt_t, xt_m, xt_rm);
+       if (found == NULL)
+               return -EINVAL;
+
+       entry = found->data;
+       if (entry == NULL)
+               return -EINVAL;
+
+       return 0;
+}
+
+
 static int connman_iptables_change_policy(struct connman_iptables *table,
                                                char *chain_name, char *policy)
 {
@@ -1171,6 +1224,9 @@ static struct connman_iptables *connman_iptables_init(const char *table_name)
        char *module = NULL;
        socklen_t s;
 
+       if (table_name == NULL)
+               table_name = "filter";
+
        if (xtables_insmod("ip_tables", NULL, TRUE) != 0)
                goto err;
 
@@ -1238,6 +1294,7 @@ err:
 
 static struct option connman_iptables_opts[] = {
        {.name = "append",        .has_arg = 1, .val = 'A'},
+       {.name = "compare",       .has_arg = 1, .val = 'C'},
        {.name = "delete",        .has_arg = 1, .val = 'D'},
        {.name = "flush-chain",   .has_arg = 1, .val = 'F'},
        {.name = "insert",        .has_arg = 1, .val = 'I'},
@@ -1405,6 +1462,52 @@ done:
        return xt_m;
 }
 
+static int parse_ip_and_mask(const char *str, struct in_addr *ip, struct in_addr *mask)
+{
+       char **tokens;
+       uint32_t prefixlength;
+       uint32_t tmp;
+       int err;
+
+       tokens = g_strsplit(str, "/", 2);
+       if (tokens == NULL)
+               return -1;
+
+       if (!inet_pton(AF_INET, tokens[0], ip)) {
+               err = -1;
+               goto out;
+       }
+
+       if (tokens[1] != NULL) {
+               prefixlength = strtol(tokens[1], NULL, 10);
+               if (prefixlength > 31) {
+                       err = -1;
+                       goto out;
+               }
+
+               tmp = ~(0xffffffff >> prefixlength);
+       } else {
+               tmp = 0xffffffff;
+       }
+
+       mask->s_addr = htonl(tmp);
+       ip->s_addr = ip->s_addr & mask->s_addr;
+       err = 0;
+out:
+       g_strfreev(tokens);
+
+       return err;
+}
+
+static struct connman_iptables *pre_load_table(char *table_name,
+                                       struct connman_iptables *table)
+{
+       if (table != NULL)
+               return table;
+
+       return connman_iptables_init(table_name);
+}
+
 int main(int argc, char *argv[])
 {
        struct connman_iptables *table;
@@ -1415,8 +1518,7 @@ int main(int argc, char *argv[])
        char *table_name, *chain, *new_chain, *match_name, *target_name;
        char *delete_chain, *flush_chain, *policy;
        int c, in_len, out_len;
-       gboolean dump, invert, delete, insert, delete_rule;
-       struct in_addr src, dst;
+       gboolean dump, invert, delete, insert, delete_rule, compare_rule;
 
        xtables_init_all(&connman_iptables_globals, NFPROTO_IPV4);
 
@@ -1425,8 +1527,9 @@ int main(int argc, char *argv[])
        delete = FALSE;
        insert = FALSE;
        delete_rule = FALSE;
-       table_name = chain = new_chain = match_name = target_name = NULL;
-       delete_chain = flush_chain = policy = NULL;
+       compare_rule = FALSE;
+       chain = new_chain = match_name = target_name = NULL;
+       delete_chain = flush_chain = policy = table_name = NULL;
        memset(&ip, 0, sizeof(struct ipt_ip));
        table = NULL;
        xt_rm = NULL;
@@ -1436,19 +1539,29 @@ int main(int argc, char *argv[])
        /* extension's options will generate false-positives errors */
        opterr = 0;
 
-       while ((c = getopt_long(argc, argv, "-A:D:F:I:L::N:P:X:d:i:j:m:o:s:t:",
+       while ((c = getopt_long(argc, argv,
+                               "-A:C:D:F:I:L::N:P:X:d:i:j:m:o:s:t:",
                                connman_iptables_globals.opts, NULL)) != -1) {
                switch (c) {
                case 'A':
-                       /* It is either -A, -D or -I at once */
+                       /* It is either -A, -C, -D or -I at once */
+                       if (chain)
+                               goto out;
+
+                       chain = optarg;
+                       break;
+
+               case 'C':
+                       /* It is either -A, -C, -D or -I at once */
                        if (chain)
                                goto out;
 
                        chain = optarg;
+                       compare_rule = TRUE;
                        break;
 
                case 'D':
-                       /* It is either -A, -D or -I at once */
+                       /* It is either -A, -C, -D or -I at once */
                        if (chain)
                                goto out;
 
@@ -1461,7 +1574,7 @@ int main(int argc, char *argv[])
                        break;
 
                case 'I':
-                       /* It is either -A, -D or -I at once */
+                       /* It is either -A, -C, -D or -I at once */
                        if (chain)
                                goto out;
 
@@ -1492,15 +1605,13 @@ int main(int argc, char *argv[])
                        break;
 
                case 'd':
-                       if (!inet_pton(AF_INET, optarg, &dst))
+                       if (!parse_ip_and_mask(optarg, &ip.dst, &ip.dmsk))
                                break;
 
-                       ip.dst = dst;
-                       inet_pton(AF_INET, "255.255.255.255", &ip.dmsk);
-
                        if (invert)
                                ip.invflags |= IPT_INV_DSTIP;
 
+
                        break;
 
                case 'i':
@@ -1519,6 +1630,11 @@ int main(int argc, char *argv[])
 
                case 'j':
                        target_name = optarg;
+
+                       table = pre_load_table(table_name, table);
+                       if (table == NULL)
+                               goto out;
+
                        xt_t = prepare_target(table, target_name);
                        if (xt_t == NULL)
                                goto out;
@@ -1527,6 +1643,11 @@ int main(int argc, char *argv[])
 
                case 'm':
                        match_name = optarg;
+
+                       table = pre_load_table(table_name, table);
+                       if (table == NULL)
+                               goto out;
+
                        xt_m = prepare_matches(table, &xt_rm, match_name);
                        if (xt_m == NULL)
                                goto out;
@@ -1548,12 +1669,9 @@ int main(int argc, char *argv[])
                        break;
 
                case 's':
-                       if (!inet_pton(AF_INET, optarg, &src))
+                       if (!parse_ip_and_mask(optarg, &ip.src, &ip.smsk))
                                break;
 
-                       ip.src = src;
-                       inet_pton(AF_INET, "255.255.255.255", &ip.smsk);
-
                        if (invert)
                                ip.invflags |= IPT_INV_SRCIP;
 
@@ -1562,7 +1680,7 @@ int main(int argc, char *argv[])
                case 't':
                        table_name = optarg;
 
-                       table = connman_iptables_init(table_name);
+                       table = pre_load_table(table_name, table);
                        if (table == NULL)
                                return -1;
 
@@ -1658,13 +1776,9 @@ int main(int argc, char *argv[])
                xt_t->final_check(xt_t->tflags);
 #endif
 
-       if (table == NULL) {
-               table_name = "filter";
-
-               table = connman_iptables_init(table_name);
-               if (table == NULL)
-                       return -1;
-       }
+       table = pre_load_table(table_name, table);
+       if (table == NULL)
+               return -1;
 
        if (delete) {
                if (delete_chain == NULL)
@@ -1711,6 +1825,20 @@ int main(int argc, char *argv[])
                        goto commit;
                }
 
+               if (compare_rule == TRUE) {
+                       int ret;
+
+                       ret = connman_iptables_compare_rule(table, &ip,
+                               chain, target_name, xt_t, xt_m, xt_rm);
+
+                       if (ret == 0)
+                               printf("Rule exists.\n");
+                       else
+                               printf("Rule does not exist.\n");
+
+                       goto out;
+               }
+
                if (delete_rule == TRUE) {
                        printf("Deleting %s to %s (match %s)\n", target_name,
                                        chain, match_name);
index 3df7d26..45e6eb8 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
index 598b0f0..24f9234 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2011  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2012  Intel Corporation. All rights reserved.
  *  Copyright (C) 2011 ProFUSION embedded systems
  *
  *  This program is free software; you can redistribute it and/or modify
@@ -176,15 +176,15 @@ int main(int argc, char *argv[])
         * IP packet: src: 192.168.219.2 dst www.connman.net
         * HTTP GET / request
         */
-       int buf[81] = { 0x45, 0x00, 0x00, 0x51, 0x5a, 0xbe, 0x00, 0x00, 0x40,
-                       0x06, 0x50, 0x73, 0xc0, 0xa8, 0xdb, 0x01, 0x3e, 0x4b,
-                       0xf5, 0x80, 0x30, 0x3b, 0x00, 0x50, 0x00, 0x00, 0x00,
-                       0x28, 0x04, 0xfd, 0xac, 0x9b, 0x50, 0x18, 0x02, 0x00,
-                       0xa1, 0xb3, 0x00, 0x00, 0x47, 0x45, 0x54, 0x20, 0x2f,
-                       0x20, 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, 0x2e, 0x31,
-                       0x0d, 0x0a, 0x48, 0x6f, 0x73, 0x74, 0x3a, 0x20, 0x77,
-                       0x77, 0x77, 0x2e, 0x63, 0x6f, 0x6e, 0x6e, 0x6d, 0x61,
-                       0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x0d, 0x0a, 0x0d, 0x0a};
+       int buf1[81] = { 0x45, 0x00, 0x00, 0x51, 0x5a, 0xbe, 0x00, 0x00, 0x40,
+                        0x06, 0x50, 0x73, 0xc0, 0xa8, 0xdb, 0x01, 0x3e, 0x4b,
+                        0xf5, 0x80, 0x30, 0x3b, 0x00, 0x50, 0x00, 0x00, 0x00,
+                        0x28, 0x04, 0xfd, 0xac, 0x9b, 0x50, 0x18, 0x02, 0x00,
+                        0xa1, 0xb3, 0x00, 0x00, 0x47, 0x45, 0x54, 0x20, 0x2f,
+                        0x20, 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, 0x2e, 0x31,
+                        0x0d, 0x0a, 0x48, 0x6f, 0x73, 0x74, 0x3a, 0x20, 0x77,
+                        0x77, 0x77, 0x2e, 0x63, 0x6f, 0x6e, 0x6e, 0x6d, 0x61,
+                        0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x0d, 0x0a, 0x0d, 0x0a};
 
 
        int buf2[81] = { 0x45, 0x00, 0x00, 0x51, 0x57, 0x9d, 0x00, 0x00, 0x40,
@@ -220,12 +220,12 @@ int main(int argc, char *argv[])
 
 
        if (!fork()) {
-               if (write(fd, buf, 81) < 0) {
-                       fprintf(stderr, "err on write() buf\n");
+               if (write(fd, buf1, sizeof(buf1)) < 0) {
+                       fprintf(stderr, "err on write() buf1\n");
                        return -1;
                }
 
-               if (write(fd, buf2, 81) < 0) {
+               if (write(fd, buf2, sizeof(buf2)) < 0) {
                        fprintf(stderr, "err on write() buf2\n");
                        return -1;
                }
index 7cb2f6f..9867a4b 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
index 96d9aa8..028a275 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
index 9648bdf..4452894 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
index dc4da67..9b33275 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
index 2717fe9..1c96823 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
@@ -894,8 +894,6 @@ static void extract_rsn(struct supplicant_bss *bss,
                }
        }
 
-       buf += 2 + (count * 4);
-       len -= 2 + (count * 4);
 }
 
 static void bss_rates(DBusMessageIter *iter, void *user_data)
@@ -1745,10 +1743,8 @@ static void interface_get_result(const char *error,
        const char *path = NULL;
        int err;
 
-       if (error != NULL) {
-               err = -EIO;
+       if (error != NULL)
                goto create;
-       }
 
        dbus_message_iter_get_basic(iter, &path);
        if (path == NULL) {
index 10d43d2..556dc7d 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
index 28e5cd5..1074c9c 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
index 82ea46b..a507397 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
@@ -150,7 +150,7 @@ int main(int argc, char *argv[])
 
        timer = g_timer_new();
 
-       if (g_web_request_get(web, argv[1], web_result, NULL) == 0) {
+       if (g_web_request_get(web, argv[1], web_result, NULL,  NULL) == 0) {
                fprintf(stderr, "Failed to start request\n");
                return 1;
        }
index 88f09c1..122bea5 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
@@ -31,6 +31,7 @@
 #include <string.h>
 #include <signal.h>
 #include <termios.h>
+#include <netdb.h>
 
 #include <gweb/gweb.h>
 
@@ -289,7 +290,7 @@ static void parser_callback(const char *str, gpointer user_data)
 
        result = g_markup_parse_context_parse(context, str, strlen(str), NULL);
        if (result == TRUE)
-               result = g_markup_parse_context_end_parse(context, NULL);
+               g_markup_parse_context_end_parse(context, NULL);
 
        g_markup_parse_context_free(context);
 }
@@ -475,6 +476,24 @@ static gboolean wispr_input(const guint8 **data, gsize *length,
        return FALSE;
 }
 
+static gboolean wispr_route(const char *addr, int ai_family, int if_index,
+               gpointer user_data)
+{
+       char *family = "unknown";
+
+       if (ai_family == AF_INET)
+               family = "IPv4";
+       else if (ai_family == AF_INET6)
+               family = "IPv6";
+
+       printf("Route request: %s %s index %d\n", family, addr, if_index);
+
+       if (ai_family != AF_INET && ai_family != AF_INET6)
+               return FALSE;
+
+       return TRUE;
+}
+
 static gboolean wispr_result(GWebResult *result, gpointer user_data)
 {
        struct wispr_session *wispr = user_data;
@@ -515,7 +534,7 @@ static gboolean wispr_result(GWebResult *result, gpointer user_data)
                printf("\n");
 
                wispr->request = g_web_request_get(wispr->web, redirect,
-                                                       wispr_result, wispr);
+                               wispr_result, wispr_route, wispr);
 
                return FALSE;
        }
@@ -575,7 +594,7 @@ static gboolean wispr_result(GWebResult *result, gpointer user_data)
                printf("\n");
 
                wispr->request = g_web_request_get(wispr->web, redirect,
-                                                       wispr_result, wispr);
+                               wispr_result, NULL, wispr);
 
                return FALSE;
        }
@@ -678,7 +697,7 @@ int main(int argc, char *argv[])
                                                parser_callback, &wispr);
 
        wispr.request = g_web_request_get(wispr.web, option_url,
-                                                       wispr_result, &wispr);
+                       wispr_result, wispr_route, &wispr);
 
        if (wispr.request == 0) {
                fprintf(stderr, "Failed to start request\n");
index b127489..a6cc792 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
index 480a1ac..14d48cf 100644 (file)
 
 #include "test-connman.h"
 
+static enum connman_session_state string2state(const char *state)
+{
+       if (g_strcmp0(state, "connected") == 0)
+               return CONNMAN_SESSION_STATE_CONNECTED;
+       if (g_strcmp0(state, "online") == 0)
+               return CONNMAN_SESSION_STATE_ONLINE;
+
+       return CONNMAN_SESSION_STATE_DISCONNECTED;
+}
+
+static enum connman_session_state string2type(const char *type)
+{
+       if (g_strcmp0(type, "local") == 0)
+               return CONNMAN_SESSION_TYPE_LOCAL;
+       if (g_strcmp0(type, "internet") == 0)
+               return CONNMAN_SESSION_TYPE_INTERNET;
+
+       return CONNMAN_SESSION_TYPE_ANY;
+}
+
 static const char *roamingpolicy2string(enum connman_session_roaming_policy policy)
 {
        switch (policy) {
@@ -163,10 +183,7 @@ static DBusMessage *notify_update(DBusConnection *conn,
                        }
                        break;
                case DBUS_TYPE_BOOLEAN:
-                       if (g_str_equal(key, "Online") == TRUE) {
-                               dbus_message_iter_get_basic(&value,
-                                                       &info->online);
-                       } else if (g_str_equal(key, "Priority") == TRUE) {
+                       if (g_str_equal(key, "Priority") == TRUE) {
                                dbus_message_iter_get_basic(&value,
                                                        &info->priority);
 
@@ -206,7 +223,12 @@ static DBusMessage *notify_update(DBusConnection *conn,
                        }
                        break;
                case DBUS_TYPE_STRING:
-                       if (g_str_equal(key, "Bearer") == TRUE) {
+                       if (g_str_equal(key, "State") == TRUE) {
+                               const char *val;
+                               dbus_message_iter_get_basic(&value, &val);
+
+                               info->state = string2state(val);
+                       } else if (g_str_equal(key, "Bearer") == TRUE) {
                                const char *val;
                                dbus_message_iter_get_basic(&value, &val);
 
@@ -239,6 +261,12 @@ static DBusMessage *notify_update(DBusConnection *conn,
 
                                info->interface = g_strdup(val);
 
+                       } else if (g_str_equal(key, "ConnectionType")
+                                                               == TRUE) {
+                               const char *val;
+                               dbus_message_iter_get_basic(&value, &val);
+
+                               info->type = string2type(val);
                        } else {
                                g_assert(FALSE);
                                return __connman_error_invalid_arguments(msg);
@@ -257,9 +285,11 @@ static DBusMessage *notify_update(DBusConnection *conn,
        return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
 }
 
-static GDBusMethodTable notify_methods[] = {
-       { "Release", "",      "", notify_release },
-       { "Update",  "a{sv}", "", notify_update  },
+static const GDBusMethodTable notify_methods[] = {
+       { GDBUS_METHOD("Release", NULL, NULL, notify_release) },
+       { GDBUS_METHOD("Update",
+                       GDBUS_ARGS({ "settings", "a{sv}" }), NULL,
+                       notify_update) },
        { },
 };
 
index 5602daa..872aabd 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
@@ -71,6 +71,18 @@ void util_session_cleanup(struct test_session *session);
 
 typedef void (* notify_cb) (struct test_session *session);
 
+enum connman_session_state {
+       CONNMAN_SESSION_STATE_DISCONNECTED   = 0,
+       CONNMAN_SESSION_STATE_CONNECTED      = 1,
+       CONNMAN_SESSION_STATE_ONLINE         = 2,
+};
+
+enum connman_session_type {
+       CONNMAN_SESSION_TYPE_ANY      = 0,
+       CONNMAN_SESSION_TYPE_LOCAL    = 1,
+       CONNMAN_SESSION_TYPE_INTERNET = 2,
+};
+
 enum connman_session_roaming_policy {
        CONNMAN_SESSION_ROAMING_POLICY_UNKNOWN          = 0,
        CONNMAN_SESSION_ROAMING_POLICY_DEFAULT          = 1,
@@ -82,7 +94,8 @@ enum connman_session_roaming_policy {
 
 struct test_session_info {
        char *bearer;
-       connman_bool_t online;
+       enum connman_session_state state;
+       enum connman_session_type type;
        char *name;
        /* ipv4, ipv6 dicts */
        GSList *allowed_bearers;
diff --git a/unit/test-ippool.c b/unit/test-ippool.c
new file mode 100644 (file)
index 0000000..2df8561
--- /dev/null
@@ -0,0 +1,258 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2012  BWM CarIT GmbH. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+
+#include "../src/connman.h"
+
+/* #define DEBUG */
+#ifdef DEBUG
+#include <stdio.h>
+
+#define LOG(fmt, arg...) do { \
+       fprintf(stdout, "%s:%s() " fmt "\n", \
+                       __FILE__, __func__ , ## arg); \
+} while (0)
+#else
+#define LOG(fmt, arg...)
+#endif
+
+static void test_ippool_basic0(void)
+{
+       struct connman_ippool *pool;
+       int i;
+
+       __connman_ippool_init();
+
+       pool = __connman_ippool_create(23, 1, 500, NULL, NULL);
+       g_assert(pool == NULL);
+
+       for (i = 0; i < 100000; i++) {
+               pool = __connman_ippool_create(23, 1, 20, NULL, NULL);
+               g_assert(pool);
+
+               __connman_ippool_unref(pool);
+       }
+
+       __connman_ippool_cleanup();
+}
+
+static void test_ippool_basic1(void)
+{
+       struct connman_ippool *pool;
+       const char *gateway;
+       const char *broadcast;
+       const char *subnet_mask;
+       const char *start_ip;
+       const char *end_ip;
+       int i;
+
+       __connman_ippool_init();
+
+       /* Test the IP range */
+       for (i = 1; i < 254; i++) {
+               pool = __connman_ippool_create(23, 1, i, NULL, NULL);
+               g_assert(pool);
+
+               gateway = __connman_ippool_get_gateway(pool);
+               broadcast = __connman_ippool_get_broadcast(pool);
+               subnet_mask = __connman_ippool_get_subnet_mask(pool);
+               start_ip = __connman_ippool_get_start_ip(pool);
+               end_ip = __connman_ippool_get_end_ip(pool);
+
+               g_assert(gateway);
+               g_assert(broadcast);
+               g_assert(subnet_mask);
+               g_assert(start_ip);
+               g_assert(end_ip);
+
+               LOG("\n\tIP range %s --> %s\n"
+                       "\tgateway %s broadcast %s mask %s", start_ip, end_ip,
+                       gateway, broadcast, subnet_mask);
+
+               __connman_ippool_unref(pool);
+       }
+
+       __connman_ippool_cleanup();
+}
+
+static void test_ippool_exhaust0(void)
+{
+       struct connman_ippool *pool;
+       const char *gateway;
+       const char *broadcast;
+       const char *subnet_mask;
+       const char *start_ip;
+       const char *end_ip;
+       GSList *list = NULL, *it;
+       int i = 0;
+
+       __connman_ippool_init();
+
+       /* Allocate all possible pools */
+
+       /*
+        *                                             Number of addresses
+        * 24-bit block         10.0.0.0    – 10.255.255.255    16,777,216
+        * 20-bit block         172.16.0.0  – 172.31.255.255     1,048,576
+        * 16-bit block         192.168.0.0 – 192.168.255.255       65,536
+        *
+        * Total                                                17,891,328
+        *
+        * Total numbers of 256 blocks:                             69,888
+        */
+
+       while (TRUE) {
+               pool = __connman_ippool_create(23, 1, 100, NULL, NULL);
+               if (pool == NULL)
+                       break;
+               i += 1;
+               g_assert(i < 69888);
+
+               list = g_slist_prepend(list, pool);
+
+               gateway = __connman_ippool_get_gateway(pool);
+               broadcast = __connman_ippool_get_broadcast(pool);
+               subnet_mask = __connman_ippool_get_subnet_mask(pool);
+               start_ip = __connman_ippool_get_start_ip(pool);
+               end_ip = __connman_ippool_get_end_ip(pool);
+
+               g_assert(gateway);
+               g_assert(broadcast);
+               g_assert(subnet_mask);
+               g_assert(start_ip);
+               g_assert(end_ip);
+       }
+
+       LOG("Number of blocks %d", i);
+
+       for (it = list; it != NULL; it = it->next) {
+               pool = it->data;
+
+               __connman_ippool_unref(pool);
+       }
+
+       g_slist_free(list);
+
+       __connman_ippool_cleanup();
+}
+
+static void collision_cb(struct connman_ippool *pool, void *user_data)
+{
+       int *flag = user_data;
+
+       LOG("collision detected");
+
+       g_assert(*flag == 0);
+       g_assert(pool);
+
+       *flag = 1;
+}
+
+static void test_ippool_collision0(void)
+{
+       struct connman_ippool *pool;
+       const char *gateway;
+       const char *broadcast;
+       const char *subnet_mask;
+       const char *start_ip;
+       const char *end_ip;
+       int flag;
+
+       __connman_ippool_init();
+
+       /* Test the IP range collision */
+
+       flag = 0;
+       pool = __connman_ippool_create(23, 1, 100, collision_cb, &flag);
+       g_assert(pool);
+
+       gateway = __connman_ippool_get_gateway(pool);
+       broadcast = __connman_ippool_get_broadcast(pool);
+       subnet_mask = __connman_ippool_get_subnet_mask(pool);
+       start_ip = __connman_ippool_get_start_ip(pool);
+       end_ip = __connman_ippool_get_end_ip(pool);
+
+       g_assert(gateway);
+       g_assert(broadcast);
+       g_assert(subnet_mask);
+       g_assert(start_ip);
+       g_assert(end_ip);
+
+       LOG("\n\tIP range %s --> %s\n"
+               "\tgateway %s broadcast %s mask %s", start_ip, end_ip,
+               gateway, broadcast, subnet_mask);
+
+       __connman_ippool_newaddr(23, start_ip, 24);
+
+       g_assert(flag == 0);
+
+       __connman_ippool_newaddr(42, start_ip, 16);
+
+       g_assert(flag == 1);
+
+       __connman_ippool_unref(pool);
+
+       flag = 0;
+
+       pool = __connman_ippool_create(23, 1, 100, collision_cb, &flag);
+       g_assert(pool);
+
+       gateway = __connman_ippool_get_gateway(pool);
+       broadcast = __connman_ippool_get_broadcast(pool);
+       subnet_mask = __connman_ippool_get_subnet_mask(pool);
+       start_ip = __connman_ippool_get_start_ip(pool);
+       end_ip = __connman_ippool_get_end_ip(pool);
+
+       g_assert(gateway);
+       g_assert(broadcast);
+       g_assert(subnet_mask);
+       g_assert(start_ip);
+       g_assert(end_ip);
+
+       LOG("\n\tIP range %s --> %s\n"
+               "\tgateway %s broadcast %s mask %s", start_ip, end_ip,
+               gateway, broadcast, subnet_mask);
+
+       __connman_ippool_newaddr(45, start_ip, 22);
+
+       g_assert(flag == 1);
+
+       __connman_ippool_unref(pool);
+
+       __connman_ippool_cleanup();
+}
+
+int main(int argc, char *argv[])
+{
+       g_test_init(&argc, &argv, NULL);
+
+       g_test_add_func("/basic0", test_ippool_basic0);
+       g_test_add_func("/basic1", test_ippool_basic1);
+       g_test_add_func("/exhaust0", test_ippool_exhaust0);
+       g_test_add_func("/collision0", test_ippool_collision0);
+
+       return g_test_run();
+}
diff --git a/unit/test-nat.c b/unit/test-nat.c
new file mode 100644 (file)
index 0000000..578ff3e
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2012  BWM CarIT GmbH. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+
+#include "../src/connman.h"
+
+/* #define DEBUG */
+#ifdef DEBUG
+#include <stdio.h>
+
+#define LOG(fmt, arg...) do { \
+       fprintf(stdout, "%s:%s() " fmt "\n", \
+                       __FILE__, __func__ , ## arg); \
+} while (0)
+#else
+#define LOG(fmt, arg...)
+#endif
+
+struct connman_notifier *nat_notifier;
+
+struct connman_service {
+       char *dummy;
+};
+
+char *connman_service_get_interface(struct connman_service *service)
+{
+       return "eth0";
+}
+
+int connman_notifier_register(struct connman_notifier *notifier)
+{
+       nat_notifier = notifier;
+
+       return 0;
+}
+
+void connman_notifier_unregister(struct connman_notifier *notifier)
+{
+       nat_notifier = NULL;
+}
+
+
+static void test_iptables_basic0(void)
+{
+       int err;
+
+       err = __connman_iptables_command("-C INPUT -i session-bridge -j ACCEPT");
+       g_assert(err != 0);
+       err = __connman_iptables_commit("filter");
+       g_assert(err == 0);
+
+       err = __connman_iptables_command("-I INPUT -i session-bridge -j ACCEPT");
+       g_assert(err == 0);
+       err = __connman_iptables_commit("filter");
+       g_assert(err == 0);
+
+       err = __connman_iptables_command("-C INPUT -i session-bridge -j ACCEPT");
+       g_assert(err == 0);
+       err = __connman_iptables_commit("filter");
+       g_assert(err == 0);
+
+       err = __connman_iptables_command("-D INPUT -i session-bridge -j ACCEPT");
+       g_assert(err == 0);
+       err = __connman_iptables_commit("filter");
+       g_assert(err == 0);
+
+       err = __connman_iptables_command("-C INPUT -i session-bridge -j ACCEPT");
+       g_assert(err != 0);
+       err = __connman_iptables_commit("filter");
+       g_assert(err == 0);
+}
+
+static void test_nat_basic0(void)
+{
+       int err;
+
+       err = __connman_nat_enable("bridge", "192.168.2.1", 24);
+       g_assert(err == 0);
+
+       /* test that table is empty */
+       err = __connman_iptables_command("-t nat -C POSTROUTING "
+                                       "-s 192.168.2.1/24 -o eth0 -j MASQUERADE");
+       g_assert(err != 0);
+       err = __connman_iptables_commit("nat");
+       g_assert(err == 0);
+
+
+       __connman_nat_disable("bridge");
+}
+
+static void test_nat_basic1(void)
+{
+       struct connman_service *service;
+       int err;
+
+       service = g_try_new0(struct connman_service, 1);
+       g_assert(service);
+
+       nat_notifier->default_changed(service);
+
+       err = __connman_nat_enable("bridge", "192.168.2.1", 24);
+       g_assert(err == 0);
+
+       /* test that table is not empty */
+       err = __connman_iptables_command("-t nat -C POSTROUTING "
+                                       "-s 192.168.2.1/24 -o eth0 -j MASQUERADE");
+       g_assert(err == 0);
+       err = __connman_iptables_commit("nat");
+       g_assert(err == 0);
+
+       __connman_nat_disable("bridge");
+
+       /* test that table is empty again */
+       err = __connman_iptables_command("-t nat -C POSTROUTING "
+                                       "-s 192.168.2.1/24 -o eth0 -j MASQUERADE");
+       g_assert(err != 0);
+       err = __connman_iptables_commit("nat");
+       g_assert(err == 0);
+}
+
+int main(int argc, char *argv[])
+{
+       int err;
+
+       g_test_init(&argc, &argv, NULL);
+
+       __connman_log_init(argv[0], "*", FALSE);
+       __connman_iptables_init();
+       __connman_nat_init();
+
+       g_test_add_func("/iptables/basic0", test_iptables_basic0);
+       g_test_add_func("/nat/basic0", test_nat_basic0);
+       g_test_add_func("/nat/basic1", test_nat_basic1);
+
+       err = g_test_run();
+
+       __connman_nat_cleanup();
+       __connman_iptables_cleanup();
+       __connman_log_cleanup();
+
+       return err;
+}
index 47704da..38ed2bf 100644 (file)
@@ -220,9 +220,9 @@ static void set_session_mode(struct test_fix *fix,
 
 static void test_session_connect_notify(struct test_session *session)
 {
-       LOG("session %p online %d", session, session->info->online);
+       LOG("session %p state %d", session, session->info->state);
 
-       if (session->info->online != TRUE)
+       if (session->info->state == CONNMAN_SESSION_STATE_DISCONNECTED)
                return;
 
        util_session_cleanup(session);
@@ -254,9 +254,9 @@ static gboolean test_session_connect(gpointer data)
 
 static void test_session_disconnect_notify(struct test_session *session)
 {
-       LOG("session %p online %d", session, session->info->online);
+       LOG("session %p state %d", session, session->info->state);
 
-       if (session->info->online != FALSE)
+       if (session->info->state >= CONNMAN_SESSION_STATE_CONNECTED)
                return;
 
        util_session_cleanup(session);
@@ -290,20 +290,20 @@ static void test_session_connect_disconnect_notify(struct test_session *session)
        enum test_session_state next_state = state;
        DBusMessage *msg;
 
-       LOG("state %d session %p %s online %d", state, session,
-               session->notify_path, session->info->online);
+       LOG("state %d session %p %s state %d", state, session,
+               session->notify_path, session->info->state);
 
        switch (state) {
        case TEST_SESSION_STATE_0:
-               if (session->info->online == FALSE)
+               if (session->info->state == CONNMAN_SESSION_STATE_DISCONNECTED)
                        next_state = TEST_SESSION_STATE_1;
                break;
        case TEST_SESSION_STATE_1:
-               if (session->info->online == TRUE)
+               if (session->info->state >= CONNMAN_SESSION_STATE_CONNECTED)
                        next_state = TEST_SESSION_STATE_2;
                break;
        case TEST_SESSION_STATE_2:
-               if (session->info->online == FALSE)
+               if (session->info->state == CONNMAN_SESSION_STATE_DISCONNECTED)
                        next_state = TEST_SESSION_STATE_3;
        default:
                break;
@@ -381,27 +381,30 @@ static void test_session_connect_free_ride_notify(struct test_session *session)
        enum test_session_state next_state = state;
        DBusMessage *msg;
 
-       LOG("state %d session %p %s online %d", state, session,
-               session->notify_path, session->info->online);
+       LOG("state %d session %p %s state %d", state, session,
+               session->notify_path, session->info->state);
 
        switch (state) {
        case TEST_SESSION_STATE_0:
-               if (session0->info->online == FALSE &&
-                               session1->info->online == FALSE) {
+               if (session0->info->state == CONNMAN_SESSION_STATE_DISCONNECTED
+                               && session1->info->state ==
+                                       CONNMAN_SESSION_STATE_DISCONNECTED) {
                        next_state = TEST_SESSION_STATE_1;
                }
 
                break;
        case TEST_SESSION_STATE_1:
-               if (session0->info->online == TRUE &&
-                               session1->info->online == TRUE) {
+               if (session0->info->state >= CONNMAN_SESSION_STATE_CONNECTED &&
+                               session1->info->state >=
+                                       CONNMAN_SESSION_STATE_CONNECTED) {
                        next_state = TEST_SESSION_STATE_2;
                }
 
                break;
        case TEST_SESSION_STATE_2:
-               if (session0->info->online == FALSE &&
-                               session1->info->online == FALSE) {
+               if (session0->info->state == CONNMAN_SESSION_STATE_DISCONNECTED
+                               && session1->info->state ==
+                                       CONNMAN_SESSION_STATE_DISCONNECTED) {
                        next_state = TEST_SESSION_STATE_3;
                }
 
index 6ff1c71..758528a 100644 (file)
@@ -137,7 +137,7 @@ void util_setup(struct test_fix *fix, gconstpointer data)
                                                connman_died,
                                                NULL, NULL);
        fix->manager_watch = g_dbus_add_signal_watch(fix->main_connection,
-                                               NULL, NULL,
+                                               CONNMAN_SERVICE, NULL,
                                                CONNMAN_MANAGER_INTERFACE,
                                                PROPERTY_CHANGED,
                                                handle_manager_changed,