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 "gobex-debug.h"
33 #define G_OBEX_DEFAULT_MTU 4096
34 #define G_OBEX_MINIMUM_MTU 255
35 #define G_OBEX_MAXIMUM_MTU 65535
37 #define G_OBEX_DEFAULT_TIMEOUT 10
38 #define G_OBEX_ABORT_TIMEOUT 5
40 #define G_OBEX_OP_NONE 0xff
42 #define FINAL_BIT 0x80
44 #define CONNID_INVALID 0xffffffff
46 guint gobex_debug = 0;
61 gboolean (*read) (GObex *obex, GError **err);
62 gboolean (*write) (GObex *obex, GError **err);
76 struct srm_config *srm;
92 GObexFunc disconn_func;
93 gpointer disconn_func_data;
95 struct pending_pkt *pending_req;
104 GObexResponseFunc rsp_func;
112 GObexRequestFunc func;
116 struct connect_data {
120 } __attribute__ ((packed));
122 struct setpath_data {
125 } __attribute__ ((packed));
127 static struct error_code {
131 { G_OBEX_RSP_CONTINUE, "Continue" },
132 { G_OBEX_RSP_SUCCESS, "Success" },
133 { G_OBEX_RSP_CREATED, "Created" },
134 { G_OBEX_RSP_ACCEPTED, "Accepted" },
135 { G_OBEX_RSP_NON_AUTHORITATIVE, "Non Authoritative" },
136 { G_OBEX_RSP_NO_CONTENT, "No Content" },
137 { G_OBEX_RSP_RESET_CONTENT, "Reset Content" },
138 { G_OBEX_RSP_PARTIAL_CONTENT, "Partial Content" },
139 { G_OBEX_RSP_MULTIPLE_CHOICES, "Multiple Choices" },
140 { G_OBEX_RSP_MOVED_PERMANENTLY, "Moved Permanently" },
141 { G_OBEX_RSP_MOVED_TEMPORARILY, "Moved Temporarily" },
142 { G_OBEX_RSP_SEE_OTHER, "See Other" },
143 { G_OBEX_RSP_NOT_MODIFIED, "Not Modified" },
144 { G_OBEX_RSP_USE_PROXY, "Use Proxy" },
145 { G_OBEX_RSP_BAD_REQUEST, "Bad Request" },
146 { G_OBEX_RSP_UNAUTHORIZED, "Unauthorized" },
147 { G_OBEX_RSP_PAYMENT_REQUIRED, "Payment Required" },
148 { G_OBEX_RSP_FORBIDDEN, "Forbidden" },
149 { G_OBEX_RSP_NOT_FOUND, "Not Found" },
150 { G_OBEX_RSP_METHOD_NOT_ALLOWED, "Method Not Allowed" },
151 { G_OBEX_RSP_NOT_ACCEPTABLE, "Not Acceptable" },
152 { G_OBEX_RSP_PROXY_AUTH_REQUIRED, "Proxy Authentication Required" },
153 { G_OBEX_RSP_REQUEST_TIME_OUT, "Request Time Out" },
154 { G_OBEX_RSP_CONFLICT, "Conflict" },
155 { G_OBEX_RSP_GONE, "Gone" },
156 { G_OBEX_RSP_LENGTH_REQUIRED, "Length Required" },
157 { G_OBEX_RSP_PRECONDITION_FAILED, "Precondition Failed" },
158 { G_OBEX_RSP_REQ_ENTITY_TOO_LARGE, "Request Entity Too Large" },
159 { G_OBEX_RSP_REQ_URL_TOO_LARGE, "Request URL Too Large" },
160 { G_OBEX_RSP_UNSUPPORTED_MEDIA_TYPE, "Unsupported Media Type" },
161 { G_OBEX_RSP_INTERNAL_SERVER_ERROR, "Internal Server Error" },
162 { G_OBEX_RSP_NOT_IMPLEMENTED, "Not Implemented" },
163 { G_OBEX_RSP_BAD_GATEWAY, "Bad Gateway" },
164 { G_OBEX_RSP_SERVICE_UNAVAILABLE, "Service Unavailable" },
165 { G_OBEX_RSP_GATEWAY_TIMEOUT, "Gateway Timeout" },
166 { G_OBEX_RSP_VERSION_NOT_SUPPORTED, "Version Not Supported" },
167 { G_OBEX_RSP_DATABASE_FULL, "Database Full" },
168 { G_OBEX_RSP_DATABASE_LOCKED, "Database Locked" },
172 const char *g_obex_strerror(guint8 err_code)
174 struct error_code *error;
176 for (error = obex_errors; error->name != NULL; error++) {
177 if (error->code == err_code)
184 static ssize_t req_header_offset(guint8 opcode)
187 case G_OBEX_OP_CONNECT:
188 return sizeof(struct connect_data);
189 case G_OBEX_OP_SETPATH:
190 return sizeof(struct setpath_data);
191 case G_OBEX_OP_DISCONNECT:
194 case G_OBEX_OP_SESSION:
195 case G_OBEX_OP_ABORT:
196 case G_OBEX_OP_ACTION:
203 static ssize_t rsp_header_offset(guint8 opcode)
206 case G_OBEX_OP_CONNECT:
207 return sizeof(struct connect_data);
208 case G_OBEX_OP_SETPATH:
209 case G_OBEX_OP_DISCONNECT:
212 case G_OBEX_OP_SESSION:
213 case G_OBEX_OP_ABORT:
214 case G_OBEX_OP_ACTION:
221 static void pending_pkt_free(struct pending_pkt *p)
224 g_obex_unref(p->obex);
226 if (p->timeout_id > 0)
227 g_source_remove(p->timeout_id);
229 g_obex_packet_free(p->pkt);
234 static gboolean req_timeout(gpointer user_data)
236 GObex *obex = user_data;
237 struct pending_pkt *p = obex->pending_req;
242 obex->pending_req = NULL;
244 err = g_error_new(G_OBEX_ERROR, G_OBEX_ERROR_TIMEOUT,
245 "Timed out waiting for response");
247 g_obex_debug(G_OBEX_DEBUG_ERROR, "%s", err->message);
250 p->rsp_func(obex, err, NULL, p->rsp_data);
258 static gboolean write_stream(GObex *obex, GError **err)
264 buf = (gchar *) &obex->tx_buf[obex->tx_sent];
265 status = g_io_channel_write_chars(obex->io, buf, obex->tx_data,
266 &bytes_written, err);
267 if (status != G_IO_STATUS_NORMAL)
270 g_obex_dump("<", buf, bytes_written);
272 obex->tx_sent += bytes_written;
273 obex->tx_data -= bytes_written;
278 static gboolean write_packet(GObex *obex, GError **err)
284 buf = (gchar *) &obex->tx_buf[obex->tx_sent];
285 status = g_io_channel_write_chars(obex->io, buf, obex->tx_data,
286 &bytes_written, err);
287 if (status != G_IO_STATUS_NORMAL)
290 if (bytes_written != obex->tx_data)
293 g_obex_dump("<", buf, bytes_written);
295 obex->tx_sent += bytes_written;
296 obex->tx_data -= bytes_written;
301 static void set_srmp(GObex *obex, guint8 srmp, gboolean outgoing)
303 struct srm_config *config = obex->srm;
308 /* Dont't reset if direction doesn't match */
309 if (srmp > G_OBEX_SRMP_NEXT_WAIT && config->outgoing != outgoing)
313 config->outgoing = outgoing;
316 static void set_srm(GObex *obex, guint8 op, guint8 srm)
318 struct srm_config *config = obex->srm;
321 if (config == NULL) {
322 if (srm == G_OBEX_SRM_DISABLE)
325 config = g_new0(struct srm_config, 1);
332 /* Indicate response, treat it as request */
333 if (config->srm == G_OBEX_SRM_INDICATE) {
334 if (srm != G_OBEX_SRM_ENABLE)
340 enable = (srm == G_OBEX_SRM_ENABLE);
341 if (config->enabled == enable)
344 config->enabled = enable;
346 g_obex_debug(G_OBEX_DEBUG_COMMAND, "SRM %s", config->enabled ?
347 "Enabled" : "Disabled");
357 static void check_srm_final(GObex *obex, guint8 op)
359 if (obex->srm == NULL || !obex->srm->enabled)
362 switch (obex->srm->op) {
363 case G_OBEX_OP_CONNECT:
366 if (op <= G_OBEX_RSP_CONTINUE)
370 set_srm(obex, op, G_OBEX_SRM_DISABLE);
373 static void setup_srm(GObex *obex, GObexPacket *pkt, gboolean outgoing)
382 op = g_obex_packet_get_operation(pkt, &final);
384 hdr = g_obex_packet_get_header(pkt, G_OBEX_HDR_SRM);
387 g_obex_header_get_uint8(hdr, &srm);
388 g_obex_debug(G_OBEX_DEBUG_COMMAND, "srm 0x%02x", srm);
389 set_srm(obex, op, srm);
392 hdr = g_obex_packet_get_header(pkt, G_OBEX_HDR_SRMP);
395 g_obex_header_get_uint8(hdr, &srmp);
396 g_obex_debug(G_OBEX_DEBUG_COMMAND, "srmp 0x%02x", srmp);
397 set_srmp(obex, srmp, outgoing);
399 set_srmp(obex, -1, outgoing);
402 check_srm_final(obex, op);
405 static gboolean write_data(GIOChannel *io, GIOCondition cond,
408 GObex *obex = user_data;
410 if (cond & G_IO_NVAL)
413 if (cond & (G_IO_HUP | G_IO_ERR))
416 if (obex->tx_data == 0) {
417 struct pending_pkt *p = g_queue_pop_head(obex->tx_queue);
423 setup_srm(obex, p->pkt, TRUE);
425 if (g_obex_srm_active(obex))
428 /* Can't send a request while there's a pending one */
429 if (obex->pending_req && p->id > 0) {
430 g_queue_push_head(obex->tx_queue, p);
435 len = g_obex_packet_encode(p->pkt, obex->tx_buf, obex->tx_mtu);
436 if (len == -EAGAIN) {
437 g_queue_push_head(obex->tx_queue, p);
438 g_obex_suspend(obex);
448 if (obex->pending_req != NULL)
449 pending_pkt_free(obex->pending_req);
450 obex->pending_req = p;
451 p->timeout_id = g_timeout_add_seconds(p->timeout,
454 /* During packet encode final bit can be set */
455 if (obex->tx_buf[0] & FINAL_BIT)
456 check_srm_final(obex,
457 obex->tx_buf[0] & ~FINAL_BIT);
465 if (obex->suspended) {
466 obex->write_source = 0;
470 if (!obex->write(obex, NULL))
474 if (obex->tx_data > 0 || g_queue_get_length(obex->tx_queue) > 0)
478 obex->rx_last_op = G_OBEX_OP_NONE;
480 obex->write_source = 0;
484 static void enable_tx(GObex *obex)
491 if (obex->write_source > 0)
494 cond = G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
495 obex->write_source = g_io_add_watch(obex->io, cond, write_data, obex);
498 static gboolean g_obex_send_internal(GObex *obex, struct pending_pkt *p,
502 if (obex->io == NULL) {
503 g_set_error(err, G_OBEX_ERROR, G_OBEX_ERROR_DISCONNECTED,
504 "The transport is not connected");
505 g_obex_debug(G_OBEX_DEBUG_ERROR, "%s", (*err)->message);
509 if (g_obex_packet_get_operation(p->pkt, NULL) == G_OBEX_OP_ABORT)
510 g_queue_push_head(obex->tx_queue, p);
512 g_queue_push_tail(obex->tx_queue, p);
514 if (obex->pending_req == NULL || p->id == 0)
520 static void init_connect_data(GObex *obex, struct connect_data *data)
524 memset(data, 0, sizeof(*data));
526 data->version = 0x10;
529 u16 = g_htons(obex->rx_mtu);
530 memcpy(&data->mtu, &u16, sizeof(u16));
533 static void prepare_connect_rsp(GObex *obex, GObexPacket *rsp)
536 struct connect_data data;
537 static guint32 next_connid = 1;
539 init_connect_data(obex, &data);
540 g_obex_packet_set_data(rsp, &data, sizeof(data), G_OBEX_DATA_COPY);
542 connid = g_obex_packet_get_header(rsp, G_OBEX_HDR_CONNECTION);
543 if (connid != NULL) {
544 g_obex_header_get_uint32(connid, &obex->conn_id);
548 obex->conn_id = next_connid++;
550 connid = g_obex_header_new_uint32(G_OBEX_HDR_CONNECTION,
552 g_obex_packet_prepend_header(rsp, connid);
555 static void prepare_srm_rsp(GObex *obex, GObexPacket *pkt)
559 if (!obex->use_srm || obex->srm == NULL)
562 if (obex->srm->enabled)
565 hdr = g_obex_packet_get_header(pkt, G_OBEX_HDR_SRM);
569 hdr = g_obex_header_new_uint8(G_OBEX_HDR_SRM, G_OBEX_SRM_ENABLE);
570 g_obex_packet_prepend_header(pkt, hdr);
573 gboolean g_obex_send(GObex *obex, GObexPacket *pkt, GError **err)
575 struct pending_pkt *p;
578 g_obex_debug(G_OBEX_DEBUG_COMMAND, "conn %u", obex->conn_id);
580 if (obex == NULL || pkt == NULL) {
581 g_set_error(err, G_OBEX_ERROR, G_OBEX_ERROR_INVALID_ARGS,
582 "Invalid arguments");
583 g_obex_debug(G_OBEX_DEBUG_ERROR, "%s", (*err)->message);
587 switch (obex->rx_last_op) {
588 case G_OBEX_OP_CONNECT:
589 prepare_connect_rsp(obex, pkt);
593 prepare_srm_rsp(obex, pkt);
597 p = g_new0(struct pending_pkt, 1);
600 ret = g_obex_send_internal(obex, p, err);
607 static void prepare_srm_req(GObex *obex, GObexPacket *pkt)
614 if (obex->srm != NULL && obex->srm->enabled)
617 hdr = g_obex_packet_get_header(pkt, G_OBEX_HDR_SRM);
621 hdr = g_obex_header_new_uint8(G_OBEX_HDR_SRM, G_OBEX_SRM_ENABLE);
622 g_obex_packet_prepend_header(pkt, hdr);
625 guint g_obex_send_req(GObex *obex, GObexPacket *req, gint timeout,
626 GObexResponseFunc func, gpointer user_data,
630 struct pending_pkt *p;
634 g_obex_debug(G_OBEX_DEBUG_COMMAND, "conn %u", obex->conn_id);
636 op = g_obex_packet_get_operation(req, NULL);
637 if (op == G_OBEX_OP_PUT || op == G_OBEX_OP_GET) {
638 /* Only enable SRM automatically for GET and PUT */
639 prepare_srm_req(obex, req);
642 if (obex->conn_id == CONNID_INVALID)
645 if (obex->rx_last_op == G_OBEX_RSP_CONTINUE)
648 if (g_obex_srm_active(obex) && obex->pending_req != NULL)
651 hdr = g_obex_packet_get_header(req, G_OBEX_HDR_CONNECTION);
655 hdr = g_obex_header_new_uint32(G_OBEX_HDR_CONNECTION, obex->conn_id);
656 g_obex_packet_prepend_header(req, hdr);
659 p = g_new0(struct pending_pkt, 1);
664 p->rsp_data = user_data;
667 p->timeout = G_OBEX_DEFAULT_TIMEOUT;
669 p->timeout = timeout;
671 if (!g_obex_send_internal(obex, p, err)) {
679 static gint pending_pkt_cmp(gconstpointer a, gconstpointer b)
681 const struct pending_pkt *p = a;
682 guint id = GPOINTER_TO_UINT(b);
687 gboolean g_obex_pending_req_abort(GObex *obex, GError **err)
689 struct pending_pkt *p = obex->pending_req;
697 g_source_remove(p->timeout_id);
698 p->timeout = G_OBEX_ABORT_TIMEOUT;
699 p->timeout_id = g_timeout_add_seconds(p->timeout, req_timeout, obex);
701 req = g_obex_packet_new(G_OBEX_OP_ABORT, TRUE, G_OBEX_HDR_INVALID);
703 return g_obex_send(obex, req, err);
706 static gboolean cancel_complete(gpointer user_data)
708 struct pending_pkt *p = user_data;
709 GObex *obex = p->obex;
712 g_assert(p->rsp_func != NULL);
714 err = g_error_new(G_OBEX_ERROR, G_OBEX_ERROR_CANCELLED,
715 "The request was cancelled");
716 p->rsp_func(obex, err, NULL, p->rsp_data);
725 gboolean g_obex_cancel_req(GObex *obex, guint req_id, gboolean remove_callback)
728 struct pending_pkt *p;
730 if (obex->pending_req && obex->pending_req->id == req_id) {
731 if (!g_obex_pending_req_abort(obex, NULL)) {
732 p = obex->pending_req;
733 obex->pending_req = NULL;
734 goto immediate_completion;
738 obex->pending_req->rsp_func = NULL;
743 match = g_queue_find_custom(obex->tx_queue, GUINT_TO_POINTER(req_id),
750 g_queue_delete_link(obex->tx_queue, match);
752 immediate_completion:
754 p->obex = g_obex_ref(obex);
756 if (remove_callback || p->rsp_func == NULL)
759 g_idle_add(cancel_complete, p);
764 gboolean g_obex_send_rsp(GObex *obex, guint8 rspcode, GError **err,
765 guint8 first_hdr_type, ...)
770 va_start(args, first_hdr_type);
771 rsp = g_obex_packet_new_valist(rspcode, TRUE, first_hdr_type, args);
774 return g_obex_send(obex, rsp, err);
777 void g_obex_set_disconnect_function(GObex *obex, GObexFunc func,
780 obex->disconn_func = func;
781 obex->disconn_func_data = user_data;
784 static gint req_handler_cmpop(gconstpointer a, gconstpointer b)
786 const struct req_handler *handler = a;
787 guint opcode = GPOINTER_TO_UINT(b);
789 return (gint) handler->opcode - (gint) opcode;
792 static gint req_handler_cmpid(gconstpointer a, gconstpointer b)
794 const struct req_handler *handler = a;
795 guint id = GPOINTER_TO_UINT(b);
797 return (gint) handler->id - (gint) id;
800 guint g_obex_add_request_function(GObex *obex, guint8 opcode,
801 GObexRequestFunc func,
804 struct req_handler *handler;
805 static guint next_id = 1;
807 handler = g_new0(struct req_handler, 1);
808 handler->id = next_id++;
809 handler->opcode = opcode;
810 handler->func = func;
811 handler->user_data = user_data;
813 obex->req_handlers = g_slist_prepend(obex->req_handlers, handler);
818 gboolean g_obex_remove_request_function(GObex *obex, guint id)
820 struct req_handler *handler;
823 match = g_slist_find_custom(obex->req_handlers, GUINT_TO_POINTER(id),
828 handler = match->data;
830 obex->req_handlers = g_slist_delete_link(obex->req_handlers, match);
836 void g_obex_suspend(GObex *obex)
838 g_obex_debug(G_OBEX_DEBUG_COMMAND, "conn %u", obex->conn_id);
840 if (obex->write_source > 0) {
841 g_source_remove(obex->write_source);
842 obex->write_source = 0;
845 obex->suspended = TRUE;
848 void g_obex_resume(GObex *obex)
850 g_obex_debug(G_OBEX_DEBUG_COMMAND, "conn %u", obex->conn_id);
852 obex->suspended = FALSE;
854 if (g_queue_get_length(obex->tx_queue) > 0 || obex->tx_data > 0)
858 gboolean g_obex_srm_active(GObex *obex)
860 gboolean ret = FALSE;
865 if (obex->srm == NULL || !obex->srm->enabled)
868 if (obex->srm->srmp <= G_OBEX_SRMP_NEXT_WAIT)
873 g_obex_debug(G_OBEX_DEBUG_COMMAND, "%s", ret ? "yes" : "no");
877 static void parse_connect_data(GObex *obex, GObexPacket *pkt)
879 const struct connect_data *data;
884 data = g_obex_packet_get_data(pkt, &data_len);
885 if (data == NULL || data_len != sizeof(*data))
888 memcpy(&u16, &data->mtu, sizeof(u16));
890 obex->tx_mtu = g_ntohs(u16);
891 if (obex->io_tx_mtu > 0 && obex->tx_mtu > obex->io_tx_mtu)
892 obex->tx_mtu = obex->io_tx_mtu;
893 obex->tx_buf = g_realloc(obex->tx_buf, obex->tx_mtu);
895 connid = g_obex_packet_get_header(pkt, G_OBEX_HDR_CONNECTION);
897 g_obex_header_get_uint32(connid, &obex->conn_id);
900 static gboolean parse_response(GObex *obex, GObexPacket *rsp)
902 struct pending_pkt *p = obex->pending_req;
903 guint8 opcode, rspcode;
906 rspcode = g_obex_packet_get_operation(rsp, &final);
908 opcode = g_obex_packet_get_operation(p->pkt, NULL);
909 if (opcode == G_OBEX_OP_CONNECT)
910 parse_connect_data(obex, rsp);
912 setup_srm(obex, rsp, FALSE);
914 if (!g_obex_srm_active(obex))
918 * Resposes have final bit set but in case of GET with SRM
919 * we should not remove the request since the remote side will
920 * continue sending responses until the transfer is finished
922 if (opcode == G_OBEX_OP_GET && rspcode == G_OBEX_RSP_CONTINUE) {
923 g_source_remove(p->timeout_id);
924 p->timeout_id = g_timeout_add_seconds(p->timeout, req_timeout,
932 static void handle_response(GObex *obex, GError *err, GObexPacket *rsp)
934 struct pending_pkt *p = obex->pending_req;
935 gboolean disconn = err ? TRUE : FALSE, final_rsp = TRUE;
938 final_rsp = parse_response(obex, rsp);
941 err = g_error_new(G_OBEX_ERROR, G_OBEX_ERROR_CANCELLED,
942 "The operation was cancelled");
945 g_obex_debug(G_OBEX_DEBUG_ERROR, "%s", err->message);
948 p->rsp_func(obex, err, rsp, p->rsp_data);
950 /* Check if user callback removed the request */
951 if (p != obex->pending_req)
960 obex->pending_req = NULL;
963 if (!disconn && g_queue_get_length(obex->tx_queue) > 0)
967 static gboolean check_connid(GObex *obex, GObexPacket *pkt)
972 if (obex->conn_id == CONNID_INVALID)
975 hdr = g_obex_packet_get_header(pkt, G_OBEX_HDR_CONNECTION);
979 g_obex_header_get_uint32(hdr, &id);
981 return obex->conn_id == id;
984 static int parse_request(GObex *obex, GObexPacket *req)
989 op = g_obex_packet_get_operation(req, &final);
991 case G_OBEX_OP_CONNECT:
992 parse_connect_data(obex, req);
994 case G_OBEX_OP_ABORT:
997 if (check_connid(obex, req))
1000 return -G_OBEX_RSP_SERVICE_UNAVAILABLE;
1003 setup_srm(obex, req, FALSE);
1008 static void handle_request(GObex *obex, GObexPacket *req)
1013 op = parse_request(obex, req);
1017 match = g_slist_find_custom(obex->req_handlers, GUINT_TO_POINTER(op),
1020 struct req_handler *handler = match->data;
1021 handler->func(obex, req, handler->user_data);
1025 op = -G_OBEX_RSP_NOT_IMPLEMENTED;
1028 g_obex_debug(G_OBEX_DEBUG_ERROR, "%s", g_obex_strerror(-op));
1029 g_obex_send_rsp(obex, -op, NULL, G_OBEX_HDR_INVALID);
1032 static gboolean read_stream(GObex *obex, GError **err)
1034 GIOChannel *io = obex->io;
1036 gsize rbytes, toread;
1040 if (obex->rx_data >= 3)
1044 toread = 3 - obex->rx_data;
1045 buf = (gchar *) &obex->rx_buf[obex->rx_data];
1047 status = g_io_channel_read_chars(io, buf, toread, &rbytes, NULL);
1048 if (status != G_IO_STATUS_NORMAL)
1051 obex->rx_data += rbytes;
1052 if (obex->rx_data < 3)
1055 memcpy(&u16, &buf[1], sizeof(u16));
1056 obex->rx_pkt_len = g_ntohs(u16);
1058 if (obex->rx_pkt_len > obex->rx_mtu) {
1059 g_set_error(err, G_OBEX_ERROR, G_OBEX_ERROR_PARSE_ERROR,
1060 "Too big incoming packet");
1061 g_obex_debug(G_OBEX_DEBUG_ERROR, "%s", (*err)->message);
1066 if (obex->rx_data >= obex->rx_pkt_len)
1070 toread = obex->rx_pkt_len - obex->rx_data;
1071 buf = (gchar *) &obex->rx_buf[obex->rx_data];
1073 status = g_io_channel_read_chars(io, buf, toread, &rbytes, NULL);
1074 if (status != G_IO_STATUS_NORMAL)
1077 obex->rx_data += rbytes;
1078 } while (rbytes > 0 && obex->rx_data < obex->rx_pkt_len);
1081 g_obex_dump(">", obex->rx_buf, obex->rx_data);
1086 static gboolean read_packet(GObex *obex, GError **err)
1088 GIOChannel *io = obex->io;
1089 GError *read_err = NULL;
1094 if (obex->rx_data > 0) {
1095 g_set_error(err, G_OBEX_ERROR, G_OBEX_ERROR_PARSE_ERROR,
1096 "RX buffer not empty before reading packet");
1100 status = g_io_channel_read_chars(io, (gchar *) obex->rx_buf,
1101 obex->rx_mtu, &rbytes, &read_err);
1102 if (status != G_IO_STATUS_NORMAL) {
1103 g_set_error(err, G_OBEX_ERROR, G_OBEX_ERROR_PARSE_ERROR,
1104 "Unable to read data: %s", read_err->message);
1105 g_error_free(read_err);
1109 obex->rx_data += rbytes;
1112 g_set_error(err, G_OBEX_ERROR, G_OBEX_ERROR_PARSE_ERROR,
1113 "Incomplete packet received");
1117 memcpy(&u16, &obex->rx_buf[1], sizeof(u16));
1118 obex->rx_pkt_len = g_ntohs(u16);
1120 if (obex->rx_pkt_len != rbytes) {
1121 g_set_error(err, G_OBEX_ERROR, G_OBEX_ERROR_PARSE_ERROR,
1122 "Data size doesn't match packet size (%zu != %u)",
1123 rbytes, obex->rx_pkt_len);
1127 g_obex_dump(">", obex->rx_buf, obex->rx_data);
1131 g_obex_debug(G_OBEX_DEBUG_ERROR, "%s", (*err)->message);
1135 static gboolean incoming_data(GIOChannel *io, GIOCondition cond,
1138 GObex *obex = user_data;
1140 ssize_t header_offset;
1144 if (cond & G_IO_NVAL)
1147 if (cond & (G_IO_HUP | G_IO_ERR)) {
1148 err = g_error_new(G_OBEX_ERROR, G_OBEX_ERROR_DISCONNECTED,
1149 "Transport got disconnected");
1153 if (!obex->read(obex, &err))
1156 if (obex->rx_data < 3 || obex->rx_data < obex->rx_pkt_len)
1159 obex->rx_last_op = obex->rx_buf[0] & ~FINAL_BIT;
1161 if (obex->pending_req) {
1162 struct pending_pkt *p = obex->pending_req;
1163 opcode = g_obex_packet_get_operation(p->pkt, NULL);
1164 header_offset = rsp_header_offset(opcode);
1166 opcode = obex->rx_last_op;
1167 /* Unexpected response -- fail silently */
1168 if (opcode > 0x1f && opcode != G_OBEX_OP_ABORT) {
1172 header_offset = req_header_offset(opcode);
1175 if (header_offset < 0) {
1176 err = g_error_new(G_OBEX_ERROR, G_OBEX_ERROR_PARSE_ERROR,
1177 "Unknown header offset for opcode 0x%02x",
1182 pkt = g_obex_packet_decode(obex->rx_buf, obex->rx_data, header_offset,
1183 G_OBEX_DATA_REF, &err);
1187 /* Protect against user callback freeing the object */
1190 if (obex->pending_req)
1191 handle_response(obex, NULL, pkt);
1193 handle_request(obex, pkt);
1203 g_obex_packet_free(pkt);
1209 g_obex_debug(G_OBEX_DEBUG_ERROR, "%s", err->message);
1211 g_io_channel_unref(obex->io);
1213 obex->io_source = 0;
1216 /* Protect against user callback freeing the object */
1219 if (obex->pending_req)
1220 handle_response(obex, err, NULL);
1222 if (obex->disconn_func)
1223 obex->disconn_func(obex, err, obex->disconn_func_data);
1232 static GDebugKey keys[] = {
1233 { "error", G_OBEX_DEBUG_ERROR },
1234 { "command", G_OBEX_DEBUG_COMMAND },
1235 { "transfer", G_OBEX_DEBUG_TRANSFER },
1236 { "header", G_OBEX_DEBUG_HEADER },
1237 { "packet", G_OBEX_DEBUG_PACKET },
1238 { "data", G_OBEX_DEBUG_DATA },
1241 GObex *g_obex_new(GIOChannel *io, GObexTransportType transport_type,
1242 gssize io_rx_mtu, gssize io_tx_mtu)
1247 if (gobex_debug == 0) {
1248 const char *env = g_getenv("GOBEX_DEBUG");
1250 gobex_debug = g_parse_debug_string(env, keys, 6);
1252 gobex_debug = G_OBEX_DEBUG_NONE;
1255 g_obex_debug(G_OBEX_DEBUG_COMMAND, "");
1260 if (io_rx_mtu >= 0 && io_rx_mtu < G_OBEX_MINIMUM_MTU)
1263 if (io_tx_mtu >= 0 && io_tx_mtu < G_OBEX_MINIMUM_MTU)
1266 obex = g_new0(GObex, 1);
1268 obex->io = g_io_channel_ref(io);
1269 obex->ref_count = 1;
1270 obex->conn_id = CONNID_INVALID;
1271 obex->rx_last_op = G_OBEX_OP_NONE;
1273 obex->io_rx_mtu = io_rx_mtu;
1274 obex->io_tx_mtu = io_tx_mtu;
1276 if (io_rx_mtu > G_OBEX_MAXIMUM_MTU)
1277 obex->rx_mtu = G_OBEX_MAXIMUM_MTU;
1278 else if (io_rx_mtu < G_OBEX_MINIMUM_MTU)
1279 obex->rx_mtu = G_OBEX_DEFAULT_MTU;
1281 obex->rx_mtu = io_rx_mtu;
1283 obex->tx_mtu = G_OBEX_MINIMUM_MTU;
1285 obex->tx_queue = g_queue_new();
1286 obex->rx_buf = g_malloc(obex->rx_mtu);
1287 obex->tx_buf = g_malloc(obex->tx_mtu);
1289 switch (transport_type) {
1290 case G_OBEX_TRANSPORT_STREAM:
1291 obex->read = read_stream;
1292 obex->write = write_stream;
1294 case G_OBEX_TRANSPORT_PACKET:
1295 obex->use_srm = TRUE;
1296 obex->read = read_packet;
1297 obex->write = write_packet;
1304 g_io_channel_set_encoding(io, NULL, NULL);
1305 g_io_channel_set_buffered(io, FALSE);
1306 cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
1307 obex->io_source = g_io_add_watch(io, cond, incoming_data, obex);
1312 GObex *g_obex_ref(GObex *obex)
1317 g_atomic_int_inc(&obex->ref_count);
1319 g_obex_debug(G_OBEX_DEBUG_COMMAND, "ref %u", obex->ref_count);
1324 void g_obex_unref(GObex *obex)
1328 last_ref = g_atomic_int_dec_and_test(&obex->ref_count);
1330 g_obex_debug(G_OBEX_DEBUG_COMMAND, "ref %u", obex->ref_count);
1335 g_slist_free_full(obex->req_handlers, g_free);
1337 g_queue_foreach(obex->tx_queue, (GFunc) pending_pkt_free, NULL);
1338 g_queue_free(obex->tx_queue);
1340 if (obex->io != NULL)
1341 g_io_channel_unref(obex->io);
1343 if (obex->io_source > 0)
1344 g_source_remove(obex->io_source);
1346 if (obex->write_source > 0)
1347 g_source_remove(obex->write_source);
1349 g_free(obex->rx_buf);
1350 g_free(obex->tx_buf);
1353 if (obex->pending_req)
1354 pending_pkt_free(obex->pending_req);
1359 /* Higher level functions */
1361 guint g_obex_connect(GObex *obex, GObexResponseFunc func, gpointer user_data,
1362 GError **err, guint8 first_hdr_id, ...)
1365 struct connect_data data;
1368 g_obex_debug(G_OBEX_DEBUG_COMMAND, "");
1370 va_start(args, first_hdr_id);
1371 req = g_obex_packet_new_valist(G_OBEX_OP_CONNECT, TRUE,
1372 first_hdr_id, args);
1375 init_connect_data(obex, &data);
1376 g_obex_packet_set_data(req, &data, sizeof(data), G_OBEX_DATA_COPY);
1378 return g_obex_send_req(obex, req, -1, func, user_data, err);
1381 guint g_obex_setpath(GObex *obex, const char *path, GObexResponseFunc func,
1382 gpointer user_data, GError **err)
1385 struct setpath_data data;
1388 g_obex_debug(G_OBEX_DEBUG_COMMAND, "conn %u", obex->conn_id);
1390 req = g_obex_packet_new(G_OBEX_OP_SETPATH, TRUE, G_OBEX_HDR_INVALID);
1392 memset(&data, 0, sizeof(data));
1394 if (path != NULL && strncmp("..", path, 2) == 0) {
1396 folder = (path[2] == '/') ? &path[3] : NULL;
1402 if (folder != NULL) {
1404 hdr = g_obex_header_new_unicode(G_OBEX_HDR_NAME, folder);
1405 g_obex_packet_add_header(req, hdr);
1408 g_obex_packet_set_data(req, &data, sizeof(data), G_OBEX_DATA_COPY);
1410 return g_obex_send_req(obex, req, -1, func, user_data, err);
1413 guint g_obex_mkdir(GObex *obex, const char *path, GObexResponseFunc func,
1414 gpointer user_data, GError **err)
1417 struct setpath_data data;
1419 g_obex_debug(G_OBEX_DEBUG_COMMAND, "conn %u", obex->conn_id);
1421 req = g_obex_packet_new(G_OBEX_OP_SETPATH, TRUE, G_OBEX_HDR_NAME, path,
1422 G_OBEX_HDR_INVALID);
1424 memset(&data, 0, sizeof(data));
1425 g_obex_packet_set_data(req, &data, sizeof(data), G_OBEX_DATA_COPY);
1427 return g_obex_send_req(obex, req, -1, func, user_data, err);
1430 guint g_obex_delete(GObex *obex, const char *name, GObexResponseFunc func,
1431 gpointer user_data, GError **err)
1435 g_obex_debug(G_OBEX_DEBUG_COMMAND, "conn %u", obex->conn_id);
1437 req = g_obex_packet_new(G_OBEX_OP_PUT, TRUE, G_OBEX_HDR_NAME, name,
1438 G_OBEX_HDR_INVALID);
1440 return g_obex_send_req(obex, req, -1, func, user_data, err);
1443 guint g_obex_copy(GObex *obex, const char *name, const char *dest,
1444 GObexResponseFunc func, gpointer user_data,
1449 g_obex_debug(G_OBEX_DEBUG_COMMAND, "conn %u", obex->conn_id);
1451 req = g_obex_packet_new(G_OBEX_OP_ACTION, TRUE,
1452 G_OBEX_HDR_ACTION, G_OBEX_ACTION_COPY,
1453 G_OBEX_HDR_NAME, name,
1454 G_OBEX_HDR_DESTNAME, dest,
1455 G_OBEX_HDR_INVALID);
1457 return g_obex_send_req(obex, req, -1, func, user_data, err);
1460 guint g_obex_move(GObex *obex, const char *name, const char *dest,
1461 GObexResponseFunc func, gpointer user_data,
1466 g_obex_debug(G_OBEX_DEBUG_COMMAND, "conn %u", obex->conn_id);
1468 req = g_obex_packet_new(G_OBEX_OP_ACTION, TRUE,
1469 G_OBEX_HDR_ACTION, G_OBEX_ACTION_MOVE,
1470 G_OBEX_HDR_NAME, name,
1471 G_OBEX_HDR_DESTNAME, dest,
1472 G_OBEX_HDR_INVALID);
1474 return g_obex_send_req(obex, req, -1, func, user_data, err);
1477 guint8 g_obex_errno_to_rsp(int err)
1481 return G_OBEX_RSP_SUCCESS;
1484 return G_OBEX_RSP_FORBIDDEN;
1486 return G_OBEX_RSP_NOT_FOUND;
1489 return G_OBEX_RSP_BAD_REQUEST;
1491 return G_OBEX_RSP_SERVICE_UNAVAILABLE;
1493 return G_OBEX_RSP_NOT_IMPLEMENTED;
1496 return G_OBEX_RSP_PRECONDITION_FAILED;
1498 return G_OBEX_RSP_INTERNAL_SERVER_ERROR;