2011-08-11 23:43:04

by Jakub Adamek

[permalink] [raw]
Subject: [RFC obexd 0/4] Rework get_next_header function

Hi,
the existing code provides a mimetype driver function that allows
drivers to specify their own return headers - get_next_header.

These patches change this function and the code that uses it
to be able to:
a) specify return headers in PUT operations (currently the function
is only called for GET),
b) change criteria for signaling that all headers have been sent
to setting the header number to OBEX_HEADER_EMPTY (currently this
is done by setting the header data length to 0, which prevents us
from actually returning headers with 0 data length),
c) provide way to return 8 and 32 bit headers (current code only
supports the variable length header types).

Jakub Adamek (4):
Call get_next_header in PUT operations
Stop get_next_headers on OBEX_HEADER_EMPTY
Adapt get_next_header for 1 and 4 byte headers
Adapt PBAP to changes in get_next_header behaviour

plugins/pbap.c | 2 +
src/obex-priv.h | 1 +
src/obex.c | 224 ++++++++++++++++++++++++++++++++++++-------------------
src/obex.h | 3 +
4 files changed, 153 insertions(+), 77 deletions(-)

--
1.7.4.1



2011-08-11 23:43:08

by Jakub Adamek

[permalink] [raw]
Subject: [RFC obexd 4/4] Adapt PBAP to changes in get_next_header behaviour

This means setting the header number when all headers
have been sent to OBEX_HEADER_EMPTY.
---
plugins/pbap.c | 2 ++
1 files changed, 2 insertions(+), 0 deletions(-)

diff --git a/plugins/pbap.c b/plugins/pbap.c
index e10fa8c..6f605d4 100644
--- a/plugins/pbap.c
+++ b/plugins/pbap.c
@@ -980,6 +980,7 @@ static ssize_t vobject_pull_get_next_header(void *object, void *buf, size_t mtu,
return array_read(obj->aparams, buf, mtu);
}

+ *hi = OBEX_HDR_EMPTY;
return 0;
}

@@ -1029,6 +1030,7 @@ static ssize_t vobject_list_get_next_header(void *object, void *buf, size_t mtu,
if (pbap->params->maxlistcount == 0)
return array_read(obj->aparams, buf, mtu);

+ *hi = OBEX_HDR_EMPTY;
return 0;
}

--
1.7.4.1


2011-08-11 23:43:07

by Jakub Adamek

[permalink] [raw]
Subject: [RFC obexd 3/4] Adapt get_next_header for 1 and 4 byte headers

In order to not complicate memory management by having drivers
allocate their own memory for multibyte headers, functions
are introduced that encode the 1 and 4 byte headers in the
given buffer.
---
src/obex.c | 40 +++++++++++++++++++++++++++++++++++++++-
src/obex.h | 3 +++
2 files changed, 42 insertions(+), 1 deletions(-)

diff --git a/src/obex.c b/src/obex.c
index e207f66..42e9f20 100644
--- a/src/obex.c
+++ b/src/obex.c
@@ -612,6 +612,24 @@ static int obex_write_stream(struct obex_session *os,
return 0;
}

+static void fill_headerdata(obex_headerdata_t *hd, uint8_t hi, void *buf)
+{
+ guint8 *val8 = buf;
+ guint32 *val32 = buf;
+ switch (hi & OBEX_HDR_TYPE_MASK) {
+ case OBEX_HDR_TYPE_BYTES:
+ case OBEX_HDR_TYPE_UNICODE:
+ hd->bs = buf;
+ break;
+ case OBEX_HDR_TYPE_UINT8:
+ hd->bq1 = *val8;
+ break;
+ case OBEX_HDR_TYPE_UINT32:
+ hd->bq4 = *val32;
+ break;
+ }
+}
+
static int obex_write(struct obex_session *os, obex_t *obex, obex_object_t *obj)
{
obex_headerdata_t hd;
@@ -657,7 +675,7 @@ static int obex_write(struct obex_session *os, obex_t *obex, obex_object_t *obj)
if (hi == OBEX_HDR_EMPTY)
break;

- hd.bs = os->buf;
+ fill_headerdata(&hd, hi, os->buf);
OBEX_ObjectAddHeader(obex, obj, hi, hd, len, 0);
}

@@ -1627,6 +1645,26 @@ int obex_aparam_write(struct obex_session *os,
OBEX_HDR_APPARAM, hd, size, 0);
}

+inline ssize_t put_hdr_u8(void *buf, size_t mtu, guint8 u8) {
+ guint8 *tmp = buf;
+ if (tmp == NULL)
+ return -1;
+ if (mtu < sizeof(guint8))
+ return -1;
+ *tmp = u8;
+ return sizeof(guint8);
+}
+
+inline ssize_t put_hdr_u32(void *buf, size_t mtu, guint32 u32) {
+ guint32 *tmp = buf;
+ if (tmp == NULL)
+ return -1;
+ if (mtu < sizeof(guint32))
+ return -1;
+ *tmp = u32;
+ return sizeof(guint32);
+}
+
int memncmp0(const void *a, size_t na, const void *b, size_t nb)
{
if (na != nb)
diff --git a/src/obex.h b/src/obex.h
index 03243f1..1fff957 100644
--- a/src/obex.h
+++ b/src/obex.h
@@ -71,5 +71,8 @@ int obex_aparam_write(struct obex_session *os, obex_object_t *obj,
const char *obex_option_root_folder(void);
gboolean obex_option_symlinks(void);

+inline gboolean put_hdr_u8(void *buf, size_t mtu, guint8 u8);
+inline gboolean put_hdr_u32(void *buf, size_t mtu, guint32 u32);
+
/* Just a thin wrapper around memcmp to deal with NULL values */
int memncmp0(const void *a, size_t na, const void *b, size_t nb);
--
1.7.4.1


2011-08-11 23:43:06

by Jakub Adamek

[permalink] [raw]
Subject: [RFC obexd 2/4] Stop get_next_headers on OBEX_HEADER_EMPTY

The current code interprets returning 0 from
get_next_header as signaling the end of headers
to be sent. This prevents drivers from sending
headers of 0 length. To alleviate this,
we now set the header number in get_next_header
to OBEX_HEADER_EMPTY after all signals have been
sent.
---
src/obex.c | 9 +++++++--
1 files changed, 7 insertions(+), 2 deletions(-)

diff --git a/src/obex.c b/src/obex.c
index 9595aad..e207f66 100644
--- a/src/obex.c
+++ b/src/obex.c
@@ -637,8 +637,10 @@ static int obex_write(struct obex_session *os, obex_t *obex, obex_object_t *obj)
if (os->buf == NULL)
os->buf = g_malloc0(os->tx_mtu);

- while ((len = os->driver->get_next_header(os->object, os->buf,
- os->tx_mtu, &hi)) != 0) {
+ while (TRUE) {
+ len = os->driver->get_next_header(os->object, os->buf,
+ os->tx_mtu, &hi);
+
if (len < 0) {
error("get_next_header(): %s (%zd)", strerror(-len),
-len);
@@ -652,6 +654,9 @@ static int obex_write(struct obex_session *os, obex_t *obex, obex_object_t *obj)
return len;
}

+ if (hi == OBEX_HDR_EMPTY)
+ break;
+
hd.bs = os->buf;
OBEX_ObjectAddHeader(obex, obj, hi, hd, len, 0);
}
--
1.7.4.1


2011-08-11 23:43:05

by Jakub Adamek

[permalink] [raw]
Subject: [RFC obexd 1/4] Call get_next_header in PUT operations

The current code only calls the get_next_header
mimetype driver function for GET operations.
---
src/obex-priv.h | 1 +
src/obex.c | 175 ++++++++++++++++++++++++++++++++-----------------------
2 files changed, 102 insertions(+), 74 deletions(-)

diff --git a/src/obex-priv.h b/src/obex-priv.h
index d640ae0..e1fbfc3 100644
--- a/src/obex-priv.h
+++ b/src/obex-priv.h
@@ -44,6 +44,7 @@ struct obex_session {
void *service_data;
struct obex_server *server;
gboolean checked;
+ gboolean body_streamed;
obex_t *obex;
obex_object_t *obj;
struct obex_mime_type_driver *driver;
diff --git a/src/obex.c b/src/obex.c
index 49a110d..9595aad 100644
--- a/src/obex.c
+++ b/src/obex.c
@@ -320,6 +320,7 @@ static void os_reset_session(struct obex_session *os)
os->size = OBJECT_SIZE_DELETE;
os->headers_sent = FALSE;
os->streaming = FALSE;
+ os->body_streamed = FALSE;
}

static void obex_session_free(struct obex_session *os)
@@ -558,78 +559,6 @@ static gboolean chk_cid(obex_t *obex, obex_object_t *obj, uint32_t cid)
return ret;
}

-static int obex_read_stream(struct obex_session *os, obex_t *obex,
- obex_object_t *obj)
-{
- int size;
- ssize_t len = 0;
- const uint8_t *buffer;
-
- DBG("name=%s type=%s rx_mtu=%d file=%p",
- os->name ? os->name : "", os->type ? os->type : "",
- os->rx_mtu, os->object);
-
- if (os->aborted)
- return -EPERM;
-
- /* workaround: client didn't send the object lenght */
- if (os->size == OBJECT_SIZE_DELETE)
- os->size = OBJECT_SIZE_UNKNOWN;
-
- /* If there's something to write and we are able to write it */
- if (os->pending > 0 && os->driver)
- goto write;
-
- size = OBEX_ObjectReadStream(obex, obj, &buffer);
- if (size < 0) {
- error("Error on OBEX stream");
- return -EIO;
- }
-
- if (size > os->rx_mtu) {
- error("Received more data than RX_MAX");
- return -EIO;
- }
-
- os->buf = g_realloc(os->buf, os->pending + size);
- memcpy(os->buf + os->pending, buffer, size);
- os->pending += size;
-
- /* only write if both object and driver are valid */
- if (os->object == NULL || os->driver == NULL) {
- DBG("Stored %" PRIu64 " bytes into temporary buffer",
- os->pending);
- return 0;
- }
-
-write:
- while (os->pending > 0) {
- ssize_t w;
-
- w = os->driver->write(os->object, os->buf + len,
- os->pending);
- if (w < 0) {
- if (w == -EINTR)
- continue;
- else {
- memmove(os->buf, os->buf + len, os->pending);
- return w;
- }
- }
-
- len += w;
- os->offset += w;
- os->pending -= w;
- }
-
- /* Flush on EOS */
- if (os->size != OBJECT_SIZE_UNKNOWN && os->size == os->offset &&
- os->driver->flush)
- return os->driver->flush(os->object) > 0 ? -EAGAIN : 0;
-
- return 0;
-}
-
static int obex_write_stream(struct obex_session *os,
obex_t *obex, obex_object_t *obj)
{
@@ -700,11 +629,14 @@ static int obex_write(struct obex_session *os, obex_t *obex, obex_object_t *obj)
return -EIO;

if (os->headers_sent)
- return obex_write_stream(os, obex, obj);
+ goto skip;

if (!os->driver->get_next_header)
goto skip;

+ if (os->buf == NULL)
+ os->buf = g_malloc0(os->tx_mtu);
+
while ((len = os->driver->get_next_header(os->object, os->buf,
os->tx_mtu, &hi)) != 0) {
if (len < 0) {
@@ -727,7 +659,92 @@ static int obex_write(struct obex_session *os, obex_t *obex, obex_object_t *obj)
skip:
os->headers_sent = TRUE;

- return obex_write_stream(os, obex, obj);
+ if (os->cmd == OBEX_CMD_GET)
+ return obex_write_stream(os, obex, obj);
+ return 0;
+}
+
+static int obex_read_stream(struct obex_session *os, obex_t *obex,
+ obex_object_t *obj)
+{
+ int size;
+ ssize_t len = 0;
+ const uint8_t *buffer;
+
+ DBG("name=%s type=%s rx_mtu=%d file=%p",
+ os->name ? os->name : "", os->type ? os->type : "",
+ os->rx_mtu, os->object);
+
+ if (os->aborted)
+ return -EPERM;
+
+ /* workaround: client didn't send the object lenght */
+ if (os->size == OBJECT_SIZE_DELETE)
+ os->size = OBJECT_SIZE_UNKNOWN;
+
+ /* If there's something to write and we are able to write it */
+ if (os->pending > 0 && os->driver)
+ goto write;
+
+ size = OBEX_ObjectReadStream(obex, obj, &buffer);
+
+ if (size == 0)
+ os->body_streamed = TRUE;
+
+ if (size < 0) {
+ error("Error on OBEX stream");
+ return -EIO;
+ }
+
+ if (size > os->rx_mtu) {
+ error("Received more data than RX_MAX");
+ return -EIO;
+ }
+
+ os->buf = g_realloc(os->buf, os->pending + size);
+ memcpy(os->buf + os->pending, buffer, size);
+ os->pending += size;
+
+ /* only write if both object and driver are valid */
+ if (os->object == NULL || os->driver == NULL) {
+ DBG("Stored %" PRIu64 " bytes into temporary buffer",
+ os->pending);
+ return 0;
+ }
+
+write:
+ while (os->pending > 0) {
+ ssize_t w;
+
+ w = os->driver->write(os->object, os->buf + len,
+ os->pending);
+ if (w < 0) {
+ if (w == -EINTR)
+ continue;
+ else {
+ memmove(os->buf, os->buf + len, os->pending);
+ return w;
+ }
+ }
+
+ len += w;
+ os->offset += w;
+ os->pending -= w;
+ }
+
+ /* Flush on EOS and start */
+ if (os->body_streamed && os->pending == 0) {
+ int ret = 0;
+ if (os->driver->flush != NULL)
+ ret = os->driver->flush(os->object) > 0 ? -EAGAIN : 0;
+
+ if (ret < 0)
+ return ret;
+
+ return obex_write(os, obex, obj);
+ }
+
+ return 0;
}

static gboolean handle_async_io(void *object, int flags, int err,
@@ -1148,6 +1165,16 @@ static void cmd_put(struct obex_session *os, obex_t *obex, obex_object_t *obj)
OBEX_SuspendRequest(obex, obj);
os->obj = obj;
os->driver->set_io_watch(os->object, handle_async_io, os);
+ return;
+ }
+
+ /* Get response headers if flush doesn't suspend */
+ err = obex_write(os, obex, obj);
+ if (err == -EAGAIN) {
+ OBEX_SuspendRequest(obex, obj);
+ os->obj = obj;
+ os->driver->set_io_watch(os->object, handle_async_io, os);
+ return;
}
}

--
1.7.4.1