libsyncml 0.5.4

- Main Page
- Modules
- Data Structures
- Files

- File List

obex_client.c
1 /*
2 * libsyncml - A syncml protocol implementation
3 * Copyright (C) 2005 Armin Bauer <armin.bauer@opensync.org>
4 * Copyright (C) 2007-2009 Michael Bell <michael.bell@opensync.org>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 *
20 */
21
22 #include <libsyncml/syncml.h>
23 #include <libsyncml/syncml_internals.h>
24
25 #include <libsyncml/sml_error_internals.h>
26 #include <libsyncml/sml_transport_internals.h>
27
28 #include "obex_internals.h"
29 #include "obex_client_internals.h"
30 #include "obex_client_vendor_internals.h"
31
32 #ifdef ENABLE_BLUETOOTH_SDPLIB
33
34 #include <bluetooth/sdp.h>
35 #include <bluetooth/sdp_lib.h>
36
37 #endif
38
39 #include <fcntl.h>
40 #ifndef WIN32
41 #include <sys/poll.h>
42 #include <sys/stat.h>
43 #include <termios.h>
44 #include <sys/socket.h>
45 #include <netinet/in.h>
46 #include <netdb.h>
47 #include <sys/socket.h>
48 #include <arpa/inet.h>
49 #include <netinet/in.h>
50 #endif
51
52 #include <sys/types.h>
53 #include <stdio.h>
54 #include <stdlib.h>
55 #include <string.h>
56 #include <unistd.h>
57 #include <strings.h>
58
59 #ifndef WIN32
60
61 static SmlBool obex_cable_at(
62 SmlTransportObexClientEnv *userdata,
63 const char *cmd,
64 char *rspbuf,
65 int rspbuflen,
66 int timeout,
67 SmlError **error)
68 {
69 smlTrace(TRACE_ENTRY, "%s(%p, %s, %p, %i, %i, %p)", __func__, userdata, VA_STRING(cmd), rspbuf, rspbuflen, timeout, error);
70 CHECK_ERROR_REF
71
72 fd_set ttyset;
73 struct timeval tv;
74 gint fd;
75
76 SmlBool statusOnly = FALSE;
77
78 char *answer = NULL;
79 char *answer_end = NULL;
80 int answer_size;
81
82 char tmpbuf[100] = {0,};
83 int actual;
84 int total = 0;
85 int done = 0;
86
87 if (!strncasecmp("ATZ", cmd, 3)) {
88 smlTrace(TRACE_INTERNAL, "%s: detected standard ATZ command", __func__);
89 statusOnly = TRUE;
90 }
91 if (!strncasecmp("AT+CPROT=0", cmd, 10)) {
92 smlTrace(TRACE_INTERNAL, "%s: detected standard AT+CPROT=0 command", __func__);
93 statusOnly = TRUE;
94 }
95
96 /* send AT command */
97 fd = userdata->fd;
98 rspbuf[0] = 0;
99 if(fd < 0) {
100 smlErrorSet(error, SML_ERROR_GENERIC,
101 "The file descriptor for the cable AT command is not legal (%d).",
102 fd);
103 goto error;
104 }
105 if(cmd != NULL) {
106 // Write command
107 gint cmdlen;
108
109 cmdlen = strlen(cmd);
110 if(write(fd, cmd, cmdlen) < cmdlen) {
111 smlErrorSet(error, SML_ERROR_GENERIC,
112 "The command cannot be written (completely) to the file descriptor.");
113 goto error;
114 }
115 }
116
117 /* read the answer
118 * the default answer for AT commands is: \nresult\n\nOK\n
119 */
120 while(!done) {
121 FD_ZERO(&ttyset);
122 FD_SET(fd, &ttyset);
123 tv.tv_sec = timeout;
124 tv.tv_usec = 0;
125 if(select(fd+1, &ttyset, NULL, NULL, &tv)) {
126 actual = read(fd, &tmpbuf[total], sizeof(tmpbuf) - total);
127 if(actual < 0) {
128 smlErrorSet(error, SML_ERROR_GENERIC,
129 "read failed with return code %i.",
130 actual);
131 goto error;
132 }
133 total += actual;
134
135 /* Answer didn't come within the length of the buffer. Cancel! */
136 if(total == sizeof(tmpbuf)) {
137 smlErrorSet(error, SML_ERROR_NOT_IMPLEMENTED,
138 "The size of the answer to the AT command %s was too large for the internal buffer.",
139 VA_STRING(cmd));
140 goto error;
141 }
142
143 if( (answer = index(tmpbuf, '\n')) && /* \n infront of response */
144 (answer_end = index(answer+1, '\n')) /* \n at end of repsonse */
145 ) {
146 /* check for an error */
147 if (!strncasecmp("ERROR", answer+1, 5)) {
148 smlErrorSet(error, SML_ERROR_GENERIC,
149 "The AT command %s failed.",
150 VA_STRING(cmd));
151 goto error;
152 }
153
154 /* read the trailing \nOK\n if necessary */
155 if (statusOnly) {
156 /* Found end of answer */
157 done = 1;
158 } else {
159 char *ok = NULL;
160 char *ok_end = NULL;
161 if ( (ok = index(answer_end+1, '\n')) && /* \n infront of ok */
162 (ok_end = index(ok+1, '\n')) /* \n at end of ok */
163 ) {
164 /* Found end of answer */
165 done = 1;
166 }
167 }
168 }
169 } else {
170 /* Anser didn't come in time. Cancel */
171 smlErrorSet(error, SML_ERROR_INTERNAL_IO_ERROR,
172 "The read operation for the answer of the AT command was timed out.");
173 goto error;
174 }
175 }
176 smlTrace(TRACE_INTERNAL, "%s: answer: %s", __func__, VA_STRING(answer));
177
178 // Remove heading and trailing \r
179 if((*answer_end == '\r') || (*answer_end == '\n'))
180 answer_end--;
181 if((*answer_end == '\r') || (*answer_end == '\n'))
182 answer_end--;
183 if((*answer == '\r') || (*answer == '\n'))
184 answer++;
185 if((*answer == '\r') || (*answer == '\n'))
186 answer++;
187
188 answer_size = (answer_end) - answer +1;
189
190 smlTrace(TRACE_INTERNAL, "%s: answer size=%d", __func__, answer_size);
191 if( (answer_size) >= rspbuflen ) {
192 smlErrorSet(error, SML_ERROR_NOT_IMPLEMENTED,
193 "The size of the answer to the AT command %s was too large.",
194 VA_STRING(cmd));
195 goto error;
196 }
197
198 strncpy(rspbuf, answer, answer_size);
199 rspbuf[answer_size] = 0;
200 smlTrace(TRACE_EXIT, "%s", __func__);
201 return TRUE;
202 error:
203 smlTrace(TRACE_EXIT, "%s - %s", __func__, smlErrorPrint(error));
204 return FALSE;
205 }
206
207 static int smlTransportObexClientCableDisconnect(obex_t *handle, gpointer ud) {
208
209 smlTrace(TRACE_INTERNAL, "%s(%p, %p)", __func__, handle, ud);
210
211 SmlTransportObexClientEnv *userdata = (SmlTransportObexClientEnv *) ud;
212 if (userdata->fd >= 0) {
213 // Send a break to get out of OBEX-mode
214 #ifdef TCSBRKP
215 if(ioctl(userdata->fd, TCSBRKP, 0) < 0) {
216 #elif defined(TCSBRK)
217 if(ioctl(userdata->fd, TCSBRK, 0) < 0) {
218 #else
219 if(tcsendbreak(userdata->fd, 0) < 0) {
220 #endif /* TCSBRKP */
221 smlTrace(TRACE_INTERNAL, "%s: Unable to send break!", __func__);
222 }
223
224 tcsetattr(userdata->fd, TCSANOW, &userdata->oldtio);
225 close(userdata->fd);
226 }
227
228 return 0;
229 }
230
231 SmlTransportObexVendorType smlTransportObexClientGetVendor(const char *manufacturer)
232 {
233 smlTrace(TRACE_ENTRY, "%s(%s)", __func__, VA_STRING(manufacturer));
234 smlAssert(manufacturer);
235 SmlTransportObexVendorType vendor = SML_OBEX_VENDOR_UNKNOWN;
236
237 char *big = g_ascii_strup(manufacturer, -1);
238
239 if (strstr(big, "SAMSUNG") != NULL) {
240 smlTrace(TRACE_INTERNAL, "%s - Samsung found.", __func__);
241 vendor = SML_OBEX_VENDOR_SAMSUNG;
242 }
243
244 smlSafeCFree(&big);
245
246 smlTrace(TRACE_EXIT, "%s - %d", __func__, vendor);
247 return vendor;
248 }
249
250 static int smlTransportObexClientCableConnect(obex_t *handle, gpointer ud)
251 {
252 smlTrace(TRACE_ENTRY, "%s", __func__);
253
254 SmlError *error = NULL;
255 SmlTransportObexClientEnv *userdata = (SmlTransportObexClientEnv *) ud;
256 struct termios newtio;
257 char rspbuf[201];
258 rspbuf[sizeof(rspbuf)-1] = 0; /* sanitize for %s in printf format */
259
260 userdata->fd = open(userdata->path, O_RDWR|O_NONBLOCK|O_NOCTTY);
261 if (userdata->fd < 0) {
262 smlErrorSet(&error, SML_ERROR_INTERNAL_IO_ERROR,
263 "A valid file descriptor must be non-negative.");
264 goto error;
265 }
266
267 tcgetattr(userdata->fd, &userdata->oldtio);
268 bzero(&newtio, sizeof(struct termios));
269 newtio.c_cflag = B115200 | CLOCAL | CS8 | CREAD | CRTSCTS;
270 newtio.c_cc[VMIN] = 1;
271 newtio.c_cc[VTIME] = 0;
272 newtio.c_iflag = IGNPAR;
273 newtio.c_oflag = 0;
274 tcflush(userdata->fd, TCIFLUSH);
275 tcsetattr(userdata->fd, TCSANOW, &newtio);
276
277 /* init the communication line */
278 if (!obex_cable_at(userdata, "ATZ\r", rspbuf, sizeof(rspbuf) - 1, 1, &error))
279 goto error;
280
281 if (strcasecmp("OK", rspbuf)) {
282 smlErrorSet(&error, SML_ERROR_GENERIC,
283 "The ATZ command was answered with %s.",
284 rspbuf);
285 goto error;
286 }
287
288 if (!userdata->at_command && !userdata->manufacturer)
289 {
290 /* get the manufacturer */
291 if (!obex_cable_at(userdata, "AT+CGMI\r", rspbuf, sizeof(rspbuf), 1, &error)) {
292 smlTrace(TRACE_ERROR, "%s: Comm-error sending AT+CGMI\\r", __func__);
293 goto error;
294 }
295 userdata->manufacturer = g_strdup(rspbuf);
296 smlTrace(TRACE_ERROR, "%s: manufacturer %s", __func__, VA_STRING(userdata->manufacturer));
297 }
298 if (!userdata->at_command && !userdata->model)
299 {
300 /* get the model */
301 if (!obex_cable_at(userdata, "AT+CGMM\r", rspbuf, sizeof(rspbuf), 1, &error)) {
302 smlTrace(TRACE_ERROR, "%s: Comm-error sending AT+CGMM\\r", __func__);
303 goto error;
304 }
305 userdata->model = g_strdup(rspbuf);
306 smlTrace(TRACE_ERROR, "%s: model %s", __func__, VA_STRING(userdata->model));
307 }
308 if (!userdata->at_command)
309 {
310 /* start vendor specific handling */
311 SmlTransportObexVendorType vendor = SML_OBEX_VENDOR_UNKNOWN;
312 if (userdata->manufacturer) {
313 smlTrace(TRACE_INTERNAL, "%s: Try to find an appropriate AT command", __func__);
314 vendor = smlTransportObexClientGetVendor(userdata->manufacturer);
315 }
316 if (vendor) {
317 switch(vendor) {
318 case SML_OBEX_VENDOR_SAMSUNG:
319 smlTrace(TRACE_INTERNAL, "%s: Samsung detected", __func__);
320 if (!smlTransportObexVendorSamsungInit(userdata)) {
321 smlErrorSet(&error, SML_ERROR_GENERIC,
322 "The intialization for Samsung failed.");
323 goto error;
324 }
325 break;
326 default:
327 smlTrace(TRACE_ERROR, "%s: Ups, vendor defined but not handled.", __func__);
328 break;
329 }
330 }
331 }
332 if (!userdata->at_command) {
333 /* setup default */
334 smlTrace(TRACE_INTERNAL, "%s: Setting up default AT command AT+CPROT=0\\r", __func__);
335 userdata->at_command = g_strdup("AT+CPROT=0\r");
336 }
337
338 /* start the synchronization */
339 if (!obex_cable_at(userdata, userdata->at_command, rspbuf, sizeof(rspbuf) - 1, 1, &error))
340 goto error;
341
342 if (strcasecmp("CONNECT", rspbuf)) {
343 smlErrorSet(&error, SML_ERROR_GENERIC,
344 "The command AT+CPROT=0 failed with answer %s.",
345 rspbuf);
346 goto error;
347 }
348 // fcntl(userdata->fd, F_SETFL, O_NONBLOCK);
349 return 0;
350 error:
351 perror(smlErrorPrint(&error));
352 smlTransportObexClientCableDisconnect(handle, userdata);
353 smlTrace(TRACE_ERROR, "%s - %s", __func__, smlErrorPrint(&error));
354 smlErrorDeref(&error);
355 return -1;
356 }
357
358 static int smlTransportObexClientCableWrite(obex_t *handle, gpointer ud, guint8 *buf, int buflen) {
359
360 smlTrace(TRACE_ENTRY, "%s(%p, %p, %p, %i)", __func__, handle, ud, buf, buflen);
361 SmlTransportObexClientEnv *userdata = (SmlTransportObexClientEnv *) ud;
362 int written = 0;
363 int ret = 0;
364
365 while (ret >= 0 && written < buflen) {
366 ret = write(userdata->fd, buf + written, buflen - written);
367 if (ret >= 0)
368 written += ret;
369 }
370
371 smlTrace(TRACE_EXIT, "%s: %i", __func__, written);
372 return written;
373 }
374
375 gint obex_cable_handleinput(obex_t *handle, gpointer ud, gint timeout) {
376 smlTrace(TRACE_ENTRY, "%s(%p, %p, %i)", __func__, handle, ud, timeout);
377 struct timeval to;
378 fd_set readfds;
379 char buf[512];
380 SmlTransportObexClientEnv *userdata;
381 int ret = 0;
382 int actual = 0;
383
384 userdata = (SmlTransportObexClientEnv*) ud;
385 FD_ZERO(&readfds);
386 FD_SET(userdata->fd, &readfds);
387 to.tv_sec = timeout;
388 to.tv_usec = 0;
389
390
391 smlTrace(TRACE_INTERNAL, "%s - %i", __func__, __LINE__);
392 ret = select(userdata->fd + 1, &readfds, NULL, NULL, &to);
393
394 if (ret < 1)
395 goto error;
396
397 smlTrace(TRACE_INTERNAL, "%s - %i", __func__, __LINE__);
398 if ((actual = read(userdata->fd, buf, sizeof(buf))) <= 0)
399 goto end;
400
401 smlTrace(TRACE_INTERNAL, "%s - %i", __func__, __LINE__);
402 OBEX_CustomDataFeed(handle, (unsigned char *) buf, actual);
403
404 end:
405 smlTrace(TRACE_EXIT, "%s(%p, %p, %i)", __func__, handle, ud, timeout);
406 return actual;
407
408 error:
409
410 smlTrace(TRACE_EXIT_ERROR, "%s: %i", __func__, ret);
411 return ret;
412
413 }
414
415 #endif
416
417 static void smlTransportObexClientEvent(obex_t *handle, obex_object_t *object, int mode, int event, int obex_cmd, int obex_rsp)
418 {
419 smlTrace(TRACE_ENTRY, "%s(%p, %p, %i, %i, %i, %i)", __func__, handle, object, mode, event, obex_cmd, obex_rsp);
420 SmlTransportObexClientEnv *env = OBEX_GetUserData(handle);
421 SmlError *error = NULL;
422
423 switch (event) {
424 case OBEX_EV_PROGRESS:
425 smlTrace(TRACE_INTERNAL, "%s: Progress", __func__);
426 break;
427 case OBEX_EV_REQDONE:
428 smlTrace(TRACE_INTERNAL, "%s: Request Done", __func__);
429 env->busy = FALSE;
430
431 if (obex_rsp != OBEX_RSP_SUCCESS) {
432 switch(obex_cmd) {
433 case OBEX_CMD_CONNECT:
434 smlErrorSet(&error, SML_ERROR_INTERNAL_MISCONFIGURATION,
435 "The OBEX client cannot connect to the configured device or resource (%s - 0x%x).",
436 OBEX_ResponseToString(obex_rsp),
437 obex_rsp);
438 break;
439 default:
440 smlErrorSet(&error, SML_ERROR_INTERNAL_IO_ERROR,
441 "%s (0x%x)",
442 OBEX_ResponseToString(obex_rsp),
443 obex_rsp);
444 break;
445 }
446 goto error;
447 }
448
449 /* Get the connection id if we connected */
450 switch (obex_cmd) {
451 case OBEX_CMD_CONNECT:;
452 uint8_t headertype = 0;
453 obex_headerdata_t header;
454 uint32_t len;
455 char *who = NULL;
456
457 while (OBEX_ObjectGetNextHeader(env->obexhandle, object, &headertype, &header, &len)) {
458 smlTrace(TRACE_INTERNAL, "%s: Next header %i, %d, %p", __func__, headertype, header.bq4, header.bs);
459 switch (headertype) {
460 case OBEX_HDR_CONNECTION:
461 smlTrace(TRACE_INTERNAL, "%s: Found connection number: %d", __func__, header.bq4);
462 env->connection_id = header.bq4;
463 break;
464 case OBEX_HDR_WHO:
465 who = g_strndup((char *)header.bs, len);
466 smlTrace(TRACE_INTERNAL, "%s: Found who: %s", __func__, VA_STRING(who));
467 break;
468 default:
469 smlTrace(TRACE_INTERNAL, "%s: Unknown header", __func__);
470 }
471 }
472
473 if (!env->connection_id) {
474 smlErrorSet(&error, SML_ERROR_GENERIC, "Missing connection id");
475 smlSafeCFree(&who);
476 goto error;
477 }
478
479 smlTrace(TRACE_INTERNAL, "%s: Got who: %s", __func__, VA_STRING(who));
480 if (!who || strcmp(who, "SYNCML-SYNC")) {
481 smlErrorSet(&error, SML_ERROR_GENERIC, "Missing or wrong who response");
482 smlSafeCFree(&who);
483 goto error;
484 }
485 smlSafeCFree(&who);
486
487 smlTransportReceiveEvent(env->tsp, NULL, SML_TRANSPORT_EVENT_CONNECT_DONE, NULL, NULL);
488 break;
489 case OBEX_CMD_DISCONNECT:;
490 if (!env->isDisconnected)
491 {
492 env->busy = FALSE;
493 env->isDisconnected = TRUE;
494 OBEX_TransportDisconnect(env->obexhandle);
495 smlTransportReceiveEvent(env->tsp, NULL, SML_TRANSPORT_EVENT_DISCONNECT_DONE, NULL, NULL);
496 }
497 break;
498 case OBEX_CMD_GET:;
499 smlTrace(TRACE_INTERNAL, "%s: Got GET command done", __func__);
500 uint32_t length = 0;
501 char *body = NULL;
502
503 while (OBEX_ObjectGetNextHeader(handle, object, &headertype, &header, &len)) {
504 smlTrace(TRACE_INTERNAL, "%s: Next header %i, %d, %p, len %i", __func__, headertype, header.bq4, header.bs, len);
505 switch (headertype) {
506 case OBEX_HDR_LENGTH:
507 smlTrace(TRACE_INTERNAL, "%s: Found length: %d", __func__, header.bq4);
508 length = header.bq4;
509 break;
510 case OBEX_HDR_BODY:
511 if (!length) {
512 smlTrace(TRACE_INTERNAL, "%s: Length not given. Calculating it to: %i", __func__, len);
513 length = len;
514 }
515
516 if (!length) {
517 smlErrorSet(&error, SML_ERROR_GENERIC, "Got zero length!");
518 goto error;
519 }
520
521 /* We need one byte more than the response length
522 * because the data can be a native XML message.
523 * If the data is a native XML message then it is
524 * sometimes directly used as string.
525 *
526 * The string is automatically NULL terminated
527 * because smlTryMalloc0 fills the new memory with NULLs.
528 */
529 body = smlTryMalloc0(length + 1, &error);
530 if (!body)
531 goto error;
532
533 memcpy(body, header.bs, length);
534 break;
535 default:
536 smlTrace(TRACE_INTERNAL, "%s: Unknown header", __func__);
537 }
538 }
539
540 if (!length) {
541 smlErrorSet(&error, SML_ERROR_GENERIC, "Missing length");
542 goto error;
543 }
544
545 if (!body) {
546 smlErrorSet(&error, SML_ERROR_GENERIC, "Missing body");
547 goto error;
548 }
549
550 SmlTransportData *tspdata = smlTransportDataNew(body, length, env->mimetype, TRUE, &error);
551 if (!tspdata)
552 goto error;
553
554 smlTransportReceiveEvent(env->tsp, NULL, SML_TRANSPORT_EVENT_DATA, tspdata, NULL);
555 smlTransportDataDeref(tspdata);
556 break;
557 }
558 break;
559 case OBEX_EV_LINKERR:
560 if (obex_rsp == 0)
561 {
562 /* This is a disconnect. */
563 if (!env->isDisconnected)
564 {
565 env->busy = FALSE;
566 env->isDisconnected = TRUE;
567 OBEX_TransportDisconnect(env->obexhandle);
568 smlTransportReceiveEvent(env->tsp, NULL, SML_TRANSPORT_EVENT_DISCONNECT_DONE, NULL, NULL);
569 }
570 } else {
571 /* This is a normal error. */
572 smlErrorSet(&error, SML_ERROR_GENERIC,
573 "Link Error: %s - 0x%x",
574 OBEX_ResponseToString(obex_rsp), obex_rsp);
575 smlTrace(TRACE_INTERNAL, "%s - %s",
576 __func__, smlErrorPrint(&error));
577 goto error;
578 }
579 break;
580 case OBEX_EV_STREAMEMPTY:
581 smlTrace(TRACE_INTERNAL, "%s: Empty Stream", __func__);
582 break;
583 }
584
585 smlTrace(TRACE_EXIT, "%s", __func__);
586 return;
587
588 error:
589 smlTransportReceiveEvent(env->tsp, NULL, SML_TRANSPORT_EVENT_ERROR, NULL, error);
590 smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(&error));
591 smlErrorDeref(&error);
592 env->busy = FALSE;
593 env->error = TRUE;
594 return;
595 }
596
597 static SmlBool smlTransportObexClientSetConfigOption(
598 SmlTransport *tsp,
599 const char *name,
600 const char *value,
601 SmlError **error)
602 {
603 smlTrace(TRACE_ENTRY, "%s(%p, %s, %s, %p)", __func__, tsp, VA_STRING(name), VA_STRING(value), error);
604 CHECK_ERROR_REF
605 smlAssert(tsp);
606 smlAssert(tsp->transport_data);
607 smlAssert(name); /* should be checked by smlTransportSetConfigOption */
608 SmlTransportObexClientEnv *env = tsp->transport_data;
609
610 /* This transport does not accept NULL as value. */
611 if (!value)
612 {
613 smlErrorSet(error, SML_ERROR_INTERNAL_MISCONFIGURATION,
614 "The configuration option %s requires a value.", name);
615 goto error;
616 }
617
618 if (!strcmp(name, SML_TRANSPORT_CONFIG_BLUETOOTH_CHANNEL)) {
619 env->port = atoi(value);
620 smlTrace(TRACE_INTERNAL, "%s: Bluetooth channel %i detected", __func__, env->port);
621 } else if (!strcmp(name, SML_TRANSPORT_CONFIG_BLUETOOTH_ADDRESS)) {
622 /* This is the MAC of the remote device. */
623 env->path = g_strdup(value);
624 smlTrace(TRACE_INTERNAL, "%s: Bluetooth MAC %s detected", __func__, VA_STRING(env->path));
625 } else if (!strcmp(name, SML_TRANSPORT_CONFIG_PORT)) {
626 if (env->type == SML_TRANSPORT_CONNECTION_TYPE_BLUETOOTH)
627 g_warning("Please use %s instead of %s.",
628 SML_TRANSPORT_CONFIG_BLUETOOTH_CHANNEL,
629 SML_TRANSPORT_CONFIG_PORT);
630 env->port = atoi(value);
631 smlTrace(TRACE_INTERNAL, "%s: Port or Bluetooth channel %i detected",
632 __func__, env->port);
633 } else if (!strcmp(name, SML_TRANSPORT_CONFIG_URL)) {
634 if (env->type == SML_TRANSPORT_CONNECTION_TYPE_BLUETOOTH)
635 g_warning("Please use %s instead of %s.",
636 SML_TRANSPORT_CONFIG_BLUETOOTH_ADDRESS,
637 SML_TRANSPORT_CONFIG_URL);
638 env->path = g_strdup(value);
639 smlTrace(TRACE_INTERNAL, "%s: URL or Bluetooth MAC %s detected",
640 __func__, VA_STRING(env->path));
641 } else if (!strcmp(name, SML_TRANSPORT_CONFIG_IRDA_SERVICE)) {
642 if (env->irda_service)
643 smlSafeCFree(&(env->irda_service));
644 env->irda_service = g_strdup(value);
645 smlTrace(TRACE_INTERNAL, "%s: IrDA service %s detected", __func__, VA_STRING(env->irda_service));
646 } else if (!strcmp(name, SML_TRANSPORT_CONFIG_AT_COMMAND)) {
647 if (env->at_command)
648 smlSafeCFree(&(env->at_command));
649 env->at_command = g_strdup(value);
650 smlTrace(TRACE_INTERNAL, "%s: AT command %s detected", __func__, VA_STRING(env->at_command));
651 } else if (!strcmp(name, SML_TRANSPORT_CONFIG_AT_MANUFACTURER)) {
652 if (env->manufacturer)
653 smlSafeCFree(&(env->manufacturer));
654 env->manufacturer = g_strdup(value);
655 smlTrace(TRACE_INTERNAL, "%s: AT manufacturer %s detected", __func__, VA_STRING(env->manufacturer));
656 } else if (!strcmp(name, SML_TRANSPORT_CONFIG_AT_MODEL)) {
657 if (env->model)
658 smlSafeCFree(&(env->model));
659 env->model = g_strdup(value);
660 smlTrace(TRACE_INTERNAL, "%s: AT model %s detected", __func__, VA_STRING(env->model));
661 } else if (!strcmp(name, SML_TRANSPORT_CONFIG_DATASTORE)) {
662 /* This is needed for vendors which signal the datastore with AT commands. */
663 SmlTransportObexDatastoreType datastore = SML_TRANSPORT_OBEX_DATASTORE_UNKNOWN;
664 if (!strcasecmp(SML_TRANSPORT_CONFIG_DATASTORE_EVENT, value))
665 datastore = SML_TRANSPORT_OBEX_DATASTORE_EVENT;
666 if (!strcasecmp(SML_TRANSPORT_CONFIG_DATASTORE_TODO, value))
667 datastore = SML_TRANSPORT_OBEX_DATASTORE_TODO;
668 if (!strcasecmp(SML_TRANSPORT_CONFIG_DATASTORE_CONTACT, value))
669 datastore = SML_TRANSPORT_OBEX_DATASTORE_CONTACT;
670 if (!strcasecmp(SML_TRANSPORT_CONFIG_DATASTORE_NOTE, value))
671 datastore = SML_TRANSPORT_OBEX_DATASTORE_NOTE;
672 if (!datastore) {
673 smlErrorSet(error, SML_ERROR_INTERNAL_MISCONFIGURATION, "Unknown datastore %s found.", value);
674 goto error;
675 }
676 SmlTransportObexDatastoreType *type = smlTryMalloc0(sizeof(SmlTransportObexDatastoreType), error);
677 if (!type)
678 goto error;
679 *type = datastore;
680 env->datastores = g_list_append(env->datastores, type);
681 smlTrace(TRACE_INTERNAL, "%s: Datastore %i detected", __func__, datastore);
682 } else {
683 smlErrorSet(error, SML_ERROR_INTERNAL_MISCONFIGURATION, "Unknown parameter %s found.", name);
684 goto error;
685 }
686
687 smlTrace(TRACE_EXIT, "%s", __func__);
688 return TRUE;
689 error:
690 smlTrace(TRACE_EXIT_ERROR, "%s - %s", __func__, smlErrorPrint(error));
691 return FALSE;
692
693 }
694
695 static SmlBool smlTransportObexClientSetConnectionType(
696 SmlTransport *tsp,
697 SmlTransportConnectionType type,
698 SmlError **error)
699 {
700 smlTrace(TRACE_ENTRY, "%s(%p, %i, %p)", __func__, tsp, type, error);
701 CHECK_ERROR_REF
702 smlAssert(tsp);
703 smlAssert(tsp->transport_data);
704 SmlTransportObexClientEnv *env = tsp->transport_data;
705
706 env->type = type;
707
708 switch (env->type) {
709 case SML_TRANSPORT_CONNECTION_TYPE_NET:
710 env->obexhandle = OBEX_Init(OBEX_TRANS_FD, smlTransportObexClientEvent, 0);
711 break;
712 case SML_TRANSPORT_CONNECTION_TYPE_IRDA:
713 env->obexhandle = OBEX_Init(OBEX_TRANS_IRDA, smlTransportObexClientEvent, 0);
714 break;
715 case SML_TRANSPORT_CONNECTION_TYPE_BLUETOOTH:
716 env->obexhandle = OBEX_Init(OBEX_TRANS_BLUETOOTH, smlTransportObexClientEvent, 0);
717 break;
718 case SML_TRANSPORT_CONNECTION_TYPE_SERIAL:
719 env->obexhandle = OBEX_Init(OBEX_TRANS_CUST, smlTransportObexClientEvent, 0);
720 break;
721 case SML_TRANSPORT_CONNECTION_TYPE_USB:
722 env->obexhandle = OBEX_Init(OBEX_TRANS_USB, smlTransportObexClientEvent, 0);
723 break;
724 default:
725 smlErrorSet(error, SML_ERROR_GENERIC, "Unknown obex type");
726 smlTrace(TRACE_EXIT_ERROR, "%s - %s", __func__, smlErrorPrint(error));
727 return FALSE;
728 }
729
730 if (!env->obexhandle) {
731 smlErrorSet(error, SML_ERROR_GENERIC, "Unable to open connection");
732 smlTrace(TRACE_EXIT_ERROR, "%s - %s", __func__, smlErrorPrint(error));
733 return FALSE;
734 }
735
736 #ifndef WIN32
737 if (env->type == SML_TRANSPORT_CONNECTION_TYPE_SERIAL) {
738 obex_ctrans_t cabletrans = {
739 smlTransportObexClientCableConnect,
740 smlTransportObexClientCableDisconnect,
741 NULL,
742 smlTransportObexClientCableWrite,
743 obex_cable_handleinput,
744 env};
745 OBEX_RegisterCTransport(env->obexhandle, &cabletrans);
746 }
747 #endif
748
749 smlTrace(TRACE_EXIT, "%s", __func__);
750 return TRUE;
751 }
752
753 static SmlBool smlTransportObexClientInit(SmlTransport *tsp, SmlError **error)
754 {
755 smlTrace(TRACE_ENTRY, "%s(%p, %p)", __func__, tsp, error);
756 CHECK_ERROR_REF
757 smlAssert(tsp);
758 smlAssert(tsp->transport_data);
759 SmlTransportObexClientEnv *env = tsp->transport_data;
760
761 OBEX_SetUserData(env->obexhandle, env);
762
763 switch (env->type) {
764 case SML_TRANSPORT_CONNECTION_TYPE_NET:
765 if (!env->path) {
766 smlErrorSet(error, SML_ERROR_GENERIC,
767 "The hostname or address is missing.");
768 goto error;
769 }
770 break;
771 case SML_TRANSPORT_CONNECTION_TYPE_BLUETOOTH:
772 if (!env->path) {
773 smlErrorSet(error, SML_ERROR_GENERIC,
774 "The bluetooth address is missing.");
775 goto error;
776 }
777 #ifndef ENABLE_BLUETOOTH_SDPLIB
778 if (!env->port) {
779 smlErrorSet(error, SML_ERROR_GENERIC,
780 "The bluetooth channel is missing.");
781 goto error;
782 }
783 #endif
784 break;
785 default:
786 /* nothing to check here */
787 break;
788 }
789
790 smlTrace(TRACE_EXIT, "%s - TRUE", __func__);
791 return TRUE;
792 error:
793 OBEX_Cleanup(env->obexhandle);
794 if (env->path)
795 smlSafeCFree(&(env->path));
796 if (env->at_command)
797 smlSafeCFree(&(env->at_command));
798 if (env->manufacturer)
799 smlSafeCFree(&(env->manufacturer));
800 if (env->model)
801 smlSafeCFree(&(env->model));
802 smlSafeFree((gpointer *)&env);
803 smlTrace(TRACE_EXIT_ERROR, "%s - %s", __func__, smlErrorPrint(error));
804 return FALSE;
805 }
806
807 static SmlBool smlTransportObexClientFinalize(void *data, SmlError **error)
808 {
809 smlTrace(TRACE_ENTRY, "%s(%p, %p)", __func__, data, error);
810 CHECK_ERROR_REF
811 smlAssert(data);
812 SmlTransportObexClientEnv *env = data;
813
814 smlAssert(env->tsp);
815 if (env->path) smlSafeCFree(&(env->path));
816
817 OBEX_Cleanup(env->obexhandle);
818
819 if (env->irda_service)
820 smlSafeCFree(&(env->irda_service));
821 if (env->at_command)
822 smlSafeCFree(&(env->at_command));
823 if (env->manufacturer)
824 smlSafeCFree(&(env->manufacturer));
825 if (env->model)
826 smlSafeCFree(&(env->model));
827 while (env->datastores) {
828 SmlTransportObexDatastoreType *type = env->datastores->data;
829 env->datastores = g_list_remove(env->datastores, type);
830 smlSafeFree((gpointer *) &type);
831 }
832
833 smlSafeFree((gpointer *)&env);
834
835 smlTrace(TRACE_EXIT, "%s", __func__);
836 return TRUE;
837 }
838
839 static void smlTransportObexClientConnect(void *data)
840 {
841 smlTrace(TRACE_ENTRY, "%s(%p)", __func__, data);
842 smlAssert(data);
843 SmlTransportObexClientEnv *env = data;
844 int fd = 0;
845 SmlError *error = NULL;
846 unsigned int obex_intf_cnt;
847 obex_interface_t *obex_intf;
848 env->isDisconnected = FALSE;
849
850 if (env->type == SML_TRANSPORT_CONNECTION_TYPE_NET) {
851 smlTrace(TRACE_INTERNAL, "%s: connecting to inet address %s:%d",
852 __func__, VA_STRING(env->path), env->port);
853 struct sockaddr_in addr;
854 memset(&addr, 0, sizeof(addr));
855 addr.sin_family = AF_INET;
856 addr.sin_port = htons(env->port);
857
858 struct hostent *hostinfo = gethostbyname (env->path);
859 if (!hostinfo) {
860 smlErrorSet(&error, SML_ERROR_GENERIC, "Unknown host %s", env->path);
861 goto error;
862 }
863 addr.sin_addr = *(struct in_addr *) hostinfo->h_addr_list[0];
864
865 fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
866 if (fd < 0) {
867 smlErrorSet(&error, SML_ERROR_GENERIC, "Cannot create socket: %s", strerror(errno));
868 goto error;
869 }
870
871 smlTrace(TRACE_INTERNAL, "%s: socket %i", __func__, fd);
872
873 char *addrstr = inet_ntoa(addr.sin_addr);
874 smlTrace(TRACE_INTERNAL, "%s: peer addr = %d %s %i", __func__, hostinfo->h_addr_list[0], VA_STRING(addrstr), env->port);
875
876 if (connect(fd, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) < 0) {
877 smlErrorSet(&error, SML_ERROR_GENERIC, "Cannot connect socket: %s", strerror(errno));
878 goto error_close;
879 }
880 smlTrace(TRACE_INTERNAL, "%s: connect done", __func__);
881
882 } else if (env->type == SML_TRANSPORT_CONNECTION_TYPE_USB) {
883 smlTrace(TRACE_INTERNAL, "%s: connecting to usb interface %i", __func__, env->port);
884
885 obex_intf_cnt = OBEX_FindInterfaces(env->obexhandle, &obex_intf);
886 smlTrace(TRACE_INTERNAL, "%s: found %i interfaces", __func__, obex_intf_cnt);
887
888 if (obex_intf_cnt <= 0) {
889 smlErrorSet(&error, SML_ERROR_GENERIC, "There are no valid USB interfaces.");
890 goto error;
891 } else if (env->port >= obex_intf_cnt) {
892 smlErrorSet(&error, SML_ERROR_GENERIC, "Unable to find the USB interface number %i", env->port);
893 goto error;
894 } else {
895 if (GET_OBEX_RESULT(OBEX_InterfaceConnect(env->obexhandle, &obex_intf[env->port])) < 0) {
896 smlErrorSet(&error, SML_ERROR_GENERIC,
897 "The interface cannot be connected. %s (%i).",
898 strerror(errno), errno);
899 goto error;
900 }
901 }
902 smlTrace(TRACE_INTERNAL, "%s: usb connect done", __func__);
903 } else if (env->type == SML_TRANSPORT_CONNECTION_TYPE_BLUETOOTH) {
904 #ifdef ENABLE_BLUETOOTH
905 smlTrace(TRACE_INTERNAL, "%s: connecting to bluetooth device %s channel %i", __func__, VA_STRING(env->path), env->port);
906
907 uint8_t channel = env->port;
908 bdaddr_t bdaddr;
909 str2ba(env->path, &bdaddr);
910
911 #ifdef ENABLE_BLUETOOTH_SDPLIB
912 if (channel == 0) {
913 /* FIXME: Is this code really memory leak free !? */
914
915 sdp_session_t *sdp_session = sdp_connect(BDADDR_ANY, &bdaddr, SDP_RETRY_IF_BUSY);
916 if (!sdp_session) {
917 smlErrorSet(&error, SML_ERROR_GENERIC, "The Bluetooth connect to search for the channel failed.");
918 goto error;
919 }
920 unsigned char syncml_client_uuid[] = {
921 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x10, 0x00,
922 0x80, 0x00, 0x00, 0x02, 0xEE, 0x00, 0x00, 0x02};
923 uuid_t uuid;
924 uint32_t range = SDP_ATTR_PROTO_DESC_LIST;
925 sdp_uuid128_create(&uuid, syncml_client_uuid);
926 sdp_list_t *attribute = sdp_list_append(0, &range);
927 sdp_list_t *class = sdp_list_append(0, &uuid);
928
929 sdp_list_t *list;
930 if (sdp_service_search_attr_req(sdp_session, class, SDP_ATTR_REQ_INDIVIDUAL, attribute, &list) < 0) {
931 sdp_close(sdp_session);
932 sdp_list_free(attribute, 0);
933 sdp_list_free(class, 0);
934
935 smlErrorSet(
936 &error, SML_ERROR_GENERIC,
937 "The service search on the Bluetooth device failed.");
938 goto error;
939 }
940 sdp_list_free(attribute, 0);
941 sdp_list_free(class, 0);
942
943 sdp_list_t *iterator;
944 for(iterator = list; iterator; iterator = iterator->next) {
945 sdp_record_t *item = (sdp_record_t *) iterator->data;
946 sdp_list_t *protos = NULL;
947
948 sdp_get_access_protos(item, &protos);
949 if(protos) {
950 channel = sdp_get_proto_port(protos, RFCOMM_UUID);
951 }
952 sdp_record_free(item);
953 }
954 sdp_list_free(list, 0);
955 sdp_close(sdp_session);
956 sleep(1);
957 smlTrace(TRACE_INTERNAL,
958 "%s: SDP SyncML channel for %s: %d",
959 __func__, env->path, channel);
960
961 }
962 #endif
963
964 if (GET_OBEX_RESULT(BtOBEX_TransportConnect(env->obexhandle, BDADDR_ANY, &bdaddr, channel)) < 0) {
965 smlErrorSet(&error, SML_ERROR_GENERIC,
966 "The Bluetooth connect failed. %s (%i).",
967 strerror(errno), errno);
968 goto error;
969 }
970
971 smlTrace(TRACE_INTERNAL, "%s: bluetooth connect done", __func__);
972 #else
973 smlErrorSet(&error, SML_ERROR_GENERIC, "Bluetooth not enabled");
974 goto error;
975 #endif
976 } else if (env->type == SML_TRANSPORT_CONNECTION_TYPE_IRDA) {
977 smlTrace(TRACE_INTERNAL, "%s: connecting to IrDA service %s", __func__, env->irda_service ? env->irda_service : "OBEX");
978
979 if (GET_OBEX_RESULT(IrOBEX_TransportConnect(env->obexhandle, env->irda_service)) < 0) {
980 smlErrorSet(&error, SML_ERROR_GENERIC,
981 "The IrDA connect failed. %s (%i).",
982 strerror(errno), errno);
983 goto error;
984 }
985
986 smlTrace(TRACE_INTERNAL, "%s: IrDA connect done", __func__);
987 } else {
988 #ifndef WIN32
989 struct termios tio;
990 memset(&tio, 0, sizeof(tio));
991
992 /* Open the file descriptor */
993 fd = open(env->path, O_RDWR | O_NOCTTY);
994 if (fd == -1) {
995 smlErrorSet(&error, SML_ERROR_GENERIC, "Unable to open %s: %s", env->path, strerror(errno));
996 goto error;
997 }
998
999 /* Lock it*/
1000 if (lockf(fd, F_TLOCK, 0)) {
1001 smlErrorSet(&error, SML_ERROR_GENERIC, "Unable to lock %s: %s", env->path, strerror(errno));
1002 goto error_close;
1003 }
1004
1005 if (tcgetattr(fd, &tio)) {
1006 smlErrorSet(&error, SML_ERROR_GENERIC, "Unable to get attr from %s", env->path);
1007 goto error_unlock;
1008 }
1009
1010 /* Make the transmission raw (8 bit clean, no echo etc) */
1011 /* Set the speed to a higher value (9600 would be default) */
1012
1013 #if defined(sun) && defined(__SVR4)
1014 tio.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
1015 tio.c_oflag &= ~OPOST;
1016 tio.c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
1017 tio.c_cflag &= ~(CSIZE|PARENB);
1018 tio.c_cflag |= CS8;
1019 #else
1020 cfmakeraw(&tio);
1021 #endif
1022
1023 cfsetispeed(&tio, B115200);
1024 cfsetospeed(&tio, B115200);
1025
1026 if (tcsetattr(fd, TCSANOW, &tio)) {
1027 smlErrorSet(&error, SML_ERROR_GENERIC, "Unable to set attr from %s", env->path);
1028 goto error_unlock;
1029 }
1030 tcflush(fd, TCIFLUSH);
1031 #endif
1032 }
1033
1034 if (env->type != SML_TRANSPORT_CONNECTION_TYPE_USB
1035 && env->type != SML_TRANSPORT_CONNECTION_TYPE_BLUETOOTH
1036 && env->type != SML_TRANSPORT_CONNECTION_TYPE_IRDA) {
1037 /* Start the obex transport */
1038 if (GET_OBEX_RESULT(FdOBEX_TransportSetup(env->obexhandle, fd, fd, 4096)) < 0) {
1039 smlErrorSet(&error, SML_ERROR_GENERIC,
1040 "The OBEX transport setup failed. %s (%i).",
1041 strerror(errno), errno);
1042 goto error_unlock;
1043 }
1044 }
1045
1046 /* Make a new connect object */
1047 obex_object_t *obj = OBEX_ObjectNew(env->obexhandle, OBEX_CMD_CONNECT);
1048 if (!obj) {
1049 smlErrorSet(&error, SML_ERROR_GENERIC, "Unable to create new connect object");
1050 goto error_transport_close;
1051 }
1052
1053 /* Now add the header for the sync target */
1054 obex_headerdata_t header;
1055 header.bs = (unsigned char *)"SYNCML-SYNC";
1056 OBEX_ObjectAddHeader(env->obexhandle, obj, OBEX_HDR_TARGET, header, strlen((char *)header.bs), OBEX_FL_FIT_ONE_PACKET);
1057
1058 env->busy = TRUE;
1059 /* Now we need to send the request */
1060 if (GET_OBEX_RESULT(OBEX_Request(env->obexhandle, obj)) < 0) {
1061 smlErrorSet(&error, SML_ERROR_GENERIC,
1062 "The OBEX request failed. %s (%i).",
1063 strerror(errno), errno);
1064 goto error_free_obj;
1065 }
1066
1067 if (env->error) {
1068 smlTrace(TRACE_EXIT, "%s: Unable to send connect request. Bailing out", __func__);
1069 return;
1070 }
1071
1072 /* Lets see if it was successfull */
1073 while (env->busy) {
1074 int result = OBEX_HandleInput(env->obexhandle, 20);
1075 if (result < 0) {
1076 smlErrorSet(&error, SML_ERROR_GENERIC,
1077 "The connect request was not successful (%i).",
1078 result);
1079 goto error;
1080 } else if (result == 0) {
1081 smlErrorSet(&error, SML_ERROR_GENERIC,
1082 "The connect request was timed out.");
1083 goto error;
1084 }
1085 }
1086
1087 smlTrace(TRACE_EXIT, "%s", __func__);
1088 return;
1089
1090 error_free_obj:
1091 OBEX_ObjectDelete(env->obexhandle, obj);
1092 error_transport_close:
1093 OBEX_Cleanup(env->obexhandle);
1094 error_unlock:
1095 #ifndef WIN32
1096 if (!lockf(fd, F_ULOCK, 0))
1097 smlTrace(TRACE_ERROR, "%s: error_unlock failed.", __func__);
1098 #endif
1099 error_close:
1100 close(fd);
1101 error:
1102 smlTransportReceiveEvent(env->tsp, NULL, SML_TRANSPORT_EVENT_ERROR, NULL, error);
1103 smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(&error));
1104 smlErrorDeref(&error);
1105 }
1106
1107 static void smlTransportObexClientDisconnect(void *data, void *linkdata)
1108 {
1109 smlTrace(TRACE_ENTRY, "%s(%p, %p)", __func__, data, linkdata);
1110 smlAssert(data);
1111 SmlTransportObexClientEnv *env = data;
1112 SmlError *error = NULL;
1113
1114 if (env->isDisconnected) {
1115 /* This is a bug. If the transport is disconnected
1116 * then there is no reason for a second disconnect.
1117 * Nevertheless this mistake should not be fatal.
1118 */
1119 smlTrace(TRACE_EXIT_ERROR,
1120 "%s - disconnect called on already disconnected transport",
1121 __func__);
1122 g_warning("%s called on disconnected OBEX connection.", __func__);
1123 return;
1124 }
1125
1126 env->error = FALSE;
1127
1128 /* Make a new disconnect object */
1129 obex_object_t *obj = OBEX_ObjectNew(env->obexhandle, OBEX_CMD_DISCONNECT);
1130 if (!obj) {
1131 smlErrorSet(&error, SML_ERROR_GENERIC, "Unable to create new disconnect object");
1132 goto error;
1133 }
1134
1135 /* Add the connection id */
1136 obex_headerdata_t header;
1137 header.bq4 = env->connection_id;
1138 OBEX_ObjectAddHeader(env->obexhandle, obj, OBEX_HDR_CONNECTION, header, sizeof(env->connection_id), OBEX_FL_FIT_ONE_PACKET);
1139
1140 env->busy = TRUE;
1141 /* Now we need to send the request */
1142 if (GET_OBEX_RESULT(OBEX_Request(env->obexhandle, obj)) < 0) {
1143 smlErrorSet(&error, SML_ERROR_GENERIC,
1144 "The OBEX request cannot be sent. %s (%i).",
1145 strerror(errno), errno);
1146 goto error_free_obj;
1147 }
1148
1149 if (env->error) {
1150 smlErrorSet(&error, SML_ERROR_GENERIC, "Unable to send disconnect request. Bailing out");
1151 goto error;
1152 }
1153
1154 /* Lets see if it was successfull. We wait a certain period of
1155 * time (3 sec) for the answer to the disconnect. If we dont receive
1156 * the answer we just disconnect anyways. Some phones never answer
1157 * with a A0 (success) to the disconnect request, so we must have
1158 * a timeout here */
1159 int maxLoop = 3;
1160 int i = 0;
1161 while (env->busy) {
1162 smlTrace(TRACE_INTERNAL, "%s: Disconnect loop %i", __func__, i);
1163 int result = OBEX_HandleInput(env->obexhandle, 1);
1164 /* timeout and success are not relevant */
1165 if (result < 0) {
1166 smlErrorSet(&error, SML_ERROR_GENERIC,
1167 "The disconnect request was not accepted (%i).",
1168 result);
1169 goto error;
1170 } else if (result == 0 && !env->busy && !env->isDisconnected) {
1171 smlErrorSet(&error, SML_ERROR_GENERIC,
1172 "The disconnect request was timed out.");
1173 goto error;
1174 }
1175
1176 if (i == maxLoop) {
1177 smlTrace(TRACE_INTERNAL, "%s: Did not receive a response to our disconnect", __func__);
1178 break;
1179 }
1180 i++;
1181 }
1182
1183 /* Note: The disconnect is signaled by OBEX_EV_LINKERR or OBEX_CMD_DISCONNECT.
1184 * We only signal disconnect here if a phone does not answer. */
1185 if (!env->isDisconnected)
1186 {
1187 env->busy = FALSE;
1188 env->isDisconnected = TRUE;
1189 OBEX_TransportDisconnect(env->obexhandle);
1190 smlTransportReceiveEvent(env->tsp, NULL, SML_TRANSPORT_EVENT_DISCONNECT_DONE, NULL, NULL);
1191 }
1192
1193 smlTrace(TRACE_EXIT, "%s", __func__);
1194 return;
1195
1196 error_free_obj:
1197 OBEX_ObjectDelete(env->obexhandle, obj);
1198 error:
1199 smlTransportReceiveEvent(env->tsp, NULL, SML_TRANSPORT_EVENT_ERROR, NULL, error);
1200 smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(&error));
1201 smlErrorDeref(&error);
1202 }
1203
1204 static void smlTransportObexClientSend(void *userdata, void *link_, SmlTransportData *data, SmlError *error)
1205 {
1206 smlTrace(TRACE_ENTRY, "%s(%p, %p, %p, %p)", __func__, userdata, link_, data, error);
1207 smlAssert(error || data);
1208 smlAssert(userdata);
1209 SmlTransportObexClientEnv *env = userdata;
1210 smlAssert(env);
1211
1212 if (error)
1213 goto error;
1214
1215 env->error = FALSE;
1216 env->mimetype = data->type;
1217
1218 /* Make a new put command */
1219 obex_object_t *obj = OBEX_ObjectNew(env->obexhandle, OBEX_CMD_PUT);
1220 if (!obj) {
1221 smlErrorSet(&error, SML_ERROR_GENERIC, "Unable to create new put object");
1222 goto error;
1223 }
1224
1225 /* Now add the header for the put target */
1226 obex_headerdata_t header;
1227
1228 smlTrace(TRACE_INTERNAL, "%s: Adding connection id %i", __func__, env->connection_id);
1229
1230 /* Add the connection id */
1231 header.bq4 = env->connection_id;
1232 OBEX_ObjectAddHeader(env->obexhandle, obj, OBEX_HDR_CONNECTION, header, sizeof(env->connection_id), OBEX_FL_FIT_ONE_PACKET);
1233
1234 const char *target = NULL;
1235 switch (data->type) {
1236 case SML_MIMETYPE_WBXML:
1237 target = SML_ELEMENT_WBXML;
1238 break;
1239 case SML_MIMETYPE_XML:
1240 target = SML_ELEMENT_XML;
1241 break;
1242 case SML_MIMETYPE_SAN:
1243 target = SML_ELEMENT_SAN;
1244 break;
1245 default:
1246 smlErrorSet(&error, SML_ERROR_GENERIC, "Unknown mime type");
1247 goto error_free_obj;
1248 }
1249 smlTrace(TRACE_INTERNAL, "%s: Target %s", __func__, VA_STRING(target));
1250
1251 /* Now convert to unicode. It requires 2 bytes per char + end*/
1252 /*unsigned char *unicode = g_malloc0(2 * strlen(target) + 2);
1253
1254 unsigned int unicodesize = OBEX_CharToUnicode(unicode, (const unsigned char *)target, 2 * strlen(target) + 2);
1255 if (unicodesize == -1) {
1256 smlErrorSet(&error, SML_ERROR_GENERIC, "unable to convert to unicode");
1257 goto error_free_obj;
1258 }*/
1259
1260 /* Now add the target mime type */
1261 /*header.bs = (unsigned char *)unicode;
1262 OBEX_ObjectAddHeader(env->obexhandle, obj, OBEX_HDR_TYPE, header, unicodesize, 0);*/
1263 header.bs = (unsigned char *)target;
1264 OBEX_ObjectAddHeader(env->obexhandle, obj, OBEX_HDR_TYPE, header, strlen(target) + 1, 0);
1265
1266 /* Now the data and size */
1267 header.bq4 = (uint32_t)data->size;
1268 OBEX_ObjectAddHeader(env->obexhandle, obj, OBEX_HDR_LENGTH, header, sizeof(uint32_t), 0);
1269
1270 header.bs = (unsigned char *)data->data;
1271 OBEX_ObjectAddHeader(env->obexhandle, obj, OBEX_HDR_BODY, header, data->size, 0);
1272
1273 env->busy = TRUE;
1274 /* Now we need to send the request */
1275 if (GET_OBEX_RESULT(OBEX_Request(env->obexhandle, obj)) < 0) {
1276 smlErrorSet(&error, SML_ERROR_GENERIC,
1277 "The OBEX request cannot be sent. %s (%i).",
1278 strerror(errno), errno);
1279 goto error_free_obj;
1280 }
1281
1282 /* Lets see if it was successfull */
1283 while (env->busy) {
1284 int result = OBEX_HandleInput(env->obexhandle, 20);
1285 if (result < 0) {
1286 smlErrorSet(&error, SML_ERROR_GENERIC,
1287 "The request was not successful (%d).",
1288 result);
1289 goto error;
1290 } else if (result == 0 && !env->busy) {
1291 smlErrorSet(&error, SML_ERROR_GENERIC,
1292 "The request was timed out.");
1293 goto error;
1294 }
1295 }
1296
1297 if (env->error) {
1298 smlErrorSet(&error, SML_ERROR_GENERIC, "Unable to send put request. Bailing out");
1299 goto error;
1300 }
1301
1302 smlTrace(TRACE_INTERNAL, "%s: Done sending the put request", __func__);
1303
1304 if (!data->needsAnswer) {
1305 smlTrace(TRACE_EXIT, "%s: No answer is needed", __func__);
1306 return;
1307 }
1308
1309 /* Make a new get command */
1310 obj = OBEX_ObjectNew(env->obexhandle, OBEX_CMD_GET);
1311 if (!obj) {
1312 smlErrorSet(&error, SML_ERROR_GENERIC, "Unable to create new get object");
1313 goto error;
1314 }
1315
1316 /* Now add the header for the get target */
1317 smlTrace(TRACE_INTERNAL, "%s: Adding connection id %i", __func__, env->connection_id);
1318
1319 /* Add the connection id */
1320 header.bq4 = env->connection_id;
1321 OBEX_ObjectAddHeader(env->obexhandle, obj, OBEX_HDR_CONNECTION, header, sizeof(env->connection_id), OBEX_FL_FIT_ONE_PACKET);
1322
1323 /* If smlTransportData.get_type is set, switch the mimetype for the GET command.
1324 This is needed for SyncML 1.2 SAN notificion:
1325
1326 If this obex send sequence sends SyncML 1.2 SAN notification, switch the
1327 mimetype back to regular mimetype (not the SAN mimetype). The mimtype for the
1328 regular sync got stored in smlTransportData.type_get */
1329
1330 if (data->type_get != SML_MIMETYPE_UNKNOWN)
1331 {
1332 switch (data->type_get) {
1333 case SML_MIMETYPE_WBXML:
1334 target = SML_ELEMENT_WBXML;
1335 break;
1336 case SML_MIMETYPE_XML:
1337 target = SML_ELEMENT_XML;
1338 break;
1339 case SML_MIMETYPE_SAN:
1340 target = SML_ELEMENT_SAN;
1341 break;
1342 default:
1343 smlErrorSet(&error, SML_ERROR_GENERIC, "Unknown mime type");
1344 goto error_free_obj;
1345 }
1346
1347 /* After this point, the entire conversion will be XML or WBXML. No more SAN mimetype. */
1348 env->mimetype = data->type_get;
1349
1350 smlTrace(TRACE_INTERNAL, "%s: Switch to new target: %s", __func__, VA_STRING(target));
1351 }
1352
1353 /* Now add the target mime type */
1354 //header.bs = (unsigned char *)unicode;
1355 //OBEX_ObjectAddHeader(env->obexhandle, obj, OBEX_HDR_TYPE, header, unicodesize, 0);
1356
1357 header.bs = (unsigned char *)target;
1358 OBEX_ObjectAddHeader(env->obexhandle, obj, OBEX_HDR_TYPE, header, strlen(target) + 1, 0);
1359
1360 env->busy = TRUE;
1361 /* Now we need to send the request */
1362 if (GET_OBEX_RESULT(OBEX_Request(env->obexhandle, obj)) < 0) {
1363 smlErrorSet(&error, SML_ERROR_GENERIC,
1364 "The OBEX request cannot be sent. %s (%i).",
1365 strerror(errno), errno);
1366 goto error_free_obj;
1367 }
1368
1369 /* Lets see if it was successfull */
1370 smlTrace(TRACE_INTERNAL, "%s: Wait for the answer ...", __func__);
1371 while (env->busy) {
1372 int result = OBEX_HandleInput(env->obexhandle, 20);
1373 if (result < 0) {
1374 smlErrorSet(&error, SML_ERROR_GENERIC,
1375 "The answer was not received successfully (%i).",
1376 result);
1377 goto error;
1378 } else if (result == 0 && !env->busy) {
1379 smlErrorSet(&error, SML_ERROR_GENERIC,
1380 "The answer was timed out.");
1381 goto error;
1382 }
1383 }
1384
1385 smlTrace(TRACE_EXIT, "%s", __func__);
1386 return;
1387
1388 error_free_obj:
1389 OBEX_ObjectDelete(env->obexhandle, obj);
1390 error:
1391 smlTransportReceiveEvent(env->tsp, NULL, SML_TRANSPORT_EVENT_ERROR, NULL, error);
1392 smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(&error));
1393 return;
1394 }
1395
1396 SmlBool smlTransportObexClientNew(SmlTransport *tsp, SmlError **error)
1397 {
1398 CHECK_ERROR_REF
1399 smlAssert(tsp);
1400
1401 tsp->functions.set_config_option = smlTransportObexClientSetConfigOption;
1402 tsp->functions.set_connection_type = smlTransportObexClientSetConnectionType;
1403 tsp->functions.initialize = smlTransportObexClientInit;
1404 tsp->functions.finalize = smlTransportObexClientFinalize;
1405 tsp->functions.connect = smlTransportObexClientConnect;
1406 tsp->functions.disconnect = smlTransportObexClientDisconnect;
1407 tsp->functions.send = smlTransportObexClientSend;
1408
1409 SmlTransportObexClientEnv *env = smlTryMalloc0(sizeof(SmlTransportObexClientEnv), error);
1410 if (!env)
1411 return FALSE;
1412 tsp->transport_data = env;
1413 env->tsp = tsp;
1414
1415 return TRUE;
1416 }
1417