upload tizen1.0 source
[profile/ivi/obexd.git] / gobex / gobex.c
1 /*
2  *
3  *  OBEX library with GLib integration
4  *
5  *  Copyright (C) 2011  Intel Corporation. All rights reserved.
6  *
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.
10  *
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.
15  *
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
19  *
20  */
21
22 #ifdef HAVE_CONFIG_H
23 # include "config.h"
24 #endif
25
26 #include <unistd.h>
27 #include <string.h>
28 #include <errno.h>
29
30 #include "gobex.h"
31 #include "glib-helper.h"
32 #include "gobex-debug.h"
33
34 #define G_OBEX_DEFAULT_MTU      4096
35 #define G_OBEX_MINIMUM_MTU      255
36 #define G_OBEX_MAXIMUM_MTU      65535
37
38 #define G_OBEX_DEFAULT_TIMEOUT  10
39 #define G_OBEX_ABORT_TIMEOUT    5
40
41 #define G_OBEX_OP_NONE          0xff
42
43 #define FINAL_BIT               0x80
44
45 #define CONNID_INVALID          0xffffffff
46
47 guint gobex_debug = 0;
48
49 struct srm_config {
50         guint8 op;
51         gboolean enabled;
52         guint8 srm;
53         guint8 srmp;
54         gboolean outgoing;
55 };
56
57 struct _GObex {
58         gint ref_count;
59         GIOChannel *io;
60         guint io_source;
61
62         gboolean (*read) (GObex *obex, GError **err);
63         gboolean (*write) (GObex *obex, GError **err);
64
65         guint8 *rx_buf;
66         size_t rx_data;
67         guint16 rx_pkt_len;
68         guint8 rx_last_op;
69
70         guint8 *tx_buf;
71         size_t tx_data;
72         size_t tx_sent;
73
74         gboolean suspended;
75         gboolean use_srm;
76
77         struct srm_config *srm;
78
79         guint write_source;
80
81         gssize io_rx_mtu;
82         gssize io_tx_mtu;
83
84         guint16 rx_mtu;
85         guint16 tx_mtu;
86
87         guint32 conn_id;
88
89         GQueue *tx_queue;
90
91         GSList *req_handlers;
92
93         GObexFunc disconn_func;
94         gpointer disconn_func_data;
95
96         struct pending_pkt *pending_req;
97 };
98
99 struct pending_pkt {
100         guint id;
101         GObex *obex;
102         GObexPacket *pkt;
103         guint timeout;
104         guint timeout_id;
105         GObexResponseFunc rsp_func;
106         gpointer rsp_data;
107         gboolean cancelled;
108 };
109
110 struct req_handler {
111         guint id;
112         guint8 opcode;
113         GObexRequestFunc func;
114         gpointer user_data;
115 };
116
117 struct connect_data {
118         guint8 version;
119         guint8 flags;
120         guint16 mtu;
121 } __attribute__ ((packed));
122
123 struct setpath_data {
124         guint8 flags;
125         guint8 constants;
126 } __attribute__ ((packed));
127
128 static struct error_code {
129         guint8 code;
130         const char *name;
131 } obex_errors[] = {
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" },
170         { 0x00,                                 NULL }
171 };
172
173 const char *g_obex_strerror(guint8 err_code)
174 {
175         struct error_code *error;
176
177         for (error = obex_errors; error->name != NULL; error++) {
178                 if (error->code == err_code)
179                         return error->name;
180         }
181
182         return "<unknown>";
183 }
184
185 static ssize_t req_header_offset(guint8 opcode)
186 {
187         switch (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:
193         case G_OBEX_OP_PUT:
194         case G_OBEX_OP_GET:
195         case G_OBEX_OP_SESSION:
196         case G_OBEX_OP_ABORT:
197         case G_OBEX_OP_ACTION:
198                 return 0;
199         default:
200                 return -1;
201         }
202 }
203
204 static ssize_t rsp_header_offset(guint8 opcode)
205 {
206         switch (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:
211         case G_OBEX_OP_PUT:
212         case G_OBEX_OP_GET:
213         case G_OBEX_OP_SESSION:
214         case G_OBEX_OP_ABORT:
215         case G_OBEX_OP_ACTION:
216                 return 0;
217         default:
218                 return -1;
219         }
220 }
221
222 static void pending_pkt_free(struct pending_pkt *p)
223 {
224         if (p->obex != NULL)
225                 g_obex_unref(p->obex);
226
227         if (p->timeout_id > 0)
228                 g_source_remove(p->timeout_id);
229
230         g_obex_packet_free(p->pkt);
231
232         g_free(p);
233 }
234
235 static gboolean req_timeout(gpointer user_data)
236 {
237         GObex *obex = user_data;
238         struct pending_pkt *p = obex->pending_req;
239         GError *err;
240
241         g_assert(p != NULL);
242
243         obex->pending_req = NULL;
244
245         err = g_error_new(G_OBEX_ERROR, G_OBEX_ERROR_TIMEOUT,
246                                         "Timed out waiting for response");
247
248         g_obex_debug(G_OBEX_DEBUG_ERROR, "%s", err->message);
249
250 #ifndef TIZEN_PATCH
251         obex->pending_req = NULL;
252 #endif
253
254         if (p->rsp_func)
255                 p->rsp_func(obex, err, NULL, p->rsp_data);
256
257         g_error_free(err);
258         pending_pkt_free(p);
259
260         return FALSE;
261 }
262
263 static gboolean write_stream(GObex *obex, GError **err)
264 {
265         GIOStatus status;
266         gsize bytes_written;
267         gchar *buf;
268
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)
273                 return FALSE;
274
275         g_obex_dump("<", buf, bytes_written);
276
277         obex->tx_sent += bytes_written;
278         obex->tx_data -= bytes_written;
279
280         return TRUE;
281 }
282
283 static gboolean write_packet(GObex *obex, GError **err)
284 {
285         GIOStatus status;
286         gsize bytes_written;
287         gchar *buf;
288
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)
293                 return FALSE;
294
295         if (bytes_written != obex->tx_data)
296                 return FALSE;
297
298         g_obex_dump("<", buf, bytes_written);
299
300         obex->tx_sent += bytes_written;
301         obex->tx_data -= bytes_written;
302
303         return TRUE;
304 }
305
306 static gboolean write_data(GIOChannel *io, GIOCondition cond,
307                                                         gpointer user_data)
308 {
309         GObex *obex = user_data;
310
311         if (cond & G_IO_NVAL)
312                 return FALSE;
313
314         if (cond & (G_IO_HUP | G_IO_ERR))
315                 goto stop_tx;
316
317         if (obex->tx_data == 0) {
318                 struct pending_pkt *p = g_queue_pop_head(obex->tx_queue);
319                 ssize_t len;
320
321                 if (p == NULL)
322                         goto stop_tx;
323
324                 if (g_obex_srm_active(obex))
325                         goto encode;
326
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);
330                         goto stop_tx;
331                 }
332
333 encode:
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);
338                         goto stop_tx;
339                 }
340
341                 if (len < 0) {
342                         pending_pkt_free(p);
343                         goto done;
344                 }
345
346                 if (p->id > 0) {
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,
351                                                         req_timeout, obex);
352                 } else
353                         pending_pkt_free(p);
354
355                 obex->tx_data = len;
356                 obex->tx_sent = 0;
357         }
358
359         if (obex->suspended) {
360                 obex->write_source = 0;
361                 return FALSE;
362         }
363
364         if (!obex->write(obex, NULL))
365                 goto stop_tx;
366
367 done:
368         if (obex->tx_data > 0 || g_queue_get_length(obex->tx_queue) > 0)
369                 return TRUE;
370
371 stop_tx:
372         obex->rx_last_op = G_OBEX_OP_NONE;
373         obex->tx_data = 0;
374         obex->write_source = 0;
375         return FALSE;
376 }
377
378 static void enable_tx(GObex *obex)
379 {
380         GIOCondition cond;
381
382         if (obex->suspended)
383                 return;
384
385         if (obex->write_source > 0)
386                 return;
387
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);
390 }
391
392 static gboolean g_obex_send_internal(GObex *obex, struct pending_pkt *p,
393                                                                 GError **err)
394 {
395
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);
400                 return FALSE;
401         }
402
403         if (g_obex_packet_get_operation(p->pkt, NULL) == G_OBEX_OP_ABORT)
404                 g_queue_push_head(obex->tx_queue, p);
405         else
406                 g_queue_push_tail(obex->tx_queue, p);
407
408         if (obex->pending_req == NULL || p->id == 0)
409                 enable_tx(obex);
410
411         return TRUE;
412 }
413
414 static void init_connect_data(GObex *obex, struct connect_data *data)
415 {
416         guint16 u16;
417
418         memset(data, 0, sizeof(*data));
419
420         data->version = 0x10;
421         data->flags = 0;
422
423         u16 = g_htons(obex->rx_mtu);
424         memcpy(&data->mtu, &u16, sizeof(u16));
425 }
426
427 static void prepare_connect_rsp(GObex *obex, GObexPacket *rsp)
428 {
429         GObexHeader *connid;
430         struct connect_data data;
431         static guint32 next_connid = 1;
432
433         init_connect_data(obex, &data);
434         g_obex_packet_set_data(rsp, &data, sizeof(data), G_OBEX_DATA_COPY);
435
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);
439                 return;
440         }
441
442         obex->conn_id = next_connid++;
443
444         connid = g_obex_header_new_uint32(G_OBEX_HDR_CONNECTION,
445                                                         obex->conn_id);
446         g_obex_packet_prepend_header(rsp, connid);
447 }
448
449 static void set_srmp(GObex *obex, guint8 srmp, gboolean outgoing)
450 {
451         struct srm_config *config = obex->srm;
452
453         if (config == NULL)
454                 return;
455
456         /* Dont't reset if direction doesn't match */
457         if (srmp > G_OBEX_SRMP_NEXT_WAIT && config->outgoing != outgoing)
458                 return;
459
460         config->srmp = srmp;
461         config->outgoing = outgoing;
462 }
463
464 static void set_srm(GObex *obex, guint8 op, guint8 srm)
465 {
466         struct srm_config *config = obex->srm;
467         gboolean enable;
468
469         if (config == NULL) {
470                 if (srm == G_OBEX_SRM_DISABLE)
471                         return;
472
473                 config = g_new0(struct srm_config, 1);
474                 config->op = op;
475                 config->srm = srm;
476                 obex->srm = config;
477                 return;
478         }
479
480         /* Indicate response, treat it as request */
481         if (config->srm == G_OBEX_SRM_INDICATE) {
482                 if (srm != G_OBEX_SRM_ENABLE)
483                         goto done;
484                 config->srm = srm;
485                 return;
486         }
487
488         enable = (srm == G_OBEX_SRM_ENABLE);
489         if (config->enabled == enable)
490                 goto done;
491
492         config->enabled = enable;
493
494         g_obex_debug(G_OBEX_DEBUG_COMMAND, "SRM %s", config->enabled ?
495                                                 "Enabled" : "Disabled");
496
497 done:
498         if (config->enabled)
499                 return;
500
501         g_free(obex->srm);
502         obex->srm = NULL;
503 }
504
505 static void setup_srm(GObex *obex, GObexPacket *pkt, gboolean outgoing)
506 {
507         GObexHeader *hdr;
508         guint8 op;
509         gboolean final;
510
511         if (!obex->use_srm)
512                 return;
513
514         op = g_obex_packet_get_operation(pkt, &final);
515
516         hdr = g_obex_packet_get_header(pkt, G_OBEX_HDR_SRM);
517         if (hdr != NULL) {
518                 guint8 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);
522         }
523
524         hdr = g_obex_packet_get_header(pkt, G_OBEX_HDR_SRMP);
525         if (hdr != NULL) {
526                 guint8 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);
530         } else
531                 set_srmp(obex, -1, outgoing);
532
533         if (obex->srm == NULL || !obex->srm->enabled || !final)
534                 return;
535
536         switch (obex->srm->op) {
537         case G_OBEX_OP_CONNECT:
538                 return;
539         default:
540                 if (op <= G_OBEX_RSP_CONTINUE)
541                         return;
542         }
543
544         set_srm(obex, op, G_OBEX_SRM_DISABLE);
545 }
546
547 static void prepare_srm_rsp(GObex *obex, GObexPacket *pkt)
548 {
549         GObexHeader *hdr;
550
551         if (!obex->use_srm || obex->srm == NULL)
552                 return;
553
554         if (obex->srm->enabled)
555                 return;
556
557         hdr = g_obex_packet_get_header(pkt, G_OBEX_HDR_SRM);
558         if (hdr != NULL)
559                 return;
560
561         hdr = g_obex_header_new_uint8(G_OBEX_HDR_SRM, G_OBEX_SRM_ENABLE);
562         g_obex_packet_prepend_header(pkt, hdr);
563 }
564
565 gboolean g_obex_send(GObex *obex, GObexPacket *pkt, GError **err)
566 {
567         struct pending_pkt *p;
568         gboolean ret;
569
570         g_obex_debug(G_OBEX_DEBUG_COMMAND, "conn %u", obex->conn_id);
571
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);
576                 return FALSE;
577         }
578
579         switch (obex->rx_last_op) {
580         case G_OBEX_OP_CONNECT:
581                 prepare_connect_rsp(obex, pkt);
582         case G_OBEX_OP_GET:
583         case G_OBEX_OP_PUT:
584                 prepare_srm_rsp(obex, pkt);
585                 break;
586         }
587
588         setup_srm(obex, pkt, TRUE);
589
590         p = g_new0(struct pending_pkt, 1);
591         p->pkt = pkt;
592
593         ret = g_obex_send_internal(obex, p, err);
594         if (ret == FALSE)
595                 pending_pkt_free(p);
596
597         return ret;
598 }
599
600 static void prepare_srm_req(GObex *obex, GObexPacket *pkt)
601 {
602         GObexHeader *hdr;
603
604         if (!obex->use_srm)
605                 return;
606
607         if (obex->srm != NULL && obex->srm->enabled)
608                 return;
609
610         hdr = g_obex_packet_get_header(pkt, G_OBEX_HDR_SRM);
611         if (hdr != NULL)
612                 return;
613
614         hdr = g_obex_header_new_uint8(G_OBEX_HDR_SRM, G_OBEX_SRM_ENABLE);
615         g_obex_packet_prepend_header(pkt, hdr);
616 }
617
618 guint g_obex_send_req(GObex *obex, GObexPacket *req, gint timeout,
619                         GObexResponseFunc func, gpointer user_data,
620                         GError **err)
621 {
622         GObexHeader *hdr;
623         struct pending_pkt *p;
624         static guint id = 1;
625         guint8 op;
626
627         g_obex_debug(G_OBEX_DEBUG_COMMAND, "conn %u", obex->conn_id);
628
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);
633         }
634
635         setup_srm(obex, req, TRUE);
636
637         if (obex->conn_id == CONNID_INVALID)
638                 goto create_pending;
639
640         if (obex->rx_last_op == G_OBEX_RSP_CONTINUE)
641                 goto create_pending;
642
643         if (g_obex_srm_active(obex) && obex->pending_req != NULL)
644                 goto create_pending;
645
646         hdr = g_obex_packet_get_header(req, G_OBEX_HDR_CONNECTION);
647         if (hdr != NULL)
648                 goto create_pending;
649
650         hdr = g_obex_header_new_uint32(G_OBEX_HDR_CONNECTION, obex->conn_id);
651         g_obex_packet_prepend_header(req, hdr);
652
653 create_pending:
654         p = g_new0(struct pending_pkt, 1);
655
656         p->pkt = req;
657         p->id = id++;
658         p->rsp_func = func;
659         p->rsp_data = user_data;
660
661         if (timeout < 0)
662                 p->timeout = G_OBEX_DEFAULT_TIMEOUT;
663         else
664                 p->timeout = timeout;
665
666         if (!g_obex_send_internal(obex, p, err)) {
667                 pending_pkt_free(p);
668                 return 0;
669         }
670
671         return p->id;
672 }
673
674 static gint pending_pkt_cmp(gconstpointer a, gconstpointer b)
675 {
676         const struct pending_pkt *p = a;
677         guint id = GPOINTER_TO_UINT(b);
678
679         return (p->id - id);
680 }
681
682 static gboolean pending_req_abort(GObex *obex, GError **err)
683 {
684         struct pending_pkt *p = obex->pending_req;
685         GObexPacket *req;
686
687         if (p->cancelled)
688                 return TRUE;
689
690         p->cancelled = TRUE;
691
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);
695
696         req = g_obex_packet_new(G_OBEX_OP_ABORT, TRUE, G_OBEX_HDR_INVALID);
697
698         return g_obex_send(obex, req, err);
699 }
700
701 static gboolean cancel_complete(gpointer user_data)
702 {
703         struct pending_pkt *p = user_data;
704         GObex *obex = p->obex;
705         GError *err;
706
707         g_assert(p->rsp_func != NULL);
708
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);
712
713         g_error_free(err);
714
715         pending_pkt_free(p);
716
717         return FALSE;
718 }
719
720 gboolean g_obex_cancel_req(GObex *obex, guint req_id, gboolean remove_callback)
721 {
722         GList *match;
723         struct pending_pkt *p;
724
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;
730                 }
731
732                 return TRUE;
733         }
734
735         match = g_queue_find_custom(obex->tx_queue, GUINT_TO_POINTER(req_id),
736                                                         pending_pkt_cmp);
737         if (match == NULL)
738                 return FALSE;
739
740         p = match->data;
741
742         g_queue_delete_link(obex->tx_queue, match);
743
744 immediate_completion:
745         p->cancelled = TRUE;
746         p->obex = g_obex_ref(obex);
747
748         if (remove_callback || p->rsp_func == NULL)
749                 pending_pkt_free(p);
750         else
751                 g_idle_add(cancel_complete, p);
752
753         return TRUE;
754 }
755
756 gboolean g_obex_send_rsp(GObex *obex, guint8 rspcode, GError **err,
757                                                 guint8 first_hdr_type, ...)
758 {
759         GObexPacket *rsp;
760         va_list args;
761
762         va_start(args, first_hdr_type);
763         rsp = g_obex_packet_new_valist(rspcode, TRUE, first_hdr_type, args);
764         va_end(args);
765
766         return g_obex_send(obex, rsp, err);
767 }
768
769 void g_obex_set_disconnect_function(GObex *obex, GObexFunc func,
770                                                         gpointer user_data)
771 {
772         obex->disconn_func = func;
773         obex->disconn_func_data = user_data;
774 }
775
776 static gint req_handler_cmpop(gconstpointer a, gconstpointer b)
777 {
778         const struct req_handler *handler = a;
779         guint opcode = GPOINTER_TO_UINT(b);
780
781         return (gint) handler->opcode - (gint) opcode;
782 }
783
784 static gint req_handler_cmpid(gconstpointer a, gconstpointer b)
785 {
786         const struct req_handler *handler = a;
787         guint id = GPOINTER_TO_UINT(b);
788
789         return (gint) handler->id - (gint) id;
790 }
791
792 guint g_obex_add_request_function(GObex *obex, guint8 opcode,
793                                                 GObexRequestFunc func,
794                                                 gpointer user_data)
795 {
796         struct req_handler *handler;
797         static guint next_id = 1;
798
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;
804
805         obex->req_handlers = g_slist_prepend(obex->req_handlers, handler);
806
807         return handler->id;
808 }
809
810 gboolean g_obex_remove_request_function(GObex *obex, guint id)
811 {
812         struct req_handler *handler;
813         GSList *match;
814
815         match = g_slist_find_custom(obex->req_handlers, GUINT_TO_POINTER(id),
816                                                         req_handler_cmpid);
817         if (match == NULL)
818                 return FALSE;
819
820         handler = match->data;
821
822         obex->req_handlers = g_slist_delete_link(obex->req_handlers, match);
823         g_free(handler);
824
825         return TRUE;
826 }
827
828 void g_obex_suspend(GObex *obex)
829 {
830         g_obex_debug(G_OBEX_DEBUG_COMMAND, "conn %u", obex->conn_id);
831
832         if (obex->write_source > 0) {
833                 g_source_remove(obex->write_source);
834                 obex->write_source = 0;
835         }
836
837         obex->suspended = TRUE;
838 }
839
840 void g_obex_resume(GObex *obex)
841 {
842         g_obex_debug(G_OBEX_DEBUG_COMMAND, "conn %u", obex->conn_id);
843
844         obex->suspended = FALSE;
845
846         if (g_queue_get_length(obex->tx_queue) > 0 || obex->tx_data > 0)
847                 enable_tx(obex);
848 }
849
850 gboolean g_obex_srm_active(GObex *obex)
851 {
852         gboolean ret = FALSE;
853
854         if (obex->srm == NULL || !obex->srm->enabled)
855                 goto done;
856
857         if (obex->srm->srmp <= G_OBEX_SRMP_NEXT_WAIT)
858                 goto done;
859
860         ret = TRUE;
861 done:
862         g_obex_debug(G_OBEX_DEBUG_COMMAND, "%s", ret ? "yes" : "no");
863         return ret;
864 }
865
866 static void parse_connect_data(GObex *obex, GObexPacket *pkt)
867 {
868         const struct connect_data *data;
869         GObexHeader *connid;
870         guint16 u16;
871         size_t data_len;
872
873         data = g_obex_packet_get_data(pkt, &data_len);
874         if (data == NULL || data_len != sizeof(*data))
875                 return;
876
877         memcpy(&u16, &data->mtu, sizeof(u16));
878
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);
883
884         connid = g_obex_packet_get_header(pkt, G_OBEX_HDR_CONNECTION);
885         if (connid != NULL)
886                 g_obex_header_get_uint32(connid, &obex->conn_id);
887 }
888
889 static gboolean parse_response(GObex *obex, GObexPacket *rsp)
890 {
891         struct pending_pkt *p = obex->pending_req;
892         guint8 opcode, rspcode;
893         gboolean final;
894
895         rspcode = g_obex_packet_get_operation(rsp, &final);
896
897         opcode = g_obex_packet_get_operation(p->pkt, NULL);
898         if (opcode == G_OBEX_OP_CONNECT)
899                 parse_connect_data(obex, rsp);
900
901         setup_srm(obex, rsp, FALSE);
902
903         if (!g_obex_srm_active(obex))
904                 return final;
905
906         /*
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
910          */
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,
914                                                                         obex);
915                 return FALSE;
916         }
917
918         return final;
919 }
920
921 static void handle_response(GObex *obex, GError *err, GObexPacket *rsp)
922 {
923         struct pending_pkt *p = obex->pending_req;
924         gboolean disconn = err ? TRUE : FALSE, final_rsp = TRUE;
925
926         if (rsp != NULL)
927                 final_rsp = parse_response(obex, rsp);
928
929         if (p->cancelled)
930                 err = g_error_new(G_OBEX_ERROR, G_OBEX_ERROR_CANCELLED,
931                                         "The operation was cancelled");
932
933         if (err)
934                 g_obex_debug(G_OBEX_DEBUG_ERROR, "%s", err->message);
935
936         if (p->rsp_func) {
937                 p->rsp_func(obex, err, rsp, p->rsp_data);
938
939                 /* Check if user callback removed the request */
940                 if (p != obex->pending_req)
941                         return;
942         }
943
944         if (p->cancelled)
945                 g_error_free(err);
946
947         if (final_rsp) {
948                 pending_pkt_free(p);
949                 obex->pending_req = NULL;
950         }
951
952         if (!disconn && g_queue_get_length(obex->tx_queue) > 0)
953                 enable_tx(obex);
954 }
955
956 static gboolean check_connid(GObex *obex, GObexPacket *pkt)
957 {
958         GObexHeader *hdr;
959         guint32 id;
960
961         if (obex->conn_id == CONNID_INVALID)
962                 return TRUE;
963
964         hdr = g_obex_packet_get_header(pkt, G_OBEX_HDR_CONNECTION);
965         if (hdr == NULL)
966                 return TRUE;
967
968         g_obex_header_get_uint32(hdr, &id);
969
970         return obex->conn_id == id;
971 }
972
973 static int parse_request(GObex *obex, GObexPacket *req)
974 {
975         guint8 op;
976         gboolean final;
977
978         op = g_obex_packet_get_operation(req, &final);
979         switch (op) {
980         case G_OBEX_OP_CONNECT:
981                 parse_connect_data(obex, req);
982                 break;
983         case G_OBEX_OP_ABORT:
984                 break;
985         default:
986                 if (check_connid(obex, req))
987                         break;
988
989                 return -G_OBEX_RSP_SERVICE_UNAVAILABLE;
990         }
991
992         setup_srm(obex, req, FALSE);
993
994         return op;
995 }
996
997 static void handle_request(GObex *obex, GObexPacket *req)
998 {
999         GSList *match;
1000         int op;
1001
1002         op = parse_request(obex, req);
1003         if (op < 0)
1004                 goto fail;
1005
1006         match = g_slist_find_custom(obex->req_handlers, GUINT_TO_POINTER(op),
1007                                                         req_handler_cmpop);
1008         if (match) {
1009                 struct req_handler *handler = match->data;
1010                 handler->func(obex, req, handler->user_data);
1011                 return;
1012         }
1013
1014         op = -G_OBEX_RSP_NOT_IMPLEMENTED;
1015
1016 fail:
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);
1019 }
1020
1021 static gboolean read_stream(GObex *obex, GError **err)
1022 {
1023         GIOChannel *io = obex->io;
1024         GIOStatus status;
1025         gsize rbytes, toread;
1026         guint16 u16;
1027         gchar *buf;
1028
1029         if (obex->rx_data >= 3)
1030                 goto read_body;
1031
1032         rbytes = 0;
1033         toread = 3 - obex->rx_data;
1034         buf = (gchar *) &obex->rx_buf[obex->rx_data];
1035
1036         status = g_io_channel_read_chars(io, buf, toread, &rbytes, NULL);
1037         if (status != G_IO_STATUS_NORMAL)
1038                 return TRUE;
1039
1040         obex->rx_data += rbytes;
1041         if (obex->rx_data < 3)
1042                 goto done;
1043
1044         memcpy(&u16, &buf[1], sizeof(u16));
1045         obex->rx_pkt_len = g_ntohs(u16);
1046
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);
1051                 return FALSE;
1052         }
1053
1054 read_body:
1055         if (obex->rx_data >= obex->rx_pkt_len)
1056                 goto done;
1057
1058         do {
1059                 toread = obex->rx_pkt_len - obex->rx_data;
1060                 buf = (gchar *) &obex->rx_buf[obex->rx_data];
1061
1062                 status = g_io_channel_read_chars(io, buf, toread, &rbytes, NULL);
1063                 if (status != G_IO_STATUS_NORMAL)
1064                         goto done;
1065
1066                 obex->rx_data += rbytes;
1067         } while (rbytes > 0 && obex->rx_data < obex->rx_pkt_len);
1068
1069 done:
1070         g_obex_dump(">", obex->rx_buf, obex->rx_data);
1071
1072         return TRUE;
1073 }
1074
1075 static gboolean read_packet(GObex *obex, GError **err)
1076 {
1077         GIOChannel *io = obex->io;
1078         GError *read_err = NULL;
1079         GIOStatus status;
1080         gsize rbytes;
1081         guint16 u16;
1082
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");
1086                 goto fail;
1087         }
1088
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);
1095                 goto fail;
1096         }
1097
1098         obex->rx_data += rbytes;
1099
1100         if (rbytes < 3) {
1101                 g_set_error(err, G_OBEX_ERROR, G_OBEX_ERROR_PARSE_ERROR,
1102                                 "Incomplete packet received");
1103                 goto fail;
1104         }
1105
1106         memcpy(&u16, &obex->rx_buf[1], sizeof(u16));
1107         obex->rx_pkt_len = g_ntohs(u16);
1108
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);
1113                 return FALSE;
1114         }
1115
1116         g_obex_dump(">", obex->rx_buf, obex->rx_data);
1117
1118         return TRUE;
1119 fail:
1120         g_obex_debug(G_OBEX_DEBUG_ERROR, "%s", (*err)->message);
1121         return FALSE;
1122 }
1123
1124 static gboolean incoming_data(GIOChannel *io, GIOCondition cond,
1125                                                         gpointer user_data)
1126 {
1127         GObex *obex = user_data;
1128         GObexPacket *pkt;
1129         ssize_t header_offset;
1130         GError *err = NULL;
1131         guint8 opcode;
1132
1133         if (cond & G_IO_NVAL)
1134                 return FALSE;
1135
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");
1139                 goto failed;
1140         }
1141
1142         if (!obex->read(obex, &err))
1143                 goto failed;
1144
1145         if (obex->rx_data < 3 || obex->rx_data < obex->rx_pkt_len)
1146                 return TRUE;
1147
1148         obex->rx_last_op = obex->rx_buf[0] & ~FINAL_BIT;
1149
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);
1154         } else {
1155                 opcode = obex->rx_last_op;
1156                 /* Unexpected response -- fail silently */
1157 #ifdef TIZEN_PATCH
1158                 if (opcode > 0x1f && opcode != G_OBEX_OP_ABORT) {
1159 #else
1160                 if (opcode > 0x1f && opcode < 0xff) {
1161 #endif
1162                         obex->rx_data = 0;
1163                         return TRUE;
1164                 }
1165                 header_offset = req_header_offset(opcode);
1166         }
1167
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",
1171                                 opcode);
1172                 goto failed;
1173         }
1174
1175         pkt = g_obex_packet_decode(obex->rx_buf, obex->rx_data, header_offset,
1176                                                         G_OBEX_DATA_REF, &err);
1177         if (pkt == NULL)
1178                 goto failed;
1179
1180         /* Protect against user callback freeing the object */
1181         g_obex_ref(obex);
1182
1183         if (obex->pending_req)
1184                 handle_response(obex, NULL, pkt);
1185         else
1186                 handle_request(obex, pkt);
1187
1188         obex->rx_data = 0;
1189
1190         g_obex_unref(obex);
1191
1192         if (err != NULL)
1193                 g_error_free(err);
1194
1195         if (pkt != NULL)
1196                 g_obex_packet_free(pkt);
1197
1198         return TRUE;
1199
1200 failed:
1201         if (err)
1202                 g_obex_debug(G_OBEX_DEBUG_ERROR, "%s", err->message);
1203
1204         g_io_channel_unref(obex->io);
1205         obex->io = NULL;
1206         obex->io_source = 0;
1207         obex->rx_data = 0;
1208
1209         /* Protect against user callback freeing the object */
1210         g_obex_ref(obex);
1211
1212         if (obex->pending_req)
1213                 handle_response(obex, err, NULL);
1214
1215         if (obex->disconn_func)
1216                 obex->disconn_func(obex, err, obex->disconn_func_data);
1217
1218         g_obex_unref(obex);
1219
1220         g_error_free(err);
1221
1222         return FALSE;
1223 }
1224
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 },
1232 };
1233
1234 GObex *g_obex_new(GIOChannel *io, GObexTransportType transport_type,
1235                                         gssize io_rx_mtu, gssize io_tx_mtu)
1236 {
1237         GObex *obex;
1238         GIOCondition cond;
1239
1240         if (gobex_debug == 0) {
1241                 const char *env = g_getenv("GOBEX_DEBUG");
1242                 if (env)
1243                         gobex_debug = g_parse_debug_string(env, keys, 6);
1244                 else
1245                         gobex_debug = G_OBEX_DEBUG_NONE;
1246         }
1247
1248         g_obex_debug(G_OBEX_DEBUG_COMMAND, "");
1249
1250         if (io == NULL)
1251                 return NULL;
1252
1253         if (io_rx_mtu >= 0 && io_rx_mtu < G_OBEX_MINIMUM_MTU)
1254                 return NULL;
1255
1256         if (io_tx_mtu >= 0 && io_tx_mtu < G_OBEX_MINIMUM_MTU)
1257                 return NULL;
1258
1259         obex = g_new0(GObex, 1);
1260
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;
1265
1266         obex->io_rx_mtu = io_rx_mtu;
1267         obex->io_tx_mtu = io_tx_mtu;
1268
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;
1273         else
1274                 obex->rx_mtu = io_rx_mtu;
1275
1276         obex->tx_mtu = G_OBEX_MINIMUM_MTU;
1277
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);
1281
1282         switch (transport_type) {
1283         case G_OBEX_TRANSPORT_STREAM:
1284                 obex->read = read_stream;
1285                 obex->write = write_stream;
1286                 break;
1287         case G_OBEX_TRANSPORT_PACKET:
1288                 obex->use_srm = TRUE;
1289                 obex->read = read_packet;
1290                 obex->write = write_packet;
1291                 break;
1292         default:
1293                 g_obex_unref(obex);
1294                 return NULL;
1295         }
1296
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);
1301
1302         return obex;
1303 }
1304
1305 GObex *g_obex_ref(GObex *obex)
1306 {
1307         if (obex == NULL)
1308                 return NULL;
1309
1310         g_atomic_int_inc(&obex->ref_count);
1311
1312         g_obex_debug(G_OBEX_DEBUG_COMMAND, "ref %u", obex->ref_count);
1313
1314         return obex;
1315 }
1316
1317 void g_obex_unref(GObex *obex)
1318 {
1319 #ifdef TIZEN_PATCH
1320         gboolean last_ref, ret;
1321 #else
1322         gboolean last_ref;
1323 #endif
1324
1325         last_ref = g_atomic_int_dec_and_test(&obex->ref_count);
1326
1327         g_obex_debug(G_OBEX_DEBUG_COMMAND, "ref %u", obex->ref_count);
1328
1329         if (!last_ref)
1330                 return;
1331
1332         g_slist_free_full(obex->req_handlers, g_free);
1333
1334 #ifdef TIZEN_PATCH
1335         do {
1336                 ret = write_data(obex->io, G_IO_OUT, obex);
1337                 if (obex->pending_req && obex->pending_req->cancelled)
1338                         break;
1339         } while(ret);
1340 #endif
1341
1342         g_queue_foreach(obex->tx_queue, (GFunc) pending_pkt_free, NULL);
1343         g_queue_free(obex->tx_queue);
1344
1345         if (obex->io != NULL)
1346                 g_io_channel_unref(obex->io);
1347
1348         if (obex->io_source > 0)
1349                 g_source_remove(obex->io_source);
1350
1351         if (obex->write_source > 0)
1352                 g_source_remove(obex->write_source);
1353
1354         g_free(obex->rx_buf);
1355         g_free(obex->tx_buf);
1356         g_free(obex->srm);
1357
1358         if (obex->pending_req)
1359                 pending_pkt_free(obex->pending_req);
1360
1361         g_free(obex);
1362 }
1363
1364 /* Higher level functions */
1365
1366 guint g_obex_connect(GObex *obex, GObexResponseFunc func, gpointer user_data,
1367                                         GError **err, guint8 first_hdr_id, ...)
1368 {
1369         GObexPacket *req;
1370         struct connect_data data;
1371         va_list args;
1372
1373         g_obex_debug(G_OBEX_DEBUG_COMMAND, "");
1374
1375         va_start(args, first_hdr_id);
1376         req = g_obex_packet_new_valist(G_OBEX_OP_CONNECT, TRUE,
1377                                                         first_hdr_id, args);
1378         va_end(args);
1379
1380         init_connect_data(obex, &data);
1381         g_obex_packet_set_data(req, &data, sizeof(data), G_OBEX_DATA_COPY);
1382
1383         return g_obex_send_req(obex, req, -1, func, user_data, err);
1384 }
1385
1386 guint g_obex_setpath(GObex *obex, const char *path, GObexResponseFunc func,
1387                                         gpointer user_data, GError **err)
1388 {
1389         GObexPacket *req;
1390         struct setpath_data data;
1391         const char *folder;
1392
1393         g_obex_debug(G_OBEX_DEBUG_COMMAND, "conn %u", obex->conn_id);
1394
1395         req = g_obex_packet_new(G_OBEX_OP_SETPATH, TRUE, G_OBEX_HDR_INVALID);
1396
1397         memset(&data, 0, sizeof(data));
1398
1399         if (path != NULL && strncmp("..", path, 2) == 0) {
1400                 data.flags = 0x03;
1401                 folder = (path[2] == '/') ? &path[3] : NULL;
1402         } else {
1403                 data.flags = 0x02;
1404                 folder = path;
1405         }
1406
1407         if (folder != NULL) {
1408                 GObexHeader *hdr;
1409                 hdr = g_obex_header_new_unicode(G_OBEX_HDR_NAME, folder);
1410                 g_obex_packet_add_header(req, hdr);
1411         }
1412
1413         g_obex_packet_set_data(req, &data, sizeof(data), G_OBEX_DATA_COPY);
1414
1415         return g_obex_send_req(obex, req, -1, func, user_data, err);
1416 }
1417
1418 guint g_obex_mkdir(GObex *obex, const char *path, GObexResponseFunc func,
1419                                         gpointer user_data, GError **err)
1420 {
1421         GObexPacket *req;
1422         struct setpath_data data;
1423
1424         g_obex_debug(G_OBEX_DEBUG_COMMAND, "conn %u", obex->conn_id);
1425
1426         req = g_obex_packet_new(G_OBEX_OP_SETPATH, TRUE, G_OBEX_HDR_NAME, path,
1427                                                         G_OBEX_HDR_INVALID);
1428
1429         memset(&data, 0, sizeof(data));
1430         g_obex_packet_set_data(req, &data, sizeof(data), G_OBEX_DATA_COPY);
1431
1432         return g_obex_send_req(obex, req, -1, func, user_data, err);
1433 }
1434
1435 guint g_obex_delete(GObex *obex, const char *name, GObexResponseFunc func,
1436                                         gpointer user_data, GError **err)
1437 {
1438         GObexPacket *req;
1439
1440         g_obex_debug(G_OBEX_DEBUG_COMMAND, "conn %u", obex->conn_id);
1441
1442         req = g_obex_packet_new(G_OBEX_OP_PUT, TRUE, G_OBEX_HDR_NAME, name,
1443                                                         G_OBEX_HDR_INVALID);
1444
1445         return g_obex_send_req(obex, req, -1, func, user_data, err);
1446 }
1447
1448 guint g_obex_copy(GObex *obex, const char *name, const char *dest,
1449                         GObexResponseFunc func, gpointer user_data,
1450                         GError **err)
1451 {
1452         GObexPacket *req;
1453
1454         g_obex_debug(G_OBEX_DEBUG_COMMAND, "conn %u", obex->conn_id);
1455
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);
1461
1462         return g_obex_send_req(obex, req, -1, func, user_data, err);
1463 }
1464
1465 guint g_obex_move(GObex *obex, const char *name, const char *dest,
1466                         GObexResponseFunc func, gpointer user_data,
1467                         GError **err)
1468 {
1469         GObexPacket *req;
1470
1471         g_obex_debug(G_OBEX_DEBUG_COMMAND, "conn %u", obex->conn_id);
1472
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);
1478
1479         return g_obex_send_req(obex, req, -1, func, user_data, err);
1480 }