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