5 * Copyright (C) 2007-2010 Nokia Corporation
6 * Copyright (C) 2007-2010 Marcel Holtmann <marcel@holtmann.org>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
36 #include <sys/types.h>
38 #include <sys/statvfs.h>
43 #include <gobex/gobex.h>
48 #include "obex-priv.h"
53 #include "transport.h"
56 /* Challenge request */
57 #define NONCE_TAG 0x00
58 #define OPTIONS_TAG 0x01 /* Optional */
59 #define REALM_TAG 0x02 /* Optional */
63 /* Challenge response */
64 #define DIGEST_TAG 0x00
65 #define USER_ID_TAG 0x01 /* Optional */
66 #define DIGEST_NONCE_TAG 0x02 /* Optional */
68 static GSList *sessions = NULL;
74 } __attribute__ ((packed)) obex_connect_hdr_t;
80 } __attribute__ ((packed));
82 /* Possible commands */
87 { G_OBEX_OP_CONNECT, "CONNECT" },
88 { G_OBEX_OP_DISCONNECT, "DISCONNECT" },
89 { G_OBEX_OP_PUT, "PUT" },
90 { G_OBEX_OP_GET, "GET" },
91 { G_OBEX_OP_SETPATH, "SETPATH" },
92 { G_OBEX_OP_SESSION, "SESSION" },
93 { G_OBEX_OP_ABORT, "ABORT" },
94 { G_OBEX_OP_ACTION, "ACTION" },
98 /* Possible Response */
102 } obex_response[] = {
103 { G_OBEX_RSP_CONTINUE, "CONTINUE" },
104 { G_OBEX_RSP_SUCCESS, "SUCCESS" },
105 { G_OBEX_RSP_CREATED, "CREATED" },
106 { G_OBEX_RSP_ACCEPTED, "ACCEPTED" },
107 { G_OBEX_RSP_NON_AUTHORITATIVE, "NON_AUTHORITATIVE" },
108 { G_OBEX_RSP_NO_CONTENT, "NO_CONTENT" },
109 { G_OBEX_RSP_RESET_CONTENT, "RESET_CONTENT" },
110 { G_OBEX_RSP_PARTIAL_CONTENT, "PARTIAL_CONTENT" },
111 { G_OBEX_RSP_MULTIPLE_CHOICES, "MULTIPLE_CHOICES" },
112 { G_OBEX_RSP_MOVED_PERMANENTLY, "MOVED_PERMANENTLY" },
113 { G_OBEX_RSP_MOVED_TEMPORARILY, "MOVED_TEMPORARILY" },
114 { G_OBEX_RSP_SEE_OTHER, "SEE_OTHER" },
115 { G_OBEX_RSP_NOT_MODIFIED, "NOT_MODIFIED" },
116 { G_OBEX_RSP_USE_PROXY, "USE_PROXY" },
117 { G_OBEX_RSP_BAD_REQUEST, "BAD_REQUEST" },
118 { G_OBEX_RSP_UNAUTHORIZED, "UNAUTHORIZED" },
119 { G_OBEX_RSP_PAYMENT_REQUIRED, "PAYMENT_REQUIRED" },
120 { G_OBEX_RSP_FORBIDDEN, "FORBIDDEN" },
121 { G_OBEX_RSP_NOT_FOUND, "NOT_FOUND" },
122 { G_OBEX_RSP_METHOD_NOT_ALLOWED, "METHOD_NOT_ALLOWED" },
123 { G_OBEX_RSP_NOT_ACCEPTABLE, "NOT_ACCEPTABLE" },
124 { G_OBEX_RSP_PROXY_AUTH_REQUIRED, "PROXY_AUTH_REQUIRED" },
125 { G_OBEX_RSP_REQUEST_TIME_OUT, "REQUEST_TIME_OUT" },
126 { G_OBEX_RSP_CONFLICT, "CONFLICT" },
127 { G_OBEX_RSP_GONE, "GONE" },
128 { G_OBEX_RSP_LENGTH_REQUIRED, "LENGTH_REQUIRED" },
129 { G_OBEX_RSP_PRECONDITION_FAILED, "PRECONDITION_FAILED" },
130 { G_OBEX_RSP_REQ_ENTITY_TOO_LARGE, "REQ_ENTITY_TOO_LARGE" },
131 { G_OBEX_RSP_REQ_URL_TOO_LARGE, "REQ_URL_TOO_LARGE" },
132 { G_OBEX_RSP_UNSUPPORTED_MEDIA_TYPE, "UNSUPPORTED_MEDIA_TYPE"},
133 { G_OBEX_RSP_INTERNAL_SERVER_ERROR, "INTERNAL_SERVER_ERROR" },
134 { G_OBEX_RSP_NOT_IMPLEMENTED, "NOT_IMPLEMENTED" },
135 { G_OBEX_RSP_BAD_GATEWAY, "BAD_GATEWAY" },
136 { G_OBEX_RSP_SERVICE_UNAVAILABLE, "SERVICE_UNAVAILABLE" },
137 { G_OBEX_RSP_GATEWAY_TIMEOUT, "GATEWAY_TIMEOUT" },
138 { G_OBEX_RSP_VERSION_NOT_SUPPORTED, "VERSION_NOT_SUPPORTED" },
139 { G_OBEX_RSP_DATABASE_FULL, "DATABASE_FULL" },
140 { G_OBEX_RSP_DATABASE_LOCKED, "DATABASE_LOCKED" },
144 static gboolean handle_async_io(void *object, int flags, int err,
147 static void print_event(int cmd, int rsp)
149 const char *cmdstr = NULL, *rspstr = NULL;
158 for (i = 0; obex_command[i].cmd != 0xFF; i++) {
159 if (obex_command[i].cmd != cmd)
161 cmdstr = obex_command[i].name;
164 for (i = 0; obex_response[i].rsp != 0xFF; i++) {
165 if (obex_response[i].rsp != rsp)
167 rspstr = obex_response[i].name;
170 obex_debug("%s(0x%x), %s(0x%x)", cmdstr, cmd, rspstr, rsp);
173 static void os_set_response(struct obex_session *os, int err)
177 rsp = g_obex_errno_to_rsp(err);
179 print_event(-1, rsp);
181 g_obex_send_rsp(os->obex, rsp, NULL, G_OBEX_HDR_INVALID);
184 static void os_session_mark_aborted(struct obex_session *os)
186 /* the session was already cancelled/aborted or size in unknown */
187 if (os->aborted || os->size == OBJECT_SIZE_UNKNOWN)
190 os->aborted = (os->size != os->offset);
193 static void os_reset_session(struct obex_session *os)
195 os_session_mark_aborted(os);
198 os->driver->set_io_watch(os->object, NULL, NULL);
199 os->driver->close(os->object);
200 if (os->aborted && os->cmd == G_OBEX_OP_PUT && os->path &&
202 os->driver->remove(os->path);
205 if (os->service && os->service->reset)
206 os->service->reset(os, os->service_data);
230 if (os->get_rsp > 0) {
231 g_obex_remove_request_function(os->obex, os->get_rsp);
240 os->size = OBJECT_SIZE_DELETE;
241 os->headers_sent = FALSE;
245 static void obex_session_free(struct obex_session *os)
247 sessions = g_slist_remove(sessions, os);
250 g_io_channel_unref(os->io);
253 g_obex_unref(os->obex);
258 /* From Imendio's GnomeVFS OBEX module (om-utils.c) */
259 static time_t parse_iso8610(const char *val, int size)
261 time_t time, tz_offset = 0;
267 memset(&tm, 0, sizeof(tm));
268 /* According to spec the time doesn't have to be null terminated */
269 date = g_strndup(val, size);
270 nr = sscanf(date, "%04u%02u%02uT%02u%02u%02u%c",
271 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
272 &tm.tm_hour, &tm.tm_min, &tm.tm_sec,
276 /* Invalid time format */
280 tm.tm_year -= 1900; /* Year since 1900 */
281 tm.tm_mon--; /* Months since January, values 0-11 */
282 tm.tm_isdst = -1; /* Daylight savings information not avail */
284 #if defined(HAVE_TM_GMTOFF)
285 tz_offset = tm.tm_gmtoff;
286 #elif defined(HAVE_TIMEZONE)
287 tz_offset = -timezone;
295 * Date/Time was in localtime (to remote device)
296 * already. Since we don't know anything about the
297 * timezone on that one we won't try to apply UTC offset
305 static uint8_t *extract_nonce(const uint8_t *buffer, unsigned int hlen)
307 struct auth_header *hdr;
308 uint8_t *nonce = NULL;
312 hdr = (void *) buffer + len;
316 if (hdr->len != NONCE_LEN)
323 len += hdr->len + sizeof(struct auth_header);
329 static uint8_t *challenge_response(const uint8_t *nonce)
335 result = g_new0(uint8_t, NONCE_LEN);
337 md5 = g_checksum_new(G_CHECKSUM_MD5);
341 g_checksum_update(md5, nonce, NONCE_LEN);
342 g_checksum_update(md5, (uint8_t *) ":BlueZ", 6);
345 g_checksum_get_digest(md5, result, &size);
347 g_checksum_free(md5);
352 static void parse_service(struct obex_session *os, GObexPacket *req)
355 const guint8 *target = NULL, *who = NULL;
356 gsize target_size = 0, who_size = 0;
358 hdr = g_obex_packet_get_header(req, G_OBEX_HDR_WHO);
362 g_obex_header_get_bytes(hdr, &who, &who_size);
365 hdr = g_obex_packet_get_header(req, G_OBEX_HDR_TARGET);
369 g_obex_header_get_bytes(hdr, &target, &target_size);
372 os->service = obex_service_driver_find(os->server->drivers,
377 static void parse_authchal(struct obex_session *session, GObexPacket *req,
381 const guint8 *data, *nonce = NULL;
383 uint8_t challenge[18];
384 struct auth_header *auth = (struct auth_header *) challenge;
387 hdr = g_obex_packet_get_header(req, G_OBEX_HDR_AUTHCHAL);
391 if (!g_obex_header_get_bytes(hdr, &data, &len))
394 nonce = extract_nonce(data, len);
395 DBG("AUTH CHALLENGE REQUEST");
397 response = challenge_response(nonce);
398 auth->tag = DIGEST_TAG;
399 auth->len = NONCE_LEN;
400 memcpy(auth->val, response, NONCE_LEN);
402 hdr = g_obex_header_new_bytes(G_OBEX_HDR_AUTHRESP, challenge,
404 g_obex_packet_add_header(rsp, hdr);
407 static void cmd_connect(GObex *obex, GObexPacket *req, void *user_data)
409 struct obex_session *os = user_data;
416 print_event(G_OBEX_OP_CONNECT, -1);
418 parse_service(os, req);
420 if (os->service == NULL || os->service->connect == NULL) {
421 error("Connect attempt to a non-supported target");
422 os_set_response(os, -EPERM);
426 DBG("Selected driver: %s", os->service->name);
428 os->service_data = os->service->connect(os, &err);
430 os_set_response(os, err);
434 os->cmd = G_OBEX_OP_CONNECT;
436 rsp = g_obex_packet_new(G_OBEX_RSP_SUCCESS, TRUE, G_OBEX_HDR_INVALID);
438 parse_authchal(os, req, rsp);
440 if (os->service->target) {
441 hdr = g_obex_header_new_bytes(G_OBEX_HDR_WHO,
443 os->service->target_size);
444 g_obex_packet_add_header(rsp, hdr);
447 g_obex_send(obex, rsp, NULL);
452 static void cmd_disconnect(GObex *obex, GObexPacket *req, void *user_data)
454 struct obex_session *os = user_data;
456 DBG("session %p", os);
458 print_event(G_OBEX_OP_DISCONNECT, -1);
460 os->cmd = G_OBEX_OP_DISCONNECT;
462 os_set_response(os, 0);
465 static ssize_t driver_write(struct obex_session *os)
469 while (os->pending > 0) {
472 w = os->driver->write(os->object, os->buf + len, os->pending);
474 error("write(): %s (%zd)", strerror(-w), -w);
477 else if (w == -EINVAL)
478 memmove(os->buf, os->buf + len, os->pending);
488 DBG("%zd written", len);
490 if (os->service->progress != NULL)
491 os->service->progress(os, os->service_data);
496 static gssize driver_read(struct obex_session *os, void *buf, gsize size)
500 if (os->object == NULL)
503 if (os->service->progress != NULL)
504 os->service->progress(os, os->service_data);
506 len = os->driver->read(os->object, buf, size);
508 error("read(): %s (%zd)", strerror(-len), -len);
512 os->driver->set_io_watch(os->object, handle_async_io,
518 DBG("%zd read", len);
523 static gssize send_data(void *buf, gsize size, gpointer user_data)
525 struct obex_session *os = user_data;
527 DBG("name=%s type=%s file=%p size=%zu", os->name, os->type, os->object,
531 return os->err < 0 ? os->err : -EPERM;
533 return driver_read(os, buf, size);
536 static void transfer_complete(GObex *obex, GError *err, gpointer user_data)
538 struct obex_session *os = user_data;
543 error("transfer failed: %s\n", err->message);
547 if (os->object && os->driver && os->driver->flush) {
548 if (os->driver->flush(os->object) == -EAGAIN) {
549 g_obex_suspend(os->obex);
550 os->driver->set_io_watch(os->object, handle_async_io,
557 os_reset_session(os);
560 static int driver_get_headers(struct obex_session *os)
568 DBG("name=%s type=%s object=%p", os->name, os->type, os->object);
571 return os->err < 0 ? os->err : -EPERM;
573 if (os->object == NULL)
576 if (os->headers_sent)
579 rsp = g_obex_packet_new(G_OBEX_RSP_CONTINUE, TRUE, G_OBEX_HDR_INVALID);
581 if (os->driver->get_next_header == NULL)
584 while ((len = os->driver->get_next_header(os->object, &data,
585 sizeof(data), &id))) {
587 error("get_next_header(): %s (%zd)", strerror(-len),
590 g_obex_packet_free(rsp);
601 hdr = g_obex_header_new_bytes(id, data, len);
602 g_obex_packet_add_header(rsp, hdr);
606 if (os->size != OBJECT_SIZE_UNKNOWN && os->size < UINT32_MAX) {
607 hdr = g_obex_header_new_uint32(G_OBEX_HDR_LENGTH, os->size);
608 g_obex_packet_add_header(rsp, hdr);
611 g_obex_get_rsp_pkt(os->obex, rsp, send_data, transfer_complete, os,
614 os->headers_sent = TRUE;
616 print_event(-1, G_OBEX_RSP_CONTINUE);
621 static gboolean handle_async_io(void *object, int flags, int err,
624 struct obex_session *os = user_data;
629 if (flags & G_IO_OUT)
630 err = driver_write(os);
631 if ((flags & G_IO_IN) && !os->headers_sent)
632 err = driver_get_headers(os);
643 g_obex_resume(os->obex);
648 static gboolean recv_data(const void *buf, gsize size, gpointer user_data)
650 struct obex_session *os = user_data;
653 DBG("name=%s type=%s file=%p size=%zu", os->name, os->type, os->object,
659 /* workaround: client didn't send the object lenght */
660 if (os->size == OBJECT_SIZE_DELETE)
661 os->size = OBJECT_SIZE_UNKNOWN;
663 os->buf = g_realloc(os->buf, os->pending + size);
664 memcpy(os->buf + os->pending, buf, size);
667 /* only write if both object and driver are valid */
668 if (os->object == NULL || os->driver == NULL) {
669 DBG("Stored %" PRIu64 " bytes into temporary buffer",
674 ret = driver_write(os);
678 if (ret == -EAGAIN) {
679 g_obex_suspend(os->obex);
680 os->driver->set_io_watch(os->object, handle_async_io, os);
687 static void parse_type(struct obex_session *os, GObexPacket *req)
696 hdr = g_obex_packet_get_header(req, G_OBEX_HDR_TYPE);
700 if (!g_obex_header_get_bytes(hdr, &type, &len))
703 /* Ensure null termination */
704 if (type[len - 1] != '\0')
707 os->type = g_strndup((const char *) type, len);
708 DBG("TYPE: %s", os->type);
711 os->driver = obex_mime_type_driver_find(os->service->target,
712 os->service->target_size,
715 os->service->who_size);
718 static void parse_name(struct obex_session *os, GObexPacket *req)
726 hdr = g_obex_packet_get_header(req, G_OBEX_HDR_NAME);
730 if (!g_obex_header_get_unicode(hdr, &name))
733 os->name = g_strdup(name);
734 DBG("NAME: %s", os->name);
737 static void parse_apparam(struct obex_session *os, GObexPacket *req)
740 const guint8 *apparam;
743 hdr = g_obex_packet_get_header(req, G_OBEX_HDR_APPARAM);
747 if (!g_obex_header_get_bytes(hdr, &apparam, &len))
750 os->apparam = g_memdup(apparam, len);
751 os->apparam_len = len;
755 static void cmd_get(GObex *obex, GObexPacket *req, gpointer user_data)
757 struct obex_session *os = user_data;
760 DBG("session %p", os);
762 print_event(G_OBEX_OP_GET, -1);
764 if (os->service == NULL) {
765 os_set_response(os, -EPERM);
769 if (os->service->get == NULL) {
770 os_set_response(os, -ENOSYS);
774 os->headers_sent = FALSE;
784 error("No driver found");
785 os_set_response(os, -ENOSYS);
789 os->cmd = G_OBEX_OP_GET;
793 parse_apparam(os, req);
795 err = os->service->get(os, os->service_data);
799 os_set_response(os, err);
802 static void cmd_setpath(GObex *obex, GObexPacket *req, gpointer user_data)
804 struct obex_session *os = user_data;
809 print_event(G_OBEX_OP_SETPATH, -1);
811 if (os->service == NULL) {
816 if (os->service->setpath == NULL) {
821 os->cmd = G_OBEX_OP_SETPATH;
825 os->nonhdr = g_obex_packet_get_data(req, &os->nonhdr_len);
827 err = os->service->setpath(os, os->service_data);
829 os_set_response(os, err);
832 int obex_get_stream_start(struct obex_session *os, const char *filename)
836 size_t size = OBJECT_SIZE_UNKNOWN;
838 object = os->driver->open(filename, O_RDONLY, 0, os->service_data,
840 if (object == NULL) {
841 error("open(%s): %s (%d)", filename, strerror(-err), -err);
849 err = driver_get_headers(os);
853 g_obex_suspend(os->obex);
854 os->driver->set_io_watch(os->object, handle_async_io, os);
858 int obex_put_stream_start(struct obex_session *os, const char *filename)
862 os->object = os->driver->open(filename, O_WRONLY | O_CREAT | O_TRUNC,
863 0600, os->service_data,
864 os->size != OBJECT_SIZE_UNKNOWN ?
865 (size_t *) &os->size : NULL, &err);
866 if (os->object == NULL) {
867 error("open(%s): %s (%d)", filename, strerror(-err), -err);
871 os->path = g_strdup(filename);
876 static void parse_length(struct obex_session *os, GObexPacket *req)
881 hdr = g_obex_packet_get_header(req, G_OBEX_HDR_LENGTH);
885 if (!g_obex_header_get_uint32(hdr, &size))
889 DBG("LENGTH: %" PRIu64, os->size);
892 static void parse_time(struct obex_session *os, GObexPacket *req)
898 hdr = g_obex_packet_get_header(req, G_OBEX_HDR_TIME);
903 if (!g_obex_header_get_bytes(hdr, &time, &len))
906 os->time = parse_iso8610((const char *) time, len);
907 DBG("TIME: %s", ctime(&os->time));
910 static gboolean check_put(GObex *obex, GObexPacket *req, void *user_data)
912 struct obex_session *os = user_data;
915 if (os->service->chkput == NULL)
918 ret = os->service->chkput(os, os->service_data);
923 g_obex_suspend(os->obex);
924 os->driver->set_io_watch(os->object, handle_async_io, os);
927 os_set_response(os, ret);
931 if (os->size == OBJECT_SIZE_DELETE || os->size == OBJECT_SIZE_UNKNOWN)
932 DBG("Got a PUT without a Length");
940 static void cmd_put(GObex *obex, GObexPacket *req, gpointer user_data)
942 struct obex_session *os = user_data;
947 print_event(G_OBEX_OP_PUT, -1);
949 if (os->service == NULL) {
950 os_set_response(os, -EPERM);
956 if (os->driver == NULL) {
957 error("No driver found");
958 os_set_response(os, -ENOSYS);
962 os->cmd = G_OBEX_OP_PUT;
965 parse_length(os, req);
967 parse_apparam(os, req);
970 if (!check_put(obex, req, user_data))
974 if (os->service->put == NULL) {
975 os_set_response(os, -ENOSYS);
979 err = os->service->put(os, os->service_data);
981 g_obex_put_rsp(obex, req, recv_data, transfer_complete, os,
982 NULL, G_OBEX_HDR_INVALID);
983 print_event(G_OBEX_OP_PUT, G_OBEX_RSP_CONTINUE);
987 os_set_response(os, err);
990 static void parse_destname(struct obex_session *os, GObexPacket *req)
993 const char *destname;
995 g_free(os->destname);
998 hdr = g_obex_packet_get_header(req, G_OBEX_HDR_DESTNAME);
1002 if (!g_obex_header_get_unicode(hdr, &destname))
1005 os->destname = g_strdup(destname);
1006 DBG("DESTNAME: %s", os->destname);
1009 static void parse_action(struct obex_session *os, GObexPacket *req)
1014 hdr = g_obex_packet_get_header(req, G_OBEX_HDR_ACTION);
1018 if (!g_obex_header_get_uint8(hdr, &id))
1022 DBG("ACTION: 0x%02x", os->action_id);
1025 static void cmd_action(GObex *obex, GObexPacket *req, gpointer user_data)
1027 struct obex_session *os = user_data;
1032 print_event(G_OBEX_OP_ACTION, -1);
1034 if (os->service == NULL) {
1039 if (os->service->action == NULL) {
1044 os->cmd = G_OBEX_OP_ACTION;
1046 parse_name(os, req);
1047 parse_destname(os, req);
1048 parse_action(os, req);
1050 os->driver = obex_mime_type_driver_find(os->service->target,
1051 os->service->target_size,
1054 os->service->who_size);
1055 if (os->driver == NULL) {
1060 err = os->service->action(os, os->service_data);
1062 os_set_response(os, err);
1065 static void cmd_abort(GObex *obex, GObexPacket *req, gpointer user_data)
1067 struct obex_session *os = user_data;
1071 print_event(G_OBEX_OP_ABORT, -1);
1073 os_reset_session(os);
1075 os_set_response(os, 0);
1078 static void obex_session_destroy(struct obex_session *os)
1082 os_reset_session(os);
1084 if (os->service && os->service->disconnect)
1085 os->service->disconnect(os, os->service_data);
1087 obex_session_free(os);
1090 static void disconn_func(GObex *obex, GError *err, gpointer user_data)
1092 struct obex_session *os = user_data;
1094 error("disconnected: %s\n", err ? err->message : "<no err>");
1095 obex_session_destroy(os);
1098 int obex_session_start(GIOChannel *io, uint16_t tx_mtu, uint16_t rx_mtu,
1099 gboolean stream, struct obex_server *server)
1101 struct obex_session *os;
1103 GObexTransportType type;
1104 static uint32_t id = 0;
1108 os = g_new0(struct obex_session, 1);
1111 os->service = obex_service_driver_find(server->drivers, NULL,
1113 os->server = server;
1114 os->size = OBJECT_SIZE_DELETE;
1116 type = stream ? G_OBEX_TRANSPORT_STREAM : G_OBEX_TRANSPORT_PACKET;
1118 obex = g_obex_new(io, type, rx_mtu, tx_mtu);
1120 obex_session_free(os);
1124 g_obex_set_disconnect_function(obex, disconn_func, os);
1125 g_obex_add_request_function(obex, G_OBEX_OP_CONNECT, cmd_connect, os);
1126 g_obex_add_request_function(obex, G_OBEX_OP_DISCONNECT, cmd_disconnect,
1128 g_obex_add_request_function(obex, G_OBEX_OP_PUT, cmd_put, os);
1129 g_obex_add_request_function(obex, G_OBEX_OP_GET, cmd_get, os);
1130 g_obex_add_request_function(obex, G_OBEX_OP_SETPATH, cmd_setpath, os);
1131 g_obex_add_request_function(obex, G_OBEX_OP_ACTION, cmd_action, os);
1132 g_obex_add_request_function(obex, G_OBEX_OP_ABORT, cmd_abort, os);
1135 os->io = g_io_channel_ref(io);
1137 sessions = g_slist_prepend(sessions, os);
1142 const char *obex_get_name(struct obex_session *os)
1147 const char *obex_get_destname(struct obex_session *os)
1149 return os->destname;
1152 void obex_set_name(struct obex_session *os, const char *name)
1155 os->name = g_strdup(name);
1156 DBG("Name changed: %s", os->name);
1159 ssize_t obex_get_size(struct obex_session *os)
1164 const char *obex_get_type(struct obex_session *os)
1169 int obex_remove(struct obex_session *os, const char *path)
1171 if (os->driver == NULL)
1174 return os->driver->remove(path);
1177 int obex_copy(struct obex_session *os, const char *source,
1178 const char *destination)
1180 if (os->driver == NULL || os->driver->copy == NULL)
1183 DBG("%s %s", source, destination);
1185 return os->driver->copy(source, destination);
1188 int obex_move(struct obex_session *os, const char *source,
1189 const char *destination)
1191 if (os->driver == NULL || os->driver->move == NULL)
1194 DBG("%s %s", source, destination);
1196 return os->driver->move(source, destination);
1199 uint8_t obex_get_action_id(struct obex_session *os)
1201 return os->action_id;
1204 ssize_t obex_get_apparam(struct obex_session *os, const uint8_t **buffer)
1206 *buffer = os->apparam;
1208 return os->apparam_len;
1211 ssize_t obex_get_non_header_data(struct obex_session *os,
1212 const uint8_t **data)
1216 return os->nonhdr_len;
1219 int obex_getpeername(struct obex_session *os, char **name)
1221 struct obex_transport_driver *transport = os->server->transport;
1223 if (transport == NULL || transport->getpeername == NULL)
1226 return transport->getpeername(os->io, name);
1229 int memncmp0(const void *a, size_t na, const void *b, size_t nb)
1240 return memcmp(a, b, na);