3 * OBEX library with GLib integration
5 * Copyright (C) 2011 Intel Corporation. All rights reserved.
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
11 * This program 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
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
31 #include "glib-helper.h"
32 #include "gobex-debug.h"
34 #define G_OBEX_DEFAULT_MTU 4096
35 #define G_OBEX_MINIMUM_MTU 255
36 #define G_OBEX_MAXIMUM_MTU 65535
38 #define G_OBEX_DEFAULT_TIMEOUT 10
39 #define G_OBEX_ABORT_TIMEOUT 5
41 #define G_OBEX_OP_NONE 0xff
43 #define FINAL_BIT 0x80
45 #define CONNID_INVALID 0xffffffff
47 guint gobex_debug = 0;
62 gboolean (*read) (GObex *obex, GError **err);
63 gboolean (*write) (GObex *obex, GError **err);
77 struct srm_config *srm;
93 GObexFunc disconn_func;
94 gpointer disconn_func_data;
96 struct pending_pkt *pending_req;
105 GObexResponseFunc rsp_func;
113 GObexRequestFunc func;
117 struct connect_data {
121 } __attribute__ ((packed));
123 struct setpath_data {
126 } __attribute__ ((packed));
128 static struct error_code {
132 { G_OBEX_RSP_CONTINUE, "Continue" },
133 { G_OBEX_RSP_SUCCESS, "Success" },
134 { G_OBEX_RSP_CREATED, "Created" },
135 { G_OBEX_RSP_ACCEPTED, "Accepted" },
136 { G_OBEX_RSP_NON_AUTHORITATIVE, "Non Authoritative" },
137 { G_OBEX_RSP_NO_CONTENT, "No Content" },
138 { G_OBEX_RSP_RESET_CONTENT, "Reset Content" },
139 { G_OBEX_RSP_PARTIAL_CONTENT, "Partial Content" },
140 { G_OBEX_RSP_MULTIPLE_CHOICES, "Multiple Choices" },
141 { G_OBEX_RSP_MOVED_PERMANENTLY, "Moved Permanently" },
142 { G_OBEX_RSP_MOVED_TEMPORARILY, "Moved Temporarily" },
143 { G_OBEX_RSP_SEE_OTHER, "See Other" },
144 { G_OBEX_RSP_NOT_MODIFIED, "Not Modified" },
145 { G_OBEX_RSP_USE_PROXY, "Use Proxy" },
146 { G_OBEX_RSP_BAD_REQUEST, "Bad Request" },
147 { G_OBEX_RSP_UNAUTHORIZED, "Unauthorized" },
148 { G_OBEX_RSP_PAYMENT_REQUIRED, "Payment Required" },
149 { G_OBEX_RSP_FORBIDDEN, "Forbidden" },
150 { G_OBEX_RSP_NOT_FOUND, "Not Found" },
151 { G_OBEX_RSP_METHOD_NOT_ALLOWED, "Method Not Allowed" },
152 { G_OBEX_RSP_NOT_ACCEPTABLE, "Not Acceptable" },
153 { G_OBEX_RSP_PROXY_AUTH_REQUIRED, "Proxy Authentication Required" },
154 { G_OBEX_RSP_REQUEST_TIME_OUT, "Request Time Out" },
155 { G_OBEX_RSP_CONFLICT, "Conflict" },
156 { G_OBEX_RSP_GONE, "Gone" },
157 { G_OBEX_RSP_LENGTH_REQUIRED, "Length Required" },
158 { G_OBEX_RSP_PRECONDITION_FAILED, "Precondition Failed" },
159 { G_OBEX_RSP_REQ_ENTITY_TOO_LARGE, "Request Entity Too Large" },
160 { G_OBEX_RSP_REQ_URL_TOO_LARGE, "Request URL Too Large" },
161 { G_OBEX_RSP_UNSUPPORTED_MEDIA_TYPE, "Unsupported Media Type" },
162 { G_OBEX_RSP_INTERNAL_SERVER_ERROR, "Internal Server Error" },
163 { G_OBEX_RSP_NOT_IMPLEMENTED, "Not Implemented" },
164 { G_OBEX_RSP_BAD_GATEWAY, "Bad Gateway" },
165 { G_OBEX_RSP_SERVICE_UNAVAILABLE, "Service Unavailable" },
166 { G_OBEX_RSP_GATEWAY_TIMEOUT, "Gateway Timeout" },
167 { G_OBEX_RSP_VERSION_NOT_SUPPORTED, "Version Not Supported" },
168 { G_OBEX_RSP_DATABASE_FULL, "Database Full" },
169 { G_OBEX_RSP_DATABASE_LOCKED, "Database Locked" },
173 const char *g_obex_strerror(guint8 err_code)
175 struct error_code *error;
177 for (error = obex_errors; error->name != NULL; error++) {
178 if (error->code == err_code)
185 static ssize_t req_header_offset(guint8 opcode)
188 case G_OBEX_OP_CONNECT:
189 return sizeof(struct connect_data);
190 case G_OBEX_OP_SETPATH:
191 return sizeof(struct setpath_data);
192 case G_OBEX_OP_DISCONNECT:
195 case G_OBEX_OP_SESSION:
196 case G_OBEX_OP_ABORT:
197 case G_OBEX_OP_ACTION:
204 static ssize_t rsp_header_offset(guint8 opcode)
207 case G_OBEX_OP_CONNECT:
208 return sizeof(struct connect_data);
209 case G_OBEX_OP_SETPATH:
210 case G_OBEX_OP_DISCONNECT:
213 case G_OBEX_OP_SESSION:
214 case G_OBEX_OP_ABORT:
215 case G_OBEX_OP_ACTION:
222 static void pending_pkt_free(struct pending_pkt *p)
225 g_obex_unref(p->obex);
227 if (p->timeout_id > 0)
228 g_source_remove(p->timeout_id);
230 g_obex_packet_free(p->pkt);
235 static gboolean req_timeout(gpointer user_data)
237 GObex *obex = user_data;
238 struct pending_pkt *p = obex->pending_req;
243 obex->pending_req = NULL;
245 err = g_error_new(G_OBEX_ERROR, G_OBEX_ERROR_TIMEOUT,
246 "Timed out waiting for response");
248 g_obex_debug(G_OBEX_DEBUG_ERROR, "%s", err->message);
251 obex->pending_req = NULL;
255 p->rsp_func(obex, err, NULL, p->rsp_data);
263 static gboolean write_stream(GObex *obex, GError **err)
269 buf = (gchar *) &obex->tx_buf[obex->tx_sent];
270 status = g_io_channel_write_chars(obex->io, buf, obex->tx_data,
271 &bytes_written, err);
272 if (status != G_IO_STATUS_NORMAL)
275 g_obex_dump("<", buf, bytes_written);
277 obex->tx_sent += bytes_written;
278 obex->tx_data -= bytes_written;
283 static gboolean write_packet(GObex *obex, GError **err)
289 buf = (gchar *) &obex->tx_buf[obex->tx_sent];
290 status = g_io_channel_write_chars(obex->io, buf, obex->tx_data,
291 &bytes_written, err);
292 if (status != G_IO_STATUS_NORMAL)
295 if (bytes_written != obex->tx_data)
298 g_obex_dump("<", buf, bytes_written);
300 obex->tx_sent += bytes_written;
301 obex->tx_data -= bytes_written;
306 static gboolean write_data(GIOChannel *io, GIOCondition cond,
309 GObex *obex = user_data;
311 if (cond & G_IO_NVAL)
314 if (cond & (G_IO_HUP | G_IO_ERR))
317 if (obex->tx_data == 0) {
318 struct pending_pkt *p = g_queue_pop_head(obex->tx_queue);
324 if (g_obex_srm_active(obex))
327 /* Can't send a request while there's a pending one */
328 if (obex->pending_req && p->id > 0) {
329 g_queue_push_head(obex->tx_queue, p);
334 len = g_obex_packet_encode(p->pkt, obex->tx_buf, obex->tx_mtu);
335 if (len == -EAGAIN) {
336 g_queue_push_head(obex->tx_queue, p);
337 g_obex_suspend(obex);
347 if (obex->pending_req != NULL)
348 pending_pkt_free(obex->pending_req);
349 obex->pending_req = p;
350 p->timeout_id = g_timeout_add_seconds(p->timeout,
359 if (obex->suspended) {
360 obex->write_source = 0;
364 if (!obex->write(obex, NULL))
368 if (obex->tx_data > 0 || g_queue_get_length(obex->tx_queue) > 0)
372 obex->rx_last_op = G_OBEX_OP_NONE;
374 obex->write_source = 0;
378 static void enable_tx(GObex *obex)
385 if (obex->write_source > 0)
388 cond = G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
389 obex->write_source = g_io_add_watch(obex->io, cond, write_data, obex);
392 static gboolean g_obex_send_internal(GObex *obex, struct pending_pkt *p,
396 if (obex->io == NULL) {
397 g_set_error(err, G_OBEX_ERROR, G_OBEX_ERROR_DISCONNECTED,
398 "The transport is not connected");
399 g_obex_debug(G_OBEX_DEBUG_ERROR, "%s", (*err)->message);
403 if (g_obex_packet_get_operation(p->pkt, NULL) == G_OBEX_OP_ABORT)
404 g_queue_push_head(obex->tx_queue, p);
406 g_queue_push_tail(obex->tx_queue, p);
408 if (obex->pending_req == NULL || p->id == 0)
414 static void init_connect_data(GObex *obex, struct connect_data *data)
418 memset(data, 0, sizeof(*data));
420 data->version = 0x10;
423 u16 = g_htons(obex->rx_mtu);
424 memcpy(&data->mtu, &u16, sizeof(u16));
427 static void prepare_connect_rsp(GObex *obex, GObexPacket *rsp)
430 struct connect_data data;
431 static guint32 next_connid = 1;
433 init_connect_data(obex, &data);
434 g_obex_packet_set_data(rsp, &data, sizeof(data), G_OBEX_DATA_COPY);
436 connid = g_obex_packet_get_header(rsp, G_OBEX_HDR_CONNECTION);
437 if (connid != NULL) {
438 g_obex_header_get_uint32(connid, &obex->conn_id);
442 obex->conn_id = next_connid++;
444 connid = g_obex_header_new_uint32(G_OBEX_HDR_CONNECTION,
446 g_obex_packet_prepend_header(rsp, connid);
449 static void set_srmp(GObex *obex, guint8 srmp, gboolean outgoing)
451 struct srm_config *config = obex->srm;
456 /* Dont't reset if direction doesn't match */
457 if (srmp > G_OBEX_SRMP_NEXT_WAIT && config->outgoing != outgoing)
461 config->outgoing = outgoing;
464 static void set_srm(GObex *obex, guint8 op, guint8 srm)
466 struct srm_config *config = obex->srm;
469 if (config == NULL) {
470 if (srm == G_OBEX_SRM_DISABLE)
473 config = g_new0(struct srm_config, 1);
480 /* Indicate response, treat it as request */
481 if (config->srm == G_OBEX_SRM_INDICATE) {
482 if (srm != G_OBEX_SRM_ENABLE)
488 enable = (srm == G_OBEX_SRM_ENABLE);
489 if (config->enabled == enable)
492 config->enabled = enable;
494 g_obex_debug(G_OBEX_DEBUG_COMMAND, "SRM %s", config->enabled ?
495 "Enabled" : "Disabled");
505 static void setup_srm(GObex *obex, GObexPacket *pkt, gboolean outgoing)
514 op = g_obex_packet_get_operation(pkt, &final);
516 hdr = g_obex_packet_get_header(pkt, G_OBEX_HDR_SRM);
519 g_obex_header_get_uint8(hdr, &srm);
520 g_obex_debug(G_OBEX_DEBUG_COMMAND, "srm 0x%02x", srm);
521 set_srm(obex, op, srm);
524 hdr = g_obex_packet_get_header(pkt, G_OBEX_HDR_SRMP);
527 g_obex_header_get_uint8(hdr, &srmp);
528 g_obex_debug(G_OBEX_DEBUG_COMMAND, "srmp 0x%02x", srmp);
529 set_srmp(obex, srmp, outgoing);
531 set_srmp(obex, -1, outgoing);
533 if (obex->srm == NULL || !obex->srm->enabled || !final)
536 switch (obex->srm->op) {
537 case G_OBEX_OP_CONNECT:
540 if (op <= G_OBEX_RSP_CONTINUE)
544 set_srm(obex, op, G_OBEX_SRM_DISABLE);
547 static void prepare_srm_rsp(GObex *obex, GObexPacket *pkt)
551 if (!obex->use_srm || obex->srm == NULL)
554 if (obex->srm->enabled)
557 hdr = g_obex_packet_get_header(pkt, G_OBEX_HDR_SRM);
561 hdr = g_obex_header_new_uint8(G_OBEX_HDR_SRM, G_OBEX_SRM_ENABLE);
562 g_obex_packet_prepend_header(pkt, hdr);
565 gboolean g_obex_send(GObex *obex, GObexPacket *pkt, GError **err)
567 struct pending_pkt *p;
570 g_obex_debug(G_OBEX_DEBUG_COMMAND, "conn %u", obex->conn_id);
572 if (obex == NULL || pkt == NULL) {
573 g_set_error(err, G_OBEX_ERROR, G_OBEX_ERROR_INVALID_ARGS,
574 "Invalid arguments");
575 g_obex_debug(G_OBEX_DEBUG_ERROR, "%s", (*err)->message);
579 switch (obex->rx_last_op) {
580 case G_OBEX_OP_CONNECT:
581 prepare_connect_rsp(obex, pkt);
584 prepare_srm_rsp(obex, pkt);
588 setup_srm(obex, pkt, TRUE);
590 p = g_new0(struct pending_pkt, 1);
593 ret = g_obex_send_internal(obex, p, err);
600 static void prepare_srm_req(GObex *obex, GObexPacket *pkt)
607 if (obex->srm != NULL && obex->srm->enabled)
610 hdr = g_obex_packet_get_header(pkt, G_OBEX_HDR_SRM);
614 hdr = g_obex_header_new_uint8(G_OBEX_HDR_SRM, G_OBEX_SRM_ENABLE);
615 g_obex_packet_prepend_header(pkt, hdr);
618 guint g_obex_send_req(GObex *obex, GObexPacket *req, gint timeout,
619 GObexResponseFunc func, gpointer user_data,
623 struct pending_pkt *p;
627 g_obex_debug(G_OBEX_DEBUG_COMMAND, "conn %u", obex->conn_id);
629 op = g_obex_packet_get_operation(req, NULL);
630 if (op == G_OBEX_OP_PUT || op == G_OBEX_OP_GET) {
631 /* Only enable SRM automatically for GET and PUT */
632 prepare_srm_req(obex, req);
635 setup_srm(obex, req, TRUE);
637 if (obex->conn_id == CONNID_INVALID)
640 if (obex->rx_last_op == G_OBEX_RSP_CONTINUE)
643 if (g_obex_srm_active(obex) && obex->pending_req != NULL)
646 hdr = g_obex_packet_get_header(req, G_OBEX_HDR_CONNECTION);
650 hdr = g_obex_header_new_uint32(G_OBEX_HDR_CONNECTION, obex->conn_id);
651 g_obex_packet_prepend_header(req, hdr);
654 p = g_new0(struct pending_pkt, 1);
659 p->rsp_data = user_data;
662 p->timeout = G_OBEX_DEFAULT_TIMEOUT;
664 p->timeout = timeout;
666 if (!g_obex_send_internal(obex, p, err)) {
674 static gint pending_pkt_cmp(gconstpointer a, gconstpointer b)
676 const struct pending_pkt *p = a;
677 guint id = GPOINTER_TO_UINT(b);
682 static gboolean pending_req_abort(GObex *obex, GError **err)
684 struct pending_pkt *p = obex->pending_req;
692 g_source_remove(p->timeout_id);
693 p->timeout = G_OBEX_ABORT_TIMEOUT;
694 p->timeout_id = g_timeout_add_seconds(p->timeout, req_timeout, obex);
696 req = g_obex_packet_new(G_OBEX_OP_ABORT, TRUE, G_OBEX_HDR_INVALID);
698 return g_obex_send(obex, req, err);
701 static gboolean cancel_complete(gpointer user_data)
703 struct pending_pkt *p = user_data;
704 GObex *obex = p->obex;
707 g_assert(p->rsp_func != NULL);
709 err = g_error_new(G_OBEX_ERROR, G_OBEX_ERROR_CANCELLED,
710 "The request was cancelled");
711 p->rsp_func(obex, err, NULL, p->rsp_data);
720 gboolean g_obex_cancel_req(GObex *obex, guint req_id, gboolean remove_callback)
723 struct pending_pkt *p;
725 if (obex->pending_req && obex->pending_req->id == req_id) {
726 if (!pending_req_abort(obex, NULL)) {
727 p = obex->pending_req;
728 obex->pending_req = NULL;
729 goto immediate_completion;
735 match = g_queue_find_custom(obex->tx_queue, GUINT_TO_POINTER(req_id),
742 g_queue_delete_link(obex->tx_queue, match);
744 immediate_completion:
746 p->obex = g_obex_ref(obex);
748 if (remove_callback || p->rsp_func == NULL)
751 g_idle_add(cancel_complete, p);
756 gboolean g_obex_send_rsp(GObex *obex, guint8 rspcode, GError **err,
757 guint8 first_hdr_type, ...)
762 va_start(args, first_hdr_type);
763 rsp = g_obex_packet_new_valist(rspcode, TRUE, first_hdr_type, args);
766 return g_obex_send(obex, rsp, err);
769 void g_obex_set_disconnect_function(GObex *obex, GObexFunc func,
772 obex->disconn_func = func;
773 obex->disconn_func_data = user_data;
776 static gint req_handler_cmpop(gconstpointer a, gconstpointer b)
778 const struct req_handler *handler = a;
779 guint opcode = GPOINTER_TO_UINT(b);
781 return (gint) handler->opcode - (gint) opcode;
784 static gint req_handler_cmpid(gconstpointer a, gconstpointer b)
786 const struct req_handler *handler = a;
787 guint id = GPOINTER_TO_UINT(b);
789 return (gint) handler->id - (gint) id;
792 guint g_obex_add_request_function(GObex *obex, guint8 opcode,
793 GObexRequestFunc func,
796 struct req_handler *handler;
797 static guint next_id = 1;
799 handler = g_new0(struct req_handler, 1);
800 handler->id = next_id++;
801 handler->opcode = opcode;
802 handler->func = func;
803 handler->user_data = user_data;
805 obex->req_handlers = g_slist_prepend(obex->req_handlers, handler);
810 gboolean g_obex_remove_request_function(GObex *obex, guint id)
812 struct req_handler *handler;
815 match = g_slist_find_custom(obex->req_handlers, GUINT_TO_POINTER(id),
820 handler = match->data;
822 obex->req_handlers = g_slist_delete_link(obex->req_handlers, match);
828 void g_obex_suspend(GObex *obex)
830 g_obex_debug(G_OBEX_DEBUG_COMMAND, "conn %u", obex->conn_id);
832 if (obex->write_source > 0) {
833 g_source_remove(obex->write_source);
834 obex->write_source = 0;
837 obex->suspended = TRUE;
840 void g_obex_resume(GObex *obex)
842 g_obex_debug(G_OBEX_DEBUG_COMMAND, "conn %u", obex->conn_id);
844 obex->suspended = FALSE;
846 if (g_queue_get_length(obex->tx_queue) > 0 || obex->tx_data > 0)
850 gboolean g_obex_srm_active(GObex *obex)
852 gboolean ret = FALSE;
854 if (obex->srm == NULL || !obex->srm->enabled)
857 if (obex->srm->srmp <= G_OBEX_SRMP_NEXT_WAIT)
862 g_obex_debug(G_OBEX_DEBUG_COMMAND, "%s", ret ? "yes" : "no");
866 static void parse_connect_data(GObex *obex, GObexPacket *pkt)
868 const struct connect_data *data;
873 data = g_obex_packet_get_data(pkt, &data_len);
874 if (data == NULL || data_len != sizeof(*data))
877 memcpy(&u16, &data->mtu, sizeof(u16));
879 obex->tx_mtu = g_ntohs(u16);
880 if (obex->io_tx_mtu > 0 && obex->tx_mtu > obex->io_tx_mtu)
881 obex->tx_mtu = obex->io_tx_mtu;
882 obex->tx_buf = g_realloc(obex->tx_buf, obex->tx_mtu);
884 connid = g_obex_packet_get_header(pkt, G_OBEX_HDR_CONNECTION);
886 g_obex_header_get_uint32(connid, &obex->conn_id);
889 static gboolean parse_response(GObex *obex, GObexPacket *rsp)
891 struct pending_pkt *p = obex->pending_req;
892 guint8 opcode, rspcode;
895 rspcode = g_obex_packet_get_operation(rsp, &final);
897 opcode = g_obex_packet_get_operation(p->pkt, NULL);
898 if (opcode == G_OBEX_OP_CONNECT)
899 parse_connect_data(obex, rsp);
901 setup_srm(obex, rsp, FALSE);
903 if (!g_obex_srm_active(obex))
907 * Resposes have final bit set but in case of GET with SRM
908 * we should not remove the request since the remote side will
909 * continue sending responses until the transfer is finished
911 if (opcode == G_OBEX_OP_GET && rspcode == G_OBEX_RSP_CONTINUE) {
912 g_source_remove(p->timeout_id);
913 p->timeout_id = g_timeout_add_seconds(p->timeout, req_timeout,
921 static void handle_response(GObex *obex, GError *err, GObexPacket *rsp)
923 struct pending_pkt *p = obex->pending_req;
924 gboolean disconn = err ? TRUE : FALSE, final_rsp = TRUE;
927 final_rsp = parse_response(obex, rsp);
930 err = g_error_new(G_OBEX_ERROR, G_OBEX_ERROR_CANCELLED,
931 "The operation was cancelled");
934 g_obex_debug(G_OBEX_DEBUG_ERROR, "%s", err->message);
937 p->rsp_func(obex, err, rsp, p->rsp_data);
939 /* Check if user callback removed the request */
940 if (p != obex->pending_req)
949 obex->pending_req = NULL;
952 if (!disconn && g_queue_get_length(obex->tx_queue) > 0)
956 static gboolean check_connid(GObex *obex, GObexPacket *pkt)
961 if (obex->conn_id == CONNID_INVALID)
964 hdr = g_obex_packet_get_header(pkt, G_OBEX_HDR_CONNECTION);
968 g_obex_header_get_uint32(hdr, &id);
970 return obex->conn_id == id;
973 static int parse_request(GObex *obex, GObexPacket *req)
978 op = g_obex_packet_get_operation(req, &final);
980 case G_OBEX_OP_CONNECT:
981 parse_connect_data(obex, req);
983 case G_OBEX_OP_ABORT:
986 if (check_connid(obex, req))
989 return -G_OBEX_RSP_SERVICE_UNAVAILABLE;
992 setup_srm(obex, req, FALSE);
997 static void handle_request(GObex *obex, GObexPacket *req)
1002 op = parse_request(obex, req);
1006 match = g_slist_find_custom(obex->req_handlers, GUINT_TO_POINTER(op),
1009 struct req_handler *handler = match->data;
1010 handler->func(obex, req, handler->user_data);
1014 op = -G_OBEX_RSP_NOT_IMPLEMENTED;
1017 g_obex_debug(G_OBEX_DEBUG_ERROR, "%s", g_obex_strerror(-op));
1018 g_obex_send_rsp(obex, -op, NULL, G_OBEX_HDR_INVALID);
1021 static gboolean read_stream(GObex *obex, GError **err)
1023 GIOChannel *io = obex->io;
1025 gsize rbytes, toread;
1029 if (obex->rx_data >= 3)
1033 toread = 3 - obex->rx_data;
1034 buf = (gchar *) &obex->rx_buf[obex->rx_data];
1036 status = g_io_channel_read_chars(io, buf, toread, &rbytes, NULL);
1037 if (status != G_IO_STATUS_NORMAL)
1040 obex->rx_data += rbytes;
1041 if (obex->rx_data < 3)
1044 memcpy(&u16, &buf[1], sizeof(u16));
1045 obex->rx_pkt_len = g_ntohs(u16);
1047 if (obex->rx_pkt_len > obex->rx_mtu) {
1048 g_set_error(err, G_OBEX_ERROR, G_OBEX_ERROR_PARSE_ERROR,
1049 "Too big incoming packet");
1050 g_obex_debug(G_OBEX_DEBUG_ERROR, "%s", (*err)->message);
1055 if (obex->rx_data >= obex->rx_pkt_len)
1059 toread = obex->rx_pkt_len - obex->rx_data;
1060 buf = (gchar *) &obex->rx_buf[obex->rx_data];
1062 status = g_io_channel_read_chars(io, buf, toread, &rbytes, NULL);
1063 if (status != G_IO_STATUS_NORMAL)
1066 obex->rx_data += rbytes;
1067 } while (rbytes > 0 && obex->rx_data < obex->rx_pkt_len);
1070 g_obex_dump(">", obex->rx_buf, obex->rx_data);
1075 static gboolean read_packet(GObex *obex, GError **err)
1077 GIOChannel *io = obex->io;
1078 GError *read_err = NULL;
1083 if (obex->rx_data > 0) {
1084 g_set_error(err, G_OBEX_ERROR, G_OBEX_ERROR_PARSE_ERROR,
1085 "RX buffer not empty before reading packet");
1089 status = g_io_channel_read_chars(io, (gchar *) obex->rx_buf,
1090 obex->rx_mtu, &rbytes, &read_err);
1091 if (status != G_IO_STATUS_NORMAL) {
1092 g_set_error(err, G_OBEX_ERROR, G_OBEX_ERROR_PARSE_ERROR,
1093 "Unable to read data: %s", read_err->message);
1094 g_error_free(read_err);
1098 obex->rx_data += rbytes;
1101 g_set_error(err, G_OBEX_ERROR, G_OBEX_ERROR_PARSE_ERROR,
1102 "Incomplete packet received");
1106 memcpy(&u16, &obex->rx_buf[1], sizeof(u16));
1107 obex->rx_pkt_len = g_ntohs(u16);
1109 if (obex->rx_pkt_len != rbytes) {
1110 g_set_error(err, G_OBEX_ERROR, G_OBEX_ERROR_PARSE_ERROR,
1111 "Data size doesn't match packet size (%zu != %u)",
1112 rbytes, obex->rx_pkt_len);
1116 g_obex_dump(">", obex->rx_buf, obex->rx_data);
1120 g_obex_debug(G_OBEX_DEBUG_ERROR, "%s", (*err)->message);
1124 static gboolean incoming_data(GIOChannel *io, GIOCondition cond,
1127 GObex *obex = user_data;
1129 ssize_t header_offset;
1133 if (cond & G_IO_NVAL)
1136 if (cond & (G_IO_HUP | G_IO_ERR)) {
1137 err = g_error_new(G_OBEX_ERROR, G_OBEX_ERROR_DISCONNECTED,
1138 "Transport got disconnected");
1142 if (!obex->read(obex, &err))
1145 if (obex->rx_data < 3 || obex->rx_data < obex->rx_pkt_len)
1148 obex->rx_last_op = obex->rx_buf[0] & ~FINAL_BIT;
1150 if (obex->pending_req) {
1151 struct pending_pkt *p = obex->pending_req;
1152 opcode = g_obex_packet_get_operation(p->pkt, NULL);
1153 header_offset = rsp_header_offset(opcode);
1155 opcode = obex->rx_last_op;
1156 /* Unexpected response -- fail silently */
1158 if (opcode > 0x1f && opcode != G_OBEX_OP_ABORT) {
1160 if (opcode > 0x1f && opcode < 0xff) {
1165 header_offset = req_header_offset(opcode);
1168 if (header_offset < 0) {
1169 err = g_error_new(G_OBEX_ERROR, G_OBEX_ERROR_PARSE_ERROR,
1170 "Unknown header offset for opcode 0x%02x",
1175 pkt = g_obex_packet_decode(obex->rx_buf, obex->rx_data, header_offset,
1176 G_OBEX_DATA_REF, &err);
1180 /* Protect against user callback freeing the object */
1183 if (obex->pending_req)
1184 handle_response(obex, NULL, pkt);
1186 handle_request(obex, pkt);
1196 g_obex_packet_free(pkt);
1202 g_obex_debug(G_OBEX_DEBUG_ERROR, "%s", err->message);
1204 g_io_channel_unref(obex->io);
1206 obex->io_source = 0;
1209 /* Protect against user callback freeing the object */
1212 if (obex->pending_req)
1213 handle_response(obex, err, NULL);
1215 if (obex->disconn_func)
1216 obex->disconn_func(obex, err, obex->disconn_func_data);
1225 static GDebugKey keys[] = {
1226 { "error", G_OBEX_DEBUG_ERROR },
1227 { "command", G_OBEX_DEBUG_COMMAND },
1228 { "transfer", G_OBEX_DEBUG_TRANSFER },
1229 { "header", G_OBEX_DEBUG_HEADER },
1230 { "packet", G_OBEX_DEBUG_PACKET },
1231 { "data", G_OBEX_DEBUG_DATA },
1234 GObex *g_obex_new(GIOChannel *io, GObexTransportType transport_type,
1235 gssize io_rx_mtu, gssize io_tx_mtu)
1240 if (gobex_debug == 0) {
1241 const char *env = g_getenv("GOBEX_DEBUG");
1243 gobex_debug = g_parse_debug_string(env, keys, 6);
1245 gobex_debug = G_OBEX_DEBUG_NONE;
1248 g_obex_debug(G_OBEX_DEBUG_COMMAND, "");
1253 if (io_rx_mtu >= 0 && io_rx_mtu < G_OBEX_MINIMUM_MTU)
1256 if (io_tx_mtu >= 0 && io_tx_mtu < G_OBEX_MINIMUM_MTU)
1259 obex = g_new0(GObex, 1);
1261 obex->io = g_io_channel_ref(io);
1262 obex->ref_count = 1;
1263 obex->conn_id = CONNID_INVALID;
1264 obex->rx_last_op = G_OBEX_OP_NONE;
1266 obex->io_rx_mtu = io_rx_mtu;
1267 obex->io_tx_mtu = io_tx_mtu;
1269 if (io_rx_mtu > G_OBEX_MAXIMUM_MTU)
1270 obex->rx_mtu = G_OBEX_MAXIMUM_MTU;
1271 else if (io_rx_mtu < G_OBEX_MINIMUM_MTU)
1272 obex->rx_mtu = G_OBEX_DEFAULT_MTU;
1274 obex->rx_mtu = io_rx_mtu;
1276 obex->tx_mtu = G_OBEX_MINIMUM_MTU;
1278 obex->tx_queue = g_queue_new();
1279 obex->rx_buf = g_malloc(obex->rx_mtu);
1280 obex->tx_buf = g_malloc(obex->tx_mtu);
1282 switch (transport_type) {
1283 case G_OBEX_TRANSPORT_STREAM:
1284 obex->read = read_stream;
1285 obex->write = write_stream;
1287 case G_OBEX_TRANSPORT_PACKET:
1288 obex->use_srm = TRUE;
1289 obex->read = read_packet;
1290 obex->write = write_packet;
1297 g_io_channel_set_encoding(io, NULL, NULL);
1298 g_io_channel_set_buffered(io, FALSE);
1299 cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
1300 obex->io_source = g_io_add_watch(io, cond, incoming_data, obex);
1305 GObex *g_obex_ref(GObex *obex)
1310 g_atomic_int_inc(&obex->ref_count);
1312 g_obex_debug(G_OBEX_DEBUG_COMMAND, "ref %u", obex->ref_count);
1317 void g_obex_unref(GObex *obex)
1320 gboolean last_ref, ret;
1325 last_ref = g_atomic_int_dec_and_test(&obex->ref_count);
1327 g_obex_debug(G_OBEX_DEBUG_COMMAND, "ref %u", obex->ref_count);
1332 g_slist_free_full(obex->req_handlers, g_free);
1336 ret = write_data(obex->io, G_IO_OUT, obex);
1337 if (obex->pending_req && obex->pending_req->cancelled)
1342 g_queue_foreach(obex->tx_queue, (GFunc) pending_pkt_free, NULL);
1343 g_queue_free(obex->tx_queue);
1345 if (obex->io != NULL)
1346 g_io_channel_unref(obex->io);
1348 if (obex->io_source > 0)
1349 g_source_remove(obex->io_source);
1351 if (obex->write_source > 0)
1352 g_source_remove(obex->write_source);
1354 g_free(obex->rx_buf);
1355 g_free(obex->tx_buf);
1358 if (obex->pending_req)
1359 pending_pkt_free(obex->pending_req);
1364 /* Higher level functions */
1366 guint g_obex_connect(GObex *obex, GObexResponseFunc func, gpointer user_data,
1367 GError **err, guint8 first_hdr_id, ...)
1370 struct connect_data data;
1373 g_obex_debug(G_OBEX_DEBUG_COMMAND, "");
1375 va_start(args, first_hdr_id);
1376 req = g_obex_packet_new_valist(G_OBEX_OP_CONNECT, TRUE,
1377 first_hdr_id, args);
1380 init_connect_data(obex, &data);
1381 g_obex_packet_set_data(req, &data, sizeof(data), G_OBEX_DATA_COPY);
1383 return g_obex_send_req(obex, req, -1, func, user_data, err);
1386 guint g_obex_setpath(GObex *obex, const char *path, GObexResponseFunc func,
1387 gpointer user_data, GError **err)
1390 struct setpath_data data;
1393 g_obex_debug(G_OBEX_DEBUG_COMMAND, "conn %u", obex->conn_id);
1395 req = g_obex_packet_new(G_OBEX_OP_SETPATH, TRUE, G_OBEX_HDR_INVALID);
1397 memset(&data, 0, sizeof(data));
1399 if (path != NULL && strncmp("..", path, 2) == 0) {
1401 folder = (path[2] == '/') ? &path[3] : NULL;
1407 if (folder != NULL) {
1409 hdr = g_obex_header_new_unicode(G_OBEX_HDR_NAME, folder);
1410 g_obex_packet_add_header(req, hdr);
1413 g_obex_packet_set_data(req, &data, sizeof(data), G_OBEX_DATA_COPY);
1415 return g_obex_send_req(obex, req, -1, func, user_data, err);
1418 guint g_obex_mkdir(GObex *obex, const char *path, GObexResponseFunc func,
1419 gpointer user_data, GError **err)
1422 struct setpath_data data;
1424 g_obex_debug(G_OBEX_DEBUG_COMMAND, "conn %u", obex->conn_id);
1426 req = g_obex_packet_new(G_OBEX_OP_SETPATH, TRUE, G_OBEX_HDR_NAME, path,
1427 G_OBEX_HDR_INVALID);
1429 memset(&data, 0, sizeof(data));
1430 g_obex_packet_set_data(req, &data, sizeof(data), G_OBEX_DATA_COPY);
1432 return g_obex_send_req(obex, req, -1, func, user_data, err);
1435 guint g_obex_delete(GObex *obex, const char *name, GObexResponseFunc func,
1436 gpointer user_data, GError **err)
1440 g_obex_debug(G_OBEX_DEBUG_COMMAND, "conn %u", obex->conn_id);
1442 req = g_obex_packet_new(G_OBEX_OP_PUT, TRUE, G_OBEX_HDR_NAME, name,
1443 G_OBEX_HDR_INVALID);
1445 return g_obex_send_req(obex, req, -1, func, user_data, err);
1448 guint g_obex_copy(GObex *obex, const char *name, const char *dest,
1449 GObexResponseFunc func, gpointer user_data,
1454 g_obex_debug(G_OBEX_DEBUG_COMMAND, "conn %u", obex->conn_id);
1456 req = g_obex_packet_new(G_OBEX_OP_ACTION, TRUE,
1457 G_OBEX_HDR_ACTION, G_OBEX_ACTION_COPY,
1458 G_OBEX_HDR_NAME, name,
1459 G_OBEX_HDR_DESTNAME, dest,
1460 G_OBEX_HDR_INVALID);
1462 return g_obex_send_req(obex, req, -1, func, user_data, err);
1465 guint g_obex_move(GObex *obex, const char *name, const char *dest,
1466 GObexResponseFunc func, gpointer user_data,
1471 g_obex_debug(G_OBEX_DEBUG_COMMAND, "conn %u", obex->conn_id);
1473 req = g_obex_packet_new(G_OBEX_OP_ACTION, TRUE,
1474 G_OBEX_HDR_ACTION, G_OBEX_ACTION_MOVE,
1475 G_OBEX_HDR_NAME, name,
1476 G_OBEX_HDR_DESTNAME, dest,
1477 G_OBEX_HDR_INVALID);
1479 return g_obex_send_req(obex, req, -1, func, user_data, err);