From: Anas Nashif Date: Tue, 15 Jan 2013 04:20:15 +0000 (-0800) Subject: Imported Upstream version 0.48 X-Git-Tag: upstream/0.48^0 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=c52b30407a5555b48205a4f92f447bfbcd88b2ce;p=platform%2Fupstream%2Fobexd.git Imported Upstream version 0.48 --- diff --git a/ChangeLog b/ChangeLog index 89a2607..eda7900 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +ver 0.48: + Fix issue with phonebook contacts query. + Add support for phonebook client filters. + Add support for message status handling. + Add support for OBEX application parameter API. + ver 0.47: Update client session API. Change D-Bus namespace to org.bluez.obex. diff --git a/Makefile.am b/Makefile.am index 97a1553..724dd5d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -20,7 +20,8 @@ gobex_sources = gobex/gobex.h gobex/gobex.c \ gobex/gobex-defs.h gobex/gobex-defs.c \ gobex/gobex-packet.c gobex/gobex-packet.h \ gobex/gobex-header.c gobex/gobex-header.h \ - gobex/gobex-transfer.c gobex/gobex-debug.h + gobex/gobex-transfer.c gobex/gobex-debug.h \ + gobex/gobex-apparam.h gobex/gobex-apparam.c noinst_PROGRAMS = libexec_PROGRAMS = @@ -62,7 +63,7 @@ builtin_sources += plugins/pbap.c plugins/phonebook.h \ builtin_modules += mas builtin_sources += plugins/mas.c plugins/messages.h \ - src/map_ap.c src/map_ap.h + src/map_ap.h builtin_modules += irmc builtin_sources += plugins/irmc.c @@ -126,7 +127,7 @@ client_obex_client_SOURCES = $(gdbus_sources) $(gobex_sources) \ client/transport.h client/transport.c \ client/dbus.h client/dbus.c \ client/driver.h client/driver.c \ - src/map_ap.h src/map_ap.c + src/map_ap.h client_obex_client_LDADD = @GLIB_LIBS@ @DBUS_LIBS@ @BLUEZ_LIBS@ endif @@ -165,11 +166,12 @@ plugins/phonebook.c: plugins/@PHONEBOOK_DRIVER@ plugins/messages.c: plugins/@MESSAGES_DRIVER@ $(AM_V_GEN)$(LN_S) @abs_top_srcdir@/$< $@ -TESTS = unit/test-gobex-header unit/test-gobex-packet unit/test-gobex \ - unit/test-gobex-transfer +TESTS = unit/test-gobex-apparam unit/test-gobex-header unit/test-gobex-packet \ + unit/test-gobex unit/test-gobex-transfer noinst_PROGRAMS += unit/test-gobex-header unit/test-gobex-packet \ - unit/test-gobex unit/test-gobex-transfer + unit/test-gobex unit/test-gobex-transfer \ + unit/test-gobex-apparam unit_test_gobex_SOURCES = $(gobex_sources) unit/test-gobex.c \ unit/util.c unit/util.h @@ -187,6 +189,10 @@ unit_test_gobex_transfer_SOURCES = $(gobex_sources) unit/util.c unit/util.h \ unit/test-gobex-transfer.c unit_test_gobex_transfer_LDADD = @GLIB_LIBS@ +unit_test_gobex_apparam_SOURCES = $(gobex_sources) unit/util.c unit/util.h \ + unit/test-gobex-apparam.c +unit_test_gobex_apparam_LDADD = @GLIB_LIBS@ + if READLINE noinst_PROGRAMS += tools/test-client tools_test_client_SOURCES = $(gobex_sources) $(btio_sources) \ diff --git a/Makefile.in b/Makefile.in index 3286903..056839e 100644 --- a/Makefile.in +++ b/Makefile.in @@ -55,7 +55,8 @@ build_triplet = @build@ host_triplet = @host@ noinst_PROGRAMS = unit/test-gobex-header$(EXEEXT) \ unit/test-gobex-packet$(EXEEXT) unit/test-gobex$(EXEEXT) \ - unit/test-gobex-transfer$(EXEEXT) $(am__EXEEXT_3) \ + unit/test-gobex-transfer$(EXEEXT) \ + unit/test-gobex-apparam$(EXEEXT) $(am__EXEEXT_3) \ tools/test-server$(EXEEXT) libexec_PROGRAMS = $(am__EXEEXT_1) $(am__EXEEXT_2) @SERVER_TRUE@am__append_1 = src/obexd.service.in @@ -66,7 +67,8 @@ libexec_PROGRAMS = $(am__EXEEXT_1) $(am__EXEEXT_2) @SERVER_TRUE@am__append_6 = src/obexd @CLIENT_TRUE@am__append_7 = client/obex-client.service.in @CLIENT_TRUE@am__append_8 = client/obex-client -TESTS = unit/test-gobex-header$(EXEEXT) \ +TESTS = unit/test-gobex-apparam$(EXEEXT) \ + unit/test-gobex-header$(EXEEXT) \ unit/test-gobex-packet$(EXEEXT) unit/test-gobex$(EXEEXT) \ unit/test-gobex-transfer$(EXEEXT) @READLINE_TRUE@am__append_9 = tools/test-client @@ -126,7 +128,8 @@ am__client_obex_client_SOURCES_DIST = gdbus/gdbus.h gdbus/mainloop.c \ gobex/gobex.c gobex/gobex-defs.h gobex/gobex-defs.c \ gobex/gobex-packet.c gobex/gobex-packet.h gobex/gobex-header.c \ gobex/gobex-header.h gobex/gobex-transfer.c \ - gobex/gobex-debug.h btio/btio.h btio/btio.c client/main.c \ + gobex/gobex-debug.h gobex/gobex-apparam.h \ + gobex/gobex-apparam.c btio/btio.h btio/btio.c client/main.c \ src/log.h src/log.c client/manager.h client/manager.c \ client/session.h client/session.c client/bluetooth.h \ client/bluetooth.c client/sync.h client/sync.c client/pbap.h \ @@ -134,13 +137,13 @@ am__client_obex_client_SOURCES_DIST = gdbus/gdbus.h gdbus/mainloop.c \ client/opp.c client/map.h client/map.c client/transfer.h \ client/transfer.c client/transport.h client/transport.c \ client/dbus.h client/dbus.c client/driver.h client/driver.c \ - src/map_ap.h src/map_ap.c + src/map_ap.h am__dirstamp = $(am__leading_dot)dirstamp am__objects_1 = gdbus/mainloop.$(OBJEXT) gdbus/watch.$(OBJEXT) \ gdbus/object.$(OBJEXT) gdbus/polkit.$(OBJEXT) am__objects_2 = gobex/gobex.$(OBJEXT) gobex/gobex-defs.$(OBJEXT) \ gobex/gobex-packet.$(OBJEXT) gobex/gobex-header.$(OBJEXT) \ - gobex/gobex-transfer.$(OBJEXT) + gobex/gobex-transfer.$(OBJEXT) gobex/gobex-apparam.$(OBJEXT) am__objects_3 = btio/btio.$(OBJEXT) @CLIENT_TRUE@am_client_obex_client_OBJECTS = $(am__objects_1) \ @CLIENT_TRUE@ $(am__objects_2) $(am__objects_3) \ @@ -151,7 +154,7 @@ am__objects_3 = btio/btio.$(OBJEXT) @CLIENT_TRUE@ client/opp.$(OBJEXT) client/map.$(OBJEXT) \ @CLIENT_TRUE@ client/transfer.$(OBJEXT) \ @CLIENT_TRUE@ client/transport.$(OBJEXT) client/dbus.$(OBJEXT) \ -@CLIENT_TRUE@ client/driver.$(OBJEXT) src/map_ap.$(OBJEXT) +@CLIENT_TRUE@ client/driver.$(OBJEXT) client_obex_client_OBJECTS = $(am_client_obex_client_OBJECTS) client_obex_client_DEPENDENCIES = AM_V_lt = $(am__v_lt_@AM_V@) @@ -163,12 +166,13 @@ am__src_obexd_SOURCES_DIST = gdbus/gdbus.h gdbus/mainloop.c \ plugins/filesystem.h plugins/pcsuite.c plugins/opp.c \ plugins/ftp.c plugins/ftp.h plugins/pbap.c plugins/phonebook.h \ plugins/vcard.h plugins/vcard.c plugins/mas.c \ - plugins/messages.h src/map_ap.c src/map_ap.h plugins/irmc.c \ + plugins/messages.h src/map_ap.h plugins/irmc.c \ plugins/syncevolution.c btio/btio.h btio/btio.c gobex/gobex.h \ gobex/gobex.c gobex/gobex-defs.h gobex/gobex-defs.c \ gobex/gobex-packet.c gobex/gobex-packet.h gobex/gobex-header.c \ gobex/gobex-header.h gobex/gobex-transfer.c \ - gobex/gobex-debug.h src/main.c src/obexd.h src/plugin.h \ + gobex/gobex-debug.h gobex/gobex-apparam.h \ + gobex/gobex-apparam.c src/main.c src/obexd.h src/plugin.h \ src/plugin.c src/log.h src/log.c src/manager.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 \ @@ -181,7 +185,7 @@ am__src_obexd_SOURCES_DIST = gdbus/gdbus.h gdbus/mainloop.c \ @SERVER_TRUE@ $(am__objects_5) plugins/opp.$(OBJEXT) \ @SERVER_TRUE@ plugins/ftp.$(OBJEXT) plugins/pbap.$(OBJEXT) \ @SERVER_TRUE@ plugins/vcard.$(OBJEXT) plugins/mas.$(OBJEXT) \ -@SERVER_TRUE@ src/map_ap.$(OBJEXT) plugins/irmc.$(OBJEXT) \ +@SERVER_TRUE@ plugins/irmc.$(OBJEXT) \ @SERVER_TRUE@ plugins/syncevolution.$(OBJEXT) @SERVER_TRUE@am_src_obexd_OBJECTS = $(am__objects_1) $(am__objects_6) \ @SERVER_TRUE@ $(am__objects_3) $(am__objects_2) \ @@ -203,7 +207,8 @@ src_obexd_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ am__tools_test_client_SOURCES_DIST = gobex/gobex.h gobex/gobex.c \ gobex/gobex-defs.h gobex/gobex-defs.c gobex/gobex-packet.c \ gobex/gobex-packet.h gobex/gobex-header.c gobex/gobex-header.h \ - gobex/gobex-transfer.c gobex/gobex-debug.h btio/btio.h \ + gobex/gobex-transfer.c gobex/gobex-debug.h \ + gobex/gobex-apparam.h gobex/gobex-apparam.c btio/btio.h \ btio/btio.c tools/test-client.c @READLINE_TRUE@am_tools_test_client_OBJECTS = $(am__objects_2) \ @READLINE_TRUE@ $(am__objects_3) tools/test-client.$(OBJEXT) @@ -217,6 +222,11 @@ am_unit_test_gobex_OBJECTS = $(am__objects_2) \ unit/test-gobex.$(OBJEXT) unit/util.$(OBJEXT) unit_test_gobex_OBJECTS = $(am_unit_test_gobex_OBJECTS) unit_test_gobex_DEPENDENCIES = +am_unit_test_gobex_apparam_OBJECTS = $(am__objects_2) \ + unit/util.$(OBJEXT) unit/test-gobex-apparam.$(OBJEXT) +unit_test_gobex_apparam_OBJECTS = \ + $(am_unit_test_gobex_apparam_OBJECTS) +unit_test_gobex_apparam_DEPENDENCIES = am_unit_test_gobex_header_OBJECTS = $(am__objects_2) \ unit/test-gobex-header.$(OBJEXT) unit/util.$(OBJEXT) unit_test_gobex_header_OBJECTS = $(am_unit_test_gobex_header_OBJECTS) @@ -259,6 +269,7 @@ am__v_GEN_0 = @echo " GEN " $@; SOURCES = $(client_obex_client_SOURCES) $(src_obexd_SOURCES) \ $(nodist_src_obexd_SOURCES) $(tools_test_client_SOURCES) \ $(tools_test_server_SOURCES) $(unit_test_gobex_SOURCES) \ + $(unit_test_gobex_apparam_SOURCES) \ $(unit_test_gobex_header_SOURCES) \ $(unit_test_gobex_packet_SOURCES) \ $(unit_test_gobex_transfer_SOURCES) @@ -266,6 +277,7 @@ DIST_SOURCES = $(am__client_obex_client_SOURCES_DIST) \ $(am__src_obexd_SOURCES_DIST) \ $(am__tools_test_client_SOURCES_DIST) \ $(tools_test_server_SOURCES) $(unit_test_gobex_SOURCES) \ + $(unit_test_gobex_apparam_SOURCES) \ $(unit_test_gobex_header_SOURCES) \ $(unit_test_gobex_packet_SOURCES) \ $(unit_test_gobex_transfer_SOURCES) @@ -462,7 +474,8 @@ gobex_sources = gobex/gobex.h gobex/gobex.c \ gobex/gobex-defs.h gobex/gobex-defs.c \ gobex/gobex-packet.c gobex/gobex-packet.h \ gobex/gobex-header.c gobex/gobex-header.h \ - gobex/gobex-transfer.c gobex/gobex-debug.h + gobex/gobex-transfer.c gobex/gobex-debug.h \ + gobex/gobex-apparam.h gobex/gobex-apparam.c @SERVER_TRUE@confdir = $(sysconfdir)/obex @SERVER_TRUE@builtin_modules = bluetooth $(am__append_2) filesystem \ @@ -473,8 +486,8 @@ gobex_sources = gobex/gobex.h gobex/gobex.c \ @SERVER_TRUE@ $(am__append_5) plugins/opp.c plugins/ftp.c \ @SERVER_TRUE@ plugins/ftp.h plugins/pbap.c plugins/phonebook.h \ @SERVER_TRUE@ plugins/vcard.h plugins/vcard.c plugins/mas.c \ -@SERVER_TRUE@ plugins/messages.h src/map_ap.c src/map_ap.h \ -@SERVER_TRUE@ plugins/irmc.c plugins/syncevolution.c +@SERVER_TRUE@ plugins/messages.h src/map_ap.h plugins/irmc.c \ +@SERVER_TRUE@ plugins/syncevolution.c @SERVER_TRUE@builtin_nodist = plugins/phonebook.c plugins/messages.c @SERVER_TRUE@src_obexd_SOURCES = $(gdbus_sources) $(builtin_sources) $(btio_sources) \ @SERVER_TRUE@ $(gobex_sources) src/main.c src/obexd.h \ @@ -510,7 +523,7 @@ gobex_sources = gobex/gobex.h gobex/gobex.c \ @CLIENT_TRUE@ client/transport.h client/transport.c \ @CLIENT_TRUE@ client/dbus.h client/dbus.c \ @CLIENT_TRUE@ client/driver.h client/driver.c \ -@CLIENT_TRUE@ src/map_ap.h src/map_ap.c +@CLIENT_TRUE@ src/map_ap.h @CLIENT_TRUE@client_obex_client_LDADD = @GLIB_LIBS@ @DBUS_LIBS@ @BLUEZ_LIBS@ service_DATA = $(service_in_files:.service.in=.service) @@ -551,6 +564,10 @@ unit_test_gobex_transfer_SOURCES = $(gobex_sources) unit/util.c unit/util.h \ unit/test-gobex-transfer.c unit_test_gobex_transfer_LDADD = @GLIB_LIBS@ +unit_test_gobex_apparam_SOURCES = $(gobex_sources) unit/util.c unit/util.h \ + unit/test-gobex-apparam.c + +unit_test_gobex_apparam_LDADD = @GLIB_LIBS@ @READLINE_TRUE@tools_test_client_SOURCES = $(gobex_sources) $(btio_sources) \ @READLINE_TRUE@ tools/test-client.c @@ -730,6 +747,8 @@ gobex/gobex-header.$(OBJEXT): gobex/$(am__dirstamp) \ gobex/$(DEPDIR)/$(am__dirstamp) gobex/gobex-transfer.$(OBJEXT): gobex/$(am__dirstamp) \ gobex/$(DEPDIR)/$(am__dirstamp) +gobex/gobex-apparam.$(OBJEXT): gobex/$(am__dirstamp) \ + gobex/$(DEPDIR)/$(am__dirstamp) btio/$(am__dirstamp): @$(MKDIR_P) btio @: > btio/$(am__dirstamp) @@ -777,8 +796,6 @@ client/dbus.$(OBJEXT): client/$(am__dirstamp) \ client/$(DEPDIR)/$(am__dirstamp) client/driver.$(OBJEXT): client/$(am__dirstamp) \ client/$(DEPDIR)/$(am__dirstamp) -src/map_ap.$(OBJEXT): src/$(am__dirstamp) \ - src/$(DEPDIR)/$(am__dirstamp) client/obex-client$(EXEEXT): $(client_obex_client_OBJECTS) $(client_obex_client_DEPENDENCIES) $(EXTRA_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) @@ -860,6 +877,11 @@ unit/util.$(OBJEXT): unit/$(am__dirstamp) \ unit/test-gobex$(EXEEXT): $(unit_test_gobex_OBJECTS) $(unit_test_gobex_DEPENDENCIES) $(EXTRA_unit_test_gobex_DEPENDENCIES) unit/$(am__dirstamp) @rm -f unit/test-gobex$(EXEEXT) $(AM_V_CCLD)$(LINK) $(unit_test_gobex_OBJECTS) $(unit_test_gobex_LDADD) $(LIBS) +unit/test-gobex-apparam.$(OBJEXT): unit/$(am__dirstamp) \ + unit/$(DEPDIR)/$(am__dirstamp) +unit/test-gobex-apparam$(EXEEXT): $(unit_test_gobex_apparam_OBJECTS) $(unit_test_gobex_apparam_DEPENDENCIES) $(EXTRA_unit_test_gobex_apparam_DEPENDENCIES) unit/$(am__dirstamp) + @rm -f unit/test-gobex-apparam$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(unit_test_gobex_apparam_OBJECTS) $(unit_test_gobex_apparam_LDADD) $(LIBS) unit/test-gobex-header.$(OBJEXT): unit/$(am__dirstamp) \ unit/$(DEPDIR)/$(am__dirstamp) unit/test-gobex-header$(EXEEXT): $(unit_test_gobex_header_OBJECTS) $(unit_test_gobex_header_DEPENDENCIES) $(EXTRA_unit_test_gobex_header_DEPENDENCIES) unit/$(am__dirstamp) @@ -896,6 +918,7 @@ mostlyclean-compile: -rm -f gdbus/object.$(OBJEXT) -rm -f gdbus/polkit.$(OBJEXT) -rm -f gdbus/watch.$(OBJEXT) + -rm -f gobex/gobex-apparam.$(OBJEXT) -rm -f gobex/gobex-defs.$(OBJEXT) -rm -f gobex/gobex-header.$(OBJEXT) -rm -f gobex/gobex-packet.$(OBJEXT) @@ -917,7 +940,6 @@ mostlyclean-compile: -rm -f src/log.$(OBJEXT) -rm -f src/main.$(OBJEXT) -rm -f src/manager.$(OBJEXT) - -rm -f src/map_ap.$(OBJEXT) -rm -f src/mimetype.$(OBJEXT) -rm -f src/obex.$(OBJEXT) -rm -f src/plugin.$(OBJEXT) @@ -926,6 +948,7 @@ mostlyclean-compile: -rm -f src/transport.$(OBJEXT) -rm -f tools/test-client.$(OBJEXT) -rm -f tools/test-server.$(OBJEXT) + -rm -f unit/test-gobex-apparam.$(OBJEXT) -rm -f unit/test-gobex-header.$(OBJEXT) -rm -f unit/test-gobex-packet.$(OBJEXT) -rm -f unit/test-gobex-transfer.$(OBJEXT) @@ -953,6 +976,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@gdbus/$(DEPDIR)/object.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@gdbus/$(DEPDIR)/polkit.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@gdbus/$(DEPDIR)/watch.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@gobex/$(DEPDIR)/gobex-apparam.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@gobex/$(DEPDIR)/gobex-defs.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@gobex/$(DEPDIR)/gobex-header.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@gobex/$(DEPDIR)/gobex-packet.Po@am__quote@ @@ -974,7 +998,6 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/log.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/main.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/manager.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/map_ap.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/mimetype.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/obex.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/plugin.Po@am__quote@ @@ -983,6 +1006,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/transport.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/test-client.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/test-server.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-gobex-apparam.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-gobex-header.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-gobex-packet.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-gobex-transfer.Po@am__quote@ diff --git a/btio/btio.c b/btio/btio.c index 9781ec4..44c2f9b 100644 --- a/btio/btio.c +++ b/btio/btio.c @@ -43,14 +43,23 @@ #endif #define ERROR_FAILED(gerr, str, err) \ - g_set_error(gerr, BT_IO_ERROR, BT_IO_ERROR_FAILED, \ + g_set_error(gerr, BT_IO_ERROR, err, \ str ": %s (%d)", strerror(err), err) #define DEFAULT_DEFER_TIMEOUT 30 +typedef enum { + BT_IO_L2CAP, + BT_IO_RFCOMM, + BT_IO_SCO, + BT_IO_INVALID, +} BtIOType; + struct set_opts { bdaddr_t src; bdaddr_t dst; + BtIOType type; + uint8_t dst_type; int defer; int sec_level; uint8_t channel; @@ -84,6 +93,48 @@ struct server { GDestroyNotify destroy; }; +static BtIOType bt_io_get_type(GIOChannel *io, GError **gerr) +{ + int sk = g_io_channel_unix_get_fd(io); + int domain, proto, err; + socklen_t len; + + domain = 0; + len = sizeof(domain); + err = getsockopt(sk, SOL_SOCKET, SO_DOMAIN, &domain, &len); + if (err < 0) { + ERROR_FAILED(gerr, "getsockopt(SO_DOMAIN)", errno); + return BT_IO_INVALID; + } + + if (domain != AF_BLUETOOTH) { + g_set_error(gerr, BT_IO_ERROR, EINVAL, + "BtIO socket domain not AF_BLUETOOTH"); + return BT_IO_INVALID; + } + + proto = 0; + len = sizeof(proto); + err = getsockopt(sk, SOL_SOCKET, SO_PROTOCOL, &proto, &len); + if (err < 0) { + ERROR_FAILED(gerr, "getsockopt(SO_PROTOCOL)", errno); + return BT_IO_INVALID; + } + + switch (proto) { + case BTPROTO_RFCOMM: + return BT_IO_RFCOMM; + case BTPROTO_SCO: + return BT_IO_SCO; + case BTPROTO_L2CAP: + return BT_IO_L2CAP; + default: + g_set_error(gerr, BT_IO_ERROR, EINVAL, + "Unknown BtIO socket type"); + return BT_IO_INVALID; + } +} + static void server_remove(struct server *server) { if (server->destroy) @@ -123,19 +174,28 @@ static gboolean accept_cb(GIOChannel *io, GIOCondition cond, gpointer user_data) { struct accept *accept = user_data; - GError *err = NULL; + GError *gerr = 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"); + if (cond & (G_IO_HUP | G_IO_ERR)) { + int err, sk_err, sock = g_io_channel_unix_get_fd(io); + socklen_t len = sizeof(sk_err); - accept->connect(io, err, accept->user_data); + if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &sk_err, &len) < 0) + err = -errno; + else + err = -sk_err; + + if (err < 0) + ERROR_FAILED(&gerr, "HUP or ERR on socket", -err); + } + + accept->connect(io, gerr, accept->user_data); - g_clear_error(&err); + g_clear_error(&gerr); return FALSE; } @@ -145,32 +205,26 @@ static gboolean connect_cb(GIOChannel *io, GIOCondition cond, { struct connect *conn = user_data; GError *gerr = NULL; + int err, sk_err, sock; + socklen_t len = sizeof(sk_err); /* If the user aborted this connect attempt */ if ((cond & G_IO_NVAL) || check_nval(io)) return FALSE; - if (cond & G_IO_OUT) { - int err, sk_err = 0, sock = g_io_channel_unix_get_fd(io); - socklen_t len = sizeof(sk_err); + sock = g_io_channel_unix_get_fd(io); - if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &sk_err, &len) < 0) - err = -errno; - else - err = -sk_err; + if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &sk_err, &len) < 0) + err = -errno; + else + err = -sk_err; - if (err < 0) - 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"); + if (err < 0) + ERROR_FAILED(&gerr, "connect error", -err); conn->connect(io, gerr, conn->user_data); - if (gerr) - g_error_free(gerr); + g_clear_error(&gerr); return FALSE; } @@ -280,8 +334,8 @@ static int l2cap_bind(int sock, const bdaddr_t *src, uint16_t psm, return 0; } -static int l2cap_connect(int sock, const bdaddr_t *dst, - uint16_t psm, uint16_t cid) +static int l2cap_connect(int sock, const bdaddr_t *dst, uint8_t dst_type, + uint16_t psm, uint16_t cid) { int err; struct sockaddr_l2 addr; @@ -294,6 +348,8 @@ static int l2cap_connect(int sock, const bdaddr_t *dst, else addr.l2_psm = htobs(psm); + addr.l2_bdaddr_type = dst_type; + err = connect(sock, (struct sockaddr *) &addr, sizeof(addr)); if (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS)) return -errno; @@ -387,7 +443,7 @@ static gboolean set_sec_level(int sock, BtIOType type, int level, GError **err) int ret; if (level < BT_SECURITY_LOW || level > BT_SECURITY_HIGH) { - g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS, + g_set_error(err, BT_IO_ERROR, EINVAL, "Valid security level range is %d-%d", BT_SECURITY_LOW, BT_SECURITY_HIGH); return FALSE; @@ -693,11 +749,13 @@ static gboolean parse_set_opts(struct set_opts *opts, GError **err, memset(opts, 0, sizeof(*opts)); /* Set defaults */ + opts->type = BT_IO_SCO; opts->defer = DEFAULT_DEFER_TIMEOUT; opts->master = -1; opts->mode = L2CAP_MODE_BASIC; opts->flushable = -1; opts->priority = 0; + opts->dst_type = BDADDR_BREDR; while (opt != BT_IO_OPT_INVALID) { switch (opt) { @@ -714,6 +772,9 @@ static gboolean parse_set_opts(struct set_opts *opts, GError **err, case BT_IO_OPT_DEST_BDADDR: bacpy(&opts->dst, va_arg(args, const bdaddr_t *)); break; + case BT_IO_OPT_DEST_TYPE: + opts->dst_type = va_arg(args, int); + break; case BT_IO_OPT_DEFER_TIMEOUT: opts->defer = va_arg(args, int); break; @@ -721,12 +782,15 @@ static gboolean parse_set_opts(struct set_opts *opts, GError **err, opts->sec_level = va_arg(args, int); break; case BT_IO_OPT_CHANNEL: + opts->type = BT_IO_RFCOMM; opts->channel = va_arg(args, int); break; case BT_IO_OPT_PSM: + opts->type = BT_IO_L2CAP; opts->psm = va_arg(args, int); break; case BT_IO_OPT_CID: + opts->type = BT_IO_L2CAP; opts->cid = va_arg(args, int); break; case BT_IO_OPT_MTU: @@ -757,7 +821,7 @@ static gboolean parse_set_opts(struct set_opts *opts, GError **err, opts->priority = va_arg(args, int); break; default: - g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS, + g_set_error(err, BT_IO_ERROR, EINVAL, "Unknown option %d", opt); return FALSE; } @@ -875,6 +939,9 @@ static gboolean l2cap_get(int sock, GError **err, BtIOOption opt1, case BT_IO_OPT_DEST_BDADDR: bacpy(va_arg(args, bdaddr_t *), &dst.l2_bdaddr); break; + case BT_IO_OPT_DEST_TYPE: + ERROR_FAILED(err, "Not implemented", EINVAL); + return FALSE; case BT_IO_OPT_DEFER_TIMEOUT: len = sizeof(int); if (getsockopt(sock, SOL_BLUETOOTH, BT_DEFER_SETUP, @@ -950,7 +1017,7 @@ static gboolean l2cap_get(int sock, GError **err, BtIOOption opt1, *(va_arg(args, uint32_t *)) = priority; break; default: - g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS, + g_set_error(err, BT_IO_ERROR, EINVAL, "Unknown option %d", opt); return FALSE; } @@ -1057,7 +1124,7 @@ static gboolean rfcomm_get(int sock, GError **err, BtIOOption opt1, memcpy(va_arg(args, uint8_t *), dev_class, 3); break; default: - g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS, + g_set_error(err, BT_IO_ERROR, EINVAL, "Unknown option %d", opt); return FALSE; } @@ -1140,7 +1207,7 @@ static gboolean sco_get(int sock, GError **err, BtIOOption opt1, va_list args) memcpy(va_arg(args, uint8_t *), dev_class, 3); break; default: - g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS, + g_set_error(err, BT_IO_ERROR, EINVAL, "Unknown option %d", opt); return FALSE; } @@ -1159,19 +1226,17 @@ static gboolean get_valist(GIOChannel *io, BtIOType type, GError **err, sock = g_io_channel_unix_get_fd(io); switch (type) { - case BT_IO_L2RAW: case BT_IO_L2CAP: - case BT_IO_L2ERTM: 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); + default: + g_set_error(err, BT_IO_ERROR, EINVAL, + "Unknown BtIO type %d", type); + return FALSE; } - - 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, @@ -1204,13 +1269,13 @@ gboolean bt_io_accept(GIOChannel *io, BtIOConnect connect, gpointer user_data, return TRUE; } -gboolean bt_io_set(GIOChannel *io, BtIOType type, GError **err, - BtIOOption opt1, ...) +gboolean bt_io_set(GIOChannel *io, GError **err, BtIOOption opt1, ...) { va_list args; gboolean ret; struct set_opts opts; int sock; + BtIOType type; va_start(args, opt1); ret = parse_set_opts(&opts, err, opt1, args); @@ -1219,12 +1284,14 @@ gboolean bt_io_set(GIOChannel *io, BtIOType type, GError **err, if (!ret) return ret; + type = bt_io_get_type(io, err); + if (type == BT_IO_INVALID) + return FALSE; + sock = g_io_channel_unix_get_fd(io); switch (type) { - case BT_IO_L2RAW: case BT_IO_L2CAP: - case BT_IO_L2ERTM: return l2cap_set(sock, opts.sec_level, opts.imtu, opts.omtu, opts.mode, opts.master, opts.flushable, opts.priority, err); @@ -1232,18 +1299,23 @@ gboolean bt_io_set(GIOChannel *io, BtIOType type, GError **err, return rfcomm_set(sock, opts.sec_level, opts.master, err); case BT_IO_SCO: return sco_set(sock, opts.mtu, err); + default: + g_set_error(err, BT_IO_ERROR, EINVAL, + "Unknown BtIO type %d", type); + return FALSE; } - 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, ...) +gboolean bt_io_get(GIOChannel *io, GError **err, BtIOOption opt1, ...) { va_list args; gboolean ret; + BtIOType type; + + type = bt_io_get_type(io, err); + if (type == BT_IO_INVALID) + return FALSE; va_start(args, opt1); ret = get_valist(io, type, err, opt1, args); @@ -1252,25 +1324,13 @@ gboolean bt_io_get(GIOChannel *io, BtIOType type, GError **err, return ret; } -static GIOChannel *create_io(BtIOType type, gboolean server, - struct set_opts *opts, GError **err) +static GIOChannel *create_io(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, - opts->cid, err) < 0) - goto failed; - if (!l2cap_set(sock, opts->sec_level, 0, 0, 0, -1, -1, 0, err)) - goto failed; - break; + switch (opts->type) { case BT_IO_L2CAP: sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP); if (sock < 0) { @@ -1285,20 +1345,6 @@ static GIOChannel *create_io(BtIOType type, gboolean server, opts->priority, err)) goto failed; break; - case BT_IO_L2ERTM: - sock = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_L2CAP); - if (sock < 0) { - ERROR_FAILED(err, "socket(STREAM, L2CAP)", errno); - return NULL; - } - if (l2cap_bind(sock, &opts->src, server ? opts->psm : 0, - opts->cid, err) < 0) - goto failed; - if (!l2cap_set(sock, opts->sec_level, opts->imtu, opts->omtu, - opts->mode, opts->master, opts->flushable, - opts->priority, err)) - goto failed; - break; case BT_IO_RFCOMM: sock = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM); if (sock < 0) { @@ -1323,8 +1369,8 @@ static GIOChannel *create_io(BtIOType type, gboolean server, goto failed; break; default: - g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS, - "Unknown BtIO type %d", type); + g_set_error(err, BT_IO_ERROR, EINVAL, + "Unknown BtIO type %d", opts->type); return NULL; } @@ -1341,9 +1387,9 @@ failed: return NULL; } -GIOChannel *bt_io_connect(BtIOType type, BtIOConnect connect, - gpointer user_data, GDestroyNotify destroy, - GError **gerr, BtIOOption opt1, ...) +GIOChannel *bt_io_connect(BtIOConnect connect, gpointer user_data, + GDestroyNotify destroy, GError **gerr, + BtIOOption opt1, ...) { GIOChannel *io; va_list args; @@ -1358,19 +1404,16 @@ GIOChannel *bt_io_connect(BtIOType type, BtIOConnect connect, if (ret == FALSE) return NULL; - io = create_io(type, FALSE, &opts, gerr); + io = create_io(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, opts.cid); - break; + switch (opts.type) { case BT_IO_L2CAP: - case BT_IO_L2ERTM: - err = l2cap_connect(sock, &opts.dst, opts.psm, opts.cid); + err = l2cap_connect(sock, &opts.dst, opts.dst_type, + opts.psm, opts.cid); break; case BT_IO_RFCOMM: err = rfcomm_connect(sock, &opts.dst, opts.channel); @@ -1379,14 +1422,13 @@ GIOChannel *bt_io_connect(BtIOType type, BtIOConnect connect, 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); + g_set_error(gerr, BT_IO_ERROR, EINVAL, + "Unknown BtIO type %d", opts.type); return NULL; } if (err < 0) { - g_set_error(gerr, BT_IO_ERROR, BT_IO_ERROR_CONNECT_FAILED, - "connect: %s (%d)", strerror(-err), -err); + ERROR_FAILED(gerr, "connect", -err); g_io_channel_unref(io); return NULL; } @@ -1396,10 +1438,9 @@ GIOChannel *bt_io_connect(BtIOType type, BtIOConnect connect, return io; } -GIOChannel *bt_io_listen(BtIOType type, BtIOConnect connect, - BtIOConfirm confirm, gpointer user_data, - GDestroyNotify destroy, GError **err, - BtIOOption opt1, ...) +GIOChannel *bt_io_listen(BtIOConnect connect, BtIOConfirm confirm, + gpointer user_data, GDestroyNotify destroy, + GError **err, BtIOOption opt1, ...) { GIOChannel *io; va_list args; @@ -1407,12 +1448,6 @@ GIOChannel *bt_io_listen(BtIOType type, BtIOConnect connect, 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); @@ -1420,7 +1455,7 @@ GIOChannel *bt_io_listen(BtIOType type, BtIOConnect connect, if (ret == FALSE) return NULL; - io = create_io(type, TRUE, &opts, err); + io = create_io(TRUE, &opts, err); if (io == NULL) return NULL; diff --git a/btio/btio.h b/btio/btio.h index 429e8c0..a6ff5a2 100644 --- a/btio/btio.h +++ b/btio/btio.h @@ -26,31 +26,17 @@ #include -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_L2ERTM, - 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_DEST_TYPE, BT_IO_OPT_DEFER_TIMEOUT, BT_IO_OPT_SEC_LEVEL, BT_IO_OPT_KEY_SIZE, @@ -92,19 +78,16 @@ 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_set(GIOChannel *io, GError **err, BtIOOption opt1, ...); -gboolean bt_io_get(GIOChannel *io, BtIOType type, GError **err, - BtIOOption opt1, ...); +gboolean bt_io_get(GIOChannel *io, GError **err, BtIOOption opt1, ...); -GIOChannel *bt_io_connect(BtIOType type, BtIOConnect connect, +GIOChannel *bt_io_connect(BtIOConnect connect, gpointer user_data, + GDestroyNotify destroy, GError **gerr, + BtIOOption opt1, ...); + +GIOChannel *bt_io_listen(BtIOConnect connect, BtIOConfirm confirm, 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 diff --git a/client/bluetooth.c b/client/bluetooth.c index 0d91ea1..d233e5c 100644 --- a/client/bluetooth.c +++ b/client/bluetooth.c @@ -160,6 +160,9 @@ static void session_destroy(struct bluetooth_session *session) g_io_channel_unref(session->io); } + if (session->sdp) + sdp_close(session->sdp); + if (session->conn) dbus_connection_unref(session->conn); @@ -191,7 +194,7 @@ static GIOChannel *transport_connect(const bdaddr_t *src, const bdaddr_t *dst, DBG("port %u", port); if (port > 31) { - io = bt_io_connect(BT_IO_L2CAP, function, user_data, + io = bt_io_connect(function, user_data, NULL, &err, BT_IO_OPT_SOURCE_BDADDR, src, BT_IO_OPT_DEST_BDADDR, dst, @@ -202,7 +205,7 @@ static GIOChannel *transport_connect(const bdaddr_t *src, const bdaddr_t *dst, BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW, BT_IO_OPT_INVALID); } else { - io = bt_io_connect(BT_IO_RFCOMM, function, user_data, + io = bt_io_connect(function, user_data, NULL, &err, BT_IO_OPT_SOURCE_BDADDR, src, BT_IO_OPT_DEST_BDADDR, dst, @@ -578,6 +581,7 @@ static guint bluetooth_connect(const char *source, const char *destination, session->id = ++id; session->func = func; + session->port = port; session->user_data = user_data; session->conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, NULL); @@ -631,7 +635,7 @@ static int bluetooth_getpacketopt(GIOChannel *io, int *tx_mtu, int *rx_mtu) if (type != SOCK_SEQPACKET) return -EINVAL; - if (!bt_io_get(io, BT_IO_L2CAP, NULL, BT_IO_OPT_OMTU, &omtu, + if (!bt_io_get(io, NULL, BT_IO_OPT_OMTU, &omtu, BT_IO_OPT_IMTU, &imtu, BT_IO_OPT_INVALID)) return -EINVAL; diff --git a/client/map.c b/client/map.c index e606cb2..dc72e4b 100644 --- a/client/map.c +++ b/client/map.c @@ -26,11 +26,15 @@ #include #include +#include #include #include +#include + #include "dbus.h" #include "log.h" +#include "map_ap.h" #include "map.h" #include "transfer.h" @@ -46,6 +50,38 @@ #define ERROR_INTERFACE "org.bluez.obex.Error" #define MAS_UUID "00001132-0000-1000-8000-00805f9b34fb" +#define DEFAULT_COUNT 1024 +#define DEFAULT_OFFSET 0 + +#define CHARSET_UTF8 1 + +static const char * const filter_list[] = { + "subject", + "timestamp", + "sender", + "sender-address", + "recipient", + "recipient-address", + "type", + "size", + "status", + "text", + "attachment", + "priority", + "read", + "sent", + "protected", + "replyto", + NULL +}; + +#define FILTER_BIT_MAX 15 +#define FILTER_ALL 0xFF + +#define STATUS_READ 0 +#define STATUS_DELETE 1 +#define FILLER_BYTE 0x30 + struct map_data { struct obc_session *session; DBusMessage *msg; @@ -72,6 +108,7 @@ struct map_msg { uint64_t size; char *status; uint8_t flags; + DBusMessage *msg; }; struct map_parser { @@ -209,17 +246,21 @@ done: dbus_message_unref(map->msg); } -static DBusMessage *map_get_folder_listing(DBusConnection *connection, - DBusMessage *message, void *user_data) +static DBusMessage *get_folder_listing(struct map_data *map, + DBusMessage *message, + GObexApparam *apparam) { - struct map_data *map = user_data; struct obc_transfer *transfer; GError *err = NULL; DBusMessage *reply; transfer = obc_transfer_get("x-obex/folder-listing", NULL, NULL, &err); - if (transfer == NULL) + if (transfer == NULL) { + g_obex_apparam_free(apparam); goto fail; + } + + obc_transfer_set_apparam(transfer, apparam); if (obc_session_queue(map->session, transfer, folder_listing_cb, map, &err)) { @@ -234,6 +275,88 @@ fail: return reply; } +static GObexApparam *parse_offset(GObexApparam *apparam, DBusMessageIter *iter) +{ + guint16 num; + + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT16) + return NULL; + + dbus_message_iter_get_basic(iter, &num); + + return g_obex_apparam_set_uint16(apparam, MAP_AP_STARTOFFSET, num); +} + +static GObexApparam *parse_max_count(GObexApparam *apparam, + DBusMessageIter *iter) +{ + guint16 num; + + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT16) + return NULL; + + dbus_message_iter_get_basic(iter, &num); + + return g_obex_apparam_set_uint16(apparam, MAP_AP_MAXLISTCOUNT, num); +} + +static GObexApparam *parse_folder_filters(GObexApparam *apparam, + DBusMessageIter *iter) +{ + DBusMessageIter array; + + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) + return NULL; + + dbus_message_iter_recurse(iter, &array); + + while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_DICT_ENTRY) { + const char *key; + DBusMessageIter value, entry; + + dbus_message_iter_recurse(&array, &entry); + dbus_message_iter_get_basic(&entry, &key); + + dbus_message_iter_next(&entry); + dbus_message_iter_recurse(&entry, &value); + + if (strcasecmp(key, "Offset") == 0) { + if (parse_offset(apparam, &value) == NULL) + return NULL; + } else if (strcasecmp(key, "MaxCount") == 0) { + if (parse_max_count(apparam, &value) == NULL) + return NULL; + } + + dbus_message_iter_next(&array); + } + + return apparam; +} + +static DBusMessage *map_list_folders(DBusConnection *connection, + DBusMessage *message, void *user_data) +{ + struct map_data *map = user_data; + GObexApparam *apparam; + DBusMessageIter args; + + dbus_message_iter_init(message, &args); + + apparam = g_obex_apparam_set_uint16(NULL, MAP_AP_MAXLISTCOUNT, + DEFAULT_COUNT); + apparam = g_obex_apparam_set_uint16(apparam, MAP_AP_STARTOFFSET, + DEFAULT_OFFSET); + + if (parse_folder_filters(apparam, &args) == NULL) { + g_obex_apparam_free(apparam); + return g_dbus_create_error(message, + ERROR_INTERFACE ".InvalidArguments", NULL); + } + + return get_folder_listing(map, message, apparam); +} + static void map_msg_free(void *data) { struct map_msg *msg = data; @@ -258,11 +381,14 @@ static DBusMessage *map_msg_get(DBusConnection *connection, struct map_msg *msg = user_data; struct obc_transfer *transfer; const char *target_file; + gboolean attachment; GError *err = NULL; DBusMessage *reply; + GObexApparam *apparam; if (dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &target_file, + DBUS_TYPE_BOOLEAN, &attachment, DBUS_TYPE_INVALID) == FALSE) return g_dbus_create_error(message, ERROR_INTERFACE ".InvalidArguments", NULL); @@ -272,6 +398,13 @@ static DBusMessage *map_msg_get(DBusConnection *connection, if (transfer == NULL) goto fail; + apparam = g_obex_apparam_set_uint8(NULL, MAP_AP_ATTACHMENT, + attachment); + apparam = g_obex_apparam_set_uint8(apparam, MAP_AP_CHARSET, + CHARSET_UTF8); + + obc_transfer_set_apparam(transfer, apparam); + if (!obc_session_queue(msg->data->session, transfer, NULL, NULL, &err)) goto fail; @@ -284,12 +417,197 @@ fail: return reply; } +static void set_message_status_cb(struct obc_session *session, + struct obc_transfer *transfer, + GError *err, void *user_data) +{ + struct map_msg *msg = user_data; + DBusMessage *reply; + + if (err != NULL) { + reply = g_dbus_create_error(msg->msg, + ERROR_INTERFACE ".Failed", + "%s", err->message); + goto done; + } + + reply = dbus_message_new_method_return(msg->msg); + if (reply == NULL) { + reply = g_dbus_create_error(msg->msg, + ERROR_INTERFACE ".Failed", + "%s", err->message); + } + +done: + g_dbus_send_message(conn, reply); + dbus_message_unref(msg->msg); + msg->msg = NULL; +} + +static DBusMessage *map_msg_set_property(DBusConnection *connection, + DBusMessage *message, + void *user_data) +{ + struct map_msg *msg = user_data; + struct obc_transfer *transfer; + char *property; + gboolean status; + GError *err = NULL; + DBusMessage *reply; + GObexApparam *apparam; + char contents[2]; + int op; + DBusMessageIter args, variant; + + dbus_message_iter_init(message, &args); + if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_STRING) + return g_dbus_create_error(message, + ERROR_INTERFACE ".InvalidArguments", NULL); + + dbus_message_iter_get_basic(&args, &property); + dbus_message_iter_next(&args); + if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_VARIANT) + return g_dbus_create_error(message, + ERROR_INTERFACE ".InvalidArguments", NULL); + + dbus_message_iter_recurse(&args, &variant); + if (dbus_message_iter_get_arg_type(&variant) != DBUS_TYPE_BOOLEAN) + return g_dbus_create_error(message, + ERROR_INTERFACE ".InvalidArguments", NULL); + + dbus_message_iter_get_basic(&variant, &status); + + /* MAP supports modifying only these two properties. */ + if (property && strcasecmp(property, "Read") == 0) { + op = STATUS_READ; + if (status) + msg->flags |= MAP_MSG_FLAG_READ; + else + msg->flags &= ~MAP_MSG_FLAG_READ; + } else if (property && strcasecmp(property, "Deleted") == 0) + op = STATUS_DELETE; + else { + return g_dbus_create_error(message, + ERROR_INTERFACE ".InvalidArguments", NULL); + } + + contents[0] = FILLER_BYTE; + contents[1] = '\0'; + + transfer = obc_transfer_put("x-bt/messageStatus", msg->handle, NULL, + contents, + sizeof(contents), &err); + if (transfer == NULL) + goto fail; + + apparam = g_obex_apparam_set_uint8(NULL, MAP_AP_STATUSINDICATOR, + op); + apparam = g_obex_apparam_set_uint8(apparam, MAP_AP_STATUSVALUE, + status); + obc_transfer_set_apparam(transfer, apparam); + + if (!obc_session_queue(msg->data->session, transfer, + set_message_status_cb, msg, &err)) + goto fail; + + msg->msg = dbus_message_ref(message); + return NULL; + +fail: + reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed", "%s", + err->message); + g_error_free(err); + return reply; +} + +static DBusMessage *map_msg_get_properties(DBusConnection *connection, + DBusMessage *message, + void *user_data) +{ + struct map_msg *msg = user_data; + GError *err = NULL; + DBusMessage *reply; + DBusMessageIter iter, data_array; + gboolean flag; + + reply = dbus_message_new_method_return(message); + if (reply == NULL) { + reply = g_dbus_create_error(message, + ERROR_INTERFACE ".Failed", + NULL); + goto done; + } + + dbus_message_iter_init_append(reply, &iter); + dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, + DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING + DBUS_TYPE_STRING_AS_STRING + DBUS_TYPE_VARIANT_AS_STRING + DBUS_DICT_ENTRY_END_CHAR_AS_STRING, + &data_array); + + + obex_dbus_dict_append(&data_array, "Subject", + DBUS_TYPE_STRING, &msg->subject); + obex_dbus_dict_append(&data_array, "Timestamp", + DBUS_TYPE_STRING, &msg->timestamp); + obex_dbus_dict_append(&data_array, "Sender", + DBUS_TYPE_STRING, &msg->sender); + obex_dbus_dict_append(&data_array, "SenderAddress", + DBUS_TYPE_STRING, &msg->sender_address); + obex_dbus_dict_append(&data_array, "ReplyTo", + DBUS_TYPE_STRING, &msg->replyto); + obex_dbus_dict_append(&data_array, "Recipient", + DBUS_TYPE_STRING, &msg->recipient); + obex_dbus_dict_append(&data_array, "RecipientAddress", + DBUS_TYPE_STRING, &msg->recipient_address); + obex_dbus_dict_append(&data_array, "Type", + DBUS_TYPE_STRING, &msg->type); + obex_dbus_dict_append(&data_array, "Status", + DBUS_TYPE_STRING, &msg->status); + obex_dbus_dict_append(&data_array, "Size", + DBUS_TYPE_UINT64, &msg->size); + + flag = (msg->flags & MAP_MSG_FLAG_PRIORITY) != 0; + obex_dbus_dict_append(&data_array, "Priority", + DBUS_TYPE_BOOLEAN, &flag); + + flag = (msg->flags & MAP_MSG_FLAG_READ) != 0; + obex_dbus_dict_append(&data_array, "Read", + DBUS_TYPE_BOOLEAN, &flag); + + flag = (msg->flags & MAP_MSG_FLAG_SENT) != 0; + obex_dbus_dict_append(&data_array, "Sent", + DBUS_TYPE_BOOLEAN, &flag); + + flag = (msg->flags & MAP_MSG_FLAG_PROTECTED) != 0; + obex_dbus_dict_append(&data_array, "Protected", + DBUS_TYPE_BOOLEAN, &flag); + + dbus_message_iter_close_container(&iter, &data_array); + + +done: + if (err) + g_error_free(err); + + return reply; +} + static const GDBusMethodTable map_msg_methods[] = { { GDBUS_METHOD("Get", - GDBUS_ARGS({ "targetfile", "s" }), + GDBUS_ARGS({ "targetfile", "s" }, + { "attachment", "b" }), GDBUS_ARGS({ "transfer", "o" }, { "properties", "a{sv}" }), map_msg_get) }, + { GDBUS_METHOD("GetProperties", + NULL, + GDBUS_ARGS({ "properties", "a{sv}" }), + map_msg_get_properties) }, + { GDBUS_ASYNC_METHOD("SetProperty", + GDBUS_ARGS({ "property", "sv" }), NULL, + map_msg_set_property) }, { } }; @@ -400,7 +718,7 @@ static void parse_size(struct map_msg *msg, const char *value, static void parse_priority(struct map_msg *msg, const char *value, DBusMessageIter *iter) { - gboolean flag = strcasecmp(value, "no"); + gboolean flag = strcasecmp(value, "no") != 0; if (flag) msg->flags |= MAP_MSG_FLAG_PRIORITY; @@ -413,7 +731,7 @@ static void parse_priority(struct map_msg *msg, const char *value, static void parse_read(struct map_msg *msg, const char *value, DBusMessageIter *iter) { - gboolean flag = strcasecmp(value, "no"); + gboolean flag = strcasecmp(value, "no") != 0; if (flag) msg->flags |= MAP_MSG_FLAG_READ; @@ -426,7 +744,7 @@ static void parse_read(struct map_msg *msg, const char *value, static void parse_sent(struct map_msg *msg, const char *value, DBusMessageIter *iter) { - gboolean flag = strcasecmp(value, "no"); + gboolean flag = strcasecmp(value, "no") != 0; if (flag) msg->flags |= MAP_MSG_FLAG_SENT; @@ -439,7 +757,7 @@ static void parse_sent(struct map_msg *msg, const char *value, static void parse_protected(struct map_msg *msg, const char *value, DBusMessageIter *iter) { - gboolean flag = strcasecmp(value, "no"); + gboolean flag = strcasecmp(value, "no") != 0; if (flag) msg->flags |= MAP_MSG_FLAG_PROTECTED; @@ -490,7 +808,7 @@ static void msg_element(GMarkupParseContext *ctxt, const gchar *element, break; } - msg = g_hash_table_lookup(data->messages, key); + msg = g_hash_table_lookup(data->messages, values[i]); if (msg == NULL) { msg = map_msg_create(data, values[i]); if (msg == NULL) @@ -594,27 +912,22 @@ done: dbus_message_unref(map->msg); } -static DBusMessage *map_get_message_listing(DBusConnection *connection, - DBusMessage *message, void *user_data) +static DBusMessage *get_message_listing(struct map_data *map, + DBusMessage *message, + const char *folder, + GObexApparam *apparam) { - struct map_data *map = user_data; struct obc_transfer *transfer; - const char *folder; - DBusMessageIter msg_iter; GError *err = NULL; DBusMessage *reply; - dbus_message_iter_init(message, &msg_iter); - - if (dbus_message_iter_get_arg_type(&msg_iter) != DBUS_TYPE_STRING) - return g_dbus_create_error(message, - ERROR_INTERFACE ".InvalidArguments", NULL); - - dbus_message_iter_get_basic(&msg_iter, &folder); - transfer = obc_transfer_get("x-bt/MAP-msg-listing", folder, NULL, &err); - if (transfer == NULL) + if (transfer == NULL) { + g_obex_apparam_free(apparam); goto fail; + } + + obc_transfer_set_apparam(transfer, apparam); if (obc_session_queue(map->session, transfer, message_listing_cb, map, &err)) { @@ -629,18 +942,380 @@ fail: return reply; } +static uint64_t get_filter_mask(const char *filterstr) +{ + int i; + + if (!filterstr) + return 0; + + if (!g_ascii_strcasecmp(filterstr, "ALL")) + return FILTER_ALL; + + for (i = 0; filter_list[i] != NULL; i++) + if (!g_ascii_strcasecmp(filterstr, filter_list[i])) + return 1ULL << i; + + return 0; +} + +static int set_field(guint32 *filter, const char *filterstr) +{ + guint64 mask; + + mask = get_filter_mask(filterstr); + + if (mask == 0) + return -EINVAL; + + *filter |= mask; + return 0; +} + +static GObexApparam *parse_fields(GObexApparam *apparam, DBusMessageIter *iter) +{ + DBusMessageIter array; + guint32 filter = 0; + + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) + return NULL; + + dbus_message_iter_recurse(iter, &array); + + while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_STRING) { + const char *string; + + dbus_message_iter_get_basic(&array, &string); + + if (set_field(&filter, string) < 0) + return NULL; + + dbus_message_iter_next(&array); + } + + return g_obex_apparam_set_uint32(apparam, MAP_AP_PARAMETERMASK, + filter); +} + +static GObexApparam *parse_filter_type(GObexApparam *apparam, + DBusMessageIter *iter) +{ + DBusMessageIter array; + guint8 types = 0; + + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) + return NULL; + + dbus_message_iter_recurse(iter, &array); + + while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_STRING) { + const char *string; + + dbus_message_iter_get_basic(&array, &string); + + if (!g_ascii_strcasecmp(string, "sms")) + types |= 0x03; /* SMS_GSM and SMS_CDMA */ + else if (!g_ascii_strcasecmp(string, "email")) + types |= 0x04; /* EMAIL */ + else if (!g_ascii_strcasecmp(string, "mms")) + types |= 0x08; /* MMS */ + else + return NULL; + } + + return g_obex_apparam_set_uint8(apparam, MAP_AP_FILTERMESSAGETYPE, + types); +} + +static GObexApparam *parse_period_begin(GObexApparam *apparam, + DBusMessageIter *iter) +{ + const char *string; + + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING) + return NULL; + + dbus_message_iter_get_basic(iter, &string); + + return g_obex_apparam_set_string(apparam, MAP_AP_FILTERPERIODBEGIN, + string); +} + +static GObexApparam *parse_period_end(GObexApparam *apparam, + DBusMessageIter *iter) +{ + const char *string; + + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING) + return NULL; + + dbus_message_iter_get_basic(iter, &string); + + return g_obex_apparam_set_string(apparam, MAP_AP_FILTERPERIODEND, + string); +} + +static GObexApparam *parse_filter_read(GObexApparam *apparam, + DBusMessageIter *iter) +{ + guint8 status = 0; + + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_BOOLEAN) + return NULL; + + dbus_message_iter_get_basic(iter, &status); + + status = (status) ? 0x01 : 0x02; + + return g_obex_apparam_set_uint8(apparam, MAP_AP_FILTERREADSTATUS, + status); +} + +static GObexApparam *parse_filter_recipient(GObexApparam *apparam, + DBusMessageIter *iter) +{ + const char *string; + + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING) + return NULL; + + dbus_message_iter_get_basic(iter, &string); + + return g_obex_apparam_set_string(apparam, MAP_AP_FILTERRECIPIENT, + string); +} + +static GObexApparam *parse_filter_sender(GObexApparam *apparam, + DBusMessageIter *iter) +{ + const char *string; + + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING) + return NULL; + + dbus_message_iter_get_basic(iter, &string); + + return g_obex_apparam_set_string(apparam, MAP_AP_FILTERORIGINATOR, + string); +} + +static GObexApparam *parse_filter_priority(GObexApparam *apparam, + DBusMessageIter *iter) +{ + guint8 priority; + + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_BOOLEAN) + return NULL; + + dbus_message_iter_get_basic(iter, &priority); + + priority = (priority) ? 0x01 : 0x02; + + return g_obex_apparam_set_uint8(apparam, MAP_AP_FILTERPRIORITY, + priority); +} + +static GObexApparam *parse_message_filters(GObexApparam *apparam, + DBusMessageIter *iter) +{ + DBusMessageIter array; + + DBG(""); + + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) + return NULL; + + dbus_message_iter_recurse(iter, &array); + + while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_DICT_ENTRY) { + const char *key; + DBusMessageIter value, entry; + + dbus_message_iter_recurse(&array, &entry); + dbus_message_iter_get_basic(&entry, &key); + + dbus_message_iter_next(&entry); + dbus_message_iter_recurse(&entry, &value); + + if (strcasecmp(key, "Offset") == 0) { + if (parse_offset(apparam, &value) == NULL) + return NULL; + } else if (strcasecmp(key, "MaxCount") == 0) { + if (parse_max_count(apparam, &value) == NULL) + return NULL; + } else if (strcasecmp(key, "Fields") == 0) { + if (parse_fields(apparam, &value) == NULL) + return NULL; + } else if (strcasecmp(key, "Types") == 0) { + if (parse_filter_type(apparam, &value) == NULL) + return NULL; + } else if (strcasecmp(key, "PeriodBegin") == 0) { + if (parse_period_begin(apparam, &value) == NULL) + return NULL; + } else if (strcasecmp(key, "PeriodEnd") == 0) { + if (parse_period_end(apparam, &value) == NULL) + return NULL; + } else if (strcasecmp(key, "Read") == 0) { + if (parse_filter_read(apparam, &value) == NULL) + return NULL; + } else if (strcasecmp(key, "Recipient") == 0) { + if (parse_filter_recipient(apparam, &value) == NULL) + return NULL; + } else if (strcasecmp(key, "Sender") == 0) { + if (parse_filter_sender(apparam, &value) == NULL) + return NULL; + } else if (strcasecmp(key, "Priority") == 0) { + if (parse_filter_priority(apparam, &value) == NULL) + return NULL; + } + + dbus_message_iter_next(&array); + } + + return apparam; +} + +static DBusMessage *map_list_messages(DBusConnection *connection, + DBusMessage *message, void *user_data) +{ + struct map_data *map = user_data; + const char *folder; + GObexApparam *apparam; + DBusMessageIter args; + + dbus_message_iter_init(message, &args); + + if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_STRING) + return g_dbus_create_error(message, + ERROR_INTERFACE ".InvalidArguments", NULL); + + dbus_message_iter_get_basic(&args, &folder); + + apparam = g_obex_apparam_set_uint16(NULL, MAP_AP_MAXLISTCOUNT, + DEFAULT_COUNT); + apparam = g_obex_apparam_set_uint16(apparam, MAP_AP_STARTOFFSET, + DEFAULT_OFFSET); + + dbus_message_iter_next(&args); + + if (parse_message_filters(apparam, &args) == NULL) { + g_obex_apparam_free(apparam); + return g_dbus_create_error(message, + ERROR_INTERFACE ".InvalidArguments", NULL); + } + + return get_message_listing(map, message, folder, apparam); +} + +static gchar **get_filter_strs(uint64_t filter, gint *size) +{ + gchar **list, **item; + gint i; + + list = g_malloc0(sizeof(gchar **) * (FILTER_BIT_MAX + 2)); + + item = list; + + for (i = 0; filter_list[i] != NULL; i++) + if (filter & (1ULL << i)) + *(item++) = g_strdup(filter_list[i]); + + *item = NULL; + *size = item - list; + return list; +} + +static DBusMessage *map_list_filter_fields(DBusConnection *connection, + DBusMessage *message, void *user_data) +{ + gchar **filters = NULL; + gint size; + DBusMessage *reply; + + filters = get_filter_strs(FILTER_ALL, &size); + reply = dbus_message_new_method_return(message); + dbus_message_append_args(reply, DBUS_TYPE_ARRAY, + DBUS_TYPE_STRING, &filters, size, + DBUS_TYPE_INVALID); + + g_strfreev(filters); + return reply; +} + +static void update_inbox_cb(struct obc_session *session, + struct obc_transfer *transfer, + GError *err, void *user_data) +{ + struct map_data *map = user_data; + DBusMessage *reply; + + if (err != NULL) { + reply = g_dbus_create_error(map->msg, + ERROR_INTERFACE ".Failed", + "%s", err->message); + goto done; + } + + reply = dbus_message_new_method_return(map->msg); + +done: + g_dbus_send_message(conn, reply); + dbus_message_unref(map->msg); +} + +static DBusMessage *map_update_inbox(DBusConnection *connection, + DBusMessage *message, void *user_data) +{ + struct map_data *map = user_data; + DBusMessage *reply; + char contents[2]; + struct obc_transfer *transfer; + GError *err = NULL; + + contents[0] = FILLER_BYTE; + contents[1] = '\0'; + + transfer = obc_transfer_put("x-bt/MAP-messageUpdate", NULL, NULL, + contents, sizeof(contents), + &err); + if (transfer == NULL) + goto fail; + + if (!obc_session_queue(map->session, transfer, update_inbox_cb, + map, &err)) + goto fail; + + map->msg = dbus_message_ref(message); + + return NULL; + +fail: + reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed", "%s", + err->message); + g_error_free(err); + return reply; +} + static const GDBusMethodTable map_methods[] = { { GDBUS_ASYNC_METHOD("SetFolder", GDBUS_ARGS({ "name", "s" }), NULL, map_setpath) }, - { GDBUS_ASYNC_METHOD("GetFolderListing", - GDBUS_ARGS({ "filter", "a{ss}" }), - GDBUS_ARGS({ "content", "aa{sv}" }), - map_get_folder_listing) }, - { GDBUS_ASYNC_METHOD("GetMessageListing", - GDBUS_ARGS({ "folder", "s" }, { "filter", "a{ss}" }), + { GDBUS_ASYNC_METHOD("ListFolders", + GDBUS_ARGS({ "filters", "a{sv}" }), + GDBUS_ARGS({ "content", "aa{sv}" }), + map_list_folders) }, + { GDBUS_ASYNC_METHOD("ListMessages", + GDBUS_ARGS({ "folder", "s" }, { "filter", "a{sv}" }), GDBUS_ARGS({ "messages", "a{oa{sv}}" }), - map_get_message_listing) }, + map_list_messages) }, + { GDBUS_METHOD("ListFilterFields", + NULL, + GDBUS_ARGS({ "fields", "as" }), + map_list_filter_fields) }, + { GDBUS_ASYNC_METHOD("UpdateInbox", + NULL, + NULL, + map_update_inbox) }, { } }; diff --git a/client/pbap.c b/client/pbap.c index 48dbac1..6444605 100644 --- a/client/pbap.c +++ b/client/pbap.c @@ -33,6 +33,7 @@ #include #include +#include #include "log.h" @@ -72,18 +73,6 @@ #define PHONEBOOKSIZE_TAG 0X08 #define NEWMISSEDCALLS_TAG 0X09 -/* The following length is in the unit of byte */ -#define ORDER_LEN 1 -#define SEARCHATTRIB_LEN 1 -#define MAXLISTCOUNT_LEN 2 -#define LISTSTARTOFFSET_LEN 2 -#define FILTER_LEN 8 -#define FORMAT_LEN 1 -#define PHONEBOOKSIZE_LEN 2 -#define NEWMISSEDCALLS_LEN 1 - -#define get_be16(val) GUINT16_FROM_BE(bt_get_unaligned((guint16 *) val)) - static const char *filter_list[] = { "VERSION", "FN", @@ -127,9 +116,6 @@ static const char *filter_list[] = { struct pbap_data { struct obc_session *session; char *path; - guint8 format; - guint8 order; - uint64_t filter; }; struct pending_request { @@ -137,38 +123,6 @@ struct pending_request { DBusMessage *msg; }; -struct pullphonebook_apparam { - uint8_t filter_tag; - uint8_t filter_len; - uint64_t filter; - uint8_t format_tag; - uint8_t format_len; - uint8_t format; - uint8_t maxlistcount_tag; - uint8_t maxlistcount_len; - uint16_t maxlistcount; - uint8_t liststartoffset_tag; - uint8_t liststartoffset_len; - uint16_t liststartoffset; -} __attribute__ ((packed)); - -struct pullvcardentry_apparam { - uint8_t filter_tag; - uint8_t filter_len; - uint64_t filter; - uint8_t format_tag; - uint8_t format_len; - uint8_t format; -} __attribute__ ((packed)); - -struct apparam_hdr { - uint8_t tag; - uint8_t len; - uint8_t val[0]; -} __attribute__ ((packed)); - -#define APPARAM_HDR_SIZE 2 - static DBusConnection *conn = NULL; static struct pending_request *pending_request_new(struct pbap_data *pbap, @@ -232,14 +186,14 @@ static gchar *build_phonebook_path(const char *location, const char *item) if (!g_ascii_strcasecmp(location, "INT") || !g_ascii_strcasecmp(location, "INTERNAL")) - path = g_strdup("telecom"); + path = g_strdup("/telecom"); else if (!g_ascii_strncasecmp(location, "SIM", 3)) { if (strlen(location) == 3) tmp = g_strdup("SIM1"); else tmp = g_ascii_strup(location, 4); - path = g_build_filename(tmp, "telecom", NULL); + path = g_build_filename("/", tmp, "telecom", NULL); g_free(tmp); } else return NULL; @@ -295,48 +249,19 @@ static void pbap_setpath_cb(struct obc_session *session, static void read_return_apparam(struct obc_transfer *transfer, guint16 *phone_book_size, guint8 *new_missed_calls) { - const struct apparam_hdr *hdr; - size_t size; + GObexApparam *apparam; *phone_book_size = 0; *new_missed_calls = 0; - hdr = obc_transfer_get_params(transfer, &size); - if (hdr == NULL) + apparam = obc_transfer_get_apparam(transfer); + if (apparam == NULL) return; - if (size < APPARAM_HDR_SIZE) - return; - - while (size > APPARAM_HDR_SIZE) { - if (hdr->len > size - APPARAM_HDR_SIZE) { - error("Unexpected PBAP pullphonebook app" - " length, tag %d, len %d", - hdr->tag, hdr->len); - return; - } - - switch (hdr->tag) { - case PHONEBOOKSIZE_TAG: - if (hdr->len == PHONEBOOKSIZE_LEN) { - guint16 val; - memcpy(&val, hdr->val, sizeof(val)); - *phone_book_size = get_be16(&val); - } - break; - case NEWMISSEDCALLS_TAG: - if (hdr->len == NEWMISSEDCALLS_LEN) - *new_missed_calls = hdr->val[0]; - break; - default: - error("Unexpected PBAP pullphonebook app" - " parameter, tag %d, len %d", - hdr->tag, hdr->len); - } - - size -= APPARAM_HDR_SIZE + hdr->len; - hdr += APPARAM_HDR_SIZE + hdr->len; - } + g_obex_apparam_get_uint16(apparam, PHONEBOOKSIZE_TAG, + phone_book_size); + g_obex_apparam_get_uint8(apparam, NEWMISSEDCALLS_TAG, + new_missed_calls); } static void phonebook_size_callback(struct obc_session *session, @@ -414,165 +339,75 @@ send: pending_request_free(request); } -static struct obc_transfer *pull_phonebook(struct pbap_data *pbap, - DBusMessage *message, - guint8 type, const char *name, - const char *targetfile, - uint64_t filter, guint8 format, - guint16 maxlistcount, - guint16 liststartoffset, - GError **err) +static GObexApparam *parse_format(GObexApparam *apparam, DBusMessageIter *iter) { - struct pending_request *request; - struct obc_transfer *transfer; - struct pullphonebook_apparam apparam; - session_callback_t func; + const char *string; + guint8 format; - transfer = obc_transfer_get("x-bt/phonebook", name, targetfile, err); - if (transfer == NULL) + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING) return NULL; - apparam.filter_tag = FILTER_TAG; - apparam.filter_len = FILTER_LEN; - apparam.filter = GUINT64_TO_BE(filter); - apparam.format_tag = FORMAT_TAG; - apparam.format_len = FORMAT_LEN; - apparam.format = format; - apparam.maxlistcount_tag = MAXLISTCOUNT_TAG; - apparam.maxlistcount_len = MAXLISTCOUNT_LEN; - apparam.maxlistcount = GUINT16_TO_BE(maxlistcount); - apparam.liststartoffset_tag = LISTSTARTOFFSET_TAG; - apparam.liststartoffset_len = LISTSTARTOFFSET_LEN; - apparam.liststartoffset = GUINT16_TO_BE(liststartoffset); + dbus_message_iter_get_basic(iter, &string); - switch (type) { - case PULLPHONEBOOK: - func = NULL; - request = NULL; - break; - case GETPHONEBOOKSIZE: - func = phonebook_size_callback; - request = pending_request_new(pbap, message); - break; - default: - error("Unexpected type : 0x%2x", type); + if (!string || g_str_equal(string, "")) + format = FORMAT_VCARD21; + else if (!g_ascii_strcasecmp(string, "vcard21")) + format = FORMAT_VCARD21; + else if (!g_ascii_strcasecmp(string, "vcard30")) + format = FORMAT_VCARD30; + else return NULL; - } - obc_transfer_set_params(transfer, &apparam, sizeof(apparam)); + return g_obex_apparam_set_uint8(apparam, FORMAT_TAG, format); +} - if (!obc_session_queue(pbap->session, transfer, func, request, err)) { - if (request != NULL) - pending_request_free(request); +static GObexApparam *parse_order(GObexApparam *apparam, DBusMessageIter *iter) +{ + const char *string; + guint8 order; + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING) return NULL; - } - - return transfer; -} + dbus_message_iter_get_basic(iter, &string); -static guint8 *fill_apparam(guint8 *dest, void *buf, guint8 tag, guint8 len) -{ - if (dest && buf) { - *dest++ = tag; - *dest++ = len; - memcpy(dest, buf, len); - dest += len; - } + if (!string || g_str_equal(string, "")) + order = ORDER_INDEXED; + else if (!g_ascii_strcasecmp(string, "indexed")) + order = ORDER_INDEXED; + else if (!g_ascii_strcasecmp(string, "alphanumeric")) + order = ORDER_ALPHANUMERIC; + else if (!g_ascii_strcasecmp(string, "phonetic")) + order = ORDER_PHONETIC; + else + return NULL; - return dest; + return g_obex_apparam_set_uint8(apparam, ORDER_TAG, order); } -static DBusMessage *pull_vcard_listing(struct pbap_data *pbap, - DBusMessage *message, const char *name, - guint8 order, char *searchval, guint8 attrib, - guint16 count, guint16 offset) +static GObexApparam *parse_offset(GObexApparam *apparam, DBusMessageIter *iter) { - struct pending_request *request; - struct obc_transfer *transfer; - guint8 *p, apparam[272]; - gint apparam_size; - GError *err = NULL; - DBusMessage *reply; - - transfer = obc_transfer_get("x-bt/vcard-listing", name, NULL, &err); - if (transfer == NULL) - goto fail; + guint16 num; - /* trunc the searchval string if it's length exceed the max value of guint8 */ - if (strlen(searchval) > 254) - searchval[255] = '\0'; - - apparam_size = APPARAM_HDR_SIZE + ORDER_LEN + - (APPARAM_HDR_SIZE + strlen(searchval) + 1) + - (APPARAM_HDR_SIZE + SEARCHATTRIB_LEN) + - (APPARAM_HDR_SIZE + MAXLISTCOUNT_LEN) + - (APPARAM_HDR_SIZE + LISTSTARTOFFSET_LEN); - - p = apparam; - - p = fill_apparam(p, &order, ORDER_TAG, ORDER_LEN); - p = fill_apparam(p, searchval, SEARCHVALUE_TAG, strlen(searchval) + 1); - p = fill_apparam(p, &attrib, SEARCHATTRIB_TAG, SEARCHATTRIB_LEN); - - count = GUINT16_TO_BE(count); - p = fill_apparam(p, &count, MAXLISTCOUNT_TAG, MAXLISTCOUNT_LEN); - - offset = GUINT16_TO_BE(offset); - fill_apparam(p, &offset, LISTSTARTOFFSET_TAG, LISTSTARTOFFSET_LEN); - - request = pending_request_new(pbap, message); - - obc_transfer_set_params(transfer, apparam, apparam_size); - - if (obc_session_queue(pbap->session, transfer, - pull_vcard_listing_callback, request, &err)) + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT16) return NULL; - pending_request_free(request); + dbus_message_iter_get_basic(iter, &num); -fail: - reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed", "%s", - err->message); - g_error_free(err); - return reply; + return g_obex_apparam_set_uint16(apparam, LISTSTARTOFFSET_TAG, num); } -static int set_format(struct pbap_data *pbap, const char *formatstr) +static GObexApparam *parse_max_count(GObexApparam *apparam, + DBusMessageIter *iter) { - if (!formatstr || g_str_equal(formatstr, "")) { - pbap->format = FORMAT_VCARD21; - return 0; - } + guint16 num; - if (!g_ascii_strcasecmp(formatstr, "vcard21")) - pbap->format = FORMAT_VCARD21; - else if (!g_ascii_strcasecmp(formatstr, "vcard30")) - pbap->format = FORMAT_VCARD30; - else - return -EINVAL; - - return 0; -} - -static int set_order(struct pbap_data *pbap, const char *orderstr) -{ - if (!orderstr || g_str_equal(orderstr, "")) { - pbap->order = ORDER_INDEXED; - return 0; - } + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT16) + return NULL; - if (!g_ascii_strcasecmp(orderstr, "indexed")) - pbap->order = ORDER_INDEXED; - else if (!g_ascii_strcasecmp(orderstr, "alphanumeric")) - pbap->order = ORDER_ALPHANUMERIC; - else if (!g_ascii_strcasecmp(orderstr, "phonetic")) - pbap->order = ORDER_PHONETIC; - else - return -EINVAL; + dbus_message_iter_get_basic(iter, &num); - return 0; + return g_obex_apparam_set_uint16(apparam, MAXLISTCOUNT_TAG, num); } static uint64_t get_filter_mask(const char *filterstr) @@ -600,53 +435,174 @@ static uint64_t get_filter_mask(const char *filterstr) return 0; } -static int add_filter(struct pbap_data *pbap, const char *filterstr) +static int set_field(guint64 *filter, const char *filterstr) { - uint64_t mask; + guint64 mask; mask = get_filter_mask(filterstr); if (mask == 0) return -EINVAL; - pbap->filter |= mask; + *filter |= mask; return 0; } -static int remove_filter(struct pbap_data *pbap, const char *filterstr) +static GObexApparam *parse_fields(GObexApparam *apparam, DBusMessageIter *iter) { - uint64_t mask; + DBusMessageIter array; + guint64 filter = 0; - mask = get_filter_mask(filterstr); + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) + return NULL; - if (mask == 0) - return -EINVAL; + dbus_message_iter_recurse(iter, &array); - pbap->filter &= ~mask; - return 0; + while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_STRING) { + const char *string; + + dbus_message_iter_get_basic(&array, &string); + + if (set_field(&filter, string) < 0) + return NULL; + + dbus_message_iter_next(&array); + } + + return g_obex_apparam_set_uint64(apparam, FILTER_TAG, filter); } -static gchar **get_filter_strs(uint64_t filter, gint *size) +static GObexApparam *parse_filters(GObexApparam *apparam, + DBusMessageIter *iter) { - gchar **list, **item; - gint i; - gint filter_list_size = sizeof(filter_list) / sizeof(filter_list[0]) - 1; + DBusMessageIter array; - list = g_malloc0(sizeof(gchar **) * (FILTER_BIT_MAX + 2)); + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) + return NULL; - item = list; + dbus_message_iter_recurse(iter, &array); + + while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_DICT_ENTRY) { + const char *key; + DBusMessageIter value, entry; + + dbus_message_iter_recurse(&array, &entry); + dbus_message_iter_get_basic(&entry, &key); + + dbus_message_iter_next(&entry); + dbus_message_iter_recurse(&entry, &value); + + if (strcasecmp(key, "Format") == 0) { + if (parse_format(apparam, &value) == NULL) + return NULL; + } else if (strcasecmp(key, "Order") == 0) { + if (parse_order(apparam, &value) == NULL) + return NULL; + } else if (strcasecmp(key, "Offset") == 0) { + if (parse_offset(apparam, &value) == NULL) + return NULL; + } else if (strcasecmp(key, "MaxCount") == 0) { + if (parse_max_count(apparam, &value) == NULL) + return NULL; + } else if (strcasecmp(key, "Fields") == 0) { + if (parse_fields(apparam, &value) == NULL) + return NULL; + } - for (i = 0; i < filter_list_size; i++) - if (filter & (1ULL << i)) - *(item++) = g_strdup(filter_list[i]); + dbus_message_iter_next(&array); + } - for (i = filter_list_size; i <= FILTER_BIT_MAX; i++) - if (filter & (1ULL << i)) - *(item++) = g_strdup_printf("%s%d", "BIT", i); + return apparam; +} - *item = NULL; - *size = item - list; - return list; +static DBusMessage *pull_phonebook(struct pbap_data *pbap, + DBusMessage *message, + guint8 type, + const char *targetfile, + GObexApparam *apparam) +{ + struct pending_request *request; + struct obc_transfer *transfer; + char *name; + session_callback_t func; + DBusMessage *reply; + GError *err = NULL; + + name = g_strconcat(g_path_skip_root(pbap->path), ".vcf", NULL); + + transfer = obc_transfer_get("x-bt/phonebook", name, targetfile, &err); + if (transfer == NULL) { + g_obex_apparam_free(apparam); + goto fail; + } + + switch (type) { + case PULLPHONEBOOK: + func = NULL; + request = NULL; + break; + case GETPHONEBOOKSIZE: + func = phonebook_size_callback; + request = pending_request_new(pbap, message); + break; + default: + error("Unexpected type : 0x%2x", type); + return NULL; + } + + obc_transfer_set_apparam(transfer, apparam); + + if (!obc_session_queue(pbap->session, transfer, func, request, &err)) { + if (request != NULL) + pending_request_free(request); + + goto fail; + } + + g_free(name); + + if (targetfile == NULL) + return NULL; + + return obc_transfer_create_dbus_reply(transfer, message); + +fail: + g_free(name); + reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed", "%s", + err->message); + g_error_free(err); + return reply; +} + +static DBusMessage *pull_vcard_listing(struct pbap_data *pbap, + DBusMessage *message, const char *name, + GObexApparam *apparam) +{ + struct pending_request *request; + struct obc_transfer *transfer; + GError *err = NULL; + DBusMessage *reply; + + transfer = obc_transfer_get("x-bt/vcard-listing", name, NULL, &err); + if (transfer == NULL) { + g_obex_apparam_free(apparam); + goto fail; + } + + obc_transfer_set_apparam(transfer, apparam); + + request = pending_request_new(pbap, message); + if (obc_session_queue(pbap->session, transfer, + pull_vcard_listing_callback, request, &err)) + return NULL; + + pending_request_free(request); + +fail: + reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed", "%s", + err->message); + g_error_free(err); + return reply; } static DBusMessage *pbap_select(DBusConnection *connection, @@ -700,74 +656,54 @@ static DBusMessage *pbap_pull_all(DBusConnection *connection, DBusMessage *message, void *user_data) { struct pbap_data *pbap = user_data; - struct obc_transfer *transfer; const char *targetfile; - char *name; - GError *err = NULL; + GObexApparam *apparam; + DBusMessageIter args; if (!pbap->path) return g_dbus_create_error(message, ERROR_INTERFACE ".Forbidden", "Call Select first of all"); - if (dbus_message_get_args(message, NULL, - DBUS_TYPE_STRING, &targetfile, - DBUS_TYPE_INVALID) == FALSE) + dbus_message_iter_init(message, &args); + + if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_STRING) return g_dbus_create_error(message, ERROR_INTERFACE ".InvalidArguments", NULL); - name = g_strconcat(pbap->path, ".vcf", NULL); + dbus_message_iter_get_basic(&args, &targetfile); + dbus_message_iter_next(&args); - transfer = pull_phonebook(pbap, message, PULLPHONEBOOK, name, - targetfile, pbap->filter, pbap->format, - DEFAULT_COUNT, DEFAULT_OFFSET, &err); - g_free(name); + apparam = g_obex_apparam_set_uint16(NULL, MAXLISTCOUNT_TAG, + DEFAULT_COUNT); + apparam = g_obex_apparam_set_uint16(apparam, LISTSTARTOFFSET_TAG, + DEFAULT_OFFSET); - if (transfer == NULL) { - DBusMessage *reply = g_dbus_create_error(message, - ERROR_INTERFACE ".Failed", "%s", - err->message); - g_error_free(err); - return reply; + if (parse_filters(apparam, &args) == NULL) { + g_obex_apparam_free(apparam); + return g_dbus_create_error(message, + ERROR_INTERFACE ".InvalidArguments", NULL); } - return obc_transfer_create_dbus_reply(transfer, message); + return pull_phonebook(pbap, message, PULLPHONEBOOK, targetfile, + apparam); } -static DBusMessage *pbap_pull_vcard(DBusConnection *connection, - DBusMessage *message, void *user_data) +static DBusMessage *pull_vcard(struct pbap_data *pbap, DBusMessage *message, + const char *name, const char *targetfile, + GObexApparam *apparam) { - struct pbap_data *pbap = user_data; struct obc_transfer *transfer; - struct pullvcardentry_apparam apparam; - const char *name, *targetfile; DBusMessage *reply; GError *err = NULL; - if (!pbap->path) - return g_dbus_create_error(message, - ERROR_INTERFACE ".Forbidden", - "Call Select first of all"); - - if (dbus_message_get_args(message, NULL, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_STRING, &targetfile, - DBUS_TYPE_INVALID) == FALSE) - return g_dbus_create_error(message, - ERROR_INTERFACE ".InvalidArguments", NULL); - transfer = obc_transfer_get("x-bt/vcard", name, targetfile, &err); - if (transfer == NULL) + if (transfer == NULL) { + g_obex_apparam_free(apparam); goto fail; + } - apparam.filter_tag = FILTER_TAG; - apparam.filter_len = FILTER_LEN; - apparam.filter = GUINT64_TO_BE(pbap->filter); - apparam.format_tag = FORMAT_TAG; - apparam.format_len = FORMAT_LEN; - apparam.format = pbap->format; - - obc_transfer_set_params(transfer, &apparam, sizeof(apparam)); + obc_transfer_set_apparam(transfer, apparam); if (!obc_session_queue(pbap->session, transfer, NULL, NULL, &err)) goto fail; @@ -781,39 +717,81 @@ fail: return reply; } -static DBusMessage *pbap_list(DBusConnection *connection, +static DBusMessage *pbap_pull_vcard(DBusConnection *connection, DBusMessage *message, void *user_data) { struct pbap_data *pbap = user_data; + GObexApparam *apparam; + const char *name, *targetfile; + DBusMessageIter args; if (!pbap->path) return g_dbus_create_error(message, - ERROR_INTERFACE ".Forbidden", - "Call Select first of all"); + ERROR_INTERFACE ".Forbidden", + "Call Select first of all"); + + dbus_message_iter_init(message, &args); + + if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_STRING) + return g_dbus_create_error(message, + ERROR_INTERFACE ".InvalidArguments", NULL); + + dbus_message_iter_get_basic(&args, &name); + dbus_message_iter_next(&args); + + if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_STRING) + return g_dbus_create_error(message, + ERROR_INTERFACE ".InvalidArguments", NULL); + + dbus_message_iter_get_basic(&args, &targetfile); + dbus_message_iter_next(&args); + + apparam = g_obex_apparam_set_uint16(NULL, MAXLISTCOUNT_TAG, + DEFAULT_COUNT); + apparam = g_obex_apparam_set_uint16(apparam, LISTSTARTOFFSET_TAG, + DEFAULT_OFFSET); - return pull_vcard_listing(pbap, message, "", pbap->order, "", - ATTRIB_NAME, DEFAULT_COUNT, DEFAULT_OFFSET); + if (parse_filters(apparam, &args) == NULL) { + g_obex_apparam_free(apparam); + return g_dbus_create_error(message, + ERROR_INTERFACE ".InvalidArguments", NULL); + } + + return pull_vcard(pbap, message, name, targetfile, apparam); } -static DBusMessage *pbap_search(DBusConnection *connection, +static DBusMessage *pbap_list(DBusConnection *connection, DBusMessage *message, void *user_data) { struct pbap_data *pbap = user_data; - char *field, *value; - guint8 attrib; - - if (dbus_message_get_args(message, NULL, - DBUS_TYPE_STRING, &field, - DBUS_TYPE_STRING, &value, - DBUS_TYPE_INVALID) == FALSE) - return g_dbus_create_error(message, - ERROR_INTERFACE ".InvalidArguments", NULL); + GObexApparam *apparam; + DBusMessageIter args; if (!pbap->path) return g_dbus_create_error(message, ERROR_INTERFACE ".Forbidden", "Call Select first of all"); + dbus_message_iter_init(message, &args); + + apparam = g_obex_apparam_set_uint16(NULL, MAXLISTCOUNT_TAG, + DEFAULT_COUNT); + apparam = g_obex_apparam_set_uint16(apparam, LISTSTARTOFFSET_TAG, + DEFAULT_OFFSET); + + if (parse_filters(apparam, &args) == NULL) { + g_obex_apparam_free(apparam); + return g_dbus_create_error(message, + ERROR_INTERFACE ".InvalidArguments", NULL); + } + + return pull_vcard_listing(pbap, message, "", apparam); +} + +static GObexApparam *parse_attribute(GObexApparam *apparam, const char *field) +{ + guint8 attrib; + if (!field || g_str_equal(field, "")) attrib = ATTRIB_NAME; else if (!g_ascii_strcasecmp(field, "name")) @@ -823,133 +801,101 @@ static DBusMessage *pbap_search(DBusConnection *connection, else if (!g_ascii_strcasecmp(field, "sound")) attrib = ATTRIB_SOUND; else - return g_dbus_create_error(message, - ERROR_INTERFACE ".InvalidArguments", NULL); + return NULL; - return pull_vcard_listing(pbap, message, "", pbap->order, value, - attrib, DEFAULT_COUNT, DEFAULT_OFFSET); + return g_obex_apparam_set_uint8(apparam, SEARCHATTRIB_TAG, attrib); } -static DBusMessage *pbap_get_size(DBusConnection *connection, +static DBusMessage *pbap_search(DBusConnection *connection, DBusMessage *message, void *user_data) { struct pbap_data *pbap = user_data; - DBusMessage *reply; - struct obc_transfer *transfer; - char *name; - GError *err = NULL; + char *field, *value; + GObexApparam *apparam; + DBusMessageIter args; if (!pbap->path) return g_dbus_create_error(message, ERROR_INTERFACE ".Forbidden", "Call Select first of all"); - name = g_strconcat(pbap->path, ".vcf", NULL); - - transfer = pull_phonebook(pbap, message, GETPHONEBOOKSIZE, name, NULL, - pbap->filter, pbap->format, 0, - DEFAULT_OFFSET, &err); - - g_free(name); - - if (transfer != NULL) - return NULL; + dbus_message_iter_init(message, &args); - reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed", "%s", - err->message); - g_error_free(err); - return reply; -} + if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_STRING) + return g_dbus_create_error(message, + ERROR_INTERFACE ".InvalidArguments", NULL); -static DBusMessage *pbap_set_format(DBusConnection *connection, - DBusMessage *message, void *user_data) -{ - struct pbap_data *pbap = user_data; - const char *format; + dbus_message_iter_get_basic(&args, &field); + dbus_message_iter_next(&args); - if (dbus_message_get_args(message, NULL, - DBUS_TYPE_STRING, &format, - DBUS_TYPE_INVALID) == FALSE) + apparam = parse_attribute(NULL, field); + if (apparam == NULL) return g_dbus_create_error(message, ERROR_INTERFACE ".InvalidArguments", NULL); - if (set_format(pbap, format) < 0) + if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_STRING) return g_dbus_create_error(message, - ERROR_INTERFACE ".InvalidArguments", - "InvalidFormat"); + ERROR_INTERFACE ".InvalidArguments", NULL); - return dbus_message_new_method_return(message); -} + dbus_message_iter_get_basic(&args, &value); + dbus_message_iter_next(&args); -static DBusMessage *pbap_set_order(DBusConnection *connection, - DBusMessage *message, void *user_data) -{ - struct pbap_data *pbap = user_data; - const char *order; + apparam = g_obex_apparam_set_uint16(apparam, MAXLISTCOUNT_TAG, + DEFAULT_COUNT); + apparam = g_obex_apparam_set_uint16(apparam, LISTSTARTOFFSET_TAG, + DEFAULT_OFFSET); + apparam = g_obex_apparam_set_string(apparam, SEARCHVALUE_TAG, value); - if (dbus_message_get_args(message, NULL, - DBUS_TYPE_STRING, &order, - DBUS_TYPE_INVALID) == FALSE) + if (parse_filters(apparam, &args) == NULL) { + g_obex_apparam_free(apparam); return g_dbus_create_error(message, ERROR_INTERFACE ".InvalidArguments", NULL); + } - if (set_order(pbap, order) < 0) - return g_dbus_create_error(message, - ERROR_INTERFACE ".InvalidArguments", - "InvalidFilter"); - - return dbus_message_new_method_return(message); + return pull_vcard_listing(pbap, message, "", apparam); } -static DBusMessage *pbap_set_filter(DBusConnection *connection, +static DBusMessage *pbap_get_size(DBusConnection *connection, DBusMessage *message, void *user_data) { struct pbap_data *pbap = user_data; - char **filters, **item; - gint size; - uint64_t oldfilter = pbap->filter; + GObexApparam *apparam; + DBusMessageIter args; - if (dbus_message_get_args(message, NULL, DBUS_TYPE_ARRAY, - DBUS_TYPE_STRING, &filters, &size, - DBUS_TYPE_INVALID) == FALSE) + if (!pbap->path) return g_dbus_create_error(message, - ERROR_INTERFACE ".InvalidArguments", NULL); + ERROR_INTERFACE ".Forbidden", + "Call Select first of all"); - remove_filter(pbap, "ALL"); - if (size == 0) - goto done; + dbus_message_iter_init(message, &args); - for (item = filters; *item; item++) { - if (add_filter(pbap, *item) < 0) { - pbap->filter = oldfilter; - g_strfreev(filters); - return g_dbus_create_error(message, - ERROR_INTERFACE ".InvalidArguments", - "InvalidFilters"); - } - } + apparam = g_obex_apparam_set_uint16(NULL, MAXLISTCOUNT_TAG, 0); + apparam = g_obex_apparam_set_uint16(apparam, LISTSTARTOFFSET_TAG, + DEFAULT_OFFSET); -done: - g_strfreev(filters); - return dbus_message_new_method_return(message); + return pull_phonebook(pbap, message, GETPHONEBOOKSIZE, NULL, apparam); } -static DBusMessage *pbap_get_filter(DBusConnection *connection, - DBusMessage *message, void *user_data) +static gchar **get_filter_strs(uint64_t filter, gint *size) { - struct pbap_data *pbap = user_data; - gchar **filters = NULL; - gint size; - DBusMessage *reply; + gchar **list, **item; + gint i; - filters = get_filter_strs(pbap->filter, &size); - reply = dbus_message_new_method_return(message); - dbus_message_append_args(reply, DBUS_TYPE_ARRAY, - DBUS_TYPE_STRING, &filters, size, - DBUS_TYPE_INVALID); + list = g_malloc0(sizeof(gchar **) * (FILTER_BIT_MAX + 2)); - g_strfreev(filters); - return reply; + item = list; + + for (i = 0; filter_list[i] != NULL; i++) + if (filter & (1ULL << i)) + *(item++) = g_strdup(filter_list[i]); + + for (; i <= FILTER_BIT_MAX; i++) + if (filter & (1ULL << i)) + *(item++) = g_strdup_printf("%s%d", "BIT", i); + + *item = NULL; + *size = item - list; + return list; } static DBusMessage *pbap_list_filter_fields(DBusConnection *connection, @@ -974,37 +920,29 @@ static const GDBusMethodTable pbap_methods[] = { GDBUS_ARGS({ "location", "s" }, { "phonebook", "s" }), NULL, pbap_select) }, { GDBUS_METHOD("PullAll", - GDBUS_ARGS({ "targetfile", "s" }), + GDBUS_ARGS({ "targetfile", "s" }, + { "filters", "a{sv}" }), GDBUS_ARGS({ "transfer", "o" }, { "properties", "a{sv}" }), pbap_pull_all) }, { GDBUS_METHOD("Pull", - GDBUS_ARGS({ "vcard", "s" }, { "targetfile", "s" }), + GDBUS_ARGS({ "vcard", "s" }, { "targetfile", "s" }, + { "filters", "a{sv}" }), GDBUS_ARGS({ "transfer", "o" }, { "properties", "a{sv}" }), pbap_pull_vcard) }, { GDBUS_ASYNC_METHOD("List", - NULL, GDBUS_ARGS({ "vcard_listing", "a(ss)" }), - pbap_list) }, + GDBUS_ARGS({ "filters", "a{sv}" }), + GDBUS_ARGS({ "vcard_listing", "a(ss)" }), + pbap_list) }, { GDBUS_ASYNC_METHOD("Search", - GDBUS_ARGS({ "field", "s" }, { "value", "s" }), - GDBUS_ARGS({ "vcard_listing", "a(ss)" }), - pbap_search) }, + GDBUS_ARGS({ "field", "s" }, { "value", "s" }, + { "filters", "a{sv}" }), + GDBUS_ARGS({ "vcard_listing", "a(ss)" }), + pbap_search) }, { GDBUS_ASYNC_METHOD("GetSize", NULL, GDBUS_ARGS({ "size", "q" }), pbap_get_size) }, - { GDBUS_METHOD("SetFormat", - GDBUS_ARGS({ "format", "s" }), NULL, - pbap_set_format) }, - { GDBUS_METHOD("SetOrder", - GDBUS_ARGS({ "order", "s" }), NULL, - pbap_set_order) }, - { GDBUS_METHOD("SetFilter", - GDBUS_ARGS({ "fields", "as" }), NULL, - pbap_set_filter) }, - { GDBUS_METHOD("GetFilter", - NULL, GDBUS_ARGS({ "fields", "as" }), - pbap_get_filter) }, { GDBUS_METHOD("ListFilterFields", NULL, GDBUS_ARGS({ "fields", "as" }), pbap_list_filter_fields) }, diff --git a/client/transfer.c b/client/transfer.c index 76f6681..cac3884 100644 --- a/client/transfer.c +++ b/client/transfer.c @@ -48,6 +48,8 @@ #define OBC_TRANSFER_ERROR obc_transfer_error_quark() +#define FIRST_PACKET_TIMEOUT 60 + static guint64 counter = 0; struct transfer_callback { @@ -55,15 +57,10 @@ struct transfer_callback { void *data; }; -struct obc_transfer_params { - void *data; - size_t size; -}; - struct obc_transfer { GObex *obex; + GObexApparam *apparam; guint8 op; - struct obc_transfer_params *params; struct transfer_callback *callback; DBusConnection *conn; DBusMessage *msg; @@ -271,10 +268,8 @@ static void obc_transfer_free(struct obc_transfer *transfer) if (transfer->fd > 0) close(transfer->fd); - if (transfer->params != NULL) { - g_free(transfer->params->data); - g_free(transfer->params); - } + if (transfer->apparam != NULL) + g_obex_apparam_free(transfer->apparam); if (transfer->conn) dbus_connection_unref(transfer->conn); @@ -403,7 +398,8 @@ struct obc_transfer *obc_transfer_put(const char *type, const char *name, struct stat st; int perr; - if (filename == NULL || strcmp(filename, "") == 0) { + if ((filename == NULL || strcmp(filename, "") == 0) && + contents == NULL) { g_set_error(err, OBC_TRANSFER_ERROR, -EINVAL, "Invalid filename given"); return NULL; @@ -430,6 +426,7 @@ struct obc_transfer *obc_transfer_put(const char *type, const char *name, "Writing all contents to file failed"); goto fail; } + lseek(transfer->fd, 0, SEEK_SET); } else { if (!transfer_open(transfer, O_RDONLY, 0, err)) goto fail; @@ -527,6 +524,7 @@ static void get_xfer_progress_first(GObex *obex, GError *err, GObexPacket *rsp, struct obc_transfer *transfer = user_data; GObexPacket *req; GObexHeader *hdr; + GObexApparam *apparam; const guint8 *buf; gsize len; guint8 rspcode; @@ -548,17 +546,9 @@ static void get_xfer_progress_first(GObex *obex, GError *err, GObexPacket *rsp, hdr = g_obex_packet_get_header(rsp, G_OBEX_HDR_APPARAM); if (hdr) { - g_obex_header_get_bytes(hdr, &buf, &len); - if (len != 0) { - if (transfer->params == NULL) - transfer->params = - g_new0(struct obc_transfer_params, 1); - else - g_free(transfer->params->data); - - transfer->params->data = g_memdup(buf, len); - transfer->params->size = len; - } + apparam = g_obex_header_get_apparam(hdr); + if (apparam != NULL) + obc_transfer_set_apparam(transfer, apparam); } hdr = g_obex_packet_get_body(rsp); @@ -640,6 +630,7 @@ static gboolean report_progress(gpointer data) static gboolean transfer_start_get(struct obc_transfer *transfer, GError **err) { GObexPacket *req; + GObexHeader *hdr; if (transfer->xfer > 0) { g_set_error(err, OBC_TRANSFER_ERROR, -EALREADY, @@ -657,12 +648,13 @@ static gboolean transfer_start_get(struct obc_transfer *transfer, GError **err) g_obex_packet_add_bytes(req, G_OBEX_HDR_TYPE, transfer->type, strlen(transfer->type) + 1); - if (transfer->params != NULL) - g_obex_packet_add_bytes(req, G_OBEX_HDR_APPARAM, - transfer->params->data, - transfer->params->size); + if (transfer->apparam != NULL) { + hdr = g_obex_header_new_apparam(transfer->apparam); + g_obex_packet_add_header(req, hdr); + } - transfer->xfer = g_obex_send_req(transfer->obex, req, -1, + transfer->xfer = g_obex_send_req(transfer->obex, req, + FIRST_PACKET_TIMEOUT, get_xfer_progress_first, transfer, err); if (transfer->xfer == 0) @@ -680,6 +672,7 @@ static gboolean transfer_start_get(struct obc_transfer *transfer, GError **err) static gboolean transfer_start_put(struct obc_transfer *transfer, GError **err) { GObexPacket *req; + GObexHeader *hdr; if (transfer->xfer > 0) { g_set_error(err, OBC_TRANSFER_ERROR, -EALREADY, @@ -700,10 +693,10 @@ static gboolean transfer_start_put(struct obc_transfer *transfer, GError **err) if (transfer->size < UINT32_MAX) g_obex_packet_add_uint32(req, G_OBEX_HDR_LENGTH, transfer->size); - if (transfer->params != NULL) - g_obex_packet_add_bytes(req, G_OBEX_HDR_APPARAM, - transfer->params->data, - transfer->params->size); + if (transfer->apparam != NULL) { + hdr = g_obex_header_new_apparam(transfer->apparam); + g_obex_packet_add_header(req, hdr); + } transfer->xfer = g_obex_put_req_pkt(transfer->obex, req, put_xfer_progress, xfer_complete, @@ -741,31 +734,20 @@ guint8 obc_transfer_get_operation(struct obc_transfer *transfer) return transfer->op; } -void obc_transfer_set_params(struct obc_transfer *transfer, - const void *data, size_t size) +void obc_transfer_set_apparam(struct obc_transfer *transfer, void *data) { - if (transfer->params != NULL) { - g_free(transfer->params->data); - g_free(transfer->params); - } + if (transfer->apparam != NULL) + g_obex_apparam_free(transfer->apparam); if (data == NULL) return; - transfer->params = g_new0(struct obc_transfer_params, 1); - transfer->params->data = g_memdup(data, size); - transfer->params->size = size; + transfer->apparam = data; } -const void *obc_transfer_get_params(struct obc_transfer *transfer, size_t *size) +void *obc_transfer_get_apparam(struct obc_transfer *transfer) { - if (transfer->params == NULL) - return NULL; - - if (size != NULL) - *size = transfer->params->size; - - return transfer->params->data; + return transfer->apparam; } int obc_transfer_get_contents(struct obc_transfer *transfer, char **contents, diff --git a/client/transfer.h b/client/transfer.h index 968903a..f7d0423 100644 --- a/client/transfer.h +++ b/client/transfer.h @@ -50,10 +50,8 @@ gboolean obc_transfer_start(struct obc_transfer *transfer, void *obex, GError **err); guint8 obc_transfer_get_operation(struct obc_transfer *transfer); -void obc_transfer_set_params(struct obc_transfer *transfer, - const void *data, size_t size); -const void *obc_transfer_get_params(struct obc_transfer *transfer, - size_t *size); +void obc_transfer_set_apparam(struct obc_transfer *transfer, void *data); +void *obc_transfer_get_apparam(struct obc_transfer *transfer); int obc_transfer_get_contents(struct obc_transfer *transfer, char **contents, size_t *size); diff --git a/configure b/configure index 768e8e7..41da84e 100755 --- a/configure +++ b/configure @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for obexd 0.47. +# Generated by GNU Autoconf 2.69 for obexd 0.48. # # # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. @@ -587,8 +587,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='obexd' PACKAGE_TARNAME='obexd' -PACKAGE_VERSION='0.47' -PACKAGE_STRING='obexd 0.47' +PACKAGE_VERSION='0.48' +PACKAGE_STRING='obexd 0.48' PACKAGE_BUGREPORT='' PACKAGE_URL='' @@ -1372,7 +1372,7 @@ if test "$ac_init_help" = "long"; then # 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.47 to adapt to many kinds of systems. +\`configure' configures obexd 0.48 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1442,7 +1442,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of obexd 0.47:";; + short | recursive ) echo "Configuration of obexd 0.48:";; esac cat <<\_ACEOF @@ -1586,7 +1586,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -obexd configure 0.47 +obexd configure 0.48 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -1951,7 +1951,7 @@ cat >config.log <<_ACEOF 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.47, which was +It was created by obexd $as_me 0.48, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -2767,7 +2767,7 @@ fi # Define the identity of the package. PACKAGE='obexd' - VERSION='0.47' + VERSION='0.48' cat >>confdefs.h <<_ACEOF @@ -12137,12 +12137,12 @@ if test -n "$BLUEZ_CFLAGS"; then pkg_cv_BLUEZ_CFLAGS="$BLUEZ_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ - { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"bluez >= 4.99\""; } >&5 - ($PKG_CONFIG --exists --print-errors "bluez >= 4.99") 2>&5 + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"bluez >= 4.100\""; } >&5 + ($PKG_CONFIG --exists --print-errors "bluez >= 4.100") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then - pkg_cv_BLUEZ_CFLAGS=`$PKG_CONFIG --cflags "bluez >= 4.99" 2>/dev/null` + pkg_cv_BLUEZ_CFLAGS=`$PKG_CONFIG --cflags "bluez >= 4.100" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes @@ -12154,12 +12154,12 @@ if test -n "$BLUEZ_LIBS"; then pkg_cv_BLUEZ_LIBS="$BLUEZ_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ - { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"bluez >= 4.99\""; } >&5 - ($PKG_CONFIG --exists --print-errors "bluez >= 4.99") 2>&5 + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"bluez >= 4.100\""; } >&5 + ($PKG_CONFIG --exists --print-errors "bluez >= 4.100") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then - pkg_cv_BLUEZ_LIBS=`$PKG_CONFIG --libs "bluez >= 4.99" 2>/dev/null` + pkg_cv_BLUEZ_LIBS=`$PKG_CONFIG --libs "bluez >= 4.100" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes @@ -12180,18 +12180,18 @@ else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then - BLUEZ_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "bluez >= 4.99" 2>&1` + BLUEZ_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "bluez >= 4.100" 2>&1` else - BLUEZ_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "bluez >= 4.99" 2>&1` + BLUEZ_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "bluez >= 4.100" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$BLUEZ_PKG_ERRORS" >&5 - as_fn_error $? "BlueZ >= 4.99 is required" "$LINENO" 5 + as_fn_error $? "BlueZ >= 4.100 is required" "$LINENO" 5 elif test $pkg_failed = untried; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } - as_fn_error $? "BlueZ >= 4.99 is required" "$LINENO" 5 + as_fn_error $? "BlueZ >= 4.100 is required" "$LINENO" 5 else BLUEZ_CFLAGS=$pkg_cv_BLUEZ_CFLAGS BLUEZ_LIBS=$pkg_cv_BLUEZ_LIBS @@ -13334,7 +13334,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # 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.47, which was +This file was extended by obexd $as_me 0.48, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -13400,7 +13400,7 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -obexd config.status 0.47 +obexd config.status 0.48 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" diff --git a/configure.ac b/configure.ac index 69d71b2..69d636f 100644 --- a/configure.ac +++ b/configure.ac @@ -1,5 +1,5 @@ AC_PREREQ(2.60) -AC_INIT(obexd, 0.47) +AC_INIT(obexd, 0.48) AM_INIT_AUTOMAKE([foreign subdir-objects color-tests]) AM_CONFIG_HEADER(config.h) @@ -79,8 +79,8 @@ PKG_CHECK_MODULES(DBUS, dbus-1 >= 1.4, dummy=yes, AC_SUBST(DBUS_CFLAGS) AC_SUBST(DBUS_LIBS) -PKG_CHECK_MODULES(BLUEZ, bluez >= 4.99, dummy=yes, - AC_MSG_ERROR(BlueZ >= 4.99 is required)) +PKG_CHECK_MODULES(BLUEZ, bluez >= 4.100, dummy=yes, + AC_MSG_ERROR(BlueZ >= 4.100 is required)) AC_SUBST(BLUEZ_CFLAGS) AC_SUBST(BLUEZ_LIBS) diff --git a/doc/client-api.txt b/doc/client-api.txt index 839a78c..19c8541 100644 --- a/doc/client-api.txt +++ b/doc/client-api.txt @@ -206,7 +206,7 @@ Methods void Select(string location, string phonebook) "mch": missing call history "cch": combination of ich och mch - object, dict PullAll(string targetfile) + object, dict PullAll(string targetfile, dict filters) Return the entire phonebook object from the PSE server in plain string with vcard format, and store it in @@ -222,14 +222,21 @@ Methods void Select(string location, string phonebook) The properties of this transfer are also returned along with the object path, to avoid a call to GetProperties. - array{string vcard, string name} List() + Possible filters: Format, Order, Offset, MaxCount and + Fields + + array{string vcard, string name} List(dict filters) Return an array of vcard-listing data where every entry consists of a pair of strings containing the vcard handle and the contact name. For example: "1.vcf" : "John" - object, dict Pull(string vcard, string targetfile) + Possible filters: Order, Offset and MaxCount + + + object, dict + Pull(string vcard, string targetfile, dict filters) Given a vcard handle, retrieve the vcard in the current phonebook object and store it in a local file. @@ -244,8 +251,11 @@ Methods void Select(string location, string phonebook) The properties of this transfer are also returned along with the object path, to avoid a call to GetProperties. + Possbile filters: Format and Fields + + array{string vcard, string name} - Search(string field, string value) + Search(string field, string value, dict filters) Search for entries matching the given condition and return an array of vcard-listing data where every entry @@ -258,47 +268,47 @@ Methods void Select(string location, string phonebook) { "name" (default) | "number" | "sound" } value : the string value to search for + + Possible filters: Order, Offset and MaxCount + uint16 GetSize() Return the number of entries in the selected phonebook object that are actually used (i.e. indexes that correspond to non-NULL entries). - void SetFormat(string format) + array{string} ListFilterFields() - Indicate the format of the vcard that should be return - by related methods. + Return All Available fields that can be used in Fields + filter. - format : { "vcard21" (default) | "vcard30" } +Filter: string Format: - void SetOrder(string order) + Items vcard format - Indicate the sorting method of the vcard-listing data - returned by List and Search methods. + Possible values: "vcard21" (default) or "vcard30" - order : { "indexed" (default) | "alphanumeric" | - "phonetic" } + string Order: - void SetFilter(array{string}) + Items order - Indicate fields that should be contained in vcards - return by related methods. + Possible values: "indexed" (default), "alphanumeric" or + "phonetic" - Give an empty array will clear the filter and return - all fields available in vcards. And this is the default - behavior. + uint16 Offset: - Possible filter fields : "VERSION", "FN", ..., "ALL", - "bit[0-63]" + Offset of the first item, default is 0 - array{string} ListFilterFields() + uint16 MaxCount: + + Maximum number of items, default is unlimited (65535) + + array{string} Fields: - Return All Available fields that can be used in - SetFilter method. + Item vcard fields, default is all values. - array{string} GetFilter() + Possible values can be query with ListFilterFields. - Return the current filter setting Synchronization hierarchy ======================= @@ -358,7 +368,7 @@ Methods void SetFolder(string name) Set working directory for current session, *name* may be the directory name or '..[/dir]'. - array{dict} GetFolderListing(dict filter) + array{dict} ListFolders(dict filter) Returns a dictionary containing information about the current folder content. @@ -367,12 +377,22 @@ Methods void SetFolder(string name) string Name : Folder name - array{object, dict} GetMessageListing(string folder, - dict filter) + Possible filters: Offset and MaxCount + + array{string} ListFilterFields() + + Return all available fields that can be used in Fields + filter. + + array{object, dict} ListMessages(string folder, dict filter) Returns an array containing the messages found in the given folder. + Possible Filters: Offset, MaxCount, Fields, Type, + PeriodStart, PeriodEnd, Status, Recipient, Sender, + Priority + Each message is represented by an object path followed by a dictionary of the properties. @@ -440,6 +460,64 @@ Methods void SetFolder(string name) Message protected flag + void UpdateInbox(void) + + Request remote to update its inbox. + + +Filter: uint16 Offset: + + Offset of the first item, default is 0 + + uint16 MaxCount: + + Maximum number of items, default is 1024 + + array{string} Fields: + + Message fields, default is all values. + + Possible values can be query with ListFilterFields. + + array{string} Types: + + Filter messages by type. + + Possible values: "sms", "email", "mms". + + string PeriodBegin: + + Filter messages by starting period. + + Possible values: Date in "YYYYMMDDTHHMMSS" format. + + string PeriodEnd: + + Filter messages by ending period. + + Possible values: Date in "YYYYMMDDTHHMMSS" format. + + boolean Read: + + Filter messages by read flag. + + Possible values: True for read or False for unread + + string Recipient: + + Filter messages by recipient address. + + string Sender: + + Filter messages by sender address. + + gboolean Priority: + + Filter messages by priority flag. + + Possible values: True for high priority or False for + non-high priority + Message hierarchy ================= @@ -447,7 +525,7 @@ Service org.bluez.obex.client Interface org.bluez.obex.Message Object path [variable prefix]/{session0,session1,...}/{message0,...} -Methods object, dict Get(string targetfile) +Methods object, dict Get(string targetfile, boolean attachment) Download message and store it in the target file. @@ -461,12 +539,91 @@ Methods object, dict Get(string targetfile) The properties of this transfer are also returned along with the object path, to avoid a call to GetProperties. + dict GetProperties() + + Returns all properties for the message. See the + properties section for available properties. + + void SetProperty (string name, variant value) + + Sets value to the mentioned property. + + Possible properties: Read and Deleted. + + +Properties string Subject [readonly] + + Message subject + + string Timestamp [readonly] + + Message timestamp + + string Sender [readonly] + + Message sender name + + string SenderAddress [readonly] + + Message sender address + + string ReplyTo [readonly] + + Message Reply-To address + + string Recipient [readonly] + + Message recipient name + + string RecipientAddress [readonly] + + Message recipient address + + string Type [readonly] + + Message type + + Possible values: "EMAIL", "SMS_GSM", + "SMS_CDMA" and "MMS" + + uint64 Size [readonly] + + Message size in bytes + + string Status [readonly] + + Message reception status + + Possible values: "complete", + "fractioned" and "notification" + + boolean Priority [readonly] + + Message priority flag + + boolean Read [read/write] + + Message read flag + + boolean Deleted [writeonly] + + Message read flag + + boolean Sent [readonly] + + Message sent flag + + boolean Protected [readonly] + + Message protected flag + + Transfer hierarchy ================== Service org.bluez.obex.client Interface org.bluez.obex.Transfer -Object path [variable prefix]/{transfer0,transfer1,...} +Object path [variable prefix]/{session0,session1,...}/{transfer0,...} Methods dict GetProperties() diff --git a/gdbus/gdbus.h b/gdbus/gdbus.h index 0a8a27c..dd21f86 100644 --- a/gdbus/gdbus.h +++ b/gdbus/gdbus.h @@ -31,6 +31,17 @@ extern "C" { #include #include +typedef enum GDBusMethodFlags GDBusMethodFlags; +typedef enum GDBusSignalFlags GDBusSignalFlags; +typedef enum GDBusPropertyFlags GDBusPropertyFlags; +typedef enum GDBusSecurityFlags GDBusSecurityFlags; + +typedef struct GDBusArgInfo GDBusArgInfo; +typedef struct GDBusMethodTable GDBusMethodTable; +typedef struct GDBusSignalTable GDBusSignalTable; +typedef struct GDBusPropertyTable GDBusPropertyTable; +typedef struct GDBusSecurityTable GDBusSecurityTable; + typedef void (* GDBusWatchFunction) (DBusConnection *connection, void *user_data); @@ -55,6 +66,18 @@ typedef void (* GDBusDestroyFunction) (void *user_data); typedef DBusMessage * (* GDBusMethodFunction) (DBusConnection *connection, DBusMessage *message, void *user_data); +typedef gboolean (*GDBusPropertyGetter)(const GDBusPropertyTable *property, + DBusMessageIter *iter, void *data); + +typedef guint32 GDBusPendingPropertySet; + +typedef void (*GDBusPropertySetter)(const GDBusPropertyTable *property, + DBusMessageIter *value, GDBusPendingPropertySet id, + void *data); + +typedef gboolean (*GDBusPropertyExists)(const GDBusPropertyTable *property, + void *data); + typedef guint32 GDBusPendingReply; typedef void (* GDBusSecurityFunction) (DBusConnection *connection, @@ -62,58 +85,61 @@ typedef void (* GDBusSecurityFunction) (DBusConnection *connection, gboolean interaction, GDBusPendingReply pending); -typedef enum { +enum GDBusMethodFlags { G_DBUS_METHOD_FLAG_DEPRECATED = (1 << 0), G_DBUS_METHOD_FLAG_NOREPLY = (1 << 1), G_DBUS_METHOD_FLAG_ASYNC = (1 << 2), -} GDBusMethodFlags; +}; -typedef enum { +enum GDBusSignalFlags { G_DBUS_SIGNAL_FLAG_DEPRECATED = (1 << 0), -} GDBusSignalFlags; +}; -typedef enum { +enum GDBusPropertyFlags { G_DBUS_PROPERTY_FLAG_DEPRECATED = (1 << 0), -} GDBusPropertyFlags; +}; -typedef enum { +enum GDBusSecurityFlags { G_DBUS_SECURITY_FLAG_DEPRECATED = (1 << 0), G_DBUS_SECURITY_FLAG_BUILTIN = (1 << 1), G_DBUS_SECURITY_FLAG_ALLOW_INTERACTION = (1 << 2), -} GDBusSecurityFlags; +}; -typedef struct { +struct GDBusArgInfo { const char *name; const char *signature; -} GDBusArgInfo; +}; -typedef struct { +struct GDBusMethodTable { const char *name; GDBusMethodFunction function; GDBusMethodFlags flags; unsigned int privilege; const GDBusArgInfo *in_args; const GDBusArgInfo *out_args; -} GDBusMethodTable; +}; -typedef struct { +struct GDBusSignalTable { const char *name; GDBusSignalFlags flags; const GDBusArgInfo *args; -} GDBusSignalTable; +}; -typedef struct { +struct GDBusPropertyTable { const char *name; const char *type; + GDBusPropertyGetter get; + GDBusPropertySetter set; + GDBusPropertyExists exists; GDBusPropertyFlags flags; -} GDBusPropertyTable; +}; -typedef struct { +struct GDBusSecurityTable { unsigned int privilege; const char *action; GDBusSecurityFlags flags; GDBusSecurityFunction function; -} GDBusSecurityTable; +}; #define GDBUS_ARGS(args...) (const GDBusArgInfo[]) { args, { } } @@ -217,9 +243,28 @@ guint g_dbus_add_signal_watch(DBusConnection *connection, const char *interface, const char *member, GDBusSignalFunction function, void *user_data, GDBusDestroyFunction destroy); +guint g_dbus_add_properties_watch(DBusConnection *connection, + const char *sender, const char *path, + const char *interface, + GDBusSignalFunction function, void *user_data, + GDBusDestroyFunction destroy); gboolean g_dbus_remove_watch(DBusConnection *connection, guint tag); void g_dbus_remove_all_watches(DBusConnection *connection); +void g_dbus_pending_property_success(GDBusPendingPropertySet id); +void g_dbus_pending_property_error_valist(GDBusPendingReply id, + const char *name, const char *format, va_list args); +void g_dbus_pending_property_error(GDBusPendingReply id, const char *name, + const char *format, ...); +void g_dbus_emit_property_changed(DBusConnection *connection, + const char *path, const char *interface, + const char *name); +gboolean g_dbus_get_properties(DBusConnection *connection, const char *path, + const char *interface, DBusMessageIter *iter); + +gboolean g_dbus_attach_object_manager(DBusConnection *connection); +gboolean g_dbus_detach_object_manager(DBusConnection *connection); + #ifdef __cplusplus } #endif diff --git a/gdbus/mainloop.c b/gdbus/mainloop.c index cff326f..099b67f 100644 --- a/gdbus/mainloop.c +++ b/gdbus/mainloop.c @@ -92,8 +92,9 @@ static gboolean watch_func(GIOChannel *chan, GIOCondition cond, gpointer data) struct watch_info *info = data; unsigned int flags = 0; DBusDispatchStatus status; + DBusConnection *conn; - dbus_connection_ref(info->conn); + conn = dbus_connection_ref(info->conn); if (cond & G_IO_IN) flags |= DBUS_WATCH_READABLE; if (cond & G_IO_OUT) flags |= DBUS_WATCH_WRITABLE; @@ -102,10 +103,10 @@ static gboolean watch_func(GIOChannel *chan, GIOCondition cond, gpointer data) dbus_watch_handle(info->watch, flags); - status = dbus_connection_get_dispatch_status(info->conn); - queue_dispatch(info->conn, status); + status = dbus_connection_get_dispatch_status(conn); + queue_dispatch(conn, status); - dbus_connection_unref(info->conn); + dbus_connection_unref(conn); return TRUE; } diff --git a/gdbus/object.c b/gdbus/object.c index 900e7ab..7cbd612 100644 --- a/gdbus/object.c +++ b/gdbus/object.c @@ -37,10 +37,28 @@ #define error(fmt...) #define debug(fmt...) +#define DBUS_INTERFACE_OBJECT_MANAGER "org.freedesktop.DBus.ObjectManager" + +#ifndef DBUS_ERROR_UNKNOWN_PROPERTY +#define DBUS_ERROR_UNKNOWN_PROPERTY "org.freedesktop.DBus.Error.UnknownProperty" +#endif + +#ifndef DBUS_ERROR_PROPERTY_READ_ONLY +#define DBUS_ERROR_PROPERTY_READ_ONLY "org.freedesktop.DBus.Error.PropertyReadOnly" +#endif + struct generic_data { unsigned int refcount; + DBusConnection *conn; + char *path; GSList *interfaces; + GSList *objects; + GSList *added; + GSList *removed; + guint process_id; + gboolean pending_prop; char *introspect; + struct generic_data *parent; }; struct interface_data { @@ -48,6 +66,7 @@ struct interface_data { const GDBusMethodTable *methods; const GDBusSignalTable *signals; const GDBusPropertyTable *properties; + GSList *pending_prop; void *user_data; GDBusDestroyFunction destroy; }; @@ -59,6 +78,19 @@ struct security_data { void *iface_user_data; }; +struct property_data { + DBusConnection *conn; + GDBusPendingPropertySet id; + DBusMessage *message; +}; + +static struct generic_data *root; + +static gboolean process_changes(gpointer user_data); +static void process_properties_from_interface(struct generic_data *data, + struct interface_data *iface); +static void process_property_changes(struct generic_data *data); + static void print_arguments(GString *gstr, const GDBusArgInfo *args, const char *direction) { @@ -76,10 +108,21 @@ static void print_arguments(GString *gstr, const GDBusArgInfo *args, } } +#define G_DBUS_ANNOTATE(prefix_, name_, value_) \ + prefix_ "\n" + +#define G_DBUS_ANNOTATE_DEPRECATED(prefix_) \ + G_DBUS_ANNOTATE(prefix_, "Deprecated", "true") + +#define G_DBUS_ANNOTATE_NOREPLY(prefix_) \ + G_DBUS_ANNOTATE(prefix_, "Method.NoReply", "true") + static void generate_interface_xml(GString *gstr, struct interface_data *iface) { const GDBusMethodTable *method; const GDBusSignalTable *signal; + const GDBusPropertyTable *property; for (method = iface->methods; method && method->name; method++) { gboolean deprecated = method->flags & @@ -90,19 +133,22 @@ static void generate_interface_xml(GString *gstr, struct interface_data *iface) if (!deprecated && !noreply && !(method->in_args && method->in_args->name) && !(method->out_args && method->out_args->name)) - g_string_append_printf(gstr, "\t\t\n", - method->name); + g_string_append_printf(gstr, + "\t\t\n", + method->name); else { - g_string_append_printf(gstr, "\t\t\n", - method->name); + g_string_append_printf(gstr, + "\t\t\n", + method->name); print_arguments(gstr, method->in_args, "in"); print_arguments(gstr, method->out_args, "out"); if (deprecated) - g_string_append_printf(gstr, "\t\t\t\n"); - + g_string_append_printf(gstr, + G_DBUS_ANNOTATE_DEPRECATED("\t\t\t")); if (noreply) - g_string_append_printf(gstr, "\t\t\t\n"); + g_string_append_printf(gstr, + G_DBUS_ANNOTATE_NOREPLY("\t\t\t")); g_string_append_printf(gstr, "\t\t\n"); } @@ -113,19 +159,40 @@ static void generate_interface_xml(GString *gstr, struct interface_data *iface) G_DBUS_SIGNAL_FLAG_DEPRECATED; if (!deprecated && !(signal->args && signal->args->name)) - g_string_append_printf(gstr, "\t\t\n", - signal->name); + g_string_append_printf(gstr, + "\t\t\n", + signal->name); else { - g_string_append_printf(gstr, "\t\t\n", - signal->name); + g_string_append_printf(gstr, + "\t\t\n", + signal->name); print_arguments(gstr, signal->args, NULL); if (deprecated) - g_string_append_printf(gstr, "\t\t\t\n"); + g_string_append_printf(gstr, + G_DBUS_ANNOTATE_DEPRECATED("\t\t\t")); g_string_append_printf(gstr, "\t\t\n"); } } + + for (property = iface->properties; property && property->name; + property++) { + gboolean deprecated = property->flags & + G_DBUS_PROPERTY_FLAG_DEPRECATED; + + g_string_append_printf(gstr, "\t\tname, property->type, + property->get ? "read" : "", + property->set ? "write" : ""); + + if (!deprecated) + g_string_append_printf(gstr, "/>\n"); + else + g_string_append_printf(gstr, + G_DBUS_ANNOTATE_DEPRECATED(">\n\t\t\t")); + } } static void generate_introspection_xml(DBusConnection *conn, @@ -226,7 +293,7 @@ void g_dbus_pending_success(DBusConnection *connection, { GSList *list; - for (list = pending_security; list; list = list->next) { + for (list = pending_security; list; list = list->next) { struct security_data *secdata = list->data; if (secdata->pending != pending) @@ -240,7 +307,7 @@ void g_dbus_pending_success(DBusConnection *connection, dbus_message_unref(secdata->message); g_free(secdata); return; - } + } } void g_dbus_pending_error_valist(DBusConnection *connection, @@ -249,7 +316,7 @@ void g_dbus_pending_error_valist(DBusConnection *connection, { GSList *list; - for (list = pending_security; list; list = list->next) { + for (list = pending_security; list; list = list->next) { struct security_data *secdata = list->data; DBusMessage *reply; @@ -268,7 +335,7 @@ void g_dbus_pending_error_valist(DBusConnection *connection, dbus_message_unref(secdata->message); g_free(secdata); return; - } + } } void g_dbus_pending_error(DBusConnection *connection, @@ -364,12 +431,177 @@ static gboolean check_privilege(DBusConnection *conn, DBusMessage *msg, return FALSE; } -static void generic_unregister(DBusConnection *connection, void *user_data) +static GDBusPendingPropertySet next_pending_property = 1; +static GSList *pending_property_set; + +static struct property_data *remove_pending_property_data( + GDBusPendingPropertySet id) { - struct generic_data *data = user_data; + struct property_data *propdata; + GSList *l; - g_free(data->introspect); - g_free(data); + for (l = pending_property_set; l != NULL; l = l->next) { + propdata = l->data; + if (propdata->id != id) + continue; + + break; + } + + if (l == NULL) + return NULL; + + pending_property_set = g_slist_delete_link(pending_property_set, l); + + return propdata; +} + +void g_dbus_pending_property_success(GDBusPendingPropertySet id) +{ + struct property_data *propdata; + + propdata = remove_pending_property_data(id); + if (propdata == NULL) + return; + + g_dbus_send_reply(propdata->conn, propdata->message, + DBUS_TYPE_INVALID); + dbus_message_unref(propdata->message); + g_free(propdata); +} + +void g_dbus_pending_property_error_valist(GDBusPendingReply id, + const char *name, const char *format, + va_list args) +{ + struct property_data *propdata; + DBusMessage *reply; + + propdata = remove_pending_property_data(id); + if (propdata == NULL) + return; + + reply = g_dbus_create_error_valist(propdata->message, name, format, + args); + if (reply != NULL) { + dbus_connection_send(propdata->conn, reply, NULL); + dbus_message_unref(reply); + } + + dbus_message_unref(propdata->message); + g_free(propdata); +} + +void g_dbus_pending_property_error(GDBusPendingReply id, const char *name, + const char *format, ...) +{ + va_list args; + + va_start(args, format); + + g_dbus_pending_property_error_valist(id, name, format, args); + + va_end(args); +} + +static void reset_parent(gpointer data, gpointer user_data) +{ + struct generic_data *child = data; + struct generic_data *parent = user_data; + + child->parent = parent; +} + +static void append_property(struct interface_data *iface, + const GDBusPropertyTable *p, DBusMessageIter *dict) +{ + DBusMessageIter entry, value; + + dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL, + &entry); + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &p->name); + dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, p->type, + &value); + + p->get(p, &value, iface->user_data); + + dbus_message_iter_close_container(&entry, &value); + dbus_message_iter_close_container(dict, &entry); +} + +static void append_properties(struct interface_data *data, + DBusMessageIter *iter) +{ + DBusMessageIter dict; + const GDBusPropertyTable *p; + + dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, + DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING + DBUS_TYPE_STRING_AS_STRING + DBUS_TYPE_VARIANT_AS_STRING + DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); + + for (p = data->properties; p && p->name; p++) { + if (p->get == NULL) + continue; + + if (p->exists != NULL && !p->exists(p, data->user_data)) + continue; + + append_property(data, p, &dict); + } + + dbus_message_iter_close_container(iter, &dict); +} + +static void append_interface(gpointer data, gpointer user_data) +{ + struct interface_data *iface = data; + DBusMessageIter *array = user_data; + DBusMessageIter entry; + + dbus_message_iter_open_container(array, DBUS_TYPE_DICT_ENTRY, NULL, + &entry); + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &iface->name); + append_properties(data, &entry); + dbus_message_iter_close_container(array, &entry); +} + +static void emit_interfaces_added(struct generic_data *data) +{ + DBusMessage *signal; + DBusMessageIter iter, array; + + if (root == NULL || data == root) + return; + + signal = dbus_message_new_signal(root->path, + DBUS_INTERFACE_OBJECT_MANAGER, + "InterfacesAdded"); + if (signal == NULL) + return; + + dbus_message_iter_init_append(signal, &iter); + dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, + &data->path); + + dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, + DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING + DBUS_TYPE_STRING_AS_STRING + DBUS_TYPE_ARRAY_AS_STRING + DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING + DBUS_TYPE_STRING_AS_STRING + DBUS_TYPE_VARIANT_AS_STRING + DBUS_DICT_ENTRY_END_CHAR_AS_STRING + DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &array); + + g_slist_foreach(data->added, append_interface, &array); + g_slist_free(data->added); + data->added = NULL; + + dbus_message_iter_close_container(&iter, &array); + + g_dbus_send_message(data->conn, signal); } static struct interface_data *find_interface(GSList *interfaces, @@ -410,6 +642,370 @@ static gboolean g_dbus_args_have_signature(const GDBusArgInfo *args, return TRUE; } +static gboolean remove_interface(struct generic_data *data, const char *name) +{ + struct interface_data *iface; + + iface = find_interface(data->interfaces, name); + if (iface == NULL) + return FALSE; + + process_properties_from_interface(data, iface); + + data->interfaces = g_slist_remove(data->interfaces, iface); + + if (iface->destroy) { + iface->destroy(iface->user_data); + iface->user_data = NULL; + } + + /* + * Interface being removed was just added, on the same mainloop + * iteration? Don't send any signal + */ + if (g_slist_find(data->added, iface)) { + data->added = g_slist_remove(data->added, iface); + g_free(iface->name); + g_free(iface); + return TRUE; + } + + if (data->parent == NULL) { + g_free(iface->name); + g_free(iface); + return TRUE; + } + + data->removed = g_slist_prepend(data->removed, iface->name); + g_free(iface); + + if (data->process_id > 0) + return TRUE; + + data->process_id = g_idle_add(process_changes, data); + + return TRUE; +} + +static struct generic_data *invalidate_parent_data(DBusConnection *conn, + const char *child_path) +{ + struct generic_data *data = NULL, *child = NULL, *parent = NULL; + char *parent_path, *slash; + + parent_path = g_strdup(child_path); + slash = strrchr(parent_path, '/'); + if (slash == NULL) + goto done; + + if (slash == parent_path && parent_path[1] != '\0') + parent_path[1] = '\0'; + else + *slash = '\0'; + + if (!strlen(parent_path)) + goto done; + + if (dbus_connection_get_object_path_data(conn, parent_path, + (void *) &data) == FALSE) { + goto done; + } + + parent = invalidate_parent_data(conn, parent_path); + + if (data == NULL) { + data = parent; + if (data == NULL) + goto done; + } + + g_free(data->introspect); + data->introspect = NULL; + + if (!dbus_connection_get_object_path_data(conn, child_path, + (void *) &child)) + goto done; + + if (child == NULL || g_slist_find(data->objects, child) != NULL) + goto done; + + data->objects = g_slist_prepend(data->objects, child); + child->parent = data; + +done: + g_free(parent_path); + return data; +} + +static inline const GDBusPropertyTable *find_property(const GDBusPropertyTable *properties, + const char *name) +{ + const GDBusPropertyTable *p; + + for (p = properties; p && p->name; p++) { + if (strcmp(name, p->name) == 0) + return p; + } + + return NULL; +} + +static DBusMessage *properties_get(DBusConnection *connection, + DBusMessage *message, void *user_data) +{ + struct generic_data *data = user_data; + struct interface_data *iface; + const GDBusPropertyTable *property; + const char *interface, *name; + DBusMessageIter iter, value; + DBusMessage *reply; + + if (!dbus_message_get_args(message, NULL, + DBUS_TYPE_STRING, &interface, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_INVALID)) + return NULL; + + iface = find_interface(data->interfaces, interface); + if (iface == NULL) + return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS, + "No such interface '%s'", interface); + + property = find_property(iface->properties, name); + if (property == NULL) + return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS, + "No such property '%s'", name); + + if (property->exists != NULL && + !property->exists(property, iface->user_data)) + return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS, + "No such property '%s'", name); + + if (property->get == NULL) + return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS, + "Property '%s' is not readable", name); + + reply = dbus_message_new_method_return(message); + if (reply == NULL) + return NULL; + + dbus_message_iter_init_append(reply, &iter); + dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, + property->type, &value); + + if (!property->get(property, &value, iface->user_data)) { + dbus_message_unref(reply); + return NULL; + } + + dbus_message_iter_close_container(&iter, &value); + + return reply; +} + +static DBusMessage *properties_get_all(DBusConnection *connection, + DBusMessage *message, void *user_data) +{ + struct generic_data *data = user_data; + struct interface_data *iface; + const char *interface; + DBusMessageIter iter; + DBusMessage *reply; + + if (!dbus_message_get_args(message, NULL, + DBUS_TYPE_STRING, &interface, + DBUS_TYPE_INVALID)) + return NULL; + + iface = find_interface(data->interfaces, interface); + if (iface == NULL) + return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS, + "No such interface '%s'", interface); + + reply = dbus_message_new_method_return(message); + if (reply == NULL) + return NULL; + + dbus_message_iter_init_append(reply, &iter); + + append_properties(iface, &iter); + + return reply; +} + +static DBusMessage *properties_set(DBusConnection *connection, + DBusMessage *message, void *user_data) +{ + struct generic_data *data = user_data; + DBusMessageIter iter, sub; + struct interface_data *iface; + const GDBusPropertyTable *property; + const char *name, *interface; + struct property_data *propdata; + + if (!dbus_message_iter_init(message, &iter)) + return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS, + "No arguments given"); + + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) + return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS, + "Invalid argument type: '%c'", + dbus_message_iter_get_arg_type(&iter)); + + dbus_message_iter_get_basic(&iter, &interface); + dbus_message_iter_next(&iter); + + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) + return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS, + "Invalid argument type: '%c'", + dbus_message_iter_get_arg_type(&iter)); + + dbus_message_iter_get_basic(&iter, &name); + dbus_message_iter_next(&iter); + + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) + return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS, + "Invalid argument type: '%c'", + dbus_message_iter_get_arg_type(&iter)); + + dbus_message_iter_recurse(&iter, &sub); + + iface = find_interface(data->interfaces, interface); + if (iface == NULL) + return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS, + "No such interface '%s'", interface); + + property = find_property(iface->properties, name); + if (property == NULL) + return g_dbus_create_error(message, + DBUS_ERROR_UNKNOWN_PROPERTY, + "No such property '%s'", name); + + if (property->set == NULL) + return g_dbus_create_error(message, + DBUS_ERROR_PROPERTY_READ_ONLY, + "Property '%s' is not writable", name); + + if (property->exists != NULL && + !property->exists(property, iface->user_data)) + return g_dbus_create_error(message, + DBUS_ERROR_UNKNOWN_PROPERTY, + "No such property '%s'", name); + + propdata = g_new(struct property_data, 1); + propdata->id = next_pending_property++; + propdata->message = dbus_message_ref(message); + propdata->conn = connection; + pending_property_set = g_slist_prepend(pending_property_set, propdata); + + property->set(property, &sub, propdata->id, iface->user_data); + + return NULL; +} + +static const GDBusMethodTable properties_methods[] = { + { GDBUS_METHOD("Get", + GDBUS_ARGS({ "interface", "s" }, { "name", "s" }), + GDBUS_ARGS({ "value", "v" }), + properties_get) }, + { GDBUS_ASYNC_METHOD("Set", + GDBUS_ARGS({ "interface", "s" }, { "name", "s" }, + { "value", "v" }), + NULL, + properties_set) }, + { GDBUS_METHOD("GetAll", + GDBUS_ARGS({ "interface", "s" }), + GDBUS_ARGS({ "properties", "a{sv}" }), + properties_get_all) }, + { } +}; + +static const GDBusSignalTable properties_signals[] = { + { GDBUS_SIGNAL("PropertiesChanged", + GDBUS_ARGS({ "interface", "s" }, + { "changed_properties", "a{sv}" }, + { "invalidated_properties", "as"})) }, + { } +}; + +static void append_name(gpointer data, gpointer user_data) +{ + char *name = data; + DBusMessageIter *iter = user_data; + + dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &name); +} + +static void emit_interfaces_removed(struct generic_data *data) +{ + DBusMessage *signal; + DBusMessageIter iter, array; + + if (root == NULL || data == root) + return; + + signal = dbus_message_new_signal(root->path, + DBUS_INTERFACE_OBJECT_MANAGER, + "InterfacesRemoved"); + if (signal == NULL) + return; + + dbus_message_iter_init_append(signal, &iter); + dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, + &data->path); + dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, + DBUS_TYPE_STRING_AS_STRING, &array); + + g_slist_foreach(data->removed, append_name, &array); + g_slist_free_full(data->removed, g_free); + data->removed = NULL; + + dbus_message_iter_close_container(&iter, &array); + + g_dbus_send_message(data->conn, signal); +} + +static gboolean process_changes(gpointer user_data) +{ + struct generic_data *data = user_data; + + data->process_id = 0; + + if (data->added != NULL) + emit_interfaces_added(data); + + /* Flush pending properties */ + if (data->pending_prop == TRUE) + process_property_changes(data); + + if (data->removed != NULL) + emit_interfaces_removed(data); + + return FALSE; +} + +static void generic_unregister(DBusConnection *connection, void *user_data) +{ + struct generic_data *data = user_data; + struct generic_data *parent = data->parent; + + if (parent != NULL) + parent->objects = g_slist_remove(parent->objects, data); + + if (data->process_id > 0) { + g_source_remove(data->process_id); + process_changes(data); + } + + g_slist_foreach(data->objects, reset_parent, data->parent); + g_slist_free(data->objects); + + dbus_connection_unref(data->conn); + g_free(data->introspect); + g_free(data->path); + g_free(data); +} + static DBusHandlerResult generic_message(DBusConnection *connection, DBusMessage *message, void *user_data) { @@ -450,48 +1046,100 @@ static DBusObjectPathVTable generic_table = { .message_function = generic_message, }; -static void invalidate_parent_data(DBusConnection *conn, const char *child_path) +static const GDBusMethodTable introspect_methods[] = { + { GDBUS_METHOD("Introspect", NULL, + GDBUS_ARGS({ "xml", "s" }), introspect) }, + { } +}; + +static void append_interfaces(struct generic_data *data, DBusMessageIter *iter) { - struct generic_data *data = NULL; - char *parent_path, *slash; + DBusMessageIter array; - parent_path = g_strdup(child_path); - slash = strrchr(parent_path, '/'); - if (slash == NULL) - goto done; + dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, + DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING + DBUS_TYPE_STRING_AS_STRING + DBUS_TYPE_ARRAY_AS_STRING + DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING + DBUS_TYPE_STRING_AS_STRING + DBUS_TYPE_VARIANT_AS_STRING + DBUS_DICT_ENTRY_END_CHAR_AS_STRING + DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &array); - if (slash == parent_path && parent_path[1] != '\0') - parent_path[1] = '\0'; - else - *slash = '\0'; + g_slist_foreach(data->interfaces, append_interface, &array); - if (!strlen(parent_path)) - goto done; + dbus_message_iter_close_container(iter, &array); +} - if (dbus_connection_get_object_path_data(conn, parent_path, - (void *) &data) == FALSE) { - goto done; - } +static void append_object(gpointer data, gpointer user_data) +{ + struct generic_data *child = data; + DBusMessageIter *array = user_data; + DBusMessageIter entry; + + dbus_message_iter_open_container(array, DBUS_TYPE_DICT_ENTRY, NULL, + &entry); + dbus_message_iter_append_basic(&entry, DBUS_TYPE_OBJECT_PATH, + &child->path); + append_interfaces(child, &entry); + dbus_message_iter_close_container(array, &entry); + + g_slist_foreach(child->objects, append_object, user_data); +} - invalidate_parent_data(conn, parent_path); +static DBusMessage *get_objects(DBusConnection *connection, + DBusMessage *message, void *user_data) +{ + struct generic_data *data = user_data; + DBusMessage *reply; + DBusMessageIter iter; + DBusMessageIter array; - if (data == NULL) - goto done; + reply = dbus_message_new_method_return(message); + if (reply == NULL) + return NULL; - g_free(data->introspect); - data->introspect = NULL; + dbus_message_iter_init_append(reply, &iter); -done: - g_free(parent_path); + dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, + DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING + DBUS_TYPE_OBJECT_PATH_AS_STRING + DBUS_TYPE_ARRAY_AS_STRING + DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING + DBUS_TYPE_STRING_AS_STRING + DBUS_TYPE_ARRAY_AS_STRING + DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING + DBUS_TYPE_STRING_AS_STRING + DBUS_TYPE_VARIANT_AS_STRING + DBUS_DICT_ENTRY_END_CHAR_AS_STRING + DBUS_DICT_ENTRY_END_CHAR_AS_STRING + DBUS_DICT_ENTRY_END_CHAR_AS_STRING, + &array); + + g_slist_foreach(data->objects, append_object, &array); + + dbus_message_iter_close_container(&iter, &array); + + return reply; } -static const GDBusMethodTable introspect_methods[] = { - { GDBUS_METHOD("Introspect", NULL, - GDBUS_ARGS({ "xml", "s" }), introspect) }, +static const GDBusMethodTable manager_methods[] = { + { GDBUS_METHOD("GetManagedObjects", NULL, + GDBUS_ARGS({ "objects", "a{oa{sa{sv}}}" }), get_objects) }, { } }; -static void add_interface(struct generic_data *data, const char *name, +static const GDBusSignalTable manager_signals[] = { + { GDBUS_SIGNAL("InterfacesAdded", + GDBUS_ARGS({ "object", "o" }, + { "interfaces", "a{sa{sv}}" })) }, + { GDBUS_SIGNAL("InterfacesRemoved", + GDBUS_ARGS({ "object", "o" }, { "interfaces", "as" })) }, + { } +}; + +static void add_interface(struct generic_data *data, + const char *name, const GDBusMethodTable *methods, const GDBusSignalTable *signals, const GDBusPropertyTable *properties, @@ -509,6 +1157,14 @@ static void add_interface(struct generic_data *data, const char *name, iface->destroy = destroy; data->interfaces = g_slist_append(data->interfaces, iface); + if (data->parent == NULL) + return; + + data->added = g_slist_append(data->added, iface); + if (data->process_id > 0) + return; + + data->process_id = g_idle_add(process_changes, data); } static struct generic_data *object_path_ref(DBusConnection *connection, @@ -525,6 +1181,8 @@ static struct generic_data *object_path_ref(DBusConnection *connection, } data = g_new0(struct generic_data, 1); + data->conn = dbus_connection_ref(connection); + data->path = g_strdup(path); data->refcount = 1; data->introspect = g_strdup(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE ""); @@ -538,31 +1196,12 @@ static struct generic_data *object_path_ref(DBusConnection *connection, invalidate_parent_data(connection, path); - add_interface(data, DBUS_INTERFACE_INTROSPECTABLE, - introspect_methods, NULL, NULL, data, NULL); + add_interface(data, DBUS_INTERFACE_INTROSPECTABLE, introspect_methods, + NULL, NULL, data, NULL); return data; } -static gboolean remove_interface(struct generic_data *data, const char *name) -{ - struct interface_data *iface; - - iface = find_interface(data->interfaces, name); - if (iface == NULL) - return FALSE; - - data->interfaces = g_slist_remove(data->interfaces, iface); - - if (iface->destroy) - iface->destroy(iface->user_data); - - g_free(iface->name); - g_free(iface); - - return TRUE; -} - static void object_path_unref(DBusConnection *connection, const char *path) { struct generic_data *data = NULL; @@ -580,10 +1219,11 @@ static void object_path_unref(DBusConnection *connection, const char *path) return; remove_interface(data, DBUS_INTERFACE_INTROSPECTABLE); + remove_interface(data, DBUS_INTERFACE_PROPERTIES); - invalidate_parent_data(connection, path); + invalidate_parent_data(data->conn, data->path); - dbus_connection_unregister_object_path(connection, path); + dbus_connection_unregister_object_path(data->conn, data->path); } static gboolean check_signal(DBusConnection *conn, const char *path, @@ -645,8 +1285,8 @@ static dbus_bool_t emit_signal_valist(DBusConnection *conn, goto fail; if (g_dbus_args_have_signature(args, signal) == FALSE) { - error("%s.%s: expected signature'%s' but got '%s'", - interface, name, args, signature); + error("%s.%s: got unexpected signature '%s'", interface, name, + dbus_message_get_signature(signal)); ret = FALSE; goto fail; } @@ -678,8 +1318,14 @@ gboolean g_dbus_register_interface(DBusConnection *connection, return FALSE; } - add_interface(data, name, methods, signals, - properties, user_data, destroy); + if (properties != NULL && !find_interface(data->interfaces, + DBUS_INTERFACE_PROPERTIES)) + add_interface(data, DBUS_INTERFACE_PROPERTIES, + properties_methods, properties_signals, NULL, + data, NULL); + + add_interface(data, name, methods, signals, properties, user_data, + destroy); g_free(data->introspect); data->introspect = NULL; @@ -708,7 +1354,7 @@ gboolean g_dbus_unregister_interface(DBusConnection *connection, g_free(data->introspect); data->introspect = NULL; - object_path_unref(connection, path); + object_path_unref(connection, data->path); return TRUE; } @@ -856,3 +1502,158 @@ gboolean g_dbus_emit_signal_valist(DBusConnection *connection, return emit_signal_valist(connection, path, interface, name, type, args); } + +static void process_properties_from_interface(struct generic_data *data, + struct interface_data *iface) +{ + GSList *l; + DBusMessage *signal; + DBusMessageIter iter, dict, array; + GSList *invalidated; + + if (iface->pending_prop == NULL) + return; + + signal = dbus_message_new_signal(data->path, + DBUS_INTERFACE_PROPERTIES, "PropertiesChanged"); + if (signal == NULL) { + error("Unable to allocate new " DBUS_INTERFACE_PROPERTIES + ".PropertiesChanged signal"); + return; + } + + iface->pending_prop = g_slist_reverse(iface->pending_prop); + + dbus_message_iter_init_append(signal, &iter); + dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &iface->name); + dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, + DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING + DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING + DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); + + invalidated = NULL; + + for (l = iface->pending_prop; l != NULL; l = l->next) { + GDBusPropertyTable *p = l->data; + + if (p->get == NULL) + continue; + + if (p->exists != NULL && !p->exists(p, iface->user_data)) { + invalidated = g_slist_prepend(invalidated, p); + continue; + } + + append_property(iface, p, &dict); + } + + dbus_message_iter_close_container(&iter, &dict); + + dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, + DBUS_TYPE_STRING_AS_STRING, &array); + for (l = invalidated; l != NULL; l = g_slist_next(l)) { + GDBusPropertyTable *p = l->data; + + dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING, + &p->name); + } + g_slist_free(invalidated); + dbus_message_iter_close_container(&iter, &array); + + g_dbus_send_message(data->conn, signal); + + g_slist_free(iface->pending_prop); + iface->pending_prop = NULL; +} + +static void process_property_changes(struct generic_data *data) +{ + GSList *l; + + for (l = data->interfaces; l != NULL; l = l->next) { + struct interface_data *iface = l->data; + + process_properties_from_interface(data, iface); + } + + data->pending_prop = FALSE; +} + +void g_dbus_emit_property_changed(DBusConnection *connection, + const char *path, const char *interface, + const char *name) +{ + const GDBusPropertyTable *property; + struct generic_data *data; + struct interface_data *iface; + + if (!dbus_connection_get_object_path_data(connection, path, + (void **) &data) || data == NULL) + return; + + iface = find_interface(data->interfaces, interface); + if (iface == NULL) + return; + + property = find_property(iface->properties, name); + if (property == NULL) { + error("Could not find property %s in %p", name, + iface->properties); + return; + } + + data->pending_prop = TRUE; + iface->pending_prop = g_slist_prepend(iface->pending_prop, + (void *) property); + + if (!data->process_id) { + data->process_id = g_idle_add(process_changes, data); + return; + } +} + +gboolean g_dbus_get_properties(DBusConnection *connection, const char *path, + const char *interface, DBusMessageIter *iter) +{ + struct generic_data *data; + struct interface_data *iface; + + if (!dbus_connection_get_object_path_data(connection, path, + (void **) &data) || data == NULL) + return FALSE; + + iface = find_interface(data->interfaces, interface); + if (iface == NULL) + return FALSE; + + append_properties(iface, iter); + + return TRUE; +} + +gboolean g_dbus_attach_object_manager(DBusConnection *connection) +{ + struct generic_data *data; + + data = object_path_ref(connection, "/"); + if (data == NULL) + return FALSE; + + add_interface(data, DBUS_INTERFACE_OBJECT_MANAGER, + manager_methods, manager_signals, + NULL, data, NULL); + root = data; + + return TRUE; +} + +gboolean g_dbus_detach_object_manager(DBusConnection *connection) +{ + if (!g_dbus_unregister_interface(connection, "/", + DBUS_INTERFACE_OBJECT_MANAGER)) + return FALSE; + + root = NULL; + + return TRUE; +} diff --git a/gdbus/watch.c b/gdbus/watch.c index d749176..9e4f994 100644 --- a/gdbus/watch.c +++ b/gdbus/watch.c @@ -78,7 +78,7 @@ struct filter_data { gboolean registered; }; -static struct filter_data *filter_data_find(DBusConnection *connection, +static struct filter_data *filter_data_find_match(DBusConnection *connection, const char *name, const char *owner, const char *path, @@ -95,28 +95,39 @@ static struct filter_data *filter_data_find(DBusConnection *connection, if (connection != data->connection) continue; - if (name && data->name && - g_str_equal(name, data->name) == FALSE) + if (g_strcmp0(name, data->name) != 0) continue; - if (owner && data->owner && - g_str_equal(owner, data->owner) == FALSE) + if (g_strcmp0(owner, data->owner) != 0) continue; - if (path && data->path && - g_str_equal(path, data->path) == FALSE) + if (g_strcmp0(path, data->path) != 0) continue; - if (interface && data->interface && - g_str_equal(interface, data->interface) == FALSE) + if (g_strcmp0(interface, data->interface) != 0) continue; - if (member && data->member && - g_str_equal(member, data->member) == FALSE) + if (g_strcmp0(member, data->member) != 0) continue; - if (argument && data->argument && - g_str_equal(argument, data->argument) == FALSE) + if (g_strcmp0(argument, data->argument) != 0) + continue; + + return data; + } + + return NULL; +} + +static struct filter_data *filter_data_find(DBusConnection *connection) +{ + GSList *current; + + for (current = listeners; + current != NULL; current = current->next) { + struct filter_data *data = current->data; + + if (connection != data->connection) continue; return data; @@ -204,7 +215,7 @@ static struct filter_data *filter_data_get(DBusConnection *connection, struct filter_data *data; const char *name = NULL, *owner = NULL; - if (filter_data_find(connection, NULL, NULL, NULL, NULL, NULL, NULL) == NULL) { + if (filter_data_find(connection) == NULL) { if (!dbus_connection_add_filter(connection, message_filter, NULL, NULL)) { error("dbus_connection_add_filter() failed"); @@ -221,16 +232,16 @@ static struct filter_data *filter_data_get(DBusConnection *connection, name = sender; proceed: - data = filter_data_find(connection, name, owner, path, interface, - member, argument); + data = filter_data_find_match(connection, name, owner, path, + interface, member, argument); if (data) return data; data = g_new0(struct filter_data, 1); data->connection = dbus_connection_ref(connection); - data->name = name ? g_strdup(name) : NULL; - data->owner = owner ? g_strdup(owner) : NULL; + data->name = g_strdup(name); + data->owner = g_strdup(owner); data->path = g_strdup(path); data->interface = g_strdup(interface); data->member = g_strdup(member); @@ -378,8 +389,7 @@ static gboolean filter_data_remove_callback(struct filter_data *data, listeners = g_slist_remove(listeners, data); /* Remove filter if there are no listeners left for the connection */ - if (filter_data_find(connection, NULL, NULL, NULL, NULL, NULL, - NULL) == NULL) + if (filter_data_find(connection) == NULL) dbus_connection_remove_filter(connection, message_filter, NULL); @@ -501,6 +511,7 @@ static DBusHandlerResult message_filter(DBusConnection *connection, { struct filter_data *data; const char *sender, *path, *iface, *member, *arg = NULL; + GSList *current, *delete_listener = NULL; /* Only filter signals */ if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_SIGNAL) @@ -512,39 +523,69 @@ static DBusHandlerResult message_filter(DBusConnection *connection, member = dbus_message_get_member(message); dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &arg, DBUS_TYPE_INVALID); - /* Sender is always bus name */ - data = filter_data_find(connection, NULL, sender, path, iface, member, - arg); - if (data == NULL) { - error("Got %s.%s signal which has no listeners", iface, member); - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - } + /* Sender is always the owner */ - if (data->handle_func) { - data->lock = TRUE; + for (current = listeners; current != NULL; current = current->next) { + data = current->data; - data->handle_func(connection, message, data); + if (connection != data->connection) + continue; - data->callbacks = data->processed; - data->processed = NULL; - data->lock = FALSE; + if (data->owner && g_str_equal(sender, data->owner) == FALSE) + continue; + + if (data->path && g_str_equal(path, data->path) == FALSE) + continue; + + if (data->interface && g_str_equal(iface, + data->interface) == FALSE) + continue; + + if (data->member && g_str_equal(member, data->member) == FALSE) + continue; + + if (data->argument && g_str_equal(arg, + data->argument) == FALSE) + continue; + + if (data->handle_func) { + data->lock = TRUE; + + data->handle_func(connection, message, data); + + data->callbacks = data->processed; + data->processed = NULL; + data->lock = FALSE; + } + + if (!data->callbacks) + delete_listener = g_slist_prepend(delete_listener, + current); } - if (data->callbacks) - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + for (current = delete_listener; current != NULL; + current = delete_listener->next) { + GSList *l = current->data; - remove_match(data); + data = l->data; - listeners = g_slist_remove(listeners, data); + /* Has any other callback added callbacks back to this data? */ + if (data->callbacks != NULL) + continue; + + remove_match(data); + listeners = g_slist_delete_link(listeners, l); + + filter_data_free(data); + } + + g_slist_free(delete_listener); /* Remove filter if there are no listeners left for the connection */ - if (filter_data_find(connection, NULL, NULL, NULL, NULL, NULL, - NULL) == NULL) + if (filter_data_find(connection) == NULL) dbus_connection_remove_filter(connection, message_filter, NULL); - filter_data_free(data); - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } @@ -711,6 +752,34 @@ guint g_dbus_add_signal_watch(DBusConnection *connection, return cb->id; } +guint g_dbus_add_properties_watch(DBusConnection *connection, + const char *sender, const char *path, + const char *interface, + GDBusSignalFunction function, void *user_data, + GDBusDestroyFunction destroy) +{ + struct filter_data *data; + struct filter_callback *cb; + + data = filter_data_get(connection, signal_filter, sender, path, + DBUS_INTERFACE_PROPERTIES, "PropertiesChanged", + interface); + if (data == NULL) + return 0; + + cb = filter_data_add_callback(data, NULL, NULL, function, destroy, + user_data); + if (cb == NULL) + return 0; + + if (data->name != NULL && data->name_watch == 0) + data->name_watch = g_dbus_add_service_watch(connection, + data->name, NULL, + NULL, NULL, NULL); + + return cb->id; +} + gboolean g_dbus_remove_watch(DBusConnection *connection, guint id) { struct filter_data *data; @@ -737,8 +806,7 @@ void g_dbus_remove_all_watches(DBusConnection *connection) { struct filter_data *data; - while ((data = filter_data_find(connection, NULL, NULL, NULL, NULL, - NULL, NULL))) { + while ((data = filter_data_find(connection))) { listeners = g_slist_remove(listeners, data); filter_data_call_and_free(data); } diff --git a/gobex/gobex-apparam.c b/gobex/gobex-apparam.c new file mode 100644 index 0000000..442a3f1 --- /dev/null +++ b/gobex/gobex-apparam.c @@ -0,0 +1,364 @@ +/* + * + * OBEX library with GLib integration + * + * Copyright (C) 2012 Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include "gobex-apparam.h" +#include "gobex-debug.h" + +struct _GObexApparam { + GHashTable *tags; +}; + +struct apparam_tag { + guint8 id; + guint8 len; + union { + /* Data is stored in network order */ + char string[0]; + guint8 data[0]; + guint8 u8; + guint16 u16; + guint32 u32; + guint64 u64; + } value; +} __attribute__ ((packed)); + +static struct apparam_tag *tag_new(guint8 id, guint8 len, const void *data) +{ + struct apparam_tag *tag; + + tag = g_malloc0(2 + len); + tag->id = id; + tag->len = len; + memcpy(tag->value.data, data, len); + + return tag; +} + +static GObexApparam *g_obex_apparam_new(void) +{ + GObexApparam *apparam; + + apparam = g_new0(GObexApparam, 1); + apparam->tags = g_hash_table_new_full(g_direct_hash, g_direct_equal, + NULL, g_free); + + return apparam; +} + +static struct apparam_tag *apparam_tag_decode(const void *data, gsize size, + gsize *parsed) +{ + struct apparam_tag *tag; + const guint8 *ptr = data; + guint8 id; + guint8 len; + + if (size < 2) + return NULL; + + id = ptr[0]; + len = ptr[1]; + + if (len > size - 2) + return NULL; + + tag = tag_new(id, len, ptr + 2); + if (tag == NULL) + return NULL; + + *parsed = 2 + tag->len; + + return tag; +} + +GObexApparam *g_obex_apparam_decode(const void *data, gsize size) +{ + GObexApparam *apparam; + GHashTable *tags; + gsize count = 0; + + if (size < 2) + return NULL; + + apparam = g_obex_apparam_new(); + + tags = apparam->tags; + while (count < size) { + struct apparam_tag *tag; + gsize parsed; + guint id; + + tag = apparam_tag_decode(data + count, size - count, &parsed); + if (tag == NULL) + break; + + id = tag->id; + g_hash_table_insert(tags, GUINT_TO_POINTER(id), tag); + + count += parsed; + } + + if (count != size) { + g_obex_apparam_free(apparam); + return NULL; + } + + return apparam; +} + +static gssize tag_encode(struct apparam_tag *tag, void *buf, gsize len) +{ + gsize count = 2 + tag->len; + + if (len < count) + return -ENOBUFS; + + memcpy(buf, tag, count); + + return count; +} + +gssize g_obex_apparam_encode(GObexApparam *apparam, void *buf, gsize len) +{ + gsize count = 0; + gssize ret; + GHashTableIter iter; + gpointer key, value; + + g_hash_table_iter_init(&iter, apparam->tags); + while (g_hash_table_iter_next(&iter, &key, &value)) { + struct apparam_tag *tag = value; + + ret = tag_encode(tag, buf + count, len - count); + if (ret < 0) + return ret; + + count += ret; + } + + return count; +} + +GObexApparam *g_obex_apparam_set_bytes(GObexApparam *apparam, guint8 id, + const void *value, gsize len) +{ + struct apparam_tag *tag; + guint uid = id; + + if (apparam == NULL) + apparam = g_obex_apparam_new(); + + tag = tag_new(id, len, value); + g_hash_table_replace(apparam->tags, GUINT_TO_POINTER(uid), tag); + + return apparam; +} + +GObexApparam *g_obex_apparam_set_uint8(GObexApparam *apparam, guint8 id, + guint8 value) +{ + g_obex_debug(G_OBEX_DEBUG_APPARAM, "tag 0x%02x value %u", id, value); + + return g_obex_apparam_set_bytes(apparam, id, &value, 1); +} + +GObexApparam *g_obex_apparam_set_uint16(GObexApparam *apparam, guint8 id, + guint16 value) +{ + guint16 num = g_htons(value); + + g_obex_debug(G_OBEX_DEBUG_APPARAM, "tag 0x%02x value %u", id, value); + + return g_obex_apparam_set_bytes(apparam, id, &num, 2); +} + +GObexApparam *g_obex_apparam_set_uint32(GObexApparam *apparam, guint8 id, + guint32 value) +{ + guint32 num = g_htonl(value); + + g_obex_debug(G_OBEX_DEBUG_APPARAM, "tag 0x%02x value %u", id, value); + + return g_obex_apparam_set_bytes(apparam, id, &num, 4); +} + +GObexApparam *g_obex_apparam_set_uint64(GObexApparam *apparam, guint8 id, + guint64 value) +{ + guint64 num = GUINT64_TO_BE(value); + + g_obex_debug(G_OBEX_DEBUG_APPARAM, "tag 0x%02x value %" + G_GUINT64_FORMAT, id, value); + + return g_obex_apparam_set_bytes(apparam, id, &num, 8); +} + +GObexApparam *g_obex_apparam_set_string(GObexApparam *apparam, guint8 id, + const char *value) +{ + gsize len; + + g_obex_debug(G_OBEX_DEBUG_APPARAM, "tag 0x%02x value %s", id, value); + + len = strlen(value) + 1; + if (len > G_MAXUINT8) { + ((char *) value)[G_MAXUINT8 - 1] = '\0'; + len = G_MAXUINT8; + } + + return g_obex_apparam_set_bytes(apparam, id, value, len); +} + +static struct apparam_tag *g_obex_apparam_find_tag(GObexApparam *apparam, + guint id) +{ + return g_hash_table_lookup(apparam->tags, GUINT_TO_POINTER(id)); +} + +gboolean g_obex_apparam_get_uint8(GObexApparam *apparam, guint8 id, + guint8 *dest) +{ + struct apparam_tag *tag; + + g_obex_debug(G_OBEX_DEBUG_APPARAM, "tag 0x%02x", id); + + tag = g_obex_apparam_find_tag(apparam, id); + if (tag == NULL) + return FALSE; + + *dest = tag->value.u8; + + g_obex_debug(G_OBEX_DEBUG_APPARAM, "%u", *dest); + + return TRUE; +} + +gboolean g_obex_apparam_get_uint16(GObexApparam *apparam, guint8 id, + guint16 *dest) +{ + struct apparam_tag *tag; + + g_obex_debug(G_OBEX_DEBUG_APPARAM, "tag 0x%02x", id); + + tag = g_obex_apparam_find_tag(apparam, id); + if (tag == NULL) + return FALSE; + + if (tag->len < sizeof(*dest)) + return FALSE; + + *dest = g_ntohs(tag->value.u16); + + g_obex_debug(G_OBEX_DEBUG_APPARAM, "%u", *dest); + + return TRUE; +} + +gboolean g_obex_apparam_get_uint32(GObexApparam *apparam, guint8 id, + guint32 *dest) +{ + struct apparam_tag *tag; + + g_obex_debug(G_OBEX_DEBUG_APPARAM, "tag 0x%02x", id); + + tag = g_obex_apparam_find_tag(apparam, id); + if (tag == NULL) + return FALSE; + + if (tag->len < sizeof(*dest)) + return FALSE; + + *dest = g_ntohl(tag->value.u32); + + g_obex_debug(G_OBEX_DEBUG_APPARAM, "%u", *dest); + + return TRUE; +} + +gboolean g_obex_apparam_get_uint64(GObexApparam *apparam, guint8 id, + guint64 *dest) +{ + struct apparam_tag *tag; + + g_obex_debug(G_OBEX_DEBUG_APPARAM, "tag 0x%02x", id); + + tag = g_obex_apparam_find_tag(apparam, id); + if (tag == NULL) + return FALSE; + + if (tag->len < sizeof(*dest)) + return FALSE; + + *dest = GUINT64_FROM_BE(tag->value.u64); + + g_obex_debug(G_OBEX_DEBUG_APPARAM, "%" G_GUINT64_FORMAT, *dest); + + return TRUE; +} + +char *g_obex_apparam_get_string(GObexApparam *apparam, guint8 id) +{ + struct apparam_tag *tag; + char *string; + + g_obex_debug(G_OBEX_DEBUG_APPARAM, "tag 0x%02x", id); + + tag = g_obex_apparam_find_tag(apparam, id); + if (tag == NULL) + return NULL; + + string = g_strndup(tag->value.string, tag->len); + + g_obex_debug(G_OBEX_DEBUG_APPARAM, "%s", string); + + return string; +} + +gboolean g_obex_apparam_get_bytes(GObexApparam *apparam, guint8 id, + const guint8 **val, gsize *len) +{ + struct apparam_tag *tag; + + g_obex_debug(G_OBEX_DEBUG_APPARAM, "tag 0x%02x", id); + + tag = g_obex_apparam_find_tag(apparam, id); + if (tag == NULL) + return FALSE; + + *len = tag->len; + *val = tag->value.data; + + return TRUE; +} + +void g_obex_apparam_free(GObexApparam *apparam) +{ + g_hash_table_unref(apparam->tags); + g_free(apparam); +} diff --git a/gobex/gobex-apparam.h b/gobex/gobex-apparam.h new file mode 100644 index 0000000..46e20d2 --- /dev/null +++ b/gobex/gobex-apparam.h @@ -0,0 +1,59 @@ +/* + * + * OBEX library with GLib integration + * + * Copyright (C) 2012 Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef __GOBEX_APPARAM_H +#define __GOBEX_APPARAM_H + +#include + +typedef struct _GObexApparam GObexApparam; + +GObexApparam *g_obex_apparam_decode(const void *data, gsize size); +gssize g_obex_apparam_encode(GObexApparam *apparam, void *buf, gsize size); + +GObexApparam *g_obex_apparam_set_bytes(GObexApparam *apparam, guint8 id, + const void *value, gsize size); +GObexApparam *g_obex_apparam_set_uint8(GObexApparam *apparam, guint8 id, + guint8 value); +GObexApparam *g_obex_apparam_set_uint16(GObexApparam *apparam, guint8 id, + guint16 value); +GObexApparam *g_obex_apparam_set_uint32(GObexApparam *apparam, guint8 id, + guint32 value); +GObexApparam *g_obex_apparam_set_uint64(GObexApparam *apparam, guint8 id, + guint64 value); +GObexApparam *g_obex_apparam_set_string(GObexApparam *apparam, guint8 id, + const char *value); + +gboolean g_obex_apparam_get_bytes(GObexApparam *apparam, guint8 id, + const guint8 **val, gsize *len); +gboolean g_obex_apparam_get_uint8(GObexApparam *apparam, guint8 id, + guint8 *value); +gboolean g_obex_apparam_get_uint16(GObexApparam *apparam, guint8 id, + guint16 *value); +gboolean g_obex_apparam_get_uint32(GObexApparam *apparam, guint8 id, + guint32 *value); +gboolean g_obex_apparam_get_uint64(GObexApparam *apparam, guint8 id, + guint64 *value); +char *g_obex_apparam_get_string(GObexApparam *apparam, guint8 id); + +void g_obex_apparam_free(GObexApparam *apparam); + +#endif /* __GOBEX_APPARAM_H */ diff --git a/gobex/gobex-debug.h b/gobex/gobex-debug.h index 589104e..14e2bcd 100644 --- a/gobex/gobex-debug.h +++ b/gobex/gobex-debug.h @@ -32,21 +32,22 @@ #define G_OBEX_DEBUG_HEADER (1 << 4) #define G_OBEX_DEBUG_PACKET (1 << 5) #define G_OBEX_DEBUG_DATA (1 << 6) +#define G_OBEX_DEBUG_APPARAM (1 << 7) extern guint gobex_debug; #define g_obex_debug(level, format, ...) \ if (gobex_debug & level) \ - g_debug("%s:%s() " format, __FILE__, __FUNCTION__, \ - ## __VA_ARGS__) + g_log("gobex", G_LOG_LEVEL_DEBUG, "%s:%s() " format, __FILE__, \ + __FUNCTION__, ## __VA_ARGS__) -static inline void g_obex_dump(const char *prefix, const void *buf, - gsize len) +static inline void g_obex_dump(guint level, const char *prefix, + const void *buf, gsize len) { const guint8 *data = buf; int n = 0; - if (!(gobex_debug & G_OBEX_DEBUG_DATA)) + if (!(gobex_debug & level)) return; while (len > 0) { diff --git a/gobex/gobex-header.c b/gobex/gobex-header.c index 56dd9b2..80e8e4e 100644 --- a/gobex/gobex-header.c +++ b/gobex/gobex-header.c @@ -337,6 +337,19 @@ gboolean g_obex_header_get_bytes(GObexHeader *header, const guint8 **val, return TRUE; } +GObexApparam *g_obex_header_get_apparam(GObexHeader *header) +{ + gboolean ret; + const guint8 *val; + gsize len; + + ret = g_obex_header_get_bytes(header, &val, &len); + if (!ret) + return NULL; + + return g_obex_apparam_decode(val, len); +} + gboolean g_obex_header_get_uint8(GObexHeader *header, guint8 *val) { g_obex_debug(G_OBEX_DEBUG_HEADER, "header 0x%02x", @@ -411,6 +424,18 @@ GObexHeader *g_obex_header_new_bytes(guint8 id, const void *data, gsize len) return header; } +GObexHeader *g_obex_header_new_apparam(GObexApparam *apparam) +{ + guint8 buf[1024]; + gssize len; + + len = g_obex_apparam_encode(apparam, buf, sizeof(buf)); + if (len < 0) + return NULL; + + return g_obex_header_new_bytes(G_OBEX_HDR_APPARAM, buf, len); +} + GObexHeader *g_obex_header_new_uint8(guint8 id, guint8 val) { GObexHeader *header; diff --git a/gobex/gobex-header.h b/gobex/gobex-header.h index 2ee8364..196cb20 100644 --- a/gobex/gobex-header.h +++ b/gobex/gobex-header.h @@ -25,6 +25,7 @@ #include #include +#include /* Header ID's */ #define G_OBEX_HDR_INVALID 0x00 @@ -77,11 +78,13 @@ gboolean g_obex_header_get_bytes(GObexHeader *header, const guint8 **val, gsize *len); gboolean g_obex_header_get_uint8(GObexHeader *header, guint8 *val); gboolean g_obex_header_get_uint32(GObexHeader *header, guint32 *val); +GObexApparam *g_obex_header_get_apparam(GObexHeader *header); GObexHeader *g_obex_header_new_unicode(guint8 id, const char *str); GObexHeader *g_obex_header_new_bytes(guint8 id, const void *data, gsize len); GObexHeader *g_obex_header_new_uint8(guint8 id, guint8 val); GObexHeader *g_obex_header_new_uint32(guint8 id, guint32 val); +GObexHeader *g_obex_header_new_apparam(GObexApparam *apparam); GSList *g_obex_header_create_list(guint8 first_hdr_id, va_list args, gsize *total_len); diff --git a/gobex/gobex.c b/gobex/gobex.c index b20542d..7c136af 100644 --- a/gobex/gobex.c +++ b/gobex/gobex.c @@ -267,7 +267,7 @@ static gboolean write_stream(GObex *obex, GError **err) if (status != G_IO_STATUS_NORMAL) return FALSE; - g_obex_dump("<", buf, bytes_written); + g_obex_dump(G_OBEX_DEBUG_DATA, "<", buf, bytes_written); obex->tx_sent += bytes_written; obex->tx_data -= bytes_written; @@ -290,7 +290,7 @@ static gboolean write_packet(GObex *obex, GError **err) if (bytes_written != obex->tx_data) return FALSE; - g_obex_dump("<", buf, bytes_written); + g_obex_dump(G_OBEX_DEBUG_DATA, "<", buf, bytes_written); obex->tx_sent += bytes_written; obex->tx_data -= bytes_written; @@ -1078,7 +1078,7 @@ read_body: } while (rbytes > 0 && obex->rx_data < obex->rx_pkt_len); done: - g_obex_dump(">", obex->rx_buf, obex->rx_data); + g_obex_dump(G_OBEX_DEBUG_DATA, ">", obex->rx_buf, obex->rx_data); return TRUE; } @@ -1124,7 +1124,7 @@ static gboolean read_packet(GObex *obex, GError **err) return FALSE; } - g_obex_dump(">", obex->rx_buf, obex->rx_data); + g_obex_dump(G_OBEX_DEBUG_DATA, ">", obex->rx_buf, obex->rx_data); return TRUE; fail: @@ -1236,6 +1236,7 @@ static GDebugKey keys[] = { { "header", G_OBEX_DEBUG_HEADER }, { "packet", G_OBEX_DEBUG_PACKET }, { "data", G_OBEX_DEBUG_DATA }, + { "apparam", G_OBEX_DEBUG_APPARAM }, }; GObex *g_obex_new(GIOChannel *io, GObexTransportType transport_type, @@ -1246,9 +1247,11 @@ GObex *g_obex_new(GIOChannel *io, GObexTransportType transport_type, if (gobex_debug == 0) { const char *env = g_getenv("GOBEX_DEBUG"); - if (env) - gobex_debug = g_parse_debug_string(env, keys, 6); - else + + if (env) { + gobex_debug = g_parse_debug_string(env, keys, 7); + g_setenv("G_MESSAGES_DEBUG", "gobex", FALSE); + } else gobex_debug = G_OBEX_DEBUG_NONE; } diff --git a/plugins/bluetooth.c b/plugins/bluetooth.c index 79de9a2..2506aec 100644 --- a/plugins/bluetooth.c +++ b/plugins/bluetooth.c @@ -325,9 +325,8 @@ static void connect_event(GIOChannel *io, GError *err, void *user_data) stream = FALSE; /* Read MTU if io is an L2CAP socket */ - bt_io_get(io, BT_IO_L2CAP, NULL, BT_IO_OPT_OMTU, &omtu, - BT_IO_OPT_IMTU, &imtu, - BT_IO_OPT_INVALID); + bt_io_get(io, NULL, BT_IO_OPT_OMTU, &omtu, BT_IO_OPT_IMTU, &imtu, + BT_IO_OPT_INVALID); done: if (obex_server_new_connection(server, io, omtu, imtu, stream) < 0) @@ -520,7 +519,7 @@ static void confirm_rfcomm(GIOChannel *io, void *user_data) char address[18]; uint8_t channel; - bt_io_get(io, BT_IO_RFCOMM, &err, + bt_io_get(io, &err, BT_IO_OPT_SOURCE, source, BT_IO_OPT_DEST, address, BT_IO_OPT_CHANNEL, &channel, @@ -545,7 +544,7 @@ static void confirm_l2cap(GIOChannel *io, void *user_data) char address[18]; uint16_t psm; - bt_io_get(io, BT_IO_L2CAP, &err, + bt_io_get(io, &err, BT_IO_OPT_SOURCE, source, BT_IO_OPT_DEST, address, BT_IO_OPT_PSM, &psm, @@ -576,7 +575,7 @@ static GSList *start(struct obex_server *server, else sec_level = BT_IO_SEC_LOW; - io = bt_io_listen(BT_IO_RFCOMM, NULL, confirm_rfcomm, + io = bt_io_listen(NULL, confirm_rfcomm, service, NULL, &err, BT_IO_OPT_CHANNEL, service->channel, BT_IO_OPT_SEC_LEVEL, sec_level, @@ -595,7 +594,7 @@ static GSList *start(struct obex_server *server, psm = service->port == OBEX_PORT_RANDOM ? 0 : service->port; - io = bt_io_listen(BT_IO_L2CAP, NULL, confirm_l2cap, + io = bt_io_listen(NULL, confirm_l2cap, service, NULL, &err, BT_IO_OPT_PSM, psm, BT_IO_OPT_MODE, BT_IO_MODE_ERTM, @@ -610,7 +609,7 @@ static GSList *start(struct obex_server *server, service->port = 0; } else { l = g_slist_prepend(l, io); - bt_io_get(io, BT_IO_L2CAP, &err, BT_IO_OPT_PSM, &service->port, + bt_io_get(io, &err, BT_IO_OPT_PSM, &service->port, BT_IO_OPT_INVALID); DBG("listening on psm %d", service->port); } @@ -656,23 +655,10 @@ static void bluetooth_stop(void *data) static int bluetooth_getpeername(GIOChannel *io, char **name) { - int sk = g_io_channel_unix_get_fd(io); GError *gerr = NULL; char address[18]; - int type; - socklen_t len = sizeof(int); - if (getsockopt(sk, SOL_SOCKET, SO_TYPE, &type, &len) < 0) - return -errno; - - if (type == SOCK_STREAM) - bt_io_get(io, BT_IO_RFCOMM, &gerr, - BT_IO_OPT_DEST, address, - BT_IO_OPT_INVALID); - else - bt_io_get(io, BT_IO_L2CAP, &gerr, - BT_IO_OPT_DEST, address, - BT_IO_OPT_INVALID); + bt_io_get(io, &gerr, BT_IO_OPT_DEST, address, BT_IO_OPT_INVALID); if (gerr) { error("%s", gerr->message); diff --git a/plugins/filesystem.c b/plugins/filesystem.c index e664fc5..1132a34 100644 --- a/plugins/filesystem.c +++ b/plugins/filesystem.c @@ -32,7 +32,6 @@ #include #include #include -#include #include #include #include diff --git a/plugins/irmc.c b/plugins/irmc.c index 87f3b6c..c9c3521 100644 --- a/plugins/irmc.c +++ b/plugins/irmc.c @@ -226,7 +226,7 @@ static void *irmc_connect(struct obex_session *os, int *err) param->maxlistcount = 0; /* to count the number of vcards... */ param->filter = 0x200085; /* UID TEL N VERSION */ irmc->params = param; - irmc->request = phonebook_pull("telecom/pb.vcf", irmc->params, + irmc->request = phonebook_pull(PB_CONTACTS, irmc->params, phonebook_size_result, irmc, err); ret = phonebook_pull_read(irmc->request); if (err) @@ -281,7 +281,7 @@ static int irmc_chkput(struct obex_session *os, void *user_data) return -EBADR; } -static void *irmc_open_devinfo(struct irmc_session *irmc, int *err) +static int irmc_open_devinfo(struct irmc_session *irmc) { if (!irmc->buffer) irmc->buffer = g_string_new(""); @@ -301,93 +301,56 @@ static void *irmc_open_devinfo(struct irmc_session *irmc, int *err) "NOTE-TYPE-RX:NONE\r\n", irmc->manu, irmc->model, irmc->sn); - return irmc; + return 0; } -static void *irmc_open_pb(const char *name, struct irmc_session *irmc, - int *err) +static int irmc_open_pb(struct irmc_session *irmc) { - GString *mybuf; int ret; - if (!g_strcmp0(name, ".vcf")) { - /* how can we tell if the vcard count call already finished? */ - irmc->request = phonebook_pull("telecom/pb.vcf", irmc->params, + /* how can we tell if the vcard count call already finished? */ + irmc->request = phonebook_pull(PB_CONTACTS, irmc->params, query_result, irmc, &ret); - if (ret < 0) { - DBG("phonebook_pull failed..."); - goto fail; - } - - ret = phonebook_pull_read(irmc->request); - if (ret < 0) { - DBG("phonebook_pull_read failed..."); - goto fail; - } + if (ret < 0) { + DBG("phonebook_pull failed..."); + return ret; + } - return irmc; + ret = phonebook_pull_read(irmc->request); + if (ret < 0) { + DBG("phonebook_pull_read failed..."); + return ret; } - if (!g_strcmp0(name, "/info.log")) { - mybuf = g_string_new(""); - g_string_printf(mybuf, "Total-Records:%d\r\n" + return 0; +} + +static int irmc_open_info(struct irmc_session *irmc) +{ + if (irmc->buffer == NULL) + irmc->buffer = g_string_new(""); + + g_string_printf(irmc->buffer, "Total-Records:%d\r\n" "Maximum-Records:%d\r\n" "IEL:2\r\n" "DID:%s\r\n", irmc->params->maxlistcount, irmc->params->maxlistcount, irmc->did); - } else if (!strncmp(name, "/luid/", 6)) { - name += 6; - if (!g_strcmp0(name, "cc.log")) { - mybuf = g_string_new(""); - g_string_printf(mybuf, "%d\r\n", - irmc->params->maxlistcount); - } else { - int l = strlen(name); - /* FIXME: - * Reply the same to any *.log so we hopefully force a - * full phonebook dump. - * Is IEL:2 ok? - */ - if (l > 4 && !g_strcmp0(name + l - 4, ".log")) { - DBG("changelog request, force whole book"); - mybuf = g_string_new(""); - g_string_printf(mybuf, "SN:%s\r\n" - "DID:%s\r\n" - "Total-Records:%d\r\n" - "Maximum-Records:%d\r\n" - "*\r\n", - irmc->sn, irmc->did, - irmc->params->maxlistcount, - irmc->params->maxlistcount); - } else { - ret = -EBADR; - goto fail; - } - } - } else { - ret = -EBADR; - goto fail; - } - if (!irmc->buffer) - irmc->buffer = mybuf; - else { - irmc->buffer = g_string_append(irmc->buffer, mybuf->str); - g_string_free(mybuf, TRUE); - } + return 0; +} - return irmc; +static int irmc_open_cc(struct irmc_session *irmc) +{ + if (irmc->buffer == NULL) + irmc->buffer = g_string_new(""); -fail: - if (err) - *err = ret; + g_string_printf(irmc->buffer, "%d\r\n", irmc->params->maxlistcount); - return NULL; + return 0; } -static void *irmc_open_cal(const char *name, struct irmc_session *irmc, - int *err) +static int irmc_open_cal(struct irmc_session *irmc) { /* no suport yet. Just return an empty buffer. cal.vcs */ DBG("unsupported, returning empty buffer"); @@ -395,11 +358,10 @@ static void *irmc_open_cal(const char *name, struct irmc_session *irmc, if (!irmc->buffer) irmc->buffer = g_string_new(""); - return irmc; + return 0; } -static void *irmc_open_nt(const char *name, struct irmc_session *irmc, - int *err) +static int irmc_open_nt(struct irmc_session *irmc) { /* no suport yet. Just return an empty buffer. nt.vnt */ DBG("unsupported, returning empty buffer"); @@ -407,7 +369,25 @@ static void *irmc_open_nt(const char *name, struct irmc_session *irmc, if (!irmc->buffer) irmc->buffer = g_string_new(""); - return irmc; + return 0; +} + +static int irmc_open_luid(struct irmc_session *irmc) +{ + if (irmc->buffer == NULL) + irmc->buffer = g_string_new(""); + + DBG("changelog request, force whole book"); + g_string_printf(irmc->buffer, "SN:%s\r\n" + "DID:%s\r\n" + "Total-Records:%d\r\n" + "Maximum-Records:%d\r\n" + "*\r\n", + irmc->sn, irmc->did, + irmc->params->maxlistcount, + irmc->params->maxlistcount); + + return 0; } static void *irmc_open(const char *name, int oflag, mode_t mode, void *context, @@ -415,7 +395,7 @@ static void *irmc_open(const char *name, int oflag, mode_t mode, void *context, { struct irmc_session *irmc = context; int ret = 0; - const char *p; + char *path; DBG("name %s context %p", name, context); @@ -423,20 +403,39 @@ static void *irmc_open(const char *name, int oflag, mode_t mode, void *context, ret = -EPERM; goto fail; } - if (name == NULL || strncmp(name, "telecom/", 8) != 0) { + + if (name == NULL) { ret = -EBADR; goto fail; } - p = name + 8; - if (!g_strcmp0(p, "devinfo.txt")) - return irmc_open_devinfo(irmc, err); - else if (!strncmp(p, "pb", 2)) - return irmc_open_pb(p+2, irmc, err); - else if (!strncmp(p, "cal", 3)) - return irmc_open_cal(p+3, irmc, err); - else if (!strncmp(p, "nt", 2)) - return irmc_open_nt(p+2, irmc, err); + /* Always contains the absolute path */ + if (g_path_is_absolute(name)) + path = g_strdup(name); + else + path = g_build_filename("/", name, NULL); + + if (g_str_equal(path, PB_DEVINFO)) + ret = irmc_open_devinfo(irmc); + else if (g_str_equal(path, PB_CONTACTS)) + ret = irmc_open_pb(irmc); + else if (g_str_equal(path, PB_INFO_LOG)) + ret = irmc_open_info(irmc); + else if (g_str_equal(path, PB_CC_LOG)) + ret = irmc_open_cc(irmc); + else if (g_str_has_prefix(path, PB_CALENDAR_FOLDER)) + ret = irmc_open_cal(irmc); + else if (g_str_has_prefix(path, PB_NOTES_FOLDER)) + ret = irmc_open_nt(irmc); + else if (g_str_has_prefix(path, PB_LUID_FOLDER)) + ret = irmc_open_luid(irmc); + else + ret = -EBADR; + + g_free(path); + + if (ret == 0) + return irmc; fail: if (err) diff --git a/plugins/mas.c b/plugins/mas.c index 576c206..1b18059 100644 --- a/plugins/mas.c +++ b/plugins/mas.c @@ -32,6 +32,7 @@ #include #include +#include #include "obexd.h" #include "plugin.h" @@ -45,6 +46,9 @@ #include "messages.h" +#define READ_STATUS_REQ 0 +#define DELETE_STATUS_REQ 1 + /* Channel number according to bluez doc/assigned-numbers.txt */ #define MAS_CHANNEL 16 @@ -112,8 +116,8 @@ struct mas_session { gboolean finished; gboolean nth_call; GString *buffer; - map_ap_t *inparams; - map_ap_t *outparams; + GObexApparam *inparams; + GObexApparam *outparams; gboolean ap_sent; }; @@ -130,14 +134,12 @@ static int get_params(struct obex_session *os, struct mas_session *mas) if (size < 0) size = 0; - mas->inparams = map_ap_decode(buffer, size); + mas->inparams = g_obex_apparam_decode(buffer, size); if (mas->inparams == NULL) { DBG("Error when parsing parameters!"); return -EBADR; } - mas->outparams = map_ap_new(); - return 0; } @@ -148,13 +150,19 @@ static void reset_request(struct mas_session *mas) mas->buffer = NULL; } - map_ap_free(mas->inparams); - mas->inparams = NULL; - map_ap_free(mas->outparams); - mas->outparams = NULL; + if (mas->inparams) { + g_obex_apparam_free(mas->inparams); + mas->inparams = NULL; + } + + if (mas->outparams) { + g_obex_apparam_free(mas->outparams); + mas->outparams = NULL; + } mas->nth_call = FALSE; mas->finished = FALSE; + mas->ap_sent = FALSE; } static void mas_clean(struct mas_session *mas) @@ -289,7 +297,7 @@ static void get_messages_listing_cb(void *session, int err, uint16_t size, return; } - map_ap_get_u16(mas->inparams, MAP_AP_MAXLISTCOUNT, &max); + g_obex_apparam_get_uint16(mas->inparams, MAP_AP_MAXLISTCOUNT, &max); if (max == 0) { if (!entry) @@ -390,10 +398,12 @@ static void get_messages_listing_cb(void *session, int err, uint16_t size, proceed: if (!entry) { - map_ap_set_u16(mas->outparams, MAP_AP_MESSAGESLISTINGSIZE, - size); - map_ap_set_u8(mas->outparams, MAP_AP_NEWMESSAGE, - newmsg ? 1 : 0); + mas->outparams = g_obex_apparam_set_uint16(mas->outparams, + MAP_AP_MESSAGESLISTINGSIZE, + size); + mas->outparams = g_obex_apparam_set_uint8(mas->outparams, + MAP_AP_NEWMESSAGE, + newmsg ? 1 : 0); } if (err != -EAGAIN) @@ -435,12 +445,14 @@ static void get_folder_listing_cb(void *session, int err, uint16_t size, return; } - map_ap_get_u16(mas->inparams, MAP_AP_MAXLISTCOUNT, &max); + g_obex_apparam_get_uint16(mas->inparams, MAP_AP_MAXLISTCOUNT, &max); if (max == 0) { if (err != -EAGAIN) - map_ap_set_u16(mas->outparams, - MAP_AP_FOLDERLISTINGSIZE, size); + mas->outparams = g_obex_apparam_set_uint16( + mas->outparams, + MAP_AP_FOLDERLISTINGSIZE, + size); if (!name) mas->finished = TRUE; @@ -477,7 +489,7 @@ proceed: obex_object_set_io_flags(mas, G_IO_IN, err); } -static void update_inbox_cb(void *session, int err, void *user_data) +static void set_status_cb(void *session, int err, void *user_data) { struct mas_session *mas = user_data; @@ -529,8 +541,8 @@ static void *folder_listing_open(const char *name, int oflag, mode_t mode, DBG("name = %s", name); - map_ap_get_u16(mas->inparams, MAP_AP_MAXLISTCOUNT, &max); - map_ap_get_u16(mas->inparams, MAP_AP_STARTOFFSET, &offset); + g_obex_apparam_get_uint16(mas->inparams, MAP_AP_MAXLISTCOUNT, &max); + g_obex_apparam_get_uint16(mas->inparams, MAP_AP_STARTOFFSET, &offset); *err = messages_get_folder_listing(mas->backend_data, name, max, offset, get_folder_listing_cb, mas); @@ -551,6 +563,9 @@ static void *msg_listing_open(const char *name, int oflag, mode_t mode, /* 1024 is the default when there was no MaxListCount sent */ uint16_t max = 1024; uint16_t offset = 0; + /* If MAP client does not specify the subject length, + then subject_len = 0 and subject should be sent unaltered. */ + uint8_t subject_len = 0; DBG(""); @@ -559,28 +574,30 @@ static void *msg_listing_open(const char *name, int oflag, mode_t mode, return NULL; } - map_ap_get_u16(mas->inparams, MAP_AP_MAXLISTCOUNT, &max); - map_ap_get_u16(mas->inparams, MAP_AP_STARTOFFSET, &offset); + g_obex_apparam_get_uint16(mas->inparams, MAP_AP_MAXLISTCOUNT, &max); + g_obex_apparam_get_uint16(mas->inparams, MAP_AP_STARTOFFSET, &offset); + g_obex_apparam_get_uint8(mas->inparams, MAP_AP_SUBJECTLENGTH, + &subject_len); - map_ap_get_u32(mas->inparams, MAP_AP_PARAMETERMASK, + g_obex_apparam_get_uint32(mas->inparams, MAP_AP_PARAMETERMASK, &filter.parameter_mask); - map_ap_get_u8(mas->inparams, MAP_AP_FILTERMESSAGETYPE, + g_obex_apparam_get_uint8(mas->inparams, MAP_AP_FILTERMESSAGETYPE, &filter.type); - filter.period_begin = map_ap_get_string(mas->inparams, + filter.period_begin = g_obex_apparam_get_string(mas->inparams, MAP_AP_FILTERPERIODBEGIN); - filter.period_end = map_ap_get_string(mas->inparams, + filter.period_end = g_obex_apparam_get_string(mas->inparams, MAP_AP_FILTERPERIODEND); - map_ap_get_u8(mas->inparams, MAP_AP_FILTERREADSTATUS, + g_obex_apparam_get_uint8(mas->inparams, MAP_AP_FILTERREADSTATUS, &filter.read_status); - filter.recipient = map_ap_get_string(mas->inparams, + filter.recipient = g_obex_apparam_get_string(mas->inparams, MAP_AP_FILTERRECIPIENT); - filter.originator = map_ap_get_string(mas->inparams, + filter.originator = g_obex_apparam_get_string(mas->inparams, MAP_AP_FILTERORIGINATOR); - map_ap_get_u8(mas->inparams, MAP_AP_FILTERPRIORITY, + g_obex_apparam_get_uint8(mas->inparams, MAP_AP_FILTERPRIORITY, &filter.priority); *err = messages_get_messages_listing(mas->backend_data, name, max, - offset, &filter, + offset, subject_len, &filter, get_messages_listing_cb, mas); mas->buffer = g_string_new(""); @@ -624,24 +641,65 @@ static void *message_update_open(const char *name, int oflag, mode_t mode, DBG(""); - if (oflag != O_WRONLY) { + if (oflag == O_RDONLY) { *err = -EBADR; return NULL; } - *err = messages_update_inbox(mas->backend_data, update_inbox_cb, mas); + *err = messages_update_inbox(mas->backend_data, set_status_cb, mas); if (*err < 0) return NULL; else return mas; } +static void *message_set_status_open(const char *name, int oflag, mode_t mode, + void *driver_data, size_t *size, + int *err) + +{ + struct mas_session *mas = driver_data; + uint8_t indicator; + uint8_t value; + + DBG(""); + + if (oflag == O_RDONLY) { + *err = -EBADR; + return NULL; + } + + if (!g_obex_apparam_get_uint8(mas->inparams, MAP_AP_STATUSINDICATOR, + &indicator)) { + *err = -EBADR; + return NULL; + } + + if (!g_obex_apparam_get_uint8(mas->inparams, MAP_AP_STATUSVALUE, + &value)) { + *err = -EBADR; + return NULL; + } + + if (indicator == READ_STATUS_REQ) + *err = messages_set_read(mas->backend_data, name, value, + set_status_cb, mas); + else if (indicator == DELETE_STATUS_REQ) + *err = messages_set_delete(mas->backend_data, name, value, + set_status_cb, mas); + else + *err = -EBADR; + + if (*err < 0) + return NULL; + + return mas; +} + static ssize_t any_get_next_header(void *object, void *buf, size_t mtu, uint8_t *hi) { struct mas_session *mas = object; - size_t len; - uint8_t *apbuf; DBG(""); @@ -654,18 +712,7 @@ static ssize_t any_get_next_header(void *object, void *buf, size_t mtu, return 0; mas->ap_sent = TRUE; - apbuf = map_ap_encode(mas->outparams, &len); - - if (len > mtu) { - DBG("MTU is to small to fit application parameters header!"); - g_free(apbuf); - - return -EIO; - } - - memcpy(buf, apbuf, len); - - return len; + return g_obex_apparam_encode(mas->outparams, buf, mtu); } static void *any_open(const char *name, int oflag, mode_t mode, @@ -764,6 +811,7 @@ static struct obex_mime_type_driver mime_msg_listing = { .target = MAS_TARGET, .target_size = TARGET_SIZE, .mimetype = "x-bt/MAP-msg-listing", + .get_next_header = any_get_next_header, .open = msg_listing_open, .close = any_close, .read = any_read, @@ -784,7 +832,7 @@ static struct obex_mime_type_driver mime_message_status = { .target = MAS_TARGET, .target_size = TARGET_SIZE, .mimetype = "x-bt/messageStatus", - .open = any_open, + .open = message_set_status_open, .close = any_close, .read = any_read, .write = any_write, diff --git a/plugins/messages-dummy.c b/plugins/messages-dummy.c index 833c31c..4c66f51 100644 --- a/plugins/messages-dummy.c +++ b/plugins/messages-dummy.c @@ -331,6 +331,7 @@ int messages_get_folder_listing(void *s, const char *name, uint16_t max, int messages_get_messages_listing(void *session, const char *name, uint16_t max, uint16_t offset, + uint8_t subject_len, const struct messages_filter *filter, messages_get_messages_listing_cb callback, void *user_data) @@ -346,12 +347,24 @@ int messages_get_message(void *session, const char *handle, return -ENOSYS; } -int messages_update_inbox(void *session, messages_update_inbox_cb callback, +int messages_update_inbox(void *session, messages_status_cb callback, void *user_data) { return -ENOSYS; } +int messages_set_read(void *session, const char *handle, uint8_t value, + messages_status_cb callback, void *user_data) +{ + return -ENOSYS; +} + +int messages_set_delete(void *session, const char *handle, uint8_t value, + messages_status_cb callback, void *user_data) +{ + return -ENOSYS; +} + void messages_abort(void *s) { struct session *session = s; diff --git a/plugins/messages-tracker.c b/plugins/messages-tracker.c index fb45210..60f3a80 100644 --- a/plugins/messages-tracker.c +++ b/plugins/messages-tracker.c @@ -303,9 +303,9 @@ int messages_get_folder_listing(void *s, const char *name, return 0; } -int messages_get_messages_listing(void *session, - const char *name, +int messages_get_messages_listing(void *session, const char *name, uint16_t max, uint16_t offset, + uint8_t subject_len, const struct messages_filter *filter, messages_get_messages_listing_cb callback, void *user_data) @@ -321,12 +321,25 @@ int messages_get_message(void *session, const char *handle, return -ENOSYS; } -int messages_update_inbox(void *session, messages_update_inbox_cb callback, +int messages_update_inbox(void *session, messages_status_cb callback, void *user_data) { return -ENOSYS; } +int messages_set_read(void *session, const char *handle, uint8_t value, + messages_status_cb callback, void *user_data) +{ + return -ENOSYS; +} + +int messages_set_delete(void *session, const char *handle, uint8_t value, + messages_status_cb callback, + void *user_data) +{ + return -ENOSYS; +} + void messages_abort(void *session) { } diff --git a/plugins/messages.h b/plugins/messages.h index 83e087a..00a16b1 100644 --- a/plugins/messages.h +++ b/plugins/messages.h @@ -215,6 +215,8 @@ int messages_get_folder_listing(void *session, const char *name, uint16_t max, * session: Backend session. * name: Optional subdirectory name. * max: Maximum number of entries to retrieve. + * offset: Offset of the first entry. + * subject_len: Maximum string length of the "subject" parameter in the entries. * filter: Filter to apply on returned message listing. * size: Total size of listing to be returned. * newmsg: Indicates presence of unread messages. @@ -229,6 +231,7 @@ typedef void (*messages_get_messages_listing_cb)(void *session, int err, int messages_get_messages_listing(void *session, const char *name, uint16_t max, uint16_t offset, + uint8_t subject_len, const struct messages_filter *filter, messages_get_messages_listing_cb callback, void *user_data); @@ -265,17 +268,39 @@ int messages_get_message(void *session, const char *handle, messages_get_message_cb callback, void *user_data); +typedef void (*messages_status_cb)(void *session, int err, void *user_data); + /* Informs Message Server to Update Inbox via network. * * session: Backend session. * user_data: User data if any to be sent. * Callback shall be called for every update inbox request received from MCE. */ -typedef void (*messages_update_inbox_cb)(void *session, int err, +int messages_update_inbox(void *session, messages_status_cb callback, void *user_data); +/* Informs Message Server to modify read status of a given message. + * + * session: Backend session. + * handle: Unique identifier to the message. + * value: Indicates the new value of the read status for a given message. + * Callback shall be called for every read status update request + * recieved from MCE. + * user_data: User data if any to be sent. + */ +int messages_set_read(void *session, const char *handle, uint8_t value, + messages_status_cb callback, void *user_data); -int messages_update_inbox(void *session, messages_update_inbox_cb callback, - void *user_data); +/* Informs Message Server to modify delete status of a given message. + * + * session: Backend session. + * handle: Unique identifier to the message. + * value: Indicates the new value of the delete status for a given message. + * Callback shall be called for every delete status update request + * recieved from MCE. + * user_data: User data if any to be sent. + */ +int messages_set_delete(void *session, const char *handle, uint8_t value, + messages_status_cb callback, void *user_data); /* Aborts currently pending request. * diff --git a/plugins/pbap.c b/plugins/pbap.c index c7e792a..c36fab2 100644 --- a/plugins/pbap.c +++ b/plugins/pbap.c @@ -39,6 +39,7 @@ #include #include +#include #include "obexd.h" #include "plugin.h" @@ -64,16 +65,6 @@ #define PHONEBOOKSIZE_TAG 0X08 #define NEWMISSEDCALLS_TAG 0X09 -/* The following length is in the unit of byte */ -#define ORDER_LEN 1 -#define SEARCHATTRIB_LEN 1 -#define MAXLISTCOUNT_LEN 2 -#define LISTSTARTOFFSET_LEN 2 -#define FILTER_LEN 8 -#define FORMAT_LEN 1 -#define PHONEBOOKSIZE_LEN 2 -#define NEWMISSEDCALLS_LEN 1 - #define PBAP_CHANNEL 15 #define PBAP_RECORD " \ @@ -117,12 +108,6 @@ \ " -struct aparam_header { - uint8_t tag; - uint8_t len; - uint8_t val[0]; -} __attribute__ ((packed)); - struct cache { gboolean valid; uint32_t index; @@ -147,7 +132,7 @@ struct pbap_session { struct pbap_object { GString *buffer; - GByteArray *aparams; + GObexApparam *apparam; gboolean firstpacket; gboolean lastpart; struct pbap_session *session; @@ -229,33 +214,6 @@ static void cache_clear(struct cache *cache) cache->entries = NULL; } -static GByteArray *append_aparam_header(GByteArray *buf, uint8_t tag, - const void *val) -{ - /* largest aparam is for phonebooksize (4 bytes) */ - uint8_t aparam[sizeof(struct aparam_header) + PHONEBOOKSIZE_LEN]; - struct aparam_header *hdr = (struct aparam_header *) aparam; - - switch (tag) { - case PHONEBOOKSIZE_TAG: - hdr->tag = PHONEBOOKSIZE_TAG; - hdr->len = PHONEBOOKSIZE_LEN; - memcpy(hdr->val, val, PHONEBOOKSIZE_LEN); - - return g_byte_array_append(buf, aparam, - sizeof(struct aparam_header) + PHONEBOOKSIZE_LEN); - case NEWMISSEDCALLS_TAG: - hdr->tag = NEWMISSEDCALLS_TAG; - hdr->len = NEWMISSEDCALLS_LEN; - memcpy(hdr->val, val, NEWMISSEDCALLS_LEN); - - return g_byte_array_append(buf, aparam, - sizeof(struct aparam_header) + NEWMISSEDCALLS_LEN); - default: - return buf; - } -} - static void phonebook_size_result(const char *buffer, size_t bufsize, int vcards, int missed, gboolean lastpart, void *user_data) @@ -275,15 +233,16 @@ static void phonebook_size_result(const char *buffer, size_t bufsize, phonebooksize = htons(vcards); - pbap->obj->aparams = g_byte_array_new(); - pbap->obj->aparams = append_aparam_header(pbap->obj->aparams, - PHONEBOOKSIZE_TAG, &phonebooksize); + pbap->obj->apparam = g_obex_apparam_set_uint16(NULL, PHONEBOOKSIZE_TAG, + phonebooksize); if (missed > 0) { DBG("missed %d", missed); - pbap->obj->aparams = append_aparam_header(pbap->obj->aparams, - NEWMISSEDCALLS_TAG, &missed); + pbap->obj->apparam = g_obex_apparam_set_uint16( + pbap->obj->apparam, + NEWMISSEDCALLS_TAG, + missed); } obex_object_set_io_flags(pbap->obj, G_IO_IN, 0); @@ -319,9 +278,10 @@ static void query_result(const char *buffer, size_t bufsize, int vcards, pbap->obj->firstpacket = TRUE; - pbap->obj->aparams = g_byte_array_new(); - pbap->obj->aparams = append_aparam_header(pbap->obj->aparams, - NEWMISSEDCALLS_TAG, &missed); + pbap->obj->apparam = g_obex_apparam_set_uint16( + pbap->obj->apparam, + NEWMISSEDCALLS_TAG, + missed); } obex_object_set_io_flags(pbap->obj, G_IO_IN, 0); @@ -450,9 +410,10 @@ static int generate_response(void *user_data) /* Ignore all other parameter and return PhoneBookSize */ uint16_t size = htons(g_slist_length(pbap->cache.entries)); - pbap->obj->aparams = g_byte_array_new(); - pbap->obj->aparams = append_aparam_header(pbap->obj->aparams, - PHONEBOOKSIZE_TAG, &size); + pbap->obj->apparam = g_obex_apparam_set_uint16( + pbap->obj->apparam, + PHONEBOOKSIZE_TAG, + size); return 0; } @@ -527,85 +488,34 @@ static void cache_entry_done(void *user_data) static struct apparam_field *parse_aparam(const uint8_t *buffer, uint32_t hlen) { + GObexApparam *apparam; struct apparam_field *param; - struct aparam_header *hdr; - uint64_t val64; - uint32_t len = 0; - uint16_t val16; - - param = g_new0(struct apparam_field, 1); - - while (len < hlen) { - hdr = (void *) buffer + len; - - switch (hdr->tag) { - case ORDER_TAG: - if (hdr->len != ORDER_LEN) - goto failed; - - param->order = hdr->val[0]; - break; - - case SEARCHATTRIB_TAG: - if (hdr->len != SEARCHATTRIB_LEN) - goto failed; - - param->searchattrib = hdr->val[0]; - break; - case SEARCHVALUE_TAG: - if (hdr->len == 0) - goto failed; - - param->searchval = g_try_malloc0(hdr->len + 1); - if (param->searchval) - memcpy(param->searchval, hdr->val, hdr->len); - break; - case FILTER_TAG: - if (hdr->len != FILTER_LEN) - goto failed; - - memcpy(&val64, hdr->val, sizeof(val64)); - param->filter = GUINT64_FROM_BE(val64); - - break; - case FORMAT_TAG: - if (hdr->len != FORMAT_LEN) - goto failed; - param->format = hdr->val[0]; - break; - case MAXLISTCOUNT_TAG: - if (hdr->len != MAXLISTCOUNT_LEN) - goto failed; + apparam = g_obex_apparam_decode(buffer, hlen); + if (apparam == NULL) + return NULL; - memcpy(&val16, hdr->val, sizeof(val16)); - param->maxlistcount = GUINT16_FROM_BE(val16); - break; - case LISTSTARTOFFSET_TAG: - if (hdr->len != LISTSTARTOFFSET_LEN) - goto failed; - - memcpy(&val16, hdr->val, sizeof(val16)); - param->liststartoffset = GUINT16_FROM_BE(val16); - break; - default: - goto failed; - } + param = g_new0(struct apparam_field, 1); - len += hdr->len + sizeof(struct aparam_header); - } + g_obex_apparam_get_uint8(apparam, ORDER_TAG, ¶m->order); + g_obex_apparam_get_uint8(apparam, SEARCHATTRIB_TAG, + ¶m->searchattrib); + g_obex_apparam_get_uint8(apparam, FORMAT_TAG, ¶m->format); + g_obex_apparam_get_uint16(apparam, MAXLISTCOUNT_TAG, + ¶m->maxlistcount); + g_obex_apparam_get_uint16(apparam, LISTSTARTOFFSET_TAG, + ¶m->liststartoffset); + g_obex_apparam_get_uint64(apparam, FILTER_TAG, ¶m->filter); + param->searchval = g_obex_apparam_get_string(apparam, SEARCHVALUE_TAG); DBG("o %x sa %x sv %s fil %" G_GINT64_MODIFIER "x for %x max %x off %x", param->order, param->searchattrib, param->searchval, param->filter, param->format, param->maxlistcount, param->liststartoffset); - return param; - -failed: - g_free(param); + g_obex_apparam_free(apparam); - return NULL; + return param; } static void *pbap_connect(struct obex_session *os, int *err) @@ -839,8 +749,8 @@ static int vobject_close(void *object) if (obj->buffer) g_string_free(obj->buffer, TRUE); - if (obj->aparams) - g_byte_array_free(obj->aparams, TRUE); + if (obj->apparam) + g_obex_apparam_free(obj->apparam); if (obj->request) phonebook_req_finalize(obj->request); @@ -952,27 +862,13 @@ fail: return NULL; } -static ssize_t array_read(GByteArray *array, void *buf, size_t count) -{ - ssize_t len; - - if (array->len == 0) - return 0; - - len = MIN(array->len, count); - memcpy(buf, array->data, len); - g_byte_array_remove_range(array, 0, len); - - return len; -} - static ssize_t vobject_pull_get_next_header(void *object, void *buf, size_t mtu, uint8_t *hi) { struct pbap_object *obj = object; struct pbap_session *pbap = obj->session; - if (!obj->buffer && !obj->aparams) + if (!obj->buffer && !obj->apparam) return -EAGAIN; *hi = G_OBEX_HDR_APPARAM; @@ -980,7 +876,7 @@ static ssize_t vobject_pull_get_next_header(void *object, void *buf, size_t mtu, if (pbap->params->maxlistcount == 0 || obj->firstpacket) { obj->firstpacket = FALSE; - return array_read(obj->aparams, buf, mtu); + return g_obex_apparam_encode(obj->apparam, buf, mtu); } return 0; @@ -1031,7 +927,7 @@ static ssize_t vobject_list_get_next_header(void *object, void *buf, size_t mtu, *hi = G_OBEX_HDR_APPARAM; if (pbap->params->maxlistcount == 0) - return array_read(obj->aparams, buf, mtu); + return g_obex_apparam_encode(obj->apparam, buf, mtu); return 0; } diff --git a/plugins/phonebook-ebook.c b/plugins/phonebook-ebook.c index a1f06b5..59a4eac 100644 --- a/plugins/phonebook-ebook.c +++ b/plugins/phonebook-ebook.c @@ -459,7 +459,7 @@ char *phonebook_set_folder(const char *current_folder, root = (g_strcmp0("/", current_folder) == 0); child = (new_folder && strlen(new_folder) != 0); - /* Evolution back-end will support telecom/pb folder only */ + /* Evolution back-end will support /telecom/pb folder only */ switch (flags) { case 0x02: @@ -471,8 +471,8 @@ char *phonebook_set_folder(const char *current_folder, /* Go down 1 level */ fullname = g_build_filename(current_folder, new_folder, NULL); - if (strcmp("/telecom", fullname) != 0 && - strcmp("/telecom/pb", fullname) != 0) { + if (strcmp(PB_TELECOM_FOLDER, fullname) != 0 && + strcmp(PB_CONTACTS_FOLDER, fullname) != 0) { g_free(fullname); fullname = NULL; ret = -ENOENT; @@ -511,8 +511,8 @@ char *phonebook_set_folder(const char *current_folder, } fullname = g_build_filename(base, new_folder, NULL); - if (strcmp(fullname, "/telecom") != 0 && - strcmp(fullname, "/telecom/pb") != 0) { + if (strcmp(fullname, PB_TELECOM_FOLDER) != 0 && + strcmp(fullname, PB_CONTACTS_FOLDER) != 0) { g_free(fullname); fullname = NULL; ret = -ENOENT; @@ -548,7 +548,7 @@ void *phonebook_pull(const char *name, const struct apparam_field *params, { struct query_context *data; - if (g_strcmp0("/telecom/pb.vcf", name) != 0) { + if (g_strcmp0(PB_CONTACTS, name) != 0) { if (err) *err = -ENOENT; @@ -638,7 +638,7 @@ void *phonebook_create_cache(const char *name, phonebook_entry_cb entry_cb, EVCardAttribute *attrib; char *uid, *tel, *cname; - if (g_strcmp0("/telecom/pb", name) != 0) { + if (g_strcmp0(PB_CONTACTS_FOLDER, name) != 0) { if (err) *err = -ENOENT; diff --git a/plugins/phonebook-tracker.c b/plugins/phonebook-tracker.c index 96635c4..2fd7ba1 100644 --- a/plugins/phonebook-tracker.c +++ b/plugins/phonebook-tracker.c @@ -513,15 +513,15 @@ static TrackerSparqlConnection *connection = NULL; static const char *name2query(const char *name) { - if (g_str_equal(name, "/telecom/pb.vcf")) + if (g_str_equal(name, PB_CONTACTS)) return CONTACTS_QUERY_ALL; - else if (g_str_equal(name, "/telecom/ich.vcf")) + else if (g_str_equal(name, PB_CALLS_INCOMING)) return INCOMING_CALLS_QUERY; - else if (g_str_equal(name, "/telecom/och.vcf")) + else if (g_str_equal(name, PB_CALLS_OUTGOING)) return OUTGOING_CALLS_QUERY; - else if (g_str_equal(name, "/telecom/mch.vcf")) + else if (g_str_equal(name, PB_CALLS_MISSED)) return MISSED_CALLS_QUERY; - else if (g_str_equal(name, "/telecom/cch.vcf")) + else if (g_str_equal(name, PB_CALLS_COMBINED)) return COMBINED_CALLS_QUERY; return NULL; @@ -529,15 +529,15 @@ static const char *name2query(const char *name) static const char *name2count_query(const char *name) { - if (g_str_equal(name, "/telecom/pb.vcf")) + if (g_str_equal(name, PB_CONTACTS)) return CONTACTS_COUNT_QUERY; - else if (g_str_equal(name, "/telecom/ich.vcf")) + else if (g_str_equal(name, PB_CALLS_INCOMING)) return INCOMING_CALLS_COUNT_QUERY; - else if (g_str_equal(name, "/telecom/och.vcf")) + else if (g_str_equal(name, PB_CALLS_OUTGOING)) return OUTGOING_CALLS_COUNT_QUERY; - else if (g_str_equal(name, "/telecom/mch.vcf")) + else if (g_str_equal(name, PB_CALLS_MISSED)) return MISSED_CALLS_COUNT_QUERY; - else if (g_str_equal(name, "/telecom/cch.vcf")) + else if (g_str_equal(name, PB_CALLS_COMBINED)) return COMBINED_CALLS_COUNT_QUERY; return NULL; @@ -550,17 +550,17 @@ static gboolean folder_is_valid(const char *folder) if (g_str_equal(folder, "/")) return TRUE; - else if (g_str_equal(folder, "/telecom")) + else if (g_str_equal(folder, PB_TELECOM_FOLDER)) return TRUE; - else if (g_str_equal(folder, "/telecom/pb")) + else if (g_str_equal(folder, PB_CONTACTS_FOLDER)) return TRUE; - else if (g_str_equal(folder, "/telecom/ich")) + else if (g_str_equal(folder, PB_CALLS_INCOMING_FOLDER)) return TRUE; - else if (g_str_equal(folder, "/telecom/och")) + else if (g_str_equal(folder, PB_CALLS_OUTGOING_FOLDER)) return TRUE; - else if (g_str_equal(folder, "/telecom/mch")) + else if (g_str_equal(folder, PB_CALLS_MISSED_FOLDER)) return TRUE; - else if (g_str_equal(folder, "/telecom/cch")) + else if (g_str_equal(folder, PB_CALLS_COMBINED_FOLDER)) return TRUE; return FALSE; @@ -568,15 +568,15 @@ static gboolean folder_is_valid(const char *folder) static const char *folder2query(const char *folder) { - if (g_str_equal(folder, "/telecom/pb")) + if (g_str_equal(folder, PB_CONTACTS_FOLDER)) return CONTACTS_QUERY_ALL_LIST; - else if (g_str_equal(folder, "/telecom/ich")) + else if (g_str_equal(folder, PB_CALLS_INCOMING_FOLDER)) return INCOMING_CALLS_LIST; - else if (g_str_equal(folder, "/telecom/och")) + else if (g_str_equal(folder, PB_CALLS_OUTGOING_FOLDER)) return OUTGOING_CALLS_LIST; - else if (g_str_equal(folder, "/telecom/mch")) + else if (g_str_equal(folder, PB_CALLS_MISSED_FOLDER)) return MISSED_CALLS_LIST; - else if (g_str_equal(folder, "/telecom/cch")) + else if (g_str_equal(folder, PB_CALLS_COMBINED_FOLDER)) return COMBINED_CALLS_LIST; return NULL; @@ -1541,11 +1541,11 @@ static int pull_newmissedcalls(const char **reply, int num_fields, } if (data->params->maxlistcount == 0) { - query = name2count_query("/telecom/mch.vcf"); + query = name2count_query(PB_CALLS_MISSED); col_amount = COUNT_QUERY_COL_AMOUNT; pull_cb = pull_contacts_size; } else { - query = name2query("/telecom/mch.vcf"); + query = name2query(PB_CALLS_MISSED); col_amount = PULL_QUERY_COL_AMOUNT; pull_cb = pull_contacts; } @@ -1613,7 +1613,7 @@ int phonebook_pull_read(void *request) data->newmissedcalls = 0; - if (g_strcmp0(data->req_name, "/telecom/mch.vcf") == 0 && + if (g_strcmp0(data->req_name, PB_CALLS_MISSED) == 0 && data->tracker_index == 0) { /* new missed calls amount should be counted only once - it * will be done during generating first part of results of diff --git a/plugins/phonebook.h b/plugins/phonebook.h index 00abc08..441cff2 100644 --- a/plugins/phonebook.h +++ b/plugins/phonebook.h @@ -29,6 +29,26 @@ #define VCARD_LISTING_ELEMENT "" EOL #define VCARD_LISTING_END "" +#define PB_TELECOM_FOLDER "/telecom" +#define PB_CONTACTS_FOLDER "/telecom/pb" +#define PB_CALENDAR_FOLDER "/telecom/cal" +#define PB_NOTES_FOLDER "/telecom/nt" +#define PB_CALLS_COMBINED_FOLDER "/telecom/cch" +#define PB_CALLS_INCOMING_FOLDER "/telecom/ich" +#define PB_CALLS_MISSED_FOLDER "/telecom/mch" +#define PB_CALLS_OUTGOING_FOLDER "/telecom/och" +#define PB_LUID_FOLDER "/telecom/luid" + +#define PB_CONTACTS "/telecom/pb.vcf" +#define PB_CALLS_COMBINED "/telecom/cch.vcf" +#define PB_CALLS_INCOMING "/telecom/ich.vcf" +#define PB_CALLS_MISSED "/telecom/mch.vcf" +#define PB_CALLS_OUTGOING "/telecom/och.vcf" +#define PB_DEVINFO "/telecom/devinfo.txt" +#define PB_INFO_LOG "/telecom/pb/info.log" +#define PB_CC_LOG "/telecom/pb/luid/cc.log" + + struct apparam_field { /* list and pull attributes */ uint16_t maxlistcount; @@ -41,7 +61,7 @@ struct apparam_field { /* list attributes only */ uint8_t order; uint8_t searchattrib; - uint8_t *searchval; + char *searchval; }; /* diff --git a/src/map_ap.c b/src/map_ap.c deleted file mode 100644 index 6efc484..0000000 --- a/src/map_ap.c +++ /dev/null @@ -1,466 +0,0 @@ -/* - * - * OBEX Server - * - * Copyright (C) 2010-2011 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 - * - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include - -#include "log.h" - -#include "map_ap.h" - -enum ap_type { - APT_UINT8, - APT_UINT16, - APT_UINT32, - APT_STR -}; - -/* NOTE: ap_defs array has to be kept in sync with map_ap_tag. */ -static const struct ap_def { - const char *name; - enum ap_type type; -} ap_defs[] = { - { "MAXLISTCOUNT", APT_UINT16 }, - { "STARTOFFSET", APT_UINT16 }, - { "FILTERMESSAGETYPE", APT_UINT8 }, - { "FILTERPERIODBEGIN", APT_STR }, - { "FILTERPERIODEND", APT_STR }, - { "FILTERREADSTATUS", APT_UINT8 }, - { "FILTERRECIPIENT", APT_STR }, - { "FILTERORIGINATOR", APT_STR }, - { "FILTERPRIORITY", APT_UINT8 }, - { "ATTACHMENT", APT_UINT8 }, - { "TRANSPARENT", APT_UINT8 }, - { "RETRY", APT_UINT8 }, - { "NEWMESSAGE", APT_UINT8 }, - { "NOTIFICATIONSTATUS", APT_UINT8 }, - { "MASINSTANCEID", APT_UINT8 }, - { "PARAMETERMASK", APT_UINT32 }, - { "FOLDERLISTINGSIZE", APT_UINT16 }, - { "MESSAGESLISTINGSIZE", APT_UINT16 }, - { "SUBJECTLENGTH", APT_UINT8 }, - { "CHARSET", APT_UINT8 }, - { "FRACTIONREQUEST", APT_UINT8 }, - { "FRACTIONDELIVER", APT_UINT8 }, - { "STATUSINDICATOR", APT_UINT8 }, - { "STATUSVALUE", APT_UINT8 }, - { "MSETIME", APT_STR }, -}; - -struct ap_entry { - enum map_ap_tag tag; - union { - uint32_t u32; - uint16_t u16; - uint8_t u8; - char *str; - } val; -}; - -/* This comes from OBEX specs */ -struct obex_ap_header { - uint8_t tag; - uint8_t len; - uint8_t val[0]; -} __attribute__ ((packed)); - -static int find_ap_def_offset(uint8_t tag) -{ - if (tag == 0 || tag > G_N_ELEMENTS(ap_defs)) - return -1; - - return tag - 1; -} - -static void ap_entry_dump(gpointer tag, gpointer val, gpointer user_data) -{ - struct ap_entry *entry = val; - int offset; - - offset = find_ap_def_offset(GPOINTER_TO_INT(tag)); - - switch (ap_defs[offset].type) { - case APT_UINT8: - DBG("%-30s %08x", ap_defs[offset].name, entry->val.u8); - break; - case APT_UINT16: - DBG("%-30s %08x", ap_defs[offset].name, entry->val.u16); - break; - case APT_UINT32: - DBG("%-30s %08x", ap_defs[offset].name, entry->val.u32); - break; - case APT_STR: - DBG("%-30s %s", ap_defs[offset].name, entry->val.str); - break; - } -} - -static void ap_entry_free(gpointer val) -{ - struct ap_entry *entry = val; - int offset; - - offset = find_ap_def_offset(entry->tag); - - if (offset >= 0 && ap_defs[offset].type == APT_STR) - g_free(entry->val.str); - - g_free(entry); -} - -map_ap_t *map_ap_new(void) -{ - return g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, - ap_entry_free); -} - -void map_ap_free(map_ap_t *ap) -{ - if (!ap) - return; - - g_hash_table_destroy(ap); -} - -static void ap_decode_u8(map_ap_t *ap, const struct obex_ap_header *hdr) -{ - if (hdr->len != 1) { - DBG("Value of tag %u is %u byte(s) long instead of expected " - "1 byte - skipped!", hdr->tag, hdr->len); - return; - } - - map_ap_set_u8(ap, hdr->tag, hdr->val[0]); -} - -static void ap_decode_u16(map_ap_t *ap, const struct obex_ap_header *hdr) -{ - uint16_t val; - - if (hdr->len != 2) { - DBG("Value of tag %u is %u byte(s) long instead of expected " - "2 bytes - skipped!", hdr->tag, hdr->len); - return; - } - - memcpy(&val, hdr->val, sizeof(val)); - map_ap_set_u16(ap, hdr->tag, GUINT16_FROM_BE(val)); -} - -static void ap_decode_u32(map_ap_t *ap, const struct obex_ap_header *hdr) -{ - uint32_t val; - - if (hdr->len != 4) { - DBG("Value of tag %u is %u byte(s) long instead of expected " - "4 bytes - skipped!", hdr->tag, hdr->len); - return; - } - - memcpy(&val, hdr->val, sizeof(val)); - map_ap_set_u32(ap, hdr->tag, GUINT32_FROM_BE(val)); -} - -static void ap_decode_str(map_ap_t *ap, const struct obex_ap_header *hdr) -{ - char *val = g_malloc0(hdr->len + 1); - - memcpy(val, hdr->val, hdr->len); - map_ap_set_string(ap, hdr->tag, val); - - g_free(val); -} - -map_ap_t *map_ap_decode(const uint8_t *buffer, size_t length) -{ - map_ap_t *ap; - struct obex_ap_header *hdr; - uint32_t done; - int offset; - - ap = map_ap_new(); - if (!ap) - return NULL; - - for (done = 0; done < length; done += hdr->len + sizeof(*hdr)) { - hdr = (struct obex_ap_header *)(buffer + done); - - offset = find_ap_def_offset(hdr->tag); - - if (offset < 0) { - DBG("Unknown tag %u (length %u) - skipped.", - hdr->tag, hdr->len); - continue; - } - - switch (ap_defs[offset].type) { - case APT_UINT8: - ap_decode_u8(ap, hdr); - break; - case APT_UINT16: - ap_decode_u16(ap, hdr); - break; - case APT_UINT32: - ap_decode_u32(ap, hdr); - break; - case APT_STR: - ap_decode_str(ap, hdr); - break; - } - } - - g_hash_table_foreach(ap, ap_entry_dump, NULL); - - return ap; -} - -static void ap_encode_u8(GByteArray *buf, struct ap_entry *entry) -{ - struct obex_ap_header *hdr; - - hdr = (struct obex_ap_header *) buf->data + buf->len; - g_byte_array_set_size(buf, buf->len + sizeof(*hdr) + 1); - - hdr->tag = entry->tag; - hdr->len = 1; - hdr->val[0] = entry->val.u8; -} - -static void ap_encode_u16(GByteArray *buf, struct ap_entry *entry) -{ - struct obex_ap_header *hdr; - uint16_t val; - - hdr = (struct obex_ap_header *) buf->data + buf->len; - - g_byte_array_set_size(buf, buf->len + sizeof(*hdr) + 2); - - hdr->tag = entry->tag; - hdr->len = 2; - - val = GUINT16_TO_BE(entry->val.u16); - memcpy(hdr->val, &val, sizeof(val)); -} - -static void ap_encode_u32(GByteArray *buf, struct ap_entry *entry) -{ - uint32_t val; - struct obex_ap_header *hdr; - - hdr = (struct obex_ap_header *) buf->data + buf->len; - g_byte_array_set_size(buf, buf->len + sizeof(*hdr) + 4); - - hdr->tag = entry->tag; - hdr->len = 4; - - val = GUINT32_TO_BE(entry->val.u16); - memcpy(hdr->val, &val, sizeof(val)); -} - -static void ap_encode_str(GByteArray *buf, struct ap_entry *entry) -{ - size_t len; - struct obex_ap_header *hdr; - - hdr = (struct obex_ap_header *) buf->data + buf->len; - len = strlen(entry->val.str); - g_byte_array_set_size(buf, buf->len + sizeof(*hdr) + len); - - hdr->tag = entry->tag; - hdr->len = len; - - memcpy(hdr->val, entry->val.str, len); -} - -uint8_t *map_ap_encode(map_ap_t *ap, size_t *length) -{ - GByteArray *buf; - GHashTableIter iter; - gpointer key, value; - struct ap_entry *entry; - int offset; - - buf = g_byte_array_new(); - g_hash_table_iter_init(&iter, ap); - - while (g_hash_table_iter_next(&iter, &key, &value)) { - entry = (struct ap_entry *) value; - offset = find_ap_def_offset(entry->tag); - - switch (ap_defs[offset].type) { - case APT_UINT8: - ap_encode_u8(buf, entry); - break; - case APT_UINT16: - ap_encode_u16(buf, entry); - break; - case APT_UINT32: - ap_encode_u32(buf, entry); - break; - case APT_STR: - ap_encode_str(buf, entry); - break; - } - } - - *length = buf->len; - - return g_byte_array_free(buf, FALSE); -} - -gboolean map_ap_get_u8(map_ap_t *ap, enum map_ap_tag tag, uint8_t *val) -{ - struct ap_entry *entry; - int offset = find_ap_def_offset(tag); - - if (offset < 0 || ap_defs[offset].type != APT_UINT8) - return FALSE; - - entry = g_hash_table_lookup(ap, GINT_TO_POINTER(tag)); - if (entry == NULL) - return FALSE; - - *val = entry->val.u8; - - return TRUE; -} - -gboolean map_ap_get_u16(map_ap_t *ap, enum map_ap_tag tag, uint16_t *val) -{ - struct ap_entry *entry; - int offset = find_ap_def_offset(tag); - - if (offset < 0 || ap_defs[offset].type != APT_UINT16) - return FALSE; - - entry = g_hash_table_lookup(ap, GINT_TO_POINTER(tag)); - if (entry == NULL) - return FALSE; - - *val = entry->val.u16; - - return TRUE; -} - -gboolean map_ap_get_u32(map_ap_t *ap, enum map_ap_tag tag, uint32_t *val) -{ - struct ap_entry *entry; - int offset = find_ap_def_offset(tag); - - if (offset < 0 || ap_defs[offset].type != APT_UINT32) - return FALSE; - - entry = g_hash_table_lookup(ap, GINT_TO_POINTER(tag)); - if (entry == NULL) - return FALSE; - - *val = entry->val.u32; - - return TRUE; -} - -const char *map_ap_get_string(map_ap_t *ap, enum map_ap_tag tag) -{ - struct ap_entry *entry; - int offset = find_ap_def_offset(tag); - - if (offset < 0 || ap_defs[offset].type != APT_STR) - return NULL; - - entry = g_hash_table_lookup(ap, GINT_TO_POINTER(tag)); - if (entry == NULL) - return NULL; - - return entry->val.str; -} - -gboolean map_ap_set_u8(map_ap_t *ap, enum map_ap_tag tag, uint8_t val) -{ - struct ap_entry *entry; - int offset = find_ap_def_offset(tag); - - if (offset < 0 || ap_defs[offset].type != APT_UINT8) - return FALSE; - - entry = g_new0(struct ap_entry, 1); - entry->tag = tag; - entry->val.u8 = val; - - g_hash_table_insert(ap, GINT_TO_POINTER(tag), entry); - - return TRUE; -} - -gboolean map_ap_set_u16(map_ap_t *ap, enum map_ap_tag tag, uint16_t val) -{ - struct ap_entry *entry; - int offset = find_ap_def_offset(tag); - - if (offset < 0 || ap_defs[offset].type != APT_UINT16) - return FALSE; - - entry = g_new0(struct ap_entry, 1); - entry->tag = tag; - entry->val.u16 = val; - - g_hash_table_insert(ap, GINT_TO_POINTER(tag), entry); - - return TRUE; -} - -gboolean map_ap_set_u32(map_ap_t *ap, enum map_ap_tag tag, uint32_t val) -{ - struct ap_entry *entry; - int offset = find_ap_def_offset(tag); - - if (offset < 0 || ap_defs[offset].type != APT_UINT32) - return FALSE; - - entry = g_new0(struct ap_entry, 1); - entry->tag = tag; - entry->val.u32 = val; - - g_hash_table_insert(ap, GINT_TO_POINTER(tag), entry); - - return TRUE; -} - -gboolean map_ap_set_string(map_ap_t *ap, enum map_ap_tag tag, const char *val) -{ - struct ap_entry *entry; - int offset = find_ap_def_offset(tag); - - if (offset < 0 || ap_defs[offset].type != APT_STR) - return FALSE; - - entry = g_new0(struct ap_entry, 1); - entry->tag = tag; - entry->val.str = g_strdup(val); - - g_hash_table_insert(ap, GINT_TO_POINTER(tag), entry); - - return TRUE; -} diff --git a/src/map_ap.h b/src/map_ap.h index 24254af..da108fe 100644 --- a/src/map_ap.h +++ b/src/map_ap.h @@ -21,9 +21,6 @@ * */ -#include -#include - /* List of OBEX application parameters tags as per MAP specification. */ enum map_ap_tag { MAP_AP_MAXLISTCOUNT = 0x01, /* uint16_t */ @@ -52,63 +49,3 @@ enum map_ap_tag { MAP_AP_STATUSVALUE = 0x18, /* uint8_t */ MAP_AP_MSETIME = 0x19, /* char * */ }; - -/* Data type representing MAP application parameters. Consider opaque. */ -typedef GHashTable map_ap_t; - -/* Creates a new empty MAP application parameters object. */ -map_ap_t *map_ap_new(void); - -/* Frees all the memory used by MAP application parameters object. */ -void map_ap_free(map_ap_t *ap); - -/* Parses given buffer that is a payload of OBEX application parameter header - * with a given length. Returned value can be used in calls to map_ap_get_*() - * and map_ap_set_*(). It has to be freed using map_ap_free(). It also takes - * care of converting all the data to host byte order, so this is the byte - * order used in map_ap_get_*()/map_ap_set_*(). - * - * Returns NULL in case of failure. - */ -map_ap_t *map_ap_decode(const uint8_t *buffer, size_t length); - -/* Takes all parameters currently set and packs them into a buffer with OBEX - * application parameters header payload format. - * - * Returns newly allocated buffer of size 'length'. Free with g_free(). - */ -uint8_t *map_ap_encode(map_ap_t *ap, size_t *length); - -/* Following family of functions reads value of MAP parameter with given tag. - * Use the one with appropriate type for a given tag, as noted above in - * map_ap_tag declaration comments. - * - * Returns TRUE when value is present. FALSE if it is not or the function is - * used get a parameter of a different type. When FALSE is returned, variable - * pointed by 'val' is left intact. - */ -gboolean map_ap_get_u8(map_ap_t *ap, enum map_ap_tag tag, uint8_t *val); -gboolean map_ap_get_u16(map_ap_t *ap, enum map_ap_tag tag, uint16_t *val); -gboolean map_ap_get_u32(map_ap_t *ap, enum map_ap_tag tag, uint32_t *val); - -/* Reads value of MAP parameter with given tag that is of a string type. - * - * Returns NULL if parameter is not present in ap or given tag is not of a - * string type. - */ -const char *map_ap_get_string(map_ap_t *ap, enum map_ap_tag tag); - -/* Following family of functions sets the value of MAP parameter with given - * tag. Use the one with appropriate type for a given tag, as noted above in - * map_ap_tag declaration comments. - * - * If there is already a parameter with given tag present, it will be - * replaced. map_ap_set_string() makes its own copy of given string. - * - * Returns TRUE on success (the tag is known and the function chosen matches - * the type of tag). - */ -gboolean map_ap_set_u8(map_ap_t *ap, enum map_ap_tag tag, uint8_t val); -gboolean map_ap_set_u16(map_ap_t *ap, enum map_ap_tag tag, uint16_t val); -gboolean map_ap_set_u32(map_ap_t *ap, enum map_ap_tag tag, uint32_t val); -gboolean map_ap_set_string(map_ap_t *ap, enum map_ap_tag tag, const char *val); diff --git a/src/server.c b/src/server.c index 52f7f1e..16be849 100644 --- a/src/server.c +++ b/src/server.c @@ -31,7 +31,6 @@ #include #include #include -#include #include #include diff --git a/test/map-client b/test/map-client index ac1a8ca..756ebb8 100755 --- a/test/map-client +++ b/test/map-client @@ -41,6 +41,18 @@ def parse_options(): help="List messages in supplied CWD subdir") parser.add_option("-g", "--get", action="store", dest="get_msg", help="Get message contents") + parser.add_option("--get-properties", action="store", dest="get_msg_properties", + help="Get message properties") + parser.add_option("--mark-read", action="store", dest="mark_msg_read", + help="Marks the messages as read") + parser.add_option("--mark-unread", action="store", dest="mark_msg_unread", + help="Marks the messages as unread") + parser.add_option("--mark-deleted", action="store", dest="mark_msg_deleted", + help="Deletes the message from the folder") + parser.add_option("--mark-undeleted", action="store", dest="mark_msg_undeleted", + help="Undeletes the message") + parser.add_option("-u", "--update-inbox", action="store_true", dest="update_inbox", + help="Checks for new mails") return parser.parse_args() @@ -105,21 +117,40 @@ class MapClient: self.map.SetFolder(new_dir) def list_folders(self): - for i in self.map.GetFolderListing(dict()): + for i in self.map.ListFolders(dict()): print "%s/" % (i["Name"]) def list_messages(self, folder): - ret = self.map.GetMessageListing(folder, dict()) + ret = self.map.ListMessages(folder, dict()) print pformat(unwrap(ret)) def get_message(self, handle): - self.map.GetMessageListing("", dict()) + self.map.ListMessages("", dict()) path = self.path + "/message" + handle obj = bus.get_object("org.bluez.obex.client", path) msg = dbus.Interface(obj, "org.bluez.obex.Message") - msg.Get("",reply_handler=self.create_transfer_reply, + msg.Get("", True, reply_handler=self.create_transfer_reply, error_handler=self.error) + def get_message_properties(self, handle): + self.map.ListMessages("", dict()) + path = self.path + "/message" + handle + obj = bus.get_object("org.bluez.obex.client", path) + msg = dbus.Interface(obj, "org.bluez.obex.Message") + ret = msg.GetProperties() + print pformat(unwrap(ret)) + + def set_message_property(self, handle, prop, flag): + self.map.ListMessages("", dict()) + path = self.path + "/message" + handle + obj = bus.get_object("org.bluez.obex.client", path) + msg = dbus.Interface(obj, "org.bluez.obex.Message") + msg.SetProperty (prop, flag); + + def update_inbox(self): + self.map.UpdateInbox() + + if __name__ == '__main__': dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) @@ -155,4 +186,22 @@ if __name__ == '__main__': if options.get_msg is not None: map_client.get_message(options.get_msg) + if options.get_msg_properties is not None: + map_client.get_message_properties(options.get_msg_properties) + + if options.mark_msg_read is not None: + map_client.set_message_property(options.mark_msg_read, "Read", True) + + if options.mark_msg_unread is not None: + map_client.set_message_property(options.mark_msg_unread, "Read", False) + + if options.mark_msg_deleted is not None: + map_client.set_message_property(options.mark_msg_deleted, "Deleted", True) + + if options.mark_msg_undeleted is not None: + map_client.set_message_property(options.mark_msg_undeleted, "Deleted", False) + + if options.update_inbox: + map_client.update_inbox() + mainloop.run() diff --git a/test/pbap-client b/test/pbap-client index 498f8a3..fbe930c 100755 --- a/test/pbap-client +++ b/test/pbap-client @@ -70,16 +70,16 @@ class PbapClient: print "Transfer finished with error %s: %s" % (code, message) mainloop.quit() - def pull(self, vcard, func): + def pull(self, vcard, params, func): req = Transfer(func) - self.pbap.Pull(vcard, "", + self.pbap.Pull(vcard, "", params, reply_handler=lambda r: self.register(r, req), error_handler=self.error) self.transfers += 1 - def pull_all(self, func): + def pull_all(self, params, func): req = Transfer(func) - self.pbap.PullAll("", + self.pbap.PullAll("", params, reply_handler=lambda r: self.register(r, req), error_handler=self.error) self.transfers += 1 @@ -135,18 +135,19 @@ if __name__ == '__main__': print "Size = %d\n" % (ret) print "\n--- List vCard ---\n" - ret = pbap_client.interface().List() + try: + ret = pbap_client.interface().List(dbus.Dictionary()) + except: + ret = [] + + params = dbus.Dictionary({ "Format" : "vcard30", + "Fields" : [ "VERSION", "FN", "TEL"] }) for item in ret: print "%s : %s" % (item[0], item[1]) - pbap_client.interface().SetFormat("vcard30") - pbap_client.interface().SetFilter(["VERSION", "FN", - "TEL"]); - pbap_client.pull(item[0], + pbap_client.pull(item[0], params, lambda x: process_result(x, None)) - pbap_client.interface().SetFormat("vcard30") - pbap_client.interface().SetFilter(["VERSION", "FN", "TEL"]); - pbap_client.pull_all(lambda x: process_result(x, + pbap_client.pull_all(params, lambda x: process_result(x, "\n--- PullAll ---\n")) pbap_client.flush_transfers(lambda: test_paths(paths[1:])) diff --git a/tools/test-client.c b/tools/test-client.c index 81a2be0..8488a20 100644 --- a/tools/test-client.c +++ b/tools/test-client.c @@ -344,69 +344,66 @@ static void conn_callback(GIOChannel *io, GError *err, gpointer user_data) transport_connect(io, transport); } -static GIOChannel *bluetooth_connect(GObexTransportType transport) +static GIOChannel *l2cap_connect(GObexTransportType transport, GError **err) { - GIOChannel *io; - GError *err = NULL; - BtIOType type; - BtIOOption option; - - if (option_dest == NULL || option_channel < 0) - return NULL; - - if (option_channel > 31) { - type = option_packet ? BT_IO_L2CAP : BT_IO_L2ERTM; - option = BT_IO_OPT_PSM; - } else { - type = BT_IO_RFCOMM; - option = BT_IO_OPT_CHANNEL; - } - - if (option_source) { - if (type == BT_IO_L2CAP) { - io = bt_io_connect(type, conn_callback, + if (option_source) + return bt_io_connect(conn_callback, GUINT_TO_POINTER(transport), - NULL, &err, + NULL, err, BT_IO_OPT_SOURCE, option_source, BT_IO_OPT_DEST, option_dest, - option, option_channel, + BT_IO_OPT_PSM, option_channel, BT_IO_OPT_MODE, BT_IO_MODE_ERTM, BT_IO_OPT_OMTU, option_omtu, BT_IO_OPT_IMTU, option_imtu, BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW, BT_IO_OPT_INVALID); - } else { - io = bt_io_connect(type, conn_callback, + + return bt_io_connect(conn_callback, GUINT_TO_POINTER(transport), - NULL, &err, - BT_IO_OPT_SOURCE, option_source, + NULL, err, BT_IO_OPT_DEST, option_dest, - option, option_channel, + BT_IO_OPT_PSM, option_channel, + BT_IO_OPT_MODE, BT_IO_MODE_ERTM, + BT_IO_OPT_OMTU, option_omtu, + BT_IO_OPT_IMTU, option_imtu, BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW, BT_IO_OPT_INVALID); - } - } else { - if (type == BT_IO_L2CAP) { - io = bt_io_connect(type, conn_callback, +} + +static GIOChannel *rfcomm_connect(GObexTransportType transport, GError **err) +{ + if (option_source) + return bt_io_connect(conn_callback, GUINT_TO_POINTER(transport), - NULL, &err, + NULL, err, + BT_IO_OPT_SOURCE, option_source, BT_IO_OPT_DEST, option_dest, - option, option_channel, - BT_IO_OPT_MODE, BT_IO_MODE_ERTM, - BT_IO_OPT_OMTU, option_omtu, - BT_IO_OPT_IMTU, option_imtu, + BT_IO_OPT_CHANNEL, option_channel, BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW, BT_IO_OPT_INVALID); - } else { - io = bt_io_connect(type, conn_callback, + + return bt_io_connect(conn_callback, GUINT_TO_POINTER(transport), - NULL, &err, + NULL, err, BT_IO_OPT_DEST, option_dest, - option, option_channel, + BT_IO_OPT_CHANNEL, option_channel, BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW, BT_IO_OPT_INVALID); - } - } +} + +static GIOChannel *bluetooth_connect(GObexTransportType transport) +{ + GIOChannel *io; + GError *err = NULL; + + if (option_dest == NULL || option_channel < 0) + return NULL; + + if (option_channel > 31) + io = l2cap_connect(transport, &err); + else + io = rfcomm_connect(transport, &err); if (io != NULL) return io; diff --git a/tools/test-server.c b/tools/test-server.c index 3a660f4..e37c56f 100644 --- a/tools/test-server.c +++ b/tools/test-server.c @@ -308,40 +308,40 @@ static gboolean bluetooth_watch(GIOChannel *chan, GIOCondition cond, gpointer da return FALSE; } +static GIOChannel *l2cap_listen(GError **err) +{ + return bt_io_listen(bluetooth_accept, NULL, NULL, + NULL, err, + BT_IO_OPT_PSM, option_channel, + BT_IO_OPT_MODE, BT_IO_MODE_ERTM, + BT_IO_OPT_OMTU, option_omtu, + BT_IO_OPT_IMTU, option_imtu, + BT_IO_OPT_INVALID); +} + +static GIOChannel *rfcomm_listen(GError **err) +{ + return bt_io_listen(bluetooth_accept, NULL, NULL, + NULL, err, + BT_IO_OPT_CHANNEL, option_channel, + BT_IO_OPT_INVALID); +} + static guint bluetooth_listen(void) { GIOChannel *io; guint id; GError *err = NULL; - BtIOType type; - BtIOOption option; if (option_channel == -1) { g_printerr("Bluetooth channel not set\n"); return 0; } - if (option_packet || option_channel > 31) { - type = option_packet ? BT_IO_L2CAP : BT_IO_L2ERTM; - option = BT_IO_OPT_PSM; - } else { - type = BT_IO_RFCOMM; - option = BT_IO_OPT_CHANNEL; - } - - if (type == BT_IO_L2CAP) - io = bt_io_listen(type, bluetooth_accept, NULL, NULL, - NULL, &err, - option, option_channel, - BT_IO_OPT_MODE, BT_IO_MODE_ERTM, - BT_IO_OPT_OMTU, option_omtu, - BT_IO_OPT_IMTU, option_imtu, - BT_IO_OPT_INVALID); + if (option_packet || option_channel > 31) + io = l2cap_listen(&err); else - io = bt_io_listen(type, bluetooth_accept, NULL, NULL, - NULL, &err, - option, option_channel, - BT_IO_OPT_INVALID); + io = rfcomm_listen(&err); if (io == NULL) { g_printerr("%s\n", err->message); diff --git a/unit/test-gobex-apparam.c b/unit/test-gobex-apparam.c new file mode 100644 index 0000000..573de99 --- /dev/null +++ b/unit/test-gobex-apparam.c @@ -0,0 +1,422 @@ +/* + * + * OBEX library with GLib integration + * + * Copyright (C) 2012 Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include +#include + +#include + +#include "util.h" + +#define TAG_U8 0x00 +#define TAG_U16 0x01 +#define TAG_U32 0x02 +#define TAG_U64 0x03 +#define TAG_STRING 0x04 +#define TAG_BYTES 0x05 + +static uint8_t tag_nval_short[] = { TAG_U8 }; +static uint8_t tag_nval_data[] = { TAG_U8, 0x01 }; +static uint8_t tag_nval2_short[] = { TAG_U8, 0x01, 0x1, TAG_U16 }; +static uint8_t tag_nval2_data[] = { TAG_U8, 0x01, 0x1, TAG_U16, 0x02 }; +static uint8_t tag_uint8[] = { TAG_U8, 0x01, 0x01 }; +static uint8_t tag_uint16[] = { TAG_U16, 0x02, 0x01, 0x02 }; +static uint8_t tag_uint32[] = { TAG_U32, 0x04, 0x01, 0x02, 0x03, 0x04 }; +static uint8_t tag_uint64[] = { TAG_U64, 0x08, 0x01, 0x02, 0x03, 0x04, + 0x05, 0x06, 0x07, 0x08 }; +static uint8_t tag_string[] = { TAG_STRING, 0x04, 'A', 'B', 'C', '\0' }; +static uint8_t tag_bytes[257] = { TAG_BYTES, 0xFF }; +static uint8_t tag_multi[] = { TAG_U8, 0x01, 0x01, + TAG_U16, 0x02, 0x01, 0x02, + TAG_U32, 0x04, 0x01, 0x02, 0x03, 0x04, + TAG_U64, 0x08, 0x01, 0x02, 0x03, 0x04, + 0x05, 0x06, 0x07, 0x08, + TAG_STRING, 0x04, 'A', 'B', 'C', '\0' }; + + +static GObexApparam *parse_and_decode(const void *data, gsize size) +{ + GObexApparam *apparam; + guint8 encoded[1024]; + gsize len; + + apparam = g_obex_apparam_decode(data, size); + + g_assert(apparam != NULL); + + len = g_obex_apparam_encode(apparam, encoded, sizeof(encoded)); + + assert_memequal(data, size, encoded, len); + + return apparam; +} + +static void test_apparam_nval_short(void) +{ + GObexApparam *apparam; + + apparam = g_obex_apparam_decode(tag_nval_short, + sizeof(tag_nval_short)); + + g_assert(apparam == NULL); +} + +static void test_apparam_nval_data(void) +{ + GObexApparam *apparam; + + apparam = g_obex_apparam_decode(tag_nval_data, + sizeof(tag_nval_data)); + + g_assert(apparam == NULL); +} + +static void test_apparam_nval2_short(void) +{ + GObexApparam *apparam; + + apparam = g_obex_apparam_decode(tag_nval2_short, + sizeof(tag_nval2_short)); + + g_assert(apparam == NULL); +} + +static void test_apparam_nval2_data(void) +{ + GObexApparam *apparam; + + apparam = g_obex_apparam_decode(tag_nval2_data, + sizeof(tag_nval2_data)); + + g_assert(apparam == NULL); +} + +static void test_apparam_get_uint8(void) +{ + GObexApparam *apparam; + guint8 data; + gboolean ret; + + apparam = parse_and_decode(tag_uint8, sizeof(tag_uint8)); + + ret = g_obex_apparam_get_uint8(apparam, TAG_U8, &data); + + g_assert(ret == TRUE); + g_assert(data == 0x01); + + g_obex_apparam_free(apparam); +} + +static void test_apparam_get_uint16(void) +{ + GObexApparam *apparam; + uint16_t data; + gboolean ret; + + apparam = parse_and_decode(tag_uint16, sizeof(tag_uint16)); + + ret = g_obex_apparam_get_uint16(apparam, TAG_U16, &data); + + g_assert(ret == TRUE); + g_assert(data == 0x0102); + + g_obex_apparam_free(apparam); +} + +static void test_apparam_get_uint32(void) +{ + GObexApparam *apparam; + uint32_t data; + gboolean ret; + + apparam = parse_and_decode(tag_uint32, sizeof(tag_uint32)); + + ret = g_obex_apparam_get_uint32(apparam, TAG_U32, &data); + + g_assert(ret == TRUE); + g_assert(data == 0x01020304); + + g_obex_apparam_free(apparam); +} + +static void test_apparam_get_uint64(void) +{ + GObexApparam *apparam; + uint64_t data; + gboolean ret; + + apparam = parse_and_decode(tag_uint64, sizeof(tag_uint64)); + + ret = g_obex_apparam_get_uint64(apparam, TAG_U64, &data); + + g_assert(ret == TRUE); + g_assert(data == 0x0102030405060708); + + g_obex_apparam_free(apparam); +} + +static void test_apparam_get_string(void) +{ + GObexApparam *apparam; + char *string; + + apparam = parse_and_decode(tag_string, sizeof(tag_string)); + + string = g_obex_apparam_get_string(apparam, TAG_STRING); + + g_assert(string != NULL); + g_assert_cmpstr(string, ==, "ABC"); + + g_free(string); + g_obex_apparam_free(apparam); +} + +static void test_apparam_get_bytes(void) +{ + GObexApparam *apparam; + const uint8_t *data; + gsize len; + gboolean ret; + + apparam = parse_and_decode(tag_bytes, sizeof(tag_bytes)); + + ret = g_obex_apparam_get_bytes(apparam, TAG_BYTES, &data, &len); + + g_assert(ret == TRUE); + assert_memequal(tag_bytes + 2, sizeof(tag_bytes) - 2, data, len); + + g_obex_apparam_free(apparam); +} + +static void test_apparam_get_multi(void) +{ + GObexApparam *apparam; + char *string; + uint8_t data8; + uint16_t data16; + uint32_t data32; + uint64_t data64; + gboolean ret; + + apparam = g_obex_apparam_decode(tag_multi, sizeof(tag_multi)); + + g_assert(apparam != NULL); + + ret = g_obex_apparam_get_uint8(apparam, TAG_U8, &data8); + + g_assert(ret == TRUE); + g_assert(data8 == 0x01); + + ret = g_obex_apparam_get_uint16(apparam, TAG_U16, &data16); + + g_assert(ret == TRUE); + g_assert(data16 == 0x0102); + + ret = g_obex_apparam_get_uint32(apparam, TAG_U32, &data32); + + g_assert(ret == TRUE); + g_assert(data32 == 0x01020304); + + ret = g_obex_apparam_get_uint64(apparam, TAG_U64, &data64); + + g_assert(ret == TRUE); + g_assert(data64 == 0x0102030405060708); + + string = g_obex_apparam_get_string(apparam, TAG_STRING); + + g_assert(string != NULL); + g_assert_cmpstr(string, ==, "ABC"); + + g_obex_apparam_free(apparam); +} + +static void test_apparam_set_uint8(void) +{ + GObexApparam *apparam; + guint8 buf[1024]; + gsize len; + + apparam = g_obex_apparam_set_uint8(NULL, TAG_U8, 0x01); + g_assert(apparam != NULL); + + len = g_obex_apparam_encode(apparam, buf, sizeof(buf)); + assert_memequal(tag_uint8, sizeof(tag_uint8), buf, len); + + g_obex_apparam_free(apparam); +} + +static void test_apparam_set_uint16(void) +{ + GObexApparam *apparam; + guint8 buf[1024]; + gsize len; + + apparam = g_obex_apparam_set_uint16(NULL, TAG_U16, 0x0102); + g_assert(apparam != NULL); + + len = g_obex_apparam_encode(apparam, buf, sizeof(buf)); + assert_memequal(tag_uint16, sizeof(tag_uint16), buf, len); + + g_obex_apparam_free(apparam); +} + +static void test_apparam_set_uint32(void) +{ + GObexApparam *apparam; + guint8 buf[1024]; + gsize len; + + apparam = g_obex_apparam_set_uint32(NULL, TAG_U32, 0x01020304); + g_assert(apparam != NULL); + + len = g_obex_apparam_encode(apparam, buf, sizeof(buf)); + assert_memequal(tag_uint32, sizeof(tag_uint32), buf, len); + + g_obex_apparam_free(apparam); +} + +static void test_apparam_set_uint64(void) +{ + GObexApparam *apparam; + guint8 buf[1024]; + gsize len; + + apparam = g_obex_apparam_set_uint64(NULL, TAG_U64, 0x0102030405060708); + g_assert(apparam != NULL); + + len = g_obex_apparam_encode(apparam, buf, sizeof(buf)); + assert_memequal(tag_uint64, sizeof(tag_uint64), buf, len); + + g_obex_apparam_free(apparam); +} + +static void test_apparam_set_string(void) +{ + GObexApparam *apparam; + guint8 buf[1024]; + gsize len; + + apparam = g_obex_apparam_set_string(NULL, TAG_STRING, "ABC"); + g_assert(apparam != NULL); + + len = g_obex_apparam_encode(apparam, buf, sizeof(buf)); + assert_memequal(tag_string, sizeof(tag_string), buf, len); + + g_obex_apparam_free(apparam); +} + +static void test_apparam_set_bytes(void) +{ + GObexApparam *apparam; + guint8 buf[1024]; + gsize len; + + apparam = g_obex_apparam_set_bytes(NULL, TAG_BYTES, tag_bytes + 2, 255); + g_assert(apparam != NULL); + + len = g_obex_apparam_encode(apparam, buf, sizeof(buf)); + assert_memequal(tag_bytes, sizeof(tag_bytes), buf, len); + + g_obex_apparam_free(apparam); +} + +static void test_apparam_set_multi(void) +{ + GObexApparam *apparam; + guint8 buf[1024]; + gsize len; + + apparam = g_obex_apparam_set_uint8(NULL, TAG_U8, 0x01); + + g_assert(apparam != NULL); + + apparam = g_obex_apparam_set_uint16(apparam, TAG_U16, 0x0102); + + g_assert(apparam != NULL); + + apparam = g_obex_apparam_set_uint32(apparam, TAG_U32, 0x01020304); + + g_assert(apparam != NULL); + + apparam = g_obex_apparam_set_uint64(apparam, TAG_U64, + 0x0102030405060708); + + g_assert(apparam != NULL); + + apparam = g_obex_apparam_set_string(apparam, TAG_STRING, "ABC"); + + g_assert(apparam != NULL); + + len = g_obex_apparam_encode(apparam, buf, sizeof(buf)); + + g_assert_cmpuint(len, ==, sizeof(tag_multi)); + + g_obex_apparam_free(apparam); +} + +int main(int argc, char *argv[]) +{ + g_test_init(&argc, &argv, NULL); + + g_test_add_func("/gobex/test_apparam_nval_short", + test_apparam_nval_short); + g_test_add_func("/gobex/test_apparam_nval_data", + test_apparam_nval_data); + + g_test_add_func("/gobex/test_apparam_nval2_short", + test_apparam_nval2_short); + g_test_add_func("/gobex/test_apparam_nval2_data", + test_apparam_nval2_data); + + g_test_add_func("/gobex/test_apparam_get_uint8", + test_apparam_get_uint8); + g_test_add_func("/gobex/test_apparam_get_uint16", + test_apparam_get_uint16); + g_test_add_func("/gobex/test_apparam_get_uint32", + test_apparam_get_uint32); + g_test_add_func("/gobex/test_apparam_get_uint64", + test_apparam_get_uint64); + g_test_add_func("/gobex/test_apparam_get_string", + test_apparam_get_string); + g_test_add_func("/gobex/test_apparam_get_bytes", + test_apparam_get_bytes); + g_test_add_func("/gobex/test_apparam_get_multi", + test_apparam_get_multi); + + g_test_add_func("/gobex/test_apparam_set_uint8", + test_apparam_set_uint8); + g_test_add_func("/gobex/test_apparam_set_uint16", + test_apparam_set_uint16); + g_test_add_func("/gobex/test_apparam_set_uint32", + test_apparam_set_uint32); + g_test_add_func("/gobex/test_apparam_set_uint64", + test_apparam_set_uint64); + g_test_add_func("/gobex/test_apparam_set_string", + test_apparam_set_string); + g_test_add_func("/gobex/test_apparam_set_bytes", + test_apparam_set_bytes); + g_test_add_func("/gobex/test_apparam_set_multi", + test_apparam_set_multi); + + g_test_run(); + + return 0; +} diff --git a/unit/test-gobex-header.c b/unit/test-gobex-header.c index 86e69f3..feb7d17 100644 --- a/unit/test-gobex-header.c +++ b/unit/test-gobex-header.c @@ -47,6 +47,8 @@ static uint8_t hdr_bytes_nval_short[] = { G_OBEX_HDR_BODY, 0xab, 0xcd, 0x01, 0x02, 0x03 }; static uint8_t hdr_bytes_nval_data[] = { G_OBEX_HDR_BODY, 0xab }; static uint8_t hdr_bytes_nval_len[] = { G_OBEX_HDR_BODY, 0x00, 0x00 }; +static uint8_t hdr_apparam[] = { G_OBEX_HDR_APPARAM, 0x00, 0x09, 0x00, 0x04, + 0x01, 0x02, 0x03, 0x04 }; static void test_header_name_empty(void) { @@ -115,6 +117,27 @@ static void test_header_bytes(void) g_obex_header_free(header); } +static void test_header_apparam(void) +{ + GObexHeader *header; + GObexApparam *apparam; + uint8_t buf[1024]; + size_t len; + + apparam = g_obex_apparam_set_uint32(NULL, 0, 0x01020304); + g_assert(apparam != NULL); + + header = g_obex_header_new_apparam(apparam); + g_assert(header != NULL); + + len = g_obex_header_encode(header, buf, sizeof(buf)); + + assert_memequal(hdr_apparam, sizeof(hdr_apparam), buf, len); + + g_obex_apparam_free(apparam); + g_obex_header_free(header); +} + static void test_header_uint8(void) { GObexHeader *header; @@ -247,6 +270,26 @@ static void test_header_encode_body(void) g_obex_header_free(header); } +static void test_header_encode_apparam(void) +{ + GObexHeader *header; + GObexApparam *apparam; + gboolean ret; + guint32 data; + + header = parse_and_encode(hdr_apparam, sizeof(hdr_apparam)); + + apparam = g_obex_header_get_apparam(header); + g_assert(apparam != NULL); + + ret = g_obex_apparam_get_uint32(apparam, 0x00, &data); + g_assert(ret == TRUE); + g_assert(data == 0x01020304); + + g_obex_apparam_free(apparam); + g_obex_header_free(header); +} + static void test_header_encode_actionid(void) { GObexHeader *header; @@ -507,6 +550,8 @@ int main(int argc, char *argv[]) test_header_encode_body); g_test_add_func("/gobex/test_header_encode_connid", test_header_encode_actionid); + g_test_add_func("/gobex/test_header_encode_apparam", + test_header_encode_apparam); g_test_add_func("/gobex/test_header_name_empty", test_header_name_empty); @@ -517,6 +562,7 @@ int main(int argc, char *argv[]) g_test_add_func("/gobex/test_header_bytes", test_header_bytes); g_test_add_func("/gobex/test_header_uint8", test_header_uint8); g_test_add_func("/gobex/test_header_uint32", test_header_uint32); + g_test_add_func("/gobex/test_header_apparam", test_header_apparam); g_test_run();