3 * OBEX IrMC Sync Server
5 * Copyright (C) 2010 Marcel Mol <marcel@mesa.nl>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
33 #include <arpa/inet.h>
34 #include <sys/types.h>
44 #include "phonebook.h"
46 #include "filesystem.h"
49 #define IRMC_CHANNEL 14
51 #define IRMC_RECORD "<?xml version=\"1.0\" encoding=\"UTF-8\" ?> \
53 <attribute id=\"0x0001\"> \
55 <uuid value=\"0x1104\"/> \
59 <attribute id=\"0x0004\"> \
62 <uuid value=\"0x0100\"/> \
65 <uuid value=\"0x0003\"/> \
66 <uint8 value=\"%u\" name=\"channel\"/> \
69 <uuid value=\"0x0008\"/> \
74 <attribute id=\"0x0009\"> \
77 <uuid value=\"0x1104\"/> \
78 <uint16 value=\"0x0100\" name=\"version\"/> \
83 <attribute id=\"0x0100\"> \
84 <text value=\"%s\" name=\"name\"/> \
87 <attribute id=\"0x0301\"> \
89 <uint8 value=\"0x01\"/> \
95 struct aparam_header {
99 } __attribute__ ((packed));
103 struct irmc_session {
104 struct obex_session *os;
105 struct apparam_field *params;
115 #define IRMC_TARGET_SIZE 9
117 static const guint8 IRMC_TARGET[IRMC_TARGET_SIZE] = {
118 0x49, 0x52, 0x4d, 0x43, 0x2d, 0x53, 0x59, 0x4e, 0x43 };
121 * the IrMC specs state the first vcard should be the owner
122 * vcard. As there is no simple way to collect ownerdetails
123 * just create an empty vcard (which is allowed according to the
126 static const char *owner_vcard =
134 static void phonebook_size_result(const char *buffer, size_t bufsize,
135 int vcards, int missed,
136 gboolean lastpart, void *user_data)
138 struct irmc_session *irmc = user_data;
140 DBG("vcards %d", vcards);
142 irmc->params->maxlistcount = vcards;
145 phonebook_req_finalize(irmc->request);
146 irmc->request = NULL;
150 static void query_result(const char *buffer, size_t bufsize, int vcards,
151 int missed, gboolean lastpart, void *user_data)
153 struct irmc_session *irmc = user_data;
156 DBG("bufsize %zu vcards %d missed %d", bufsize, vcards, missed);
159 phonebook_req_finalize(irmc->request);
160 irmc->request = NULL;
163 /* first add a 'owner' vcard */
165 irmc->buffer = g_string_new(owner_vcard);
167 irmc->buffer = g_string_append(irmc->buffer, owner_vcard);
172 /* loop around buffer and add X-IRMC-LUID attribs */
174 while ((t = strstr(s, "UID:")) != NULL) {
175 /* add up to UID: into buffer */
176 irmc->buffer = g_string_append_len(irmc->buffer, s, t-s);
178 * add UID: line into buffer
179 * Not sure if UID is still needed if X-IRMC-LUID is there
182 t = strstr(s, "\r\n");
184 irmc->buffer = g_string_append_len(irmc->buffer, s, t-s);
185 /* add X-IRMC-LUID with same number as UID */
186 irmc->buffer = g_string_append_len(irmc->buffer,
188 s += 4; /* point to uid number */
189 irmc->buffer = g_string_append_len(irmc->buffer, s, t-s);
192 /* add remaining bit of buffer */
193 irmc->buffer = g_string_append(irmc->buffer, s);
196 obex_object_set_io_flags(irmc, G_IO_IN, 0);
199 static void *irmc_connect(struct obex_session *os, int *err)
201 struct irmc_session *irmc;
202 struct apparam_field *param;
207 manager_register_session(os);
209 irmc = g_new0(struct irmc_session, 1);
213 * Ideally get capabilities info here and use that to define
214 * IrMC DID and SN etc parameters.
215 * For now lets used hostname and some 'random' value
217 gethostname(irmc->did, DID_LEN);
218 strncpy(irmc->sn, "12345", sizeof(irmc->sn) - 1);
219 strncpy(irmc->manu, "obex", sizeof(irmc->manu) - 1);
220 strncpy(irmc->model, "mymodel", sizeof(irmc->model) - 1);
222 /* We need to know the number of contact/cal/nt entries
223 * somewhere so why not do it now.
225 param = g_new0(struct apparam_field, 1);
226 param->maxlistcount = 0; /* to count the number of vcards... */
227 param->filter = 0x200085; /* UID TEL N VERSION */
228 irmc->params = param;
229 irmc->request = phonebook_pull("telecom/pb.vcf", irmc->params,
230 phonebook_size_result, irmc, err);
231 ret = phonebook_pull_read(irmc->request);
238 static int irmc_get(struct obex_session *os, void *user_data)
240 struct irmc_session *irmc = user_data;
241 const char *type = obex_get_type(os);
242 const char *name = obex_get_name(os);
246 DBG("name %s type %s irmc %p", name, type ? type : "NA", irmc);
248 path = g_strdup(name);
250 ret = obex_get_stream_start(os, path);
257 static void irmc_disconnect(struct obex_session *os, void *user_data)
259 struct irmc_session *irmc = user_data;
263 manager_unregister_session(os);
266 if (irmc->params->searchval)
267 g_free(irmc->params->searchval);
268 g_free(irmc->params);
272 g_string_free(irmc->buffer, TRUE);
277 static int irmc_chkput(struct obex_session *os, void *user_data)
280 /* Reject all PUTs */
284 static void *irmc_open_devinfo(struct irmc_session *irmc, int *err)
287 irmc->buffer = g_string_new("");
289 g_string_append_printf(irmc->buffer,
293 "IRMC-VERSION:1.1\r\n"
294 "PB-TYPE-TX:VCARD2.1\r\n"
295 "PB-TYPE-RX:NONE\r\n"
296 "CAL-TYPE-TX:NONE\r\n"
297 "CAL-TYPE-RX:NONE\r\n"
298 "MSG-TYPE-TX:NONE\r\n"
299 "MSG-TYPE-RX:NONE\r\n"
300 "NOTE-TYPE-TX:NONE\r\n"
301 "NOTE-TYPE-RX:NONE\r\n",
302 irmc->manu, irmc->model, irmc->sn);
307 static void *irmc_open_pb(const char *name, struct irmc_session *irmc,
313 if (!g_strcmp0(name, ".vcf")) {
314 /* how can we tell if the vcard count call already finished? */
315 irmc->request = phonebook_pull("telecom/pb.vcf", irmc->params,
316 query_result, irmc, &ret);
318 DBG("phonebook_pull failed...");
322 ret = phonebook_pull_read(irmc->request);
324 DBG("phonebook_pull_read failed...");
331 if (!g_strcmp0(name, "/info.log")) {
332 mybuf = g_string_new("");
333 g_string_printf(mybuf, "Total-Records:%d\r\n"
334 "Maximum-Records:%d\r\n"
337 irmc->params->maxlistcount,
338 irmc->params->maxlistcount, irmc->did);
339 } else if (!strncmp(name, "/luid/", 6)) {
341 if (!g_strcmp0(name, "cc.log")) {
342 mybuf = g_string_new("");
343 g_string_printf(mybuf, "%d\r\n",
344 irmc->params->maxlistcount);
346 int l = strlen(name);
348 * Reply the same to any *.log so we hopefully force a
349 * full phonebook dump.
352 if (l > 4 && !g_strcmp0(name + l - 4, ".log")) {
353 DBG("changelog request, force whole book");
354 mybuf = g_string_new("");
355 g_string_printf(mybuf, "SN:%s\r\n"
357 "Total-Records:%d\r\n"
358 "Maximum-Records:%d\r\n"
361 irmc->params->maxlistcount,
362 irmc->params->maxlistcount);
374 irmc->buffer = mybuf;
376 irmc->buffer = g_string_append(irmc->buffer, mybuf->str);
377 g_string_free(mybuf, TRUE);
389 static void *irmc_open_cal(const char *name, struct irmc_session *irmc,
392 /* no suport yet. Just return an empty buffer. cal.vcs */
393 DBG("unsupported, returning empty buffer");
396 irmc->buffer = g_string_new("");
401 static void *irmc_open_nt(const char *name, struct irmc_session *irmc,
404 /* no suport yet. Just return an empty buffer. nt.vnt */
405 DBG("unsupported, returning empty buffer");
408 irmc->buffer = g_string_new("");
413 static void *irmc_open(const char *name, int oflag, mode_t mode, void *context,
414 size_t *size, int *err)
416 struct irmc_session *irmc = context;
420 DBG("name %s context %p", name, context);
422 if (oflag != O_RDONLY) {
426 if (name == NULL || strncmp(name, "telecom/", 8) != 0) {
432 if (!g_strcmp0(p, "devinfo.txt"))
433 return irmc_open_devinfo(irmc, err);
434 else if (!strncmp(p, "pb", 2))
435 return irmc_open_pb(p+2, irmc, err);
436 else if (!strncmp(p, "cal", 3))
437 return irmc_open_cal(p+3, irmc, err);
438 else if (!strncmp(p, "nt", 2))
439 return irmc_open_nt(p+2, irmc, err);
448 static int irmc_close(void *object)
450 struct irmc_session *irmc = object;
455 g_string_free(irmc->buffer, TRUE);
460 phonebook_req_finalize(irmc->request);
461 irmc->request = NULL;
467 static ssize_t irmc_read(void *object, void *buf, size_t count)
469 struct irmc_session *irmc = object;
472 DBG("buffer %p count %zu", irmc->buffer, count);
476 len = string_read(irmc->buffer, buf, count);
477 DBG("returning %d bytes", len);
481 static struct obex_mime_type_driver irmc_driver = {
482 .target = IRMC_TARGET,
483 .target_size = IRMC_TARGET_SIZE,
489 static struct obex_service_driver irmc = {
490 .name = "IRMC Sync server",
491 .service = OBEX_IRMC,
492 .channel = IRMC_CHANNEL,
494 .record = IRMC_RECORD,
495 .target = IRMC_TARGET,
496 .target_size = IRMC_TARGET_SIZE,
497 .connect = irmc_connect,
499 .disconnect = irmc_disconnect,
500 .chkput = irmc_chkput
503 static int irmc_init(void)
508 err = phonebook_init();
512 err = obex_mime_type_driver_register(&irmc_driver);
516 err = obex_service_driver_register(&irmc);
523 obex_mime_type_driver_unregister(&irmc_driver);
530 static void irmc_exit(void)
533 obex_service_driver_unregister(&irmc);
534 obex_mime_type_driver_unregister(&irmc_driver);
538 OBEX_PLUGIN_DEFINE(irmc, irmc_init, irmc_exit)