~timchen119/ubuntu/trusty/gnome-bluetooth/lp1035431

« back to all changes in this revision

Viewing changes to applet/bluetooth-applet.c

  • Committer: Bazaar Package Importer
  • Author(s): Emilio Pozuelo Monfort
  • Date: 2011-02-27 15:45:22 UTC
  • mfrom: (1.3.2 upstream)
  • mto: (2.2.3 experimental) (1.5.1)
  • mto: This revision was merged to the branch mainline in revision 53.
  • Revision ID: james.westby@ubuntu.com-20110227154522-dnnoqasv5v3mv42a
Tags: upstream-2.91.5
ImportĀ upstreamĀ versionĀ 2.91.5

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*- tab-width: 8 -*-
 
2
 *
 
3
 *  BlueZ - Bluetooth protocol stack for Linux
 
4
 *
 
5
 *  Copyright (C) 2010 Giovanni Campagna <scampa.giovanni@gmail.com>
 
6
 *
 
7
 *
 
8
 *  This program is free software; you can redistribute it and/or modify
 
9
 *  it under the terms of the GNU General Public License as published by
 
10
 *  the Free Software Foundation; either version 2 of the License, or
 
11
 *  (at your option) any later version.
 
12
 *
 
13
 *  This program is distributed in the hope that it will be useful,
 
14
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
15
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
16
 *  GNU General Public License for more details.
 
17
 *
 
18
 *  You should have received a copy of the GNU General Public License
 
19
 *  along with this program; if not, write to the Free Software
 
20
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 
21
 *
 
22
 */
 
23
 
 
24
#include <glib/gi18n.h>
 
25
#include <gtk/gtk.h>
 
26
#include <dbus/dbus-glib.h>
 
27
#include <gio/gio.h>
 
28
 
 
29
#include <bluetooth-applet.h>
 
30
#include <bluetooth-client.h>
 
31
#include <bluetooth-client-private.h>
 
32
#include <bluetooth-killswitch.h>
 
33
#include <bluetooth-agent.h>
 
34
 
 
35
#include <marshal.h>
 
36
 
 
37
static gpointer
 
38
bluetooth_simple_device_copy (gpointer boxed)
 
39
{
 
40
        BluetoothSimpleDevice* origin = (BluetoothSimpleDevice*) boxed;
 
41
 
 
42
        BluetoothSimpleDevice* result = g_new (BluetoothSimpleDevice, 1);
 
43
        result->bdaddr = g_strdup (origin->bdaddr);
 
44
        result->device_path = g_strdup (origin->device_path);
 
45
        result->alias = g_strdup (origin->alias);
 
46
        result->connected = origin->connected;
 
47
        result->can_connect = origin->can_connect;
 
48
        result->capabilities = origin->capabilities;
 
49
        result->type = origin->type;
 
50
 
 
51
        return (gpointer)result;
 
52
}
 
53
 
 
54
static void
 
55
bluetooth_simple_device_free (gpointer boxed)
 
56
{
 
57
        BluetoothSimpleDevice* obj = (BluetoothSimpleDevice*) boxed;
 
58
 
 
59
        g_free (obj->device_path);
 
60
        g_free (obj->bdaddr);
 
61
        g_free (obj->alias);
 
62
        g_free (obj);
 
63
}
 
64
 
 
65
G_DEFINE_BOXED_TYPE(BluetoothSimpleDevice, bluetooth_simple_device, bluetooth_simple_device_copy, bluetooth_simple_device_free)
 
66
 
 
67
struct _BluetoothApplet
 
68
{
 
69
        GObject parent_instance;
 
70
 
 
71
        BluetoothKillswitch* killswitch_manager;
 
72
        BluetoothClient* client;
 
73
        GtkTreeModel* device_model;
 
74
        gulong signal_row_added;
 
75
        gulong signal_row_changed;
 
76
        gulong signal_row_deleted;
 
77
        DBusGProxy* default_adapter;
 
78
        BluetoothAgent* agent;
 
79
        GHashTable* pending_requests;
 
80
 
 
81
        gint num_adapters_powered;
 
82
        gint num_adapters_present;
 
83
};
 
84
 
 
85
struct _BluetoothAppletClass {
 
86
        GObjectClass parent_class;
 
87
};
 
88
 
 
89
G_DEFINE_TYPE(BluetoothApplet, bluetooth_applet, G_TYPE_OBJECT)
 
90
 
 
91
enum {
 
92
        PROP_0,
 
93
        PROP_KILLSWITCH_STATE,
 
94
        PROP_DISCOVERABLE,
 
95
        PROP_FULL_MENU,
 
96
        PROP_LAST
 
97
};
 
98
static GParamSpec *properties[PROP_LAST];
 
99
 
 
100
enum {
 
101
        SIGNAL_DEVICES_CHANGED,
 
102
 
 
103
        SIGNAL_PINCODE_REQUEST,
 
104
        SIGNAL_CONFIRM_REQUEST,
 
105
        SIGNAL_AUTHORIZE_REQUEST,
 
106
        SIGNAL_CANCEL_REQUEST,
 
107
 
 
108
        SIGNAL_LAST
 
109
};
 
110
 
 
111
guint signals[SIGNAL_LAST];
 
112
 
 
113
typedef struct {
 
114
  GSimpleAsyncResult *result;
 
115
  guint timestamp;
 
116
} MountClosure;
 
117
 
 
118
static void
 
119
mount_ready_cb (GObject *object,
 
120
                GAsyncResult *result,
 
121
                gpointer user_data)
 
122
{
 
123
        GError *error = NULL;
 
124
        GFile *file = G_FILE (object);
 
125
        char *uri = g_file_get_uri (file);
 
126
        MountClosure *closure = user_data;
 
127
 
 
128
        if (g_file_mount_enclosing_volume_finish (file, result, &error) == FALSE) {
 
129
                /* Ignore "already mounted" error */
 
130
                if (error->domain == G_IO_ERROR &&
 
131
                    error->code == G_IO_ERROR_ALREADY_MOUNTED) {
 
132
                        g_error_free (error);
 
133
                        error = NULL;
 
134
                }
 
135
        }
 
136
 
 
137
        if (!error) {
 
138
                gtk_show_uri (NULL, uri, closure->timestamp, &error);
 
139
        }
 
140
 
 
141
        if (error) {
 
142
                g_simple_async_result_set_from_error (closure->result, error);
 
143
                g_error_free (error);
 
144
        } else {
 
145
                g_simple_async_result_set_op_res_gboolean (closure->result, TRUE);
 
146
        }
 
147
 
 
148
        g_simple_async_result_complete (closure->result);
 
149
 
 
150
        g_free (uri);
 
151
        g_object_unref (closure->result);
 
152
        g_free (closure);
 
153
}
 
154
 
 
155
/**
 
156
 * bluetooth_applet_browse_address_finish:
 
157
 *
 
158
 * @applet: a #BluetoothApplet
 
159
 * @result: the #GAsyncResult from the callback
 
160
 * @error:
 
161
 *
 
162
 * Returns: TRUE if the operation was successful, FALSE if error is set
 
163
 */
 
164
gboolean
 
165
bluetooth_applet_browse_address_finish (BluetoothApplet *applet,
 
166
                                        GAsyncResult *result,
 
167
                                        GError **error)
 
168
{
 
169
        GSimpleAsyncResult *simple;
 
170
 
 
171
        g_return_val_if_fail (BLUETOOTH_IS_APPLET (applet), FALSE);
 
172
        g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (applet), bluetooth_applet_browse_address), FALSE);
 
173
 
 
174
        simple = G_SIMPLE_ASYNC_RESULT (result);
 
175
        if (g_simple_async_result_propagate_error (simple, error))
 
176
                return FALSE;
 
177
        else
 
178
                return TRUE;
 
179
}
 
180
 
 
181
/**
 
182
 * bluetooth_applet_browse_address:
 
183
 *
 
184
 * Opens a Bluetooth device in Nautilus
 
185
 * @applet: a #BluetoothApplet
 
186
 * @address: the bluetooth device to browse
 
187
 * @callback: (scope async): the completion callback
 
188
 * @user_data:
 
189
 */
 
190
void bluetooth_applet_browse_address (BluetoothApplet *applet,
 
191
                                      const char *address,
 
192
                                      guint timestamp,
 
193
                                      GAsyncReadyCallback callback,
 
194
                                      gpointer user_data)
 
195
{
 
196
        GFile *file;
 
197
        char *uri;
 
198
        MountClosure *closure;
 
199
 
 
200
        g_return_if_fail (BLUETOOTH_IS_APPLET (applet));
 
201
        g_return_if_fail (address != NULL);
 
202
 
 
203
        uri = g_strdup_printf ("obex://[%s]/", address);
 
204
        file = g_file_new_for_uri (uri);
 
205
 
 
206
        closure = g_new (MountClosure, 1);
 
207
        closure->result = g_simple_async_result_new (G_OBJECT (applet), callback, user_data, bluetooth_applet_browse_address);
 
208
        closure->timestamp = timestamp;
 
209
        g_file_mount_enclosing_volume(file, G_MOUNT_MOUNT_NONE, NULL, NULL, mount_ready_cb, closure);
 
210
 
 
211
        g_free (uri);
 
212
        g_object_unref (file);
 
213
}
 
214
 
 
215
/**
 
216
 * bluetooth_applet_send_to_address:
 
217
 *
 
218
 * Send a file to a bluetooth device
 
219
 * @applet: a #BluetoothApplet
 
220
 * @address: the target
 
221
 * @alias: the string to display for the device
 
222
 */
 
223
void bluetooth_applet_send_to_address (BluetoothApplet *applet,
 
224
                                       const char *address,
 
225
                                       const char *alias)
 
226
{
 
227
        GPtrArray *a;
 
228
        GError *err = NULL;
 
229
        guint i;
 
230
 
 
231
        g_return_if_fail (BLUETOOTH_IS_APPLET (applet));
 
232
 
 
233
        a = g_ptr_array_new ();
 
234
        g_ptr_array_add (a, "bluetooth-sendto");
 
235
        if (address != NULL) {
 
236
                char *s;
 
237
 
 
238
                s = g_strdup_printf ("--device=\"%s\"", address);
 
239
                g_ptr_array_add (a, s);
 
240
        }
 
241
        if (address != NULL && alias != NULL) {
 
242
                char *s;
 
243
 
 
244
                s = g_strdup_printf ("--name=\"%s\"", alias);
 
245
                g_ptr_array_add (a, s);
 
246
        }
 
247
        g_ptr_array_add (a, NULL);
 
248
 
 
249
        if (g_spawn_async(NULL, (char **) a->pdata, NULL,
 
250
                          G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, &err) == FALSE) {
 
251
                g_printerr("Couldn't execute command: %s\n", err->message);
 
252
                g_error_free (err);
 
253
        }
 
254
 
 
255
        for (i = 1; a->pdata[i] != NULL; i++)
 
256
                g_free (a->pdata[i]);
 
257
 
 
258
        g_ptr_array_free (a, TRUE);
 
259
}
 
260
 
 
261
/**
 
262
 * bluetooth_applet_agent_reply_pincode:
 
263
 *
 
264
 * @self: a #BluetoothApplet
 
265
 * @request_key: an opaque token given in the pincode-request signal
 
266
 * @pincode: (allow-none): the PIN code entered by the user, as a string, or NULL if the dialog was dismissed
 
267
 */
 
268
void
 
269
bluetooth_applet_agent_reply_pincode (BluetoothApplet *self,
 
270
                                      const char      *request_key,
 
271
                                      const char      *pincode)
 
272
{
 
273
        DBusGMethodInvocation* context;
 
274
 
 
275
        g_return_if_fail (BLUETOOTH_IS_APPLET (self));
 
276
        g_return_if_fail (request_key != NULL);
 
277
 
 
278
        context = g_hash_table_lookup (self->pending_requests, request_key);
 
279
 
 
280
        if (pincode != NULL) {
 
281
                dbus_g_method_return (context, pincode);
 
282
        } else {
 
283
                GError *error;
 
284
                error = g_error_new (AGENT_ERROR, AGENT_ERROR_REJECT,
 
285
                                     "Pairing request rejected");
 
286
                dbus_g_method_return_error (context, error);
 
287
        }
 
288
 
 
289
        g_hash_table_remove (self->pending_requests, request_key);
 
290
}
 
291
 
 
292
/**
 
293
 * bluetooth_applet_agent_reply_passkey:
 
294
 *
 
295
 * @self: a #BluetoothApplet
 
296
 * @request_key: an opaque token given in the pincode-request signal
 
297
 * @passkey: the numeric PIN code entered by the user, or -1 if the dialog was dismissed
 
298
 */
 
299
void
 
300
bluetooth_applet_agent_reply_passkey (BluetoothApplet *self,
 
301
                                      const char      *request_key,
 
302
                                      int              passkey)
 
303
{
 
304
        DBusGMethodInvocation* context;
 
305
 
 
306
        g_return_if_fail (BLUETOOTH_IS_APPLET (self));
 
307
        g_return_if_fail (request_key != NULL);
 
308
 
 
309
        context = g_hash_table_lookup (self->pending_requests, request_key);
 
310
 
 
311
        if (passkey != -1) {
 
312
                dbus_g_method_return (context, passkey);
 
313
        } else {
 
314
                GError *error;
 
315
                error = g_error_new (AGENT_ERROR, AGENT_ERROR_REJECT,
 
316
                                     "Pairing request rejected");
 
317
                dbus_g_method_return_error (context, error);
 
318
        }
 
319
 
 
320
        g_hash_table_remove (self->pending_requests, request_key);
 
321
}
 
322
 
 
323
/**
 
324
 * bluetooth_applet_agent_reply_confirm:
 
325
 *
 
326
 * @self: a #BluetoothApplet
 
327
 * @request_key: an opaque token given in the pincode-request signal
 
328
 * @confirm: TRUE if operation was confirmed, FALSE otherwise
 
329
 */
 
330
void
 
331
bluetooth_applet_agent_reply_confirm (BluetoothApplet *self,
 
332
                                      const char      *request_key,
 
333
                                      gboolean         confirm)
 
334
{
 
335
        DBusGMethodInvocation* context;
 
336
 
 
337
        g_return_if_fail (BLUETOOTH_IS_APPLET (self));
 
338
        g_return_if_fail (request_key != NULL);
 
339
 
 
340
        context = g_hash_table_lookup (self->pending_requests, request_key);
 
341
 
 
342
        if (confirm) {
 
343
                dbus_g_method_return (context);
 
344
        } else {
 
345
                GError *error;
 
346
                error = g_error_new (AGENT_ERROR, AGENT_ERROR_REJECT,
 
347
                                     "Confirmation request rejected");
 
348
                dbus_g_method_return_error (context, error);
 
349
        }
 
350
 
 
351
        g_hash_table_remove (self->pending_requests, request_key);
 
352
}
 
353
 
 
354
/**
 
355
 * bluetooth_applet_agent_reply_auth:
 
356
 *
 
357
 * @self: a #BluetoothApplet
 
358
 * @request_key: an opaque token given in the pincode-request signal
 
359
 * @auth: TRUE if operation was authorized, FALSE otherwise
 
360
 * @trusted: TRUE if the operation should be authorized automatically in the future
 
361
 */
 
362
void
 
363
bluetooth_applet_agent_reply_auth (BluetoothApplet *self,
 
364
                                   const char      *request_key,
 
365
                                   gboolean         auth,
 
366
                                   gboolean         trusted)
 
367
{
 
368
        DBusGMethodInvocation* context;
 
369
 
 
370
        g_return_if_fail (BLUETOOTH_IS_APPLET (self));
 
371
        g_return_if_fail (request_key != NULL);
 
372
 
 
373
        context = g_hash_table_lookup (self->pending_requests, request_key);
 
374
 
 
375
        if (auth) {
 
376
                if (trusted)
 
377
                        bluetooth_client_set_trusted (self->client, request_key, TRUE);
 
378
 
 
379
                dbus_g_method_return (context);
 
380
        } else {
 
381
                GError *error;
 
382
                error = g_error_new (AGENT_ERROR, AGENT_ERROR_REJECT,
 
383
                                     "Confirmation request rejected");
 
384
                dbus_g_method_return_error (context, error);
 
385
        }
 
386
 
 
387
        g_hash_table_remove (self->pending_requests, request_key);
 
388
}
 
389
 
 
390
#ifndef DBUS_TYPE_G_DICTIONARY
 
391
#define DBUS_TYPE_G_DICTIONARY \
 
392
        (dbus_g_type_get_map("GHashTable", G_TYPE_STRING, G_TYPE_VALUE))
 
393
#endif
 
394
 
 
395
static char *
 
396
device_get_name (DBusGProxy *proxy, char **long_name)
 
397
{
 
398
        GHashTable *hash;
 
399
        GValue *value;
 
400
        char *alias, *address;
 
401
 
 
402
        g_return_val_if_fail (long_name != NULL, NULL);
 
403
 
 
404
        if (dbus_g_proxy_call (proxy, "GetProperties",  NULL,
 
405
                               G_TYPE_INVALID,
 
406
                               DBUS_TYPE_G_DICTIONARY, &hash,
 
407
                               G_TYPE_INVALID) == FALSE) {
 
408
                return NULL;
 
409
        }
 
410
 
 
411
        value = g_hash_table_lookup (hash, "Address");
 
412
        if (value == NULL) {
 
413
                g_hash_table_destroy (hash);
 
414
                return NULL;
 
415
        }
 
416
        address = g_value_dup_string (value);
 
417
 
 
418
        value = g_hash_table_lookup (hash, "Name");
 
419
        alias = value ? g_value_dup_string (value) : address;
 
420
 
 
421
        g_hash_table_destroy (hash);
 
422
 
 
423
        if (value)
 
424
                *long_name = g_strdup_printf ("'%s' (%s)", alias, address);
 
425
        else
 
426
                *long_name = g_strdup_printf ("'%s'", address);
 
427
 
 
428
        if (alias != address)
 
429
                g_free (address);
 
430
        return alias;
 
431
}
 
432
 
 
433
static gboolean
 
434
pincode_request (DBusGMethodInvocation *context,
 
435
                 DBusGProxy            *device,
 
436
                 gpointer               user_data)
 
437
{
 
438
        BluetoothApplet* self = BLUETOOTH_APPLET (user_data);
 
439
        char *name;
 
440
        char *long_name = NULL;
 
441
        const char *path;
 
442
 
 
443
        name = device_get_name (device, &long_name);
 
444
        path = dbus_g_proxy_get_path (device);
 
445
        g_hash_table_insert (self->pending_requests, g_strdup (path), context);
 
446
 
 
447
        g_signal_emit (self, signals[SIGNAL_PINCODE_REQUEST], 0, path, name, long_name, FALSE);
 
448
 
 
449
        g_free (name);
 
450
        g_free (long_name);
 
451
 
 
452
        return TRUE;
 
453
}
 
454
 
 
455
static gboolean
 
456
passkey_request (DBusGMethodInvocation *context,
 
457
                 DBusGProxy            *device,
 
458
                 gpointer               user_data)
 
459
{
 
460
        BluetoothApplet* self = BLUETOOTH_APPLET (user_data);
 
461
        char *name;
 
462
        char *long_name = NULL;
 
463
        const char *path;
 
464
 
 
465
        name = device_get_name (device, &long_name);
 
466
        path = dbus_g_proxy_get_path (device);
 
467
        g_hash_table_insert (self->pending_requests, g_strdup (path), context);
 
468
 
 
469
        g_signal_emit (self, signals[SIGNAL_PINCODE_REQUEST], 0, path, name, long_name, TRUE);
 
470
 
 
471
        g_free (name);
 
472
        g_free (long_name);
 
473
 
 
474
        return TRUE;
 
475
}
 
476
 
 
477
static gboolean
 
478
confirm_request (DBusGMethodInvocation *context,
 
479
                 DBusGProxy *device,
 
480
                 guint pin,
 
481
                 gpointer user_data)
 
482
{
 
483
        BluetoothApplet* self = BLUETOOTH_APPLET (user_data);
 
484
        char *name;
 
485
        char *long_name = NULL;
 
486
        const char *path;
 
487
 
 
488
        name = device_get_name (device, &long_name);
 
489
        path = dbus_g_proxy_get_path (device);
 
490
        g_hash_table_insert (self->pending_requests, g_strdup (path), context);
 
491
 
 
492
        g_signal_emit (self, signals[SIGNAL_CONFIRM_REQUEST], 0, path, name, long_name, pin);
 
493
 
 
494
        g_free (name);
 
495
        g_free (long_name);
 
496
 
 
497
        return TRUE;
 
498
}
 
499
 
 
500
static gboolean
 
501
authorize_request (DBusGMethodInvocation *context,
 
502
                   DBusGProxy *device,
 
503
                   const char *uuid,
 
504
                   gpointer user_data)
 
505
{
 
506
        BluetoothApplet* self = BLUETOOTH_APPLET (user_data);
 
507
        char *name;
 
508
        char *long_name = NULL;
 
509
        const char *path;
 
510
 
 
511
        name = device_get_name (device, &long_name);
 
512
        path = dbus_g_proxy_get_path (device);
 
513
        g_hash_table_insert (self->pending_requests, g_strdup (path), context);
 
514
 
 
515
        g_signal_emit (self, signals[SIGNAL_AUTHORIZE_REQUEST], 0, path, name, long_name, uuid);
 
516
 
 
517
        g_free (name);
 
518
        g_free (long_name);
 
519
 
 
520
        return TRUE;
 
521
}
 
522
 
 
523
static void
 
524
cancel_request_single (gpointer key, gpointer value, gpointer user_data)
 
525
{
 
526
        DBusGMethodInvocation* request_context = value;
 
527
        GError* result;
 
528
 
 
529
        if (value) {
 
530
                result = g_error_new (AGENT_ERROR, AGENT_ERROR_REJECT, "Agent callback cancelled");
 
531
                dbus_g_method_return_error (request_context, result);
 
532
        }
 
533
}
 
534
 
 
535
static gboolean
 
536
cancel_request(DBusGMethodInvocation *context,
 
537
               gpointer user_data)
 
538
{
 
539
        BluetoothApplet* self = BLUETOOTH_APPLET (user_data);
 
540
 
 
541
        g_hash_table_foreach (self->pending_requests, cancel_request_single, NULL);
 
542
        g_hash_table_remove_all (self->pending_requests);
 
543
 
 
544
        g_signal_emit (self, signals[SIGNAL_CANCEL_REQUEST], 0);
 
545
 
 
546
        return TRUE;
 
547
}
 
548
 
 
549
static void
 
550
device_added_or_changed (GtkTreeModel *model,
 
551
                         GtkTreePath  *path,
 
552
                         GtkTreeIter  *iter,
 
553
                         gpointer      data)
 
554
{
 
555
        BluetoothApplet *self = BLUETOOTH_APPLET (data);
 
556
 
 
557
        g_signal_emit (self, signals[SIGNAL_DEVICES_CHANGED], 0);
 
558
}
 
559
 
 
560
static void
 
561
device_removed (GtkTreeModel *model,
 
562
                GtkTreePath *path,
 
563
                gpointer user_data)
 
564
{
 
565
        device_added_or_changed (model, path, NULL, user_data);
 
566
}
 
567
 
 
568
static void
 
569
default_adapter_changed (GObject    *client,
 
570
                         GParamSpec *spec,
 
571
                         gpointer    data)
 
572
{
 
573
        BluetoothApplet* self = BLUETOOTH_APPLET (data);
 
574
 
 
575
        if (self->default_adapter)
 
576
                g_object_unref (self->default_adapter);
 
577
        self->default_adapter = bluetooth_client_get_default_adapter (self->client);
 
578
 
 
579
        if (self->device_model) {
 
580
                g_signal_handler_disconnect (self->device_model, self->signal_row_added);
 
581
                g_signal_handler_disconnect (self->device_model, self->signal_row_changed);
 
582
                g_signal_handler_disconnect (self->device_model, self->signal_row_deleted);
 
583
                g_object_unref (self->device_model);
 
584
        }
 
585
        if (self->default_adapter)
 
586
                self->device_model = bluetooth_client_get_device_model (self->client, self->default_adapter);
 
587
        else
 
588
                self->device_model = NULL;
 
589
 
 
590
        if (self->device_model) {
 
591
                self->signal_row_added = g_signal_connect (self->device_model, "row-inserted",
 
592
                                                           G_CALLBACK(device_added_or_changed), self);
 
593
                self->signal_row_deleted = g_signal_connect (self->device_model, "row-deleted",
 
594
                                                             G_CALLBACK(device_removed), self);
 
595
                self->signal_row_changed = g_signal_connect (self->device_model, "row-changed",
 
596
                                                             G_CALLBACK (device_added_or_changed), self);
 
597
        }
 
598
 
 
599
        if (self->agent)
 
600
                g_object_unref (self->agent);
 
601
 
 
602
        if (self->default_adapter) {
 
603
                self->agent = bluetooth_agent_new ();
 
604
                g_object_add_weak_pointer (G_OBJECT (self->agent), (void**) &(self->agent));
 
605
 
 
606
                bluetooth_agent_set_pincode_func (self->agent, pincode_request, self);
 
607
                bluetooth_agent_set_passkey_func (self->agent, passkey_request, self);
 
608
                bluetooth_agent_set_authorize_func (self->agent, authorize_request, self);
 
609
                bluetooth_agent_set_confirm_func (self->agent, confirm_request, self);
 
610
                bluetooth_agent_set_cancel_func (self->agent, cancel_request, self);
 
611
 
 
612
                bluetooth_agent_register (self->agent, self->default_adapter);
 
613
        }
 
614
 
 
615
        g_signal_emit (self, signals[SIGNAL_DEVICES_CHANGED], 0);
 
616
}
 
617
 
 
618
static void
 
619
default_adapter_powered_changed (GObject    *client,
 
620
                                 GParamSpec *spec,
 
621
                                 gpointer    data)
 
622
{
 
623
        BluetoothApplet *self = BLUETOOTH_APPLET (data);
 
624
 
 
625
        g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FULL_MENU]);
 
626
}
 
627
 
 
628
static void
 
629
default_adapter_discoverable_changed (GObject    *client,
 
630
                                 GParamSpec *spec,
 
631
                                 gpointer    data)
 
632
{
 
633
        BluetoothApplet *self = BLUETOOTH_APPLET (data);
 
634
 
 
635
        g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DISCOVERABLE]);
 
636
}
 
637
 
 
638
static gboolean
 
639
set_powered_foreach (GtkTreeModel *model,
 
640
                     GtkTreePath  *path,
 
641
                     GtkTreeIter  *iter,
 
642
                     gpointer      data)
 
643
{
 
644
        DBusGProxy *proxy = NULL;
 
645
        GValue value = { 0, };
 
646
 
 
647
        gtk_tree_model_get (model, iter,
 
648
                            BLUETOOTH_COLUMN_PROXY, &proxy, -1);
 
649
        if (proxy == NULL)
 
650
                return FALSE;
 
651
 
 
652
        g_value_init (&value, G_TYPE_BOOLEAN);
 
653
        g_value_set_boolean (&value, TRUE);
 
654
 
 
655
        dbus_g_proxy_call_no_reply (proxy, "SetProperty",
 
656
                                    G_TYPE_STRING, "Powered",
 
657
                                    G_TYPE_VALUE, &value,
 
658
                                    G_TYPE_INVALID,
 
659
                                    G_TYPE_INVALID);
 
660
 
 
661
        g_value_unset (&value);
 
662
        g_object_unref (proxy);
 
663
 
 
664
        return FALSE;
 
665
}
 
666
 
 
667
static void
 
668
set_adapter_powered (BluetoothApplet* self)
 
669
{
 
670
        GtkTreeModel *adapters;
 
671
 
 
672
        adapters = bluetooth_client_get_adapter_model (self->client);
 
673
        gtk_tree_model_foreach (adapters, set_powered_foreach, NULL);
 
674
        g_object_unref (adapters);
 
675
}
 
676
 
 
677
static gboolean
 
678
device_has_uuid (const char **uuids, const char *uuid)
 
679
{
 
680
        guint i;
 
681
 
 
682
        if (uuids == NULL)
 
683
                return FALSE;
 
684
 
 
685
        for (i = 0; uuids[i] != NULL; i++) {
 
686
                if (g_str_equal (uuid, uuids[i]) != FALSE)
 
687
                        return TRUE;
 
688
        }
 
689
        return FALSE;
 
690
}
 
691
 
 
692
static void
 
693
killswitch_state_change (BluetoothKillswitch *kill_switch, KillswitchState state, gpointer user_data)
 
694
{
 
695
  BluetoothApplet *self = BLUETOOTH_APPLET (user_data);
 
696
 
 
697
  g_object_notify (G_OBJECT (self), "killswitch-state");
 
698
}
 
699
 
 
700
typedef struct {
 
701
        BluetoothApplet* self;
 
702
        BluetoothAppletConnectFunc func;
 
703
        gpointer user_data;
 
704
} ConnectionClosure;
 
705
 
 
706
static void
 
707
connection_callback (BluetoothClient* client, gboolean success, gpointer data)
 
708
{
 
709
        ConnectionClosure *closure = (ConnectionClosure*) data;
 
710
 
 
711
        (*(closure->func)) (closure->self, success, closure->user_data);
 
712
 
 
713
        g_free (closure);
 
714
}
 
715
 
 
716
/**
 
717
 * bluetooth_applet_connect_device:
 
718
 *
 
719
 * @applet: a #BluetoothApplet
 
720
 * @device: the device to connect
 
721
 * @func: (scope async): a completion callback
 
722
 * @data: user data
 
723
 */
 
724
gboolean
 
725
bluetooth_applet_connect_device (BluetoothApplet* applet,
 
726
                                 const char* device,
 
727
                                 BluetoothAppletConnectFunc func,
 
728
                                 gpointer data)
 
729
{
 
730
        ConnectionClosure *closure;
 
731
 
 
732
        g_return_val_if_fail (BLUETOOTH_IS_APPLET (applet), FALSE);
 
733
        g_return_val_if_fail (device != NULL, FALSE);
 
734
        g_return_val_if_fail (func != NULL, FALSE);
 
735
 
 
736
        closure = g_new (ConnectionClosure, 1);
 
737
        closure->self = applet;
 
738
        closure->func = func;
 
739
        closure->user_data = data;
 
740
 
 
741
        return bluetooth_client_connect_service (applet->client, device, connection_callback, closure);
 
742
}
 
743
 
 
744
/**
 
745
 * bluetooth_applet_disconnect_device:
 
746
 *
 
747
 * @applet: a #BluetoothApplet
 
748
 * @device: the device to disconnect
 
749
 * @func: (scope async): a completion callback
 
750
 * @data: user data
 
751
 */
 
752
gboolean
 
753
bluetooth_applet_disconnect_device (BluetoothApplet* applet,
 
754
                                 const char* device,
 
755
                                 BluetoothAppletConnectFunc func,
 
756
                                 gpointer data)
 
757
{
 
758
        ConnectionClosure *closure;
 
759
 
 
760
        g_return_val_if_fail (BLUETOOTH_IS_APPLET (applet), FALSE);
 
761
        g_return_val_if_fail (device != NULL, FALSE);
 
762
        g_return_val_if_fail (func != NULL, FALSE);
 
763
 
 
764
        closure = g_new (ConnectionClosure, 1);
 
765
        closure->self = applet;
 
766
        closure->func = func;
 
767
        closure->user_data = data;
 
768
 
 
769
        return bluetooth_client_disconnect_service (applet->client, device, connection_callback, closure);
 
770
}
 
771
 
 
772
/**
 
773
 * bluetooth_applet_get_discoverable:
 
774
 *
 
775
 * @self: a #BluetoothApplet
 
776
 *
 
777
 * Returns: TRUE if the default adapter is discoverable, false otherwise
 
778
 */
 
779
gboolean
 
780
bluetooth_applet_get_discoverable (BluetoothApplet* self)
 
781
{
 
782
        g_return_val_if_fail (BLUETOOTH_IS_APPLET (self), FALSE);
 
783
 
 
784
        return bluetooth_client_get_discoverable (self->client);
 
785
}
 
786
 
 
787
/**
 
788
 * bluetooth_applet_set_discoverable:
 
789
 *
 
790
 * @self: a #BluetoothApplet
 
791
 * @disc:
 
792
 */
 
793
void
 
794
bluetooth_applet_set_discoverable (BluetoothApplet* self, gboolean disc)
 
795
{
 
796
        g_return_if_fail (BLUETOOTH_IS_APPLET (self));
 
797
 
 
798
        bluetooth_client_set_discoverable (self->client, disc, 0);
 
799
}
 
800
 
 
801
/**
 
802
 * bluetooth_applet_get_killswitch_state:
 
803
 *
 
804
 * @self: a #BluetoothApplet
 
805
 *
 
806
 * Returns: the state of the killswitch, if one is present, or BLUETOOTH_KILLSWITCH_STATE_NO_ADAPTER otherwise
 
807
 */
 
808
BluetoothKillswitchState
 
809
bluetooth_applet_get_killswitch_state (BluetoothApplet* self)
 
810
{
 
811
 
 
812
        g_return_val_if_fail (BLUETOOTH_IS_APPLET (self), BLUETOOTH_KILLSWITCH_STATE_NO_ADAPTER);
 
813
 
 
814
        if (bluetooth_killswitch_has_killswitches (self->killswitch_manager))
 
815
                return bluetooth_killswitch_get_state (self->killswitch_manager);
 
816
        else
 
817
                return BLUETOOTH_KILLSWITCH_STATE_NO_ADAPTER;
 
818
}
 
819
 
 
820
/**
 
821
 * bluetooth_applet_set_killswitch_state:
 
822
 *
 
823
 * @self: a #BluetoothApplet
 
824
 * @state: the new state
 
825
 *
 
826
 * Returns: TRUE if the operation could be performed, FALSE otherwise
 
827
 */
 
828
gboolean
 
829
bluetooth_applet_set_killswitch_state (BluetoothApplet* self, BluetoothKillswitchState state)
 
830
{
 
831
 
 
832
        g_return_val_if_fail (BLUETOOTH_IS_APPLET (self), FALSE);
 
833
 
 
834
        if (bluetooth_killswitch_has_killswitches (self->killswitch_manager)) {
 
835
                bluetooth_killswitch_set_state (self->killswitch_manager, state);
 
836
                return TRUE;
 
837
        }
 
838
        return FALSE;
 
839
}
 
840
 
 
841
/**
 
842
 * bluetooth_applet_get_show_full_menu:
 
843
 *
 
844
 * @self: a #BluetoothApplet
 
845
 *
 
846
 * Returns: TRUE if the full menu is to be shown, FALSE otherwise
 
847
 * (full menu includes device submenus and global actions)
 
848
 */
 
849
gboolean
 
850
bluetooth_applet_get_show_full_menu (BluetoothApplet* self)
 
851
{
 
852
        gboolean has_adapter, has_powered_adapter;
 
853
        g_return_val_if_fail (BLUETOOTH_IS_APPLET (self), FALSE);
 
854
 
 
855
        has_adapter = self->default_adapter != NULL;
 
856
        g_object_get (G_OBJECT (self->client), "default-adapter-powered", &has_powered_adapter, NULL);
 
857
 
 
858
        if (!has_adapter)
 
859
                return FALSE;
 
860
 
 
861
        return has_powered_adapter &&
 
862
                bluetooth_applet_get_killswitch_state(self) == BLUETOOTH_KILLSWITCH_STATE_UNBLOCKED;
 
863
}
 
864
 
 
865
static BluetoothSimpleDevice *
 
866
bluetooth_applet_create_device_from_iter (GtkTreeModel *model,
 
867
                                          GtkTreeIter  *iter,
 
868
                                          gboolean      check_proxy)
 
869
{
 
870
        BluetoothSimpleDevice *dev;
 
871
        GHashTable *services;
 
872
        DBusGProxy *proxy;
 
873
        char **uuids;
 
874
 
 
875
        dev = g_new0 (BluetoothSimpleDevice, 1);
 
876
 
 
877
        gtk_tree_model_get (model, iter,
 
878
                            BLUETOOTH_COLUMN_ADDRESS, &dev->bdaddr,
 
879
                            BLUETOOTH_COLUMN_PROXY, &proxy,
 
880
                            BLUETOOTH_COLUMN_SERVICES, &services,
 
881
                            BLUETOOTH_COLUMN_ALIAS, &dev->alias,
 
882
                            BLUETOOTH_COLUMN_UUIDS, &uuids,
 
883
                            BLUETOOTH_COLUMN_TYPE, &dev->type,
 
884
                            -1);
 
885
 
 
886
        if (dev->bdaddr == NULL || dev->alias == NULL ||
 
887
            (check_proxy != FALSE && proxy == NULL)) {
 
888
                if (proxy != NULL)
 
889
                        g_object_unref (proxy);
 
890
                g_strfreev (uuids);
 
891
                if (services != NULL)
 
892
                        g_hash_table_unref (services);
 
893
                bluetooth_simple_device_free (dev);
 
894
 
 
895
                return NULL;
 
896
        }
 
897
 
 
898
        if (proxy != NULL) {
 
899
                dev->device_path = g_strdup (dbus_g_proxy_get_path (proxy));
 
900
                g_object_unref (proxy);
 
901
        }
 
902
 
 
903
        /* If one service is connected, then we're connected */
 
904
        dev->connected = FALSE;
 
905
        dev->can_connect = FALSE;
 
906
        if (services != NULL) {
 
907
                GList *list, *l;
 
908
 
 
909
                dev->can_connect = TRUE;
 
910
                list = g_hash_table_get_values (services);
 
911
                for (l = list; l != NULL; l = l->next) {
 
912
                        BluetoothStatus val = GPOINTER_TO_INT (l->data);
 
913
                        if (val == BLUETOOTH_STATUS_CONNECTED ||
 
914
                            val == BLUETOOTH_STATUS_PLAYING) {
 
915
                                dev->connected = TRUE;
 
916
                                break;
 
917
                        }
 
918
                }
 
919
                g_list_free (list);
 
920
        }
 
921
 
 
922
        dev->capabilities = 0;
 
923
        dev->capabilities |= device_has_uuid ((const char **) uuids, "OBEXObjectPush") ? BLUETOOTH_CAPABILITIES_OBEX_PUSH : 0;
 
924
        dev->capabilities |= device_has_uuid ((const char **) uuids, "OBEXFileTransfer") ? BLUETOOTH_CAPABILITIES_OBEX_FILE_TRANSFER : 0;
 
925
 
 
926
        if (services != NULL)
 
927
                g_hash_table_unref (services);
 
928
        g_strfreev (uuids);
 
929
 
 
930
        return dev;
 
931
}
 
932
 
 
933
/**
 
934
 * bluetooth_applet_get_devices:
 
935
 *
 
936
 * @self: a #BluetoothApplet
 
937
 *
 
938
 * Returns: (element-type GnomeBluetoothApplet.SimpleDevice) (transfer full): Returns the devices which should be shown to the user
 
939
 */
 
940
GList*
 
941
bluetooth_applet_get_devices (BluetoothApplet* self)
 
942
{
 
943
        GList* result = NULL;
 
944
        GtkTreeIter iter;
 
945
        gboolean cont;
 
946
 
 
947
        g_return_val_if_fail (BLUETOOTH_IS_APPLET (self), NULL);
 
948
 
 
949
        /* No adapter */
 
950
        if (self->default_adapter == NULL)
 
951
                return NULL;
 
952
 
 
953
        cont = gtk_tree_model_get_iter_first (self->device_model, &iter);
 
954
        while (cont) {
 
955
                BluetoothSimpleDevice *dev;
 
956
 
 
957
                dev = bluetooth_applet_create_device_from_iter (self->device_model, &iter, TRUE);
 
958
 
 
959
                if (dev != NULL)
 
960
                        result = g_list_prepend (result, dev);
 
961
 
 
962
                cont = gtk_tree_model_iter_next (self->device_model, &iter);
 
963
        }
 
964
 
 
965
        result = g_list_reverse (result);
 
966
 
 
967
        return result;
 
968
}
 
969
 
 
970
static void
 
971
bluetooth_applet_get_property (GObject    *self,
 
972
                               guint       property_id,
 
973
                               GValue     *value,
 
974
                               GParamSpec *pspec)
 
975
{
 
976
        switch (property_id) {
 
977
        case PROP_FULL_MENU:
 
978
                g_value_set_boolean (value, bluetooth_applet_get_show_full_menu (BLUETOOTH_APPLET (self)));
 
979
                return;
 
980
        case PROP_KILLSWITCH_STATE:
 
981
                g_value_set_int (value, bluetooth_applet_get_killswitch_state (BLUETOOTH_APPLET (self)));
 
982
                return;
 
983
        case PROP_DISCOVERABLE:
 
984
                g_value_set_boolean (value, bluetooth_applet_get_discoverable (BLUETOOTH_APPLET (self)));
 
985
                return;
 
986
        default:
 
987
                G_OBJECT_WARN_INVALID_PROPERTY_ID (self, property_id, pspec);
 
988
        }
 
989
}
 
990
 
 
991
static void
 
992
bluetooth_applet_set_property (GObject      *gobj,
 
993
                               guint         property_id,
 
994
                               const GValue *value,
 
995
                               GParamSpec   *pspec)
 
996
{
 
997
        BluetoothApplet *self = BLUETOOTH_APPLET (gobj);
 
998
 
 
999
        switch (property_id) {
 
1000
        case PROP_KILLSWITCH_STATE:
 
1001
                bluetooth_applet_set_killswitch_state (self, g_value_get_int (value));
 
1002
                return;
 
1003
        case PROP_DISCOVERABLE:
 
1004
                bluetooth_applet_set_discoverable (self, g_value_get_boolean (value));
 
1005
                return;
 
1006
        default:
 
1007
                G_OBJECT_WARN_INVALID_PROPERTY_ID (self, property_id, pspec);
 
1008
        }
 
1009
}
 
1010
 
 
1011
static void
 
1012
bluetooth_applet_init (BluetoothApplet *self)
 
1013
{
 
1014
        self->client = bluetooth_client_new ();
 
1015
        self->device_model = NULL;
 
1016
 
 
1017
        self->default_adapter = NULL;
 
1018
        self->agent = NULL;
 
1019
 
 
1020
        self->killswitch_manager = bluetooth_killswitch_new ();
 
1021
        g_signal_connect (self->killswitch_manager, "state-changed", G_CALLBACK(killswitch_state_change), self);
 
1022
 
 
1023
        self->pending_requests = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
 
1024
        dbus_g_error_domain_register (AGENT_ERROR, "org.bluez.Error", AGENT_ERROR_TYPE);
 
1025
 
 
1026
        /* Make sure all the unblocked adapters are powered,
 
1027
         * so as to avoid seeing unpowered, but unblocked
 
1028
         * devices */
 
1029
        set_adapter_powered (self);
 
1030
        default_adapter_changed (NULL, NULL, self);
 
1031
 
 
1032
        g_signal_connect (self->client, "notify::default-adapter", G_CALLBACK (default_adapter_changed), self);
 
1033
        g_signal_connect (self->client, "notify::default-adapter-powered", G_CALLBACK (default_adapter_powered_changed), self);
 
1034
        g_signal_connect (self->client, "notify::default-adapter-discoverable", G_CALLBACK (default_adapter_discoverable_changed), self);
 
1035
}
 
1036
 
 
1037
static void
 
1038
bluetooth_applet_dispose (GObject* self)
 
1039
{
 
1040
 
 
1041
        BluetoothApplet* applet = BLUETOOTH_APPLET (self);
 
1042
 
 
1043
        if (applet->client) {
 
1044
                g_object_unref (applet->client);
 
1045
                applet->client = NULL;
 
1046
        }
 
1047
 
 
1048
        if (applet->killswitch_manager) {
 
1049
                g_object_unref (applet->killswitch_manager);
 
1050
                applet->killswitch_manager = NULL;
 
1051
        }
 
1052
 
 
1053
        if (applet->device_model) {
 
1054
                g_object_unref (applet->device_model);
 
1055
                applet->device_model = NULL;
 
1056
        }
 
1057
 
 
1058
        if (applet->agent) {
 
1059
                g_object_unref (applet->agent);
 
1060
                applet->agent = NULL;
 
1061
        }
 
1062
}
 
1063
 
 
1064
static void
 
1065
bluetooth_applet_class_init (BluetoothAppletClass *klass)
 
1066
{
 
1067
        GObjectClass* gobject_class = G_OBJECT_CLASS (klass);
 
1068
 
 
1069
        gobject_class->dispose = bluetooth_applet_dispose;
 
1070
        gobject_class->get_property = bluetooth_applet_get_property;
 
1071
        gobject_class->set_property = bluetooth_applet_set_property;
 
1072
 
 
1073
        /* should be enum, but KillswitchState is not registered */
 
1074
        properties[PROP_KILLSWITCH_STATE] = g_param_spec_int ("killswitch-state",
 
1075
                                                              "Killswitch state",
 
1076
                                                              "State of Bluetooth hardware switches",
 
1077
                                                              KILLSWITCH_STATE_NO_ADAPTER, KILLSWITCH_STATE_HARD_BLOCKED, KILLSWITCH_STATE_NO_ADAPTER,
 
1078
                                                              G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
 
1079
        g_object_class_install_property (gobject_class, PROP_KILLSWITCH_STATE, properties[PROP_KILLSWITCH_STATE]);
 
1080
 
 
1081
        properties[PROP_DISCOVERABLE] = g_param_spec_boolean ("discoverable",
 
1082
                                                              "Adapter visibility",
 
1083
                                                              "Whether the adapter is visible or not",
 
1084
                                                              FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
 
1085
        g_object_class_install_property (gobject_class, PROP_DISCOVERABLE, properties[PROP_DISCOVERABLE]);
 
1086
 
 
1087
        properties[PROP_FULL_MENU] = g_param_spec_boolean ("show-full-menu",
 
1088
                                                           "Show the full applet menu",
 
1089
                                                           "Show actions related to the adapter and other miscellanous in the main menu",
 
1090
                                                           TRUE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
 
1091
        g_object_class_install_property (gobject_class, PROP_FULL_MENU, properties[PROP_FULL_MENU]);
 
1092
 
 
1093
        signals[SIGNAL_DEVICES_CHANGED] = g_signal_new ("devices-changed", G_TYPE_FROM_CLASS (gobject_class),
 
1094
                                                        G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE, 0, NULL, NULL, g_cclosure_marshal_VOID__VOID,
 
1095
                                                        G_TYPE_NONE, 0);
 
1096
 
 
1097
        signals[SIGNAL_PINCODE_REQUEST] = g_signal_new ("pincode-request", G_TYPE_FROM_CLASS (gobject_class),
 
1098
                                                        G_SIGNAL_RUN_FIRST, 0, NULL, NULL, marshal_VOID__STRING_STRING_STRING_BOOLEAN,
 
1099
                                                        G_TYPE_NONE, 4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN);
 
1100
 
 
1101
        signals[SIGNAL_CONFIRM_REQUEST] = g_signal_new ("confirm-request", G_TYPE_FROM_CLASS (gobject_class),
 
1102
                                                        G_SIGNAL_RUN_FIRST, 0, NULL, NULL, marshal_VOID__STRING_STRING_STRING_UINT,
 
1103
                                                        G_TYPE_NONE, 4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_UINT);
 
1104
 
 
1105
        signals[SIGNAL_AUTHORIZE_REQUEST] = g_signal_new ("auth-request", G_TYPE_FROM_CLASS (gobject_class),
 
1106
                                                          G_SIGNAL_RUN_FIRST, 0, NULL, NULL, marshal_VOID__STRING_STRING_STRING_STRING,
 
1107
                                                          G_TYPE_NONE, 4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
 
1108
 
 
1109
        signals[SIGNAL_CANCEL_REQUEST] = g_signal_new ("cancel-request", G_TYPE_FROM_CLASS (gobject_class),
 
1110
                                                       G_SIGNAL_RUN_FIRST, 0, NULL, NULL, g_cclosure_marshal_VOID__VOID,
 
1111
                                                       G_TYPE_NONE, 0);
 
1112
}
 
1113