libsyncml  0.5.4
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 
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;
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);
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 
1410  if (!env)
1411  return FALSE;
1412  tsp->transport_data = env;
1413  env->tsp = tsp;
1414 
1415  return TRUE;
1416 }
1417 
const char * smlErrorPrint(SmlError **error)
Returns the message of the error.
Definition: sml_error.c:299
void smlTrace(SmlTraceType type, const char *message,...)
Used for tracing the application.
Definition: sml_support.c:120
void * smlTryMalloc0(long n_bytes, SmlError **error)
Safely mallocs.
Definition: sml_support.c:335
void smlErrorSet(SmlError **error, SmlErrorType type, const char *format,...)
Sets the error.
Definition: sml_error.c:355
Represent an error.