[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