/* MultiSync SyncML plugin - Implementation of SyncML 1.1 and 1.0 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; In addition, as a special exception, Bo Lincoln gives permission to link the code of this program with the OpenSSL library (or with modified versions of OpenSSL that use the same license as OpenSSL), and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. 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: syncml_cmd.c,v 1.38 2004/04/03 17:08:02 lincoln Exp $ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "config.h" #include "syncml_cmd.h" #include "syncml_engine.h" #include "syncml_plugin.h" #include "syncml_ssl.h" #if USE_LIBWBXML #include #endif extern gboolean multisync_debug; // Like read() but with a timeout of timeout seconds int syncml_conn_read(int fd, char *data, int len, int timeout) { struct timeval tv; fd_set rset, wset, xset; int lenleft = len; if (fd < 0) return(0); while (lenleft > 0) { FD_ZERO(&rset); FD_ZERO(&wset); FD_ZERO(&xset); FD_SET(fd, &rset); tv.tv_sec = timeout; tv.tv_usec = 0; if (select(fd+1, &rset, &wset, &xset, &tv)) { int ret; ret = read(fd, data+len-lenleft, lenleft); if (ret > 0) { lenleft -= ret; } else { return(len-lenleft); } } else { return(-1); } } return(len); } // Like write() but with a timeout of timeout seconds int syncml_conn_write(int fd, char *data, int len, int timeout) { struct timeval tv; fd_set rset, wset, xset; int lenleft = len; if (fd < 0) return(0); while (lenleft > 0) { FD_ZERO(&rset); FD_ZERO(&wset); FD_ZERO(&xset); FD_SET(fd, &wset); tv.tv_sec = timeout; tv.tv_usec = 0; if (select(fd+1, &rset, &wset, &xset, &tv)) { int ret; ret = write(fd, data+len-lenleft, lenleft); if (ret > 0) { lenleft -= ret; } else { return(ret); } } else { return(-1); } } return(len); } // Receive one line, with timeout int syncml_conn_recv_line(syncml_state *state, char *line, int len) { int i = 0; if (len == 0) return(0); if (state->connfd >= 0) { int res = -1; i = -1; do { i++; if (state->conntype == SYNCML_CONN_TYPE_HTTP) res = syncml_conn_read(state->connfd, line+i, 1, SYNCML_CONN_TIMEOUT); if (state->conntype == SYNCML_CONN_TYPE_HTTPS) res = syncml_ssl_read(state, line+i, 1, SYNCML_CONN_TIMEOUT); } while (res == 1 && i < len && line[i] != '\n'); if (line[i] != '\n' || res <= 0) { // Timeout or close dd(printf("SyncML: Disconnection, length %d.\n", res)); syncml_conn_disconnect(state, res==-1?SYNCML_DISCONNECT_TIMEOUT: SYNCML_DISCONNECT_CLOSED); return(-1); } line[i] = 0; if (i > 0 && line[i-1] == '\r') line[--i] = 0; } else return(-1); return(i); } int syncml_conn_recv(syncml_state *state, char *data, int len) { if (len == 0) return(0); if (state->connfd >= 0) { int res = -1; if (state->conntype == SYNCML_CONN_TYPE_HTTP) res = syncml_conn_read(state->connfd, data, len, SYNCML_CONN_TIMEOUT); if (state->conntype == SYNCML_CONN_TYPE_HTTPS) res = syncml_ssl_read(state, data, len, SYNCML_CONN_TIMEOUT); if (res < len) { syncml_conn_disconnect(state, res==-1?SYNCML_DISCONNECT_TIMEOUT: SYNCML_DISCONNECT_CLOSED); return(-1); } } else return(-1); dd(printf("%s\n", data)); return(len); } int syncml_conn_send(syncml_state *state, char *data, int len) { int res = 0; if (len == 0) return(0); if (state->connfd >= 0) { int res = -1; if (state->conntype == SYNCML_CONN_TYPE_HTTP) res = syncml_conn_write(state->connfd, data, len, SYNCML_CONN_TIMEOUT); if (state->conntype == SYNCML_CONN_TYPE_HTTPS) res = syncml_ssl_write(state, data, len, SYNCML_CONN_TIMEOUT); if (res < len) { syncml_conn_disconnect(state, res==-1?SYNCML_DISCONNECT_TIMEOUT: SYNCML_DISCONNECT_CLOSED); return(-1); } } else return(-1); dd(printf("%s\n", data)); return(len); } // Receive all data on a stream until EOF. Return as g_malloc'ed block. int syncml_conn_recv_all(syncml_state *state, char **data) { int len = 0; *data = NULL; if (state->connfd >= 0) { int blocklen = 1024, offset=0, res; *data = g_malloc(blocklen); while((res = syncml_conn_read(state->connfd, *data+offset, blocklen-offset, SYNCML_CONN_TIMEOUT)) == blocklen-offset) { char *tmp = *data; offset = blocklen; *data = g_malloc(blocklen*2); memcpy(*data, tmp, blocklen); blocklen *= 2; g_free(tmp); } len = offset; if (res >= 0) len += res; syncml_conn_disconnect(state, SYNCML_DISCONNECT_DISCONNECT); } return(len); } void syncml_conn_disconnect(syncml_state *state, syncml_disconnect_reason reason) { if (state->conntype == SYNCML_CONN_TYPE_HTTPS) syncml_ssl_disconnect(state); if (state->connfd >= 0) close(state->connfd); state->connfd = -1; state->tcpreuseconnection = FALSE; if (reason != SYNCML_DISCONNECT_DISCONNECT) { dd(printf("SyncML: SyncML disconnected.\n")); syncml_disconnected(state, reason); } } char *syncml_http_rsp_to_string(syncml_http_code code) { switch(code) { case SYNCML_HTTP_CONTINUE: return("Continue"); case SYNCML_HTTP_OK: return("OK"); case SYNCML_HTTP_CREATED: return("Created"); case SYNCML_HTTP_ACCEPTED: return("Accepted"); case SYNCML_HTTP_NOTFOUND: return("Not found"); } return(NULL); } int syncml_http_send_rsp(syncml_state *state, char *data, int len, int code, char *contenttype) { char buf[1024]; char datestr[1024], *end; char *sendbuf; time_t currtime; // Send HTTP response if (state->connfd < 0) return -1; time(&currtime); ctime_r(&currtime, datestr); end = strstr(datestr, "\n"); if (end) end[0] = 0; snprintf(buf, 1023, "HTTP/1.1 %d %s\r\n" "Date: %s\r\n" "Expires: %s\r\n" "Content-Length: %d\r\n" "Content-Type: %s\r\n" "Accept-Charset: UTF-8\r\n" "Accept: application/vnd.syncml+xml, application/vnd.syncml+wbxml\r\n" "Cache-Control: no-store\r\n" "Server: MultiSync Plugin\r\n" "\r\n", code, syncml_http_rsp_to_string(code), datestr, datestr, len, contenttype); sendbuf = g_malloc(strlen(buf)+len); memcpy(sendbuf, buf, strlen(buf)); memcpy(sendbuf+strlen(buf), data, len); if (syncml_conn_send(state, sendbuf, len+strlen(buf)) == len+strlen(buf)) { g_free(sendbuf); return(0); } g_free(sendbuf); /*if (syncml_conn_send(state, buf, strlen(buf)) == strlen(buf)) { if (data) { if (syncml_conn_send(state, data, len) == len) return(0); } else return(0); }*/ return(-1); } int syncml_http_send_cont(syncml_state *state) { char buf[256]; // Send HTTP response if (state->connfd < 0) return -1; snprintf(buf, 255, "HTTP/1.1 100 Continue\r\n\r\n"); if (syncml_conn_send(state, buf, strlen(buf)) == strlen(buf)) { return(0); } return(-1); } int syncml_http_send_req(syncml_state *state, char *data, int len, char *cmd, char *contenttype) { char buf[1024]; char *file; char *hostname, *host; int port=80,t; // Send HTTP response if (state->connfd < 0) { if (!syncml_conn_connect(state)) return -1; } file = syncml_get_URI_file(state->otherURI); hostname = syncml_get_URI_host(state->otherURI); port = syncml_get_URI_port(state->otherURI); host = g_strdup_printf("%s:%d", hostname, port); g_free(hostname); snprintf(buf, 1023, "%s %s HTTP/1.1\r\n" "Content-Length: %d\r\n" "Content-Type: %s\r\n" "Accept: application/vnd.syncml+xml, application/vnd.syncml+wbxml\r\n" "Host: %s\r\n" "Accept-Charset: UTF-8\r\n" "Cache-Control: no-store\r\n" "\r\n", cmd, file, len, contenttype, host); g_free(file); g_free(host); /*printf("SyncML: Sending:\n"); for (t = 0; t < len; t++) { if (!(t & 0x3f)) printf("\n"); if (data[t] >= ' ' && data[t] <= 'z') printf("%c", data[t]); else printf(" %d ", data[t]); }*/ if (syncml_conn_send(state, buf, strlen(buf)) == strlen(buf)) { if (syncml_conn_send(state, data, len) == len) return(0); } return(-1); } // Possibly convert an outgoing message to WBXML. // WILL free the string in if converted, and return a new string char *syncml_xml_out_convert(syncml_state *state, char *in, int *outlen) { char *ret = NULL; #if USE_LIBWBXML if (in && state->wbxml) { WB_UTINY *wbxml = NULL, *xml; WB_LONG wbxml_len = 0; WBXMLError res = 0; WBXMLConvXML2WBXMLParams params = {WBXML_VERSION_11, TRUE, TRUE}; xml = in; if ((res = wbxml_conv_xml2wbxml(xml, &wbxml, &wbxml_len, ¶ms)) == WBXML_OK) { ret = g_malloc(wbxml_len); memcpy(ret, wbxml, wbxml_len); if (outlen) *outlen = wbxml_len; wbxml_free(wbxml); g_free(in); return(ret); } dd(printf("SyncML: WBXML convert error: %d\n", res)); } #endif if (outlen) { if (in) *outlen = strlen(in); else *outlen = 0; } return(in); } void syncml_print_binary(unsigned char *data, int len) { int t; for (t = 0; t < len; t++) { if (data[t] >= ' ' && data[t] <= 'z') dd(printf("%c ", data[t])); else dd(printf("%02x ", data[t])); } dd(printf("\n")); } int syncml_transport_msg_size(syncml_state *state, unsigned char* data, int len) { char *copy = g_strdup(data); int size = 0; copy = syncml_xml_out_convert(state, copy, &size); g_free(copy); return(size); } void syncml_http_recv(syncml_state *state) { // We have connection data int res = 0; char line[1024]; dd(printf("SyncML: We got some request data.\n")); if ((res = syncml_conn_recv_line(state, line, 1024)) >= 0) { char httpcmd[32] = ""; char file[256]; char httpver[32]; int rspcode = 0; gboolean httpreq=FALSE; gboolean httprsp=FALSE; // Parse HTTP line dd(printf("SyncML: Line: %s\n", line)); if (sscanf(line, "%31s %255s HTTP/%31s", httpcmd, file, httpver) == 3) { if (!strcmp(httpcmd, "POST")) { httpreq=TRUE; state->lastreq = TRUE; } else { char *rsp = "No such file or directory."; syncml_http_send_rsp(state, rsp, strlen(rsp), 404, "text/plain"); return; } } if (sscanf(line, "HTTP/%31s %d", httpver, &rspcode) == 2) { if (rspcode == SYNCML_HTTP_OK || rspcode == SYNCML_HTTP_ACCEPTED) { httprsp=TRUE; state->lastreq = FALSE; } } if (httpreq || httprsp) { int contentlength = -1; int res = 0; int expect = 0; char *data = NULL; char contenttype[1024] = "", transferencoding[256] = ""; char cookie[1024] = ""; gboolean headers = TRUE; // Get headers while (headers) { if (syncml_conn_recv_line(state, line, 1024) >= 0) { char key[256], value[1024]; dd(printf("SyncML: Line: %s\n", line)); if (strlen(line) > 0) { if (sscanf(line, "%255[^:]: %1023[^\n]", key, value) == 2) { if (!g_strcasecmp(key, "content-length")) sscanf(value, "%d", &contentlength); if (!g_strcasecmp(key, "content-type")) sscanf(value, "%1023[^;]", &contenttype); if (!g_strcasecmp(key, "expect")) sscanf(value, "%d", &expect); if (!g_strcasecmp(key, "transfer-encoding")) sscanf(value, "%255[^;]", &transferencoding); if (!g_strcasecmp(key, "cookie")) { if (sscanf(value, "%1023[^\n]", &cookie) >= 1) { char *pos = cookie; while (pos) { char name[256]="",value[256]; sscanf(pos, "%255[^=;]=%255[^;]", name, value); if (!strcmp(name, "syncml-session")) { if (!state->tcpreuseconnection && state->sessionidcookie && !strcmp(state->sessionidcookie, value)) // This is a continued session dd(printf("SyncML: Session cookie OK.\n")); state->tcpreuseconnection = TRUE; } pos = strstr(pos,";"); while (pos && *pos && (*pos == ' ' || *pos == ';')) pos++; } } } } } else headers = FALSE; } else headers = FALSE; } if (httpreq) { // Parse the HTTP variables in file URI char *pos = strstr(file, "?"); if (pos) pos++; while (pos) { char key[256], value[256]; if (sscanf(pos, "%255[^=&]=%255[^&]", key, value) == 2) { if (!strcmp(key, "sessionid") && state->sessionidcookie && !strcmp(value, state->sessionidcookie)) { dd(printf("SyncML: Session ID OK.\n")); state->tcpreuseconnection = TRUE; } } pos = strstr(file, "&"); if (pos) pos++; } } if (!state->tcpreuseconnection) syncml_reset_state(state); // This is a new, unknown session state->tcpreuseconnection = TRUE; if (expect == 100) syncml_http_send_cont(state); if (contentlength < 0) { char *tmp; if (!g_strcasecmp(transferencoding, "chunked")) { int len = 0, totlen = 0; dd(printf("SyncML: Reading chunked data.\n")); do { if ((res = syncml_conn_recv_line(state, line, 1024)) >= 0) { sscanf(line, "%x", &len); } else len = 0; if (len > 0) { dd(printf("SyncML: Reading %d bytes.\n", len)); tmp = g_malloc(totlen+len); if (totlen > 0) { memcpy(tmp, data, totlen); g_free(data); } data = tmp; res = syncml_conn_recv(state, data+totlen, len); if (res == len) totlen += len; } } while (len > 0); contentlength = totlen; } else { res = contentlength = syncml_conn_recv_all(state, &tmp); data = g_malloc(contentlength+1); memcpy(data, tmp, contentlength); g_free(tmp); data[contentlength]=0; } } else { data = g_malloc(contentlength+1); res = syncml_conn_recv(state, data, contentlength); data[contentlength]=0; } if (res > 0) { char *syncmlrsp; #if USE_LIBWBXML if (!strcmp(contenttype, "application/vnd.syncml+wbxml")) { // We got WBXML data, time to convert WB_UTINY *in = data, *xml = NULL; WB_LONG insize = contentlength; WBXMLConvWBXML2XMLParams params = {WBXML_ENCODER_XML_GEN_INDENT, WBXML_LANG_UNKNOWN, 2, TRUE }; syncml_print_binary(in, insize); if (wbxml_conv_wbxml2xml(in, insize, &xml, ¶ms) == WBXML_OK) { strcpy(contenttype, "application/vnd.syncml+xml"); state->wbxml = TRUE; g_free(data); data = g_strdup(xml); wbxml_free(xml); contentlength = strlen(data); dd(printf("SyncML: Successfully converted from WBXML to XML. Data:\n%s\n", data)); } } #endif // Send data to SyncML engine if (!strcmp(contenttype, "application/vnd.syncml+xml")) { syncml_parse_msg(state, data, contentlength); syncmlrsp = syncml_action(state); if (syncmlrsp) { int len = 0; // Immediate response syncmlrsp = syncml_xml_out_convert(state, syncmlrsp, &len); if (httpreq) syncml_http_send_rsp(state, syncmlrsp, len, SYNCML_HTTP_OK, state->wbxml?"application/vnd.syncml+wbxml":"application/vnd.syncml+xml"); else syncml_http_send_req(state, syncmlrsp, len, "POST", state->wbxml?"application/vnd.syncml+wbxml":"application/vnd.syncml+xml"); g_free(syncmlrsp); } } else { dd(printf("SyncML: Got data of type %s, which I cannot parse.\n", contenttype)); if (httpreq) { char *rsp = "No such file or directory."; syncml_http_send_rsp(state, rsp, strlen(rsp), SYNCML_HTTP_NOTFOUND, "text/plain"); } } } if (data) g_free(data); } } } gboolean syncml_conn_connect(syncml_state *state) { char *hostname = NULL; int hostport = 80; if (state->connfd >= 0) return(TRUE); hostname = syncml_get_URI_host(state->otherURI); if (hostname && !strcmp(hostname, "")) { g_free(hostname); hostname = g_strdup("localhost"); } if (!hostname || state->isserver) { syncml_conn_disconnect(state, SYNCML_DISCONNECT_CONNECTIONFAILED); return(FALSE); } hostport = syncml_get_URI_port(state->otherURI); if ((state->connfd = socket(PF_INET, SOCK_STREAM, 0)) >= 0) { struct hostent *hostent; struct sockaddr_in servaddr; // Look up name IP dd(printf("SyncML: Looking up %s\n", hostname)); if ((hostent = gethostbyname(hostname))) { unsigned char *a; servaddr.sin_family = AF_INET; servaddr.sin_port = htons(hostport); servaddr.sin_addr.s_addr = (*((unsigned int*)hostent->h_addr_list[0])); a = (unsigned char*) &servaddr.sin_addr.s_addr; dd(printf("SyncML: Connecting to %d.%d.%d.%d...\n", a[0],a[1],a[2],a[3])); if (!connect(state->connfd, (struct sockaddr*) &servaddr, sizeof(struct sockaddr_in))) { char *txt; fcntl(state->connfd, F_SETFL, O_NONBLOCK); if (state->conntype == SYNCML_CONN_TYPE_HTTPS) syncml_ssl_client_connect(state); // Prepare for SSL connection txt = g_strdup_printf("Connected to %s.", hostname); syncml_info(state, state->userdata, txt); g_free(txt); return(TRUE); } } close(state->connfd); } state->connfd = -1; syncml_conn_disconnect(state, SYNCML_DISCONNECT_CONNECTIONFAILED); return(FALSE); } gpointer syncml_main_thread(gpointer statep) { syncml_state *state = statep; gboolean quit = FALSE; while (!quit) { int n = 0; struct timeval tv; time_t timeleft = 0; fd_set rset, wset, xset; if (state->socketfd > n) n = state->socketfd; if (state->readmsg > n) n = state->readmsg; if (state->connfd > n) n = state->connfd; FD_ZERO(&rset); FD_ZERO(&wset); FD_ZERO(&xset); if (state->socketfd >= 0) FD_SET(state->socketfd, &rset); FD_SET(state->readmsg, &rset); if (state->connfd >= 0) { FD_SET(state->connfd, &rset); FD_SET(state->connfd, &xset); } if (state->unconnectedtimeout && state->connfd < 0) { timeleft = state->unconnectedtimeout-time(NULL); if (timeleft <= 0) timeleft = 1; tv.tv_sec = timeleft; tv.tv_usec = 0; } if (state->connectedtimeout && state->connfd >= 0) { timeleft = state->connectedtimeout-time(NULL); if (timeleft <= 0) timeleft = 1; tv.tv_sec = timeleft; tv.tv_usec = 0; } if (select(n+1, &rset, &wset, &xset, timeleft?&tv:NULL)) { if (state->connfd >= 0 && FD_ISSET(state->connfd, &xset)) { // Our connection is closed! dd(printf("SyncML: The other end closed the connection.\n")); syncml_conn_disconnect(state, SYNCML_DISCONNECT_CLOSED); } if (state->socketfd >= 0 && FD_ISSET(state->socketfd, &rset)) { struct sockaddr_in otheraddr; socklen_t addrlen = sizeof(struct sockaddr_in); unsigned char *a; char *txt; state->connfd = accept(state->socketfd, (struct sockaddr*) &otheraddr, &addrlen); fcntl(state->connfd, F_SETFL, O_NONBLOCK); state->tcpreuseconnection = FALSE; a = (unsigned char*) &otheraddr.sin_addr.s_addr; dd(printf("SyncML: Client connected from %d.%d.%d.%d.\n", a[0],a[1],a[2],a[3])); txt = g_strdup_printf("Client connected from %d.%d.%d.%d.", a[0],a[1],a[2],a[3]); syncml_info(state, state->userdata, txt); g_free(txt); if (state->conntype == SYNCML_CONN_TYPE_HTTPS) syncml_ssl_server_connect(state); // Prepare for SSL connection if (!state->connectedtimeout) { // Close if no activity state->connectedtimeout = time(NULL)+SYNCML_CONN_TIMEOUT; } } if (state->connfd >= 0 && FD_ISSET(state->connfd, &rset)) { syncml_http_recv(state); } if (FD_ISSET(state->readmsg, &rset)) { syncml_engine_cmd cmd; if (read(state->readmsg, &cmd, sizeof(syncml_engine_cmd)) == sizeof(syncml_engine_cmd)) { dd(printf("SyncML: Got engine cmd: %d\n", cmd.cmd)); switch (cmd.cmd) { case SYNCML_ENGINE_CMD_QUIT: if (state->connfd >=0) close(state->connfd); if (state->socketfd >=0) close(state->socketfd); close(state->readmsg); syncml_ssl_exit(state); syncml_free_state(state); return(NULL); break; default: syncml_do_cmd(state, &cmd); break; } } } } else { // Timeout syncml_conn_disconnect(state, SYNCML_DISCONNECT_TIMEOUT); state->connectedtimeout = 0; state->unconnectedtimeout = 0; } } { // Quit OK. if (state->connfd >= 0) { shutdown(state->connfd, SHUT_RDWR); close(state->connfd); } if (state->socketfd >= 0) { shutdown(state->socketfd, SHUT_RDWR); close(state->socketfd); } if (state->readmsg >= 0) close(state->readmsg); if (state->writemsg >= 0) close(state->writemsg); } return(NULL); } // Add a command to the SyncML engine command list and execute it // if possible. void syncml_do_cmd(syncml_state *state, syncml_engine_cmd *cmd) { syncml_engine_cmd *cmdcpy = g_malloc(sizeof(syncml_engine_cmd)); char *syncmlcmd; memcpy(cmdcpy, cmd, sizeof(syncml_engine_cmd)); state->engine_cmds = g_list_append(state->engine_cmds, cmdcpy); syncmlcmd = syncml_action(state); if (syncmlcmd) { int len = 0; // Immediate response syncmlcmd = syncml_xml_out_convert(state, syncmlcmd, &len); if (state->lastreq) syncml_http_send_rsp(state, syncmlcmd, len, SYNCML_HTTP_OK, state->wbxml?"application/vnd.syncml+wbxml":"application/vnd.syncml+xml"); else syncml_http_send_req(state, syncmlcmd, len, "POST", state->wbxml?"application/vnd.syncml+wbxml":"application/vnd.syncml+xml"); g_free(syncmlcmd); } } syncml_state* syncml_create(gboolean isserver, char* uri, char *statefilename, gpointer userdata) { syncml_state *state; int msgpipe[2]; syncml_db_pair *pair; syncml_error_type error = SYNCML_ERROR_UNKNOWN; state = g_malloc0(sizeof(syncml_state)); state->isserver = isserver; state->inited = FALSE; state->socketfd = -1; state->connfd = -1; state->syncmlversion = SYNCML_VER_11; state->wbxml = FALSE; state->usedauth = state->defaultauth = SYNCML_AUTH_MD5; state->userdata = userdata; state->statefilename = g_strdup(statefilename); syncml_reset_state(state); // Reset the sync engine state syncml_load_engine_state(state); if (!state->devID) { int t; char nrs[] = "0123456789ABCDEF"; // Create a "unique" ID for this device state->devID = g_malloc0(12+1); for (t = 0; t < 12; t++) { long r = random(); state->devID[t] = nrs[r&0xf]; } } state->sessid = 1; state->msgid = 1; state->cmdid = 1; if (!state->isserver) { state->authok = TRUE; // Server does not need to authenticate itself // This means we are vulnerable to man-in-the-middle attack. if (uri) state->otherURI = g_strdup(uri); state->myURI = g_strdup(state->devID); state->conntype = syncml_get_URI_proto(state->otherURI); if (state->conntype == SYNCML_CONN_TYPE_HTTPS) if (!syncml_ssl_init_client(state)) { goto err; } } else { int serverport = syncml_get_URI_port(uri); state->myURI = g_strdup(uri); state->socketfd = socket(PF_INET, SOCK_STREAM, 0); state->conntype = syncml_get_URI_proto(state->myURI); if (state->conntype == SYNCML_CONN_TYPE_HTTPS) if (!syncml_ssl_init_server(state)) { goto err; } if (state->socketfd >= 0) { struct sockaddr_in servaddr; servaddr.sin_family = AF_INET; servaddr.sin_port = htons(serverport); servaddr.sin_addr.s_addr = INADDR_ANY; if (!bind(state->socketfd, (struct sockaddr*) &servaddr, sizeof(struct sockaddr_in))) { listen(state->socketfd, 0); } else { error = SYNCML_ERROR_NOPORT; goto err; } } else { error = SYNCML_ERROR_NOPORT; goto err; } dd(printf("SyncML: Socket opened.\n")); } pipe(msgpipe); state->readmsg = msgpipe[0]; state->writemsg = msgpipe[1]; pthread_create(&state->thread, NULL, syncml_main_thread, state); return(state); err: syncml_error(state, state->userdata, error); if (state->socketfd >= 0) close(state->socketfd); state->socketfd = -1; return(NULL); } // Load syncml internal data from the open FILE f void syncml_load_engine_state(syncml_state *state) { char line[256]; FILE *f; if ((f = fopen(state->statefilename, "r"))) { while (fgets(line, 256, f)) { char prop[128], data[256]; if (sscanf(line, "%127s = %255[^\n]", prop, data) == 2) { if (!strcmp(prop, "devID")) state->devID = g_strdup(data); if (!strcmp(prop, "mynextnonce")) state->mynextnonce = g_strdup(data); if (!strcmp(prop, "othernextnonce")) state->othernextnonce = g_strdup(data); if (!strcmp(prop, "dbinfo")) { char dbname[256]="",otherlast[256]="",mylast[256]=""; if (sscanf(data, "%255[^;];%255[^;];%255[^;]", dbname,otherlast, mylast) > 0) { syncml_db_anchors *anch = g_malloc0(sizeof(syncml_db_anchors)); anch->db = g_strdup(dbname); anch->mylast = g_strdup(mylast); anch->otherlast = g_strdup(otherlast); state->dbanchors = g_list_append(state->dbanchors, anch); } } } } fclose(f); } } // Save syncml internal data from open FILE f void syncml_save_engine_state(syncml_state *state) { FILE *f; if ((f = fopen(state->statefilename, "w"))) { GList *pairs = state->db_pairs; if (state->devID) fprintf(f, "devID = %s\n", state->devID); if (state->mynextnonce) fprintf(f, "mynextnonce = %s\n", state->mynextnonce); if (state->othernextnonce) fprintf(f, "othernextnonce = %s\n", state->othernextnonce); while (pairs) { syncml_db_pair *pair = pairs->data; if (pair && pair->myDB) { fprintf(f, "dbinfo = %s;", pair->myDB); if (pair->lastanchor) fprintf(f, "%s", pair->lastanchor); fprintf(f, ";"); if (pair->mylastanchor) fprintf(f, "%s", pair->mylastanchor); fprintf(f, "\n"); } pairs = pairs->next; } fclose(f); } } // Return true if the clien/server in the other end is a MultiSync plugin gboolean syncml_is_partner_multisync(syncml_state *state) { if (state->otherdevinfo) { if (!g_strcasecmp(state->otherdevinfo->manufacturer, "The MultiSync Project")) return(TRUE); } return(FALSE); } void syncml_cmd_send_changes(syncml_state *state, change_info *info) { syncml_engine_cmd cmd; cmd.cmd = SYNCML_ENGINE_CMD_SYNC; cmd.data = info; // Send message to command loop write(state->writemsg, &cmd, sizeof(syncml_engine_cmd)); } void syncml_cmd_send_changes_result(syncml_state *state, GList *result) { syncml_engine_cmd cmd; cmd.cmd = SYNCML_ENGINE_CMD_SYNC_STATUS; cmd.data = result; // Send message to command loop write(state->writemsg, &cmd, sizeof(syncml_engine_cmd)); } void syncml_cmd_send_sync_done(syncml_state *state) { syncml_engine_cmd cmd; cmd.cmd = SYNCML_ENGINE_CMD_MAP_STATUS; cmd.data = NULL; // Send message to command loop write(state->writemsg, &cmd, sizeof(syncml_engine_cmd)); } void syncml_cmd_send_sync_serverinit(syncml_state *state, sync_object_type newdbs) { syncml_engine_cmd cmd; cmd.cmd = SYNCML_ENGINE_CMD_SYNC_SERVERINIT; cmd.data = (gpointer) newdbs; // Send message to command loop write(state->writemsg, &cmd, sizeof(syncml_engine_cmd)); } void syncml_cmd_get_devinfo(syncml_state *state) { syncml_engine_cmd cmd; cmd.cmd = SYNCML_ENGINE_CMD_GETDEVINFO; cmd.data = NULL; // Send message to command loop write(state->writemsg, &cmd, sizeof(syncml_engine_cmd)); } void syncml_cmd_quit(syncml_state *state) { syncml_engine_cmd cmd; cmd.cmd = SYNCML_ENGINE_CMD_QUIT; cmd.data = NULL; write(state->writemsg, &cmd, sizeof(syncml_engine_cmd)); } // Set login and password void syncml_set_login(syncml_state *state, char *login, char *passwd) { if (!state) return; SYNCML_FREE_STRING(state->user); SYNCML_FREE_STRING(state->passwd); state->user = g_strdup(login); state->passwd = g_strdup(passwd); } void syncml_add_db(syncml_state *state, char* localdb, sync_object_type objtype) { GList *anchors; syncml_db_pair *pair; if (!state) return; pair = syncml_db_pair_new(localdb, NULL, NULL); pair->object_type = objtype; anchors = state->dbanchors; while (anchors) { // Get lastanchors from loaded engine state syncml_db_anchors *anch = anchors->data; if (anch && anch->db && !g_strcasecmp(anch->db, localdb)) { if (anch->otherlast) { SYNCML_FREE_STRING(pair->lastanchor); pair->lastanchor = g_strdup(anch->otherlast); } if (anch->mylast) { SYNCML_FREE_STRING(pair->mylastanchor); pair->mylastanchor = g_strdup(anch->mylast); } } anchors = anchors->next; } state->db_pairs = g_list_append(state->db_pairs, pair); } void syncml_add_remote_db(syncml_state *state, char* localdb, char *remotedb) { GList *pairs = state->db_pairs; while (pairs) { syncml_db_pair *pair = pairs->data; if (!g_strcasecmp(pair->myDB, localdb)) { if (pair->otherDB) g_free(pair->otherDB); pair->otherDB = g_strdup(remotedb); pair->dosynchronize = TRUE; } pairs = pairs->next; } } // Initialization, called only once void syncml_init() { syncml_ssl_init(); xmlInitParser(); }