From: Marcel Holtmann <marcel@holtmann.org>
To: Marcel Mol <marcel@mesa.nl>
Cc: linux-bluetooth@vger.kernel.org
Subject: Re: [PATCH] IrMC sync server support
Date: Thu, 12 Aug 2010 07:41:39 -0400 [thread overview]
Message-ID: <1281613299.12579.266.camel@localhost.localdomain> (raw)
In-Reply-To: <201008121013.o7CADR8r004125@joshua.mesa.nl>
Hi Marcel,
> A reasonable working IrMC SYNC server (only full phonebook sync support)
> Support for cal and note by just returning nothing.
> ---
> Makefile.am | 3 +
> plugins/irmc.c | 488 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> src/main.c | 12 ++-
> src/obex.h | 1 +
> 4 files changed, 502 insertions(+), 2 deletions(-)
> create mode 100644 plugins/irmc.c
>
> diff --git a/Makefile.am b/Makefile.am
> index 73e2f28..ce8c675 100644
> --- a/Makefile.am
> +++ b/Makefile.am
> @@ -58,6 +58,9 @@ builtin_sources += plugins/pbap.c plugins/phonebook.h \
> builtin_modules += syncevolution
> builtin_sources += plugins/syncevolution.c
>
> +builtin_modules += irmc
> +builtin_sources += plugins/irmc.c plugins/phonebook.h
> +
> builtin_nodist += plugins/phonebook.c
>
> libexec_PROGRAMS += src/obexd
> diff --git a/plugins/irmc.c b/plugins/irmc.c
> new file mode 100644
> index 0000000..96fd807
> --- /dev/null
> +++ b/plugins/irmc.c
> @@ -0,0 +1,488 @@
> +/*
> + *
> + * OBEX IrMC Sync Server
> + *
> + * Copyright (C) 2010 Marcel Mol <marcel@mesa.nl>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
> + *
> + */
> +
> +#ifdef HAVE_CONFIG_H
> +#include <config.h>
> +#endif
> +
> +#include <stdio.h>
> +#include <string.h>
> +#include <errno.h>
> +#include <glib.h>
> +#include <stdlib.h>
> +#include <unistd.h>
> +#include <arpa/inet.h>
> +#include <sys/types.h>
> +#include <sys/stat.h>
> +#include <fcntl.h>
> +
> +#include <openobex/obex.h>
> +#include <openobex/obex_const.h>
> +
> +#include "plugin.h"
> +#include "log.h"
> +#include "obex.h"
> +#include "service.h"
> +#include "phonebook.h"
> +#include "mimetype.h"
> +#include "filesystem.h"
> +#include "dbus.h"
> +
> +#define IRMC_CHANNEL 17
we did have a list of defined RFCOMM channels for various protocols, but
just don't remember where it is.
Johan, we should put that into the doc/ directory actually. Can you dig
up that list and commit it as text file.
> +
> +#define IRMC_RECORD "<?xml version=\"1.0\" encoding=\"UTF-8\" ?> \
> +<record> \
> + <attribute id=\"0x0001\"> \
> + <sequence> \
> + <uuid value=\"0x1104\"/> \
> + </sequence> \
> + </attribute> \
> + \
> + <attribute id=\"0x0004\"> \
> + <sequence> \
> + <sequence> \
> + <uuid value=\"0x0100\"/> \
> + </sequence> \
> + <sequence> \
> + <uuid value=\"0x0003\"/> \
> + <uint8 value=\"%u\" name=\"channel\"/> \
> + </sequence> \
> + <sequence> \
> + <uuid value=\"0x0008\"/> \
> + </sequence> \
> + </sequence> \
> + </attribute> \
> + \
> + <attribute id=\"0x0009\"> \
> + <sequence> \
> + <sequence> \
> + <uuid value=\"0x1104\"/> \
> + <uint16 value=\"0x0100\" name=\"version\"/> \
> + </sequence> \
> + </sequence> \
> + </attribute> \
> + \
> + <attribute id=\"0x0100\"> \
> + <text value=\"%s\" name=\"name\"/> \
> + </attribute> \
> + \
> + <attribute id=\"0x0301\"> \
> + <sequence> \
> + <uint8 value=\"0x01\"/> \
> + </sequence> \
> + </attribute> \
> +</record>"
> +
> +
> +struct aparam_header {
> + uint8_t tag;
> + uint8_t len;
> + uint8_t val[0];
> +} __attribute__ ((packed));
> +
> +#define DIDLEN 18
> +struct irmc_session {
> + struct obex_session *os;
> + struct apparam_field *params;
> + uint16_t entries;
> + GString *buffer;
> + char sn[DIDLEN];
> + char did[DIDLEN];
> + char manu[DIDLEN];
> + char model[DIDLEN];
> +};
What is DIDLEN.
> +
> +#define IRMC_TARGET_SIZE 9
> +
> +static const guint8 IRMC_TARGET[IRMC_TARGET_SIZE] = {
> + 0x49, 0x52, 0x4d, 0x43, 0x2d, 0x53, 0x59, 0x4e, 0x43 };
> +
> +static void phonebook_size_result(const char *buffer, size_t bufsize,
> + int vcards, int missed, void *user_data)
> +{
> + struct irmc_session *irmc = user_data;
> +
> + DBG("vcards %d", vcards);
> +
> + irmc->params->maxlistcount = vcards;
> +}
> +
> +static void query_result(const char *buffer, size_t bufsize, int vcards,
> + int missed, void *user_data)
> +{
> + struct irmc_session *irmc = user_data;
> + const char *s;
> + char *t;
> +
> + DBG("bufsize %d vcards %d missed %d", bufsize, vcards, missed);
> +
> + // first create an empty 'owner' vcard
As Luiz already mentioned, please use /* */ style comments.
> + t = "BEGIN:VCARD\r\n"
> + "VERSION:2.1\r\n"
> + "N:\r\n"
> + "TEL:\r\n"
> + "X-IRMX-LUID:0\r\n"
> + "END:VCARDi\r\n";
You really wanna use const char *t here. And I would even prefer if you
make that one static and global.
> + if (!irmc->buffer)
> + irmc->buffer = g_string_new_len(t, strlen(t));
> + else
> + irmc->buffer = g_string_append_len(irmc->buffer, t, strlen(t));
Why the string_append_len. Just append it it.
> + // loop around buffer and add X-IRMC-LUID attribs
> + s = buffer;
> + while ((t = strstr(s, "UID:")) != NULL) {
> + // add upto UID: into buffer
> + irmc->buffer = g_string_append_len(irmc->buffer, s, t-s);
> + // add UID: line into buffer
> + // Not sure if UID is still needed if X-IRMC-LUID is there
> + s = t;
> + t = strstr(s, "\r\n");
> + t += 2;
> + irmc->buffer = g_string_append_len(irmc->buffer, s, t-s);
> + // add X-IRMC-LUID with same number as UID
> + irmc->buffer = g_string_append_len(irmc->buffer,
> + "X-IRMC-LUID:", 12);
> + s += 4; // point to uid number
> + irmc->buffer = g_string_append_len(irmc->buffer, s, t-s);
> + s = t;
> + }
> + // add remaining bit of buffer
> + irmc->buffer = g_string_append_len(irmc->buffer, s,
> + bufsize - (s-buffer));
> +
> + obex_object_set_io_flags(irmc, G_IO_IN, 0);
> +}
> +
> +static void *irmc_connect(struct obex_session *os, int *err)
> +{
> + struct irmc_session *irmc;
> + struct apparam_field *param;
> +
> + DBG("");
> +
> + manager_register_session(os);
> +
> + irmc = g_new0(struct irmc_session, 1);
> + irmc->os = os;
> +
> + /*
> + * ideally get capabilities info here and use that to define
> + * IrMC DID and SN etc parameters.
> + * For now lets used hostname and some 'random' value
> + */
> + gethostname(irmc->did, DIDLEN);
> + strncpy(irmc->sn, "12345", DIDLEN);
> + strncpy(irmc->manu, "obex", DIDLEN);
> + strncpy(irmc->model, "mymodel", DIDLEN);
> +
> + /*
> + * we need to know the number of contact/cal/nt entries
> + * somewhere so why not do it now.
> + */
> + param = g_new0(struct apparam_field, 1);
> + param->maxlistcount = 0; // to count the number of vcards...
> + param->filter = 0x200085; // UID TEL N VERSION
> + irmc->params = param;
> + phonebook_pull("telecom/pb.vcf", irmc->params, phonebook_size_result,
> + irmc);
> +
> + if (err)
> + *err = 0;
> +
> + return irmc;
> +}
> +
> +static int irmc_get(struct obex_session *os, obex_object_t *obj,
> + gboolean *stream, void *user_data)
> +{
> + struct irmc_session *irmc = user_data;
> + const char *type = obex_get_type(os);
> + const char *name = obex_get_name(os);
> + char *path;
> + int ret;
> +
> + DBG("name %s type %s irmc %p", name, type ? type : "NA", irmc);
> +
> + path = g_strdup(name);
> + *stream = TRUE;
> +
> + ret = obex_get_stream_start(os, path);
> +
> + g_free(path);
> +
> + return ret;
> +}
> +
> +static void irmc_disconnect(struct obex_session *os, void *user_data)
> +{
> + struct irmc_session *irmc = user_data;
> +
> + DBG("");
> +
> + manager_unregister_session(os);
> +
> + if (irmc->params) {
> + if (irmc->params->searchval)
> + g_free(irmc->params->searchval);
> + g_free(irmc->params);
> + }
> + if (irmc->buffer) {
> + string_free(irmc->buffer);
> + irmc->buffer = NULL;
> + }
> +
> + g_free(irmc);
> +}
> +
> +static int irmc_chkput(struct obex_session *os, void *user_data)
> +{
> + DBG("");
> + /* Reject all PUTs */
> + return -EBADR;
> +}
> +
> +static void *irmc_open_devinfo(struct irmc_session *irmc, int *err)
> +{
> + char mybuf[1024];
> +
> + sprintf(mybuf, "MANU:%s\r\n"
> + "MOD:%s\r\n"
> + "SN:%s\r\n"
> + "PB-TYPE-TX:VCARD2.1\r\n",
> + irmc->manu, irmc->model, irmc->sn);
> +
> + if (!irmc->buffer)
> + irmc->buffer = g_string_new(mybuf);
> + else
> + irmc->buffer = g_string_append(irmc->buffer, mybuf);
We have something that is called g_string_append_printf(). Use that one
and not allocate a buf up-front.
> +
> + return irmc;
> +}
> +
> +static void *irmc_open_pb(const char *name,
> + struct irmc_session *irmc, int *err)
> +{
> + char mybuf[1024];
> + int ret;
> +
> + if (!g_strcmp0(name, ".vcf")) {
> + // how can we tell if the vcard count call already finished?
> + ret = phonebook_pull("telecom/pb.vcf", irmc->params,
> + query_result, irmc);
> + if (ret < 0) {
> + DBG("phonebook_pull failed...");
> + goto fail;
> + }
> + return irmc;
> + }
> +
> + if (!g_strcmp0(name, "/info.log")) {
> + sprintf(mybuf, "Total-Records:%d\r\n"
> + "Maximum-Records:%d\r\n"
> + "DID:%s\r\n",
> + irmc->params->maxlistcount,
> + irmc->params->maxlistcount, irmc->did);
> + }
Same here. A GLib _printf() function is way better usage.
> + else if (!strncmp(name, "/luid/", 6)) {
> + name += 6;
> + if (!g_strcmp0(name, "cc.log"))
> + sprintf(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");
> + sprintf(mybuf, "SN:%s\r\n"
> + "IEL:2\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 = g_string_new(mybuf);
> + else
> + irmc->buffer = g_string_append(irmc->buffer, mybuf);
> +
> + return irmc;
> +
> +fail:
> + if (err)
> + *err = ret;
> +
> + return NULL;
> +}
> +
> +static void *irmc_open_cal(const char *name,
> + struct irmc_session *irmc, int *err)
> +{
> + // no suport yet. Just return an empty buffer
> + //cal.vcs
> + DBG("unsupported, returning empty buffer");
> + if (!irmc->buffer)
> + irmc->buffer = g_string_new("");
> + else
> + irmc->buffer = g_string_append(irmc->buffer, "");
> +
> + return irmc;
Empty buffer is empty buffer. Just return it.
> +}
> +
> +static void *irmc_open_nt(const char *name,
> + struct irmc_session *irmc, int *err)
> +{
> + // no suport yet. Just return an empty buffer
> + //nt.vnt
> + DBG("unsupported, returning empty buffer");
> + if (!irmc->buffer)
> + irmc->buffer = g_string_new("");
> + else
> + irmc->buffer = g_string_append(irmc->buffer, "");
> +
> + return irmc;
Same here.
> +}
> +
> +static void *irmc_open(const char *name, int oflag, mode_t mode,
> + void *context, size_t *size, int *err)
> +{
> + struct irmc_session *irmc = context;
> + int ret;
> + const char *p;
> +
> + DBG("name %s context %p", name, context);
> + if (oflag != O_RDONLY) {
> + ret = -EPERM;
> + goto fail;
> + }
> + if (strncmp(name, "telecom/", 8) != 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);
> +
> +fail:
> + if (err)
> + *err = ret;
> +
> + return NULL;
> +}
> +
> +static int irmc_close(void *object)
> +{
> + struct irmc_session *irmc = object;
> +
> + DBG("");
> + if (irmc->buffer) {
> + string_free(irmc->buffer);
> + irmc->buffer = NULL;
> + }
> +
> + return 0;
> +}
> +
> +static ssize_t irmc_read(void *object, void *buf, size_t count, uint8_t *hi)
> +{
> + struct irmc_session *irmc = object;
> + int len;
> +
> + DBG("buffer %p count %d", irmc->buffer, count);
> + if (!irmc->buffer)
> + return -EAGAIN;
> +
> + *hi = OBEX_HDR_BODY;
> + len = string_read(irmc->buffer, buf, count);
> + DBG("returning %d bytes", len);
> + return len;
> +}
> +
> +static struct obex_mime_type_driver irmc_driver = {
> + .target = IRMC_TARGET,
> + .target_size = IRMC_TARGET_SIZE,
> + .open = irmc_open,
> + .close = irmc_close,
> + .read = irmc_read,
> +};
> +
> +static struct obex_service_driver irmc = {
> + .name = "IRMC Sync server",
> + .service = OBEX_IRMC,
> + .channel = IRMC_CHANNEL,
> + .record = IRMC_RECORD,
> + .target = IRMC_TARGET,
> + .target_size = IRMC_TARGET_SIZE,
> + .connect = irmc_connect,
> + .get = irmc_get,
> + .disconnect = irmc_disconnect,
> + .chkput = irmc_chkput
> +};
> +
> +static int irmc_init(void)
> +{
> + int err;
> +
> + DBG("");
> + err = phonebook_init();
> + if (err < 0)
> + return err;
> +
> + err = obex_mime_type_driver_register(&irmc_driver);
> + if (err < 0)
> + return err;
Don't you need to phonebook_exit() here?
> +
> + return obex_service_driver_register(&irmc);
> +}
Also what happens if this one fails. You do need to clean up.
> +
> +static void irmc_exit(void)
> +{
> + DBG("");
> + obex_service_driver_unregister(&irmc);
> + obex_mime_type_driver_unregister(&irmc_driver);
> + phonebook_exit();
> +}
> +
> +OBEX_PLUGIN_DEFINE(irmc, irmc_init, irmc_exit)
> diff --git a/src/main.c b/src/main.c
> index 649acf9..96523a6 100644
> --- a/src/main.c
> +++ b/src/main.c
> @@ -83,6 +83,7 @@ static gboolean option_pbap = FALSE;
> static gboolean option_pcsuite = FALSE;
> static gboolean option_symlinks = FALSE;
> static gboolean option_syncevolution = FALSE;
> +static gboolean option_irmc = FALSE;
>
> static gboolean parse_debug(const char *key, const char *value,
> gpointer user_data, GError **error)
> @@ -122,6 +123,8 @@ static GOptionEntry options[] = {
> "Enable PC Suite Services server" },
> { "syncevolution", 'e', 0, G_OPTION_ARG_NONE, &option_syncevolution,
> "Enable OBEX server for SyncEvolution" },
> + { "irmc", 'i', 0, G_OPTION_ARG_NONE, &option_irmc,
> + "Enable IrMCSync server" },
> { NULL },
> };
Just put irmc before syncevolution. Like nicer ;)
>
> @@ -208,9 +211,10 @@ int main(int argc, char *argv[])
>
> if (option_opp == FALSE && option_ftp == FALSE &&
> option_pbap == FALSE &&
> - option_syncevolution == FALSE) {
> + option_syncevolution == FALSE &&
> + option_irmc == FALSE) {
> fprintf(stderr, "No server selected (use either "
> - "--opp, --ftp, --pbap or --syncevolution)\n");
> + "--opp, --ftp, --pbap --syncevolution or --irmc)\n");
> exit(EXIT_FAILURE);
> }
Same here. Leave syncevolution at the end.
>
> @@ -270,6 +274,10 @@ int main(int argc, char *argv[])
> obex_server_init(OBEX_SYNCEVOLUTION, NULL, TRUE, FALSE,
> FALSE, NULL);
>
> + if (option_irmc == TRUE)
> + obex_server_init(OBEX_IRMC, NULL, TRUE, FALSE, FALSE,
> + option_capability);
> +
And here again.
> if (!root_folder_setup(option_root, option_root_setup)) {
> error("Unable to setup root folder %s", option_root);
> exit(EXIT_FAILURE);
> diff --git a/src/obex.h b/src/obex.h
> index 9424b6b..cfe9159 100644
> --- a/src/obex.h
> +++ b/src/obex.h
> @@ -35,6 +35,7 @@
> #define OBEX_PBAP (1 << 4)
> #define OBEX_PCSUITE (1 << 5)
> #define OBEX_SYNCEVOLUTION (1 << 6)
> +#define OBEX_IRMC (1 << 7)
>
And here as well. Just use 1 << 6 for IRMC and move SYNCEVO to 1 << 7.
> #define TARGET_SIZE 16
>
Regards
Marcel
next prev parent reply other threads:[~2010-08-12 11:41 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2010-07-27 17:04 [PATCH] IrMC sync server support Marcel Mol
2010-08-12 11:16 ` Luiz Augusto von Dentz
2010-08-12 14:56 ` Marcel J.E. Mol
[not found] ` <20100812121212.GA16738@joshua.mesa.nl>
2010-08-12 14:57 ` Luiz Augusto von Dentz
2010-08-12 15:39 ` Marcel J.E. Mol
2010-08-12 11:41 ` Marcel Holtmann [this message]
2010-08-12 14:56 ` Marcel J.E. Mol
2010-08-16 17:22 ` Marcel J.E. Mol
2010-08-16 19:47 ` Luiz Augusto von Dentz
2010-08-17 8:48 ` Marcel J.E. Mol
2010-08-19 10:53 ` Luiz Augusto von Dentz
2010-08-19 15:02 ` Johan Hedberg
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1281613299.12579.266.camel@localhost.localdomain \
--to=marcel@holtmann.org \
--cc=linux-bluetooth@vger.kernel.org \
--cc=marcel@mesa.nl \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox