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 as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
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 /* Challenge request */
48 #define NONCE_TAG 0x00
51 /* Challenge response */
52 #define DIGEST_TAG 0x00
54 guint gobex_debug = 0;
69 gboolean (*read) (GObex *obex, GError **err);
70 gboolean (*write) (GObex *obex, GError **err);
84 struct srm_config *srm;
95 GObexApparam *authchal;
101 GObexFunc disconn_func;
102 gpointer disconn_func_data;
104 struct pending_pkt *pending_req;
113 GObexResponseFunc rsp_func;
117 gboolean authenticating;
123 GObexRequestFunc func;
127 struct connect_data {
131 } __attribute__ ((packed));
133 struct setpath_data {
136 } __attribute__ ((packed));
138 static struct error_code {
142 { G_OBEX_RSP_CONTINUE, "Continue" },
143 { G_OBEX_RSP_SUCCESS, "Success" },
144 { G_OBEX_RSP_CREATED, "Created" },
145 { G_OBEX_RSP_ACCEPTED, "Accepted" },
146 { G_OBEX_RSP_NON_AUTHORITATIVE, "Non Authoritative" },
147 { G_OBEX_RSP_NO_CONTENT, "No Content" },
148 { G_OBEX_RSP_RESET_CONTENT, "Reset Content" },
149 { G_OBEX_RSP_PARTIAL_CONTENT, "Partial Content" },
150 { G_OBEX_RSP_MULTIPLE_CHOICES, "Multiple Choices" },
151 { G_OBEX_RSP_MOVED_PERMANENTLY, "Moved Permanently" },
152 { G_OBEX_RSP_MOVED_TEMPORARILY, "Moved Temporarily" },
153 { G_OBEX_RSP_SEE_OTHER, "See Other" },
154 { G_OBEX_RSP_NOT_MODIFIED, "Not Modified" },
155 { G_OBEX_RSP_USE_PROXY, "Use Proxy" },
156 { G_OBEX_RSP_BAD_REQUEST, "Bad Request" },
157 { G_OBEX_RSP_UNAUTHORIZED, "Unauthorized" },
158 { G_OBEX_RSP_PAYMENT_REQUIRED, "Payment Required" },
159 { G_OBEX_RSP_FORBIDDEN, "Forbidden" },
160 { G_OBEX_RSP_NOT_FOUND, "Not Found" },
161 { G_OBEX_RSP_METHOD_NOT_ALLOWED, "Method Not Allowed" },
162 { G_OBEX_RSP_NOT_ACCEPTABLE, "Not Acceptable" },
163 { G_OBEX_RSP_PROXY_AUTH_REQUIRED, "Proxy Authentication Required" },
164 { G_OBEX_RSP_REQUEST_TIME_OUT, "Request Time Out" },
165 { G_OBEX_RSP_CONFLICT, "Conflict" },
166 { G_OBEX_RSP_GONE, "Gone" },
167 { G_OBEX_RSP_LENGTH_REQUIRED, "Length Required" },
168 { G_OBEX_RSP_PRECONDITION_FAILED, "Precondition Failed" },
169 { G_OBEX_RSP_REQ_ENTITY_TOO_LARGE, "Request Entity Too Large" },
170 { G_OBEX_RSP_REQ_URL_TOO_LARGE, "Request URL Too Large" },
171 { G_OBEX_RSP_UNSUPPORTED_MEDIA_TYPE, "Unsupported Media Type" },
172 { G_OBEX_RSP_INTERNAL_SERVER_ERROR, "Internal Server Error" },
173 { G_OBEX_RSP_NOT_IMPLEMENTED, "Not Implemented" },
174 { G_OBEX_RSP_BAD_GATEWAY, "Bad Gateway" },
175 { G_OBEX_RSP_SERVICE_UNAVAILABLE, "Service Unavailable" },
176 { G_OBEX_RSP_GATEWAY_TIMEOUT, "Gateway Timeout" },
177 { G_OBEX_RSP_VERSION_NOT_SUPPORTED, "Version Not Supported" },
178 { G_OBEX_RSP_DATABASE_FULL, "Database Full" },
179 { G_OBEX_RSP_DATABASE_LOCKED, "Database Locked" },
183 const char *g_obex_strerror(guint8 err_code)
185 struct error_code *error;
187 for (error = obex_errors; error->name != NULL; error++) {
188 if (error->code == err_code)
195 static ssize_t req_header_offset(guint8 opcode)
198 case G_OBEX_OP_CONNECT:
199 return sizeof(struct connect_data);
200 case G_OBEX_OP_SETPATH:
201 return sizeof(struct setpath_data);
202 case G_OBEX_OP_DISCONNECT:
205 case G_OBEX_OP_SESSION:
206 case G_OBEX_OP_ABORT:
207 case G_OBEX_OP_ACTION:
214 static ssize_t rsp_header_offset(guint8 opcode)
217 case G_OBEX_OP_CONNECT:
218 return sizeof(struct connect_data);
219 case G_OBEX_OP_SETPATH:
220 case G_OBEX_OP_DISCONNECT:
223 case G_OBEX_OP_SESSION:
224 case G_OBEX_OP_ABORT:
225 case G_OBEX_OP_ACTION:
232 static void pending_pkt_free(struct pending_pkt *p)
235 g_obex_unref(p->obex);
237 if (p->timeout_id > 0)
238 g_source_remove(p->timeout_id);
240 g_obex_packet_free(p->pkt);
245 static gboolean req_timeout(gpointer user_data)
247 GObex *obex = user_data;
248 struct pending_pkt *p = obex->pending_req;
254 obex->pending_req = NULL;
256 err = g_error_new(G_OBEX_ERROR, G_OBEX_ERROR_TIMEOUT,
257 "Timed out waiting for response");
259 g_obex_debug(G_OBEX_DEBUG_ERROR, "%s", err->message);
262 p->rsp_func(obex, err, NULL, p->rsp_data);
270 static gboolean write_stream(GObex *obex, GError **err)
276 buf = (char *) &obex->tx_buf[obex->tx_sent];
277 status = g_io_channel_write_chars(obex->io, buf, obex->tx_data,
278 &bytes_written, err);
279 if (status != G_IO_STATUS_NORMAL)
282 g_obex_dump(G_OBEX_DEBUG_DATA, "<", buf, bytes_written);
284 obex->tx_sent += bytes_written;
285 obex->tx_data -= bytes_written;
290 static gboolean write_packet(GObex *obex, GError **err)
296 buf = (char *) &obex->tx_buf[obex->tx_sent];
297 status = g_io_channel_write_chars(obex->io, buf, obex->tx_data,
298 &bytes_written, err);
299 if (status != G_IO_STATUS_NORMAL)
302 if (bytes_written != obex->tx_data)
305 g_obex_dump(G_OBEX_DEBUG_DATA, "<", buf, bytes_written);
307 obex->tx_sent += bytes_written;
308 obex->tx_data -= bytes_written;
313 static void set_srmp(GObex *obex, guint8 srmp, gboolean outgoing)
315 struct srm_config *config = obex->srm;
320 /* Dont't reset if direction doesn't match */
321 if (srmp > G_OBEX_SRMP_NEXT_WAIT && config->outgoing != outgoing)
325 config->outgoing = outgoing;
328 static void set_srm(GObex *obex, guint8 op, guint8 srm)
330 struct srm_config *config = obex->srm;
333 if (config == NULL) {
334 if (srm == G_OBEX_SRM_DISABLE)
337 config = g_new0(struct srm_config, 1);
344 /* Indicate response, treat it as request */
345 if (config->srm == G_OBEX_SRM_INDICATE) {
346 if (srm != G_OBEX_SRM_ENABLE)
352 enable = (srm == G_OBEX_SRM_ENABLE);
353 if (config->enabled == enable)
356 config->enabled = enable;
358 g_obex_debug(G_OBEX_DEBUG_COMMAND, "SRM %s", config->enabled ?
359 "Enabled" : "Disabled");
369 static gboolean g_obex_srm_enabled(GObex *obex)
374 if (obex->srm == NULL)
377 return obex->srm->enabled;
380 static void check_srm_final(GObex *obex, guint8 op)
382 if (!g_obex_srm_enabled(obex))
385 switch (obex->srm->op) {
386 case G_OBEX_OP_CONNECT:
389 if (op <= G_OBEX_RSP_CONTINUE)
393 set_srm(obex, op, G_OBEX_SRM_DISABLE);
396 static void setup_srm(GObex *obex, GObexPacket *pkt, gboolean outgoing)
405 op = g_obex_packet_get_operation(pkt, &final);
407 hdr = g_obex_packet_get_header(pkt, G_OBEX_HDR_SRM);
410 g_obex_header_get_uint8(hdr, &srm);
411 g_obex_debug(G_OBEX_DEBUG_COMMAND, "srm 0x%02x", srm);
412 set_srm(obex, op, srm);
413 } else if (!g_obex_srm_enabled(obex))
414 set_srm(obex, op, G_OBEX_SRM_DISABLE);
416 hdr = g_obex_packet_get_header(pkt, G_OBEX_HDR_SRMP);
419 g_obex_header_get_uint8(hdr, &srmp);
420 g_obex_debug(G_OBEX_DEBUG_COMMAND, "srmp 0x%02x", srmp);
421 set_srmp(obex, srmp, outgoing);
422 } else if (obex->pending_req && obex->pending_req->suspended)
423 g_obex_packet_add_uint8(pkt, G_OBEX_HDR_SRMP, G_OBEX_SRMP_WAIT);
425 set_srmp(obex, -1, outgoing);
428 check_srm_final(obex, op);
431 static gboolean write_data(GIOChannel *io, GIOCondition cond,
434 GObex *obex = user_data;
436 if (cond & G_IO_NVAL)
439 if (cond & (G_IO_HUP | G_IO_ERR))
442 if (obex->tx_data == 0) {
443 struct pending_pkt *p = g_queue_pop_head(obex->tx_queue);
449 setup_srm(obex, p->pkt, TRUE);
451 if (g_obex_srm_enabled(obex))
454 /* Can't send a request while there's a pending one */
455 if (obex->pending_req && p->id > 0) {
456 g_queue_push_head(obex->tx_queue, p);
461 len = g_obex_packet_encode(p->pkt, obex->tx_buf, obex->tx_mtu);
462 if (len == -EAGAIN) {
463 g_queue_push_head(obex->tx_queue, p);
464 g_obex_suspend(obex);
474 if (obex->pending_req != NULL)
475 pending_pkt_free(obex->pending_req);
476 obex->pending_req = p;
477 p->timeout_id = g_timeout_add_seconds(p->timeout,
480 /* During packet encode final bit can be set */
481 if (obex->tx_buf[0] & FINAL_BIT)
482 check_srm_final(obex,
483 obex->tx_buf[0] & ~FINAL_BIT);
491 if (obex->suspended) {
492 obex->write_source = 0;
496 if (!obex->write(obex, NULL))
500 if (obex->tx_data > 0 || g_queue_get_length(obex->tx_queue) > 0)
504 obex->rx_last_op = G_OBEX_OP_NONE;
506 obex->write_source = 0;
510 static void enable_tx(GObex *obex)
517 if (!obex->io || obex->write_source > 0)
520 cond = G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
521 obex->write_source = g_io_add_watch(obex->io, cond, write_data, obex);
524 void g_obex_drop_tx_queue(GObex *obex)
526 struct pending_pkt *p;
528 g_obex_debug(G_OBEX_DEBUG_COMMAND, "");
530 while ((p = g_queue_pop_head(obex->tx_queue)))
534 static gboolean g_obex_send_internal(GObex *obex, struct pending_pkt *p,
538 if (obex->io == NULL) {
541 g_set_error(err, G_OBEX_ERROR, G_OBEX_ERROR_DISCONNECTED,
542 "The transport is not connected");
543 g_obex_debug(G_OBEX_DEBUG_ERROR, "%s", (*err)->message);
547 if (g_obex_packet_get_operation(p->pkt, NULL) == G_OBEX_OP_ABORT)
548 g_queue_push_head(obex->tx_queue, p);
550 g_queue_push_tail(obex->tx_queue, p);
552 if (obex->pending_req == NULL || p->id == 0)
558 static void init_connect_data(GObex *obex, struct connect_data *data)
562 memset(data, 0, sizeof(*data));
564 data->version = 0x10;
567 u16 = g_htons(obex->rx_mtu);
568 memcpy(&data->mtu, &u16, sizeof(u16));
571 static guint8 *digest_response(const guint8 *nonce)
577 result = g_new0(guint8, NONCE_LEN);
579 md5 = g_checksum_new(G_CHECKSUM_MD5);
583 g_checksum_update(md5, nonce, NONCE_LEN);
584 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
585 g_checksum_update(md5, (guint8 *) ":0000", 5);
587 g_checksum_update(md5, (guint8 *) ":BlueZ", 6);
588 #endif /* TIZEN_FEATURE_BLUEZ_MODIFY */
592 g_checksum_get_digest(md5, result, &size);
594 g_checksum_free(md5);
599 static void prepare_auth_rsp(GObex *obex, GObexPacket *rsp)
602 GObexApparam *authrsp;
607 /* Check if client is already responding to authentication challenge */
608 hdr = g_obex_packet_get_header(rsp, G_OBEX_HDR_AUTHRESP);
612 if (!g_obex_apparam_get_bytes(obex->authchal, NONCE_TAG, &nonce, &len))
615 if (len != NONCE_LEN)
618 result = digest_response(nonce);
619 authrsp = g_obex_apparam_set_bytes(NULL, DIGEST_TAG, result, NONCE_LEN);
621 hdr = g_obex_header_new_tag(G_OBEX_HDR_AUTHRESP, authrsp);
622 g_obex_packet_add_header(rsp, hdr);
624 g_obex_apparam_free(authrsp);
628 g_obex_apparam_free(obex->authchal);
629 obex->authchal = NULL;
632 static void prepare_connect_rsp(GObex *obex, GObexPacket *rsp)
635 struct connect_data data;
636 static guint32 next_connid = 1;
638 init_connect_data(obex, &data);
639 g_obex_packet_set_data(rsp, &data, sizeof(data), G_OBEX_DATA_COPY);
641 hdr = g_obex_packet_get_header(rsp, G_OBEX_HDR_CONNECTION);
643 g_obex_header_get_uint32(hdr, &obex->conn_id);
647 obex->conn_id = next_connid++;
649 hdr = g_obex_header_new_uint32(G_OBEX_HDR_CONNECTION, obex->conn_id);
650 g_obex_packet_prepend_header(rsp, hdr);
654 prepare_auth_rsp(obex, rsp);
657 static void prepare_srm_rsp(GObex *obex, GObexPacket *pkt)
661 if (!obex->use_srm || obex->srm == NULL)
664 if (obex->srm->enabled)
667 hdr = g_obex_packet_get_header(pkt, G_OBEX_HDR_SRM);
671 hdr = g_obex_header_new_uint8(G_OBEX_HDR_SRM, G_OBEX_SRM_ENABLE);
672 g_obex_packet_prepend_header(pkt, hdr);
675 gboolean g_obex_send(GObex *obex, GObexPacket *pkt, GError **err)
677 struct pending_pkt *p;
680 g_obex_debug(G_OBEX_DEBUG_COMMAND, "conn %u", obex->conn_id);
682 if (obex == NULL || pkt == NULL) {
685 g_set_error(err, G_OBEX_ERROR, G_OBEX_ERROR_INVALID_ARGS,
686 "Invalid arguments");
687 g_obex_debug(G_OBEX_DEBUG_ERROR, "%s", (*err)->message);
691 switch (obex->rx_last_op) {
692 case G_OBEX_OP_CONNECT:
693 prepare_connect_rsp(obex, pkt);
697 prepare_srm_rsp(obex, pkt);
701 p = g_new0(struct pending_pkt, 1);
704 ret = g_obex_send_internal(obex, p, err);
711 static void prepare_srm_req(GObex *obex, GObexPacket *pkt)
718 if (obex->srm != NULL && obex->srm->enabled)
721 hdr = g_obex_packet_get_header(pkt, G_OBEX_HDR_SRM);
725 hdr = g_obex_header_new_uint8(G_OBEX_HDR_SRM, G_OBEX_SRM_ENABLE);
726 g_obex_packet_prepend_header(pkt, hdr);
729 guint g_obex_send_req(GObex *obex, GObexPacket *req, int timeout,
730 GObexResponseFunc func, gpointer user_data,
734 struct pending_pkt *p;
738 g_obex_debug(G_OBEX_DEBUG_COMMAND, "conn %u", obex->conn_id);
740 op = g_obex_packet_get_operation(req, NULL);
741 if (op == G_OBEX_OP_PUT || op == G_OBEX_OP_GET) {
742 /* Only enable SRM automatically for GET and PUT */
743 prepare_srm_req(obex, req);
746 if (obex->conn_id == CONNID_INVALID)
749 if (obex->rx_last_op == G_OBEX_RSP_CONTINUE)
752 if (g_obex_srm_enabled(obex) && obex->pending_req != NULL)
755 hdr = g_obex_packet_get_header(req, G_OBEX_HDR_CONNECTION);
759 hdr = g_obex_header_new_uint32(G_OBEX_HDR_CONNECTION, obex->conn_id);
760 g_obex_packet_prepend_header(req, hdr);
763 p = g_new0(struct pending_pkt, 1);
768 p->rsp_data = user_data;
771 p->timeout = G_OBEX_DEFAULT_TIMEOUT;
773 p->timeout = timeout;
775 if (!g_obex_send_internal(obex, p, err)) {
783 static int pending_pkt_cmp(gconstpointer a, gconstpointer b)
785 const struct pending_pkt *p = a;
786 guint id = GPOINTER_TO_UINT(b);
791 static gboolean pending_req_abort(GObex *obex, GError **err)
793 struct pending_pkt *p = obex->pending_req;
801 if (p->timeout_id > 0)
802 g_source_remove(p->timeout_id);
804 p->timeout = G_OBEX_ABORT_TIMEOUT;
805 p->timeout_id = g_timeout_add_seconds(p->timeout, req_timeout, obex);
807 req = g_obex_packet_new(G_OBEX_OP_ABORT, TRUE, G_OBEX_HDR_INVALID);
809 return g_obex_send(obex, req, err);
812 static gboolean cancel_complete(gpointer user_data)
814 struct pending_pkt *p = user_data;
815 GObex *obex = p->obex;
818 g_assert(p->rsp_func != NULL);
820 err = g_error_new(G_OBEX_ERROR, G_OBEX_ERROR_CANCELLED,
821 "The request was cancelled");
822 p->rsp_func(obex, err, NULL, p->rsp_data);
831 gboolean g_obex_cancel_req(GObex *obex, guint req_id, gboolean remove_callback)
834 struct pending_pkt *p;
836 if (obex->pending_req && obex->pending_req->id == req_id) {
837 if (!pending_req_abort(obex, NULL)) {
838 p = obex->pending_req;
839 obex->pending_req = NULL;
840 goto immediate_completion;
844 obex->pending_req->rsp_func = NULL;
849 match = g_queue_find_custom(obex->tx_queue, GUINT_TO_POINTER(req_id),
856 g_queue_delete_link(obex->tx_queue, match);
858 immediate_completion:
860 p->obex = g_obex_ref(obex);
862 if (remove_callback || p->rsp_func == NULL)
865 g_idle_add(cancel_complete, p);
870 gboolean g_obex_send_rsp(GObex *obex, guint8 rspcode, GError **err,
871 guint first_hdr_type, ...)
876 va_start(args, first_hdr_type);
877 rsp = g_obex_packet_new_valist(rspcode, TRUE, first_hdr_type, args);
880 return g_obex_send(obex, rsp, err);
883 void g_obex_set_disconnect_function(GObex *obex, GObexFunc func,
886 obex->disconn_func = func;
887 obex->disconn_func_data = user_data;
890 static int req_handler_cmpop(gconstpointer a, gconstpointer b)
892 const struct req_handler *handler = a;
893 guint opcode = GPOINTER_TO_UINT(b);
895 return (int) handler->opcode - (int) opcode;
898 static int req_handler_cmpid(gconstpointer a, gconstpointer b)
900 const struct req_handler *handler = a;
901 guint id = GPOINTER_TO_UINT(b);
903 return (int) handler->id - (int) id;
906 guint g_obex_add_request_function(GObex *obex, guint8 opcode,
907 GObexRequestFunc func,
910 struct req_handler *handler;
911 static guint next_id = 1;
913 handler = g_new0(struct req_handler, 1);
914 handler->id = next_id++;
915 handler->opcode = opcode;
916 handler->func = func;
917 handler->user_data = user_data;
919 obex->req_handlers = g_slist_prepend(obex->req_handlers, handler);
924 gboolean g_obex_remove_request_function(GObex *obex, guint id)
926 struct req_handler *handler;
929 match = g_slist_find_custom(obex->req_handlers, GUINT_TO_POINTER(id),
934 handler = match->data;
936 obex->req_handlers = g_slist_delete_link(obex->req_handlers, match);
942 static void g_obex_srm_suspend(GObex *obex)
944 struct pending_pkt *p = obex->pending_req;
947 if (p->timeout_id > 0) {
948 g_source_remove(p->timeout_id);
954 req = g_obex_packet_new(G_OBEX_OP_GET, TRUE,
955 G_OBEX_HDR_SRMP, G_OBEX_SRMP_WAIT,
958 g_obex_send(obex, req, NULL);
961 void g_obex_suspend(GObex *obex)
963 struct pending_pkt *req = obex->pending_req;
965 g_obex_debug(G_OBEX_DEBUG_COMMAND, "conn %u", obex->conn_id);
967 if (!g_obex_srm_active(obex) || !req)
970 /* Send SRMP wait in case of GET */
971 if (g_obex_packet_get_operation(req->pkt, NULL) == G_OBEX_OP_GET) {
972 g_obex_srm_suspend(obex);
977 obex->suspended = TRUE;
979 if (obex->write_source > 0) {
980 g_source_remove(obex->write_source);
981 obex->write_source = 0;
985 static void g_obex_srm_resume(GObex *obex)
987 struct pending_pkt *p = obex->pending_req;
990 p->timeout_id = g_timeout_add_seconds(p->timeout, req_timeout, obex);
991 p->suspended = FALSE;
993 req = g_obex_packet_new(G_OBEX_OP_GET, TRUE, G_OBEX_HDR_INVALID);
995 g_obex_send(obex, req, NULL);
998 void g_obex_resume(GObex *obex)
1000 struct pending_pkt *req = obex->pending_req;
1002 g_obex_debug(G_OBEX_DEBUG_COMMAND, "conn %u", obex->conn_id);
1004 obex->suspended = FALSE;
1006 if (g_obex_srm_active(obex) || !req)
1009 if (g_obex_packet_get_operation(req->pkt, NULL) == G_OBEX_OP_GET)
1010 g_obex_srm_resume(obex);
1013 if (g_queue_get_length(obex->tx_queue) > 0 || obex->tx_data > 0)
1017 gboolean g_obex_srm_active(GObex *obex)
1019 gboolean ret = FALSE;
1021 if (!g_obex_srm_enabled(obex))
1024 if (obex->srm->srmp <= G_OBEX_SRMP_NEXT_WAIT)
1029 g_obex_debug(G_OBEX_DEBUG_COMMAND, "%s", ret ? "yes" : "no");
1033 static void auth_challenge(GObex *obex)
1035 struct pending_pkt *p = obex->pending_req;
1037 if (p->authenticating)
1040 p->authenticating = TRUE;
1042 prepare_auth_rsp(obex, p->pkt);
1044 /* Remove it as pending and add it back to the queue so it gets sent
1046 if (p->timeout_id > 0) {
1047 g_source_remove(p->timeout_id);
1050 obex->pending_req = NULL;
1051 g_obex_send_internal(obex, p, NULL);
1054 static void parse_connect_data(GObex *obex, GObexPacket *pkt)
1056 const struct connect_data *data;
1061 data = g_obex_packet_get_data(pkt, &data_len);
1062 if (data == NULL || data_len != sizeof(*data))
1065 memcpy(&u16, &data->mtu, sizeof(u16));
1067 obex->tx_mtu = g_ntohs(u16);
1068 if (obex->io_tx_mtu > 0 && obex->tx_mtu > obex->io_tx_mtu)
1069 obex->tx_mtu = obex->io_tx_mtu;
1070 obex->tx_buf = g_realloc(obex->tx_buf, obex->tx_mtu);
1072 hdr = g_obex_packet_get_header(pkt, G_OBEX_HDR_CONNECTION);
1074 g_obex_header_get_uint32(hdr, &obex->conn_id);
1076 hdr = g_obex_packet_get_header(pkt, G_OBEX_HDR_AUTHCHAL);
1078 obex->authchal = g_obex_header_get_apparam(hdr);
1081 static gboolean parse_response(GObex *obex, GObexPacket *rsp)
1083 struct pending_pkt *p = obex->pending_req;
1084 guint8 opcode, rspcode;
1087 rspcode = g_obex_packet_get_operation(rsp, &final);
1089 opcode = g_obex_packet_get_operation(p->pkt, NULL);
1090 if (opcode == G_OBEX_OP_CONNECT) {
1091 parse_connect_data(obex, rsp);
1092 if (rspcode == G_OBEX_RSP_UNAUTHORIZED && obex->authchal)
1093 auth_challenge(obex);
1096 setup_srm(obex, rsp, FALSE);
1098 if (!g_obex_srm_enabled(obex))
1102 * Resposes have final bit set but in case of GET with SRM
1103 * we should not remove the request since the remote side will
1104 * continue sending responses until the transfer is finished
1106 if (opcode == G_OBEX_OP_GET && rspcode == G_OBEX_RSP_CONTINUE) {
1107 if (p->timeout_id > 0)
1108 g_source_remove(p->timeout_id);
1110 p->timeout_id = g_timeout_add_seconds(p->timeout, req_timeout,
1118 static void handle_response(GObex *obex, GError *err, GObexPacket *rsp)
1120 struct pending_pkt *p;
1121 gboolean disconn = err ? TRUE : FALSE, final_rsp = TRUE;
1124 final_rsp = parse_response(obex, rsp);
1126 if (!obex->pending_req)
1129 p = obex->pending_req;
1131 /* Reset if final so it can no longer be cancelled */
1133 obex->pending_req = NULL;
1136 err = g_error_new(G_OBEX_ERROR, G_OBEX_ERROR_CANCELLED,
1137 "The operation was cancelled");
1140 g_obex_debug(G_OBEX_DEBUG_ERROR, "%s", err->message);
1143 p->rsp_func(obex, err, rsp, p->rsp_data);
1145 /* Check if user callback removed the request */
1146 if (!final_rsp && p != obex->pending_req)
1154 pending_pkt_free(p);
1156 if (!disconn && g_queue_get_length(obex->tx_queue) > 0)
1160 static gboolean check_connid(GObex *obex, GObexPacket *pkt)
1165 if (obex->conn_id == CONNID_INVALID)
1168 hdr = g_obex_packet_get_header(pkt, G_OBEX_HDR_CONNECTION);
1172 g_obex_header_get_uint32(hdr, &id);
1174 return obex->conn_id == id;
1177 static int parse_request(GObex *obex, GObexPacket *req)
1182 op = g_obex_packet_get_operation(req, &final);
1184 case G_OBEX_OP_CONNECT:
1185 parse_connect_data(obex, req);
1187 case G_OBEX_OP_ABORT:
1190 if (check_connid(obex, req))
1193 return -G_OBEX_RSP_SERVICE_UNAVAILABLE;
1196 setup_srm(obex, req, FALSE);
1201 static void handle_request(GObex *obex, GObexPacket *req)
1206 op = parse_request(obex, req);
1210 match = g_slist_find_custom(obex->req_handlers, GUINT_TO_POINTER(op),
1213 struct req_handler *handler = match->data;
1214 handler->func(obex, req, handler->user_data);
1218 op = -G_OBEX_RSP_NOT_IMPLEMENTED;
1221 g_obex_debug(G_OBEX_DEBUG_ERROR, "%s", g_obex_strerror(-op));
1222 g_obex_send_rsp(obex, -op, NULL, G_OBEX_HDR_INVALID);
1225 static gboolean read_stream(GObex *obex, GError **err)
1227 GIOChannel *io = obex->io;
1229 gsize rbytes, toread;
1233 if (obex->rx_data >= 3)
1237 toread = 3 - obex->rx_data;
1238 buf = (char *) &obex->rx_buf[obex->rx_data];
1240 status = g_io_channel_read_chars(io, buf, toread, &rbytes, NULL);
1241 if (status != G_IO_STATUS_NORMAL)
1244 obex->rx_data += rbytes;
1245 if (obex->rx_data < 3)
1248 memcpy(&u16, &buf[1], sizeof(u16));
1249 obex->rx_pkt_len = g_ntohs(u16);
1251 if (obex->rx_pkt_len > obex->rx_mtu) {
1254 g_set_error(err, G_OBEX_ERROR, G_OBEX_ERROR_PARSE_ERROR,
1255 "Too big incoming packet");
1256 g_obex_debug(G_OBEX_DEBUG_ERROR, "%s", (*err)->message);
1261 if (obex->rx_data >= obex->rx_pkt_len)
1265 toread = obex->rx_pkt_len - obex->rx_data;
1266 buf = (char *) &obex->rx_buf[obex->rx_data];
1268 status = g_io_channel_read_chars(io, buf, toread, &rbytes, NULL);
1269 if (status != G_IO_STATUS_NORMAL)
1272 obex->rx_data += rbytes;
1273 } while (rbytes > 0 && obex->rx_data < obex->rx_pkt_len);
1276 g_obex_dump(G_OBEX_DEBUG_DATA, ">", obex->rx_buf, obex->rx_data);
1281 static gboolean read_packet(GObex *obex, GError **err)
1283 GIOChannel *io = obex->io;
1284 GError *read_err = NULL;
1289 if (obex->rx_data > 0) {
1290 g_set_error(err, G_OBEX_ERROR, G_OBEX_ERROR_PARSE_ERROR,
1291 "RX buffer not empty before reading packet");
1295 status = g_io_channel_read_chars(io, (char *) obex->rx_buf,
1296 obex->rx_mtu, &rbytes, &read_err);
1297 if (status != G_IO_STATUS_NORMAL) {
1298 g_set_error(err, G_OBEX_ERROR, G_OBEX_ERROR_PARSE_ERROR,
1299 "Unable to read data: %s", read_err->message);
1300 g_error_free(read_err);
1304 obex->rx_data += rbytes;
1307 g_set_error(err, G_OBEX_ERROR, G_OBEX_ERROR_PARSE_ERROR,
1308 "Incomplete packet received");
1312 memcpy(&u16, &obex->rx_buf[1], sizeof(u16));
1313 obex->rx_pkt_len = g_ntohs(u16);
1315 if (obex->rx_pkt_len != rbytes) {
1316 g_set_error(err, G_OBEX_ERROR, G_OBEX_ERROR_PARSE_ERROR,
1317 "Data size doesn't match packet size (%zu != %u)",
1318 rbytes, obex->rx_pkt_len);
1322 g_obex_dump(G_OBEX_DEBUG_DATA, ">", obex->rx_buf, obex->rx_data);
1327 g_obex_debug(G_OBEX_DEBUG_ERROR, "%s", (*err)->message);
1332 static gboolean incoming_data(GIOChannel *io, GIOCondition cond,
1335 GObex *obex = user_data;
1337 ssize_t header_offset;
1341 if (cond & G_IO_NVAL)
1344 if (cond & (G_IO_HUP | G_IO_ERR)) {
1345 err = g_error_new(G_OBEX_ERROR, G_OBEX_ERROR_DISCONNECTED,
1346 "Transport got disconnected");
1350 if (!obex->read(obex, &err))
1353 if (obex->rx_data < 3 || obex->rx_data < obex->rx_pkt_len)
1356 obex->rx_last_op = obex->rx_buf[0] & ~FINAL_BIT;
1358 if (obex->pending_req) {
1359 struct pending_pkt *p = obex->pending_req;
1360 opcode = g_obex_packet_get_operation(p->pkt, NULL);
1361 header_offset = rsp_header_offset(opcode);
1363 opcode = obex->rx_last_op;
1364 /* Unexpected response -- fail silently */
1365 if (opcode > 0x1f && opcode != G_OBEX_OP_ABORT) {
1369 header_offset = req_header_offset(opcode);
1372 if (header_offset < 0) {
1373 err = g_error_new(G_OBEX_ERROR, G_OBEX_ERROR_PARSE_ERROR,
1374 "Unknown header offset for opcode 0x%02x",
1379 pkt = g_obex_packet_decode(obex->rx_buf, obex->rx_data, header_offset,
1380 G_OBEX_DATA_REF, &err);
1384 /* Protect against user callback freeing the object */
1387 if (obex->pending_req)
1388 handle_response(obex, NULL, pkt);
1390 handle_request(obex, pkt);
1400 g_obex_packet_free(pkt);
1406 g_obex_debug(G_OBEX_DEBUG_ERROR, "%s", err->message);
1408 g_io_channel_unref(obex->io);
1410 obex->io_source = 0;
1413 /* Protect against user callback freeing the object */
1416 if (obex->pending_req)
1417 handle_response(obex, err, NULL);
1419 if (obex->disconn_func)
1420 obex->disconn_func(obex, err, obex->disconn_func_data);
1429 static GDebugKey keys[] = {
1430 { "error", G_OBEX_DEBUG_ERROR },
1431 { "command", G_OBEX_DEBUG_COMMAND },
1432 { "transfer", G_OBEX_DEBUG_TRANSFER },
1433 { "header", G_OBEX_DEBUG_HEADER },
1434 { "packet", G_OBEX_DEBUG_PACKET },
1435 { "data", G_OBEX_DEBUG_DATA },
1436 { "apparam", G_OBEX_DEBUG_APPARAM },
1439 GObex *g_obex_new(GIOChannel *io, GObexTransportType transport_type,
1440 gssize io_rx_mtu, gssize io_tx_mtu)
1445 if (gobex_debug == 0) {
1446 const char *env = g_getenv("GOBEX_DEBUG");
1449 gobex_debug = g_parse_debug_string(env, keys, 7);
1450 g_setenv("G_MESSAGES_DEBUG", "gobex", FALSE);
1452 gobex_debug = G_OBEX_DEBUG_NONE;
1455 g_obex_debug(G_OBEX_DEBUG_COMMAND, "");
1460 if (io_rx_mtu >= 0 && io_rx_mtu < G_OBEX_MINIMUM_MTU)
1463 if (io_tx_mtu >= 0 && io_tx_mtu < G_OBEX_MINIMUM_MTU)
1466 obex = g_new0(GObex, 1);
1468 obex->io = g_io_channel_ref(io);
1469 obex->ref_count = 1;
1470 obex->conn_id = CONNID_INVALID;
1471 obex->rx_last_op = G_OBEX_OP_NONE;
1473 obex->io_rx_mtu = io_rx_mtu;
1474 obex->io_tx_mtu = io_tx_mtu;
1476 if (io_rx_mtu > G_OBEX_MAXIMUM_MTU)
1477 obex->rx_mtu = G_OBEX_MAXIMUM_MTU;
1478 else if (io_rx_mtu < G_OBEX_MINIMUM_MTU)
1479 obex->rx_mtu = G_OBEX_DEFAULT_MTU;
1481 obex->rx_mtu = io_rx_mtu;
1483 obex->tx_mtu = G_OBEX_MINIMUM_MTU;
1485 obex->tx_queue = g_queue_new();
1486 obex->rx_buf = g_malloc(obex->rx_mtu);
1487 obex->tx_buf = g_malloc(obex->tx_mtu);
1489 switch (transport_type) {
1490 case G_OBEX_TRANSPORT_STREAM:
1491 obex->read = read_stream;
1492 obex->write = write_stream;
1494 case G_OBEX_TRANSPORT_PACKET:
1495 obex->use_srm = TRUE;
1496 obex->read = read_packet;
1497 obex->write = write_packet;
1504 g_io_channel_set_encoding(io, NULL, NULL);
1505 g_io_channel_set_buffered(io, FALSE);
1506 cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
1507 obex->io_source = g_io_add_watch(io, cond, incoming_data, obex);
1512 GObex *g_obex_ref(GObex *obex)
1519 refs = __sync_add_and_fetch(&obex->ref_count, 1);
1521 g_obex_debug(G_OBEX_DEBUG_COMMAND, "ref %u", refs);
1526 static void tx_queue_free(void *data, void *user_data)
1528 pending_pkt_free(data);
1531 void g_obex_unref(GObex *obex)
1535 refs = __sync_sub_and_fetch(&obex->ref_count, 1);
1537 g_obex_debug(G_OBEX_DEBUG_COMMAND, "ref %u", refs);
1542 g_slist_free_full(obex->req_handlers, g_free);
1544 g_queue_foreach(obex->tx_queue, tx_queue_free, NULL);
1545 g_queue_free(obex->tx_queue);
1547 if (obex->io != NULL)
1548 g_io_channel_unref(obex->io);
1550 if (obex->io_source > 0)
1551 g_source_remove(obex->io_source);
1553 if (obex->write_source > 0)
1554 g_source_remove(obex->write_source);
1556 g_free(obex->rx_buf);
1557 g_free(obex->tx_buf);
1560 if (obex->pending_req)
1561 pending_pkt_free(obex->pending_req);
1564 g_obex_apparam_free(obex->authchal);
1569 /* Higher level functions */
1570 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
1571 void g_obex_io_shutdown(GObex *obex)
1574 if (obex->io != NULL)
1575 g_io_channel_shutdown(obex->io, FALSE, &err);
1578 guint g_obex_connect(GObex *obex, GObexResponseFunc func, gpointer user_data,
1579 GError **err, guint first_hdr_id, ...)
1582 struct connect_data data;
1585 g_obex_debug(G_OBEX_DEBUG_COMMAND, "");
1587 va_start(args, first_hdr_id);
1588 req = g_obex_packet_new_valist(G_OBEX_OP_CONNECT, TRUE,
1589 first_hdr_id, args);
1592 init_connect_data(obex, &data);
1593 g_obex_packet_set_data(req, &data, sizeof(data), G_OBEX_DATA_COPY);
1595 return g_obex_send_req(obex, req, -1, func, user_data, err);
1598 guint g_obex_disconnect(GObex *obex, GObexResponseFunc func, gpointer user_data,
1603 g_obex_debug(G_OBEX_DEBUG_COMMAND, "");
1605 req = g_obex_packet_new(G_OBEX_OP_DISCONNECT, TRUE, G_OBEX_HDR_INVALID);
1607 return g_obex_send_req(obex, req, -1, func, user_data, err);
1610 guint g_obex_setpath(GObex *obex, const char *path, GObexResponseFunc func,
1611 gpointer user_data, GError **err)
1614 struct setpath_data data;
1617 g_obex_debug(G_OBEX_DEBUG_COMMAND, "conn %u", obex->conn_id);
1619 req = g_obex_packet_new(G_OBEX_OP_SETPATH, TRUE, G_OBEX_HDR_INVALID);
1621 memset(&data, 0, sizeof(data));
1623 if (path != NULL && strncmp("..", path, 2) == 0) {
1625 folder = (path[2] == '/') ? &path[3] : NULL;
1631 if (folder != NULL) {
1633 hdr = g_obex_header_new_unicode(G_OBEX_HDR_NAME, folder);
1634 g_obex_packet_add_header(req, hdr);
1637 g_obex_packet_set_data(req, &data, sizeof(data), G_OBEX_DATA_COPY);
1639 return g_obex_send_req(obex, req, -1, func, user_data, err);
1642 guint g_obex_mkdir(GObex *obex, const char *path, GObexResponseFunc func,
1643 gpointer user_data, GError **err)
1646 struct setpath_data data;
1648 g_obex_debug(G_OBEX_DEBUG_COMMAND, "conn %u", obex->conn_id);
1650 req = g_obex_packet_new(G_OBEX_OP_SETPATH, TRUE, G_OBEX_HDR_NAME, path,
1651 G_OBEX_HDR_INVALID);
1653 memset(&data, 0, sizeof(data));
1654 g_obex_packet_set_data(req, &data, sizeof(data), G_OBEX_DATA_COPY);
1656 return g_obex_send_req(obex, req, -1, func, user_data, err);
1659 guint g_obex_delete(GObex *obex, const char *name, GObexResponseFunc func,
1660 gpointer user_data, GError **err)
1664 g_obex_debug(G_OBEX_DEBUG_COMMAND, "conn %u", obex->conn_id);
1666 req = g_obex_packet_new(G_OBEX_OP_PUT, TRUE, G_OBEX_HDR_NAME, name,
1667 G_OBEX_HDR_INVALID);
1669 return g_obex_send_req(obex, req, -1, func, user_data, err);
1672 guint g_obex_copy(GObex *obex, const char *name, const char *dest,
1673 GObexResponseFunc func, gpointer user_data,
1678 g_obex_debug(G_OBEX_DEBUG_COMMAND, "conn %u", obex->conn_id);
1680 req = g_obex_packet_new(G_OBEX_OP_ACTION, TRUE,
1681 G_OBEX_HDR_ACTION, G_OBEX_ACTION_COPY,
1682 G_OBEX_HDR_NAME, name,
1683 G_OBEX_HDR_DESTNAME, dest,
1684 G_OBEX_HDR_INVALID);
1686 return g_obex_send_req(obex, req, -1, func, user_data, err);
1689 guint g_obex_move(GObex *obex, const char *name, const char *dest,
1690 GObexResponseFunc func, gpointer user_data,
1695 g_obex_debug(G_OBEX_DEBUG_COMMAND, "conn %u", obex->conn_id);
1697 req = g_obex_packet_new(G_OBEX_OP_ACTION, TRUE,
1698 G_OBEX_HDR_ACTION, G_OBEX_ACTION_MOVE,
1699 G_OBEX_HDR_NAME, name,
1700 G_OBEX_HDR_DESTNAME, dest,
1701 G_OBEX_HDR_INVALID);
1703 return g_obex_send_req(obex, req, -1, func, user_data, err);
1706 guint g_obex_abort(GObex *obex, GObexResponseFunc func, gpointer user_data,
1711 req = g_obex_packet_new(G_OBEX_OP_ABORT, TRUE, G_OBEX_HDR_INVALID);
1713 return g_obex_send_req(obex, req, -1, func, user_data, err);
1716 guint8 g_obex_errno_to_rsp(int err)
1720 return G_OBEX_RSP_SUCCESS;
1723 return G_OBEX_RSP_FORBIDDEN;
1725 return G_OBEX_RSP_NOT_FOUND;
1728 return G_OBEX_RSP_BAD_REQUEST;
1730 return G_OBEX_RSP_SERVICE_UNAVAILABLE;
1732 return G_OBEX_RSP_NOT_IMPLEMENTED;
1735 return G_OBEX_RSP_PRECONDITION_FAILED;
1737 return G_OBEX_RSP_INTERNAL_SERVER_ERROR;