Hi Marcel, On Tue, Jul 27, 2010 at 8:04 PM, Marcel Mol <marcel@xxxxxxx> wrote: > 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 > + > +#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]; > +}; > + > +#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 > + 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"; > + if (!irmc->buffer) > + irmc->buffer = g_string_new_len(t, strlen(t)); > + else > + irmc->buffer = g_string_append_len(irmc->buffer, t, strlen(t)); > + > + // 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); > + > + 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); > + } > + 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; > +} > + > +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; > +} > + > +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; > + > + return obex_service_driver_register(&irmc); > +} > + > +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 }, > }; > > @@ -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); > } > > @@ -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); > + > 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) > > #define TARGET_SIZE 16 > > -- > 1.7.2 Looks pretty good, but you need to fix those c++ comments (//) please use /*, also the you don't necessary need to abstract the file devinfo.txt so platform can just write their own stuff there, if that is not sufficient than we can thing of supporting scripts like we do with x-obex/capability but I don't think we really need it since the device info should be static, right? -- Luiz Augusto von Dentz Computer Engineer -- 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