Re: [PATCH] IrMC sync server support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Thu, Aug 12, 2010 at 07:41:39AM -0400, Marcel Holtmann wrote:
> 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@xxxxxxx>
> > + *
> > + *  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.

Just to have a symbolic name for the max length of the char strings in 
the irmc_session struct.
All strings may need a different max length, but for now a single value
suffices. (see also the comment in irmc_connect())

> 
> > +
> > +#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.

Fine

> 
> > +	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.

Ok will do.
 
> > +	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.

Don't know :). Will change 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.

thanks, was not aware of this.

> 
> > +
> > +	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.

Ok wil look into that. This one may need some more thinking to make in nice and clean 

> 
> > +	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.

Well, an empty buffer it is. irmc->buffer may not be NULL after as that
will be seen as 'no data available yet'.

> 
> > +}
> > +
> > +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.

Oops, should have seen this when it was fixsed for pbap.

> 
> > +
> > +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 ;)

Agree :)
 
> >  
> > @@ -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
> 

thanks for your comments Marcel

-Marcel
-- 
     ======--------         Marcel J.E. Mol                MESA Consulting B.V.
    =======---------        ph. +31-(0)6-54724868          P.O. Box 112
    =======---------        marcel@xxxxxxx                 2630 AC  Nootdorp
__==== www.mesa.nl ---____U_n_i_x______I_n_t_e_r_n_e_t____ The Netherlands ____
 They couldn't think of a number,           Linux user 1148  --  counter.li.org
    so they gave me a name!  -- Rupert Hine  --  www.ruperthine.com
--
To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Bluez Devel]     [Linux Wireless Networking]     [Linux Wireless Personal Area Networking]     [Linux ATH6KL]     [Linux USB Devel]     [Linux Media Drivers]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Big List of Linux Books]

  Powered by Linux