/* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2005-2008 Marcel Holtmann * Copyright (C) 2013 Intel Corporation. * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #define OBEX_SERVICE "org.bluez.obex" #define OBEX_PATH "/org/bluez/obex" #define TRANSFER_IFACE "org.bluez.obex.Transfer1" #define OPP_IFACE "org.bluez.obex.ObjectPush1" #define CLIENT_IFACE "org.bluez.obex.Client1" #define RESPONSE_RETRY 1 static GDBusConnection *conn = NULL; static GDBusProxy *client_proxy = NULL; static GDBusProxy *session = NULL; static GDBusProxy *current_transfer = NULL; static GCancellable *cancellable = NULL; static GtkWidget *dialog; static GtkWidget *label_from; static GtkWidget *image_status; static GtkWidget *label_status; static GtkWidget *progress; static gchar *option_device = NULL; static gchar *option_device_name = NULL; static gchar **option_files = NULL; static guint64 current_size = 0; static guint64 total_size = 0; static guint64 total_sent = 0; static int file_count = 0; static int file_index = 0; static gint64 first_update = 0; static gint64 last_update = 0; static void on_transfer_properties (GVariant *props); static void on_transfer_progress (guint64 transferred); static void on_transfer_complete (void); static void on_transfer_error (void); static gint64 get_system_time (void) { struct timeval tmp; gettimeofday(&tmp, NULL); return (gint64) tmp.tv_usec + (gint64) tmp.tv_sec * G_GINT64_CONSTANT(1000000); } static void update_from_label (void) { char *filename = option_files[file_index]; GFile *file, *dir; char *text, *markup; file = g_file_new_for_path (filename); dir = g_file_get_parent (file); g_object_unref (file); if (g_file_has_uri_scheme (dir, "file") != FALSE) { text = g_file_get_path (dir); } else { text = g_file_get_uri (dir); } markup = g_markup_escape_text (text, -1); g_free (text); g_object_unref (dir); gtk_label_set_markup (GTK_LABEL (label_from), markup); g_free (markup); } static char * cleanup_error (GError *error) { char *remote_error; if (!error || *error->message == '\0') return g_strdup (_("An unknown error occurred")); if (g_dbus_error_is_remote_error (error) == FALSE) return g_strdup (error->message); remote_error = g_dbus_error_get_remote_error (error); g_debug ("Remote error is: %s", remote_error); g_free (remote_error); g_dbus_error_strip_remote_error (error); g_debug ("Error message is: %s", error->message); /* And now, take advantage of the fact that obexd isn't translated */ if (g_strcmp0 (error->message, "Unable to find service record") == 0) { return g_strdup (_("Make sure that the remote device is switched on and that it accepts Bluetooth connections")); } return g_strdup (error->message); } static void handle_error (GError *error) { char *message; message = cleanup_error (error); gtk_widget_show (image_status); gtk_label_set_markup (GTK_LABEL (label_status), message); g_clear_error (&error); g_free (message); /* Clear the progress bar as it may be saying 'Connecting' or * 'Sending file 1 of 1' which is not true. */ gtk_progress_bar_set_text (GTK_PROGRESS_BAR (progress), ""); gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), RESPONSE_RETRY, TRUE); } static void transfer_properties_changed (GDBusProxy *proxy, GVariant *changed_properties, GStrv invalidated_properties, gpointer user_data) { GVariantIter iter; const char *key; GVariant *value; g_variant_iter_init (&iter, changed_properties); while (g_variant_iter_next (&iter, "{&sv}", &key, &value)) { if (g_str_equal (key, "Status")) { const char *status; status = g_variant_get_string (value, NULL); if (g_str_equal (status, "complete")) { on_transfer_complete (); } else if (g_str_equal (status, "error")) { on_transfer_error (); } } else if (g_str_equal (key, "Transferred")) { guint64 transferred = g_variant_get_uint64 (value); on_transfer_progress (transferred); } g_variant_unref (value); } } static void transfer_proxy (GDBusProxy *proxy, GAsyncResult *res, gpointer user_data) { GError *error = NULL; current_transfer = g_dbus_proxy_new_finish (res, &error); if (current_transfer == NULL) { if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { g_error_free (error); return; } handle_error (error); return; } g_signal_connect (G_OBJECT (current_transfer), "g-properties-changed", G_CALLBACK (transfer_properties_changed), NULL); } static void transfer_created (GDBusProxy *proxy, GAsyncResult *res, gpointer user_data) { GError *error = NULL; GVariant *variant, *properties; const char *transfer; variant = g_dbus_proxy_call_finish (proxy, res, &error); if (variant == NULL) { if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { g_error_free (error); return; } handle_error (error); return; } gtk_progress_bar_set_text (GTK_PROGRESS_BAR (progress), NULL); first_update = get_system_time (); g_variant_get (variant, "(&o@a{sv})", &transfer, &properties); on_transfer_properties (properties); g_dbus_proxy_new (conn, G_DBUS_PROXY_FLAGS_NONE, NULL, OBEX_SERVICE, transfer, TRANSFER_IFACE, cancellable, (GAsyncReadyCallback) transfer_proxy, NULL); g_variant_unref (properties); g_variant_unref (variant); } static void send_next_file (void) { update_from_label (); g_dbus_proxy_call (session, "SendFile", g_variant_new ("(s)", option_files[file_index]), G_DBUS_CALL_FLAGS_NONE, -1, cancellable, (GAsyncReadyCallback) transfer_created, NULL); } static void session_proxy (GDBusProxy *proxy, GAsyncResult *res, gpointer user_data) { GError *error = NULL; g_clear_object (&session); session = g_dbus_proxy_new_finish (res, &error); if (session == NULL) { if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { g_error_free (error); return; } handle_error (error); return; } send_next_file (); } static void session_created (GDBusProxy *proxy, GAsyncResult *res, gpointer user_data) { GError *error = NULL; GVariant *variant; const char *session; variant = g_dbus_proxy_call_finish (proxy, res, &error); if (variant == NULL) { if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { g_error_free (error); return; } handle_error (error); return; } g_variant_get (variant, "(&o)", &session); g_dbus_proxy_new (conn, G_DBUS_PROXY_FLAGS_NONE, NULL, OBEX_SERVICE, session, OPP_IFACE, cancellable, (GAsyncReadyCallback) session_proxy, NULL); g_variant_unref (variant); } static void send_files (void) { GVariant *parameters; GVariantBuilder *builder; builder = g_variant_builder_new (G_VARIANT_TYPE_DICTIONARY); g_variant_builder_add (builder, "{sv}", "Target", g_variant_new_string ("opp")); parameters = g_variant_new ("(sa{sv})", option_device, builder); g_dbus_proxy_call (client_proxy, "CreateSession", parameters, G_DBUS_CALL_FLAGS_NONE, -1, cancellable, (GAsyncReadyCallback) session_created, NULL); g_variant_builder_unref (builder); } static gchar *filename_to_path(const gchar *filename) { GFile *file; gchar *path; file = g_file_new_for_commandline_arg(filename); path = g_file_get_path(file); g_object_unref(file); return path; } static gchar *format_time(gint seconds) { gint hours, minutes; if (seconds < 0) seconds = 0; if (seconds < 60) return g_strdup_printf(ngettext("%'d second", "%'d seconds", seconds), seconds); if (seconds < 60 * 60) { minutes = (seconds + 30) / 60; return g_strdup_printf(ngettext("%'d minute", "%'d minutes", minutes), minutes); } hours = seconds / (60 * 60); if (seconds < 60 * 60 * 4) { gchar *res, *h, *m; minutes = (seconds - hours * 60 * 60 + 30) / 60; h = g_strdup_printf(ngettext("%'d hour", "%'d hours", hours), hours); m = g_strdup_printf(ngettext("%'d minute", "%'d minutes", minutes), minutes); res = g_strconcat(h, ", ", m, NULL); g_free(h); g_free(m); return res; } return g_strdup_printf(ngettext("approximately %'d hour", "approximately %'d hours", hours), hours); } static void response_callback(GtkWidget *dialog, gint response, gpointer user_data) { if (response == RESPONSE_RETRY) { /* Reset buttons */ gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), RESPONSE_RETRY, FALSE); /* Reset status and progress bar */ gtk_progress_bar_set_text (GTK_PROGRESS_BAR (progress), _("Connecting…")); gtk_label_set_text (GTK_LABEL (label_status), ""); gtk_widget_hide (image_status); /* If we have a session, we don't need to create another one. */ if (session) send_next_file (); else send_files (); return; } /* Cancel any ongoing dbus calls we may have */ g_cancellable_cancel (cancellable); if (current_transfer != NULL) { g_dbus_proxy_call (current_transfer, "Cancel", NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, (GAsyncReadyCallback) NULL, NULL); g_object_unref (current_transfer); current_transfer = NULL; } gtk_widget_destroy(dialog); gtk_main_quit(); } static void create_window(void) { GtkWidget *vbox, *hbox; GtkWidget *table; GtkWidget *label; gchar *text; dialog = g_object_new (GTK_TYPE_DIALOG, "use-header-bar", 1, "title", _("Bluetooth File Transfer"), NULL); gtk_dialog_add_buttons(GTK_DIALOG (dialog), _("_Cancel"), GTK_RESPONSE_CANCEL, _("_Retry"), RESPONSE_RETRY, NULL); gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), RESPONSE_RETRY, FALSE); gtk_window_set_type_hint(GTK_WINDOW(dialog), GDK_WINDOW_TYPE_HINT_NORMAL); gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER); gtk_window_set_default_size(GTK_WINDOW(dialog), 400, -1); gtk_container_set_border_width(GTK_CONTAINER(dialog), 6); vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); gtk_box_set_spacing(GTK_BOX(vbox), 6); gtk_container_set_border_width(GTK_CONTAINER(vbox), 6); gtk_container_add (GTK_CONTAINER (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), vbox); table = gtk_grid_new(); gtk_grid_set_column_spacing(GTK_GRID(table), 4); gtk_grid_set_row_spacing(GTK_GRID(table), 4); gtk_box_pack_start(GTK_BOX(vbox), table, TRUE, TRUE, 9); label = gtk_label_new(NULL); gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); text = g_markup_printf_escaped("%s", _("From:")); gtk_label_set_markup(GTK_LABEL(label), text); g_free(text); gtk_grid_attach(GTK_GRID(table), label, 0, 0, 1, 1); label_from = gtk_label_new(NULL); gtk_misc_set_alignment(GTK_MISC(label_from), 0, 0.5); gtk_label_set_ellipsize(GTK_LABEL(label_from), PANGO_ELLIPSIZE_MIDDLE); gtk_grid_attach(GTK_GRID(table), label_from, 1, 0, 1, 1); update_from_label (); label = gtk_label_new(NULL); gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); text = g_markup_printf_escaped("%s", _("To:")); gtk_label_set_markup(GTK_LABEL(label), text); g_free(text); gtk_grid_attach(GTK_GRID(table), label, 0, 1, 1, 1); label = gtk_label_new(NULL); gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); gtk_label_set_ellipsize(GTK_LABEL(label), PANGO_ELLIPSIZE_END); gtk_label_set_text(GTK_LABEL(label), option_device_name); gtk_grid_attach(GTK_GRID(table), label, 1, 1, 1, 1); progress = gtk_progress_bar_new(); gtk_progress_bar_set_show_text (GTK_PROGRESS_BAR (progress), TRUE); gtk_progress_bar_set_ellipsize(GTK_PROGRESS_BAR(progress), PANGO_ELLIPSIZE_END); gtk_progress_bar_set_text(GTK_PROGRESS_BAR(progress), _("Connecting…")); gtk_box_pack_start(GTK_BOX(vbox), progress, TRUE, TRUE, 0); hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4); image_status = gtk_image_new_from_icon_name ("dialog-warning", GTK_ICON_SIZE_MENU); gtk_widget_set_no_show_all (image_status, TRUE); gtk_box_pack_start(GTK_BOX (hbox), image_status, FALSE, FALSE, 4); label_status = gtk_label_new(NULL); gtk_misc_set_alignment(GTK_MISC(label_status), 0, 0.5); gtk_label_set_line_wrap(GTK_LABEL(label_status), TRUE); gtk_box_pack_start(GTK_BOX (hbox), label_status, TRUE, TRUE, 4); gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 2); g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(response_callback), NULL); gtk_widget_show_all(dialog); } static gchar *get_device_name(const gchar *address) { BluetoothClient *client; GtkTreeModel *model; GtkTreeIter iter; gboolean cont; char *found_name; found_name = NULL; client = bluetooth_client_new (); model = bluetooth_client_get_model (client); if (model == NULL) { g_object_unref (client); return NULL; } cont = gtk_tree_model_get_iter_first(model, &iter); while (cont != FALSE) { char *bdaddr, *name; gtk_tree_model_get(model, &iter, BLUETOOTH_COLUMN_ADDRESS, &bdaddr, BLUETOOTH_COLUMN_ALIAS, &name, -1); if (g_strcmp0 (bdaddr, address) == 0) { g_free (bdaddr); found_name = name; break; } g_free (bdaddr); g_free (name); cont = gtk_tree_model_iter_next(model, &iter); } g_object_unref (model); g_object_unref (client); return found_name; } static void on_transfer_properties (GVariant *props) { char *filename = option_files[file_index]; char *basename, *text, *markup; GVariant *size; size = g_variant_lookup_value (props, "Size", G_VARIANT_TYPE_UINT64); if (size) { current_size = g_variant_get_uint64 (size); last_update = get_system_time (); } basename = g_path_get_basename(filename); text = g_strdup_printf(_("Sending %s"), basename); g_free(basename); markup = g_markup_printf_escaped("%s", text); gtk_label_set_markup(GTK_LABEL(label_status), markup); g_free(markup); g_free(text); text = g_strdup_printf(_("Sending file %d of %d"), file_index + 1, file_count); gtk_progress_bar_set_text(GTK_PROGRESS_BAR(progress), text); g_free(text); } static void on_transfer_progress (guint64 transferred) { gint64 current_time; gint elapsed_time; gint remaining_time; gint transfer_rate; guint64 current_sent; gdouble fraction; gchar *time, *rate, *file, *text; current_sent = total_sent + transferred; if (total_size == 0) fraction = 0.0; else fraction = (gdouble) current_sent / (gdouble) total_size; gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progress), fraction); current_time = get_system_time(); elapsed_time = (current_time - first_update) / 1000000; if (current_time < last_update + 1000000) return; last_update = current_time; if (elapsed_time == 0) return; transfer_rate = current_sent / elapsed_time; if (transfer_rate == 0) return; remaining_time = (total_size - current_sent) / transfer_rate; time = format_time(remaining_time); if (transfer_rate >= 3000) rate = g_strdup_printf(_("%d kB/s"), transfer_rate / 1000); else rate = g_strdup_printf(_("%d B/s"), transfer_rate); file = g_strdup_printf(_("Sending file %d of %d"), file_index + 1, file_count); text = g_strdup_printf("%s (%s, %s)", file, rate, time); g_free(file); g_free(rate); g_free(time); gtk_progress_bar_set_text(GTK_PROGRESS_BAR(progress), text); g_free(text); } static void on_transfer_complete (void) { total_sent += current_size; file_index++; /* And we're done with the transfer */ g_object_unref (current_transfer); current_transfer = NULL; if (file_index == file_count) { GtkWidget *button; char *complete; gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progress), 1.0); gtk_progress_bar_set_text(GTK_PROGRESS_BAR(progress), ""); complete = g_strdup_printf (ngettext ("%u transfer complete", "%u transfers complete", file_count), file_count); gtk_label_set_text (GTK_LABEL (label_status), complete); g_free (complete); button = gtk_dialog_get_widget_for_response(GTK_DIALOG (dialog), GTK_RESPONSE_CANCEL); gtk_button_set_label (GTK_BUTTON (button), _("_Close")); } else { send_next_file (); } } static void on_transfer_error (void) { gtk_widget_show (image_status); gtk_label_set_markup (GTK_LABEL (label_status), _("There was an error")); gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), RESPONSE_RETRY, TRUE); g_object_unref (current_transfer); current_transfer = NULL; } static void select_device_changed(BluetoothChooser *sel, char *address, gpointer user_data) { GtkDialog *dialog = user_data; char *icon; if (address == NULL) goto bail; icon = bluetooth_chooser_get_selected_device_icon (sel); if (icon == NULL) goto bail; /* Apple's device don't have OBEX */ if (g_str_equal (icon, "phone-apple-iphone")) goto bail; gtk_dialog_set_response_sensitive (dialog, GTK_RESPONSE_ACCEPT, TRUE); return; bail: gtk_dialog_set_response_sensitive (dialog, GTK_RESPONSE_ACCEPT, FALSE); } static void select_device_activated(BluetoothChooser *sel, char *address, gpointer user_data) { GtkDialog *dialog = user_data; gtk_dialog_response(dialog, GTK_RESPONSE_ACCEPT); } static char * show_browse_dialog (char **device_name) { GtkWidget *dialog, *selector, *send_button, *content_area; char *bdaddr; int response_id; GtkStyleContext *context; dialog = g_object_new (GTK_TYPE_DIALOG, "title", _("Select device to send to"), "use-header-bar", 1, NULL); gtk_dialog_add_buttons(GTK_DIALOG (dialog), _("_Cancel"), GTK_RESPONSE_CANCEL, _("_Send"), GTK_RESPONSE_ACCEPT, NULL); gtk_window_set_type_hint (GTK_WINDOW (dialog), GDK_WINDOW_TYPE_HINT_NORMAL); send_button = gtk_dialog_get_widget_for_response(GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT); context = gtk_widget_get_style_context(send_button); gtk_style_context_add_class (context, "suggested-action"); gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT, FALSE); gtk_window_set_default_size(GTK_WINDOW(dialog), 480, 400); gtk_container_set_border_width (GTK_CONTAINER (dialog), 5); content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog)); gtk_box_set_spacing (GTK_BOX (content_area), 2); selector = bluetooth_chooser_new(); gtk_container_set_border_width(GTK_CONTAINER(selector), 5); gtk_widget_show(selector); g_object_set(selector, "show-searching", TRUE, "show-device-category", TRUE, "show-device-type", TRUE, NULL); g_signal_connect(selector, "selected-device-changed", G_CALLBACK(select_device_changed), dialog); g_signal_connect(selector, "selected-device-activated", G_CALLBACK(select_device_activated), dialog); gtk_box_pack_start (GTK_BOX (content_area), selector, TRUE, TRUE, 0); bluetooth_chooser_start_discovery (BLUETOOTH_CHOOSER (selector)); bdaddr = NULL; response_id = gtk_dialog_run (GTK_DIALOG (dialog)); if (response_id == GTK_RESPONSE_ACCEPT) { bdaddr = bluetooth_chooser_get_selected_device (BLUETOOTH_CHOOSER (selector)); *device_name = bluetooth_chooser_get_selected_device_name (BLUETOOTH_CHOOSER (selector)); } gtk_widget_destroy (dialog); return bdaddr; } static char ** show_select_dialog(void) { GtkWidget *dialog, *button; gchar **files = NULL; GtkStyleContext *context; dialog = g_object_new (GTK_TYPE_FILE_CHOOSER_DIALOG, "title", _("Choose files to send"), "action", GTK_FILE_CHOOSER_ACTION_OPEN, "use-header-bar", 1, NULL); gtk_dialog_add_buttons(GTK_DIALOG (dialog), _("_Cancel"), GTK_RESPONSE_CANCEL, _("Select"), GTK_RESPONSE_ACCEPT, NULL); gtk_window_set_type_hint (GTK_WINDOW (dialog), GDK_WINDOW_TYPE_HINT_NORMAL); gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE); button = gtk_dialog_get_widget_for_response(GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT); context = gtk_widget_get_style_context(button); gtk_style_context_add_class (context, "suggested-action"); if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { GSList *list, *filenames; int i; filenames = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog)); files = g_new(gchar *, g_slist_length(filenames) + 1); for (list = filenames, i = 0; list; list = list->next, i++) files[i] = list->data; files[i] = NULL; g_slist_free(filenames); } gtk_widget_destroy(dialog); return files; } static GOptionEntry options[] = { { "device", 0, 0, G_OPTION_ARG_STRING, &option_device, N_("Remote device to use"), N_("ADDRESS") }, { "name", 0, 0, G_OPTION_ARG_STRING, &option_device_name, N_("Remote device’s name"), N_("NAME") }, { "dest", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, &option_device, NULL, NULL }, { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &option_files }, { NULL }, }; int main(int argc, char *argv[]) { GError *error = NULL; int i; bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR); bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8"); textdomain(GETTEXT_PACKAGE); error = NULL; if (gtk_init_with_args(&argc, &argv, _("[FILE…]"), options, GETTEXT_PACKAGE, &error) == FALSE) { if (error != NULL) { g_printerr("%s\n", error->message); g_error_free(error); } else g_printerr("An unknown error occurred\n"); return 1; } gtk_window_set_default_icon_name("bluetooth"); cancellable = g_cancellable_new (); /* A device name, but no device? */ if (option_device == NULL && option_device_name != NULL) { if (option_files != NULL) g_strfreev(option_files); g_free (option_device_name); return 1; } if (option_files == NULL) { option_files = show_select_dialog(); if (option_files == NULL) return 1; } if (option_device == NULL) { option_device = show_browse_dialog(&option_device_name); if (option_device == NULL) { g_strfreev(option_files); return 1; } } file_count = g_strv_length(option_files); for (i = 0; i < file_count; i++) { gchar *filename; struct stat st; filename = filename_to_path(option_files[i]); if (filename != NULL) { g_free(option_files[i]); option_files[i] = filename; } if (g_file_test(option_files[i], G_FILE_TEST_IS_REGULAR) == FALSE) { option_files[i][0] = '\0'; continue; } if (g_stat(option_files[i], &st) < 0) option_files[i][0] = '\0'; else total_size += st.st_size; } conn = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error); if (conn == NULL) { if (error != NULL) { g_printerr("Connecting to session bus failed: %s\n", error->message); g_error_free(error); } else g_print("An unknown error occurred\n"); return 1; } client_proxy = g_dbus_proxy_new_sync (conn, G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS, NULL, OBEX_SERVICE, OBEX_PATH, CLIENT_IFACE, cancellable, &error); if (client_proxy == NULL) { g_printerr("Acquiring proxy failed: %s\n", error->message); g_error_free (error); return 1; } if (option_device_name == NULL) option_device_name = get_device_name(option_device); if (option_device_name == NULL) option_device_name = g_strdup(option_device); create_window(); if (!g_cancellable_is_cancelled (cancellable)) send_files (); gtk_main(); g_cancellable_cancel (cancellable); g_clear_object (&cancellable); g_clear_object (¤t_transfer); g_clear_object (&session); g_object_unref (client_proxy); g_object_unref (conn); g_strfreev(option_files); g_free(option_device); g_free(option_device_name); return 0; }