[meego-commits] 8682: Changes to Trunk/obexd

Peter Zhu no_reply at build.meego.com
Wed Oct 20 12:09:21 UTC 2010


Hi,
I have made the following changes to obexd in project Trunk. Please review and accept ASAP.

Thank You,
Peter Zhu

[This message was auto-generated]

---

Request #8682:

  submit:   Trunk:Testing/obexd(r13) -> Trunk/obexd


Message:
    Move to Trunk

State:   new          2010-10-20T05:09:21 peter
Comment: None



changes files:
--------------
--- obexd.changes
+++ obexd.changes
@@ -0,0 +1,3 @@
+* Mon Oct 18 2010 Zhu Yanhai <yanhai.zhu at linux.intel.com> - 0.35
+- Upgrade to 0.35
+

old:
----
  obexd-0.32.tar.gz

new:
----
  obexd-0.35.tar.gz

spec files:
-----------
--- obexd.spec
+++ obexd.spec
@@ -1,13 +1,13 @@
 # 
-# Do not Edit! Generated by:
-# spectacle version 0.17
+# Do NOT Edit the Auto-generated Part!
+# Generated by: spectacle version 0.18
 # 
 # >> macros
 # << macros
 
 Name:       obexd
 Summary:    D-Bus service for Obex Client access
-Version:    0.32
+Version:    0.35
 Release:    1
 Group:      System/Daemons
 License:    GPLv2+

other changes:
--------------

++++++ obexd-0.32.tar.gz -> obexd-0.35.tar.gz
--- ChangeLog
+++ ChangeLog
@@ -1,3 +1,26 @@
+ver 0.35:
+	Fix regression on suspend on opening.
+	Fix suspend stream on opening if no data.
+	Fix memory leaks in phonebook-tracker module.
+	Fix not responding Not Found for filtered vcard-listing.
+	Fix not responding Not Found when no entry is found.
+	Fix issue with X-IRMC-CALL-DATETIME format.
+
+ver 0.34:
+	Fix issue with error detection and last part of transfer buffer.
+	Fix issue with not detecting errors on small files.
+	Fix issue with emitting TransferCompleted twice.
+	Fix issue with default call type from call log.
+	Fix issue with websiteUrl query field not being generic.
+	Add support for binding mch, ich, och and cch call logs.
+	Add handling of TITLE contact's field in vCard.
+
+ver 0.33:
+	Fix issue with handling of phonebook size during pull request.
+	Add support for handling of UID contact field in vCard.
+	Add support for handling more than one address in vCard.
+	Add support for merging address fields into single field.
+
 ver 0.32:
 	Fix issues caused by dependencies on phonebook source.
 	Fix handling empty fields in vCards.
--- Makefile.am
+++ Makefile.am
@@ -11,13 +11,16 @@
 		test/pull-business-card test/exchange-business-cards \
 		test/list-folders test/pbap-client test/ftp-client
 
-gdbus_sources = gdbus/gdbus.h gdbus/mainloop.c gdbus/object.c gdbus/watch.c
+gdbus_sources = gdbus/gdbus.h gdbus/mainloop.c gdbus/watch.c \
+					gdbus/object.c gdbus/polkit.c
 
 gwobex_sources = gwobex/gw-obex.h gwobex/gw-obex.c \
 			gwobex/obex-priv.h gwobex/obex-priv.c \
 			gwobex/obex-xfer.h gwobex/obex-xfer.c \
 			gwobex/utils.h gwobex/utils.c gwobex/log.h
 
+btio_sources = btio/btio.h btio/btio.c
+
 libexec_PROGRAMS =
 
 if SERVER
@@ -65,11 +68,10 @@
 
 libexec_PROGRAMS += src/obexd
 
-src_obexd_SOURCES = $(gdbus_sources) $(builtin_sources) \
+src_obexd_SOURCES = $(gdbus_sources) $(builtin_sources) $(btio_sources) \
 			src/main.c src/obexd.h src/plugin.h src/plugin.c \
-			src/log.h src/log.c src/btio.h src/btio.c \
-			src/dbus.h src/manager.c src/obex.h src/obex.c \
-			src/obex-priv.h \
+			src/log.h src/log.c src/dbus.h src/manager.c \
+			src/obex.h src/obex.c src/obex-priv.h \
 			src/mimetype.h src/mimetype.c \
 			src/service.h src/service.c \
 			src/transport.h src/transport.c \
@@ -107,13 +109,12 @@
 
 libexec_PROGRAMS += client/obex-client
 
-client_obex_client_SOURCES = $(gdbus_sources) $(gwobex_sources) client/main.c \
-				client/session.h client/session.c \
+client_obex_client_SOURCES = $(gdbus_sources) $(gwobex_sources) $(btio_sources) \
+				client/main.c client/session.h client/session.c \
 				src/log.h src/log.c \
 				client/pbap.h client/pbap.c \
 				client/sync.h client/sync.c \
-				client/transfer.h client/transfer.c \
-				src/btio.c src/btio.h
+				client/transfer.h client/transfer.c
 
 client_obex_client_LDADD = @GLIB_LIBS@ @DBUS_LIBS@ @OPENOBEX_LIBS@ @BLUEZ_LIBS@
 endif
@@ -126,7 +127,8 @@
 			-DOBEX_PLUGIN_BUILTIN -DPLUGINDIR=\""$(plugindir)"\"
 
 INCLUDES = -I$(builddir)/src -I$(srcdir)/src -I$(srcdir)/plugins \
-				-I$(srcdir)/gdbus -I$(srcdir)/gwobex
+				-I$(srcdir)/gdbus -I$(srcdir)/gwobex \
+				-I$(srcdir)/btio
 
 CLEANFILES = $(service_DATA) $(builtin_files)
 
--- Makefile.in
+++ Makefile.in
@@ -91,56 +91,57 @@
 @CLIENT_TRUE at am__EXEEXT_2 = client/obex-client$(EXEEXT)
 PROGRAMS = $(libexec_PROGRAMS) $(noinst_PROGRAMS)
 am__client_obex_client_SOURCES_DIST = gdbus/gdbus.h gdbus/mainloop.c \
-	gdbus/object.c gdbus/watch.c gwobex/gw-obex.h gwobex/gw-obex.c \
-	gwobex/obex-priv.h gwobex/obex-priv.c gwobex/obex-xfer.h \
-	gwobex/obex-xfer.c gwobex/utils.h gwobex/utils.c gwobex/log.h \
+	gdbus/watch.c gdbus/object.c gdbus/polkit.c gwobex/gw-obex.h \
+	gwobex/gw-obex.c gwobex/obex-priv.h gwobex/obex-priv.c \
+	gwobex/obex-xfer.h gwobex/obex-xfer.c gwobex/utils.h \
+	gwobex/utils.c gwobex/log.h btio/btio.h btio/btio.c \
 	client/main.c client/session.h client/session.c src/log.h \
 	src/log.c client/pbap.h client/pbap.c client/sync.h \
-	client/sync.c client/transfer.h client/transfer.c src/btio.c \
-	src/btio.h
+	client/sync.c client/transfer.h client/transfer.c
 am__dirstamp = $(am__leading_dot)dirstamp
-am__objects_1 = gdbus/mainloop.$(OBJEXT) gdbus/object.$(OBJEXT) \
-	gdbus/watch.$(OBJEXT)
+am__objects_1 = gdbus/mainloop.$(OBJEXT) gdbus/watch.$(OBJEXT) \
+	gdbus/object.$(OBJEXT) gdbus/polkit.$(OBJEXT)
 am__objects_2 = gwobex/gw-obex.$(OBJEXT) gwobex/obex-priv.$(OBJEXT) \
 	gwobex/obex-xfer.$(OBJEXT) gwobex/utils.$(OBJEXT)
+am__objects_3 = btio/btio.$(OBJEXT)
 @CLIENT_TRUE at am_client_obex_client_OBJECTS = $(am__objects_1) \
- at CLIENT_TRUE@	$(am__objects_2) client/main.$(OBJEXT) \
- at CLIENT_TRUE@	client/session.$(OBJEXT) src/log.$(OBJEXT) \
- at CLIENT_TRUE@	client/pbap.$(OBJEXT) client/sync.$(OBJEXT) \
- at CLIENT_TRUE@	client/transfer.$(OBJEXT) src/btio.$(OBJEXT)
+ at CLIENT_TRUE@	$(am__objects_2) $(am__objects_3) \
+ at CLIENT_TRUE@	client/main.$(OBJEXT) client/session.$(OBJEXT) \
+ at CLIENT_TRUE@	src/log.$(OBJEXT) client/pbap.$(OBJEXT) \
+ at CLIENT_TRUE@	client/sync.$(OBJEXT) client/transfer.$(OBJEXT)
 client_obex_client_OBJECTS = $(am_client_obex_client_OBJECTS)
 client_obex_client_DEPENDENCIES =
 AM_V_lt = $(am__v_lt_$(V))
 am__v_lt_ = $(am__v_lt_$(AM_DEFAULT_VERBOSITY))
 am__v_lt_0 = --silent
 am__src_obexd_SOURCES_DIST = gdbus/gdbus.h gdbus/mainloop.c \
-	gdbus/object.c gdbus/watch.c plugins/bluetooth.c plugins/usb.c \
-	plugins/filesystem.c plugins/filesystem.h \
-	plugins/nokia-backup.c plugins/opp.c plugins/ftp.c \
-	plugins/pbap.c plugins/phonebook.h plugins/vcard.h \
-	plugins/vcard.c plugins/irmc.c plugins/syncevolution.c \
-	src/main.c src/obexd.h src/plugin.h src/plugin.c src/log.h \
-	src/log.c src/btio.h src/btio.c src/dbus.h src/manager.c \
-	src/obex.h src/obex.c src/obex-priv.h src/mimetype.h \
-	src/mimetype.c src/service.h src/service.c src/transport.h \
-	src/transport.c src/server.h src/server.c
- at SERVER_TRUE@@USB_TRUE at am__objects_3 = plugins/usb.$(OBJEXT)
- at NOKIA_BACKUP_TRUE@@SERVER_TRUE at am__objects_4 = plugins/nokia-backup.$(OBJEXT)
- at SERVER_TRUE@am__objects_5 = plugins/bluetooth.$(OBJEXT) \
- at SERVER_TRUE@	$(am__objects_3) plugins/filesystem.$(OBJEXT) \
- at SERVER_TRUE@	$(am__objects_4) plugins/opp.$(OBJEXT) \
+	gdbus/watch.c gdbus/object.c gdbus/polkit.c \
+	plugins/bluetooth.c plugins/usb.c plugins/filesystem.c \
+	plugins/filesystem.h plugins/nokia-backup.c plugins/opp.c \
+	plugins/ftp.c plugins/pbap.c plugins/phonebook.h \
+	plugins/vcard.h plugins/vcard.c plugins/irmc.c \
+	plugins/syncevolution.c btio/btio.h btio/btio.c src/main.c \
+	src/obexd.h src/plugin.h src/plugin.c src/log.h src/log.c \
+	src/dbus.h src/manager.c src/obex.h src/obex.c src/obex-priv.h \
+	src/mimetype.h src/mimetype.c src/service.h src/service.c \
+	src/transport.h src/transport.c src/server.h src/server.c
+ at SERVER_TRUE@@USB_TRUE at am__objects_4 = plugins/usb.$(OBJEXT)
+ at NOKIA_BACKUP_TRUE@@SERVER_TRUE at am__objects_5 = plugins/nokia-backup.$(OBJEXT)
+ at SERVER_TRUE@am__objects_6 = plugins/bluetooth.$(OBJEXT) \
+ at SERVER_TRUE@	$(am__objects_4) plugins/filesystem.$(OBJEXT) \
+ at SERVER_TRUE@	$(am__objects_5) plugins/opp.$(OBJEXT) \
 @SERVER_TRUE@	plugins/ftp.$(OBJEXT) plugins/pbap.$(OBJEXT) \
 @SERVER_TRUE@	plugins/vcard.$(OBJEXT) plugins/irmc.$(OBJEXT) \
 @SERVER_TRUE@	plugins/syncevolution.$(OBJEXT)
- at SERVER_TRUE@am_src_obexd_OBJECTS = $(am__objects_1) $(am__objects_5) \
- at SERVER_TRUE@	src/main.$(OBJEXT) src/plugin.$(OBJEXT) \
- at SERVER_TRUE@	src/log.$(OBJEXT) src/btio.$(OBJEXT) \
+ at SERVER_TRUE@am_src_obexd_OBJECTS = $(am__objects_1) $(am__objects_6) \
+ at SERVER_TRUE@	$(am__objects_3) src/main.$(OBJEXT) \
+ at SERVER_TRUE@	src/plugin.$(OBJEXT) src/log.$(OBJEXT) \
 @SERVER_TRUE@	src/manager.$(OBJEXT) src/obex.$(OBJEXT) \
 @SERVER_TRUE@	src/mimetype.$(OBJEXT) src/service.$(OBJEXT) \
 @SERVER_TRUE@	src/transport.$(OBJEXT) src/server.$(OBJEXT)
- at SERVER_TRUE@am__objects_6 = plugins/phonebook.$(OBJEXT)
- at SERVER_TRUE@am__objects_7 = $(am__objects_6)
- at SERVER_TRUE@nodist_src_obexd_OBJECTS = $(am__objects_7)
+ at SERVER_TRUE@am__objects_7 = plugins/phonebook.$(OBJEXT)
+ at SERVER_TRUE@am__objects_8 = $(am__objects_7)
+ at SERVER_TRUE@nodist_src_obexd_OBJECTS = $(am__objects_8)
 src_obexd_OBJECTS = $(am_src_obexd_OBJECTS) \
 	$(nodist_src_obexd_OBJECTS)
 src_obexd_DEPENDENCIES =
@@ -336,12 +337,15 @@
 		test/pull-business-card test/exchange-business-cards \
 		test/list-folders test/pbap-client test/ftp-client
 
-gdbus_sources = gdbus/gdbus.h gdbus/mainloop.c gdbus/object.c gdbus/watch.c
+gdbus_sources = gdbus/gdbus.h gdbus/mainloop.c gdbus/watch.c \
+					gdbus/object.c gdbus/polkit.c
+
 gwobex_sources = gwobex/gw-obex.h gwobex/gw-obex.c \
 			gwobex/obex-priv.h gwobex/obex-priv.c \
 			gwobex/obex-xfer.h gwobex/obex-xfer.c \
 			gwobex/utils.h gwobex/utils.c gwobex/log.h
 
+btio_sources = btio/btio.h btio/btio.c
 @SERVER_TRUE at confdir = $(sysconfdir)/obex
 @SERVER_TRUE at builtin_modules = bluetooth $(am__append_2) filesystem \
 @SERVER_TRUE@	$(am__append_4) opp ftp pbap irmc syncevolution
@@ -352,11 +356,10 @@
 @SERVER_TRUE@	plugins/vcard.h plugins/vcard.c plugins/irmc.c \
 @SERVER_TRUE@	plugins/syncevolution.c
 @SERVER_TRUE at builtin_nodist = plugins/phonebook.c
- at SERVER_TRUE@src_obexd_SOURCES = $(gdbus_sources) $(builtin_sources) \
+ at SERVER_TRUE@src_obexd_SOURCES = $(gdbus_sources) $(builtin_sources) $(btio_sources) \
 @SERVER_TRUE@			src/main.c src/obexd.h src/plugin.h src/plugin.c \
- at SERVER_TRUE@			src/log.h src/log.c src/btio.h src/btio.c \
- at SERVER_TRUE@			src/dbus.h src/manager.c src/obex.h src/obex.c \
- at SERVER_TRUE@			src/obex-priv.h \
+ at SERVER_TRUE@			src/log.h src/log.c src/dbus.h src/manager.c \
+ at SERVER_TRUE@			src/obex.h src/obex.c src/obex-priv.h \
 @SERVER_TRUE@			src/mimetype.h src/mimetype.c \
 @SERVER_TRUE@			src/service.h src/service.c \
 @SERVER_TRUE@			src/transport.h src/transport.c \
@@ -373,13 +376,12 @@
 @SERVER_TRUE at plugin_LTLIBRARIES = 
 @SERVER_TRUE at test_obex_test_SOURCES = $(gwobex_sources) test/main.c
 @SERVER_TRUE at test_obex_test_LDADD = @OPENOBEX_LIBS@ @BLUEZ_LIBS@ @GLIB_LIBS@
- at CLIENT_TRUE@client_obex_client_SOURCES = $(gdbus_sources) $(gwobex_sources) client/main.c \
- at CLIENT_TRUE@				client/session.h client/session.c \
+ at CLIENT_TRUE@client_obex_client_SOURCES = $(gdbus_sources) $(gwobex_sources) $(btio_sources) \
+ at CLIENT_TRUE@				client/main.c client/session.h client/session.c \
 @CLIENT_TRUE@				src/log.h src/log.c \
 @CLIENT_TRUE@				client/pbap.h client/pbap.c \
 @CLIENT_TRUE@				client/sync.h client/sync.c \
- at CLIENT_TRUE@				client/transfer.h client/transfer.c \
- at CLIENT_TRUE@				src/btio.c src/btio.h
+ at CLIENT_TRUE@				client/transfer.h client/transfer.c
 
 @CLIENT_TRUE at client_obex_client_LDADD = @GLIB_LIBS@ @DBUS_LIBS@ @OPENOBEX_LIBS@ @BLUEZ_LIBS@
 service_DATA = $(service_in_files:.service.in=.service)
@@ -389,7 +391,8 @@
 			-DOBEX_PLUGIN_BUILTIN -DPLUGINDIR=\""$(plugindir)"\"
 
 INCLUDES = -I$(builddir)/src -I$(srcdir)/src -I$(srcdir)/plugins \
-				-I$(srcdir)/gdbus -I$(srcdir)/gwobex
+				-I$(srcdir)/gdbus -I$(srcdir)/gwobex \
+				-I$(srcdir)/btio
 
 CLEANFILES = $(service_DATA) $(builtin_files)
 EXTRA_DIST = src/genbuiltin $(doc_files) $(test_files) src/obex.conf \
@@ -549,9 +552,11 @@
 	@: > gdbus/$(DEPDIR)/$(am__dirstamp)
 gdbus/mainloop.$(OBJEXT): gdbus/$(am__dirstamp) \
 	gdbus/$(DEPDIR)/$(am__dirstamp)
+gdbus/watch.$(OBJEXT): gdbus/$(am__dirstamp) \
+	gdbus/$(DEPDIR)/$(am__dirstamp)
 gdbus/object.$(OBJEXT): gdbus/$(am__dirstamp) \
 	gdbus/$(DEPDIR)/$(am__dirstamp)
-gdbus/watch.$(OBJEXT): gdbus/$(am__dirstamp) \
+gdbus/polkit.$(OBJEXT): gdbus/$(am__dirstamp) \
 	gdbus/$(DEPDIR)/$(am__dirstamp)
 gwobex/$(am__dirstamp):
 	@$(MKDIR_P) gwobex
@@ -567,6 +572,14 @@
 	gwobex/$(DEPDIR)/$(am__dirstamp)
 gwobex/utils.$(OBJEXT): gwobex/$(am__dirstamp) \
 	gwobex/$(DEPDIR)/$(am__dirstamp)
+btio/$(am__dirstamp):
+	@$(MKDIR_P) btio
+	@: > btio/$(am__dirstamp)
+btio/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) btio/$(DEPDIR)
+	@: > btio/$(DEPDIR)/$(am__dirstamp)
+btio/btio.$(OBJEXT): btio/$(am__dirstamp) \
+	btio/$(DEPDIR)/$(am__dirstamp)
 client/$(am__dirstamp):
 	@$(MKDIR_P) client
 	@: > client/$(am__dirstamp)
@@ -590,7 +603,6 @@
 	client/$(DEPDIR)/$(am__dirstamp)
 client/transfer.$(OBJEXT): client/$(am__dirstamp) \
 	client/$(DEPDIR)/$(am__dirstamp)
-src/btio.$(OBJEXT): src/$(am__dirstamp) src/$(DEPDIR)/$(am__dirstamp)
 client/obex-client$(EXEEXT): $(client_obex_client_OBJECTS) $(client_obex_client_DEPENDENCIES) client/$(am__dirstamp)
 	@rm -f client/obex-client$(EXEEXT)
 	$(AM_V_CCLD)$(LINK) $(client_obex_client_OBJECTS) $(client_obex_client_LDADD) $(LIBS)
@@ -653,6 +665,7 @@
 
 mostlyclean-compile:
 	-rm -f *.$(OBJEXT)
+	-rm -f btio/btio.$(OBJEXT)
 	-rm -f client/main.$(OBJEXT)
 	-rm -f client/pbap.$(OBJEXT)
 	-rm -f client/session.$(OBJEXT)
@@ -660,6 +673,7 @@
 	-rm -f client/transfer.$(OBJEXT)
 	-rm -f gdbus/mainloop.$(OBJEXT)
 	-rm -f gdbus/object.$(OBJEXT)
+	-rm -f gdbus/polkit.$(OBJEXT)
 	-rm -f gdbus/watch.$(OBJEXT)
 	-rm -f gwobex/gw-obex.$(OBJEXT)
 	-rm -f gwobex/obex-priv.$(OBJEXT)
@@ -676,7 +690,6 @@
 	-rm -f plugins/syncevolution.$(OBJEXT)
 	-rm -f plugins/usb.$(OBJEXT)
 	-rm -f plugins/vcard.$(OBJEXT)
-	-rm -f src/btio.$(OBJEXT)
 	-rm -f src/log.$(OBJEXT)
 	-rm -f src/main.$(OBJEXT)
 	-rm -f src/manager.$(OBJEXT)
@@ -691,6 +704,7 @@
 distclean-compile:
 	-rm -f *.tab.c
 
+ at AMDEP_TRUE@@am__include@ @am__quote at btio/$(DEPDIR)/btio.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at client/$(DEPDIR)/main.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at client/$(DEPDIR)/pbap.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at client/$(DEPDIR)/session.Po at am__quote@
@@ -698,6 +712,7 @@
 @AMDEP_TRUE@@am__include@ @am__quote at client/$(DEPDIR)/transfer.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at gdbus/$(DEPDIR)/mainloop.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at gdbus/$(DEPDIR)/object.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at gdbus/$(DEPDIR)/polkit.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at gdbus/$(DEPDIR)/watch.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at gwobex/$(DEPDIR)/gw-obex.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at gwobex/$(DEPDIR)/obex-priv.Po at am__quote@
@@ -714,7 +729,6 @@
 @AMDEP_TRUE@@am__include@ @am__quote at plugins/$(DEPDIR)/syncevolution.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at plugins/$(DEPDIR)/usb.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at plugins/$(DEPDIR)/vcard.Po at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at src/$(DEPDIR)/btio.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at src/$(DEPDIR)/log.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at src/$(DEPDIR)/main.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at src/$(DEPDIR)/manager.Po at am__quote@
@@ -1013,6 +1027,8 @@
 distclean-generic:
 	-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
 	-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+	-rm -f btio/$(DEPDIR)/$(am__dirstamp)
+	-rm -f btio/$(am__dirstamp)
 	-rm -f client/$(DEPDIR)/$(am__dirstamp)
 	-rm -f client/$(am__dirstamp)
 	-rm -f gdbus/$(DEPDIR)/$(am__dirstamp)
@@ -1037,7 +1053,7 @@
 
 distclean: distclean-am
 	-rm -f $(am__CONFIG_DISTCLEAN_FILES)
-	-rm -rf client/$(DEPDIR) gdbus/$(DEPDIR) gwobex/$(DEPDIR) plugins/$(DEPDIR) src/$(DEPDIR) test/$(DEPDIR)
+	-rm -rf btio/$(DEPDIR) client/$(DEPDIR) gdbus/$(DEPDIR) gwobex/$(DEPDIR) plugins/$(DEPDIR) src/$(DEPDIR) test/$(DEPDIR)
 	-rm -f Makefile
 distclean-am: clean-am distclean-compile distclean-generic \
 	distclean-hdr distclean-libtool distclean-tags
@@ -1085,7 +1101,7 @@
 maintainer-clean: maintainer-clean-am
 	-rm -f $(am__CONFIG_DISTCLEAN_FILES)
 	-rm -rf $(top_srcdir)/autom4te.cache
-	-rm -rf client/$(DEPDIR) gdbus/$(DEPDIR) gwobex/$(DEPDIR) plugins/$(DEPDIR) src/$(DEPDIR) test/$(DEPDIR)
+	-rm -rf btio/$(DEPDIR) client/$(DEPDIR) gdbus/$(DEPDIR) gwobex/$(DEPDIR) plugins/$(DEPDIR) src/$(DEPDIR) test/$(DEPDIR)
 	-rm -f Makefile
 maintainer-clean-am: distclean-am maintainer-clean-generic
 
--- btio
+++ btio
+(directory)
--- btio/btio.c
+++ btio/btio.c
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2009-2010  Marcel Holtmann <marcel at holtmann.org>
+ *  Copyright (C) 2009-2010  Nokia Corporation
+ *
+ *
+ *  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
+ *
+ */
+#include <stdarg.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <poll.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/l2cap.h>
+#include <bluetooth/rfcomm.h>
+#include <bluetooth/sco.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+
+#include <glib.h>
+
+#include "btio.h"
+
+#define ERROR_FAILED(gerr, str, err) \
+		g_set_error(gerr, BT_IO_ERROR, BT_IO_ERROR_FAILED, \
+				str ": %s (%d)", strerror(err), err)
+
+#define DEFAULT_DEFER_TIMEOUT 30
+
+struct set_opts {
+	bdaddr_t src;
+	bdaddr_t dst;
+	int defer;
+	int sec_level;
+	uint8_t channel;
+	uint16_t psm;
+	uint16_t mtu;
+	uint16_t imtu;
+	uint16_t omtu;
+	int master;
+	uint8_t mode;
+};
+
+struct connect {
+	BtIOConnect connect;
+	gpointer user_data;
+	GDestroyNotify destroy;
+};
+
+struct accept {
+	BtIOConnect connect;
+	gpointer user_data;
+	GDestroyNotify destroy;
+};
+
+struct server {
+	BtIOConnect connect;
+	BtIOConfirm confirm;
+	gpointer user_data;
+	GDestroyNotify destroy;
+};
+
+static void server_remove(struct server *server)
+{
+	if (server->destroy)
+		server->destroy(server->user_data);
+	g_free(server);
+}
+
+static void connect_remove(struct connect *conn)
+{
+	if (conn->destroy)
+		conn->destroy(conn->user_data);
+	g_free(conn);
+}
+
+static void accept_remove(struct accept *accept)
+{
+	if (accept->destroy)
+		accept->destroy(accept->user_data);
+	g_free(accept);
+}
+
+static gboolean check_nval(GIOChannel *io)
+{
+	struct pollfd fds;
+
+	memset(&fds, 0, sizeof(fds));
+	fds.fd = g_io_channel_unix_get_fd(io);
+	fds.events = POLLNVAL;
+
+	if (poll(&fds, 1, 0) > 0 && (fds.revents & POLLNVAL))
+		return TRUE;
+
+	return FALSE;
+}
+
+static gboolean accept_cb(GIOChannel *io, GIOCondition cond,
+							gpointer user_data)
+{
+	struct accept *accept = user_data;
+	GError *err = NULL;
+
+	/* If the user aborted this accept attempt */
+	if ((cond & G_IO_NVAL) || check_nval(io))
+		return FALSE;
+
+	if (cond & (G_IO_HUP | G_IO_ERR))
+		g_set_error(&err, BT_IO_ERROR, BT_IO_ERROR_DISCONNECTED,
+				"HUP or ERR on socket");
+
+	accept->connect(io, err, accept->user_data);
+
+	g_clear_error(&err);
+
+	return FALSE;
+}
+
+static gboolean connect_cb(GIOChannel *io, GIOCondition cond,
+							gpointer user_data)
+{
+	struct connect *conn = user_data;
+	GError *gerr = NULL;
+
+	/* If the user aborted this connect attempt */
+	if ((cond & G_IO_NVAL) || check_nval(io))
+		return FALSE;
+
+	if (cond & G_IO_OUT) {
+		int err = 0, sock = g_io_channel_unix_get_fd(io);
+		socklen_t len = sizeof(err);
+
+		if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &err, &len) < 0)
+			err = errno;
+
+		if (err)
+			g_set_error(&gerr, BT_IO_ERROR,
+					BT_IO_ERROR_CONNECT_FAILED, "%s (%d)",
+					strerror(err), err);
+	} else if (cond & (G_IO_HUP | G_IO_ERR))
+		g_set_error(&gerr, BT_IO_ERROR, BT_IO_ERROR_CONNECT_FAILED,
+				"HUP or ERR on socket");
+
+	conn->connect(io, gerr, conn->user_data);
+
+	if (gerr)
+		g_error_free(gerr);
+
+	return FALSE;
+}
+
+static gboolean server_cb(GIOChannel *io, GIOCondition cond,
+							gpointer user_data)
+{
+	struct server *server = user_data;
+	int srv_sock, cli_sock;
+	GIOChannel *cli_io;
+
+	/* If the user closed the server */
+	if ((cond & G_IO_NVAL) || check_nval(io))
+		return FALSE;
+
+	srv_sock = g_io_channel_unix_get_fd(io);
+
+	cli_sock = accept(srv_sock, NULL, NULL);
+	if (cli_sock < 0)
+		return TRUE;
+
+	cli_io = g_io_channel_unix_new(cli_sock);
+
+	g_io_channel_set_close_on_unref(cli_io, TRUE);
+	g_io_channel_set_flags(cli_io, G_IO_FLAG_NONBLOCK, NULL);
+
+	if (server->confirm)
+		server->confirm(cli_io, server->user_data);
+	else
+		server->connect(cli_io, NULL, server->user_data);
+
+	g_io_channel_unref(cli_io);
+
+	return TRUE;
+}
+
+static void server_add(GIOChannel *io, BtIOConnect connect,
+				BtIOConfirm confirm, gpointer user_data,
+				GDestroyNotify destroy)
+{
+	struct server *server;
+	GIOCondition cond;
+
+	server = g_new0(struct server, 1);
+	server->connect = connect;
+	server->confirm = confirm;
+	server->user_data = user_data;
+	server->destroy = destroy;
+
+	cond = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
+	g_io_add_watch_full(io, G_PRIORITY_DEFAULT, cond, server_cb, server,
+					(GDestroyNotify) server_remove);
+}
+
+static void connect_add(GIOChannel *io, BtIOConnect connect,
+				gpointer user_data, GDestroyNotify destroy)
+{
+	struct connect *conn;
+	GIOCondition cond;
+
+	conn = g_new0(struct connect, 1);
+	conn->connect = connect;
+	conn->user_data = user_data;
+	conn->destroy = destroy;
+
+	cond = G_IO_OUT | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
+	g_io_add_watch_full(io, G_PRIORITY_DEFAULT, cond, connect_cb, conn,
+					(GDestroyNotify) connect_remove);
+}
+
+static void accept_add(GIOChannel *io, BtIOConnect connect, gpointer user_data,
+							GDestroyNotify destroy)
+{
+	struct accept *accept;
+	GIOCondition cond;
+
+	accept = g_new0(struct accept, 1);
+	accept->connect = connect;
+	accept->user_data = user_data;
+	accept->destroy = destroy;
+
+	cond = G_IO_OUT | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
+	g_io_add_watch_full(io, G_PRIORITY_DEFAULT, cond, accept_cb, accept,
+					(GDestroyNotify) accept_remove);
+}
+
+static int l2cap_bind(int sock, const bdaddr_t *src, uint16_t psm, GError **err)
+{
+	struct sockaddr_l2 addr;
+
+	memset(&addr, 0, sizeof(addr));
+	addr.l2_family = AF_BLUETOOTH;
+	bacpy(&addr.l2_bdaddr, src);
+	addr.l2_psm = htobs(psm);
+
+	if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		ERROR_FAILED(err, "l2cap_bind", errno);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int l2cap_connect(int sock, const bdaddr_t *dst, uint16_t psm)
+{
+	int err;
+	struct sockaddr_l2 addr;
+
+	memset(&addr, 0, sizeof(addr));
+	addr.l2_family = AF_BLUETOOTH;
+	bacpy(&addr.l2_bdaddr, dst);
+	addr.l2_psm = htobs(psm);
+
+	err = connect(sock, (struct sockaddr *) &addr, sizeof(addr));
+	if (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS))
+		return err;
+
+	return 0;
+}
+
+static int l2cap_set_master(int sock, int master)
+{
+	int flags;
+	socklen_t len;
+
+	len = sizeof(flags);
+	if (getsockopt(sock, SOL_L2CAP, L2CAP_LM, &flags, &len) < 0)
+		return -errno;
+
+	if (master) {
+		if (flags & L2CAP_LM_MASTER)
+			return 0;
+		flags |= L2CAP_LM_MASTER;
+	} else {
+		if (!(flags & L2CAP_LM_MASTER))
+			return 0;
+		flags &= ~L2CAP_LM_MASTER;
+	}
+
+	if (setsockopt(sock, SOL_L2CAP, L2CAP_LM, &flags, sizeof(flags)) < 0)
+		return -errno;
+
+	return 0;
+}
+
+static int rfcomm_set_master(int sock, int master)
+{
+	int flags;
+	socklen_t len;
+
+	len = sizeof(flags);
+	if (getsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &flags, &len) < 0)
+		return -errno;
+
+	if (master) {
+		if (flags & RFCOMM_LM_MASTER)
+			return 0;
+		flags |= RFCOMM_LM_MASTER;
+	} else {
+		if (!(flags & RFCOMM_LM_MASTER))
+			return 0;
+		flags &= ~RFCOMM_LM_MASTER;
+	}
+
+	if (setsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &flags, sizeof(flags)) < 0)
+		return -errno;
+
+	return 0;
+}
+
+static int l2cap_set_lm(int sock, int level)
+{
+	int lm_map[] = {
+		0,
+		L2CAP_LM_AUTH,
+		L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT,
+		L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT | L2CAP_LM_SECURE,
+	}, opt = lm_map[level];
+
+	if (setsockopt(sock, SOL_L2CAP, L2CAP_LM, &opt, sizeof(opt)) < 0)
+		return -errno;
+
+	return 0;
+}
+
+static int rfcomm_set_lm(int sock, int level)
+{
+	int lm_map[] = {
+		0,
+		RFCOMM_LM_AUTH,
+		RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT,
+		RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT | RFCOMM_LM_SECURE,
+	}, opt = lm_map[level];
+
+	if (setsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &opt, sizeof(opt)) < 0)
+		return -errno;
+
+	return 0;
+}
+
+static gboolean set_sec_level(int sock, BtIOType type, int level, GError **err)
+{
+	struct bt_security sec;
+	int ret;
+
+	if (level < BT_SECURITY_LOW || level > BT_SECURITY_HIGH) {
+		g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+				"Valid security level range is %d-%d",
+				BT_SECURITY_LOW, BT_SECURITY_HIGH);
+		return FALSE;
+	}
+
+	memset(&sec, 0, sizeof(sec));
+	sec.level = level;
+
+	if (setsockopt(sock, SOL_BLUETOOTH, BT_SECURITY, &sec,
+							sizeof(sec)) == 0)
+		return TRUE;
+
+	if (errno != ENOPROTOOPT) {
+		ERROR_FAILED(err, "setsockopt(BT_SECURITY)", errno);
+		return FALSE;
+	}
+
+	if (type == BT_IO_L2CAP)
+		ret = l2cap_set_lm(sock, level);
+	else
+		ret = rfcomm_set_lm(sock, level);
+
+	if (ret < 0) {
+		ERROR_FAILED(err, "setsockopt(LM)", -ret);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static int l2cap_get_lm(int sock, int *sec_level)
+{
+	int opt;
+	socklen_t len;
+
+	len = sizeof(opt);
+	if (getsockopt(sock, SOL_L2CAP, L2CAP_LM, &opt, &len) < 0)
+		return -errno;
+
+	*sec_level = 0;
+
+	if (opt & L2CAP_LM_AUTH)
+		*sec_level = BT_SECURITY_LOW;
+	if (opt & L2CAP_LM_ENCRYPT)
+		*sec_level = BT_SECURITY_MEDIUM;
+	if (opt & L2CAP_LM_SECURE)
+		*sec_level = BT_SECURITY_HIGH;
+
+	return 0;
+}
+
+static int rfcomm_get_lm(int sock, int *sec_level)
+{
+	int opt;
+	socklen_t len;
+
+	len = sizeof(opt);
+	if (getsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &opt, &len) < 0)
+		return -errno;
+
+	*sec_level = 0;
+
+	if (opt & RFCOMM_LM_AUTH)
+		*sec_level = BT_SECURITY_LOW;
+	if (opt & RFCOMM_LM_ENCRYPT)
+		*sec_level = BT_SECURITY_MEDIUM;
+	if (opt & RFCOMM_LM_SECURE)
+		*sec_level = BT_SECURITY_HIGH;
+
+	return 0;
+}
+
+static gboolean get_sec_level(int sock, BtIOType type, int *level,
+								GError **err)
+{
+	struct bt_security sec;
+	socklen_t len;
+	int ret;
+
+	memset(&sec, 0, sizeof(sec));
+	len = sizeof(sec);
+	if (getsockopt(sock, SOL_BLUETOOTH, BT_SECURITY, &sec, &len) == 0) {
+		*level = sec.level;
+		return TRUE;
+	}
+
+	if (errno != ENOPROTOOPT) {
+		ERROR_FAILED(err, "getsockopt(BT_SECURITY)", errno);
+		return FALSE;
+	}
+
+	if (type == BT_IO_L2CAP)
+		ret = l2cap_get_lm(sock, level);
+	else
+		ret = rfcomm_get_lm(sock, level);
+
+	if (ret < 0) {
+		ERROR_FAILED(err, "getsockopt(LM)", -ret);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static gboolean l2cap_set(int sock, int sec_level, uint16_t imtu, uint16_t omtu,
+					uint8_t mode, int master, GError **err)
+{
+	if (imtu || omtu || mode) {
+		struct l2cap_options l2o;
+		socklen_t len;
+
+		memset(&l2o, 0, sizeof(l2o));
+		len = sizeof(l2o);
+		if (getsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, &l2o,
+								&len) < 0) {
+			ERROR_FAILED(err, "getsockopt(L2CAP_OPTIONS)", errno);
+			return FALSE;
+		}
+
+		if (imtu)
+			l2o.imtu = imtu;
+		if (omtu)
+			l2o.omtu = omtu;
+		if (mode)
+			l2o.mode = mode;
+
+		if (setsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, &l2o,
+							sizeof(l2o)) < 0) {
+			ERROR_FAILED(err, "setsockopt(L2CAP_OPTIONS)", errno);
+			return FALSE;
+		}
+	}
+
+	if (master >= 0 && l2cap_set_master(sock, master) < 0) {
+		ERROR_FAILED(err, "l2cap_set_master", errno);
+		return FALSE;
+	}
+
+	if (sec_level && !set_sec_level(sock, BT_IO_L2CAP, sec_level, err))
+		return FALSE;
+
+	return TRUE;
+}
+
+static int rfcomm_bind(int sock,
+		const bdaddr_t *src, uint8_t channel, GError **err)
+{
+	struct sockaddr_rc addr;
+
+	memset(&addr, 0, sizeof(addr));
+	addr.rc_family = AF_BLUETOOTH;
+	bacpy(&addr.rc_bdaddr, src);
+	addr.rc_channel = channel;
+
+	if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		ERROR_FAILED(err, "rfcomm_bind", errno);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int rfcomm_connect(int sock, const bdaddr_t *dst, uint8_t channel)
+{
+	int err;
+	struct sockaddr_rc addr;
+
+	memset(&addr, 0, sizeof(addr));
+	addr.rc_family = AF_BLUETOOTH;
+	bacpy(&addr.rc_bdaddr, dst);
+	addr.rc_channel = channel;
+
+	err = connect(sock, (struct sockaddr *) &addr, sizeof(addr));
+	if (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS))
+		return err;
+
+	return 0;
+}
+
+static gboolean rfcomm_set(int sock, int sec_level, int master, GError **err)
+{
+	if (sec_level && !set_sec_level(sock, BT_IO_RFCOMM, sec_level, err))
+		return FALSE;
+
+	if (master >= 0 && rfcomm_set_master(sock, master) < 0) {
+		ERROR_FAILED(err, "rfcomm_set_master", errno);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static int sco_bind(int sock, const bdaddr_t *src, GError **err)
+{
+	struct sockaddr_sco addr;
+
+	memset(&addr, 0, sizeof(addr));
+	addr.sco_family = AF_BLUETOOTH;
+	bacpy(&addr.sco_bdaddr, src);
+
+	if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		ERROR_FAILED(err, "sco_bind", errno);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int sco_connect(int sock, const bdaddr_t *dst)
+{
+	struct sockaddr_sco addr;
+	int err;
+
+	memset(&addr, 0, sizeof(addr));
+	addr.sco_family = AF_BLUETOOTH;
+	bacpy(&addr.sco_bdaddr, dst);
+
+	err = connect(sock, (struct sockaddr *) &addr, sizeof(addr));
+	if (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS))
+		return err;
+
+	return 0;
+}
+
+static gboolean sco_set(int sock, uint16_t mtu, GError **err)
+{
+	struct sco_options sco_opt;
+	socklen_t len;
+
+	if (!mtu)
+		return TRUE;
+
+	len = sizeof(sco_opt);
+	memset(&sco_opt, 0, len);
+	if (getsockopt(sock, SOL_SCO, SCO_OPTIONS, &sco_opt, &len) < 0) {
+		ERROR_FAILED(err, "getsockopt(SCO_OPTIONS)", errno);
+		return FALSE;
+	}
+
+	sco_opt.mtu = mtu;
+	if (setsockopt(sock, SOL_SCO, SCO_OPTIONS, &sco_opt,
+						sizeof(sco_opt)) < 0) {
+		ERROR_FAILED(err, "setsockopt(SCO_OPTIONS)", errno);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static gboolean parse_set_opts(struct set_opts *opts, GError **err,
+						BtIOOption opt1, va_list args)
+{
+	BtIOOption opt = opt1;
+	const char *str;
+
+	memset(opts, 0, sizeof(*opts));
+
+	/* Set defaults */
+	opts->defer = DEFAULT_DEFER_TIMEOUT;
+	opts->master = -1;
+	opts->sec_level = BT_IO_SEC_MEDIUM;
+	opts->mode = L2CAP_MODE_BASIC;
+
+	while (opt != BT_IO_OPT_INVALID) {
+		switch (opt) {
+		case BT_IO_OPT_SOURCE:
+			str = va_arg(args, const char *);
+			if (strncasecmp(str, "hci", 3) == 0)
+				hci_devba(atoi(str + 3), &opts->src);
+			else
+				str2ba(str, &opts->src);
+			break;
+		case BT_IO_OPT_SOURCE_BDADDR:
+			bacpy(&opts->src, va_arg(args, const bdaddr_t *));
+			break;
+		case BT_IO_OPT_DEST:
+			str2ba(va_arg(args, const char *), &opts->dst);
+			break;
+		case BT_IO_OPT_DEST_BDADDR:
+			bacpy(&opts->dst, va_arg(args, const bdaddr_t *));
+			break;
+		case BT_IO_OPT_DEFER_TIMEOUT:
+			opts->defer = va_arg(args, int);
+			break;
+		case BT_IO_OPT_SEC_LEVEL:
+			opts->sec_level = va_arg(args, int);
+			break;
+		case BT_IO_OPT_CHANNEL:
+			opts->channel = va_arg(args, int);
+			break;
+		case BT_IO_OPT_PSM:
+			opts->psm = va_arg(args, int);
+			break;
+		case BT_IO_OPT_MTU:
+			opts->mtu = va_arg(args, int);
+			opts->imtu = opts->mtu;
+			opts->omtu = opts->mtu;
+			break;
+		case BT_IO_OPT_OMTU:
+			opts->omtu = va_arg(args, int);
+			if (!opts->mtu)
+				opts->mtu = opts->omtu;
+			break;
+		case BT_IO_OPT_IMTU:
+			opts->imtu = va_arg(args, int);
+			if (!opts->mtu)
+				opts->mtu = opts->imtu;
+			break;
+		case BT_IO_OPT_MASTER:
+			opts->master = va_arg(args, gboolean);
+			break;
+		case BT_IO_OPT_MODE:
+			opts->mode = va_arg(args, int);
+			break;
+		default:
+			g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+					"Unknown option %d", opt);
+			return FALSE;
+		}
+
+		opt = va_arg(args, int);
+	}
+
+	return TRUE;
+}
+
+static gboolean get_peers(int sock, struct sockaddr *src, struct sockaddr *dst,
+				socklen_t len, GError **err)
+{
+	socklen_t olen;
+
+	memset(src, 0, len);
+	olen = len;
+	if (getsockname(sock, src, &olen) < 0) {
+		ERROR_FAILED(err, "getsockname", errno);
+		return FALSE;
+	}
+
+	memset(dst, 0, len);
+	olen = len;
+	if (getpeername(sock, dst, &olen) < 0) {
+		ERROR_FAILED(err, "getpeername", errno);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static int l2cap_get_info(int sock, uint16_t *handle, uint8_t *dev_class)
+{
+	struct l2cap_conninfo info;
+	socklen_t len;
+
+	len = sizeof(info);
+	if (getsockopt(sock, SOL_L2CAP, L2CAP_CONNINFO, &info, &len) < 0)
+		return -errno;
+
+	if (handle)
+		*handle = info.hci_handle;
+
+	if (dev_class)
+		memcpy(dev_class, info.dev_class, 3);
+
+	return 0;
+}
+
+static gboolean l2cap_get(int sock, GError **err, BtIOOption opt1,
+								va_list args)
+{
+	BtIOOption opt = opt1;
+	struct sockaddr_l2 src, dst;
+	struct l2cap_options l2o;
+	int flags;
+	uint8_t dev_class[3];
+	uint16_t handle;
+	socklen_t len;
+
+	len = sizeof(l2o);
+	memset(&l2o, 0, len);
+	if (getsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, &l2o, &len) < 0) {
+		ERROR_FAILED(err, "getsockopt(L2CAP_OPTIONS)", errno);
+		return FALSE;
+	}
+
+	if (!get_peers(sock, (struct sockaddr *) &src,
+				(struct sockaddr *) &dst, sizeof(src), err))
+		return FALSE;
+
+	while (opt != BT_IO_OPT_INVALID) {
+		switch (opt) {
+		case BT_IO_OPT_SOURCE:
+			ba2str(&src.l2_bdaddr, va_arg(args, char *));
+			break;
+		case BT_IO_OPT_SOURCE_BDADDR:
+			bacpy(va_arg(args, bdaddr_t *), &src.l2_bdaddr);
+			break;
+		case BT_IO_OPT_DEST:
+			ba2str(&dst.l2_bdaddr, va_arg(args, char *));
+			break;
+		case BT_IO_OPT_DEST_BDADDR:
+			bacpy(va_arg(args, bdaddr_t *), &dst.l2_bdaddr);
+			break;
+		case BT_IO_OPT_DEFER_TIMEOUT:
+			len = sizeof(int);
+			if (getsockopt(sock, SOL_BLUETOOTH, BT_DEFER_SETUP,
+					va_arg(args, int *), &len) < 0) {
+				ERROR_FAILED(err, "getsockopt(DEFER_SETUP)",
+									errno);
+				return FALSE;
+			}
+			break;
+		case BT_IO_OPT_SEC_LEVEL:
+			if (!get_sec_level(sock, BT_IO_L2CAP,
+						va_arg(args, int *), err))
+				return FALSE;
+			break;
+		case BT_IO_OPT_PSM:
+			*(va_arg(args, uint16_t *)) = src.l2_psm ?
+						src.l2_psm : dst.l2_psm;
+			break;
+		case BT_IO_OPT_OMTU:
+			*(va_arg(args, uint16_t *)) = l2o.omtu;
+			break;
+		case BT_IO_OPT_IMTU:
+			*(va_arg(args, uint16_t *)) = l2o.imtu;
+			break;
+		case BT_IO_OPT_MASTER:
+			len = sizeof(flags);
+			if (getsockopt(sock, SOL_L2CAP, L2CAP_LM, &flags,
+								&len) < 0) {
+				ERROR_FAILED(err, "getsockopt(L2CAP_LM)",
+									errno);
+				return FALSE;
+			}
+			*(va_arg(args, gboolean *)) =
+				(flags & L2CAP_LM_MASTER) ? TRUE : FALSE;
+			break;
+		case BT_IO_OPT_HANDLE:
+			if (l2cap_get_info(sock, &handle, dev_class) < 0) {
+				ERROR_FAILED(err, "L2CAP_CONNINFO", errno);
+				return FALSE;
+			}
+			*(va_arg(args, uint16_t *)) = handle;
+			break;
+		case BT_IO_OPT_CLASS:
+			if (l2cap_get_info(sock, &handle, dev_class) < 0) {
+				ERROR_FAILED(err, "L2CAP_CONNINFO", errno);
+				return FALSE;
+			}
+			memcpy(va_arg(args, uint8_t *), dev_class, 3);
+			break;
+		default:
+			g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+					"Unknown option %d", opt);
+			return FALSE;
+		}
+
+		opt = va_arg(args, int);
+	}
+
+	return TRUE;
+}
+
+static int rfcomm_get_info(int sock, uint16_t *handle, uint8_t *dev_class)
+{
+	struct rfcomm_conninfo info;
+	socklen_t len;
+
+	len = sizeof(info);
+	if (getsockopt(sock, SOL_RFCOMM, RFCOMM_CONNINFO, &info, &len) < 0)
+		return -errno;
+
+	if (handle)
+		*handle = info.hci_handle;
+
+	if (dev_class)
+		memcpy(dev_class, info.dev_class, 3);
+
+	return 0;
+}
+
+static gboolean rfcomm_get(int sock, GError **err, BtIOOption opt1,
+								va_list args)
+{
+	BtIOOption opt = opt1;
+	struct sockaddr_rc src, dst;
+	int flags;
+	socklen_t len;
+	uint8_t dev_class[3];
+	uint16_t handle;
+
+	if (!get_peers(sock, (struct sockaddr *) &src,
+				(struct sockaddr *) &dst, sizeof(src), err))
+		return FALSE;
+
+	while (opt != BT_IO_OPT_INVALID) {
+		switch (opt) {
+		case BT_IO_OPT_SOURCE:
+			ba2str(&src.rc_bdaddr, va_arg(args, char *));
+			break;
+		case BT_IO_OPT_SOURCE_BDADDR:
+			bacpy(va_arg(args, bdaddr_t *), &src.rc_bdaddr);
+			break;
+		case BT_IO_OPT_DEST:
+			ba2str(&dst.rc_bdaddr, va_arg(args, char *));
+			break;
+		case BT_IO_OPT_DEST_BDADDR:
+			bacpy(va_arg(args, bdaddr_t *), &dst.rc_bdaddr);
+			break;
+		case BT_IO_OPT_DEFER_TIMEOUT:
+			len = sizeof(int);
+			if (getsockopt(sock, SOL_BLUETOOTH, BT_DEFER_SETUP,
+					va_arg(args, int *), &len) < 0) {
+				ERROR_FAILED(err, "getsockopt(DEFER_SETUP)",
+									errno);
+				return FALSE;
+			}
+			break;
+		case BT_IO_OPT_SEC_LEVEL:
+			if (!get_sec_level(sock, BT_IO_RFCOMM,
+						va_arg(args, int *), err))
+				return FALSE;
+			break;
+		case BT_IO_OPT_CHANNEL:
+			*(va_arg(args, uint8_t *)) = src.rc_channel ?
+					src.rc_channel : dst.rc_channel;
+			break;
+		case BT_IO_OPT_SOURCE_CHANNEL:
+			*(va_arg(args, uint8_t *)) = src.rc_channel;
+			break;
+		case BT_IO_OPT_DEST_CHANNEL:
+			*(va_arg(args, uint8_t *)) = dst.rc_channel;
+			break;
+		case BT_IO_OPT_MASTER:
+			len = sizeof(flags);
+			if (getsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &flags,
+								&len) < 0) {
+				ERROR_FAILED(err, "getsockopt(RFCOMM_LM)",
+									errno);
+				return FALSE;
+			}
+			*(va_arg(args, gboolean *)) =
+				(flags & RFCOMM_LM_MASTER) ? TRUE : FALSE;
+			break;
+		case BT_IO_OPT_HANDLE:
+			if (rfcomm_get_info(sock, &handle, dev_class) < 0) {
+				ERROR_FAILED(err, "RFCOMM_CONNINFO", errno);
+				return FALSE;
+			}
+			*(va_arg(args, uint16_t *)) = handle;
+			break;
+		case BT_IO_OPT_CLASS:
+			if (rfcomm_get_info(sock, &handle, dev_class) < 0) {
+				ERROR_FAILED(err, "RFCOMM_CONNINFO", errno);
+				return FALSE;
+			}
+			memcpy(va_arg(args, uint8_t *), dev_class, 3);
+			break;
+		default:
+			g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+					"Unknown option %d", opt);
+			return FALSE;
+		}
+
+		opt = va_arg(args, int);
+	}
+
+	return TRUE;
+}
+
+static int sco_get_info(int sock, uint16_t *handle, uint8_t *dev_class)
+{
+	struct sco_conninfo info;
+	socklen_t len;
+
+	len = sizeof(info);
+	if (getsockopt(sock, SOL_SCO, SCO_CONNINFO, &info, &len) < 0)
+		return -errno;
+
+	if (handle)
+		*handle = info.hci_handle;
+
+	if (dev_class)
+		memcpy(dev_class, info.dev_class, 3);
+
+	return 0;
+}
+
+static gboolean sco_get(int sock, GError **err, BtIOOption opt1, va_list args)
+{
+	BtIOOption opt = opt1;
+	struct sockaddr_sco src, dst;
+	struct sco_options sco_opt;
+	socklen_t len;
+	uint8_t dev_class[3];
+	uint16_t handle;
+
+	len = sizeof(sco_opt);
+	memset(&sco_opt, 0, len);
+	if (getsockopt(sock, SOL_SCO, SCO_OPTIONS, &sco_opt, &len) < 0) {
+		ERROR_FAILED(err, "getsockopt(SCO_OPTIONS)", errno);
+		return FALSE;
+	}
+
+	if (!get_peers(sock, (struct sockaddr *) &src,
+				(struct sockaddr *) &dst, sizeof(src), err))
+		return FALSE;
+
+	while (opt != BT_IO_OPT_INVALID) {
+		switch (opt) {
+		case BT_IO_OPT_SOURCE:
+			ba2str(&src.sco_bdaddr, va_arg(args, char *));
+			break;
+		case BT_IO_OPT_SOURCE_BDADDR:
+			bacpy(va_arg(args, bdaddr_t *), &src.sco_bdaddr);
+			break;
+		case BT_IO_OPT_DEST:
+			ba2str(&dst.sco_bdaddr, va_arg(args, char *));
+			break;
+		case BT_IO_OPT_DEST_BDADDR:
+			bacpy(va_arg(args, bdaddr_t *), &dst.sco_bdaddr);
+			break;
+		case BT_IO_OPT_MTU:
+		case BT_IO_OPT_IMTU:
+		case BT_IO_OPT_OMTU:
+			*(va_arg(args, uint16_t *)) = sco_opt.mtu;
+			break;
+		case BT_IO_OPT_HANDLE:
+			if (sco_get_info(sock, &handle, dev_class) < 0) {
+				ERROR_FAILED(err, "RFCOMM_CONNINFO", errno);
+				return FALSE;
+			}
+			*(va_arg(args, uint16_t *)) = handle;
+			break;
+		case BT_IO_OPT_CLASS:
+			if (sco_get_info(sock, &handle, dev_class) < 0) {
+				ERROR_FAILED(err, "RFCOMM_CONNINFO", errno);
+				return FALSE;
+			}
+			memcpy(va_arg(args, uint8_t *), dev_class, 3);
+			break;
+		default:
+			g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+					"Unknown option %d", opt);
+			return FALSE;
+		}
+
+		opt = va_arg(args, int);
+	}
+
+	return TRUE;
+}
+
+static gboolean get_valist(GIOChannel *io, BtIOType type, GError **err,
+						BtIOOption opt1, va_list args)
+{
+	int sock;
+
+	sock = g_io_channel_unix_get_fd(io);
+
+	switch (type) {
+	case BT_IO_L2RAW:
+	case BT_IO_L2CAP:
+		return l2cap_get(sock, err, opt1, args);
+	case BT_IO_RFCOMM:
+		return rfcomm_get(sock, err, opt1, args);
+	case BT_IO_SCO:
+		return sco_get(sock, err, opt1, args);
+	}
+
+	g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+			"Unknown BtIO type %d", type);
+	return FALSE;
+}
+
+gboolean bt_io_accept(GIOChannel *io, BtIOConnect connect, gpointer user_data,
+					GDestroyNotify destroy, GError **err)
+{
+	int sock;
+	char c;
+	struct pollfd pfd;
+
+	sock = g_io_channel_unix_get_fd(io);
+
+	memset(&pfd, 0, sizeof(pfd));
+	pfd.fd = sock;
+	pfd.events = POLLOUT;
+
+	if (poll(&pfd, 1, 0) < 0) {
+		ERROR_FAILED(err, "poll", errno);
+		return FALSE;
+	}
+
+	if (!(pfd.revents & POLLOUT)) {
+		int ret;
+		ret = read(sock, &c, 1);
+	}
+
+	accept_add(io, connect, user_data, destroy);
+
+	return TRUE;
+}
+
+gboolean bt_io_set(GIOChannel *io, BtIOType type, GError **err,
+							BtIOOption opt1, ...)
+{
+	va_list args;
+	gboolean ret;
+	struct set_opts opts;
+	int sock;
+
+	va_start(args, opt1);
+	ret = parse_set_opts(&opts, err, opt1, args);
+	va_end(args);
+
+	if (!ret)
+		return ret;
+
+	sock = g_io_channel_unix_get_fd(io);
+
+	switch (type) {
+	case BT_IO_L2RAW:
+	case BT_IO_L2CAP:
+		return l2cap_set(sock, opts.sec_level, opts.imtu, opts.omtu,
+						opts.mode, opts.master, err);
+	case BT_IO_RFCOMM:
+		return rfcomm_set(sock, opts.sec_level, opts.master, err);
+	case BT_IO_SCO:
+		return sco_set(sock, opts.mtu, err);
+	}
+
+	g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+			"Unknown BtIO type %d", type);
+	return FALSE;
+}
+
+gboolean bt_io_get(GIOChannel *io, BtIOType type, GError **err,
+							BtIOOption opt1, ...)
+{
+	va_list args;
+	gboolean ret;
+
+	va_start(args, opt1);
+	ret = get_valist(io, type, err, opt1, args);
+	va_end(args);
+
+	return ret;
+}
+
+static GIOChannel *create_io(BtIOType type, gboolean server,
+					struct set_opts *opts, GError **err)
+{
+	int sock;
+	GIOChannel *io;
+
+	switch (type) {
+	case BT_IO_L2RAW:
+		sock = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_L2CAP);
+		if (sock < 0) {
+			ERROR_FAILED(err, "socket(RAW, L2CAP)", errno);
+			return NULL;
+		}
+		if (l2cap_bind(sock, &opts->src,
+					server ? opts->psm : 0, err) < 0)
+			goto failed;
+		if (!l2cap_set(sock, opts->sec_level, 0, 0, 0, -1, err))
+			goto failed;
+		break;
+	case BT_IO_L2CAP:
+		sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
+		if (sock < 0) {
+			ERROR_FAILED(err, "socket(SEQPACKET, L2CAP)", errno);
+			return NULL;
+		}
+		if (l2cap_bind(sock, &opts->src,
+					server ? opts->psm : 0, err) < 0)
+			goto failed;
+		if (!l2cap_set(sock, opts->sec_level, opts->imtu, opts->omtu,
+						opts->mode, opts->master, err))
+			goto failed;
+		break;
+	case BT_IO_RFCOMM:
+		sock = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
+		if (sock < 0) {
+			ERROR_FAILED(err, "socket(STREAM, RFCOMM)", errno);
+			return NULL;
+		}
+		if (rfcomm_bind(sock, &opts->src,
+					server ? opts->channel : 0, err) < 0)
+			goto failed;
+		if (!rfcomm_set(sock, opts->sec_level, opts->master, err))
+			goto failed;
+		break;
+	case BT_IO_SCO:
+		sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
+		if (sock < 0) {
+			ERROR_FAILED(err, "socket(SEQPACKET, SCO)", errno);
+			return NULL;
+		}
+		if (sco_bind(sock, &opts->src, err) < 0)
+			goto failed;
+		if (!sco_set(sock, opts->mtu, err))
+			goto failed;
+		break;
+	default:
+		g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+				"Unknown BtIO type %d", type);
+		return NULL;
+	}
+
+	io = g_io_channel_unix_new(sock);
+
+	g_io_channel_set_close_on_unref(io, TRUE);
+	g_io_channel_set_flags(io, G_IO_FLAG_NONBLOCK, NULL);
+
+	return io;
+
+failed:
+	close(sock);
+
+	return NULL;
+}
+
+GIOChannel *bt_io_connect(BtIOType type, BtIOConnect connect,
+				gpointer user_data, GDestroyNotify destroy,
+				GError **gerr, BtIOOption opt1, ...)
+{
+	GIOChannel *io;
+	va_list args;
+	struct set_opts opts;
+	int err, sock;
+	gboolean ret;
+
+	va_start(args, opt1);
+	ret = parse_set_opts(&opts, gerr, opt1, args);
+	va_end(args);
+
+	if (ret == FALSE)
+		return NULL;
+
+	io = create_io(type, FALSE, &opts, gerr);
+	if (io == NULL)
+		return NULL;
+
+	sock = g_io_channel_unix_get_fd(io);
+
+	switch (type) {
+	case BT_IO_L2RAW:
+		err = l2cap_connect(sock, &opts.dst, 0);
+		break;
+	case BT_IO_L2CAP:
+		err = l2cap_connect(sock, &opts.dst, opts.psm);
+		break;
+	case BT_IO_RFCOMM:
+		err = rfcomm_connect(sock, &opts.dst, opts.channel);
+		break;
+	case BT_IO_SCO:
+		err = sco_connect(sock, &opts.dst);
+		break;
+	default:
+		g_set_error(gerr, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+						"Unknown BtIO type %d", type);
+		return NULL;
+	}
+
+	if (err < 0) {
+		g_set_error(gerr, BT_IO_ERROR, BT_IO_ERROR_CONNECT_FAILED,
+				"connect: %s (%d)", strerror(-err), -err);
+		g_io_channel_unref(io);
+		return NULL;
+	}
+
+	connect_add(io, connect, user_data, destroy);
+
+	return io;
+}
+
+GIOChannel *bt_io_listen(BtIOType type, BtIOConnect connect,
+				BtIOConfirm confirm, gpointer user_data,
+				GDestroyNotify destroy, GError **err,
+				BtIOOption opt1, ...)
+{
+	GIOChannel *io;
+	va_list args;
+	struct set_opts opts;
+	int sock;
+	gboolean ret;
+
+	if (type == BT_IO_L2RAW) {
+		g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+				"Server L2CAP RAW sockets not supported");
+		return NULL;
+	}
+
+	va_start(args, opt1);
+	ret = parse_set_opts(&opts, err, opt1, args);
+	va_end(args);
+
+	if (ret == FALSE)
+		return NULL;
+
+	io = create_io(type, TRUE, &opts, err);
+	if (io == NULL)
+		return NULL;
+
+	sock = g_io_channel_unix_get_fd(io);
+
+	if (confirm)
+		setsockopt(sock, SOL_BLUETOOTH, BT_DEFER_SETUP, &opts.defer,
+							sizeof(opts.defer));
+
+	if (listen(sock, 5) < 0) {
+		ERROR_FAILED(err, "listen", errno);
+		g_io_channel_unref(io);
+		return NULL;
+	}
+
+	server_add(io, connect, confirm, user_data, destroy);
+
+	return io;
+}
+
+GQuark bt_io_error_quark(void)
+{
+	return g_quark_from_static_string("bt-io-error-quark");
+}
--- btio/btio.h
+++ btio/btio.h
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2009-2010  Marcel Holtmann <marcel at holtmann.org>
+ *  Copyright (C) 2009-2010  Nokia Corporation
+ *
+ *
+ *  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
+ *
+ */
+#ifndef BT_IO_H
+#define BT_IO_H
+
+#include <glib.h>
+
+typedef enum {
+	BT_IO_ERROR_DISCONNECTED,
+	BT_IO_ERROR_CONNECT_FAILED,
+	BT_IO_ERROR_FAILED,
+	BT_IO_ERROR_INVALID_ARGS,
+} BtIOError;
+
+#define BT_IO_ERROR bt_io_error_quark()
+
+GQuark bt_io_error_quark(void);
+
+typedef enum {
+	BT_IO_L2RAW,
+	BT_IO_L2CAP,
+	BT_IO_RFCOMM,
+	BT_IO_SCO,
+} BtIOType;
+
+typedef enum {
+	BT_IO_OPT_INVALID = 0,
+	BT_IO_OPT_SOURCE,
+	BT_IO_OPT_SOURCE_BDADDR,
+	BT_IO_OPT_DEST,
+	BT_IO_OPT_DEST_BDADDR,
+	BT_IO_OPT_DEFER_TIMEOUT,
+	BT_IO_OPT_SEC_LEVEL,
+	BT_IO_OPT_CHANNEL,
+	BT_IO_OPT_SOURCE_CHANNEL,
+	BT_IO_OPT_DEST_CHANNEL,
+	BT_IO_OPT_PSM,
+	BT_IO_OPT_MTU,
+	BT_IO_OPT_OMTU,
+	BT_IO_OPT_IMTU,
+	BT_IO_OPT_MASTER,
+	BT_IO_OPT_HANDLE,
+	BT_IO_OPT_CLASS,
+	BT_IO_OPT_MODE,
+} BtIOOption;
+
+typedef enum {
+	BT_IO_SEC_SDP = 0,
+	BT_IO_SEC_LOW,
+	BT_IO_SEC_MEDIUM,
+	BT_IO_SEC_HIGH,
+} BtIOSecLevel;
+
+typedef void (*BtIOConfirm)(GIOChannel *io, gpointer user_data);
+
+typedef void (*BtIOConnect)(GIOChannel *io, GError *err, gpointer user_data);
+
+gboolean bt_io_accept(GIOChannel *io, BtIOConnect connect, gpointer user_data,
+					GDestroyNotify destroy, GError **err);
+
+gboolean bt_io_set(GIOChannel *io, BtIOType type, GError **err,
+						BtIOOption opt1, ...);
+
+gboolean bt_io_get(GIOChannel *io, BtIOType type, GError **err,
+						BtIOOption opt1, ...);
+
+GIOChannel *bt_io_connect(BtIOType type, BtIOConnect connect,
+				gpointer user_data, GDestroyNotify destroy,
+				GError **err, BtIOOption opt1, ...);
+
+GIOChannel *bt_io_listen(BtIOType type, BtIOConnect connect,
+				BtIOConfirm confirm, gpointer user_data,
+				GDestroyNotify destroy, GError **err,
+				BtIOOption opt1, ...);
+
+#endif
--- client/session.c
+++ client/session.c
@@ -1274,10 +1274,8 @@
 
 	transfer = transfer_register(session, filename, targetname, NULL,
 					NULL);
-	if (transfer == NULL) {
-		err = -EINVAL;
-		goto fail;
-	}
+	if (transfer == NULL)
+		return -EINVAL;
 
 	/* Transfer should start if it is the first in the pending list */
 	if (transfer != session->pending->data)
--- client/transfer.c
+++ client/transfer.c
@@ -379,8 +379,10 @@
 
 		transfer->filled += len;
 
-		if (transfer->filled == 0)
+		if (transfer->filled == 0) {
+			gw_obex_xfer_close(xfer, &err);
 			goto done;
+		}
 
 		if (gw_obex_xfer_write(xfer, transfer->buffer,
 					transfer->filled,
--- configure
+++ configure
@@ -1,6 +1,6 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.63 for obexd 0.32.
+# Generated by GNU Autoconf 2.63 for obexd 0.35.
 #
 # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
 # 2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
@@ -743,8 +743,8 @@
 # Identity of this package.
 PACKAGE_NAME='obexd'
 PACKAGE_TARNAME='obexd'
-PACKAGE_VERSION='0.32'
-PACKAGE_STRING='obexd 0.32'
+PACKAGE_VERSION='0.35'
+PACKAGE_STRING='obexd 0.35'
 PACKAGE_BUGREPORT=''
 
 ac_default_prefix=/usr/local
@@ -1517,7 +1517,7 @@
   # Omit some internal or obsolete options to make the list less imposing.
   # This message is too long to be a string in the A/UX 3.1 sh.
   cat <<_ACEOF
-\`configure' configures obexd 0.32 to adapt to many kinds of systems.
+\`configure' configures obexd 0.35 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1587,7 +1587,7 @@
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of obexd 0.32:";;
+     short | recursive ) echo "Configuration of obexd 0.35:";;
    esac
   cat <<\_ACEOF
 
@@ -1719,7 +1719,7 @@
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-obexd configure 0.32
+obexd configure 0.35
 generated by GNU Autoconf 2.63
 
 Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
@@ -1733,7 +1733,7 @@
 This file contains any messages produced by compilers while
 running configure, to aid debugging if configure makes a mistake.
 
-It was created by obexd $as_me 0.32, which was
+It was created by obexd $as_me 0.35, which was
 generated by GNU Autoconf 2.63.  Invocation command line was
 
   $ $0 $@
@@ -2583,7 +2583,7 @@
 
 # Define the identity of the package.
  PACKAGE='obexd'
- VERSION='0.32'
+ VERSION='0.35'
 
 
 cat >>confdefs.h <<_ACEOF
@@ -13115,7 +13115,7 @@
 # report actual input values of CONFIG_FILES etc. instead of their
 # values after options handling.
 ac_log="
-This file was extended by obexd $as_me 0.32, which was
+This file was extended by obexd $as_me 0.35, which was
 generated by GNU Autoconf 2.63.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -13178,7 +13178,7 @@
 _ACEOF
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
 ac_cs_version="\\
-obexd config.status 0.32
+obexd config.status 0.35
 configured by $0, generated by GNU Autoconf 2.63,
   with options \\"`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\"
 
--- configure.ac
+++ configure.ac
@@ -1,5 +1,5 @@
 AC_PREREQ(2.60)
-AC_INIT(obexd, 0.32)
+AC_INIT(obexd, 0.35)
 
 AM_INIT_AUTOMAKE([foreign subdir-objects])
 AM_CONFIG_HEADER(config.h)
--- gdbus/gdbus.h
+++ gdbus/gdbus.h
@@ -55,6 +55,13 @@
 typedef DBusMessage * (* GDBusMethodFunction) (DBusConnection *connection,
 					DBusMessage *message, void *user_data);
 
+typedef guint32 GDBusPendingReply;
+
+typedef void (* GDBusSecurityFunction) (DBusConnection *connection,
+						const char *action,
+						gboolean interaction,
+						GDBusPendingReply pending);
+
 typedef enum {
 	G_DBUS_METHOD_FLAG_DEPRECATED = (1 << 0),
 	G_DBUS_METHOD_FLAG_NOREPLY    = (1 << 1),
@@ -69,12 +76,19 @@
 	G_DBUS_PROPERTY_FLAG_DEPRECATED = (1 << 0),
 } GDBusPropertyFlags;
 
+typedef enum {
+	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 {
 	const char *name;
 	const char *signature;
 	const char *reply;
 	GDBusMethodFunction function;
 	GDBusMethodFlags flags;
+	unsigned int privilege;
 } GDBusMethodTable;
 
 typedef struct {
@@ -89,6 +103,13 @@
 	GDBusPropertyFlags flags;
 } GDBusPropertyTable;
 
+typedef struct {
+	unsigned int privilege;
+	const char *action;
+	GDBusSecurityFlags flags;
+	GDBusSecurityFunction function;
+} GDBusSecurityTable;
+
 gboolean g_dbus_register_interface(DBusConnection *connection,
 					const char *path, const char *name,
 					const GDBusMethodTable *methods,
@@ -99,6 +120,19 @@
 gboolean g_dbus_unregister_interface(DBusConnection *connection,
 					const char *path, const char *name);
 
+gboolean g_dbus_register_security(const GDBusSecurityTable *security);
+gboolean g_dbus_unregister_security(const GDBusSecurityTable *security);
+
+void g_dbus_pending_success(DBusConnection *connection,
+					GDBusPendingReply pending);
+void g_dbus_pending_error(DBusConnection *connection,
+				GDBusPendingReply pending,
+				const char *name, const char *format, ...)
+					__attribute__((format(printf, 4, 5)));
+void g_dbus_pending_error_valist(DBusConnection *connection,
+				GDBusPendingReply pending, const char *name,
+					const char *format, va_list args);
+
 DBusMessage *g_dbus_create_error(DBusMessage *message, const char *name,
 						const char *format, ...)
 					__attribute__((format(printf, 3, 4)));
--- gdbus/mainloop.c
+++ gdbus/mainloop.c
@@ -95,6 +95,7 @@
 {
 	struct watch_info *info = data;
 	unsigned int flags = 0;
+	DBusDispatchStatus status;
 
 	dbus_connection_ref(info->conn);
 
@@ -105,6 +106,9 @@
 
 	dbus_watch_handle(info->watch, flags);
 
+	status = dbus_connection_get_dispatch_status(info->conn);
+	queue_dispatch(info->conn, status);
+
 	dbus_connection_unref(info->conn);
 
 	return TRUE;
--- gdbus/object.c
+++ gdbus/object.c
@@ -52,6 +52,13 @@
 	GDBusDestroyFunction destroy;
 };
 
+struct security_data {
+	GDBusPendingReply pending;
+	DBusMessage *message;
+	const GDBusMethodTable *method;
+	void *iface_user_data;
+};
+
 static void print_arguments(GString *gstr, const char *sig,
 						const char *direction)
 {
@@ -208,6 +215,183 @@
 	return reply;
 }
 
+static DBusHandlerResult process_message(DBusConnection *connection,
+			DBusMessage *message, const GDBusMethodTable *method,
+							void *iface_user_data)
+{
+	DBusMessage *reply;
+
+	reply = method->function(connection, message, iface_user_data);
+
+	if (method->flags & G_DBUS_METHOD_FLAG_NOREPLY) {
+		if (reply != NULL)
+			dbus_message_unref(reply);
+		return DBUS_HANDLER_RESULT_HANDLED;
+	}
+
+	if (method->flags & G_DBUS_METHOD_FLAG_ASYNC) {
+		if (reply == NULL)
+			return DBUS_HANDLER_RESULT_HANDLED;
+	}
+
+	if (reply == NULL)
+		return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+	dbus_connection_send(connection, reply, NULL);
+	dbus_message_unref(reply);
+
+	return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static GDBusPendingReply next_pending = 1;
+static GSList *pending_security = NULL;
+
+static const GDBusSecurityTable *security_table = NULL;
+
+void g_dbus_pending_success(DBusConnection *connection,
+					GDBusPendingReply pending)
+{
+	GSList *list;
+
+        for (list = pending_security; list; list = list->next) {
+		struct security_data *secdata = list->data;
+		DBusHandlerResult result;
+
+		if (secdata->pending != pending)
+			continue;
+
+		pending_security = g_slist_remove(pending_security, secdata);
+
+		result = process_message(connection, secdata->message,
+				secdata->method, secdata->iface_user_data);
+
+		dbus_message_unref(secdata->message);
+		g_free(secdata);
+		return;
+        }
+}
+
+void g_dbus_pending_error_valist(DBusConnection *connection,
+				GDBusPendingReply pending, const char *name,
+					const char *format, va_list args)
+{
+	GSList *list;
+
+        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,
+							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,
+				GDBusPendingReply pending,
+				const char *name, const char *format, ...)
+{
+	va_list args;
+
+	va_start(args, format);
+
+	g_dbus_pending_error_valist(connection, pending, name, format, args);
+
+	va_end(args);
+}
+
+int polkit_check_authorization(DBusConnection *conn,
+				const char *action, gboolean interaction,
+				void (*function) (dbus_bool_t authorized,
+							void *user_data),
+						void *user_data, int timeout);
+
+struct builtin_security_data {
+	DBusConnection *conn;
+	GDBusPendingReply pending;
+};
+
+static void builtin_security_result(dbus_bool_t authorized, void *user_data)
+{
+	struct builtin_security_data *data = user_data;
+
+	if (authorized == TRUE)
+		g_dbus_pending_success(data->conn, data->pending);
+	else
+		g_dbus_pending_error(data->conn, data->pending,
+						DBUS_ERROR_AUTH_FAILED, NULL);
+
+	g_free(data);
+}
+
+static void builtin_security_function(DBusConnection *conn,
+						const char *action,
+						gboolean interaction,
+						GDBusPendingReply pending)
+{
+	struct builtin_security_data *data;
+
+	data = g_new0(struct builtin_security_data, 1);
+	data->conn = conn;
+	data->pending = pending;
+
+	if (polkit_check_authorization(conn, action, interaction,
+				builtin_security_result, data, 30000) < 0)
+		g_dbus_pending_error(conn, pending, NULL, NULL);
+}
+
+static gboolean check_privilege(DBusConnection *conn, DBusMessage *msg,
+			const GDBusMethodTable *method, void *iface_user_data)
+{
+	const GDBusSecurityTable *security;
+
+	for (security = security_table; security && security->privilege;
+								security++) {
+		struct security_data *secdata;
+		gboolean interaction;
+
+		if (security->privilege != method->privilege)
+			continue;
+
+		secdata = g_new(struct security_data, 1);
+		secdata->pending = next_pending++;
+		secdata->message = dbus_message_ref(msg);
+		secdata->method = method;
+		secdata->iface_user_data = iface_user_data;
+
+		pending_security = g_slist_prepend(pending_security, secdata);
+
+		if (security->flags & G_DBUS_SECURITY_FLAG_ALLOW_INTERACTION)
+			interaction = TRUE;
+		else
+			interaction = FALSE;
+
+		if (!(security->flags & G_DBUS_SECURITY_FLAG_BUILTIN) &&
+							security->function)
+			security->function(conn, security->action,
+						interaction, secdata->pending);
+		else
+			builtin_security_function(conn, security->action,
+						interaction, secdata->pending);
+
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
 static void generic_unregister(DBusConnection *connection, void *user_data)
 {
 	struct generic_data *data = user_data;
@@ -249,8 +433,6 @@
 
 	for (method = iface->methods; method &&
 			method->name && method->function; method++) {
-		DBusMessage *reply;
-
 		if (dbus_message_is_method_call(message, iface->name,
 							method->name) == FALSE)
 			continue;
@@ -259,26 +441,12 @@
 						method->signature) == FALSE)
 			continue;
 
-		reply = method->function(connection, message, iface->user_data);
-
-		if (method->flags & G_DBUS_METHOD_FLAG_NOREPLY) {
-			if (reply != NULL)
-				dbus_message_unref(reply);
+		if (check_privilege(connection, message, method,
+						iface->user_data) == TRUE)
 			return DBUS_HANDLER_RESULT_HANDLED;
-		}
-
-		if (method->flags & G_DBUS_METHOD_FLAG_ASYNC) {
-			if (reply == NULL)
-				return DBUS_HANDLER_RESULT_HANDLED;
-		}
 
-		if (reply == NULL)
-			return DBUS_HANDLER_RESULT_NEED_MEMORY;
-
-		dbus_connection_send(connection, reply, NULL);
-		dbus_message_unref(reply);
-
-		return DBUS_HANDLER_RESULT_HANDLED;
+		return process_message(connection, message, method,
+							iface->user_data);
 	}
 
 	return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
@@ -362,11 +530,10 @@
 	}
 
 	data = g_new0(struct generic_data, 1);
+	data->refcount = 1;
 
 	data->introspect = g_strdup(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE "<node></node>");
 
-	data->refcount = 1;
-
 	if (!dbus_connection_register_object_path(connection, path,
 						&generic_table, data)) {
 		g_free(data->introspect);
@@ -555,6 +722,23 @@
 
 	return TRUE;
 }
+
+gboolean g_dbus_register_security(const GDBusSecurityTable *security)
+{
+	if (security_table != NULL)
+		return FALSE;
+
+	security_table = security;
+
+	return TRUE;
+}
+
+gboolean g_dbus_unregister_security(const GDBusSecurityTable *security)
+{
+	security_table = NULL;
+
+	return TRUE;
+}
 
 DBusMessage *g_dbus_create_error_valist(DBusMessage *message, const char *name,
 					const char *format, va_list args)
--- gdbus/polkit.c
+++ gdbus/polkit.c
+/*
+ *
+ *  D-Bus helper library
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel at 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 <errno.h>
+
+#include <dbus/dbus.h>
+
+#include <glib.h>
+
+int polkit_check_authorization(DBusConnection *conn,
+				const char *action, gboolean interaction,
+				void (*function) (dbus_bool_t authorized,
+							void *user_data),
+						void *user_data, int timeout);
+
+static void add_dict_with_string_value(DBusMessageIter *iter,
+					const char *key, const char *str)
+{
+	DBusMessageIter dict, entry, value;
+
+	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);
+	dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY,
+								NULL, &entry);
+
+	dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
+
+	dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
+					DBUS_TYPE_STRING_AS_STRING, &value);
+	dbus_message_iter_append_basic(&value, DBUS_TYPE_STRING, &str);
+	dbus_message_iter_close_container(&entry, &value);
+
+	dbus_message_iter_close_container(&dict, &entry);
+	dbus_message_iter_close_container(iter, &dict);
+}
+
+static void add_empty_string_dict(DBusMessageIter *iter)
+{
+	DBusMessageIter dict;
+
+	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+			DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+			DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_STRING_AS_STRING
+			DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+	dbus_message_iter_close_container(iter, &dict);
+}
+
+static void add_arguments(DBusConnection *conn, DBusMessageIter *iter,
+				const char *action, dbus_uint32_t flags)
+{
+	const char *busname = dbus_bus_get_unique_name(conn);
+	const char *kind = "system-bus-name";
+	const char *cancel = "";
+	DBusMessageIter subject;
+
+	dbus_message_iter_open_container(iter, DBUS_TYPE_STRUCT,
+							NULL, &subject);
+	dbus_message_iter_append_basic(&subject, DBUS_TYPE_STRING, &kind);
+	add_dict_with_string_value(&subject, "name", busname);
+	dbus_message_iter_close_container(iter, &subject);
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &action);
+	add_empty_string_dict(iter);
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32, &flags);
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &cancel);
+}
+
+static dbus_bool_t parse_result(DBusMessageIter *iter)
+{
+	DBusMessageIter result;
+	dbus_bool_t authorized, challenge;
+
+	dbus_message_iter_recurse(iter, &result);
+
+	dbus_message_iter_get_basic(&result, &authorized);
+	dbus_message_iter_get_basic(&result, &challenge);
+
+	return authorized;
+}
+
+struct authorization_data {
+	void (*function) (dbus_bool_t authorized, void *user_data);
+	void *user_data;
+};
+
+static void authorization_reply(DBusPendingCall *call, void *user_data)
+{
+	struct authorization_data *data = user_data;
+	DBusMessage *reply;
+	DBusMessageIter iter;
+	dbus_bool_t authorized = FALSE;
+
+	reply = dbus_pending_call_steal_reply(call);
+
+	if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR)
+		goto done;
+
+	if (dbus_message_has_signature(reply, "(bba{ss})") == FALSE)
+		goto done;
+
+	dbus_message_iter_init(reply, &iter);
+
+	authorized = parse_result(&iter);
+
+done:
+	if (data->function != NULL)
+		data->function(authorized, data->user_data);
+
+	dbus_message_unref(reply);
+
+	dbus_pending_call_unref(call);
+}
+
+#define AUTHORITY_DBUS	"org.freedesktop.PolicyKit1"
+#define AUTHORITY_INTF	"org.freedesktop.PolicyKit1.Authority"
+#define AUTHORITY_PATH	"/org/freedesktop/PolicyKit1/Authority"
+
+int polkit_check_authorization(DBusConnection *conn,
+				const char *action, gboolean interaction,
+				void (*function) (dbus_bool_t authorized,
+							void *user_data),
+						void *user_data, int timeout)
+{
+	struct authorization_data *data;
+	DBusMessage *msg;
+	DBusMessageIter iter;
+	DBusPendingCall *call;
+	dbus_uint32_t flags = 0x00000000;
+
+	if (conn == NULL)
+		return -EINVAL;
+
+	data = dbus_malloc0(sizeof(*data));
+	if (data == NULL)
+		return -ENOMEM;
+
+	msg = dbus_message_new_method_call(AUTHORITY_DBUS, AUTHORITY_PATH,
+				AUTHORITY_INTF, "CheckAuthorization");
+	if (!msg) {
+		dbus_free(data);
+		return -ENOMEM;
+	}
+
+	if (interaction == TRUE)
+		flags |= 0x00000001;
+
+	if (action == NULL)
+		action = "org.freedesktop.policykit.exec";
+
+	dbus_message_iter_init_append(msg, &iter);
+	add_arguments(conn, &iter, action, flags);
+
+	if (dbus_connection_send_with_reply(conn, msg,
+						&call, timeout) == FALSE) {
+		dbus_message_unref(msg);
+		dbus_free(data);
+		return -EIO;
+	}
+
+	if (call == NULL) {
+		dbus_message_unref(msg);
+		dbus_free(data);
+		return -EIO;
+	}
+
+	data->function = function;
+	data->user_data = user_data;
+
+	dbus_pending_call_set_notify(call, authorization_reply,
+							data, dbus_free);
+
+	dbus_message_unref(msg);
+
+	return 0;
+}
--- gdbus/watch.c
+++ gdbus/watch.c
@@ -43,11 +43,21 @@
 static guint listener_id = 0;
 static GSList *listeners = NULL;
 
+struct service_data {
+	DBusConnection *conn;
+	DBusPendingCall *call;
+	char *name;
+	const char *owner;
+	guint id;
+	struct filter_callback *callback;
+};
+
 struct filter_callback {
 	GDBusWatchFunction conn_func;
 	GDBusWatchFunction disc_func;
 	GDBusSignalFunction signal_func;
 	GDBusDestroyFunction destroy_func;
+	struct service_data *data;
 	void *user_data;
 	guint id;
 };
@@ -55,19 +65,22 @@
 struct filter_data {
 	DBusConnection *connection;
 	DBusHandleMessageFunction handle_func;
-	char *sender;
+	char *name;
+	char *owner;
 	char *path;
 	char *interface;
 	char *member;
 	char *argument;
 	GSList *callbacks;
 	GSList *processed;
+	guint name_watch;
 	gboolean lock;
 	gboolean registered;
 };
 
 static struct filter_data *filter_data_find(DBusConnection *connection,
-							const char *sender,
+							const char *name,
+							const char *owner,
 							const char *path,
 							const char *interface,
 							const char *member,
@@ -82,8 +95,12 @@
 		if (connection != data->connection)
 			continue;
 
-		if (sender && data->sender &&
-				g_str_equal(sender, data->sender) == FALSE)
+		if (name && data->name &&
+				g_str_equal(name, data->name) == FALSE)
+			continue;
+
+		if (owner && data->owner &&
+				g_str_equal(owner, data->owner) == FALSE)
 			continue;
 
 		if (path && data->path &&
@@ -110,13 +127,15 @@
 
 static void format_rule(struct filter_data *data, char *rule, size_t size)
 {
+	const char *sender;
 	int offset;
 
 	offset = snprintf(rule, size, "type='signal'");
+	sender = data->name ? : data->owner;
 
-	if (data->sender)
+	if (sender)
 		offset += snprintf(rule + offset, size - offset,
-				",sender='%s'", data->sender);
+				",sender='%s'", sender);
 	if (data->path)
 		offset += snprintf(rule + offset, size - offset,
 				",path='%s'", data->path);
@@ -183,8 +202,10 @@
 					const char *argument)
 {
 	struct filter_data *data;
+	const char *name = NULL, *owner = NULL;
 
-	if (!filter_data_find(connection, NULL, NULL, NULL, NULL, NULL)) {
+	if (!filter_data_find(connection, NULL, NULL, NULL, NULL, NULL,
+				NULL)) {
 		if (!dbus_connection_add_filter(connection,
 					message_filter, NULL, NULL)) {
 			error("dbus_connection_add_filter() failed");
@@ -192,15 +213,25 @@
 		}
 	}
 
-	data = filter_data_find(connection, sender, path, interface, member,
-					argument);
+	if (sender == NULL)
+		goto proceed;
+
+	if (sender[0] == ':')
+		owner = sender;
+	else
+		name = sender;
+
+proceed:
+	data = filter_data_find(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->sender = g_strdup(sender);
+	data->name = name ? g_strdup(name) : NULL;
+	data->owner = owner ? g_strdup(owner) : NULL;
 	data->path = g_strdup(path);
 	data->interface = g_strdup(interface);
 	data->member = g_strdup(member);
@@ -244,7 +275,9 @@
 		g_free(l->data);
 
 	g_slist_free(data->callbacks);
-	g_free(data->sender);
+	g_dbus_remove_watch(data->connection, data->name_watch);
+	g_free(data->name);
+	g_free(data->owner);
 	g_free(data->path);
 	g_free(data->interface);
 	g_free(data->member);
@@ -279,7 +312,7 @@
 {
 	struct filter_callback *cb = NULL;
 
-	cb = g_new(struct filter_callback, 1);
+	cb = g_new0(struct filter_callback, 1);
 
 	cb->conn_func = connect;
 	cb->disc_func = disconnect;
@@ -296,6 +329,24 @@
 	return cb;
 }
 
+static void service_data_free(struct service_data *data)
+{
+	struct filter_callback *callback = data->callback;
+
+	dbus_connection_unref(data->conn);
+
+	if (data->call)
+		dbus_pending_call_unref(data->call);
+
+	if (data->id)
+		g_source_remove(data->id);
+
+	g_free(data->name);
+	g_free(data);
+
+	callback->data = NULL;
+}
+
 static gboolean filter_data_remove_callback(struct filter_data *data,
 						struct filter_callback *cb)
 {
@@ -304,6 +355,13 @@
 	data->callbacks = g_slist_remove(data->callbacks, cb);
 	data->processed = g_slist_remove(data->processed, cb);
 
+	/* Cancel pending operations */
+	if (cb->data) {
+		if (cb->data->call)
+			dbus_pending_call_cancel(cb->data->call);
+		service_data_free(cb->data);
+	}
+
 	if (cb->destroy_func)
 		cb->destroy_func(cb->user_data);
 
@@ -322,7 +380,8 @@
 	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);
+	data = filter_data_find(connection, NULL, NULL, NULL, NULL, NULL,
+					NULL);
 	if (!data)
 		dbus_connection_remove_filter(connection, message_filter,
 						NULL);
@@ -359,6 +418,37 @@
 	return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 }
 
+static void update_name_cache(const char *name, const char *owner)
+{
+	GSList *l;
+
+	for (l = listeners; l != NULL; l = l->next) {
+		struct filter_data *data = l->data;
+
+		if (g_strcmp0(data->name, name) != 0)
+			continue;
+
+		g_free(data->owner);
+		data->owner = g_strdup(owner);
+	}
+}
+
+static const char *check_name_cache(const char *name)
+{
+	GSList *l;
+
+	for (l = listeners; l != NULL; l = l->next) {
+		struct filter_data *data = l->data;
+
+		if (g_strcmp0(data->name, name) != 0)
+			continue;
+
+		return data->owner;
+	}
+
+	return NULL;
+}
+
 static DBusHandlerResult service_filter(DBusConnection *connection,
 					DBusMessage *message, void *user_data)
 {
@@ -375,6 +465,8 @@
 		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 	}
 
+	update_name_cache(name, new);
+
 	while (data->callbacks) {
 		cb = data->callbacks->data;
 
@@ -386,18 +478,19 @@
 				cb->conn_func(connection, cb->user_data);
 		}
 
+		/* Only auto remove if it is a bus name watch */
+		if (data->argument[0] == ':' &&
+				(!cb->conn_func || !cb->disc_func)) {
+			filter_data_remove_callback(data, cb);
+			continue;
+		}
+
 		/* Check if the watch was removed/freed by the callback
 		 * function */
 		if (!g_slist_find(data->callbacks, cb))
 			continue;
 
 		data->callbacks = g_slist_remove(data->callbacks, cb);
-
-		if (!cb->conn_func || !cb->disc_func) {
-			g_free(cb);
-			continue;
-		}
-
 		data->processed = g_slist_append(data->processed, cb);
 	}
 
@@ -421,7 +514,9 @@
 	member = dbus_message_get_member(message);
 	dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &arg, DBUS_TYPE_INVALID);
 
-	data = filter_data_find(connection, sender, path, iface, member, arg);
+	/* Sender is always bus name */
+	data = filter_data_find(connection, NULL, sender, path, iface, member,
+					arg);
 	if (!data) {
 		error("Got %s.%s signal which has no listeners", iface, member);
 		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
@@ -446,7 +541,8 @@
 	filter_data_free(data);
 
 	/* Remove filter if there no listener left for the connection */
-	data = filter_data_find(connection, NULL, NULL, NULL, NULL, NULL);
+	data = filter_data_find(connection, NULL, NULL, NULL, NULL, NULL,
+					NULL);
 	if (!data)
 		dbus_connection_remove_filter(connection, message_filter,
 						NULL);
@@ -454,49 +550,57 @@
 	return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 }
 
-struct service_data {
-	DBusConnection *conn;
-	GDBusWatchFunction conn_func;
-	void *user_data;
-};
+static gboolean update_service(void *user_data)
+{
+	struct service_data *data = user_data;
+	struct filter_callback *cb = data->callback;
+
+	update_name_cache(data->name, data->owner);
+	if (cb->conn_func)
+		cb->conn_func(data->conn, cb->user_data);
+
+	service_data_free(data);
+
+	return FALSE;
+}
 
 static void service_reply(DBusPendingCall *call, void *user_data)
 {
 	struct service_data *data = user_data;
 	DBusMessage *reply;
-	DBusError error;
-	dbus_bool_t has_owner;
+	DBusError err;
 
 	reply = dbus_pending_call_steal_reply(call);
 	if (reply == NULL)
 		return;
 
-	dbus_error_init(&error);
-
-	if (dbus_message_get_args(reply, &error,
-					DBUS_TYPE_BOOLEAN, &has_owner,
-						DBUS_TYPE_INVALID) == FALSE) {
-		if (dbus_error_is_set(&error) == TRUE) {
-			error("%s", error.message);
-			dbus_error_free(&error);
-		} else {
-			error("Wrong arguments for NameHasOwner reply");
-		}
-		goto done;
-	}
+	dbus_error_init(&err);
 
-	if (has_owner && data->conn_func)
-		data->conn_func(data->conn, data->user_data);
+	if (dbus_set_error_from_message(&err, reply))
+		goto fail;
 
+	if (dbus_message_get_args(reply, &err,
+					DBUS_TYPE_STRING, &data->owner,
+						DBUS_TYPE_INVALID) == FALSE)
+		goto fail;
+
+	update_service(data);
+
+	goto done;
+
+fail:
+	error("%s", err.message);
+	dbus_error_free(&err);
+	service_data_free(data);
 done:
 	dbus_message_unref(reply);
 }
 
-static void check_service(DBusConnection *connection, const char *name,
-				GDBusWatchFunction connect, void *user_data)
+static void check_service(DBusConnection *connection,
+					const char *name,
+					struct filter_callback *callback)
 {
 	DBusMessage *message;
-	DBusPendingCall *call;
 	struct service_data *data;
 
 	data = g_try_malloc0(sizeof(*data));
@@ -505,12 +609,19 @@
 		return;
 	}
 
-	data->conn = connection;
-	data->conn_func = connect;
-	data->user_data = user_data;
+	data->conn = dbus_connection_ref(connection);
+	data->name = g_strdup(name);
+	data->callback = callback;
+	callback->data = data;
+
+	data->owner = check_name_cache(name);
+	if (data->owner != NULL) {
+		data->id = g_idle_add(update_service, data);
+		return;
+	}
 
 	message = dbus_message_new_method_call(DBUS_SERVICE_DBUS,
-			DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, "NameHasOwner");
+			DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, "GetNameOwner");
 	if (message == NULL) {
 		error("Can't allocate new message");
 		g_free(data);
@@ -521,21 +632,19 @@
 							DBUS_TYPE_INVALID);
 
 	if (dbus_connection_send_with_reply(connection, message,
-							&call, -1) == FALSE) {
+							&data->call, -1) == FALSE) {
 		error("Failed to execute method call");
 		g_free(data);
 		goto done;
 	}
 
-	if (call == NULL) {
+	if (data->call == NULL) {
 		error("D-Bus connection not available");
 		g_free(data);
 		goto done;
 	}
 
-	dbus_pending_call_set_notify(call, service_reply, data, g_free);
-
-	dbus_pending_call_unref(call);
+	dbus_pending_call_set_notify(data->call, service_reply, data, NULL);
 
 done:
 	dbus_message_unref(message);
@@ -564,7 +673,7 @@
 		return 0;
 
 	if (connect)
-		check_service(connection, name, connect, user_data);
+		check_service(connection, name, cb);
 
 	return cb->id;
 }
@@ -596,6 +705,11 @@
 	if (!cb)
 		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;
 }
 
@@ -625,7 +739,8 @@
 {
 	struct filter_data *data;
 
-	while ((data = filter_data_find(connection, NULL, NULL, NULL, NULL, NULL))) {
+	while ((data = filter_data_find(connection, NULL, NULL, NULL, NULL,
+					NULL, NULL))) {
 		listeners = g_slist_remove(listeners, data);
 		filter_data_call_and_free(data);
 	}
--- plugins/pbap.c
+++ plugins/pbap.c
@@ -228,6 +228,9 @@
 	struct aparam_header *hdr = (struct aparam_header *) aparam;
 	uint16_t phonebooksize;
 
+	if (vcards < 0)
+		vcards = 0;
+
 	DBG("vcards %d", vcards);
 
 	phonebooksize = htons(vcards);
@@ -248,6 +251,11 @@
 
 	DBG("");
 
+	if (vcards < 0) {
+		obex_object_set_io_flags(pbap, G_IO_ERR, -ENOENT);
+		return;
+	}
+
 	if (!pbap->buffer)
 		pbap->buffer = g_string_new_len(buffer, bufsize);
 	else
@@ -389,6 +397,7 @@
 		pbap->buffer = g_string_new_len(aparam, sizeof(aparam));
 		goto done;
 	}
+
 	/*
 	 * Don't free the sorted list content: this list contains
 	 * only the reference for the "real" cache entry.
@@ -397,6 +406,12 @@
 			pbap->params->searchattrib,
 			(const char *) pbap->params->searchval);
 
+	if (sorted == NULL) {
+		pbap->cache.valid = TRUE;
+		obex_object_set_io_flags(pbap, G_IO_ERR, -ENOENT);
+		return;
+	}
+
 	/* Computing offset considering first entry of the phonebook */
 	l = g_slist_nth(sorted, pbap->params->liststartoffset);
 
--- plugins/phonebook-tracker.c
+++ plugins/phonebook-tracker.c
@@ -43,16 +43,18 @@
 #define TRACKER_RESOURCES_INTERFACE "org.freedesktop.Tracker1.Resources"
 
 #define TRACKER_DEFAULT_CONTACT_ME "http://www.semanticdesktop.org/ontologies/2007/03/22/nco#default-contact-me"
-#define CONTACTS_ID_COL 28
-#define PULL_QUERY_COL_AMOUNT 29
+#define CONTACTS_ID_COL 38
+#define PULL_QUERY_COL_AMOUNT 39
 #define COL_HOME_NUMBER 0
 #define COL_HOME_EMAIL 7
 #define COL_WORK_NUMBER 8
 #define COL_FAX_NUMBER 16
 #define COL_WORK_EMAIL 17
-#define COL_DATE 25
-#define COL_SENT 26
-#define COL_ANSWERED 27
+#define COL_OTHER_NUMBER 34
+#define COL_DATE 35
+#define COL_SENT 36
+#define COL_ANSWERED 37
+#define ADDR_FIELD_AMOUNT 7
 
 #define CONTACTS_QUERY_ALL						\
 	"SELECT ?v nco:fullname(?c) "					\
@@ -62,9 +64,12 @@
 	"nco:phoneNumber(?w) nco:pobox(?p) nco:extendedAddress(?p) "	\
 	"nco:streetAddress(?p) nco:locality(?p) nco:region(?p) "	\
 	"nco:postalcode(?p) nco:country(?p) ?f nco:emailAddress(?ew) "	\
-	"nco:birthDate(?c) nco:nickname(?c) nco:websiteUrl(?c) "	\
+	"nco:birthDate(?c) nco:nickname(?c) nco:url(?c) "		\
 	"nco:photo(?c) nco:fullname(?o) nco:department(?a) "		\
-	"nco:role(?a) "							\
+	"nco:role(?a) nco:pobox(?pw) nco:extendedAddress(?pw) "		\
+	"nco:streetAddress(?pw) nco:locality(?pw) nco:region(?pw) "	\
+	"nco:postalcode(?pw) nco:country(?pw) nco:contactUID(?c) "	\
+	"nco:title(?a) nco:phoneNumber(?t) "				\
 	"\"NOTACALL\" \"false\" \"false\" ?c "				\
 	"WHERE { "							\
 		"?c a nco:PersonContact . "				\
@@ -84,6 +89,7 @@
 		"?c nco:hasAffiliation ?a . "				\
 		"OPTIONAL { ?a nco:hasPhoneNumber ?w . } " 		\
 		"OPTIONAL { ?a nco:hasEmailAddress ?ew . } "		\
+		"OPTIONAL { ?a nco:hasPostalAddress ?pw . } "		\
 		"OPTIONAL { ?a nco:org ?o . } "				\
 	"} "								\
 	"}"
@@ -109,33 +115,56 @@
 	"nco:nameHonorificSuffix(?c) nco:emailAddress(?e) "		\
 	"nco:phoneNumber(?w) nco:pobox(?p) nco:extendedAddress(?p) "	\
 	"nco:streetAddress(?p) nco:locality(?p) nco:region(?p) "	\
-	"nco:postalcode(?p) nco:country(?p) ?f nco:emailAddress(?ew) "	\
-	"nco:birthDate(?c) nco:nickname(?c) nco:websiteUrl(?c) "	\
+	"nco:postalcode(?p) nco:country(?p) \"\" nco:emailAddress(?ew) "\
+	"nco:birthDate(?c) nco:nickname(?c) nco:url(?c) "		\
 	"nco:photo(?c) nco:fullname(?o) nco:department(?a) "		\
-	"nco:role(?a) "							\
-	"nmo:receivedDate(?call) "					\
-	"nmo:isSent(?call) nmo:isAnswered(?call) ?c "			\
+	"nco:role(?a) nco:pobox(?pw) nco:extendedAddress(?pw) "		\
+	"nco:streetAddress(?pw) nco:locality(?pw) nco:region(?pw) "	\
+	"nco:postalcode(?pw) nco:country(?pw) nco:contactUID(?c) "	\
+	"nco:title(?a) nco:phoneNumber(?t) nmo:receivedDate(?call) "	\
+	"nmo:isSent(?call) nmo:isAnswered(?call) ?x "			\
 	"WHERE { "							\
+	"{ "								\
+		"?x a nco:Contact . "					\
+		"?x nco:hasPhoneNumber ?h . "				\
 		"?call a nmo:Call ; "					\
-		"nmo:from ?c ; "					\
+		"nmo:from ?x ; "					\
 		"nmo:isSent false ; "					\
-		"nmo:isAnswered false ."				\
-		"?c a nco:Contact . "					\
-	"OPTIONAL { ?c nco:hasPhoneNumber ?h . "			\
-		"OPTIONAL {"						\
-		"?h a nco:FaxNumber ; "					\
-		"nco:phoneNumber ?f . "					\
-		"}"							\
-	"} "								\
-	"OPTIONAL { ?c nco:hasEmailAddress ?e . } "			\
-	"OPTIONAL { ?c nco:hasPostalAddress ?p . } "			\
-	"OPTIONAL { "							\
+		"nmo:isAnswered false . "				\
+		"?c a nco:PersonContact . "				\
+		"?c nco:hasPhoneNumber ?h . "				\
+		"OPTIONAL { ?c nco:hasEmailAddress ?e . } "		\
+		"OPTIONAL { ?c nco:hasPostalAddress ?p . } "		\
+		"OPTIONAL { "						\
+			"?c nco:hasAffiliation ?a . "			\
+			"OPTIONAL { ?a nco:hasEmailAddress ?ew . } "	\
+			"OPTIONAL { ?a nco:hasPostalAddress ?pw . } "	\
+			"OPTIONAL { ?a nco:org ?o . } "			\
+		"} "							\
+	"} UNION { "							\
+		"?x a nco:Contact . "					\
+		"?x nco:hasPhoneNumber ?w . "				\
+		"?call a nmo:Call ; "					\
+		"nmo:from ?x ; "					\
+		"nmo:isSent false ; "					\
+		"nmo:isAnswered false . "				\
+		"?c a nco:PersonContact . "				\
+		"OPTIONAL { ?c nco:hasEmailAddress ?e . } "		\
+		"OPTIONAL { ?c nco:hasPostalAddress ?p . } "		\
 		"?c nco:hasAffiliation ?a . "				\
-		"OPTIONAL { ?a nco:hasPhoneNumber ?w . } " 		\
+		"?a nco:hasPhoneNumber ?w . "				\
 		"OPTIONAL { ?a nco:hasEmailAddress ?ew . } "		\
+		"OPTIONAL { ?a nco:hasPostalAddress ?pw . } "		\
 		"OPTIONAL { ?a nco:org ?o . } "				\
+	"} UNION { "							\
+		"?x a nco:Contact . "					\
+		"?x nco:hasPhoneNumber ?t . "				\
+		"?call a nmo:Call ; "					\
+		"nmo:from ?x ; "					\
+		"nmo:isSent false ; "					\
+		"nmo:isAnswered false . "				\
 	"} "								\
-	"} ORDER BY DESC(nmo:receivedDate(?call))"
+	"} ORDER BY DESC(nmo:receivedDate(?call)) "
 
 #define MISSED_CALLS_LIST						\
 	"SELECT ?c nco:nameFamily(?c) "					\
@@ -158,33 +187,56 @@
 	"nco:nameHonorificSuffix(?c) nco:emailAddress(?e) "		\
 	"nco:phoneNumber(?w) nco:pobox(?p) nco:extendedAddress(?p) "	\
 	"nco:streetAddress(?p) nco:locality(?p) nco:region(?p) "	\
-	"nco:postalcode(?p) nco:country(?p) ?f nco:emailAddress(?ew) "	\
-	"nco:birthDate(?c) nco:nickname(?c) nco:websiteUrl(?c) "	\
+	"nco:postalcode(?p) nco:country(?p) \"\" nco:emailAddress(?ew) "\
+	"nco:birthDate(?c) nco:nickname(?c) nco:url(?c) "		\
 	"nco:photo(?c) nco:fullname(?o) nco:department(?a) "		\
-	"nco:role(?a) "							\
-	"nmo:receivedDate(?call) "					\
-	"nmo:isSent(?call) nmo:isAnswered(?call) ?c "			\
+	"nco:role(?a) nco:pobox(?pw) nco:extendedAddress(?pw) "		\
+	"nco:streetAddress(?pw) nco:locality(?pw) nco:region(?pw) "	\
+	"nco:postalcode(?pw) nco:country(?pw) nco:contactUID(?c) "	\
+	"nco:title(?a) nco:phoneNumber(?t) nmo:receivedDate(?call) "	\
+	"nmo:isSent(?call) nmo:isAnswered(?call) ?x "			\
 	"WHERE { "							\
+	"{ "								\
+		"?x a nco:Contact . "					\
+		"?x nco:hasPhoneNumber ?h . "				\
 		"?call a nmo:Call ; "					\
-		"nmo:from ?c ; "					\
+		"nmo:from ?x ; "					\
 		"nmo:isSent false ; "					\
-		"nmo:isAnswered true ."					\
-		"?c a nco:Contact . "					\
-	"OPTIONAL { ?c nco:hasPhoneNumber ?h . "			\
-		"OPTIONAL {"						\
-		"?h a nco:FaxNumber ; "					\
-		"nco:phoneNumber ?f . "					\
-		"}"							\
-	"} "								\
-	"OPTIONAL { ?c nco:hasEmailAddress ?e . } "			\
-	"OPTIONAL { ?c nco:hasPostalAddress ?p . } "			\
-	"OPTIONAL { "							\
+		"nmo:isAnswered true . "				\
+		"?c a nco:PersonContact . "				\
+		"?c nco:hasPhoneNumber ?h . "				\
+		"OPTIONAL { ?c nco:hasEmailAddress ?e . } "		\
+		"OPTIONAL { ?c nco:hasPostalAddress ?p . } "		\
+		"OPTIONAL { "						\
+			"?c nco:hasAffiliation ?a . "			\
+			"OPTIONAL { ?a nco:hasEmailAddress ?ew . } "	\
+			"OPTIONAL { ?a nco:hasPostalAddress ?pw . } "	\
+			"OPTIONAL { ?a nco:org ?o . } "			\
+		"} "							\
+	"} UNION { "							\
+		"?x a nco:Contact . "					\
+		"?x nco:hasPhoneNumber ?w . "				\
+		"?call a nmo:Call ; "					\
+		"nmo:from ?x ; "					\
+		"nmo:isSent false ; "					\
+		"nmo:isAnswered true . "				\
+		"?c a nco:PersonContact . "				\
+		"OPTIONAL { ?c nco:hasEmailAddress ?e . } "		\
+		"OPTIONAL { ?c nco:hasPostalAddress ?p . } "		\
 		"?c nco:hasAffiliation ?a . "				\
-		"OPTIONAL { ?a nco:hasPhoneNumber ?w . } " 		\
+		"?a nco:hasPhoneNumber ?w . "				\
 		"OPTIONAL { ?a nco:hasEmailAddress ?ew . } "		\
+		"OPTIONAL { ?a nco:hasPostalAddress ?pw . } "		\
 		"OPTIONAL { ?a nco:org ?o . } "				\
+	"} UNION { "							\
+		"?x a nco:Contact . "					\
+		"?x nco:hasPhoneNumber ?t . "				\
+		"?call a nmo:Call ; "					\
+		"nmo:from ?x ; "					\
+		"nmo:isSent false ; "					\
+		"nmo:isAnswered true . "				\
 	"} "								\
-	"} ORDER BY DESC(nmo:receivedDate(?call))"
+	"} ORDER BY DESC(nmo:receivedDate(?call)) "
 
 #define INCOMING_CALLS_LIST						\
 	"SELECT ?c nco:nameFamily(?c) "					\
@@ -207,32 +259,53 @@
 	"nco:nameHonorificSuffix(?c) nco:emailAddress(?e) "		\
 	"nco:phoneNumber(?w) nco:pobox(?p) nco:extendedAddress(?p) "	\
 	"nco:streetAddress(?p) nco:locality(?p) nco:region(?p) "	\
-	"nco:postalcode(?p) nco:country(?p) ?f nco:emailAddress(?ew)"	\
-	"nco:birthDate(?c) nco:nickname(?c) nco:websiteUrl(?c) "	\
+	"nco:postalcode(?p) nco:country(?p) \"\" nco:emailAddress(?ew)"	\
+	"nco:birthDate(?c) nco:nickname(?c) nco:url(?c) "		\
 	"nco:photo(?c) nco:fullname(?o) nco:department(?a) "		\
-	"nco:role(?a) "							\
-	"nmo:receivedDate(?call) "					\
-	"nmo:isSent(?call) nmo:isAnswered(?call) ?c "			\
+	"nco:role(?a) nco:pobox(?pw) nco:extendedAddress(?pw) "		\
+	"nco:streetAddress(?pw) nco:locality(?pw) nco:region(?pw) "	\
+	"nco:postalcode(?pw) nco:country(?pw) nco:contactUID(?c) "	\
+	"nco:title(?a) nco:phoneNumber(?t) nmo:receivedDate(?call) "	\
+	"nmo:isSent(?call) nmo:isAnswered(?call) ?x "			\
 	"WHERE { "							\
+	"{ "								\
+		"?x a nco:Contact . "					\
+		"?x nco:hasPhoneNumber ?h . "				\
 		"?call a nmo:Call ; "					\
-		"nmo:to ?c ; "						\
+		"nmo:to ?x ; "						\
 		"nmo:isSent true . "					\
-		"?c a nco:Contact . "					\
-	"OPTIONAL { ?c nco:hasPhoneNumber ?h . "			\
-		"OPTIONAL {"						\
-		"?h a nco:FaxNumber ; "					\
-		"nco:phoneNumber ?f . "					\
-		"}"							\
-	"} "								\
-	"OPTIONAL { ?c nco:hasEmailAddress ?e . } "			\
-	"OPTIONAL { ?c nco:hasPostalAddress ?p . } "			\
-	"OPTIONAL { "							\
+		"?c a nco:PersonContact . "				\
+		"?c nco:hasPhoneNumber ?h . "				\
+		"OPTIONAL { ?c nco:hasEmailAddress ?e . } "		\
+		"OPTIONAL { ?c nco:hasPostalAddress ?p . } "		\
+		"OPTIONAL { "						\
+			"?c nco:hasAffiliation ?a . "			\
+			"OPTIONAL { ?a nco:hasEmailAddress ?ew . } "	\
+			"OPTIONAL { ?a nco:hasPostalAddress ?pw . } "	\
+			"OPTIONAL { ?a nco:org ?o . } "			\
+		"} "							\
+	"} UNION { "							\
+		"?x a nco:Contact . "					\
+		"?x nco:hasPhoneNumber ?w . "				\
+		"?call a nmo:Call ; "					\
+		"nmo:to ?x ; "						\
+		"nmo:isSent true . "					\
+		"?c a nco:PersonContact . "				\
+		"OPTIONAL { ?c nco:hasEmailAddress ?e . } "		\
+		"OPTIONAL { ?c nco:hasPostalAddress ?p . } "		\
 		"?c nco:hasAffiliation ?a . "				\
-		"OPTIONAL { ?a nco:hasPhoneNumber ?w . } " 		\
+		"?a nco:hasPhoneNumber ?w . "				\
 		"OPTIONAL { ?a nco:hasEmailAddress ?ew . } "		\
+		"OPTIONAL { ?a nco:hasPostalAddress ?pw . } "		\
 		"OPTIONAL { ?a nco:org ?o . } "				\
+	"} UNION { "							\
+		"?x a nco:Contact . "					\
+		"?x nco:hasPhoneNumber ?t . "				\
+		"?call a nmo:Call ; "					\
+		"nmo:to ?x ; "						\
+		"nmo:isSent true . "					\
 	"} "								\
-	"} ORDER BY DESC(nmo:sentDate(?call))"
+	"} ORDER BY DESC(nmo:sentDate(?call)) "
 
 #define OUTGOING_CALLS_LIST						\
 	"SELECT ?c nco:nameFamily(?c) "					\
@@ -254,50 +327,93 @@
 	"nco:nameHonorificSuffix(?c) nco:emailAddress(?e) "		\
 	"nco:phoneNumber(?w) nco:pobox(?p) nco:extendedAddress(?p) "	\
 	"nco:streetAddress(?p) nco:locality(?p) nco:region(?p) "	\
-	"nco:postalcode(?p) nco:country(?p) ?f nco:emailAddress(?ew) "	\
-	"nco:birthDate(?c) nco:nickname(?c) nco:websiteUrl(?c) "	\
+	"nco:postalcode(?p) nco:country(?p) \"\" nco:emailAddress(?ew) "\
+	"nco:birthDate(?c) nco:nickname(?c) nco:url(?c) "		\
 	"nco:photo(?c) nco:fullname(?o) nco:department(?a) "		\
-	"nco:role(?a) "							\
-	"nmo:receivedDate(?call) "					\
-	"nmo:isSent(?call) nmo:isAnswered(?call) ?c "			\
+	"nco:role(?a) nco:pobox(?pw) nco:extendedAddress(?pw) "		\
+	"nco:streetAddress(?pw) nco:locality(?pw) nco:region(?pw) "	\
+	"nco:postalcode(?pw) nco:country(?pw) nco:contactUID(?c) "	\
+	"nco:title(?a) nco:phoneNumber(?t) nmo:receivedDate(?call) "	\
+	"nmo:isSent(?call) nmo:isAnswered(?call) ?x "			\
 	"WHERE { "							\
 	"{ "								\
+		"{ "							\
+		"?x a nco:Contact . "					\
+		"?x nco:hasPhoneNumber ?h . "				\
 		"?call a nmo:Call ; "					\
-		"nmo:to ?c ; "						\
+		"nmo:to ?x ; "						\
 		"nmo:isSent true . "					\
-		"?c a nco:Contact . "					\
-	"OPTIONAL { ?c nco:hasPhoneNumber ?h . "			\
-		"OPTIONAL {"						\
-		"?h a nco:FaxNumber ; "					\
-		"nco:phoneNumber ?f . "					\
-		"}"							\
-	"} "								\
-	"OPTIONAL { ?c nco:hasEmailAddress ?e . } "			\
-	"OPTIONAL { ?c nco:hasPostalAddress ?p . } "			\
-	"OPTIONAL { ?c nco:hasAffiliation ?a . "			\
-		"OPTIONAL { ?a nco:hasPhoneNumber ?w . } " 		\
+		"?c a nco:PersonContact . "				\
+		"?c nco:hasPhoneNumber ?h . "				\
+		"OPTIONAL { ?c nco:hasEmailAddress ?e . } "		\
+		"OPTIONAL { ?c nco:hasPostalAddress ?p . } "		\
+		"OPTIONAL { "						\
+			"?c nco:hasAffiliation ?a . "			\
+			"OPTIONAL { ?a nco:hasEmailAddress ?ew . } "	\
+			"OPTIONAL { ?a nco:hasPostalAddress ?pw . } "	\
+			"OPTIONAL { ?a nco:org ?o . } "			\
+		"} "							\
+		"} UNION { "						\
+		"?x a nco:Contact . "					\
+		"?x nco:hasPhoneNumber ?w . "				\
+		"?call a nmo:Call ; "					\
+		"nmo:to ?x ; "						\
+		"nmo:isSent true . "					\
+		"?c a nco:PersonContact . "				\
+		"OPTIONAL { ?c nco:hasEmailAddress ?e . } "		\
+		"OPTIONAL { ?c nco:hasPostalAddress ?p . } "		\
+		"?c nco:hasAffiliation ?a . "				\
+		"?a nco:hasPhoneNumber ?w . "				\
 		"OPTIONAL { ?a nco:hasEmailAddress ?ew . } "		\
+		"OPTIONAL { ?a nco:hasPostalAddress ?pw . } "		\
 		"OPTIONAL { ?a nco:org ?o . } "				\
-	"} "								\
+		"} UNION { "						\
+		"?x a nco:Contact . "					\
+		"?x nco:hasPhoneNumber ?t . "				\
+		"?call a nmo:Call ; "					\
+		"nmo:to ?x ; "						\
+		"nmo:isSent true . "					\
+		"} "							\
 	"} UNION { "							\
+		"{ "							\
+		"?x a nco:Contact . "					\
+		"?x nco:hasPhoneNumber ?h . "				\
 		"?call a nmo:Call ; "					\
-		"nmo:from ?c ; "					\
+		"nmo:from ?x ; "					\
 		"nmo:isSent false . "					\
-		"?c a nco:Contact . "					\
-	"OPTIONAL { ?c nco:hasPhoneNumber ?h . "			\
-		"OPTIONAL {"						\
-		"?h a nco:FaxNumber ; "					\
-		"nco:phoneNumber ?f . "					\
-		"}"							\
-	"} "								\
-	"OPTIONAL { ?c nco:hasEmailAddress ?e . } "			\
-	"OPTIONAL { ?c nco:hasPostalAddress ?p . } "			\
-	"OPTIONAL { ?c nco:hasAffiliation ?a . "			\
-		"OPTIONAL { ?a nco:hasPhoneNumber ?w . } " 		\
+		"?c a nco:PersonContact . "				\
+		"?c nco:hasPhoneNumber ?h . "				\
+		"OPTIONAL { ?c nco:hasEmailAddress ?e . } "		\
+		"OPTIONAL { ?c nco:hasPostalAddress ?p . } "		\
+		"OPTIONAL { "						\
+			"?c nco:hasAffiliation ?a . "			\
+			"OPTIONAL { ?a nco:hasEmailAddress ?ew . } "	\
+			"OPTIONAL { ?a nco:hasPostalAddress ?pw . } "	\
+			"OPTIONAL { ?a nco:org ?o . } "			\
+		"} "							\
+		"} UNION { "						\
+		"?x a nco:Contact . "					\
+		"?x nco:hasPhoneNumber ?w . "				\
+		"?call a nmo:Call ; "					\
+		"nmo:from ?x ; "					\
+		"nmo:isSent false . "					\
+		"?c a nco:PersonContact . "				\
+		"OPTIONAL { ?c nco:hasEmailAddress ?e . } "		\
+		"OPTIONAL { ?c nco:hasPostalAddress ?p . } "		\
+		"?c nco:hasAffiliation ?a . "				\
+		"?a nco:hasPhoneNumber ?w . "				\
 		"OPTIONAL { ?a nco:hasEmailAddress ?ew . } "		\
+		"OPTIONAL { ?a nco:hasPostalAddress ?pw . } "		\
 		"OPTIONAL { ?a nco:org ?o . } "				\
+		"} UNION { "						\
+		"?x a nco:Contact . "					\
+		"?x nco:hasPhoneNumber ?t . "				\
+		"?call a nmo:Call ; "					\
+		"nmo:from ?x ; "					\
+		"nmo:isSent false . "					\
+		"} "							\
 	"} "								\
-	"} } ORDER BY DESC(nmo:receivedDate(?call))"
+	"} ORDER BY DESC(nmo:receivedDate(?call)) "
 
 #define COMBINED_CALLS_LIST						\
 	"SELECT ?c nco:nameFamily(?c) nco:nameGiven(?c) "		\
@@ -327,9 +443,12 @@
 	"nco:phoneNumber(?w) nco:pobox(?p) nco:extendedAddress(?p) "	\
 	"nco:streetAddress(?p) nco:locality(?p) nco:region(?p) "	\
 	"nco:postalcode(?p) nco:country(?p) ?f  nco:emailAddress(?ew)"	\
-	"nco:birthDate(<%s>) nco:nickname(<%s>) nco:websiteUrl(<%s>) "	\
+	"nco:birthDate(<%s>) nco:nickname(<%s>) nco:url(<%s>) "		\
 	"nco:photo(<%s>) nco:fullname(?o) nco:department(?a) "		\
-	"nco:role(?a) "							\
+	"nco:role(?a) nco:pobox(?pw) nco:extendedAddress(?pw) "		\
+	"nco:streetAddress(?pw) nco:locality(?pw) nco:region(?pw) "	\
+	"nco:postalcode(?pw) nco:country(?pw) nco:contactUID(<%s>) "	\
+	"nco:title(?a) nco:phoneNumber(?t) "				\
 	"\"NOTACALL\" \"false\" \"false\" <%s> "			\
 	"WHERE { "							\
 		"<%s> a nco:Contact . "					\
@@ -349,6 +468,7 @@
 		"<%s> nco:hasAffiliation ?a . "				\
 		"OPTIONAL { ?a nco:hasPhoneNumber ?w . }" 		\
 		"OPTIONAL { ?a nco:hasEmailAddress ?ew . }"		\
+		"OPTIONAL { ?a nco:hasPostalAddress ?pw . } "		\
 		"OPTIONAL { ?a nco:org ?o . } "				\
 	"} "								\
 	"}"
@@ -585,7 +705,7 @@
 
 	memset(&tm, 0, sizeof(tm));
 
-	nr = sscanf(datetime, "%04u-%02u-%02uT%02u:%02u:%02u%c",
+	nr = sscanf(datetime, "%04u%02u%02uT%02u%02u%02u%c",
 			&tm.tm_year, &tm.tm_mon, &tm.tm_mday,
 			&tm.tm_hour, &tm.tm_min, &tm.tm_sec,
 			&tz);
@@ -606,7 +726,7 @@
 
 	local = localtime(&time);
 
-	strftime(localdate, sizeof(localdate), "%Y-%m-%dT%H:%M:%S", local);
+	strftime(localdate, sizeof(localdate), "%Y%m%dT%H%M%S", local);
 
 	return g_strdup(localdate);
 }
@@ -654,10 +774,24 @@
 static struct phonebook_number *find_phone(GSList *numbers, const char *phone,
 								int type)
 {
-	GSList *l;
+	GSList *l = numbers;
 	struct phonebook_number *pb_num;
 
-	for (l = numbers; l; l = l->next) {
+	if (g_slist_length(l) == 1 && (pb_num = l->data) &&
+					g_strcmp0(pb_num->tel, phone) == 0) {
+
+		if ((type == TEL_TYPE_HOME || type == TEL_TYPE_WORK) &&
+					pb_num->type == TEL_TYPE_OTHER)	{
+			pb_num->type = type;
+			return pb_num;
+		}
+
+		if (type == TEL_TYPE_OTHER && (pb_num->type == TEL_TYPE_HOME ||
+					pb_num->type == TEL_TYPE_WORK))
+			return pb_num;
+	}
+
+	for (; l; l = l->next) {
 		pb_num = l->data;
 		/* Returning phonebook number if phone values and type values
 		 * are equal */
@@ -721,6 +855,41 @@
 	contact->emails = g_slist_append(contact->emails, email);
 }
 
+static struct phonebook_address *find_address(GSList *addresses,
+					const char *address, int type)
+{
+	GSList *l;
+
+	for (l = addresses; l; l = l->next) {
+		struct phonebook_address *addr = l->data;
+		if (g_strcmp0(addr->addr, address) == 0 &&
+						addr->type == type)
+			return addr;
+	}
+
+	return NULL;
+}
+
+static void add_address(struct phonebook_contact *contact,
+					const char *address, int type)
+{
+	struct phonebook_address *addr;
+
+	if (address == NULL || address_fields_present(address) == FALSE)
+		return;
+
+	/* Not adding address if there is already added with the same value */
+	if (find_address(contact->addresses, address, type))
+		return;
+
+	addr = g_new0(struct phonebook_address, 1);
+
+	addr->addr = g_strdup(address);
+	addr->type = type;
+
+	contact->addresses = g_slist_append(contact->addresses, addr);
+}
+
 static GString *gen_vcards(GSList *contacts,
 					const struct apparam_field *params)
 {
@@ -753,6 +922,12 @@
 	GString *vcards;
 	int last_index, i;
 	gboolean cdata_present = FALSE;
+	char *home_addr, *work_addr;
+
+	if (num_fields < 0) {
+		data->cb(NULL, 0, num_fields, 0, data->user_data);
+		goto fail;
+	}
 
 	DBG("reply %p", reply);
 
@@ -786,13 +961,11 @@
 
 	data->index++;
 
-	/* Just interested in knowing the phonebook size */
-	if (!data->vcardentry && params->maxlistcount == 0)
-		return;
-
 	last_index = params->liststartoffset + params->maxlistcount;
 
-	if (data->index <= params->liststartoffset || data->index > last_index)
+	if ((data->index <= params->liststartoffset ||
+			data->index > last_index) &&
+			params->maxlistcount > 0)
 		return;
 
 add_entry:
@@ -803,20 +976,15 @@
 	contact->additional = g_strdup(reply[4]);
 	contact->prefix = g_strdup(reply[5]);
 	contact->suffix = g_strdup(reply[6]);
-	contact->pobox = g_strdup(reply[9]);
-	contact->extended = g_strdup(reply[10]);
-	contact->street = g_strdup(reply[11]);
-	contact->locality = g_strdup(reply[12]);
-	contact->region = g_strdup(reply[13]);
-	contact->postal = g_strdup(reply[14]);
-	contact->country = g_strdup(reply[15]);
 	contact->birthday = g_strdup(reply[18]);
 	contact->nickname = g_strdup(reply[19]);
 	contact->website = g_strdup(reply[20]);
 	contact->photo = g_strdup(reply[21]);
 	contact->company = g_strdup(reply[22]);
 	contact->department = g_strdup(reply[23]);
-	contact->title = g_strdup(reply[24]);
+	contact->role = g_strdup(reply[24]);
+	contact->uid = g_strdup(reply[32]);
+	contact->title = g_strdup(reply[33]);
 
 	set_call_type(contact, reply[COL_DATE], reply[COL_SENT],
 			reply[COL_ANSWERED]);
@@ -826,11 +994,27 @@
 	add_phone_number(contact, reply[COL_HOME_NUMBER], TEL_TYPE_HOME);
 	add_phone_number(contact, reply[COL_WORK_NUMBER], TEL_TYPE_WORK);
 	add_phone_number(contact, reply[COL_FAX_NUMBER], TEL_TYPE_FAX);
+	add_phone_number(contact, reply[COL_OTHER_NUMBER], TEL_TYPE_OTHER);
 
 	/* Adding emails */
 	add_email(contact, reply[COL_HOME_EMAIL], EMAIL_TYPE_HOME);
 	add_email(contact, reply[COL_WORK_EMAIL], EMAIL_TYPE_WORK);
 
+	/* Adding addresses */
+	home_addr = g_strdup_printf("%s;%s;%s;%s;%s;%s;%s",
+				reply[9], reply[10], reply[11], reply[12],
+				reply[13], reply[14], reply[15]);
+
+	work_addr = g_strdup_printf("%s;%s;%s;%s;%s;%s;%s",
+				reply[25], reply[26], reply[27], reply[28],
+				reply[29], reply[30], reply[31]);
+
+	add_address(contact, home_addr, ADDR_TYPE_HOME);
+	add_address(contact, work_addr, ADDR_TYPE_WORK);
+
+	g_free(home_addr);
+	g_free(work_addr);
+
 	DBG("contact %p", contact);
 
 	/* Adding contacts data to wrapper struct - this data will be used to
@@ -848,11 +1032,13 @@
 	vcards = gen_vcards(data->contacts, params);
 
 	if (num_fields == 0)
-		data->cb(vcards->str, vcards->len, data->index, 0,
-							data->user_data);
+		data->cb(vcards->str, vcards->len,
+				g_slist_length(data->contacts), 0,
+				data->user_data);
 
-	g_slist_free(data->contacts);
 	g_string_free(vcards, TRUE);
+fail:
+	g_slist_free(data->contacts);
 	g_free(data);
 }
 
@@ -862,7 +1048,7 @@
 	char *formatted;
 	int i;
 
-	if (reply == NULL)
+	if (reply == NULL || num_fields < 0)
 		goto done;
 
 	/* the first element is the URI, always not empty */
@@ -895,7 +1081,7 @@
 	return;
 
 done:
-	if (num_fields == 0)
+	if (num_fields <= 0)
 		cache->ready_cb(cache->user_data);
 
 	g_free(cache);
@@ -1022,7 +1208,7 @@
 
 	query = g_strdup_printf(CONTACTS_QUERY_FROM_URI, id, id, id, id, id,
 						id, id, id, id, id, id, id,
-						id, id, id, id);
+						id, id, id, id, id);
 
 	ret = query_tracker(query, PULL_QUERY_COL_AMOUNT, pull_contacts, data);
 
--- plugins/vcard.c
+++ plugins/vcard.c
@@ -31,6 +31,7 @@
 
 #include "vcard.h"
 
+#define ADDR_FIELD_AMOUNT 7
 #define LEN_MAX 128
 #define TYPE_INTERNATIONAL 145
 
@@ -155,22 +156,20 @@
 	return FALSE;
 }
 
-static gboolean address_fields_present(struct phonebook_contact *contact)
+gboolean address_fields_present(const char *address)
 {
-	if (contact->pobox && strlen(contact->pobox))
-		return TRUE;
-	if (contact->extended && strlen(contact->extended))
-		return TRUE;
-	if (contact->street && strlen(contact->street))
-		return TRUE;
-	if (contact->locality && strlen(contact->locality))
-		return TRUE;
-	if (contact->region && strlen(contact->region))
-		return TRUE;
-	if (contact->postal && strlen(contact->postal))
-		return TRUE;
-	if (contact->country && strlen(contact->country))
-		return TRUE;
+	gchar **fields = g_strsplit(address, ";", ADDR_FIELD_AMOUNT);
+	int i;
+
+	for (i = 0; i < ADDR_FIELD_AMOUNT; ++i) {
+
+		if (strlen(fields[i]) != 0) {
+			g_strfreev(fields);
+			return TRUE;
+		}
+	}
+
+	g_strfreev(fields);
 
 	return FALSE;
 }
@@ -242,9 +241,9 @@
 		break;
 	case TEL_TYPE_OTHER:
 		if (format == FORMAT_VCARD21)
-			category_string = "VOICE";
+			category_string = "OTHER;VOICE";
 		else if (format == FORMAT_VCARD30)
-			category_string = "TYPE=VOICE";
+			category_string = "TYPE=OTHER;TYPE=VOICE";
 		break;
 	}
 
@@ -358,9 +357,6 @@
 	if (contact->department && strlen(contact->department))
 		return TRUE;
 
-	if (contact->title && strlen(contact->title))
-		return TRUE;
-
 	return FALSE;
 }
 
@@ -372,22 +368,58 @@
 		return;
 	}
 
-	vcard_printf(vcards, "ORG:%s;%s;%s", contact->company,
-				contact->department, contact->title);
+	vcard_printf(vcards, "ORG:%s;%s", contact->company,
+				contact->department);
 }
 
-static void vcard_printf_adr(GString *vcards,
-					struct phonebook_contact *contact)
+static void vcard_printf_address(GString *vcards, uint8_t format,
+					const char *address,
+					enum phonebook_address_type category)
 {
-	if (address_fields_present(contact) == FALSE) {
+	char buf[LEN_MAX];
+	char field[ADDR_FIELD_AMOUNT][LEN_MAX];
+	const char *category_string = "";
+	int len, i;
+	gchar **address_fields;
+
+	if (!address || address_fields_present(address) == FALSE) {
 		vcard_printf(vcards, "ADR:");
 		return;
 	}
 
-	vcard_printf(vcards, "ADR:%s;%s;%s;%s;%s;%s;%s", contact->pobox,
-					contact->extended, contact->street,
-					contact->locality, contact->region,
-					contact->postal, contact->country);
+	switch (category) {
+	case ADDR_TYPE_HOME:
+		if (format == FORMAT_VCARD21)
+			category_string = "HOME";
+		else if (format == FORMAT_VCARD30)
+			category_string = "TYPE=HOME";
+		break;
+	case ADDR_TYPE_WORK:
+		if (format == FORMAT_VCARD21)
+			category_string = "WORK";
+		else if (format == FORMAT_VCARD30)
+			category_string = "TYPE=WORK";
+		break;
+	default:
+		if (format == FORMAT_VCARD21)
+			category_string = "OTHER";
+		else if (format == FORMAT_VCARD30)
+			category_string = "TYPE=OTHER";
+		break;
+	}
+
+	address_fields = g_strsplit(address, ";", ADDR_FIELD_AMOUNT);
+
+	for (i = 0; i < ADDR_FIELD_AMOUNT; ++i) {
+		len = strlen(address_fields[i]);
+		add_slash(field[i], address_fields[i], LEN_MAX, len);
+	}
+
+	snprintf(buf, LEN_MAX, "%s;%s;%s;%s;%s;%s;%s",
+	field[0], field[1], field[2], field[3], field[4], field[5], field[6]);
+	g_strfreev(address_fields);
+
+	vcard_printf(vcards,"ADR;%s:%s", category_string, buf);
 }
 
 static void vcard_printf_datetime(GString *vcards,
@@ -420,7 +452,6 @@
 static void vcard_printf_end(GString *vcards)
 {
 	vcard_printf(vcards, "END:VCARD");
-	vcard_printf(vcards, "");
 }
 
 void phonebook_add_contact(GString *vcards, struct phonebook_contact *contact,
@@ -433,6 +464,9 @@
 
 	vcard_printf_begin(vcards, format);
 
+	if (filter & FILTER_UID)
+		vcard_printf_tag(vcards, format, "UID", NULL, contact->uid);
+
 	if (filter & FILTER_N)
 		vcard_printf_name(vcards, contact);
 
@@ -468,8 +502,19 @@
 		}
 	}
 
-	if (filter & FILTER_ADR)
-		vcard_printf_adr(vcards, contact);
+	if (filter & FILTER_ADR) {
+		GSList *l = contact->addresses;
+
+		if (g_slist_length(l) == 0)
+			vcard_printf_address(vcards, format, NULL,
+							ADDR_TYPE_OTHER);
+
+		for (; l; l = l->next) {
+			struct phonebook_address *addr = l->data;
+			vcard_printf_address(vcards, format, addr->addr,
+								addr->type);
+		}
+	}
 
 	if (filter & FILTER_BDAY)
 		vcard_printf_tag(vcards, format, "BDAY", NULL,
@@ -490,6 +535,12 @@
 	if (filter & FILTER_ORG)
 		vcard_printf_org(vcards, contact);
 
+	if (filter & FILTER_ROLE)
+		vcard_printf_tag(vcards, format, "ROLE", NULL, contact->role);
+
+	if (filter & FILTER_TITLE)
+		vcard_printf_tag(vcards, format, "TITLE", NULL, contact->title);
+
 	if (filter & FILTER_X_IRMC_CALL_DATETIME)
 		vcard_printf_datetime(vcards, contact);
 
@@ -512,6 +563,14 @@
 	g_free(email);
 }
 
+static void address_free(gpointer data, gpointer user_data)
+{
+	struct phonebook_address *addr = data;
+
+	g_free(addr->addr);
+	g_free(addr);
+}
+
 void phonebook_contact_free(struct phonebook_contact *contact)
 {
 	if (contact == NULL)
@@ -523,25 +582,23 @@
 	g_slist_foreach(contact->emails, email_free, NULL);
 	g_slist_free(contact->emails);
 
+	g_slist_foreach(contact->addresses, address_free, NULL);
+	g_slist_free(contact->addresses);
+
+	g_free(contact->uid);
 	g_free(contact->fullname);
 	g_free(contact->given);
 	g_free(contact->family);
 	g_free(contact->additional);
 	g_free(contact->prefix);
 	g_free(contact->suffix);
-	g_free(contact->pobox);
-	g_free(contact->extended);
-	g_free(contact->street);
-	g_free(contact->locality);
-	g_free(contact->region);
-	g_free(contact->postal);
-	g_free(contact->country);
 	g_free(contact->birthday);
 	g_free(contact->nickname);
 	g_free(contact->website);
 	g_free(contact->photo);
 	g_free(contact->company);
 	g_free(contact->department);
+	g_free(contact->role);
 	g_free(contact->title);
 	g_free(contact->datetime);
 	g_free(contact);
--- plugins/vcard.h
+++ plugins/vcard.h
@@ -40,6 +40,12 @@
 	CALL_TYPE_OUTGOING,
 };
 
+enum phonebook_address_type {
+	ADDR_TYPE_HOME,
+	ADDR_TYPE_WORK,
+	ADDR_TYPE_OTHER,
+};
+
 struct phonebook_number {
 	char *tel;
 	int type;
@@ -50,7 +56,13 @@
 	int  type;
 };
 
+struct phonebook_address {
+	char *addr;
+	int type;
+};
+
 struct phonebook_contact {
+	char *uid;
 	char *fullname;
 	char *given;
 	char *family;
@@ -59,19 +71,14 @@
 	GSList *emails;
 	char *prefix;
 	char *suffix;
-	char *pobox;
-	char *extended;
-	char *street;
-	char *locality;
-	char *region;
-	char *postal;
-	char *country;
+	GSList *addresses;
 	char *birthday;
 	char *nickname;
 	char *website;
 	char *photo;
 	char *company;
 	char *department;
+	char *role;
 	char *title;
 	char *datetime;
 	int calltype;
@@ -81,3 +88,5 @@
 					uint64_t filter, uint8_t format);
 
 void phonebook_contact_free(struct phonebook_contact *contact);
+
+gboolean address_fields_present(const char *address);
--- src/btio.c
+++ src/btio.c
-/*
- *
- *  BlueZ - Bluetooth protocol stack for Linux
- *
- *  Copyright (C) 2009-2010  Marcel Holtmann <marcel at holtmann.org>
- *  Copyright (C) 2009-2010  Nokia Corporation
- *
- *
- *  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
- *
- */
-#include <stdarg.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <errno.h>
-#include <poll.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/l2cap.h>
-#include <bluetooth/rfcomm.h>
-#include <bluetooth/sco.h>
-#include <bluetooth/hci.h>
-#include <bluetooth/hci_lib.h>
-
-#include <glib.h>
-
-#include "btio.h"
-
-#define ERROR_FAILED(gerr, str, err) \
-		g_set_error(gerr, BT_IO_ERROR, BT_IO_ERROR_FAILED, \
-				str ": %s (%d)", strerror(err), err)
-
-#define DEFAULT_DEFER_TIMEOUT 30
-
-struct set_opts {
-	bdaddr_t src;
-	bdaddr_t dst;
-	int defer;
-	int sec_level;
-	uint8_t channel;
-	uint16_t psm;
-	uint16_t mtu;
-	uint16_t imtu;
-	uint16_t omtu;
-	int master;
-	uint8_t mode;
-};
-
-struct connect {
-	BtIOConnect connect;
-	void *user_data;
-	GDestroyNotify destroy;
-};
-
-struct accept {
-	BtIOConnect connect;
-	void *user_data;
-	GDestroyNotify destroy;
-};
-
-struct server {
-	BtIOConnect connect;
-	BtIOConfirm confirm;
-	void *user_data;
-	GDestroyNotify destroy;
-};
-
-static void server_remove(struct server *server)
-{
-	if (server->destroy)
-		server->destroy(server->user_data);
-	g_free(server);
-}
-
-static void connect_remove(struct connect *conn)
-{
-	if (conn->destroy)
-		conn->destroy(conn->user_data);
-	g_free(conn);
-}
-
-static void accept_remove(struct accept *accept)
-{
-	if (accept->destroy)
-		accept->destroy(accept->user_data);
-	g_free(accept);
-}
-
-static gboolean check_nval(GIOChannel *io)
-{
-	struct pollfd fds;
-
-	memset(&fds, 0, sizeof(fds));
-	fds.fd = g_io_channel_unix_get_fd(io);
-	fds.events = POLLNVAL;
-
-	if (poll(&fds, 1, 0) > 0 && (fds.revents & POLLNVAL))
-		return TRUE;
-
-	return FALSE;
-}
-
-static gboolean accept_cb(GIOChannel *io, GIOCondition cond,
-							void *user_data)
-{
-	struct accept *accept = user_data;
-	GError *err = NULL;
-
-	/* If the user aborted this accept attempt */
-	if ((cond & G_IO_NVAL) || check_nval(io))
-		return FALSE;
-
-	if (cond & (G_IO_HUP | G_IO_ERR))
-		g_set_error(&err, BT_IO_ERROR, BT_IO_ERROR_DISCONNECTED,
-				"HUP or ERR on socket");
-
-	accept->connect(io, err, accept->user_data);
-
-	g_clear_error(&err);
-
-	return FALSE;
-}
-
-static gboolean connect_cb(GIOChannel *io, GIOCondition cond,
-							void *user_data)
-{
-	struct connect *conn = user_data;
-	GError *gerr = NULL;
-
-	/* If the user aborted this connect attempt */
-	if ((cond & G_IO_NVAL) || check_nval(io))
-		return FALSE;
-
-	if (cond & G_IO_OUT) {
-		int err = 0, sock = g_io_channel_unix_get_fd(io);
-		socklen_t len = sizeof(err);
-
-		if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &err, &len) < 0)
-			err = errno;
-
-		if (err)
-			g_set_error(&gerr, BT_IO_ERROR,
-					BT_IO_ERROR_CONNECT_FAILED, "%s (%d)",
-					strerror(err), err);
-	} else if (cond & (G_IO_HUP | G_IO_ERR))
-		g_set_error(&gerr, BT_IO_ERROR, BT_IO_ERROR_CONNECT_FAILED,
-				"HUP or ERR on socket");
-
-	conn->connect(io, gerr, conn->user_data);
-
-	if (gerr)
-		g_error_free(gerr);
-
-	return FALSE;
-}
-
-static gboolean server_cb(GIOChannel *io, GIOCondition cond,
-							void *user_data)
-{
-	struct server *server = user_data;
-	int srv_sock, cli_sock;
-	GIOChannel *cli_io;
-
-	/* If the user closed the server */
-	if ((cond & G_IO_NVAL) || check_nval(io))
-		return FALSE;
-
-	srv_sock = g_io_channel_unix_get_fd(io);
-
-	cli_sock = accept(srv_sock, NULL, NULL);
-	if (cli_sock < 0)
-		return TRUE;
-
-	cli_io = g_io_channel_unix_new(cli_sock);
-
-	g_io_channel_set_close_on_unref(cli_io, TRUE);
-	g_io_channel_set_flags(cli_io, G_IO_FLAG_NONBLOCK, NULL);
-
-	if (server->confirm)
-		server->confirm(cli_io, server->user_data);
-	else
-		server->connect(cli_io, NULL, server->user_data);
-
-	g_io_channel_unref(cli_io);
-
-	return TRUE;
-}
-
-static void server_add(GIOChannel *io, BtIOConnect connect,
-				BtIOConfirm confirm, void *user_data,
-				GDestroyNotify destroy)
-{
-	struct server *server;
-	GIOCondition cond;
-
-	server = g_new0(struct server, 1);
-	server->connect = connect;
-	server->confirm = confirm;
-	server->user_data = user_data;
-	server->destroy = destroy;
-
-	cond = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
-	g_io_add_watch_full(io, G_PRIORITY_DEFAULT, cond, server_cb, server,
-					(GDestroyNotify) server_remove);
-}
-
-static void connect_add(GIOChannel *io, BtIOConnect connect,
-				void *user_data, GDestroyNotify destroy)
-{
-	struct connect *conn;
-	GIOCondition cond;
-
-	conn = g_new0(struct connect, 1);
-	conn->connect = connect;
-	conn->user_data = user_data;
-	conn->destroy = destroy;
-
-	cond = G_IO_OUT | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
-	g_io_add_watch_full(io, G_PRIORITY_DEFAULT, cond, connect_cb, conn,
-					(GDestroyNotify) connect_remove);
-}
-
-static void accept_add(GIOChannel *io, BtIOConnect connect, void *user_data,
-							GDestroyNotify destroy)
-{
-	struct accept *accept;
-	GIOCondition cond;
-
-	accept = g_new0(struct accept, 1);
-	accept->connect = connect;
-	accept->user_data = user_data;
-	accept->destroy = destroy;
-
-	cond = G_IO_OUT | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
-	g_io_add_watch_full(io, G_PRIORITY_DEFAULT, cond, accept_cb, accept,
-					(GDestroyNotify) accept_remove);
-}
-
-static int l2cap_bind(int sock, const bdaddr_t *src, uint16_t psm, GError **err)
-{
-	struct sockaddr_l2 addr;
-
-	memset(&addr, 0, sizeof(addr));
-	addr.l2_family = AF_BLUETOOTH;
-	bacpy(&addr.l2_bdaddr, src);
-	addr.l2_psm = htobs(psm);
-
-	if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
-		ERROR_FAILED(err, "l2cap_bind", errno);
-		return -1;
-	}
-
-	return 0;
-}
-
-static int l2cap_connect(int sock, const bdaddr_t *dst, uint16_t psm)
-{
-	int err;
-	struct sockaddr_l2 addr;
-
-	memset(&addr, 0, sizeof(addr));
-	addr.l2_family = AF_BLUETOOTH;
-	bacpy(&addr.l2_bdaddr, dst);
-	addr.l2_psm = htobs(psm);
-
-	err = connect(sock, (struct sockaddr *) &addr, sizeof(addr));
-	if (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS))
-		return err;
-
-	return 0;
-}
-
-static int l2cap_set_master(int sock, int master)
-{
-	int flags;
-	socklen_t len;
-
-	len = sizeof(flags);
-	if (getsockopt(sock, SOL_L2CAP, L2CAP_LM, &flags, &len) < 0)
-		return -errno;
-
-	if (master) {
-		if (flags & L2CAP_LM_MASTER)
-			return 0;
-		flags |= L2CAP_LM_MASTER;
-	} else {
-		if (!(flags & L2CAP_LM_MASTER))
-			return 0;
-		flags &= ~L2CAP_LM_MASTER;
-	}
-
-	if (setsockopt(sock, SOL_L2CAP, L2CAP_LM, &flags, sizeof(flags)) < 0)
-		return -errno;
-
-	return 0;
-}
-
-static int rfcomm_set_master(int sock, int master)
-{
-	int flags;
-	socklen_t len;
-
-	len = sizeof(flags);
-	if (getsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &flags, &len) < 0)
-		return -errno;
-
-	if (master) {
-		if (flags & RFCOMM_LM_MASTER)
-			return 0;
-		flags |= RFCOMM_LM_MASTER;
-	} else {
-		if (!(flags & RFCOMM_LM_MASTER))
-			return 0;
-		flags &= ~RFCOMM_LM_MASTER;
-	}
-
-	if (setsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &flags, sizeof(flags)) < 0)
-		return -errno;
-
-	return 0;
-}
-
-static int l2cap_set_lm(int sock, int level)
-{
-	int lm_map[] = {
-		0,
-		L2CAP_LM_AUTH,
-		L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT,
-		L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT | L2CAP_LM_SECURE,
-	}, opt = lm_map[level];
-
-	if (setsockopt(sock, SOL_L2CAP, L2CAP_LM, &opt, sizeof(opt)) < 0)
-		return -errno;
-
-	return 0;
-}
-
-static int rfcomm_set_lm(int sock, int level)
-{
-	int lm_map[] = {
-		0,
-		RFCOMM_LM_AUTH,
-		RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT,
-		RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT | RFCOMM_LM_SECURE,
-	}, opt = lm_map[level];
-
-	if (setsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &opt, sizeof(opt)) < 0)
-		return -errno;
-
-	return 0;
-}
-
-static gboolean set_sec_level(int sock, BtIOType type, int level, GError **err)
-{
-	struct bt_security sec;
-	int ret;
-
-	if (level < BT_SECURITY_LOW || level > BT_SECURITY_HIGH) {
-		g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
-				"Valid security level range is %d-%d",
-				BT_SECURITY_LOW, BT_SECURITY_HIGH);
-		return FALSE;
-	}
-
-	memset(&sec, 0, sizeof(sec));
-	sec.level = level;
-
-	if (setsockopt(sock, SOL_BLUETOOTH, BT_SECURITY, &sec,
-							sizeof(sec)) == 0)
-		return TRUE;
-
-	if (errno != ENOPROTOOPT) {
-		ERROR_FAILED(err, "setsockopt(BT_SECURITY)", errno);
-		return FALSE;
-	}
-
-	if (type == BT_IO_L2CAP)
-		ret = l2cap_set_lm(sock, level);
-	else
-		ret = rfcomm_set_lm(sock, level);
-
-	if (ret < 0) {
-		ERROR_FAILED(err, "setsockopt(LM)", -ret);
-		return FALSE;
-	}
-
-	return TRUE;
-}
-
-static int l2cap_get_lm(int sock, int *sec_level)
-{
-	int opt;
-	socklen_t len;
-
-	len = sizeof(opt);
-	if (getsockopt(sock, SOL_L2CAP, L2CAP_LM, &opt, &len) < 0)
-		return -errno;
-
-	*sec_level = 0;
-
-	if (opt & L2CAP_LM_AUTH)
-		*sec_level = BT_SECURITY_LOW;
-	if (opt & L2CAP_LM_ENCRYPT)
-		*sec_level = BT_SECURITY_MEDIUM;
-	if (opt & L2CAP_LM_SECURE)
-		*sec_level = BT_SECURITY_HIGH;
-
-	return 0;
-}
-
-static int rfcomm_get_lm(int sock, int *sec_level)
-{
-	int opt;
-	socklen_t len;
-
-	len = sizeof(opt);
-	if (getsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &opt, &len) < 0)
-		return -errno;
-
-	*sec_level = 0;
-
-	if (opt & RFCOMM_LM_AUTH)
-		*sec_level = BT_SECURITY_LOW;
-	if (opt & RFCOMM_LM_ENCRYPT)
-		*sec_level = BT_SECURITY_MEDIUM;
-	if (opt & RFCOMM_LM_SECURE)
-		*sec_level = BT_SECURITY_HIGH;
-
-	return 0;
-}
-
-static gboolean get_sec_level(int sock, BtIOType type, int *level,
-								GError **err)
-{
-	struct bt_security sec;
-	socklen_t len;
-	int ret;
-
-	memset(&sec, 0, sizeof(sec));
-	len = sizeof(sec);
-	if (getsockopt(sock, SOL_BLUETOOTH, BT_SECURITY, &sec, &len) == 0) {
-		*level = sec.level;
-		return TRUE;
-	}
-
-	if (errno != ENOPROTOOPT) {
-		ERROR_FAILED(err, "getsockopt(BT_SECURITY)", errno);
-		return FALSE;
-	}
-
-	if (type == BT_IO_L2CAP)
-		ret = l2cap_get_lm(sock, level);
-	else
-		ret = rfcomm_get_lm(sock, level);
-
-	if (ret < 0) {
-		ERROR_FAILED(err, "getsockopt(LM)", -ret);
-		return FALSE;
-	}
-
-	return TRUE;
-}
-
-static gboolean l2cap_set(int sock, int sec_level, uint16_t imtu, uint16_t omtu,
-					uint8_t mode, int master, GError **err)
-{
-	if (imtu || omtu || mode) {
-		struct l2cap_options l2o;
-		socklen_t len;
-
-		memset(&l2o, 0, sizeof(l2o));
-		len = sizeof(l2o);
-		if (getsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, &l2o,
-								&len) < 0) {
-			ERROR_FAILED(err, "getsockopt(L2CAP_OPTIONS)", errno);
-			return FALSE;
-		}
-
-		if (imtu)
-			l2o.imtu = imtu;
-		if (omtu)
-			l2o.omtu = omtu;
-		if (mode)
-			l2o.mode = mode;
-
-		if (setsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, &l2o,
-							sizeof(l2o)) < 0) {
-			ERROR_FAILED(err, "setsockopt(L2CAP_OPTIONS)", errno);
-			return FALSE;
-		}
-	}
-
-	if (master >= 0 && l2cap_set_master(sock, master) < 0) {
-		ERROR_FAILED(err, "l2cap_set_master", errno);
-		return FALSE;
-	}
-
-	if (sec_level && !set_sec_level(sock, BT_IO_L2CAP, sec_level, err))
-		return FALSE;
-
-	return TRUE;
-}
-
-static int rfcomm_bind(int sock,
-		const bdaddr_t *src, uint8_t channel, GError **err)
-{
-	struct sockaddr_rc addr;
-
-	memset(&addr, 0, sizeof(addr));
-	addr.rc_family = AF_BLUETOOTH;
-	bacpy(&addr.rc_bdaddr, src);
-	addr.rc_channel = channel;
-
-	if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
-		ERROR_FAILED(err, "rfcomm_bind", errno);
-		return -1;
-	}
-
-	return 0;
-}
-
-static int rfcomm_connect(int sock, const bdaddr_t *dst, uint8_t channel)
-{
-	int err;
-	struct sockaddr_rc addr;
-
-	memset(&addr, 0, sizeof(addr));
-	addr.rc_family = AF_BLUETOOTH;
-	bacpy(&addr.rc_bdaddr, dst);
-	addr.rc_channel = channel;
-
-	err = connect(sock, (struct sockaddr *) &addr, sizeof(addr));
-	if (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS))
-		return err;
-
-	return 0;
-}
-
-static gboolean rfcomm_set(int sock, int sec_level, int master, GError **err)
-{
-	if (sec_level && !set_sec_level(sock, BT_IO_RFCOMM, sec_level, err))
-		return FALSE;
-
-	if (master >= 0 && rfcomm_set_master(sock, master) < 0) {
-		ERROR_FAILED(err, "rfcomm_set_master", errno);
-		return FALSE;
-	}
-
-	return TRUE;
-}
-
-static int sco_bind(int sock, const bdaddr_t *src, GError **err)
-{
-	struct sockaddr_sco addr;
-
-	memset(&addr, 0, sizeof(addr));
-	addr.sco_family = AF_BLUETOOTH;
-	bacpy(&addr.sco_bdaddr, src);
-
-	if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
-		ERROR_FAILED(err, "sco_bind", errno);
-		return -1;
-	}
-
-	return 0;
-}
-
-static int sco_connect(int sock, const bdaddr_t *dst)
-{
-	struct sockaddr_sco addr;
-	int err;
-
-	memset(&addr, 0, sizeof(addr));
-	addr.sco_family = AF_BLUETOOTH;
-	bacpy(&addr.sco_bdaddr, dst);
-
-	err = connect(sock, (struct sockaddr *) &addr, sizeof(addr));
-	if (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS))
-		return err;
-
-	return 0;
-}
-
-static gboolean sco_set(int sock, uint16_t mtu, GError **err)
-{
-	struct sco_options sco_opt;
-	socklen_t len;
-
-	if (!mtu)
-		return TRUE;
-
-	len = sizeof(sco_opt);
-	memset(&sco_opt, 0, len);
-	if (getsockopt(sock, SOL_SCO, SCO_OPTIONS, &sco_opt, &len) < 0) {
-		ERROR_FAILED(err, "getsockopt(SCO_OPTIONS)", errno);
-		return FALSE;
-	}
-
-	sco_opt.mtu = mtu;
-	if (setsockopt(sock, SOL_SCO, SCO_OPTIONS, &sco_opt,
-						sizeof(sco_opt)) < 0) {
-		ERROR_FAILED(err, "setsockopt(SCO_OPTIONS)", errno);
-		return FALSE;
-	}
-
-	return TRUE;
-}
-
-static gboolean parse_set_opts(struct set_opts *opts, GError **err,
-						BtIOOption opt1, va_list args)
-{
-	BtIOOption opt = opt1;
-	const char *str;
-
-	memset(opts, 0, sizeof(*opts));
-
-	/* Set defaults */
-	opts->defer = DEFAULT_DEFER_TIMEOUT;
-	opts->master = -1;
-	opts->sec_level = BT_IO_SEC_MEDIUM;
-	opts->mode = L2CAP_MODE_BASIC;
-
-	while (opt != BT_IO_OPT_INVALID) {
-		switch (opt) {
-		case BT_IO_OPT_SOURCE:
-			str = va_arg(args, const char *);
-			if (strncasecmp(str, "hci", 3) == 0)
-				hci_devba(atoi(str + 3), &opts->src);
-			else
-				str2ba(str, &opts->src);
-			break;
-		case BT_IO_OPT_SOURCE_BDADDR:
-			bacpy(&opts->src, va_arg(args, const bdaddr_t *));
-			break;
-		case BT_IO_OPT_DEST:
-			str2ba(va_arg(args, const char *), &opts->dst);
-			break;
-		case BT_IO_OPT_DEST_BDADDR:
-			bacpy(&opts->dst, va_arg(args, const bdaddr_t *));
-			break;
-		case BT_IO_OPT_DEFER_TIMEOUT:
-			opts->defer = va_arg(args, int);
-			break;
-		case BT_IO_OPT_SEC_LEVEL:
-			opts->sec_level = va_arg(args, int);
-			break;
-		case BT_IO_OPT_CHANNEL:
-			opts->channel = va_arg(args, int);
-			break;
-		case BT_IO_OPT_PSM:
-			opts->psm = va_arg(args, int);
-			break;
-		case BT_IO_OPT_MTU:
-			opts->mtu = va_arg(args, int);
-			opts->imtu = opts->mtu;
-			opts->omtu = opts->mtu;
-			break;
-		case BT_IO_OPT_OMTU:
-			opts->omtu = va_arg(args, int);
-			if (!opts->mtu)
-				opts->mtu = opts->omtu;
-			break;
-		case BT_IO_OPT_IMTU:
-			opts->imtu = va_arg(args, int);
-			if (!opts->mtu)
-				opts->mtu = opts->imtu;
-			break;
-		case BT_IO_OPT_MASTER:
-			opts->master = va_arg(args, gboolean);
-			break;
-		case BT_IO_OPT_MODE:
-			opts->mode = va_arg(args, int);
-			break;
-		default:
-			g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
-					"Unknown option %d", opt);
-			return FALSE;
-		}
-
-		opt = va_arg(args, int);
-	}
-
-	return TRUE;
-}
-
-static gboolean get_peers(int sock, struct sockaddr *src, struct sockaddr *dst,
-				socklen_t len, GError **err)
-{
-	socklen_t olen;
-
-	memset(src, 0, len);
-	olen = len;
-	if (getsockname(sock, src, &olen) < 0) {
-		ERROR_FAILED(err, "getsockname", errno);
-		return FALSE;
-	}
-
-	memset(dst, 0, len);
-	olen = len;
-	if (getpeername(sock, dst, &olen) < 0) {
-		ERROR_FAILED(err, "getpeername", errno);
-		return FALSE;
-	}
-
-	return TRUE;
-}
-
-static int l2cap_get_info(int sock, uint16_t *handle, uint8_t *dev_class)
-{
-	struct l2cap_conninfo info;
-	socklen_t len;
-
-	len = sizeof(info);
-	if (getsockopt(sock, SOL_L2CAP, L2CAP_CONNINFO, &info, &len) < 0)
-		return -errno;
-
-	if (handle)
-		*handle = info.hci_handle;
-
-	if (dev_class)
-		memcpy(dev_class, info.dev_class, 3);
-
-	return 0;
-}
-
-static gboolean l2cap_get(int sock, GError **err, BtIOOption opt1,
-								va_list args)
-{
-	BtIOOption opt = opt1;
-	struct sockaddr_l2 src, dst;
-	struct l2cap_options l2o;
-	int flags;
-	uint8_t dev_class[3];
-	uint16_t handle;
-	socklen_t len;
-
-	len = sizeof(l2o);
-	memset(&l2o, 0, len);
-	if (getsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, &l2o, &len) < 0) {
-		ERROR_FAILED(err, "getsockopt(L2CAP_OPTIONS)", errno);
-		return FALSE;
-	}
-
-	if (!get_peers(sock, (struct sockaddr *) &src,
-				(struct sockaddr *) &dst, sizeof(src), err))
-		return FALSE;
-
-	while (opt != BT_IO_OPT_INVALID) {
-		switch (opt) {
-		case BT_IO_OPT_SOURCE:
-			ba2str(&src.l2_bdaddr, va_arg(args, char *));
-			break;
-		case BT_IO_OPT_SOURCE_BDADDR:
-			bacpy(va_arg(args, bdaddr_t *), &src.l2_bdaddr);
-			break;
-		case BT_IO_OPT_DEST:
-			ba2str(&dst.l2_bdaddr, va_arg(args, char *));
-			break;
-		case BT_IO_OPT_DEST_BDADDR:
-			bacpy(va_arg(args, bdaddr_t *), &dst.l2_bdaddr);
-			break;
-		case BT_IO_OPT_DEFER_TIMEOUT:
-			len = sizeof(int);
-			if (getsockopt(sock, SOL_BLUETOOTH, BT_DEFER_SETUP,
-					va_arg(args, int *), &len) < 0) {
-				ERROR_FAILED(err, "getsockopt(DEFER_SETUP)",
-									errno);
-				return FALSE;
-			}
-			break;
-		case BT_IO_OPT_SEC_LEVEL:
-			if (!get_sec_level(sock, BT_IO_L2CAP,
-						va_arg(args, int *), err))
-				return FALSE;
-			break;
-		case BT_IO_OPT_PSM:
-			*(va_arg(args, uint16_t *)) = src.l2_psm ?
-						src.l2_psm : dst.l2_psm;
-			break;
-		case BT_IO_OPT_OMTU:
-			*(va_arg(args, uint16_t *)) = l2o.omtu;
-			break;
-		case BT_IO_OPT_IMTU:
-			*(va_arg(args, uint16_t *)) = l2o.imtu;
-			break;
-		case BT_IO_OPT_MASTER:
-			len = sizeof(flags);
-			if (getsockopt(sock, SOL_L2CAP, L2CAP_LM, &flags,
-								&len) < 0) {
-				ERROR_FAILED(err, "getsockopt(L2CAP_LM)",
-									errno);
-				return FALSE;
-			}
-			*(va_arg(args, gboolean *)) =
-				(flags & L2CAP_LM_MASTER) ? TRUE : FALSE;
-			break;
-		case BT_IO_OPT_HANDLE:
-			if (l2cap_get_info(sock, &handle, dev_class) < 0) {
-				ERROR_FAILED(err, "L2CAP_CONNINFO", errno);
-				return FALSE;
-			}
-			*(va_arg(args, uint16_t *)) = handle;
-			break;
-		case BT_IO_OPT_CLASS:
-			if (l2cap_get_info(sock, &handle, dev_class) < 0) {
-				ERROR_FAILED(err, "L2CAP_CONNINFO", errno);
-				return FALSE;
-			}
-			memcpy(va_arg(args, uint8_t *), dev_class, 3);
-			break;
-		default:
-			g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
-					"Unknown option %d", opt);
-			return FALSE;
-		}
-
-		opt = va_arg(args, int);
-	}
-
-	return TRUE;
-}
-
-static int rfcomm_get_info(int sock, uint16_t *handle, uint8_t *dev_class)
-{
-	struct rfcomm_conninfo info;
-	socklen_t len;
-
-	len = sizeof(info);
-	if (getsockopt(sock, SOL_RFCOMM, RFCOMM_CONNINFO, &info, &len) < 0)
-		return -errno;
-
-	if (handle)
-		*handle = info.hci_handle;
-
-	if (dev_class)
-		memcpy(dev_class, info.dev_class, 3);
-
-	return 0;
-}
-
-static gboolean rfcomm_get(int sock, GError **err, BtIOOption opt1,
-								va_list args)
-{
-	BtIOOption opt = opt1;
-	struct sockaddr_rc src, dst;
-	int flags;
-	socklen_t len;
-	uint8_t dev_class[3];
-	uint16_t handle;
-
-	if (!get_peers(sock, (struct sockaddr *) &src,
-				(struct sockaddr *) &dst, sizeof(src), err))
-		return FALSE;
-
-	while (opt != BT_IO_OPT_INVALID) {
-		switch (opt) {
-		case BT_IO_OPT_SOURCE:
-			ba2str(&src.rc_bdaddr, va_arg(args, char *));
-			break;
-		case BT_IO_OPT_SOURCE_BDADDR:
-			bacpy(va_arg(args, bdaddr_t *), &src.rc_bdaddr);
-			break;
-		case BT_IO_OPT_DEST:
-			ba2str(&dst.rc_bdaddr, va_arg(args, char *));
-			break;
-		case BT_IO_OPT_DEST_BDADDR:
-			bacpy(va_arg(args, bdaddr_t *), &dst.rc_bdaddr);
-			break;
-		case BT_IO_OPT_DEFER_TIMEOUT:
-			len = sizeof(int);
-			if (getsockopt(sock, SOL_BLUETOOTH, BT_DEFER_SETUP,
-					va_arg(args, int *), &len) < 0) {
-				ERROR_FAILED(err, "getsockopt(DEFER_SETUP)",
-									errno);
-				return FALSE;
-			}
-			break;
-		case BT_IO_OPT_SEC_LEVEL:
-			if (!get_sec_level(sock, BT_IO_RFCOMM,
-						va_arg(args, int *), err))
-				return FALSE;
-			break;
-		case BT_IO_OPT_CHANNEL:
-			*(va_arg(args, uint8_t *)) = src.rc_channel ?
-					src.rc_channel : dst.rc_channel;
-			break;
-		case BT_IO_OPT_SOURCE_CHANNEL:
-			*(va_arg(args, uint8_t *)) = src.rc_channel;
-			break;
-		case BT_IO_OPT_DEST_CHANNEL:
-			*(va_arg(args, uint8_t *)) = dst.rc_channel;
-			break;
-		case BT_IO_OPT_MASTER:
-			len = sizeof(flags);
-			if (getsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &flags,
-								&len) < 0) {
-				ERROR_FAILED(err, "getsockopt(RFCOMM_LM)",
-									errno);
-				return FALSE;
-			}
-			*(va_arg(args, gboolean *)) =
-				(flags & RFCOMM_LM_MASTER) ? TRUE : FALSE;
-			break;
-		case BT_IO_OPT_HANDLE:
-			if (rfcomm_get_info(sock, &handle, dev_class) < 0) {
-				ERROR_FAILED(err, "RFCOMM_CONNINFO", errno);
-				return FALSE;
-			}
-			*(va_arg(args, uint16_t *)) = handle;
-			break;
-		case BT_IO_OPT_CLASS:
-			if (rfcomm_get_info(sock, &handle, dev_class) < 0) {
-				ERROR_FAILED(err, "RFCOMM_CONNINFO", errno);
-				return FALSE;
-			}
-			memcpy(va_arg(args, uint8_t *), dev_class, 3);
-			break;
-		default:
-			g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
-					"Unknown option %d", opt);
-			return FALSE;
-		}
-
-		opt = va_arg(args, int);
-	}
-
-	return TRUE;
-}
-
-static int sco_get_info(int sock, uint16_t *handle, uint8_t *dev_class)
-{
-	struct sco_conninfo info;
-	socklen_t len;
-
-	len = sizeof(info);
-	if (getsockopt(sock, SOL_SCO, SCO_CONNINFO, &info, &len) < 0)
-		return -errno;
-
-	if (handle)
-		*handle = info.hci_handle;
-
-	if (dev_class)
-		memcpy(dev_class, info.dev_class, 3);
-
-	return 0;
-}
-
-static gboolean sco_get(int sock, GError **err, BtIOOption opt1, va_list args)
-{
-	BtIOOption opt = opt1;
-	struct sockaddr_sco src, dst;
-	struct sco_options sco_opt;
-	socklen_t len;
-	uint8_t dev_class[3];
-	uint16_t handle;
-
-	len = sizeof(sco_opt);
-	memset(&sco_opt, 0, len);
-	if (getsockopt(sock, SOL_SCO, SCO_OPTIONS, &sco_opt, &len) < 0) {
-		ERROR_FAILED(err, "getsockopt(SCO_OPTIONS)", errno);
-		return FALSE;
-	}
-
-	if (!get_peers(sock, (struct sockaddr *) &src,
-				(struct sockaddr *) &dst, sizeof(src), err))
-		return FALSE;
-
-	while (opt != BT_IO_OPT_INVALID) {
-		switch (opt) {
-		case BT_IO_OPT_SOURCE:
-			ba2str(&src.sco_bdaddr, va_arg(args, char *));
-			break;
-		case BT_IO_OPT_SOURCE_BDADDR:
-			bacpy(va_arg(args, bdaddr_t *), &src.sco_bdaddr);
-			break;
-		case BT_IO_OPT_DEST:
-			ba2str(&dst.sco_bdaddr, va_arg(args, char *));
-			break;
-		case BT_IO_OPT_DEST_BDADDR:
-			bacpy(va_arg(args, bdaddr_t *), &dst.sco_bdaddr);
-			break;
-		case BT_IO_OPT_MTU:
-		case BT_IO_OPT_IMTU:
-		case BT_IO_OPT_OMTU:
-			*(va_arg(args, uint16_t *)) = sco_opt.mtu;
-			break;
-		case BT_IO_OPT_HANDLE:
-			if (sco_get_info(sock, &handle, dev_class) < 0) {
-				ERROR_FAILED(err, "RFCOMM_CONNINFO", errno);
-				return FALSE;
-			}
-			*(va_arg(args, uint16_t *)) = handle;
-			break;
-		case BT_IO_OPT_CLASS:
-			if (sco_get_info(sock, &handle, dev_class) < 0) {
-				ERROR_FAILED(err, "RFCOMM_CONNINFO", errno);
-				return FALSE;
-			}
-			memcpy(va_arg(args, uint8_t *), dev_class, 3);
-			break;
-		default:
-			g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
-					"Unknown option %d", opt);
-			return FALSE;
-		}
-
-		opt = va_arg(args, int);
-	}
-
-	return TRUE;
-}
-
-static gboolean get_valist(GIOChannel *io, BtIOType type, GError **err,
-						BtIOOption opt1, va_list args)
-{
-	int sock;
-
-	sock = g_io_channel_unix_get_fd(io);
-
-	switch (type) {
-	case BT_IO_L2RAW:
-	case BT_IO_L2CAP:
-		return l2cap_get(sock, err, opt1, args);
-	case BT_IO_RFCOMM:
-		return rfcomm_get(sock, err, opt1, args);
-	case BT_IO_SCO:
-		return sco_get(sock, err, opt1, args);
-	}
-
-	g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
-			"Unknown BtIO type %d", type);
-	return FALSE;
-}
-
-gboolean bt_io_accept(GIOChannel *io, BtIOConnect connect, void *user_data,
-					GDestroyNotify destroy, GError **err)
-{
-	int sock;
-	char c;
-	struct pollfd pfd;
-
-	sock = g_io_channel_unix_get_fd(io);
-
-	memset(&pfd, 0, sizeof(pfd));
-	pfd.fd = sock;
-	pfd.events = POLLOUT;
-
-	if (poll(&pfd, 1, 0) < 0) {
-		ERROR_FAILED(err, "poll", errno);
-		return FALSE;
-	}
-
-	if (!(pfd.revents & POLLOUT)) {
-		int ret;
-		ret = read(sock, &c, 1);
-	}
-
-	accept_add(io, connect, user_data, destroy);
-
-	return TRUE;
-}
-
-gboolean bt_io_set(GIOChannel *io, BtIOType type, GError **err,
-							BtIOOption opt1, ...)
-{
-	va_list args;
-	gboolean ret;
-	struct set_opts opts;
-	int sock;
-
-	va_start(args, opt1);
-	ret = parse_set_opts(&opts, err, opt1, args);
-	va_end(args);
-
-	if (!ret)
-		return ret;
-
-	sock = g_io_channel_unix_get_fd(io);
-
-	switch (type) {
-	case BT_IO_L2RAW:
-	case BT_IO_L2CAP:
-		return l2cap_set(sock, opts.sec_level, opts.imtu, opts.omtu,
-						opts.mode, opts.master, err);
-	case BT_IO_RFCOMM:
-		return rfcomm_set(sock, opts.sec_level, opts.master, err);
-	case BT_IO_SCO:
-		return sco_set(sock, opts.mtu, err);
-	}
-
-	g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
-			"Unknown BtIO type %d", type);
-	return FALSE;
-}
-
-gboolean bt_io_get(GIOChannel *io, BtIOType type, GError **err,
-							BtIOOption opt1, ...)
-{
-	va_list args;
-	gboolean ret;
-
-	va_start(args, opt1);
-	ret = get_valist(io, type, err, opt1, args);
-	va_end(args);
-
-	return ret;
-}
-
-static GIOChannel *create_io(BtIOType type, gboolean server,
-					struct set_opts *opts, GError **err)
-{
-	int sock;
-	GIOChannel *io;
-
-	switch (type) {
-	case BT_IO_L2RAW:
-		sock = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_L2CAP);
-		if (sock < 0) {
-			ERROR_FAILED(err, "socket(RAW, L2CAP)", errno);
-			return NULL;
-		}
-		if (l2cap_bind(sock, &opts->src,
-					server ? opts->psm : 0, err) < 0)
-			goto failed;
-		if (!l2cap_set(sock, opts->sec_level, 0, 0, 0, -1, err))
-			goto failed;
-		break;
-	case BT_IO_L2CAP:
-		sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
-		if (sock < 0) {
-			ERROR_FAILED(err, "socket(SEQPACKET, L2CAP)", errno);
-			return NULL;
-		}
-		if (l2cap_bind(sock, &opts->src,
-					server ? opts->psm : 0, err) < 0)
-			goto failed;
-		if (!l2cap_set(sock, opts->sec_level, opts->imtu, opts->omtu,
-						opts->mode, opts->master, err))
-			goto failed;
-		break;
-	case BT_IO_RFCOMM:
-		sock = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
-		if (sock < 0) {
-			ERROR_FAILED(err, "socket(STREAM, RFCOMM)", errno);
-			return NULL;
-		}
-		if (rfcomm_bind(sock, &opts->src,
-					server ? opts->channel : 0, err) < 0)
-			goto failed;
-		if (!rfcomm_set(sock, opts->sec_level, opts->master, err))
-			goto failed;
-		break;
-	case BT_IO_SCO:
-		sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
-		if (sock < 0) {
-			ERROR_FAILED(err, "socket(SEQPACKET, SCO)", errno);
-			return NULL;
-		}
-		if (sco_bind(sock, &opts->src, err) < 0)
-			goto failed;
-		if (!sco_set(sock, opts->mtu, err))
-			goto failed;
-		break;
-	default:
-		g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
-				"Unknown BtIO type %d", type);
-		return NULL;
-	}
-
-	io = g_io_channel_unix_new(sock);
-
-	g_io_channel_set_close_on_unref(io, TRUE);
-	g_io_channel_set_flags(io, G_IO_FLAG_NONBLOCK, NULL);
-
-	return io;
-
-failed:
-	close(sock);
-
-	return NULL;
-}
-
-GIOChannel *bt_io_connect(BtIOType type, BtIOConnect connect,
-				void *user_data, GDestroyNotify destroy,
-				GError **gerr, BtIOOption opt1, ...)
-{
-	GIOChannel *io;
-	va_list args;
-	struct set_opts opts;
-	int err, sock;
-	gboolean ret;
-
-	va_start(args, opt1);
-	ret = parse_set_opts(&opts, gerr, opt1, args);
-	va_end(args);
-
-	if (ret == FALSE)
-		return NULL;
-
-	io = create_io(type, FALSE, &opts, gerr);
-	if (io == NULL)
-		return NULL;
-
-	sock = g_io_channel_unix_get_fd(io);
-
-	switch (type) {
-	case BT_IO_L2RAW:
-		err = l2cap_connect(sock, &opts.dst, 0);
-		break;
-	case BT_IO_L2CAP:
-		err = l2cap_connect(sock, &opts.dst, opts.psm);
-		break;
-	case BT_IO_RFCOMM:
-		err = rfcomm_connect(sock, &opts.dst, opts.channel);
-		break;
-	case BT_IO_SCO:
-		err = sco_connect(sock, &opts.dst);
-		break;
-	default:
-		g_set_error(gerr, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
-						"Unknown BtIO type %d", type);
-		return NULL;
-	}
-
-	if (err < 0) {
-		g_set_error(gerr, BT_IO_ERROR, BT_IO_ERROR_CONNECT_FAILED,
-				"connect: %s (%d)", strerror(-err), -err);
-		g_io_channel_unref(io);
-		return NULL;
-	}
-
-	connect_add(io, connect, user_data, destroy);
-
-	return io;
-}
-
-GIOChannel *bt_io_listen(BtIOType type, BtIOConnect connect,
-				BtIOConfirm confirm, void *user_data,
-				GDestroyNotify destroy, GError **err,
-				BtIOOption opt1, ...)
-{
-	GIOChannel *io;
-	va_list args;
-	struct set_opts opts;
-	int sock;
-	gboolean ret;
-
-	if (type == BT_IO_L2RAW) {
-		g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
-				"Server L2CAP RAW sockets not supported");
-		return NULL;
-	}
-
-	va_start(args, opt1);
-	ret = parse_set_opts(&opts, err, opt1, args);
-	va_end(args);
-
-	if (ret == FALSE)
-		return NULL;
-
-	io = create_io(type, TRUE, &opts, err);
-	if (io == NULL)
-		return NULL;
-
-	sock = g_io_channel_unix_get_fd(io);
-
-	if (confirm)
-		setsockopt(sock, SOL_BLUETOOTH, BT_DEFER_SETUP, &opts.defer,
-							sizeof(opts.defer));
-
-	if (listen(sock, 5) < 0) {
-		ERROR_FAILED(err, "listen", errno);
-		g_io_channel_unref(io);
-		return NULL;
-	}
-
-	server_add(io, connect, confirm, user_data, destroy);
-
-	return io;
-}
-
-GQuark bt_io_error_quark(void)
-{
-	return g_quark_from_static_string("bt-io-error-quark");
-}
-
--- src/btio.h
+++ src/btio.h
-/*
- *
- *  BlueZ - Bluetooth protocol stack for Linux
- *
- *  Copyright (C) 2009-2010  Marcel Holtmann <marcel at holtmann.org>
- *  Copyright (C) 2009-2010  Nokia Corporation
- *
- *
- *  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
- *
- */
-#ifndef BT_IO_H
-#define BT_IO_H
-
-#include <glib.h>
-
-typedef enum {
-	BT_IO_ERROR_DISCONNECTED,
-	BT_IO_ERROR_CONNECT_FAILED,
-	BT_IO_ERROR_FAILED,
-	BT_IO_ERROR_INVALID_ARGS,
-} BtIOError;
-
-#define BT_IO_ERROR bt_io_error_quark()
-
-GQuark bt_io_error_quark(void);
-
-typedef enum {
-	BT_IO_L2RAW,
-	BT_IO_L2CAP,
-	BT_IO_RFCOMM,
-	BT_IO_SCO,
-} BtIOType;
-
-typedef enum {
-	BT_IO_OPT_INVALID = 0,
-	BT_IO_OPT_SOURCE,
-	BT_IO_OPT_SOURCE_BDADDR,
-	BT_IO_OPT_DEST,
-	BT_IO_OPT_DEST_BDADDR,
-	BT_IO_OPT_DEFER_TIMEOUT,
-	BT_IO_OPT_SEC_LEVEL,
-	BT_IO_OPT_CHANNEL,
-	BT_IO_OPT_SOURCE_CHANNEL,
-	BT_IO_OPT_DEST_CHANNEL,
-	BT_IO_OPT_PSM,
-	BT_IO_OPT_MTU,
-	BT_IO_OPT_OMTU,
-	BT_IO_OPT_IMTU,
-	BT_IO_OPT_MASTER,
-	BT_IO_OPT_HANDLE,
-	BT_IO_OPT_CLASS,
-	BT_IO_OPT_MODE,
-} BtIOOption;
-
-typedef enum {
-	BT_IO_SEC_SDP = 0,
-	BT_IO_SEC_LOW,
-	BT_IO_SEC_MEDIUM,
-	BT_IO_SEC_HIGH,
-} BtIOSecLevel;
-
-typedef void (*BtIOConfirm)(GIOChannel *io, void *user_data);
-
-typedef void (*BtIOConnect)(GIOChannel *io, GError *err, void *user_data);
-
-gboolean bt_io_accept(GIOChannel *io, BtIOConnect connect, void *user_data,
-					GDestroyNotify destroy, GError **err);
-
-gboolean bt_io_set(GIOChannel *io, BtIOType type, GError **err,
-						BtIOOption opt1, ...);
-
-gboolean bt_io_get(GIOChannel *io, BtIOType type, GError **err,
-						BtIOOption opt1, ...);
-
-GIOChannel *bt_io_connect(BtIOType type, BtIOConnect connect,
-				void *user_data, GDestroyNotify destroy,
-				GError **err, BtIOOption opt1, ...);
-
-GIOChannel *bt_io_listen(BtIOType type, BtIOConnect connect,
-				BtIOConfirm confirm, void *user_data,
-				GDestroyNotify destroy, GError **err,
-				BtIOOption opt1, ...);
-
-#endif
--- src/manager.c
+++ src/manager.c
@@ -648,7 +648,8 @@
 
 void manager_emit_transfer_completed(struct obex_session *os)
 {
-	emit_transfer_completed(os->cid, !os->aborted);
+	if (os->object)
+		emit_transfer_completed(os->cid, !os->aborted);
 }
 
 DBusConnection *obex_dbus_get_connection(void)
--- src/obex.c
+++ src/obex.c
@@ -555,7 +555,7 @@
 						obex_object_t *obj)
 {
 	int size;
-	int32_t len = 0;
+	ssize_t len = 0;
 	const uint8_t *buffer;
 
 	DBG("name=%s type=%s rx_mtu=%d file=%p",
@@ -587,14 +587,16 @@
 	os->buf = g_realloc(os->buf, os->pending + size);
 	memcpy(os->buf + os->pending, buffer, size);
 	os->pending += size;
-	if (os->object == NULL) {
+
+	/* only write if both object and driver are valid */
+	if (os->object == NULL || os->driver == NULL) {
 		DBG("Stored %u bytes into temporary buffer", os->pending);
 		return 0;
 	}
 
 write:
 	while (os->pending > 0) {
-		int w;
+		ssize_t w;
 
 		w = os->driver->write(os->object, os->buf + len,
 					os->pending);
@@ -620,7 +622,7 @@
 {
 	obex_headerdata_t hd;
 	uint8_t *ptr;
-	int32_t len;
+	ssize_t len;
 	unsigned int flags;
 	uint8_t hi;
 
@@ -642,7 +644,7 @@
 
 	len = os->driver->read(os->object, os->buf, os->tx_mtu, &hi);
 	if (len < 0) {
-		error("read(): %s (%d)", strerror(-len), -len);
+		error("read(): %s (%zd)", strerror(-len), -len);
 		if (len == -EAGAIN)
 			return len;
 		else if (len == -ENOSTR)
@@ -666,6 +668,9 @@
 	case OBEX_HDR_APPARAM:
 		flags =  0;
 		break;
+	default:
+		error("read(): unkown header type %u", hi);
+		return -EIO;
 	}
 
 	OBEX_ObjectAddHeader(obex, obj, hi, hd, len, flags);
@@ -673,12 +678,11 @@
 	if (len == 0) {
 		g_free(os->buf);
 		os->buf = NULL;
-		return len;
 	}
 
 	os->offset += len;
 
-	return len;
+	return 0;
 }
 
 static gboolean handle_async_io(void *object, int flags, int err,
@@ -808,11 +812,23 @@
 		os->obj = obj;
 		os->driver->set_io_watch(os->object, handle_async_io, os);
 		return;
-	} else
+	} else {
 		/* Standard data stream */
 		OBEX_ObjectAddHeader (obex, obj, OBEX_HDR_BODY,
 				hd, 0, OBEX_FL_STREAM_START);
 
+		/* Try to write to stream and suspend the stream immediately
+		 * if no data available to send. */
+		err = obex_write_stream(os, obex, obj);
+		if (err == -EAGAIN) {
+			OBEX_SuspendRequest(obex, obj);
+			os->obj = obj;
+			os->driver->set_io_watch(os->object, handle_async_io,
+									os);
+			return;
+		}
+	}
+
 done:
 	os_set_response(obj, err);
 }
@@ -878,7 +894,7 @@
 								&size, &err);
 	if (object == NULL) {
 		error("open(%s): %s (%d)", filename, strerror(-err), -err);
-		goto fail;
+		return err;
 	}
 
 	os->object = object;
@@ -889,12 +905,6 @@
 		os->buf = g_malloc0(os->tx_mtu);
 
 	return 0;
-
-fail:
-	if (object)
-		os->driver->close(object);
-
-	return err;
 }
 
 int obex_put_stream_start(struct obex_session *os, const char *filename)

++++++ obexd.yaml
--- obexd.yaml
+++ obexd.yaml
@@ -1,6 +1,6 @@
 Name: obexd
 Summary: D-Bus service for Obex Client access
-Version: 0.32
+Version: 0.35
 Release: 1
 Group: System/Daemons
 License: GPLv2+




More information about the MeeGo-commits mailing list