gobex: Fix segfault caused by interrupted transfer
[platform/upstream/bluez.git] / gobex / gobex-transfer.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 as published by
9  *  the Free Software Foundation; either version 2 of the License, or
10  *  (at your option) any later version.
11  *
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.
16  *
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
20  *
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <string.h>
28 #include <errno.h>
29
30 #include "gobex/gobex.h"
31 #include "gobex/gobex-debug.h"
32
33 #define FIRST_PACKET_TIMEOUT 60
34
35 static GSList *transfers = NULL;
36
37 static void transfer_response(GObex *obex, GError *err, GObexPacket *rsp,
38                                                         gpointer user_data);
39
40 struct transfer {
41         guint id;
42         guint8 opcode;
43
44         GObex *obex;
45
46         guint req_id;
47
48         guint put_id;
49         guint get_id;
50         guint abort_id;
51
52         GObexDataProducer data_producer;
53         GObexDataConsumer data_consumer;
54         GObexFunc complete_func;
55
56         gpointer user_data;
57 };
58
59 static void transfer_free(struct transfer *transfer)
60 {
61         g_obex_debug(G_OBEX_DEBUG_TRANSFER, "transfer %u", transfer->id);
62
63         transfers = g_slist_remove(transfers, transfer);
64
65         if (transfer->req_id > 0)
66                 g_obex_cancel_req(transfer->obex, transfer->req_id, TRUE);
67
68         if (transfer->put_id > 0)
69                 g_obex_remove_request_function(transfer->obex,
70                                                         transfer->put_id);
71
72         if (transfer->get_id > 0)
73                 g_obex_remove_request_function(transfer->obex,
74                                                         transfer->get_id);
75
76         if (transfer->abort_id > 0)
77                 g_obex_remove_request_function(transfer->obex,
78                                                         transfer->abort_id);
79
80         g_obex_unref(transfer->obex);
81         g_free(transfer);
82 }
83
84 static struct transfer *find_transfer(guint id)
85 {
86         GSList *l;
87
88         for (l = transfers; l != NULL; l = g_slist_next(l)) {
89                 struct transfer *t = l->data;
90                 if (t->id == id)
91                         return t;
92         }
93
94         return NULL;
95 }
96
97 static void transfer_complete(struct transfer *transfer, GError *err)
98 {
99         guint id = transfer->id;
100
101         g_obex_debug(G_OBEX_DEBUG_TRANSFER, "transfer %u", id);
102
103         if (err) {
104                 /* No further tx must be performed */
105                 g_obex_drop_tx_queue(transfer->obex);
106         }
107
108         transfer->complete_func(transfer->obex, err, transfer->user_data);
109         /* Check if the complete_func removed the transfer */
110         if (find_transfer(id) == NULL)
111                 return;
112
113         transfer_free(transfer);
114 }
115
116 static void transfer_abort_response(GObex *obex, GError *err, GObexPacket *rsp,
117                                                         gpointer user_data)
118 {
119         struct transfer *transfer = user_data;
120
121         g_obex_debug(G_OBEX_DEBUG_TRANSFER, "transfer %u", transfer->id);
122
123         transfer->req_id = 0;
124
125         /* Intentionally override error */
126         err = g_error_new(G_OBEX_ERROR, G_OBEX_ERROR_CANCELLED,
127                                                 "Operation was aborted");
128         g_obex_debug(G_OBEX_DEBUG_ERROR, "%s", err->message);
129         transfer_complete(transfer, err);
130         g_error_free(err);
131 }
132
133
134 static gssize put_get_data(void *buf, gsize len, gpointer user_data)
135 {
136         struct transfer *transfer = user_data;
137         GObexPacket *req;
138         GError *err = NULL;
139         gssize ret;
140
141         ret = transfer->data_producer(buf, len, transfer->user_data);
142         if (ret == 0 || ret == -EAGAIN)
143                 return ret;
144
145         if (ret > 0) {
146                 /* Check if SRM is active */
147                 if (!g_obex_srm_active(transfer->obex))
148                         return ret;
149
150                 /* Generate next packet */
151                 req = g_obex_packet_new(transfer->opcode, FALSE,
152                                                         G_OBEX_HDR_INVALID);
153                 g_obex_packet_add_body(req, put_get_data, transfer);
154                 transfer->req_id = g_obex_send_req(transfer->obex, req, -1,
155                                                 transfer_response, transfer,
156                                                 &err);
157                 goto done;
158         }
159
160         transfer->req_id = g_obex_abort(transfer->obex, transfer_abort_response,
161                                                                 transfer, &err);
162 done:
163         if (err != NULL) {
164                 transfer_complete(transfer, err);
165                 g_error_free(err);
166         }
167
168         return ret;
169 }
170
171 static gboolean handle_get_body(struct transfer *transfer, GObexPacket *rsp,
172                                                                 GError **err)
173 {
174         GObexHeader *body = g_obex_packet_get_body(rsp);
175         gboolean ret;
176         const guint8 *buf;
177         gsize len;
178
179         if (body == NULL)
180                 return TRUE;
181
182         g_obex_header_get_bytes(body, &buf, &len);
183         if (len == 0)
184                 return TRUE;
185
186         ret = transfer->data_consumer(buf, len, transfer->user_data);
187         if (ret == FALSE)
188                 g_set_error(err, G_OBEX_ERROR, G_OBEX_ERROR_CANCELLED,
189                                 "Data consumer callback failed");
190
191         return ret;
192 }
193
194 static void transfer_response(GObex *obex, GError *err, GObexPacket *rsp,
195                                                         gpointer user_data)
196 {
197         struct transfer *transfer = user_data;
198         GObexPacket *req;
199         gboolean rspcode, final;
200         guint id;
201
202         g_obex_debug(G_OBEX_DEBUG_TRANSFER, "transfer %u", transfer->id);
203
204         id = transfer->req_id;
205         transfer->req_id = 0;
206
207         if (err != NULL) {
208                 transfer_complete(transfer, err);
209                 return;
210         }
211
212         rspcode = g_obex_packet_get_operation(rsp, &final);
213         if (rspcode != G_OBEX_RSP_SUCCESS && rspcode != G_OBEX_RSP_CONTINUE) {
214                 err = g_error_new(G_OBEX_ERROR, rspcode, "%s",
215                                                 g_obex_strerror(rspcode));
216                 goto failed;
217         }
218
219         if (transfer->opcode == G_OBEX_OP_GET) {
220                 handle_get_body(transfer, rsp, &err);
221                 if (err != NULL)
222                         goto failed;
223         }
224
225         if (rspcode == G_OBEX_RSP_SUCCESS) {
226                 transfer_complete(transfer, NULL);
227                 return;
228         }
229
230         if (transfer->opcode == G_OBEX_OP_PUT) {
231                 req = g_obex_packet_new(transfer->opcode, FALSE,
232                                                         G_OBEX_HDR_INVALID);
233                 g_obex_packet_add_body(req, put_get_data, transfer);
234         } else if (!g_obex_srm_active(transfer->obex)) {
235                 req = g_obex_packet_new(transfer->opcode, TRUE,
236                                                         G_OBEX_HDR_INVALID);
237         } else {
238                 /* Keep id since request still outstanting */
239                 transfer->req_id = id;
240                 return;
241         }
242
243         transfer->req_id = g_obex_send_req(obex, req, -1, transfer_response,
244                                                         transfer, &err);
245 failed:
246         if (err != NULL) {
247                 g_obex_debug(G_OBEX_DEBUG_ERROR, "%s", err->message);
248                 transfer_complete(transfer, err);
249                 g_error_free(err);
250         }
251 }
252
253 static struct transfer *transfer_new(GObex *obex, guint8 opcode,
254                                 GObexFunc complete_func, gpointer user_data)
255 {
256         static guint next_id = 1;
257         struct transfer *transfer;
258
259         g_obex_debug(G_OBEX_DEBUG_TRANSFER, "obex %p opcode %u", obex, opcode);
260
261         transfer = g_new0(struct transfer, 1);
262
263         transfer->id = next_id++;
264         transfer->opcode = opcode;
265         transfer->obex = g_obex_ref(obex);
266         transfer->complete_func = complete_func;
267         transfer->user_data = user_data;
268
269         transfers = g_slist_append(transfers, transfer);
270
271         return transfer;
272 }
273
274 guint g_obex_put_req_pkt(GObex *obex, GObexPacket *req,
275                         GObexDataProducer data_func, GObexFunc complete_func,
276                         gpointer user_data, GError **err)
277 {
278         struct transfer *transfer;
279
280         g_obex_debug(G_OBEX_DEBUG_TRANSFER, "obex %p", obex);
281
282         if (g_obex_packet_get_operation(req, NULL) != G_OBEX_OP_PUT)
283                 return 0;
284
285         transfer = transfer_new(obex, G_OBEX_OP_PUT, complete_func, user_data);
286         transfer->data_producer = data_func;
287
288         g_obex_packet_add_body(req, put_get_data, transfer);
289
290         transfer->req_id = g_obex_send_req(obex, req, FIRST_PACKET_TIMEOUT,
291                                         transfer_response, transfer, err);
292         if (transfer->req_id == 0) {
293                 transfer_free(transfer);
294                 return 0;
295         }
296
297         g_obex_debug(G_OBEX_DEBUG_TRANSFER, "transfer %u", transfer->id);
298
299         return transfer->id;
300 }
301
302 guint g_obex_put_req(GObex *obex, GObexDataProducer data_func,
303                         GObexFunc complete_func, gpointer user_data,
304                         GError **err, guint first_hdr_id, ...)
305 {
306         GObexPacket *req;
307         va_list args;
308
309         g_obex_debug(G_OBEX_DEBUG_TRANSFER, "obex %p", obex);
310
311         va_start(args, first_hdr_id);
312         req = g_obex_packet_new_valist(G_OBEX_OP_PUT, FALSE,
313                                                         first_hdr_id, args);
314         va_end(args);
315
316         return g_obex_put_req_pkt(obex, req, data_func, complete_func,
317                                                         user_data, err);
318 }
319
320 static void transfer_abort_req(GObex *obex, GObexPacket *req, gpointer user_data)
321 {
322         struct transfer *transfer = user_data;
323         GObexPacket *rsp;
324         GError *err;
325
326         g_obex_debug(G_OBEX_DEBUG_TRANSFER, "transfer %u", transfer->id);
327
328         err = g_error_new(G_OBEX_ERROR, G_OBEX_ERROR_CANCELLED,
329                                                 "Request was aborted");
330         rsp = g_obex_packet_new(G_OBEX_RSP_SUCCESS, TRUE, G_OBEX_HDR_INVALID);
331         g_obex_send(obex, rsp, NULL);
332
333         transfer_complete(transfer, err);
334         g_error_free(err);
335 }
336
337 static guint8 put_get_bytes(struct transfer *transfer, GObexPacket *req)
338 {
339         GObexHeader *body;
340         gboolean final;
341         guint8 rsp;
342         const guint8 *buf;
343         gsize len;
344
345         g_obex_debug(G_OBEX_DEBUG_TRANSFER, "transfer %u", transfer->id);
346
347         g_obex_packet_get_operation(req, &final);
348         if (final)
349                 rsp = G_OBEX_RSP_SUCCESS;
350         else
351                 rsp = G_OBEX_RSP_CONTINUE;
352
353         body = g_obex_packet_get_body(req);
354         if (body == NULL)
355                 return rsp;
356
357         g_obex_header_get_bytes(body, &buf, &len);
358         if (len == 0)
359                 return rsp;
360
361         if (transfer->data_consumer(buf, len, transfer->user_data) == FALSE)
362                 rsp = G_OBEX_RSP_FORBIDDEN;
363
364         return rsp;
365 }
366
367 static void transfer_put_req_first(struct transfer *transfer, GObexPacket *req,
368                                         guint8 first_hdr_id, va_list args)
369 {
370         GError *err = NULL;
371         GObexPacket *rsp;
372         guint8 rspcode;
373
374         g_obex_debug(G_OBEX_DEBUG_TRANSFER, "transfer %u", transfer->id);
375
376         rspcode = put_get_bytes(transfer, req);
377
378         rsp = g_obex_packet_new_valist(rspcode, TRUE, first_hdr_id, args);
379
380         if (!g_obex_send(transfer->obex, rsp, &err)) {
381                 transfer_complete(transfer, err);
382                 g_error_free(err);
383                 return;
384         }
385
386         if (rspcode != G_OBEX_RSP_CONTINUE)
387                 transfer_complete(transfer, NULL);
388 }
389
390 static void transfer_put_req(GObex *obex, GObexPacket *req, gpointer user_data)
391 {
392         struct transfer *transfer = user_data;
393         GError *err = NULL;
394         GObexPacket *rsp;
395         guint8 rspcode;
396
397         g_obex_debug(G_OBEX_DEBUG_TRANSFER, "transfer %u", transfer->id);
398
399         rspcode = put_get_bytes(transfer, req);
400
401         /* Don't send continue while SRM is active */
402         if (g_obex_srm_active(transfer->obex) &&
403                                 rspcode == G_OBEX_RSP_CONTINUE)
404                 goto done;
405
406         rsp = g_obex_packet_new(rspcode, TRUE, G_OBEX_HDR_INVALID);
407
408         if (!g_obex_send(obex, rsp, &err)) {
409                 transfer_complete(transfer, err);
410                 g_error_free(err);
411                 return;
412         }
413
414 done:
415         if (rspcode != G_OBEX_RSP_CONTINUE)
416                 transfer_complete(transfer, NULL);
417 }
418
419 guint g_obex_put_rsp(GObex *obex, GObexPacket *req,
420                         GObexDataConsumer data_func, GObexFunc complete_func,
421                         gpointer user_data, GError **err,
422                         guint first_hdr_id, ...)
423 {
424         struct transfer *transfer;
425         va_list args;
426         guint id;
427
428         g_obex_debug(G_OBEX_DEBUG_TRANSFER, "obex %p", obex);
429
430         transfer = transfer_new(obex, G_OBEX_OP_PUT, complete_func, user_data);
431         transfer->data_consumer = data_func;
432
433         va_start(args, first_hdr_id);
434         transfer_put_req_first(transfer, req, first_hdr_id, args);
435         va_end(args);
436         if (!g_slist_find(transfers, transfer))
437                 return 0;
438
439         id = g_obex_add_request_function(obex, G_OBEX_OP_PUT, transfer_put_req,
440                                                                 transfer);
441         transfer->put_id = id;
442
443         id = g_obex_add_request_function(obex, G_OBEX_OP_ABORT,
444                                                 transfer_abort_req, transfer);
445         transfer->abort_id = id;
446
447         g_obex_debug(G_OBEX_DEBUG_TRANSFER, "transfer %u", transfer->id);
448
449         return transfer->id;
450 }
451
452 guint g_obex_get_req_pkt(GObex *obex, GObexPacket *req,
453                         GObexDataConsumer data_func, GObexFunc complete_func,
454                         gpointer user_data, GError **err)
455 {
456         struct transfer *transfer;
457
458         g_obex_debug(G_OBEX_DEBUG_TRANSFER, "obex %p", obex);
459
460         if (g_obex_packet_get_operation(req, NULL) != G_OBEX_OP_GET)
461                 return 0;
462
463         transfer = transfer_new(obex, G_OBEX_OP_GET, complete_func, user_data);
464         transfer->data_consumer = data_func;
465         transfer->req_id = g_obex_send_req(obex, req, FIRST_PACKET_TIMEOUT,
466                                         transfer_response, transfer, err);
467         if (transfer->req_id == 0) {
468                 transfer_free(transfer);
469                 return 0;
470         }
471
472         g_obex_debug(G_OBEX_DEBUG_TRANSFER, "transfer %u", transfer->id);
473
474         return transfer->id;
475 }
476
477 guint g_obex_get_req(GObex *obex, GObexDataConsumer data_func,
478                         GObexFunc complete_func, gpointer user_data,
479                         GError **err, guint first_hdr_id, ...)
480 {
481         struct transfer *transfer;
482         GObexPacket *req;
483         va_list args;
484
485         g_obex_debug(G_OBEX_DEBUG_TRANSFER, "obex %p", obex);
486
487         transfer = transfer_new(obex, G_OBEX_OP_GET, complete_func, user_data);
488         transfer->data_consumer = data_func;
489
490         va_start(args, first_hdr_id);
491         req = g_obex_packet_new_valist(G_OBEX_OP_GET, TRUE,
492                                                         first_hdr_id, args);
493         va_end(args);
494
495         transfer->req_id = g_obex_send_req(obex, req, FIRST_PACKET_TIMEOUT,
496                                         transfer_response, transfer, err);
497         if (transfer->req_id == 0) {
498                 transfer_free(transfer);
499                 return 0;
500         }
501
502         g_obex_debug(G_OBEX_DEBUG_TRANSFER, "transfer %u", transfer->id);
503
504         return transfer->id;
505 }
506
507 static gssize get_get_data(void *buf, gsize len, gpointer user_data)
508 {
509         struct transfer *transfer = user_data;
510         GObexPacket *req, *rsp;
511         GError *err = NULL;
512         gssize ret;
513         guint8 op;
514
515         g_obex_debug(G_OBEX_DEBUG_TRANSFER, "transfer %u", transfer->id);
516
517         ret = transfer->data_producer(buf, len, transfer->user_data);
518         if (ret > 0) {
519                 if (!g_obex_srm_active(transfer->obex))
520                         return ret;
521
522                 /* Generate next response */
523                 rsp = g_obex_packet_new(G_OBEX_RSP_CONTINUE, TRUE,
524                                                         G_OBEX_HDR_INVALID);
525                 g_obex_packet_add_body(rsp, get_get_data, transfer);
526
527                 if (!g_obex_send(transfer->obex, rsp, &err)) {
528                         transfer_complete(transfer, err);
529                         g_error_free(err);
530                 }
531
532                 return ret;
533         }
534
535         if (ret == -EAGAIN)
536                 return ret;
537
538         if (ret == 0) {
539                 transfer_complete(transfer, NULL);
540                 return ret;
541         }
542
543         op = g_obex_errno_to_rsp(ret);
544
545         req = g_obex_packet_new(op, TRUE, G_OBEX_HDR_INVALID);
546         g_obex_send(transfer->obex, req, NULL);
547
548         err = g_error_new(G_OBEX_ERROR, G_OBEX_ERROR_CANCELLED,
549                                 "Data producer function failed");
550         g_obex_debug(G_OBEX_DEBUG_ERROR, "%s", err->message);
551         transfer_complete(transfer, err);
552         g_error_free(err);
553
554         return ret;
555 }
556
557 static gboolean transfer_get_req_first(struct transfer *transfer,
558                                                         GObexPacket *rsp)
559 {
560         GError *err = NULL;
561
562         g_obex_debug(G_OBEX_DEBUG_TRANSFER, "transfer %u", transfer->id);
563
564         g_obex_packet_add_body(rsp, get_get_data, transfer);
565
566         if (!g_obex_send(transfer->obex, rsp, &err)) {
567                 transfer_complete(transfer, err);
568                 g_error_free(err);
569                 return FALSE;
570         }
571
572         return TRUE;
573 }
574
575 static void transfer_get_req(GObex *obex, GObexPacket *req, gpointer user_data)
576 {
577         struct transfer *transfer = user_data;
578         GError *err = NULL;
579         GObexPacket *rsp;
580
581         g_obex_debug(G_OBEX_DEBUG_TRANSFER, "transfer %u", transfer->id);
582
583         rsp = g_obex_packet_new(G_OBEX_RSP_CONTINUE, TRUE, G_OBEX_HDR_INVALID);
584         g_obex_packet_add_body(rsp, get_get_data, transfer);
585
586         if (!g_obex_send(obex, rsp, &err)) {
587                 transfer_complete(transfer, err);
588                 g_error_free(err);
589         }
590 }
591
592 guint g_obex_get_rsp_pkt(GObex *obex, GObexPacket *rsp,
593                         GObexDataProducer data_func, GObexFunc complete_func,
594                         gpointer user_data, GError **err)
595 {
596         struct transfer *transfer;
597         guint id;
598
599         g_obex_debug(G_OBEX_DEBUG_TRANSFER, "obex %p", obex);
600
601         transfer = transfer_new(obex, G_OBEX_OP_GET, complete_func, user_data);
602         transfer->data_producer = data_func;
603
604         if (!transfer_get_req_first(transfer, rsp))
605                 return 0;
606
607         if (!g_slist_find(transfers, transfer))
608                 return 0;
609
610         id = g_obex_add_request_function(obex, G_OBEX_OP_GET, transfer_get_req,
611                                                                 transfer);
612         transfer->get_id = id;
613
614         id = g_obex_add_request_function(obex, G_OBEX_OP_ABORT,
615                                                 transfer_abort_req, transfer);
616         transfer->abort_id = id;
617
618         g_obex_debug(G_OBEX_DEBUG_TRANSFER, "transfer %u", transfer->id);
619
620         return transfer->id;
621 }
622
623 guint g_obex_get_rsp(GObex *obex, GObexDataProducer data_func,
624                         GObexFunc complete_func, gpointer user_data,
625                         GError **err, guint first_hdr_id, ...)
626 {
627         GObexPacket *rsp;
628         va_list args;
629
630         g_obex_debug(G_OBEX_DEBUG_TRANSFER, "obex %p", obex);
631
632         va_start(args, first_hdr_id);
633         rsp = g_obex_packet_new_valist(G_OBEX_RSP_CONTINUE, TRUE,
634                                                         first_hdr_id, args);
635         va_end(args);
636
637         return g_obex_get_rsp_pkt(obex, rsp, data_func, complete_func,
638                                                         user_data, err);
639 }
640
641 gboolean g_obex_cancel_transfer(guint id, GObexFunc complete_func,
642                         gpointer user_data)
643 {
644         struct transfer *transfer = NULL;
645         gboolean ret = TRUE;
646
647         g_obex_debug(G_OBEX_DEBUG_TRANSFER, "transfer %u", id);
648
649         transfer = find_transfer(id);
650
651         if (transfer == NULL)
652                 return FALSE;
653
654         if (complete_func == NULL)
655                 goto done;
656
657         transfer->complete_func = complete_func;
658         transfer->user_data = user_data;
659
660         if (!transfer->req_id) {
661                 transfer->req_id = g_obex_abort(transfer->obex,
662                                                 transfer_abort_response,
663                                                 transfer, NULL);
664                 if (transfer->req_id)
665                         return TRUE;
666         }
667
668         ret = g_obex_cancel_req(transfer->obex, transfer->req_id, FALSE);
669         if (ret)
670                 return TRUE;
671
672 done:
673         transfer_free(transfer);
674         return ret;
675 }