/* MultiSync IrMC Plugin - Synchronize IrMC (mobile) devices Copyright (C) 2002-2003 Bo Lincoln This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS SOFTWARE IS DISCLAIMED. */ /* * $Id: irmc_obex.c,v 1.30 2004/02/09 18:53:31 lincoln Exp $ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if HAVE_IRDA #include #include #endif #include "irmc_obex.h" #include "irmc_sync.h" #include "irmc_bluetooth.h" #include "irmc_irda.h" #if HAVE_BT_OBEX #include #endif #include #include gpointer (*plugin_function)(); #define CALL_PLUGIN(mod, name, args) (g_module_symbol(mod,name,(gpointer*)&plugin_function)?(*plugin_function)args:NULL) GModule *bluetoothplugin = NULL; extern gboolean multisync_debug; void obex_event(obex_t *handle, obex_object_t *object, gint mode, gint event, gint obex_cmd, gint obex_rsp) { obexdata_t *userdata; userdata = (obexdata_t*) OBEX_GetUserData(handle); switch (event) { case OBEX_EV_PROGRESS: break; case OBEX_EV_REQDONE: if(mode == OBEX_CLIENT) { client_done(handle, object, obex_cmd, obex_rsp); } else { server_done(handle, object, obex_cmd, obex_rsp); } break; case OBEX_EV_REQHINT: /* Comes BEFORE the lib parses anything. */ switch(obex_cmd) { case OBEX_CMD_PUT: case OBEX_CMD_CONNECT: case OBEX_CMD_DISCONNECT: OBEX_ObjectSetRsp(object, OBEX_RSP_CONTINUE, OBEX_RSP_SUCCESS); break; default: OBEX_ObjectSetRsp(object, OBEX_RSP_NOT_IMPLEMENTED, OBEX_RSP_NOT_IMPLEMENTED); break; } break; case OBEX_EV_REQ: /* Comes when a server-request has been received. */ //server_request(object, event, obex_cmd); break; case OBEX_EV_LINKERR: case OBEX_EV_PARSEERR: case OBEX_EV_ABORT: userdata->state = IRMC_OBEX_REQFAILED; userdata->error = SYNC_MSG_CONNECTIONERROR; break; default: g_print("Unknown event!\n"); break; } } void server_done(obex_t *handle, obex_object_t *object, gint obex_cmd, gint obex_rsp) { obexdata_t *ud; ud = OBEX_GetUserData(handle); ud->state = IRMC_OBEX_REQDONE; } void client_done(obex_t *handle, obex_object_t *object, gint obex_cmd, gint obex_rsp) { obexdata_t *ud; ud = OBEX_GetUserData(handle); ud->state = IRMC_OBEX_REQDONE; switch(obex_cmd) { case OBEX_CMD_CONNECT: break; case OBEX_CMD_DISCONNECT: break; case OBEX_CMD_PUT: put_client_done(handle, object, obex_rsp); break; case OBEX_CMD_GET: get_client_done(handle, object, obex_rsp); break; case OBEX_CMD_SETPATH: break; } } int obex_error_to_sync_msg(int obexerr) { switch(obexerr) { case OBEX_RSP_NO_CONTENT: return(SYNC_MSG_DONTEXISTERROR); break; case OBEX_RSP_BAD_REQUEST: case OBEX_RSP_NOT_IMPLEMENTED: return(SYNC_MSG_PROTOCOLERROR); break; case OBEX_RSP_UNAUTHORIZED: case OBEX_RSP_PAYMENT_REQUIRED: case OBEX_RSP_FORBIDDEN: case OBEX_RSP_METHOD_NOT_ALLOWED: return(SYNC_MSG_NORIGHTSERROR); break; case OBEX_RSP_INTERNAL_SERVER_ERROR: case OBEX_RSP_CONFLICT: case OBEX_RSP_DATABASE_LOCKED: return(SYNC_MSG_CONNECTIONERROR); // Means: Try again later break; case OBEX_RSP_DATABASE_FULL: return(SYNC_MSG_DATABASEFULLERROR); break; default: return(SYNC_MSG_REQFAILED); } return(SYNC_MSG_REQFAILED); } void put_client_done(obex_t *handle, obex_object_t *object, gint obex_rsp) { obexdata_t *ud; obex_headerdata_t hv; guint8 hi; gint hlen; const char *body = NULL; int body_len = 0; ud = OBEX_GetUserData(handle); if(obex_rsp != OBEX_RSP_SUCCESS) { ud->state = IRMC_OBEX_REQFAILED; ud->error = obex_error_to_sync_msg(obex_rsp); return; } while(OBEX_ObjectGetNextHeader(handle, object, &hi, &hv, &hlen)) { if (hi == OBEX_HDR_APPARAM) { body = (char*) hv.bs; body_len = (int) hlen; } } if (body && ud->databuf && ud->databuflen && *(ud->databuflen) >= body_len) { // Copy received data to buffer memcpy(ud->databuf, body, body_len); *(ud->databuflen) = body_len; } if (!body) *(ud->databuflen) = 0; } void get_client_done(obex_t *handle, obex_object_t *object, gint obex_rsp) { obex_headerdata_t hv; guint8 hi; gint hlen; obexdata_t *ud; const char *body = NULL; int body_len = 0; ud = OBEX_GetUserData(handle); if(obex_rsp != OBEX_RSP_SUCCESS) { ud->state = IRMC_OBEX_REQFAILED; ud->error = obex_error_to_sync_msg(obex_rsp); return; } while(OBEX_ObjectGetNextHeader(handle, object, &hi, &hv, &hlen)) { if(hi == OBEX_HDR_BODY) { body = (char*) hv.bs; body_len = (int) hlen; break; } } if(!body) { ud->state = IRMC_OBEX_REQFAILED; ud->error = SYNC_MSG_PROTOCOLERROR; return; } if (ud->databuf && ud->databuflen && *(ud->databuflen) >= body_len) { // Copy received data to buffer memcpy(ud->databuf, body, body_len); *(ud->databuflen) = body_len; } else ud->state = IRMC_OBEX_REQFAILED; } // // Send an AT-command and return back one line of answer if any. // Code stolen from open-obex-apps-0.98 // gint obex_cable_at(obexdata_t *userdata, gchar *cmd, gchar *rspbuf, gint rspbuflen, gint timeout) { fd_set ttyset; struct timeval tv; gint fd; char *answer = NULL; char *answer_end = NULL; unsigned int answer_size; char tmpbuf[100] = {0,}; int actual; int total = 0; int done = 0; fd = userdata->fd; rspbuf[0] = 0; if(fd < 0) return -1; if(cmd != NULL) { // Write command gint cmdlen; cmdlen = strlen(cmd); if(write(fd, cmd, cmdlen) < cmdlen) { perror("Error writing to port"); return -1; } } while(!done) { FD_ZERO(&ttyset); FD_SET(fd, &ttyset); tv.tv_sec = timeout; tv.tv_usec = 0; if(select(fd+1, &ttyset, NULL, NULL, &tv)) { actual = read(fd, &tmpbuf[total], sizeof(tmpbuf) - total); if(actual < 0) return actual; total += actual; /* Answer didn't come within the length of the buffer. Cancel! */ if(total == sizeof(tmpbuf)) return -1; if( (answer = index(tmpbuf, '\n')) ) { // Remove first line (echo) if( (answer_end = index(answer+1, '\n')) ) { // Found end of answer done = 1; } } } else { /* Anser didn't come in time. Cancel */ return -1; } } // printf("Answer: %s\n", answer); // Remove heading and trailing \r if((*answer_end == '\r') || (*answer_end == '\n')) answer_end--; if((*answer_end == '\r') || (*answer_end == '\n')) answer_end--; if((*answer == '\r') || (*answer == '\n')) answer++; if((*answer == '\r') || (*answer == '\n')) answer++; answer_size = (answer_end) - answer +1; // printf("Answer size=%d\n", answer_size); if( (answer_size) >= rspbuflen ) return -1; strncpy(rspbuf, answer, answer_size); rspbuf[answer_size] = 0; return 0; } gint obex_cable_connect(obex_t *handle, gpointer ud) { obexdata_t *userdata; struct termios newtio; char rspbuf[200]; userdata = (obexdata_t*) ud; userdata->fd = open(userdata->cabledev, O_RDWR|O_NONBLOCK|O_NOCTTY); if (userdata->fd < 0) { return(SYNC_MSG_CONNECTIONERROR); } tcgetattr(userdata->fd, &userdata->oldtio); bzero(&newtio, sizeof(struct termios)); newtio.c_cflag = B115200 | CLOCAL | CS8 | CREAD | CRTSCTS; newtio.c_cc[VMIN] = 1; newtio.c_cc[VTIME] = 0; newtio.c_iflag = IGNPAR; newtio.c_oflag = 0; tcflush(userdata->fd, TCIFLUSH); tcsetattr(userdata->fd, TCSANOW, &newtio); if (userdata->cabletype == IRMC_CABLE_SIEMENS) return(cobex_connect(handle, ud)); if (userdata->cabletype != IRMC_CABLE_ERICSSON) goto err; /*if(obex_cable_at(userdata, "ATZ\r", rspbuf, sizeof(rspbuf), 1) < 0) { dd(printf("Comm-error sending ATZ\n")); } if(strcasecmp("OK", rspbuf) != 0) { dd(printf("Error doing ATZ (%s)\n", rspbuf)); goto err; } if(obex_cable_at(userdata, "AT+GMI\r", rspbuf, sizeof(rspbuf), 1) < 0) { dd(printf("Comm-error sending AT+GMI\n")); } if(!strcasecmp("ERICSSON", rspbuf) != 0) { dd(printf("Detected Ericsson.\n")); userdata->cabletype = IRMC_CABLE_ERICSSON; // Remove extra "OK" on e.g. Ericsson 520 obex_cable_at(userdata, NULL, rspbuf, sizeof(rspbuf), 1); } if(!strcasecmp("SONYERICSSON", rspbuf) != 0) { dd(printf("Detected SonyEricsson.\n")); userdata->cabletype = IRMC_CABLE_ERICSSON; } if(!strcasecmp("SIEMENS", rspbuf) != 0) { dd(printf("Detected Siemens.\n")); userdata->cabletype = IRMC_CABLE_SIEMENS; close(userdata->fd); return(cobex_connect(handle, ud)); } if (userdata->cabletype == IRMC_CABLE_UNKNOWN) goto err; */ // Set up Ericsson phone in OBEX mode. if(obex_cable_at(userdata, "ATZ\r", rspbuf, sizeof(rspbuf), 1) < 0) { dd(printf("Comm-error sending ATZ\n")); } if(strcasecmp("OK", rspbuf) != 0) { dd(printf("Error doing ATZ (%s)\n", rspbuf)); goto err; } if(obex_cable_at(userdata, "AT*EOBEX\r", rspbuf, sizeof(rspbuf), 1) < 0) { dd(printf("Comm-error sending AT*EOBEX\n")); goto err; } if(strcasecmp("CONNECT", rspbuf) != 0) { dd(printf("Error doing AT*EOBEX (%s)\n", rspbuf)); goto err; } fcntl(userdata->fd, F_SETFL, O_NONBLOCK); return(0); err: obex_cable_disconnect(handle, ud); return (SYNC_MSG_CONNECTIONERROR); } gint obex_cable_disconnect(obex_t *handle, gpointer ud) { obexdata_t *userdata; userdata = (obexdata_t*) ud; if (userdata->fd >= 0) { // Send a break to get out of OBEX-mode if(ioctl(userdata->fd, TCSBRKP, 0) < 0) { dd(printf("Unable to send break!\n")); } tcsetattr(userdata->fd, TCSANOW, &userdata->oldtio); close(userdata->fd); } return(0); } gint obex_cable_listen(obex_t *handle, gpointer ud) { obexdata_t *userdata; userdata = (obexdata_t*) ud; return(0); } gint obex_cable_write(obex_t *handle, gpointer ud, guint8 *buf, gint buflen) { int written = 0; int ret = 0; obexdata_t *userdata; userdata = (obexdata_t*) ud; while (ret >= 0 && written < buflen) { ret=write(userdata->fd,buf+written,buflen-written); if (ret >= 0) written+=ret; } return(written); } gint obex_cable_handleinput(obex_t *handle, gpointer ud, gint timeout) { int n; struct timeval to; fd_set readfds; char buf[2048]; int tot=0; obexdata_t *userdata; int ret = 0; userdata = (obexdata_t*) ud; n = userdata->fd + 1; FD_ZERO(&readfds); FD_SET(userdata->fd, &readfds); to.tv_sec = timeout; to.tv_usec = 0; while (userdata->state >= 0 && (ret=select(n, &readfds, NULL, NULL, &to)) > 0) { if((tot = read(userdata->fd, buf, 2048)) > 0) { OBEX_CustomDataFeed(handle, buf, tot); } else { userdata->state = IRMC_OBEX_REQFAILED; userdata->error = SYNC_MSG_CONNECTIONERROR; } to.tv_sec = timeout; to.tv_usec = 0; } if (userdata->state >= 0 && ret == 0) { userdata->state = IRMC_OBEX_REQFAILED; userdata->error = SYNC_MSG_CONNECTIONERROR; } return(0); } obex_t* irmc_obex_client(irmc_connection *conn) { obex_ctrans_t bttrans; obex_ctrans_t cabletrans = { obex_cable_connect, cobex_disconnect, NULL, cobex_write, cobex_handleinput, 0 }; #if HAVE_IRDA obex_ctrans_t irdatrans = { obex_irda_connect, obex_irda_disconnect, obex_cable_listen, obex_cable_write, obex_cable_handleinput, 0 }; #endif obexdata_t *userdata; obex_t *handle = NULL; if (bluetoothplugin) { g_module_symbol(bluetoothplugin,"obex_connect", (gpointer*) &bttrans.connect); g_module_symbol(bluetoothplugin,"obex_disconnect", (gpointer*) &bttrans.disconnect); g_module_symbol(bluetoothplugin,"obex_write",(gpointer*) &bttrans.write); g_module_symbol(bluetoothplugin,"obex_listen",(gpointer*) &bttrans.listen); g_module_symbol(bluetoothplugin,"obex_handleinput", (gpointer*) &bttrans.handleinput); } userdata = (obexdata_t*) g_malloc0(sizeof(obexdata_t)); #if OBEX_CUSTOMDATA bttrans.customdata = userdata; #if HAVE_IRDA irdatrans.customdata = userdata; #endif cabletrans.customdata = userdata; #endif #if OBEX_USERDATA bttrans.userdata = userdata; #if HAVE_IRDA irdatrans.userdata = userdata; #endif cabletrans.userdata = userdata; #endif memcpy(&userdata->btu, &conn->btunit, sizeof(struct bt_unit)); userdata->channel = conn->btchannel; strncpy(userdata->cabledev, conn->cabledev, 19); userdata->cabletype = conn->cabletype; memcpy(&userdata->irunit, &conn->irunit, sizeof(irmc_ir_unit)); #if HAVE_IRDA userdata->ir_addr = conn->ir_addr; // Temp absolute IR address #endif userdata->connectmedium = conn->connectmedium; userdata->state = IRMC_OBEX_OFFLINE; userdata->connected = 0; switch(userdata->connectmedium) { case MEDIUM_BLUETOOTH: #if HAVE_BT_OBEX if (!(handle = OBEX_Init(OBEX_TRANS_BLUETOOTH, obex_event, 0))) return(0); #else if (!(handle = OBEX_Init(OBEX_TRANS_CUST, obex_event, 0))) return(0); if (bluetoothplugin) OBEX_RegisterCTransport(handle, &bttrans); #endif break; case MEDIUM_IR: #if HAVE_IRDA if (!(handle = OBEX_Init(OBEX_TRANS_CUST, obex_event, 0))) return(0); OBEX_RegisterCTransport(handle, &irdatrans); #endif break; case MEDIUM_CABLE: if (!(handle = OBEX_Init(OBEX_TRANS_CUST, obex_event, 0))) return(0); OBEX_RegisterCTransport(handle, &cabletrans); break; } OBEX_SetUserData(handle, (gpointer) userdata); return(handle); } gint irmc_obex_connect(obex_t* handle, char* target) { int ret = -1; obex_object_t *object; obex_headerdata_t hd; obexdata_t *userdata; struct sockaddr_un addr; userdata = (obexdata_t*) OBEX_GetUserData(handle); userdata->connected = 0; switch(userdata->connectmedium) { case MEDIUM_BLUETOOTH: #if HAVE_BT_OBEX ret=BtOBEX_TransportConnect(handle, NULL, &userdata->btu.bdaddr, userdata->channel); #else ret=OBEX_TransportConnect (handle, (struct sockaddr*) &addr, 0); #endif break; case MEDIUM_IR: // ret = -1; ret=OBEX_TransportConnect (handle, (struct sockaddr*) &addr, 0); break; case MEDIUM_CABLE: ret=OBEX_TransportConnect (handle, (struct sockaddr*) &addr, 0); break; } if (ret < 0) return(SYNC_MSG_CONNECTIONERROR); userdata->connected = 1; if ((object = OBEX_ObjectNew(handle, OBEX_CMD_CONNECT))) { if (target) { hd.bs = target; OBEX_ObjectAddHeader(handle, object, OBEX_HDR_TARGET, hd, strlen(target), 0); } ret = OBEX_Request(handle, object); if (ret < 0) return(SYNC_MSG_CONNECTIONERROR); } userdata->state = IRMC_OBEX_CONNECTING; irmc_obex_handleinput(handle, 10); if (userdata->state == IRMC_OBEX_REQDONE) { if (strlen(userdata->irunit.serial) > 0) { #if HAVE_IRDA if (!userdata->ir_addr) { #endif char *serial = irmc_obex_get_serial(handle); if (!serial || strcmp(serial, userdata->irunit.serial)) { dd(printf("Device serial number is not correct.\n")); if (serial) g_free(serial); irmc_obex_disconnect(handle); return(SYNC_MSG_CONNECTIONERROR); } g_free(serial); #if HAVE_IRDA } #endif } // Connected and serial number is OK! return(0); } return(SYNC_MSG_CONNECTIONERROR); } gint irmc_obex_disconnect(obex_t* handle) { obex_object_t *object; obexdata_t *userdata; userdata = (obexdata_t*) OBEX_GetUserData(handle); if (userdata->connected) { if ((object = OBEX_ObjectNew(handle, OBEX_CMD_DISCONNECT))) { if(OBEX_Request(handle, object) < 0) return(SYNC_MSG_CONNECTIONERROR); } userdata->state = IRMC_OBEX_DISCONNECTING; irmc_obex_handleinput(handle, 10); OBEX_TransportDisconnect(handle); userdata->connected = 0; } return(0); } void irmc_obex_cleanup(obex_t* handle) { if (handle) { obexdata_t *userdata; userdata = (obexdata_t*) OBEX_GetUserData(handle); OBEX_Cleanup(handle); g_free(userdata); } } // Return the serial number of an already connected device. // The string must be freed with g_free() char* irmc_obex_get_serial(obex_t* handle) { char data[10240]; int len = 10240; char *sn; sn = g_malloc(128); len = 10240; if (irmc_obex_get(handle, "telecom/devinfo.txt", data, &len)>=0){ char *p = data; while (p && p < data+len) { if (sscanf(p, "SN:%127s", sn) >= 1) { return(sn); } else { if (sscanf(p, "SN;%*[^:]%s", sn) >= 1) { return(sn); } else { p = strstr(p, "\n"); if (p) p++; } } } } return(NULL); } gint irmc_obex_put(obex_t* handle, char* name, char *type, char *body, gint body_size, char *rspbuf, int *rspbuflen, char *apparam, int apparamlen) { obex_object_t *object; obex_headerdata_t hd; char unicodename[1024]; obexdata_t *userdata; int namesize = 0; userdata = (obexdata_t*) OBEX_GetUserData(handle); if ((object = OBEX_ObjectNew(handle, OBEX_CMD_PUT))) { hd.bq4 = body_size; OBEX_ObjectAddHeader(handle, object, OBEX_HDR_LENGTH, hd, 4, 0); /* Add unicode name header*/ namesize = OBEX_CharToUnicode(unicodename, name, 1024); hd.bs = unicodename; OBEX_ObjectAddHeader(handle, object, OBEX_HDR_NAME, hd, namesize, 0); if (type) { hd.bs = type; OBEX_ObjectAddHeader(handle, object, OBEX_HDR_TYPE, hd, strlen(type), 0); } if (apparam) { hd.bs = apparam; OBEX_ObjectAddHeader(handle, object, OBEX_HDR_APPARAM, hd, apparamlen, 0); } if (body) { /* Add body header*/ hd.bs = body; OBEX_ObjectAddHeader(handle, object, OBEX_HDR_BODY, hd, body_size, 0); } if(OBEX_Request(handle, object) < 0) { return(SYNC_MSG_CONNECTIONERROR); } userdata->state = IRMC_OBEX_PUTTING; userdata->databuf = rspbuf; userdata->databuflen = rspbuflen; irmc_obex_handleinput(handle, 30); if (userdata->state == IRMC_OBEX_REQDONE) return(0); return(userdata->error); } return(SYNC_MSG_REQFAILED); } int irmc_obex_get(obex_t *handle, char* name, char* buffer, int *buflen) { obex_object_t *object; obex_headerdata_t hd; char unicodename[1024]; obexdata_t *userdata; int namesize = 0; userdata = (obexdata_t*) OBEX_GetUserData(handle); if((object = OBEX_ObjectNew(handle, OBEX_CMD_GET))) { /* Add unicode name header*/ namesize = OBEX_CharToUnicode(unicodename, name, 1024); hd.bs = unicodename; OBEX_ObjectAddHeader(handle, object, OBEX_HDR_NAME, hd, namesize, 0); userdata->databuf = buffer; userdata->databuflen = buflen; OBEX_Request(handle, object); userdata->state = IRMC_OBEX_GETTING; irmc_obex_handleinput(handle, 30); if (userdata->state == IRMC_OBEX_REQDONE) { return(0); } return(userdata->error); } return(SYNC_MSG_REQFAILED); } gint irmc_obex_handleinput(obex_t* handle, int timeout) { return(OBEX_HandleInput(handle, timeout)); } void irmc_obex_init() { char *path; path = g_module_build_path(PLUGINDIR, "irmc_bluetooth"); bluetoothplugin = g_module_open(path,0); if (bluetoothplugin) CALL_PLUGIN(bluetoothplugin, "plugin_init", ()); g_free(path); }