From patchwork Tue Sep 17 07:42:13 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: =?utf-8?q?Fr=C3=A9d=C3=A9ric_Danis?= X-Patchwork-Id: 829448 Received: from bali.collaboradmins.com (bali.collaboradmins.com [148.251.105.195]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D73D97BB15 for ; Tue, 17 Sep 2024 07:42:26 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=148.251.105.195 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1726558949; cv=none; b=lKagBb4mYfQDRv3f6yUCEls5aR8+VnjOPVox335ZegXQ+iiTjoliMJqtflTUC3W71YRRkCNa6ZTF2BhGDU8LFwKG+TIMVHo8/mesy7Jnq4Z1dYJ7npqUBvyZoujzZAE7YdX3BblthFBRiV3sHjrcHXVUxn80Cg4ic4P5Rguwtxc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1726558949; c=relaxed/simple; bh=p7qzDefy4KmvnMHlngGPsS8f648+E4Q2NNUAQa67wWo=; h=From:To:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version:Content-Type; b=EkqyN8E4S1J6zGg2eQ666YYJiAFP/Vf+DpBnUxRcqa0h6QF2VnwYc37doWY8YBbCiYJcPej9luDqVBdXjR+Oc0Md8bALe8fq3tiheyKDj/Lsdl9qLfjYczWjywpBsbNHEpDeBT4ca2ksS++hlhvbfj410pB0QOAJGQ7FieLBGIc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com; spf=pass smtp.mailfrom=collabora.com; dkim=pass (2048-bit key) header.d=collabora.com header.i=@collabora.com header.b=PAIKT9At; arc=none smtp.client-ip=148.251.105.195 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=collabora.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=collabora.com header.i=@collabora.com header.b="PAIKT9At" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=collabora.com; s=mail; t=1726558944; bh=p7qzDefy4KmvnMHlngGPsS8f648+E4Q2NNUAQa67wWo=; h=From:To:Subject:Date:In-Reply-To:References:From; b=PAIKT9At7183ZvbmnuK1xFEb0Pr6neATEVJGjbQt0D7EvhAErcdAOHuM2hkrpWtso 6hwV+r03uSspT2lcDjlGR0EbcQB9lG8euHefdKcLh0s+lh4aUoKkCEkwLoSnbTpe9m Uq7fAoL4dVIw3gUgksRc+HuWnqr8rghjxZA7WKLj/tC3pCD7Rfhb3rBUovgYmG8CHj ovuLq7qCdCz/ohrXgFq3A6QgSegwi61eD0TLUhq0BnZMthkuhBo6ZKwb703/lU50se S91K+VjA+gUm6EKsvMziMSvb9xVc+ShHzhVkMVBIvCsGhv0qCD4mKRcJeGoXsJx9b5 oqK2t83H1bz0w== Received: from fdanis-XPS-13-9370.. (67.227.121.78.rev.sfr.net [78.121.227.67]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) (Authenticated sender: fdanis) by bali.collaboradmins.com (Postfix) with ESMTPSA id CEA6D17E0FD6 for ; Tue, 17 Sep 2024 09:42:24 +0200 (CEST) From: =?utf-8?q?Fr=C3=A9d=C3=A9ric_Danis?= To: linux-bluetooth@vger.kernel.org Subject: [PATCH BlueZ v4 1/5] obexd: Add BIP client for AVRCP cover art download Date: Tue, 17 Sep 2024 09:42:13 +0200 Message-Id: <20240917074217.231677-2-frederic.danis@collabora.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240917074217.231677-1-frederic.danis@collabora.com> References: <20240917074217.231677-1-frederic.danis@collabora.com> Precedence: bulk X-Mailing-List: linux-bluetooth@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 The cover art image handle is available in the metadata of the track when the OBEX BIP session is connected to the PSM port provided in AVRCP SDP record and available as org.bluez.MediaPlayer property. This service allows to get the thumbnail. --- Makefile.obexd | 1 + doc/org.bluez.obex.Client.rst | 1 + obexd/client/bip.c | 171 ++++++++++++++++++++++++++++++++++ obexd/client/bip.h | 12 +++ obexd/client/manager.c | 2 + 5 files changed, 187 insertions(+) create mode 100644 obexd/client/bip.c create mode 100644 obexd/client/bip.h diff --git a/Makefile.obexd b/Makefile.obexd index 4cdce73af..866147dd1 100644 --- a/Makefile.obexd +++ b/Makefile.obexd @@ -81,6 +81,7 @@ obexd_src_obexd_SOURCES = $(btio_sources) $(gobex_sources) \ obexd/client/ftp.h obexd/client/ftp.c \ obexd/client/opp.h obexd/client/opp.c \ obexd/client/map.h obexd/client/map.c \ + obexd/client/bip.h obexd/client/bip.c \ obexd/client/map-event.h obexd/client/map-event.c \ obexd/client/transfer.h obexd/client/transfer.c \ obexd/client/transport.h obexd/client/transport.c \ diff --git a/doc/org.bluez.obex.Client.rst b/doc/org.bluez.obex.Client.rst index 5ae7cc5e8..f20dd5baa 100644 --- a/doc/org.bluez.obex.Client.rst +++ b/doc/org.bluez.obex.Client.rst @@ -43,6 +43,7 @@ object CreateSession(string destination, dict args) :"opp": :"pbap": :"sync": + :"bip-avrcp": :string Source: diff --git a/obexd/client/bip.c b/obexd/client/bip.c new file mode 100644 index 000000000..252bc4cec --- /dev/null +++ b/obexd/client/bip.c @@ -0,0 +1,171 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * + * OBEX Client + * + * Copyright (C) 2024 Collabora Ltd. + * + * + */ + +#define _GNU_SOURCE +#include +#include +#include + +#include "gdbus/gdbus.h" +#include "gobex/gobex.h" + +#include "obexd/src/log.h" +#include "transfer.h" +#include "session.h" +#include "driver.h" +#include "bip.h" + +#define OBEX_BIP_AVRCP_UUID \ + "\x71\x63\xDD\x54\x4A\x7E\x11\xE2\xB4\x7C\x00\x50\xC2\x49\x00\x48" +#define OBEX_BIP_AVRCP_UUID_LEN 16 + +#define IMAGE_INTERFACE "org.bluez.obex.Image1" +#define ERROR_INTERFACE "org.bluez.obex.Error" +#define IMAGE_UUID "0000111A-0000-1000-8000-00805f9b34fb" + +#define IMG_HANDLE_TAG 0x30 + +static DBusConnection *conn; + +struct bip_avrcp_data { + struct obc_session *session; +}; + +static DBusMessage *get_thumbnail(DBusConnection *connection, + DBusMessage *message, void *user_data) +{ + struct bip_avrcp_data *bip_avrcp = user_data; + const char *handle = NULL, *image_path = NULL; + struct obc_transfer *transfer; + GObexHeader *header; + DBusMessage *reply = NULL; + GError *err = NULL; + + DBG(""); + + if (dbus_message_get_args(message, NULL, + DBUS_TYPE_STRING, &image_path, + DBUS_TYPE_STRING, &handle, + DBUS_TYPE_INVALID) == FALSE) { + reply = g_dbus_create_error(message, + ERROR_INTERFACE ".InvalidArguments", NULL); + return reply; + } + + transfer = obc_transfer_get("x-bt/img-thm", NULL, image_path, &err); + if (transfer == NULL) + goto fail; + + header = g_obex_header_new_unicode(IMG_HANDLE_TAG, handle); + obc_transfer_add_header(transfer, header); + + if (!obc_session_queue(bip_avrcp->session, transfer, NULL, NULL, &err)) + goto fail; + + return obc_transfer_create_dbus_reply(transfer, message); + +fail: + reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed", "%s", + err->message); + g_error_free(err); + return reply; +} + +static const GDBusMethodTable bip_avrcp_methods[] = { + { GDBUS_ASYNC_METHOD("GetThumbnail", + GDBUS_ARGS({ "file", "s" }, { "handle", "s"}), + GDBUS_ARGS({ "transfer", "o" }, { "properties", "a{sv}" }), + get_thumbnail) }, + { } +}; + +static void bip_avrcp_free(void *data) +{ + struct bip_avrcp_data *bip_avrcp = data; + + obc_session_unref(bip_avrcp->session); + g_free(bip_avrcp); +} + +static int bip_avrcp_probe(struct obc_session *session) +{ + struct bip_avrcp_data *bip_avrcp; + const char *path; + + path = obc_session_get_path(session); + + DBG("%s", path); + + bip_avrcp = g_try_new0(struct bip_avrcp_data, 1); + if (!bip_avrcp) + return -ENOMEM; + + bip_avrcp->session = obc_session_ref(session); + + if (!g_dbus_register_interface(conn, path, IMAGE_INTERFACE, + bip_avrcp_methods, + NULL, NULL, + bip_avrcp, bip_avrcp_free)) { + bip_avrcp_free(bip_avrcp); + return -ENOMEM; + } + + return 0; +} + +static void bip_avrcp_remove(struct obc_session *session) +{ + const char *path = obc_session_get_path(session); + + DBG("%s", path); + + g_dbus_unregister_interface(conn, path, IMAGE_INTERFACE); +} + +static struct obc_driver bip_avrcp = { + .service = "BIP-AVRCP", + .uuid = IMAGE_UUID, + .target = OBEX_BIP_AVRCP_UUID, + .target_len = OBEX_BIP_AVRCP_UUID_LEN, + .probe = bip_avrcp_probe, + .remove = bip_avrcp_remove +}; + +int bip_init(void) +{ + int err; + + DBG(""); + + conn = dbus_bus_get(DBUS_BUS_SESSION, NULL); + if (!conn) + return -EIO; + + err = obc_driver_register(&bip_avrcp); + if (err < 0) + goto failed; + + return 0; + +failed: + dbus_connection_unref(conn); + conn = NULL; + return err; +} + +void bip_exit(void) +{ + DBG(""); + + dbus_connection_unref(conn); + conn = NULL; + + obc_driver_unregister(&bip_avrcp); +} diff --git a/obexd/client/bip.h b/obexd/client/bip.h new file mode 100644 index 000000000..18e3360f3 --- /dev/null +++ b/obexd/client/bip.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * + * OBEX Client + * + * Copyright (C) 2024 Collabora Ltd. + * + * + */ + +int bip_init(void); +void bip_exit(void); diff --git a/obexd/client/manager.c b/obexd/client/manager.c index 52c00fb0c..52f4d0179 100644 --- a/obexd/client/manager.c +++ b/obexd/client/manager.c @@ -32,6 +32,7 @@ #include "pbap.h" #include "sync.h" #include "map.h" +#include "bip.h" #include "manager.h" #define CLIENT_INTERFACE "org.bluez.obex.Client1" @@ -258,6 +259,7 @@ static const struct obc_module { { "pbap", pbap_init, pbap_exit }, { "sync", sync_init, sync_exit }, { "map", map_init, map_exit }, + { "bip", bip_init, bip_exit }, { } }; From patchwork Tue Sep 17 07:42:14 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: =?utf-8?q?Fr=C3=A9d=C3=A9ric_Danis?= X-Patchwork-Id: 829270 Received: from bali.collaboradmins.com (bali.collaboradmins.com [148.251.105.195]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D739571750 for ; Tue, 17 Sep 2024 07:42:26 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=148.251.105.195 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1726558949; cv=none; b=icjrH823+sZSeEjiMJlzzQC25ybMZLnzLzcLD9jQmv8k4Ag2anPU6FK0syvxC/i8xW8dwNf5MVDqWci+C3BBGw20jylkkliOZfdEgPzC8KR62+6yPXWQ25tqE+Wq4NX5zA3iGrPsc7VfV3xIS05lUlXqrhrwE8+wTDtFynqxX8E= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1726558949; c=relaxed/simple; bh=cOleKAOYlJn1YyNtlpmsG4/xhxdvPlkulUW8AmT/xLw=; h=From:To:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version:Content-Type; b=dTv4C4tF0PkYvrliVbZf8/ndS4VBv9wXQLAOixJcgW9bEaSZLeS3gthZcMS8U1I0snS+C05b02kr3M60FowOA6X8rbCZ13/9oimrLR377kbfzyEiyso5+QVCJAv8B8jim8bpbUmGQA+KinhLDnGp+aMJNNdkhmV32tDPg5JJjIg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com; spf=pass smtp.mailfrom=collabora.com; dkim=pass (2048-bit key) header.d=collabora.com header.i=@collabora.com header.b=KPDy/rjI; arc=none smtp.client-ip=148.251.105.195 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=collabora.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=collabora.com header.i=@collabora.com header.b="KPDy/rjI" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=collabora.com; s=mail; t=1726558945; bh=cOleKAOYlJn1YyNtlpmsG4/xhxdvPlkulUW8AmT/xLw=; h=From:To:Subject:Date:In-Reply-To:References:From; b=KPDy/rjIPpo14vW/m5ymSV5SusincmxxZFx8iOoMB578xJadTt00MOTiGpDsKGGzD FV4HHAjmRA8qRkp2hUapuJtJOAhCFMWbMSxEa6eVMvNk7srUDnG+ek+zTa6JAIH78K wP5yEnmuk2Hrx/bskK/yUjdha2PfdbRRYbkq0BAdD7jeNYOZrFWXvTLVN0eRZFVhKR dIu2HiFxoN3UzVj7yFGYvljWFMbRzbVlIS0jtpaSucYkD3kykT/J0pAeyKRQgZjXYU BK6gP3xCo+UIGzwSE3/SxmUszXDCdxRi9nOjE5N+mwbnbJLYox+VZjEgQx7QPAE0Qw vM7Hei7QAEvDw== Received: from fdanis-XPS-13-9370.. (67.227.121.78.rev.sfr.net [78.121.227.67]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) (Authenticated sender: fdanis) by bali.collaboradmins.com (Postfix) with ESMTPSA id 1CC7917E0FE7 for ; Tue, 17 Sep 2024 09:42:25 +0200 (CEST) From: =?utf-8?q?Fr=C3=A9d=C3=A9ric_Danis?= To: linux-bluetooth@vger.kernel.org Subject: [PATCH BlueZ v4 2/5] obexd: Add Properties to org.bluez.obex.Image Date: Tue, 17 Sep 2024 09:42:14 +0200 Message-Id: <20240917074217.231677-3-frederic.danis@collabora.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240917074217.231677-1-frederic.danis@collabora.com> References: <20240917074217.231677-1-frederic.danis@collabora.com> Precedence: bulk X-Mailing-List: linux-bluetooth@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 This allows to get the different version of the image provided by the remote device to chose the one to use with Get. This bip-common.[ch] files are based on previous work done by Jakub Adamek for GSoC 2011, see [1] and [2]. [1] https://www.bluez.org/gsoc-basic-image-profilebip/ [2] https://github.com/enkait/Basic-Imaging-Profile-in-obexd/blob/gsoc_final/plugins/bip_util.c --- Makefile.obexd | 1 + obexd/client/bip-common.c | 781 ++++++++++++++++++++++++++++++++++++++ obexd/client/bip-common.h | 19 + obexd/client/bip.c | 101 +++++ 4 files changed, 902 insertions(+) create mode 100644 obexd/client/bip-common.c create mode 100644 obexd/client/bip-common.h diff --git a/Makefile.obexd b/Makefile.obexd index 866147dd1..74dd977a0 100644 --- a/Makefile.obexd +++ b/Makefile.obexd @@ -82,6 +82,7 @@ obexd_src_obexd_SOURCES = $(btio_sources) $(gobex_sources) \ obexd/client/opp.h obexd/client/opp.c \ obexd/client/map.h obexd/client/map.c \ obexd/client/bip.h obexd/client/bip.c \ + obexd/client/bip-common.h obexd/client/bip-common.c \ obexd/client/map-event.h obexd/client/map-event.c \ obexd/client/transfer.h obexd/client/transfer.c \ obexd/client/transport.h obexd/client/transport.c \ diff --git a/obexd/client/bip-common.c b/obexd/client/bip-common.c new file mode 100644 index 000000000..9e96c81d8 --- /dev/null +++ b/obexd/client/bip-common.c @@ -0,0 +1,781 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * + * OBEX Client + * + * Copyright (C) 2024 Collabora Ltd. + * Based on previous work done by Jakub Adamek for GSoC 2011 + * + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include + +#include "gobex/gobex.h" + +#include "obexd/src/log.h" +#include "bip-common.h" + +#define HANDLE_LEN 7 +#define HANDLE_LIMIT 10000000 + +struct encconv_pair { + gchar *bip, *im; +}; + +struct encconv_pair encconv_table[] = { + { "JPEG", "JPEG" }, + { "GIF", "GIF" }, + { "WBMP", "WBMP" }, + { "PNG", "PNG" }, + { "JPEG2000", "JP2" }, + { "BMP", "BMP" }, + { } +}; + +static const gchar *convBIP2IM(const gchar *encoding) +{ + struct encconv_pair *et = encconv_table; + + while (et->bip) { + if (g_strcmp0(encoding, et->bip) == 0) + return et->im; + et++; + } + return NULL; +} + +static gboolean parse_pixel_range(const gchar *dim, unsigned int *lower_ret, + unsigned int *upper_ret, + gboolean *fixed_ratio_ret) +{ + static regex_t no_range; + static regex_t range; + static regex_t range_fixed; + static int regex_initialized; + unsigned int lower[2], upper[2]; + gboolean fixed_ratio = FALSE; + + if (!regex_initialized) { + regcomp(&no_range, "^([[:digit:]]{1,5})\\*([[:digit:]]{1,5})$", + REG_EXTENDED); + regcomp(&range, "^([[:digit:]]{1,5})\\*([[:digit:]]{1,5})" + "-([[:digit:]]{1,5})\\*([[:digit:]]{1,5})$", + REG_EXTENDED); + regcomp(&range_fixed, "^([[:digit:]]{1,5})\\*\\*" + "-([[:digit:]]{1,5})\\*([[:digit:]]{1,5})$", + REG_EXTENDED); + regex_initialized = 1; + } + if (dim == NULL) + return FALSE; + if (regexec(&no_range, dim, 0, NULL, 0) == 0) { + if (sscanf(dim, "%u*%u", &lower[0], &lower[1]) != 2) + return FALSE; + upper[0] = lower[0]; + upper[1] = lower[1]; + fixed_ratio = FALSE; + } else if (regexec(&range, dim, 0, NULL, 0) == 0) { + if (sscanf(dim, "%u*%u-%u*%u", &lower[0], &lower[1], + &upper[0], &upper[1]) != 4) + return FALSE; + fixed_ratio = FALSE; + } else if (regexec(&range_fixed, dim, 0, NULL, 0) == 0) { + if (sscanf(dim, "%u**-%u*%u", &lower[0], &upper[0], + &upper[1]) != 3) + return FALSE; + lower[1] = 0; + fixed_ratio = TRUE; + } else { + return FALSE; + } + if (lower[0] > 65535 || lower[1] > 65535 || + upper[0] > 65535 || upper[1] > 65535) + return FALSE; + if (lower_ret == NULL || upper_ret == NULL || fixed_ratio_ret == NULL) + return TRUE; + if (upper[0] < lower[0] || upper[1] < lower[1]) + return FALSE; + lower_ret[0] = lower[0]; + lower_ret[1] = lower[1]; + upper_ret[0] = upper[0]; + upper_ret[1] = upper[1]; + *fixed_ratio_ret = fixed_ratio; + + return TRUE; +} + +static gboolean verify_unsignednumber(const char *size) +{ + static regex_t unumber; + static int regex_initialized; + + if (!regex_initialized) { + regcomp(&unumber, "^[[:digit:]]+$", REG_EXTENDED); + regex_initialized = 1; + } + if (regexec(&unumber, size, 0, NULL, 0) != 0) + return FALSE; + + return TRUE; +} + +static uint64_t parse_unsignednumber(const char *size) +{ + if (!verify_unsignednumber(size)) + return 0; + + return g_ascii_strtoll(size, NULL, 10); +} + +char *transforms[] = { + "crop", + "stretch", + "fill", + NULL +}; + +static gboolean verify_transform(const char *transform) +{ + char **str = transforms; + + while (*str != NULL) { + if (g_str_equal(transform, *str)) + return TRUE; + str++; + } + return FALSE; +} + +static char *parse_transform_list(const char *transform) +{ + char **args = NULL, **arg = NULL; + gboolean used[3] = { FALSE, FALSE, FALSE }; + + if (transform == NULL) + return NULL; + if (strlen(transform) == 0) + return NULL; + args = g_strsplit(transform, " ", 0); + for (arg = args; *arg != NULL; arg++) { + char *t = *arg; + + if (!verify_transform(t)) { + g_strfreev(args); + return NULL; + } + switch (t[0]) { + case 's': + if (used[0]) + goto failure; + used[0] = TRUE; + break; + case 'c': + if (used[1]) + goto failure; + used[1] = TRUE; + break; + case 'f': + if (used[2]) + goto failure; + used[2] = TRUE; + break; + } + } + g_strfreev(args); + return g_strdup(transform); +failure: + g_strfreev(args); + return NULL; +} + +static time_t parse_iso8601_bip(const gchar *str, int len) +{ + gchar *tstr; + struct tm tm; + gint nr; + gchar tz; + time_t time; + time_t tz_offset = 0; + + if (str == NULL) + return -1; + + memset(&tm, 0, sizeof(struct tm)); + + /* According to spec the time doesn't have to be null terminated */ + if (str[len - 1] != '\0') { + tstr = g_malloc(len + 1); + strncpy(tstr, str, len); + tstr[len] = '\0'; + } else + tstr = g_strdup(str); + + nr = sscanf(tstr, "%04u%02u%02uT%02u%02u%02u%c", + &tm.tm_year, &tm.tm_mon, &tm.tm_mday, + &tm.tm_hour, &tm.tm_min, &tm.tm_sec, + &tz); + + g_free(tstr); + + /* Fixup the tm values */ + tm.tm_year -= 1900; /* Year since 1900 */ + tm.tm_mon--; /* Months since January, values 0-11 */ + tm.tm_isdst = -1; /* Daylight savings information not avail */ + + if (nr < 6) { + /* Invalid time format */ + return -1; + } + + time = mktime(&tm); + +#if defined(HAVE_TM_GMTOFF) + tz_offset = tm.tm_gmtoff; +#elif defined(HAVE_TIMEZONE) + tz_offset = -timezone; + if (tm.tm_isdst > 0) + tz_offset += 3600; +#endif + + if (nr == 7) { /* Date/Time was in localtime (to remote device) + * already. Since we don't know anything about the + * timezone on that one we won't try to apply UTC offset + */ + time += tz_offset; + } + + return time; +} + +static int parse_handle(const char *data) +{ + int handle; + char *ptr; + + if (data == NULL) + return -1; + if (strlen(data) != HANDLE_LEN) + return -1; + handle = strtol(data, &ptr, 10); + if (ptr != data + HANDLE_LEN) + return -1; + if (handle < 0 || handle >= HANDLE_LIMIT) + return -1; + return handle; +} + +struct native_prop { + char *encoding, *pixel; + uint64_t size; +}; + +struct variant_prop { + char *encoding, *pixel, *transform; + uint64_t maxsize; +}; + +struct att_prop { + char *content_type, *charset, *name; + uint64_t size; + time_t ctime, mtime; +}; + +struct prop_object { + char *handle, *name; + GSList *native, *variant, *att; +}; + +static void free_native_prop(struct native_prop *prop) +{ + DBG(""); + + if (prop == NULL) + return; + g_free(prop->encoding); + g_free(prop->pixel); + g_free(prop); +} + +static void free_variant_prop(struct variant_prop *prop) +{ + DBG(""); + + if (prop == NULL) + return; + g_free(prop->encoding); + g_free(prop->pixel); + g_free(prop->transform); + g_free(prop); +} + +static void free_att_prop(struct att_prop *prop) +{ + DBG(""); + + if (prop == NULL) + return; + g_free(prop->content_type); + g_free(prop->charset); + g_free(prop->name); + g_free(prop); +} + +static void free_prop_object(struct prop_object *object) +{ + GSList *list; + + DBG(""); + + if (object == NULL) + return; + for (list = object->native; list != NULL; list = g_slist_next(list)) + free_native_prop(list->data); + for (list = object->variant; list != NULL; list = g_slist_next(list)) + free_variant_prop(list->data); + for (list = object->att; list != NULL; list = g_slist_next(list)) + free_att_prop(list->data); + g_slist_free(object->native); + g_slist_free(object->variant); + g_slist_free(object->att); + g_free(object->handle); + g_free(object->name); + g_free(object); +} + +static gboolean parse_attrib_native(struct native_prop *prop, const gchar *key, + const gchar *value, GError **gerr) +{ + DBG(""); + + if (g_str_equal(key, "encoding")) { + if (convBIP2IM(value) == NULL) + goto invalid; + prop->encoding = g_strdup(value); + } else if (g_str_equal(key, "pixel")) { + if (!parse_pixel_range(value, NULL, NULL, NULL)) + goto invalid; + prop->pixel = g_strdup(value); + } else if (g_str_equal(key, "size")) { + prop->size = parse_unsignednumber(value); + if (prop->size == 0) + goto invalid; + } else { + g_set_error(gerr, G_MARKUP_ERROR, + G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE, NULL); + return FALSE; + } + return TRUE; +invalid: + g_set_error(gerr, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, + NULL); + return FALSE; +} + +static gboolean parse_attrib_variant(struct variant_prop *prop, + const gchar *key, + const gchar *value, GError **gerr) +{ + DBG(""); + + if (g_str_equal(key, "encoding")) { + if (convBIP2IM(value) == NULL) + goto invalid; + prop->encoding = g_strdup(value); + } else if (g_str_equal(key, "pixel")) { + if (!parse_pixel_range(value, NULL, NULL, NULL)) + goto invalid; + prop->pixel = g_strdup(value); + } else if (g_str_equal(key, "maxsize")) { + prop->maxsize = parse_unsignednumber(value); + if (prop->maxsize == 0) + goto invalid; + } else if (g_str_equal(key, "transform")) { + prop->transform = parse_transform_list(value); + if (prop->transform == NULL) + goto invalid; + } else { + g_set_error(gerr, G_MARKUP_ERROR, + G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE, NULL); + return FALSE; + } + return TRUE; +invalid: + g_set_error(gerr, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, + NULL); + return FALSE; +} + +static gboolean parse_attrib_att(struct att_prop *prop, const gchar *key, + const gchar *value, GError **gerr) +{ + DBG(""); + + if (g_str_equal(key, "content-type")) { + prop->content_type = g_strdup(value); + } else if (g_str_equal(key, "charset")) { + prop->charset = g_strdup(value); + } else if (g_str_equal(key, "name")) { + prop->name = g_strdup(value); + } else if (g_str_equal(key, "size")) { + prop->size = parse_unsignednumber(value); + if (prop->size == 0) + goto invalid; + } else if (g_str_equal(key, "created")) { + prop->ctime = parse_iso8601_bip(value, strlen(value)); + if (prop->ctime == -1) + goto invalid; + } else if (g_str_equal(key, "modified")) { + prop->mtime = parse_iso8601_bip(value, strlen(value)); + if (prop->mtime == -1) + goto invalid; + } else { + g_set_error(gerr, G_MARKUP_ERROR, + G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE, NULL); + return FALSE; + } + return TRUE; +invalid: + g_set_error(gerr, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, + NULL); + return FALSE; +} + +static struct att_prop *parse_elem_att(const gchar **names, + const gchar **values, GError **gerr) +{ + gchar **key; + struct att_prop *prop = g_new0(struct att_prop, 1); + + DBG(""); + + for (key = (gchar **) names; *key; key++, values++) { + if (!parse_attrib_att(prop, *key, *values, gerr)) { + free_att_prop(prop); + return NULL; + } + } + return prop; +} + +static struct variant_prop *parse_elem_variant(const gchar **names, + const gchar **values, GError **gerr) +{ + gchar **key; + struct variant_prop *prop = g_new0(struct variant_prop, 1); + + DBG(""); + + for (key = (gchar **) names; *key; key++, values++) { + if (!parse_attrib_variant(prop, *key, *values, gerr)) { + free_variant_prop(prop); + return NULL; + } + } + if (prop->transform == NULL) + prop->transform = g_strdup("stretch crop fill"); + return prop; +} + +static struct native_prop *parse_elem_native(const gchar **names, + const gchar **values, GError **gerr) +{ + gchar **key; + struct native_prop *prop = g_new0(struct native_prop, 1); + + DBG(""); + + for (key = (gchar **) names; *key; key++, values++) { + if (!parse_attrib_native(prop, *key, *values, gerr)) { + free_native_prop(prop); + return NULL; + } + } + return prop; +} + +static gboolean parse_attrib_prop(struct prop_object *prop, const gchar *key, + const gchar *value, GError **gerr) +{ + DBG(""); + + if (g_str_equal(key, "handle")) { + if (parse_handle(value) < 0) + goto invalid; + prop->handle = g_strdup(value); + } else if (g_str_equal(key, "friendly-name")) { + prop->name = g_strdup(value); + } else if (g_str_equal(key, "version")) { + // pass; + } else { + g_set_error(gerr, G_MARKUP_ERROR, + G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE, NULL); + return FALSE; + } + return TRUE; +invalid: + g_set_error(gerr, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, + NULL); + return FALSE; +} + +static struct prop_object *parse_elem_prop(const gchar **names, + const gchar **values, GError **gerr) +{ + gchar **key; + struct prop_object *prop = g_new0(struct prop_object, 1); + + DBG(""); + + for (key = (gchar **) names; *key; key++, values++) { + if (!parse_attrib_prop(prop, *key, *values, gerr)) { + free_prop_object(prop); + return NULL; + } + } + return prop; +} + +static void prop_element(GMarkupParseContext *ctxt, + const gchar *element, + const gchar **names, + const gchar **values, + gpointer user_data, + GError **gerr) +{ + struct prop_object **obj = user_data; + + DBG(""); + + if (g_str_equal(element, "image-properties")) { + if (*obj != NULL) { + free_prop_object(*obj); + *obj = NULL; + goto invalid; + } + *obj = parse_elem_prop(names, values, gerr); + } else if (g_str_equal(element, "native")) { + struct native_prop *prop; + + if (*obj == NULL) + goto invalid; + prop = parse_elem_native(names, values, gerr); + (*obj)->native = g_slist_append((*obj)->native, prop); + } else if (g_str_equal(element, "variant")) { + struct variant_prop *prop; + + if (*obj == NULL) + goto invalid; + prop = parse_elem_variant(names, values, gerr); + (*obj)->variant = g_slist_append((*obj)->variant, prop); + } else if (g_str_equal(element, "attachment")) { + struct att_prop *prop; + + if (*obj == NULL) + goto invalid; + prop = parse_elem_att(names, values, gerr); + (*obj)->att = g_slist_append((*obj)->att, prop); + } else { + if (*obj != NULL) { + free_prop_object(*obj); + *obj = NULL; + } + goto invalid; + } + + return; +invalid: + g_set_error(gerr, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, + NULL); +} + +static const GMarkupParser properties_parser = { + prop_element, + NULL, + NULL, + NULL, + NULL +}; + +struct prop_object *parse_properties(char *data, unsigned int length, + int *err) +{ + struct prop_object *prop = NULL; + gboolean status; + GError *gerr = NULL; + GMarkupParseContext *ctxt = g_markup_parse_context_new( + &properties_parser, 0, &prop, NULL); + + DBG(""); + + if (err != NULL) + *err = 0; + status = g_markup_parse_context_parse(ctxt, data, length, &gerr); + g_markup_parse_context_free(ctxt); + if (!status) { + if (err != NULL) + *err = -EINVAL; + free_prop_object(prop); + prop = NULL; + } + return prop; +} + +gboolean verify_properties(struct prop_object *obj) +{ + GSList *list; + + if (obj->handle == NULL) + return FALSE; + + for (list = obj->native; list != NULL; list = g_slist_next(list)) { + struct native_prop *prop = list->data; + + if (prop->encoding == NULL || prop->pixel == NULL) + return FALSE; + } + + for (list = obj->variant; list != NULL; list = g_slist_next(list)) { + struct variant_prop *prop = list->data; + + if (prop->encoding == NULL || prop->pixel == NULL) + return FALSE; + } + + for (list = obj->att; list != NULL; list = g_slist_next(list)) { + struct att_prop *prop = list->data; + + if (prop->content_type == NULL || prop->name == NULL) + return FALSE; + } + + return TRUE; +} + +void append_properties(DBusMessageIter *args, struct prop_object *obj) +{ + DBusMessageIter dict, iter; + GSList *list; + + dbus_message_iter_open_container(args, DBUS_TYPE_ARRAY, + 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, + &dict); + + dbus_message_iter_open_container(&dict, 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, + &iter); + g_dbus_dict_append_entry(&iter, "handle", DBUS_TYPE_STRING, + &obj->handle); + g_dbus_dict_append_entry(&iter, "name", DBUS_TYPE_STRING, &obj->name); + dbus_message_iter_close_container(&dict, &iter); + + for (list = obj->native; list != NULL; list = g_slist_next(list)) { + struct native_prop *prop = list->data; + static char *native_str = "native"; + + dbus_message_iter_open_container(&dict, 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, + &iter); + g_dbus_dict_append_entry(&iter, "type", DBUS_TYPE_STRING, + &native_str); + if (prop->encoding) + g_dbus_dict_append_entry(&iter, "encoding", + DBUS_TYPE_STRING, + &prop->encoding); + if (prop->pixel) + g_dbus_dict_append_entry(&iter, "pixel", + DBUS_TYPE_STRING, + &prop->pixel); + if (prop->size) + g_dbus_dict_append_entry(&iter, "size", + DBUS_TYPE_UINT64, + &prop->size); + dbus_message_iter_close_container(&dict, &iter); + } + + for (list = obj->variant; list != NULL; list = g_slist_next(list)) { + struct variant_prop *prop = list->data; + static char *variant_str = "variant"; + + dbus_message_iter_open_container(&dict, 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, + &iter); + g_dbus_dict_append_entry(&iter, "type", DBUS_TYPE_STRING, + &variant_str); + if (prop->encoding) + g_dbus_dict_append_entry(&iter, "encoding", + DBUS_TYPE_STRING, + &prop->encoding); + if (prop->pixel) + g_dbus_dict_append_entry(&iter, "pixel", + DBUS_TYPE_STRING, + &prop->pixel); + if (prop->maxsize) + g_dbus_dict_append_entry(&iter, "maxsize", + DBUS_TYPE_UINT64, + &prop->maxsize); + if (prop->transform) + g_dbus_dict_append_entry(&iter, "transformation", + DBUS_TYPE_STRING, + &prop->transform); + dbus_message_iter_close_container(&dict, &iter); + } + + for (list = obj->att; list != NULL; list = g_slist_next(list)) { + struct att_prop *prop = list->data; + static char *attachment_str = "attachment"; + + dbus_message_iter_open_container(&dict, 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, + &iter); + g_dbus_dict_append_entry(&iter, "type", DBUS_TYPE_STRING, + &attachment_str); + if (prop->content_type) + g_dbus_dict_append_entry(&iter, "content-type", + DBUS_TYPE_STRING, + &prop->content_type); + if (prop->charset) + g_dbus_dict_append_entry(&iter, "charset", + DBUS_TYPE_STRING, + &prop->charset); + if (prop->name) + g_dbus_dict_append_entry(&iter, "name", + DBUS_TYPE_STRING, + &prop->name); + if (prop->size) + g_dbus_dict_append_entry(&iter, "size", + DBUS_TYPE_UINT64, + &prop->size); + if (prop->ctime) + g_dbus_dict_append_entry(&iter, "ctime", + DBUS_TYPE_UINT64, + &prop->ctime); + if (prop->mtime) + g_dbus_dict_append_entry(&iter, "mtime", + DBUS_TYPE_UINT64, + &prop->mtime); + dbus_message_iter_close_container(&dict, &iter); + } + + dbus_message_iter_close_container(args, &dict); +} diff --git a/obexd/client/bip-common.h b/obexd/client/bip-common.h new file mode 100644 index 000000000..0fee54636 --- /dev/null +++ b/obexd/client/bip-common.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * + * OBEX Client + * + * Copyright (C) 2024 Collabora Ltd. + * + * + */ + +#include +#include "gdbus/gdbus.h" + +struct prop_object; + +struct prop_object *parse_properties(char *data, unsigned int length, + int *err); +gboolean verify_properties(struct prop_object *obj); +void append_properties(DBusMessageIter *args, struct prop_object *obj); diff --git a/obexd/client/bip.c b/obexd/client/bip.c index 252bc4cec..e5ea5468c 100644 --- a/obexd/client/bip.c +++ b/obexd/client/bip.c @@ -20,6 +20,7 @@ #include "transfer.h" #include "session.h" #include "driver.h" +#include "bip-common.h" #include "bip.h" #define OBEX_BIP_AVRCP_UUID \ @@ -38,6 +39,102 @@ struct bip_avrcp_data { struct obc_session *session; }; +static void image_properties_complete_cb(struct obc_session *session, + struct obc_transfer *transfer, + GError *err, void *user_data) +{ + DBusMessage *message = user_data; + DBusMessage *reply = NULL; + DBusMessageIter iter; + char *contents = NULL; + size_t size; + int perr; + struct prop_object *prop = NULL; + + if (err != NULL) { + reply = g_dbus_create_error(message, + ERROR_INTERFACE ".Failed", + "%s", err->message); + goto done; + } + + perr = obc_transfer_get_contents(transfer, &contents, &size); + if (perr < 0) { + reply = g_dbus_create_error(message, + ERROR_INTERFACE ".Failed", + "Error reading contents: %s", + strerror(-perr)); + goto done; + } + + prop = parse_properties(contents, size, &perr); + if (prop == NULL) { + reply = g_dbus_create_error(message, + ERROR_INTERFACE ".Failed", + "Error parsing contents: %s", + strerror(-perr)); + goto done; + } + + if (!verify_properties(prop)) { + reply = g_dbus_create_error(message, + ERROR_INTERFACE ".Failed", + "Error verifying contents"); + goto done; + } + + reply = dbus_message_new_method_return(message); + dbus_message_iter_init_append(reply, &iter); + append_properties(&iter, prop); + +done: + g_dbus_send_message(conn, reply); + g_free(contents); + dbus_message_unref(message); +} + +static DBusMessage *get_image_properties(DBusConnection *connection, + DBusMessage *message, void *user_data) +{ + struct bip_avrcp_data *bip_avrcp = user_data; + const char *handle = NULL; + struct obc_transfer *transfer; + GObexHeader *header; + DBusMessage *reply = NULL; + GError *err = NULL; + + DBG(""); + + if (dbus_message_get_args(message, NULL, + DBUS_TYPE_STRING, &handle, + DBUS_TYPE_INVALID) == FALSE) { + reply = g_dbus_create_error(message, + ERROR_INTERFACE ".InvalidArguments", NULL); + return reply; + } + + transfer = obc_transfer_get("x-bt/img-properties", NULL, NULL, &err); + if (transfer == NULL) + goto fail; + + header = g_obex_header_new_unicode(IMG_HANDLE_TAG, handle); + obc_transfer_add_header(transfer, header); + + if (!obc_session_queue(bip_avrcp->session, transfer, + image_properties_complete_cb, message, &err)) + goto fail; + + 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 *get_thumbnail(DBusConnection *connection, DBusMessage *message, void *user_data) { @@ -79,6 +176,10 @@ fail: } static const GDBusMethodTable bip_avrcp_methods[] = { + { GDBUS_ASYNC_METHOD("Properties", + GDBUS_ARGS({ "handle", "s"}), + GDBUS_ARGS({ "properties", "aa{sv}" }), + get_image_properties) }, { GDBUS_ASYNC_METHOD("GetThumbnail", GDBUS_ARGS({ "file", "s" }, { "handle", "s"}), GDBUS_ARGS({ "transfer", "o" }, { "properties", "a{sv}" }), From patchwork Tue Sep 17 07:42:15 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: =?utf-8?q?Fr=C3=A9d=C3=A9ric_Danis?= X-Patchwork-Id: 829271 Received: from bali.collaboradmins.com (bali.collaboradmins.com [148.251.105.195]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4CE72136337 for ; Tue, 17 Sep 2024 07:42:27 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=148.251.105.195 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1726558949; cv=none; b=EYbeOH+ShXwfNFKG9D3LFyMUIONO5atUmSJAq8VkKU6ZijK/gS8e1O9vDgGmth/1Kwq+Ezn19BunFAbjFLc9KV4570CryZvq4rKTLNhEk0U6lvZIaWpZN46RTybhf8yQdUxZGFgpJhjtNCYVzGWIPoa/FVzQHAQ7NLiKC5D+HaE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1726558949; c=relaxed/simple; bh=smFRCYsWNavwVvSktnaHmaigHf+1dqSKNebGultjZW8=; h=From:To:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version:Content-Type; b=KdnesKbx00ZSIrJiqKYWkNwpIrdEebjt4nPLwzoGQI6X3nS7hCsAbO05VXLYdarDYhw9WKoWDBo7p91zXjqGrucz/fTazI13EXlS3U45cnyOO2Oo7pzasodHV4Rl+0dPi4KVnsHocakpkbs+iwgD+nsuuv4yVW/GK8WnDxwdCOY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com; spf=pass smtp.mailfrom=collabora.com; dkim=pass (2048-bit key) header.d=collabora.com header.i=@collabora.com header.b=l5nmGY0L; arc=none smtp.client-ip=148.251.105.195 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=collabora.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=collabora.com header.i=@collabora.com header.b="l5nmGY0L" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=collabora.com; s=mail; t=1726558945; bh=smFRCYsWNavwVvSktnaHmaigHf+1dqSKNebGultjZW8=; h=From:To:Subject:Date:In-Reply-To:References:From; b=l5nmGY0LJ07kq7kgXnaa0qEetlSq3VSQj/DQ51FZ0QbEDgYKmbmOc+m1OGF/dReaR pf5hm6ZiQUGGvAGGkl3EQ6V13+cJgVacTQzvlomIVyPWDhebknM/FW0Ec8pW362fRY 2WuAiWGlTgsz8dK2C7/KVn9PjcY39Exmn58BzUl7kfgOKT20EJdZl5tLdgxjtmzD6z VeNfOGiZGrCceE1mHkitM/O/N7t0NbAmyZcu55NiJnHAY3ZtmmcQ54Mr8KK2RwEtcD B/VQc7KlHu75hwW/W5okUAQVlLgdPqXsA67JdFg4IQmBqnTT+amxE28ut/IzcMhmY9 bs7+Mv8V20Hgw== Received: from fdanis-XPS-13-9370.. (67.227.121.78.rev.sfr.net [78.121.227.67]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) (Authenticated sender: fdanis) by bali.collaboradmins.com (Postfix) with ESMTPSA id 62BA917E100A for ; Tue, 17 Sep 2024 09:42:25 +0200 (CEST) From: =?utf-8?q?Fr=C3=A9d=C3=A9ric_Danis?= To: linux-bluetooth@vger.kernel.org Subject: [PATCH BlueZ v4 3/5] obexd: Add Get to org.bluez.obex.Image Date: Tue, 17 Sep 2024 09:42:15 +0200 Message-Id: <20240917074217.231677-4-frederic.danis@collabora.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240917074217.231677-1-frederic.danis@collabora.com> References: <20240917074217.231677-1-frederic.danis@collabora.com> Precedence: bulk X-Mailing-List: linux-bluetooth@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Retrieves the image corresponding to the handle and the description, as one of the descriptions retrieved by Properties, and store it in a local file. If the "transform" property description exists it should be set to one of the value listed by Properties for this description. --- obexd/client/bip-common.c | 21 ++++- obexd/client/bip-common.h | 5 ++ obexd/client/bip.c | 168 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 193 insertions(+), 1 deletion(-) diff --git a/obexd/client/bip-common.c b/obexd/client/bip-common.c index 9e96c81d8..613b52ceb 100644 --- a/obexd/client/bip-common.c +++ b/obexd/client/bip-common.c @@ -49,7 +49,7 @@ static const gchar *convBIP2IM(const gchar *encoding) return NULL; } -static gboolean parse_pixel_range(const gchar *dim, unsigned int *lower_ret, +gboolean parse_pixel_range(const gchar *dim, unsigned int *lower_ret, unsigned int *upper_ret, gboolean *fixed_ratio_ret) { @@ -139,6 +139,18 @@ char *transforms[] = { NULL }; +gboolean verify_encoding(const char *encoding) +{ + struct encconv_pair *et = encconv_table; + + while (et->bip) { + if (g_strcmp0(encoding, et->bip) == 0) + return TRUE; + et++; + } + return FALSE; +} + static gboolean verify_transform(const char *transform) { char **str = transforms; @@ -151,6 +163,13 @@ static gboolean verify_transform(const char *transform) return FALSE; } +char *parse_transform(const char *transform) +{ + if (!verify_transform(transform)) + return NULL; + return g_strdup(transform); +} + static char *parse_transform_list(const char *transform) { char **args = NULL, **arg = NULL; diff --git a/obexd/client/bip-common.h b/obexd/client/bip-common.h index 0fee54636..6e7aac375 100644 --- a/obexd/client/bip-common.h +++ b/obexd/client/bip-common.h @@ -13,6 +13,11 @@ struct prop_object; +gboolean parse_pixel_range(const gchar *dim, unsigned int *lower_ret, + unsigned int *upper_ret, + gboolean *fixed_ratio_ret); +gboolean verify_encoding(const char *encoding); +char *parse_transform(const char *transform); struct prop_object *parse_properties(char *data, unsigned int length, int *err); gboolean verify_properties(struct prop_object *obj); diff --git a/obexd/client/bip.c b/obexd/client/bip.c index e5ea5468c..9d9ecec81 100644 --- a/obexd/client/bip.c +++ b/obexd/client/bip.c @@ -32,6 +32,14 @@ #define IMAGE_UUID "0000111A-0000-1000-8000-00805f9b34fb" #define IMG_HANDLE_TAG 0x30 +#define IMG_DESC_TAG 0x71 + +#define EOL_CHARS "\n" +#define IMG_DESC_BEGIN "" EOL_CHARS +#define IMG_BEGIN "" EOL_CHARS +#define IMG_DESC_END "" EOL_CHARS static DBusConnection *conn; @@ -175,11 +183,171 @@ fail: return reply; } +static gboolean parse_get_image_dict(DBusMessage *msg, char **path, + char **handle, char **pixel, + char **encoding, uint64_t *maxsize, + char **transform) +{ + DBusMessageIter iter, array; + + DBG(""); + + *path = NULL; + *handle = NULL; + *pixel = NULL; + *encoding = NULL; + *transform = NULL; + + dbus_message_iter_init(msg, &iter); + + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) + goto failed; + dbus_message_iter_get_basic(&iter, path); + *path = g_strdup(*path); + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) + goto failed; + dbus_message_iter_next(&iter); + dbus_message_iter_get_basic(&iter, handle); + *handle = g_strdup(*handle); + dbus_message_iter_next(&iter); + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) + goto failed; + + dbus_message_iter_recurse(&iter, &array); + + while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_DICT_ENTRY) { + DBusMessageIter entry, value; + const char *key, *val; + + dbus_message_iter_recurse(&array, &entry); + + if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING) + return FALSE; + dbus_message_iter_get_basic(&entry, &key); + dbus_message_iter_next(&entry); + dbus_message_iter_recurse(&entry, &value); + switch (dbus_message_iter_get_arg_type(&value)) { + case DBUS_TYPE_STRING: + dbus_message_iter_get_basic(&value, &val); + if (g_str_equal(key, "pixel")) { + if (!parse_pixel_range(val, NULL, NULL, NULL)) + goto failed; + *pixel = g_strdup(val); + } else if (g_str_equal(key, "encoding")) { + if (!verify_encoding(val)) + goto failed; + *encoding = g_strdup(val); + if (*encoding == NULL) + goto failed; + } else if (g_str_equal(key, "transformation")) { + *transform = parse_transform(val); + if (*transform == NULL) + goto failed; + } + break; + case DBUS_TYPE_UINT64: + if (g_str_equal(key, "maxsize") == TRUE) { + dbus_message_iter_get_basic(&value, maxsize); + if (*maxsize == 0) + goto failed; + } + break; + } + dbus_message_iter_next(&array); + } + + if (*pixel == NULL) + *pixel = strdup(""); + if (*encoding == NULL) + *encoding = strdup(""); + + DBG("pixel: '%s' encoding: '%s' maxsize: '%lu' transform: '%s'", + *pixel, *encoding, *maxsize, *transform + ); + + return TRUE; +failed: + g_free(*path); + g_free(*handle); + g_free(*pixel); + g_free(*encoding); + g_free(*transform); + return FALSE; +} + +static DBusMessage *get_image(DBusConnection *connection, + DBusMessage *message, void *user_data) +{ + struct bip_avrcp_data *bip_avrcp = user_data; + char *handle = NULL, *image_path = NULL, *transform = NULL, + *encoding = NULL, *pixel = NULL; + uint64_t maxsize; + struct obc_transfer *transfer; + GObexHeader *header; + DBusMessage *reply = NULL; + GString *descriptor = NULL; + GError *err = NULL; + + DBG(""); + + if (!parse_get_image_dict(message, &image_path, &handle, &pixel, + &encoding, &maxsize, &transform)) + return g_dbus_create_error(message, + ERROR_INTERFACE ".InvalidArguments", NULL); + + transfer = obc_transfer_get("x-bt/img-img", NULL, image_path, &err); + if (transfer == NULL) { + reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed", + "%s", + err->message); + g_error_free(err); + goto fail; + } + + header = g_obex_header_new_unicode(IMG_HANDLE_TAG, handle); + obc_transfer_add_header(transfer, header); + + descriptor = g_string_new(IMG_DESC_BEGIN); + g_string_append_printf(descriptor, IMG_BEGIN, encoding, pixel); + if (transform != NULL) + g_string_append_printf(descriptor, IMG_TRANSFORM, transform); + g_string_append(descriptor, IMG_END); + descriptor = g_string_append(descriptor, IMG_DESC_END); + header = g_obex_header_new_bytes(IMG_DESC_TAG, descriptor->str, + descriptor->len); + obc_transfer_add_header(transfer, header); + g_string_free(descriptor, TRUE); + + if (!obc_session_queue(bip_avrcp->session, transfer, NULL, NULL, + &err)) { + reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed", + "%s", + err->message); + g_error_free(err); + goto fail; + } + + reply = obc_transfer_create_dbus_reply(transfer, message); + +fail: + g_free(handle); + g_free(image_path); + g_free(transform); + g_free(encoding); + g_free(pixel); + return reply; +} + static const GDBusMethodTable bip_avrcp_methods[] = { { GDBUS_ASYNC_METHOD("Properties", GDBUS_ARGS({ "handle", "s"}), GDBUS_ARGS({ "properties", "aa{sv}" }), get_image_properties) }, + { GDBUS_ASYNC_METHOD("Get", + GDBUS_ARGS({ "file", "s" }, { "handle", "s"}, + {"properties", "a{sv}"}), + GDBUS_ARGS({ "transfer", "o" }, { "properties", "a{sv}" }), + get_image) }, { GDBUS_ASYNC_METHOD("GetThumbnail", GDBUS_ARGS({ "file", "s" }, { "handle", "s"}), GDBUS_ARGS({ "transfer", "o" }, { "properties", "a{sv}" }), From patchwork Tue Sep 17 07:42:16 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: =?utf-8?q?Fr=C3=A9d=C3=A9ric_Danis?= X-Patchwork-Id: 829447 Received: from bali.collaboradmins.com (bali.collaboradmins.com [148.251.105.195]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B0D2D136349 for ; Tue, 17 Sep 2024 07:42:27 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=148.251.105.195 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1726558949; cv=none; b=MtY2smTYEWrlEguR9th43bSt1Kt0JCYSG5bk0qxdIxCZsFFb6LoFDIAPNfQU0sg17XCVm//YLuXs90R+KkIXYjQpFCuXheGHc9Yf18kRkZdF0OnhZYAZljquQ2ok4T8lRP84utfzh9TIFJOLxfG15sBhotEhrQtxZvG2lynfpow= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1726558949; c=relaxed/simple; bh=xfgCIiupl1IIWSNbxZ4zd48NLjvNMVHp8UG6XCfe7NI=; h=From:To:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version:Content-Type; b=CR8+tQcUTIZOGnv6GaNOxRMlVkWd8cAhTCnjcq1MsrH+zO7g1XtqSgd6V1pUwfX6oBiJK4swxT04otxunOBMFaDBS34thl1qwAQ28cR/KipR4s6SLuRLoMiGXOd5crQYbgUv7ucOGcxvaQi/01dV+DBZXm+IdF9R9pJzyUYdG3c= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com; spf=pass smtp.mailfrom=collabora.com; dkim=pass (2048-bit key) header.d=collabora.com header.i=@collabora.com header.b=odBJAWop; arc=none smtp.client-ip=148.251.105.195 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=collabora.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=collabora.com header.i=@collabora.com header.b="odBJAWop" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=collabora.com; s=mail; t=1726558945; bh=xfgCIiupl1IIWSNbxZ4zd48NLjvNMVHp8UG6XCfe7NI=; h=From:To:Subject:Date:In-Reply-To:References:From; b=odBJAWopP2BFm3WVYCVuaa5I9MkEVPHqgVk/4chmAI9JpgeLIiCn71upzLYtw9qWj a3mkm5AAwoQ28HaPi8PwpcwnebFt6IQEDCBuBiqT2Dp8PwM2nEtS9E1zEtmelm9VVK xtnn/NSTjV4dWWV3iHhkQlaog8L07DEfBU3bfRyzY7YlStY9+FwIi9xrTmypp+0Tux ExpkFPmhmUEpEay/R06Vc9yfBnf4+GoGvGfmXLP0lg+BsExW0U6hvdYiaTO67LG9Qt r9sI8C4brtB3PHa4UF/NzQkF09LXJt5nojxNhr0lkn4SE+GchEuhDuawBlGZqonRWp 0JW331eYIjslw== Received: from fdanis-XPS-13-9370.. (67.227.121.78.rev.sfr.net [78.121.227.67]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) (Authenticated sender: fdanis) by bali.collaboradmins.com (Postfix) with ESMTPSA id A302C17E1013 for ; Tue, 17 Sep 2024 09:42:25 +0200 (CEST) From: =?utf-8?q?Fr=C3=A9d=C3=A9ric_Danis?= To: linux-bluetooth@vger.kernel.org Subject: [PATCH BlueZ v4 4/5] avrcp: Update controller SDP record with cover art support Date: Tue, 17 Sep 2024 09:42:16 +0200 Message-Id: <20240917074217.231677-5-frederic.danis@collabora.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240917074217.231677-1-frederic.danis@collabora.com> References: <20240917074217.231677-1-frederic.danis@collabora.com> Precedence: bulk X-Mailing-List: linux-bluetooth@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 --- profiles/audio/avrcp.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/profiles/audio/avrcp.c b/profiles/audio/avrcp.c index fe24b5a92..c2c901a65 100644 --- a/profiles/audio/avrcp.c +++ b/profiles/audio/avrcp.c @@ -413,12 +413,14 @@ static sdp_record_t *avrcp_ct_record(bool browsing) sdp_record_t *record; sdp_data_t *psm[2], *version, *features; uint16_t lp = AVCTP_CONTROL_PSM; - uint16_t avctp_ver = 0x0103; + uint16_t avctp_ver = 0x0106; uint16_t feat = ( AVRCP_FEATURE_CATEGORY_1 | - AVRCP_FEATURE_CATEGORY_2 | - AVRCP_FEATURE_CATEGORY_3 | - AVRCP_FEATURE_CATEGORY_4 | - AVRCP_FEATURE_CT_GET_THUMBNAIL); + AVRCP_FEATURE_CATEGORY_2 | + AVRCP_FEATURE_CATEGORY_3 | + AVRCP_FEATURE_CATEGORY_4 | + AVRCP_FEATURE_CT_GET_IMAGE_PROP | + AVRCP_FEATURE_CT_GET_IMAGE | + AVRCP_FEATURE_CT_GET_THUMBNAIL); record = sdp_record_alloc(); if (!record) From patchwork Tue Sep 17 07:42:17 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: =?utf-8?q?Fr=C3=A9d=C3=A9ric_Danis?= X-Patchwork-Id: 829269 Received: from bali.collaboradmins.com (bali.collaboradmins.com [148.251.105.195]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id CF41C1531CB for ; Tue, 17 Sep 2024 07:42:30 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=148.251.105.195 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1726558952; cv=none; b=TgFanOemqwcj1O9P/A5jF5CyBHm0pUOXtcVUKFbdlCdDWI58ejQfxUEcH3U+gtj3hgtkR6nCbTQdr5e6kWyrvUVwbzL8G0fseS/wH3CuVyH+9VAjkEleyoHBvt4TozjDIq70cCN6xw2FXoveNi4HEW0MiU8q2EwsqfNmcYL8h/8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1726558952; c=relaxed/simple; bh=gEWuxhGv6DQWM/f5KSXhlN++SrGJaN6FwEHyxQ1KIdA=; h=From:To:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version:Content-Type; b=Sk4afWU7lXh3Fk49zO24QGjL3x7iU9bNEsOATUpxFfI0rPfcyH16mDk+hNtBf5tOzwxzqbSTbSYHxSIa7PAr+OYd/M3ERGYc3kH5OVVOCrFUiZ+8WHgLWKU3BVsqS0cjGZxGCht+4tT6ZNCHjB8tTw/RGo7DlmZt5Lr/iK15FVU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com; spf=pass smtp.mailfrom=collabora.com; dkim=pass (2048-bit key) header.d=collabora.com header.i=@collabora.com header.b=NP6k5Rb2; arc=none smtp.client-ip=148.251.105.195 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=collabora.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=collabora.com header.i=@collabora.com header.b="NP6k5Rb2" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=collabora.com; s=mail; t=1726558946; bh=gEWuxhGv6DQWM/f5KSXhlN++SrGJaN6FwEHyxQ1KIdA=; h=From:To:Subject:Date:In-Reply-To:References:From; b=NP6k5Rb2vLcCXfTgjx5q0kWYdUAJfeLJfAMbjmxqPm3XX90GF3cL9fu2CVpuVixom H3K3NU8gZTvklAUWcCAk7inwRxIz2TQhZlZnOJ8lzJcA1yQXKmhhFn791PZIx9vZkT Ld3yLNPADMtHrgToyJBuuzCVBgo88nlPjsA8hy/35CdXnJ8WAd2SONjAIp9JdqxMVB 3B4NYPS6fexVAGY4KK3KTn4SRUWQda8P+a11ROWpCnTHNzbvFXYfeLwc05F7DjUwva TxoVfCvdKwhKMqHjzIscjFyVTmme3pPbkasOaMt9B9aGqWLKD06O7moAQuUXUqzCSk e6C0YdMge/8YQ== Received: from fdanis-XPS-13-9370.. (67.227.121.78.rev.sfr.net [78.121.227.67]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) (Authenticated sender: fdanis) by bali.collaboradmins.com (Postfix) with ESMTPSA id E142717E1034 for ; Tue, 17 Sep 2024 09:42:25 +0200 (CEST) From: =?utf-8?q?Fr=C3=A9d=C3=A9ric_Danis?= To: linux-bluetooth@vger.kernel.org Subject: [PATCH BlueZ v4 5/5] doc: Add description of org.bluez.obex.Image Date: Tue, 17 Sep 2024 09:42:17 +0200 Message-Id: <20240917074217.231677-6-frederic.danis@collabora.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240917074217.231677-1-frederic.danis@collabora.com> References: <20240917074217.231677-1-frederic.danis@collabora.com> Precedence: bulk X-Mailing-List: linux-bluetooth@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 This new interface allows to get the image referenced in the audio metadata ImgHandle available in org.bluez.MediaPlayer track properties. The image handle is only available in track info if an OBEX session is connected to the ObexPort port provided in org.bluez.MediaPlayer properties. --- Makefile.am | 9 ++- doc/org.bluez.obex.Image.rst | 118 +++++++++++++++++++++++++++++++++++ 2 files changed, 124 insertions(+), 3 deletions(-) create mode 100644 doc/org.bluez.obex.Image.rst diff --git a/Makefile.am b/Makefile.am index 88044aa05..3eb0a5302 100644 --- a/Makefile.am +++ b/Makefile.am @@ -382,7 +382,8 @@ man_MANS += doc/org.bluez.obex.Client.5 doc/org.bluez.obex.Session.5 \ doc/org.bluez.obex.PhonebookAccess.5 \ doc/org.bluez.obex.MessageAccess.5 \ doc/org.bluez.obex.Message.5 \ - doc/org.bluez.obex.AgentManager.5 doc/org.bluez.obex.Agent.5 + doc/org.bluez.obex.AgentManager.5 doc/org.bluez.obex.Agent.5 \ + doc/org.bluez.obex.Image.5 endif manual_pages += src/bluetoothd.8 manual_pages += doc/l2cap.7 doc/rfcomm.7 @@ -415,7 +416,8 @@ manual_pages += doc/org.bluez.obex.Client.5 doc/org.bluez.obex.Session.5 \ doc/org.bluez.obex.PhonebookAccess.5 \ doc/org.bluez.obex.MessageAccess.5 \ doc/org.bluez.obex.Message.5 \ - doc/org.bluez.obex.AgentManager.5 doc/org.bluez.obex.Agent.5 + doc/org.bluez.obex.AgentManager.5 doc/org.bluez.obex.Agent.5 \ + doc/org.bluez.obex.Image.5 EXTRA_DIST += src/genbuiltin src/bluetooth.conf \ src/main.conf profiles/network/network.conf \ @@ -497,7 +499,8 @@ EXTRA_DIST += doc/org.bluez.obex.Client.rst doc/org.bluez.obex.Session.rst \ doc/org.bluez.obex.PhonebookAccess.rst \ doc/org.bluez.obex.MessageAccess.rst \ doc/org.bluez.obex.Message.rst \ - doc/org.bluez.obex.AgentManager.rst doc/org.bluez.obex.Agent.rst + doc/org.bluez.obex.AgentManager.rst doc/org.bluez.obex.Agent.rst \ + doc/org.bluez.obex.Image.rst EXTRA_DIST += doc/pics-opp.txt doc/pixit-opp.txt \ doc/pts-opp.txt diff --git a/doc/org.bluez.obex.Image.rst b/doc/org.bluez.obex.Image.rst new file mode 100644 index 000000000..386805203 --- /dev/null +++ b/doc/org.bluez.obex.Image.rst @@ -0,0 +1,118 @@ +==================== +org.bluez.obex.Image +==================== + +-------------------------------------------------- +BlueZ D-Bus OBEX Image API documentation +-------------------------------------------------- + +:Version: BlueZ +:Date: August 2024 +:Manual section: 5 +:Manual group: Linux System Administration + +Interface +========= + +:Service: org.bluez.obex +:Interface: org.bluez.obex.Image1 [experimental] +:Object path: [Session object path] + +Methods +------- + +object, dict Get(string targetfile, string handle, dict description) +```````````````````````````````````````````````````````````````````` + + Retrieves the image corresponding to the handle and the description, as + one of the descriptions retrieved by GetImageProperties, and store it in + a local file. + + If the "transform" property description exists it should be set to one + of the value listed by GetImageProperties for this description. + + If description is an empty dict, the native image will be retrieved. + + Possible errors: + + :org.bluez.obex.Error.InvalidArguments: + :org.bluez.obex.Error.Failed: + +array{dict} Properties(string handle) +````````````````````````````````````` + + Retrieves the image properties corresponding to the handle. + + The first dict entry is mandatory and correspond to 'handle' and 'name' + of the image. + + The second dict entry is mandatory and correspond to the native description + ('type':'native') of the image. + + The following dict entries are optional and correspond to variant + descriptions of the image. If the 'transform' entry exists in the + description, it lists the available possible image transformations and + should be set to one of them before using the description as parameter + to GetImage. + + Possible property values: + + :string type: + + Type of dict properties. Mandatory for each dict. + + Possible values: + + :"native": + :"variant": + + :string encoding: + + File encoding format. + + Possible values: + + :"BMP": + :"GIF": + :"JPEG": + :"JPEG2000": + :"PNG": + :"WBMP": + + :string pixel: + + File encoding format size of form "*". + + :uint64 size: + + File size. + + :uint64 maxsize: + + File maximum size. + + :string transformation: + + List of available transformations separated by space. + + Possible values: + + :"crop": + :"fill": + :"stretch": + + Possible errors: + + :org.bluez.obex.Error.InvalidArguments: + :org.bluez.obex.Error.Failed: + +object, dict GetThumbnail(string targetfile, string handle) +``````````````````````````````````````````````````````````` + + Retrieves the image thumbnail corresponding to the handle and store it in + a local file. + + Possible errors: + + :org.bluez.obex.Error.InvalidArguments: + :org.bluez.obex.Error.Failed: