c0c7d080b0b95099419262b233850dd5ce97935c
[platform/core/connectivity/bluetooth-frwk.git] / bt-service / bt-service-opp-client.c
1 /*
2  * bluetooth-frwk
3  *
4  * Copyright (c) 2012-2013 Samsung Electronics Co., Ltd.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *              http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *
18  */
19
20 #include <dbus/dbus-glib.h>
21 #include <dbus/dbus.h>
22 #include <glib.h>
23 #include <dlog.h>
24 #include <string.h>
25
26 #include "bluetooth-api.h"
27 #include "bt-internal-types.h"
28
29 #include "bt-service-common.h"
30 #include "bt-service-event.h"
31 #include "bt-service-util.h"
32 #include "bt-service-opp-client.h"
33 #include "bt-service-obex-agent.h"
34
35 static BtObexAgent *opc_obex_agent = NULL;
36 static GSList *transfer_list = NULL;
37
38 bt_sending_info_t *sending_info;
39
40 static gboolean __bt_release_callback(DBusGMethodInvocation *context,
41                                         gpointer user_data);
42
43 static gboolean __bt_request_callback(DBusGMethodInvocation *context,
44                                         DBusGProxy *transfer,
45                                         gpointer user_data);
46
47 static gboolean __bt_progress_callback(DBusGMethodInvocation *context,
48                                         DBusGProxy *transfer,
49                                         guint64 transferred,
50                                         gpointer user_data);
51
52 static gboolean __bt_complete_callback(DBusGMethodInvocation *context,
53                                         DBusGProxy *transfer,
54                                         gpointer user_data);
55
56 static gboolean __bt_error_callback(DBusGMethodInvocation *context,
57                                         DBusGProxy *transfer,
58                                         const char *message,
59                                         gpointer user_data);
60
61
62 static int __bt_opp_client_start_sending(int request_id, char *address,
63                                         char **file_name_array);
64
65 static int __bt_opp_client_agent_init(void)
66 {
67         opc_obex_agent = _bt_obex_agent_new();
68         retv_if(opc_obex_agent == NULL, BLUETOOTH_ERROR_INTERNAL);
69
70         _bt_obex_set_release_cb(opc_obex_agent,
71                                     __bt_release_callback, NULL);
72         _bt_obex_set_request_cb(opc_obex_agent,
73                                     __bt_request_callback, NULL);
74         _bt_obex_set_progress_cb(opc_obex_agent,
75                                      __bt_progress_callback, NULL);
76         _bt_obex_set_complete_cb(opc_obex_agent,
77                                      __bt_complete_callback, NULL);
78         _bt_obex_set_error_cb(opc_obex_agent,
79                                 __bt_error_callback, NULL);
80
81         _bt_obex_setup(opc_obex_agent, BT_OBEX_CLIENT_AGENT_PATH);
82
83         return BLUETOOTH_ERROR_NONE;
84 }
85
86 static void __bt_opp_client_agent_deinit(void)
87 {
88         ret_if(opc_obex_agent == NULL);
89
90         g_object_unref(opc_obex_agent);
91         opc_obex_agent = NULL;
92 }
93
94 static GQuark __bt_opc_error_quark(void)
95 {
96         static GQuark quark = 0;
97         if (!quark)
98                 quark = g_quark_from_static_string("agent");
99
100         return quark;
101 }
102
103 static void __bt_free_transfer_info(bt_transfer_info_t *info)
104 {
105         ret_if(info == NULL);
106
107         if (info->proxy)
108                 g_object_unref(info->proxy);
109
110         g_free(info->transfer_name);
111         g_free(info->file_name);
112         g_free(info);
113 }
114
115 static void __bt_free_sending_info(bt_sending_info_t *info)
116 {
117         ret_if(info == NULL);
118
119         /* Free the sending variable */
120         __bt_free_transfer_info(info->transfer_info);
121
122         g_free(info->address);
123         g_free(info);
124 }
125
126 static void __bt_value_free(GValue *value)
127 {
128         g_value_unset(value);
129         g_free(value);
130 }
131
132 static gboolean __bt_cancel_push_cb(gpointer data)
133 {
134         int result = BLUETOOTH_ERROR_CANCEL_BY_USER;
135
136         retv_if(sending_info == NULL, FALSE);
137
138         /* Send the event in only error none case */
139         _bt_send_event(BT_OPP_CLIENT_EVENT,
140                         BLUETOOTH_EVENT_OPC_CONNECTED,
141                         DBUS_TYPE_INT32, &result,
142                         DBUS_TYPE_STRING, &sending_info->address,
143                         DBUS_TYPE_INT32, &sending_info->request_id,
144                         DBUS_TYPE_INVALID);
145
146         __bt_free_sending_info(sending_info);
147         sending_info = NULL;
148
149         __bt_opp_client_agent_deinit();
150
151         /* Operate remain works */
152         if (g_slist_length(transfer_list) > 0) {
153                 bt_sending_data_t *node = NULL;
154
155                 node = transfer_list->data;
156                 if (node == NULL) {
157                         BT_DBG("data is NULL");
158                         return FALSE;
159                 }
160
161                 transfer_list = g_slist_remove(transfer_list, node);
162
163                 if (__bt_opp_client_start_sending(node->request_id,
164                                 node->address,
165                                 node->file_path) != BLUETOOTH_ERROR_NONE) {
166                         BT_DBG("Fail to start sending");
167                 }
168         }
169
170         return FALSE;
171 }
172
173 static gboolean __bt_progress_callback(DBusGMethodInvocation *context,
174                                         DBusGProxy *transfer,
175                                         guint64 transferred,
176                                         gpointer user_data)
177 {
178         int percentage_progress;
179         gint64 size;
180         int result = BLUETOOTH_ERROR_NONE;
181
182         dbus_g_method_return(context);
183
184         retv_if(sending_info == NULL, TRUE);
185         retv_if(sending_info->transfer_info == NULL, TRUE);
186
187         size = sending_info->transfer_info->size;
188
189         if (size != 0)
190                 percentage_progress = (int)(((gdouble)transferred /
191                                 (gdouble)size) * 100);
192         else
193                 percentage_progress = 0;
194
195         /* Send the event in only error none case */
196         _bt_send_event(BT_OPP_CLIENT_EVENT,
197                         BLUETOOTH_EVENT_OPC_TRANSFER_PROGRESS,
198                         DBUS_TYPE_INT32, &result,
199                         DBUS_TYPE_STRING, &sending_info->transfer_info->file_name,
200                         DBUS_TYPE_UINT64, &sending_info->transfer_info->size,
201                         DBUS_TYPE_INT32, &percentage_progress,
202                         DBUS_TYPE_INT32, &sending_info->request_id,
203                         DBUS_TYPE_INVALID);
204
205         return TRUE;
206 }
207
208 static gboolean __bt_complete_callback(DBusGMethodInvocation *context,
209                                         DBusGProxy *transfer,
210                                         gpointer user_data)
211 {
212         int result = BLUETOOTH_ERROR_NONE;
213
214         dbus_g_method_return(context);
215
216         /* Send the event in only error none case */
217         _bt_send_event(BT_OPP_CLIENT_EVENT,
218                         BLUETOOTH_EVENT_OPC_TRANSFER_COMPLETE,
219                         DBUS_TYPE_INT32, &result,
220                         DBUS_TYPE_STRING, &sending_info->transfer_info->file_name,
221                         DBUS_TYPE_UINT64, &sending_info->transfer_info->size,
222                         DBUS_TYPE_INT32, &sending_info->request_id,
223                         DBUS_TYPE_INVALID);
224
225         return TRUE;
226 }
227
228 static gboolean __bt_request_callback(DBusGMethodInvocation *context,
229                                         DBusGProxy *transfer,
230                                         gpointer user_data)
231 {
232         GValue *value;
233         const char *transfer_name;
234         const char *file_name;
235         int size;
236         int result = BLUETOOTH_ERROR_NONE;
237         GHashTable *hash = NULL;
238         GError *error;
239
240         if (sending_info == NULL || sending_info->is_canceled == TRUE) {
241                 result = BLUETOOTH_ERROR_CANCEL_BY_USER;
242                 goto canceled;
243         }
244
245         dbus_g_method_return(context, "");
246
247         __bt_free_transfer_info(sending_info->transfer_info);
248
249         sending_info->transfer_info = g_malloc0(sizeof(bt_transfer_info_t));
250         sending_info->transfer_info->proxy = g_object_ref(transfer);
251
252         dbus_g_proxy_call(transfer, "GetProperties", NULL,
253                                 G_TYPE_INVALID,
254                                 dbus_g_type_get_map("GHashTable", G_TYPE_STRING, G_TYPE_VALUE),
255                                 &hash, G_TYPE_INVALID);
256
257         if (hash == NULL)
258                 goto fail;
259
260         value = g_hash_table_lookup(hash, "Name");
261         transfer_name = value ? g_value_get_string(value) : NULL;
262
263         value = g_hash_table_lookup(hash, "Filename");
264         file_name = value ? g_value_get_string(value) : NULL;
265
266         value = g_hash_table_lookup(hash, "Size");
267         size = value ? g_value_get_uint64(value) : 0;
268
269         sending_info->transfer_info->transfer_name = g_strdup(transfer_name);
270         sending_info->transfer_info->file_name = g_strdup(file_name);
271         sending_info->transfer_info->size = size;
272         sending_info->result = BLUETOOTH_ERROR_NONE;
273
274         g_hash_table_destroy(hash);
275
276         _bt_send_event(BT_OPP_CLIENT_EVENT,
277                         BLUETOOTH_EVENT_OPC_TRANSFER_STARTED,
278                         DBUS_TYPE_INT32, &result,
279                         DBUS_TYPE_STRING, &sending_info->transfer_info->file_name,
280                         DBUS_TYPE_UINT64, &sending_info->transfer_info->size,
281                         DBUS_TYPE_INT32, &sending_info->request_id,
282                         DBUS_TYPE_INVALID);
283
284         return TRUE;
285 canceled:
286         error = g_error_new(__bt_opc_error_quark(), BT_OBEX_AGENT_ERROR_CANCEL,
287                         "CancelledByUser");
288
289         dbus_g_method_return_error(context, error);
290         g_error_free(error);
291
292         return FALSE;
293 fail:
294         result = BLUETOOTH_ERROR_INTERNAL;
295
296         /* Send the event in only error none case */
297         _bt_send_event(BT_OPP_CLIENT_EVENT,
298                         BLUETOOTH_EVENT_OPC_DISCONNECTED,
299                         DBUS_TYPE_INT32, &result,
300                         DBUS_TYPE_STRING, &sending_info->address,
301                         DBUS_TYPE_INT32, &sending_info->request_id,
302                         DBUS_TYPE_INVALID);
303
304         __bt_free_sending_info(sending_info);
305         sending_info = NULL;
306
307         __bt_opp_client_agent_deinit();
308
309         return TRUE;
310 }
311
312 static void __bt_free_sending_data(gpointer data)
313 {
314         int i;
315         bt_sending_data_t *info = data;
316
317         ret_if(info == NULL);
318
319         for (i = 0; i < info->file_count; i++) {
320                 g_free(info->file_path[i]);
321         }
322
323         _bt_delete_request_id(info->request_id);
324
325         g_free(info->file_path);
326         g_free(info->address);
327         g_free(info);
328 }
329
330 static gboolean __bt_release_callback(DBusGMethodInvocation *context,
331                                         gpointer user_data)
332 {
333         dbus_g_method_return(context);
334
335         retv_if(sending_info == NULL, FALSE);
336
337         /* Send the event in only error none case */
338         _bt_send_event(BT_OPP_CLIENT_EVENT,
339                         BLUETOOTH_EVENT_OPC_DISCONNECTED,
340                         DBUS_TYPE_INT32, &sending_info->result,
341                         DBUS_TYPE_STRING, &sending_info->address,
342                         DBUS_TYPE_INT32, &sending_info->request_id,
343                         DBUS_TYPE_INVALID);
344
345         __bt_free_sending_info(sending_info);
346         sending_info = NULL;
347
348         __bt_opp_client_agent_deinit();
349
350         /* Operate remain works */
351         if (g_slist_length(transfer_list) > 0) {
352                 bt_sending_data_t *data = NULL;
353
354                 data = transfer_list->data;
355                 if (data == NULL)
356                         goto fail;
357
358                 transfer_list = g_slist_remove(transfer_list, data);
359
360                 if (__bt_opp_client_start_sending(data->request_id,
361                                 data->address,
362                                 data->file_path) != BLUETOOTH_ERROR_NONE) {
363                         goto fail;
364                 }
365         }
366
367         return TRUE;
368 fail:
369         g_slist_free_full(transfer_list,
370                                 (GDestroyNotify)__bt_free_sending_data);
371         transfer_list = NULL;
372         return TRUE;
373 }
374
375 static gboolean __bt_error_callback(DBusGMethodInvocation *context,
376                                         DBusGProxy *transfer,
377                                         const char *message,
378                                         gpointer user_data)
379 {
380         int result;
381
382         dbus_g_method_return(context);
383
384         retv_if(sending_info == NULL, FALSE);
385         retv_if(sending_info->transfer_info == NULL, FALSE);
386
387         if (sending_info->is_canceled == TRUE)  {
388                 result = BLUETOOTH_ERROR_CANCEL_BY_USER;
389         } else if (g_strcmp0(message, "Forbidden") == 0) {
390                 result = BLUETOOTH_ERROR_ACCESS_DENIED;
391         } else if (g_str_has_prefix(message,
392                 "Transport endpoint is not connected") == TRUE) {
393                 result = BLUETOOTH_ERROR_NOT_CONNECTED;
394         } else if (g_strcmp0(message, "Database full") == 0) {
395                 result = BLUETOOTH_ERROR_OUT_OF_MEMORY;
396         } else {
397                 result = BLUETOOTH_ERROR_INTERNAL;
398         }
399
400         sending_info->result = result;
401
402         /* Send the event in only error none case */
403         _bt_send_event(BT_OPP_CLIENT_EVENT,
404                         BLUETOOTH_EVENT_OPC_TRANSFER_COMPLETE,
405                         DBUS_TYPE_INT32, &result,
406                         DBUS_TYPE_STRING, &sending_info->transfer_info->file_name,
407                         DBUS_TYPE_UINT64, &sending_info->transfer_info->size,
408                         DBUS_TYPE_INT32, &sending_info->request_id,
409                         DBUS_TYPE_INVALID);
410         return TRUE;
411 }
412
413 static void __bt_send_files_cb(DBusGProxy *proxy, DBusGProxyCall *call,
414                                 void *user_data)
415 {
416         GError *error = NULL;
417         int result = BLUETOOTH_ERROR_NONE;
418
419         if (dbus_g_proxy_end_call(proxy, call, &error,
420                                         G_TYPE_INVALID) == FALSE) {
421
422                 BT_ERR("%s", error->message);
423                 g_error_free(error);
424
425                 result = BLUETOOTH_ERROR_INTERNAL;
426         }
427
428         g_object_unref(proxy);
429         ret_if(sending_info == NULL);
430
431         sending_info->sending_proxy = NULL;
432
433         /* Send the event in only error none case */
434         _bt_send_event(BT_OPP_CLIENT_EVENT,
435                         BLUETOOTH_EVENT_OPC_CONNECTED,
436                         DBUS_TYPE_INT32, &result,
437                         DBUS_TYPE_STRING, &sending_info->address,
438                         DBUS_TYPE_INT32, &sending_info->request_id,
439                         DBUS_TYPE_INVALID);
440
441         if (result != BLUETOOTH_ERROR_NONE) {
442                 __bt_free_sending_info(sending_info);
443                 sending_info = NULL;
444         }
445 }
446
447 static int __bt_opp_client_start_sending(int request_id, char *address,
448                                         char **file_name_array)
449 {
450         GHashTable *hash;
451         GValue *value;
452         DBusGConnection *g_conn;
453         DBusGProxy *client_proxy;
454         DBusGProxyCall *proxy_call;
455         char *agent_path;
456
457         BT_CHECK_PARAMETER(address);
458         BT_CHECK_PARAMETER(file_name_array);
459
460         /* Get the session bus. */
461         g_conn = _bt_get_session_gconn();
462         retv_if(g_conn == NULL, BLUETOOTH_ERROR_INTERNAL);
463
464         client_proxy =  dbus_g_proxy_new_for_name(g_conn, BT_OBEX_SERVICE_NAME,
465                                         "/", BT_OBEX_CLIENT_INTERFACE);
466
467         retv_if(client_proxy == NULL, BLUETOOTH_ERROR_INTERNAL);
468
469         hash = g_hash_table_new_full(g_str_hash, g_str_equal,
470                                      NULL, (GDestroyNotify)__bt_value_free);
471
472         value = g_new0(GValue, 1);
473         g_value_init(value, G_TYPE_STRING);
474         g_value_set_string(value, address);
475         g_hash_table_insert(hash, "Destination", value);
476
477         __bt_free_sending_info(sending_info);
478
479         sending_info = g_malloc0(sizeof(bt_sending_info_t));
480         sending_info->address = g_strdup(address);
481         sending_info->request_id = request_id;
482
483         __bt_opp_client_agent_deinit();
484         __bt_opp_client_agent_init();
485
486         agent_path = g_strdup(BT_OBEX_CLIENT_AGENT_PATH);
487
488         proxy_call = dbus_g_proxy_begin_call(client_proxy, "SendFiles",
489                                 __bt_send_files_cb, NULL, NULL,
490                                 dbus_g_type_get_map("GHashTable", G_TYPE_STRING,
491                                                     G_TYPE_VALUE), hash,
492                                 G_TYPE_STRV, file_name_array,
493                                 DBUS_TYPE_G_OBJECT_PATH, agent_path,
494                                 G_TYPE_INVALID);
495
496         g_free(agent_path);
497
498         if (proxy_call == NULL) {
499                         BT_ERR("Fail to Send files");
500                         g_hash_table_destroy(hash);
501                         g_object_unref(client_proxy);
502                         __bt_free_sending_info(sending_info);
503                         __bt_opp_client_agent_deinit();
504                         sending_info = NULL;
505                         return BLUETOOTH_ERROR_INTERNAL;
506         }
507
508         sending_info->sending_proxy = proxy_call;
509         g_hash_table_destroy(hash);
510
511         return BLUETOOTH_ERROR_NONE;
512 }
513
514 int _bt_opp_client_push_files(int request_id, DBusGMethodInvocation *context,
515                                 bluetooth_device_address_t *remote_address,
516                                 char **file_path, int file_count)
517 {
518         char address[BT_ADDRESS_STRING_SIZE] = { 0 };
519         bt_sending_data_t *data;
520         GArray *out_param1 = NULL;
521         GArray *out_param2 = NULL;
522         int result = BLUETOOTH_ERROR_NONE;
523         int i;
524
525         BT_CHECK_PARAMETER(remote_address);
526         BT_CHECK_PARAMETER(file_path);
527
528         /* Implement the queue */
529         _bt_convert_addr_type_to_string(address, remote_address->addr);
530
531         if (sending_info == NULL) {
532                 result = __bt_opp_client_start_sending(request_id,
533                                                 address, file_path);
534         } else {
535                 /* Insert data in the queue */
536                 data = g_malloc0(sizeof(bt_sending_data_t));
537                 data->file_path = g_new0(char *, file_count + 1);
538                 data->address = g_strdup(address);
539                 data->file_count = file_count;
540                 data->request_id = request_id;
541
542                 for (i = 0; i < file_count; i++) {
543                         data->file_path[i] = g_strdup(file_path[i]);
544                         BT_DBG("file[%d]: %s", i, data->file_path[i]);
545                 }
546
547                 transfer_list = g_slist_append(transfer_list, data);
548         }
549
550         out_param1 = g_array_new(FALSE, FALSE, sizeof(gchar));
551         out_param2 = g_array_new(FALSE, FALSE, sizeof(gchar));
552
553         g_array_append_vals(out_param1, &request_id,
554                                 sizeof(int));
555         g_array_append_vals(out_param2, &result, sizeof(int));
556
557         dbus_g_method_return(context, out_param1, out_param2);
558
559         g_array_free(out_param1, TRUE);
560         g_array_free(out_param2, TRUE);
561
562         return result;
563 }
564
565 int _bt_opp_client_cancel_push(void)
566 {
567         DBusGConnection *g_conn;
568         DBusGProxy *client_proxy;
569
570         retv_if(sending_info == NULL, BLUETOOTH_ERROR_NOT_IN_OPERATION);
571
572         sending_info->is_canceled = TRUE;
573
574         if (sending_info->transfer_info) {
575                 dbus_g_proxy_call_no_reply(sending_info->transfer_info->proxy,
576                                         "Cancel", G_TYPE_INVALID,
577                                         G_TYPE_INVALID);
578         } else {
579                 retv_if(sending_info->sending_proxy == NULL,
580                                         BLUETOOTH_ERROR_INTERNAL);
581
582                 g_conn = _bt_get_session_gconn();
583                 retv_if(g_conn == NULL, BLUETOOTH_ERROR_INTERNAL);
584
585                 client_proxy =  dbus_g_proxy_new_for_name(g_conn, BT_OBEX_SERVICE_NAME,
586                                                 "/", BT_OBEX_CLIENT_INTERFACE);
587
588                 retv_if(client_proxy == NULL, BLUETOOTH_ERROR_INTERNAL);
589
590                 dbus_g_proxy_cancel_call(client_proxy,
591                                         sending_info->sending_proxy);
592
593                 g_idle_add(__bt_cancel_push_cb, NULL);
594         }
595
596         return BLUETOOTH_ERROR_NONE;
597 }
598
599 int _bt_opp_client_cancel_all_transfers(void)
600 {
601         if (transfer_list) {
602                 g_slist_free_full(transfer_list,
603                         (GDestroyNotify)__bt_free_sending_data);
604
605                 transfer_list = NULL;
606         }
607
608         _bt_opp_client_cancel_push();
609
610         return BLUETOOTH_ERROR_NONE;
611 }
612
613 int _bt_opp_client_is_sending(gboolean *sending)
614 {
615         BT_CHECK_PARAMETER(sending);
616
617         *sending = sending_info ? TRUE : FALSE;
618
619         return BLUETOOTH_ERROR_NONE;
620 }