~ubuntu-branches/ubuntu/quantal/libdmapsharing/quantal

« back to all changes in this revision

Viewing changes to libdmapsharing/dacp-share.c

  • Committer: Bazaar Package Importer
  • Author(s): Josselin Mouette
  • Date: 2011-03-29 19:52:57 UTC
  • mfrom: (0.1.2 experimental) (1.1.6 upstream)
  • Revision ID: james.westby@ubuntu.com-20110329195257-0zas0lq4c03gwo46
Tags: 2.9.7-1
Initial release. (Closes: #620060)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
2
 
 *
3
 
 * Implmentation of DACP (e.g., iTunes Remote) sharing
 
1
/* Implmentation of DACP (e.g., iTunes Remote) sharing
4
2
 *
5
3
 * Copyright (C) 2010 Alexandre Rosenfeld <airmind@gmail.com>
6
4
 *
47
45
#include <libdmapsharing/dacp-share.h>
48
46
#include <libdmapsharing/dacp-player.h>
49
47
 
50
 
static void dacp_share_set_property  (GObject *object,
51
 
                                         guint prop_id,
52
 
                                         const GValue *value,
53
 
                                         GParamSpec *pspec);
54
 
static void dacp_share_get_property  (GObject *object,
55
 
                                         guint prop_id,
56
 
                                         GValue *value,
57
 
                                         GParamSpec *pspec);
58
 
static void dacp_share_dispose  (GObject *object);
59
 
const char *dacp_share_get_type_of_service (DMAPShare *share);
60
 
void dacp_share_ctrl_int (DMAPShare *share,
61
 
                      SoupServer        *server,
62
 
                      SoupMessage       *message,
63
 
                      const char        *path,
64
 
                      GHashTable        *query,
65
 
                      SoupClientContext *context);
66
 
void dacp_share_login (DMAPShare *share,
67
 
                  SoupServer        *server,
68
 
                  SoupMessage       *message,
69
 
                  const char        *path,
70
 
                  GHashTable        *query,
71
 
                  SoupClientContext *context);
 
48
static void dacp_share_set_property (GObject * object,
 
49
                                     guint prop_id,
 
50
                                     const GValue * value,
 
51
                                     GParamSpec * pspec);
 
52
static void dacp_share_get_property (GObject * object,
 
53
                                     guint prop_id,
 
54
                                     GValue * value, GParamSpec * pspec);
 
55
static void dacp_share_dispose (GObject * object);
 
56
const char *dacp_share_get_type_of_service (DMAPShare * share);
 
57
void dacp_share_ctrl_int (DMAPShare * share,
 
58
                          SoupServer * server,
 
59
                          SoupMessage * message,
 
60
                          const char *path,
 
61
                          GHashTable * query, SoupClientContext * context);
 
62
void dacp_share_login (DMAPShare * share,
 
63
                       SoupServer * server,
 
64
                       SoupMessage * message,
 
65
                       const char *path,
 
66
                       GHashTable * query, SoupClientContext * context);
72
67
 
73
 
static gchar *dacp_share_pairing_code(DACPShare *share, gchar* pair_txt, gchar passcode[4]);
74
 
static void dacp_share_send_playstatusupdate (DACPShare *share);
75
 
static void dacp_share_fill_playstatusupdate (DACPShare *share, SoupMessage *message);
 
68
static gchar *dacp_share_pairing_code (DACPShare * share, gchar * pair_txt,
 
69
                                       gchar passcode[4]);
 
70
static void dacp_share_send_playstatusupdate (DACPShare * share);
 
71
static void dacp_share_fill_playstatusupdate (DACPShare * share,
 
72
                                              SoupMessage * message);
76
73
 
77
74
#define DACP_TYPE_OF_SERVICE "_touch-able._tcp"
78
75
#define DACP_PORT 3689
79
76
 
80
 
struct DACPSharePrivate {
 
77
struct DACPSharePrivate
 
78
{
81
79
        DMAPMdnsBrowser *mdns_browser;
82
80
 
83
81
        gchar *library_name;
93
91
/*
94
92
 * Internal representation of a DACP remote.
95
93
 */
96
 
typedef struct {
 
94
typedef struct
 
95
{
97
96
        gchar *host;
98
97
        guint port;
99
98
        gchar *pair_txt;
100
99
        DMAPConnection *connection;
101
100
} DACPRemoteInfo;
102
101
 
103
 
#define DACP_SHARE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), TYPE_DACP_SHARE, DACPSharePrivate))
 
102
#define DACP_SHARE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), DACP_TYPE_SHARE, DACPSharePrivate))
104
103
 
105
 
enum {
 
104
enum
 
105
{
106
106
        PROP_0,
107
107
        PROP_LIBRARY_NAME,
108
108
        PROP_PLAYER
109
109
};
110
110
 
111
 
enum {
 
111
enum
 
112
{
112
113
        REMOTE_FOUND,
113
114
        REMOTE_LOST,
114
115
        REMOTE_PAIRED,
119
120
        LAST_SIGNAL
120
121
};
121
122
 
122
 
static guint signals [LAST_SIGNAL] = { 0, };
 
123
static guint signals[LAST_SIGNAL] = { 0, };
123
124
 
124
 
G_DEFINE_TYPE (DACPShare, dacp_share, TYPE_DAAP_SHARE)
 
125
G_DEFINE_TYPE (DACPShare, dacp_share, DAAP_TYPE_SHARE);
125
126
 
126
127
static void
127
 
dacp_share_class_init (DACPShareClass *klass)
 
128
dacp_share_class_init (DACPShareClass * klass)
128
129
{
129
130
        GObjectClass *object_class = G_OBJECT_CLASS (klass);
130
131
        DMAPShareClass *dmap_class = DMAP_SHARE_CLASS (object_class);
133
134
        object_class->set_property = dacp_share_set_property;
134
135
        object_class->dispose = dacp_share_dispose;
135
136
 
136
 
        dmap_class->get_type_of_service  = dacp_share_get_type_of_service;
 
137
        dmap_class->get_type_of_service = dacp_share_get_type_of_service;
137
138
        dmap_class->ctrl_int = dacp_share_ctrl_int;
138
139
        dmap_class->login = dacp_share_login;
139
140
 
140
141
        g_object_class_install_property (object_class,
141
 
                                         PROP_LIBRARY_NAME,
142
 
                                         g_param_spec_string ("library-name",
143
 
                                                              "Library Name",
144
 
                                                              "Library name as will be shown in the Remote",
145
 
                                                              NULL,
146
 
                                                              G_PARAM_READWRITE));
 
142
                                         PROP_LIBRARY_NAME,
 
143
                                         g_param_spec_string ("library-name",
 
144
                                                              "Library Name",
 
145
                                                              "Library name as will be shown in the Remote",
 
146
                                                              NULL,
 
147
                                                              G_PARAM_READWRITE));
147
148
 
148
149
        g_object_class_install_property (object_class,
149
 
                                         PROP_PLAYER,
150
 
                                         g_param_spec_object ("player",
151
 
                                                              "Player",
152
 
                                                              "Player",
153
 
                                                              G_TYPE_OBJECT,
154
 
                                                              G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
155
 
        
 
150
                                         PROP_PLAYER,
 
151
                                         g_param_spec_object ("player",
 
152
                                                              "Player",
 
153
                                                              "Player",
 
154
                                                              G_TYPE_OBJECT,
 
155
                                                              G_PARAM_READWRITE
 
156
                                                              |
 
157
                                                              G_PARAM_CONSTRUCT_ONLY));
 
158
 
156
159
        /**
157
160
         * DACPShare::remote-found
158
161
         * @share: the #DACPShare that received the signal.
161
164
         *
162
165
         * Signal emited when a remote is found in the local network.
163
166
         */
164
 
        signals [REMOTE_FOUND] =
 
167
        signals[REMOTE_FOUND] =
165
168
                g_signal_new ("remote-found",
166
169
                              G_TYPE_FROM_CLASS (object_class),
167
170
                              G_SIGNAL_RUN_LAST,
169
172
                              NULL,
170
173
                              NULL,
171
174
                              dmap_marshal_VOID__STRING_STRING,
172
 
                              G_TYPE_NONE,
173
 
                              2, G_TYPE_STRING, G_TYPE_STRING);
174
 
                              
 
175
                              G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING);
 
176
 
175
177
        /**
176
178
         * DACPShare::remote-lost
177
179
         * @share: the #DACPShare that received the signal
179
181
         *
180
182
         * Signal emited when a remote is lost in the local network.
181
183
         */
182
 
        signals [REMOTE_LOST] =
 
184
        signals[REMOTE_LOST] =
183
185
                g_signal_new ("remote-lost",
184
186
                              G_TYPE_FROM_CLASS (object_class),
185
187
                              G_SIGNAL_RUN_LAST,
187
189
                              NULL,
188
190
                              NULL,
189
191
                              g_cclosure_marshal_VOID__STRING,
190
 
                              G_TYPE_NONE,
191
 
                              1, G_TYPE_STRING);
 
192
                              G_TYPE_NONE, 1, G_TYPE_STRING);
192
193
 
193
194
        /**
194
195
         * DACPShare::remote-paired
198
199
         *
199
200
         * Signal emited when a remote is paired.
200
201
         */
201
 
        signals [REMOTE_PAIRED] =
 
202
        signals[REMOTE_PAIRED] =
202
203
                g_signal_new ("remote-paired",
203
204
                              G_TYPE_FROM_CLASS (object_class),
204
205
                              G_SIGNAL_RUN_LAST,
206
207
                              NULL,
207
208
                              NULL,
208
209
                              dmap_marshal_VOID__STRING_BOOLEAN,
209
 
                              G_TYPE_NONE,
210
 
                              2, G_TYPE_STRING, G_TYPE_BOOLEAN);
 
210
                              G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_BOOLEAN);
211
211
 
212
212
        /**
213
213
         * DACPShare::lookup-guid
218
218
         * validated. An implementation must implement this signal to lookup
219
219
         * for guids saved by ::add-guid
220
220
         */
221
 
        signals [LOOKUP_GUID] =
 
221
        signals[LOOKUP_GUID] =
222
222
                g_signal_new ("lookup-guid",
223
223
                              G_TYPE_FROM_CLASS (object_class),
224
224
                              G_SIGNAL_RUN_LAST,
226
226
                              NULL,
227
227
                              NULL,
228
228
                              dmap_marshal_BOOLEAN__STRING,
229
 
                              G_TYPE_BOOLEAN,
230
 
                              1, G_TYPE_STRING);
 
229
                              G_TYPE_BOOLEAN, 1, G_TYPE_STRING);
231
230
 
232
231
        /**
233
232
         * DACPShare::add-guid
242
241
         * a button to forget previously connected remotes, so that the user may
243
242
         * disconnect all previously connected remotes.
244
243
         */
245
 
        signals [ADD_GUID] =
 
244
        signals[ADD_GUID] =
246
245
                g_signal_new ("add-guid",
247
246
                              G_TYPE_FROM_CLASS (object_class),
248
247
                              G_SIGNAL_RUN_LAST,
250
249
                              NULL,
251
250
                              NULL,
252
251
                              g_cclosure_marshal_VOID__STRING,
253
 
                              G_TYPE_NONE,
254
 
                              1, G_TYPE_STRING);
255
 
        
 
252
                              G_TYPE_NONE, 1, G_TYPE_STRING);
 
253
 
256
254
        g_type_class_add_private (klass, sizeof (DACPSharePrivate));
257
255
}
258
256
 
259
257
static void
260
 
dacp_share_init (DACPShare *share)
 
258
dacp_share_init (DACPShare * share)
261
259
{
262
260
        share->priv = DACP_SHARE_GET_PRIVATE (share);
263
261
 
264
262
        share->priv->current_revision = 2;
265
 
        
266
 
        share->priv->remotes = g_hash_table_new_full ((GHashFunc)g_str_hash,
267
 
                                                      (GEqualFunc)g_str_equal,
268
 
                                                      (GDestroyNotify)g_free,
269
 
                                                      (GDestroyNotify)g_free);
 
263
 
 
264
        share->priv->remotes = g_hash_table_new_full ((GHashFunc) g_str_hash,
 
265
                                                      (GEqualFunc)
 
266
                                                      g_str_equal,
 
267
                                                      (GDestroyNotify) g_free,
 
268
                                                      (GDestroyNotify)
 
269
                                                      g_free);
270
270
}
271
271
 
272
272
static gchar *
273
 
get_dbid (void) 
 
273
get_dbid (void)
274
274
{
275
275
        static gchar *dbid;
 
276
 
276
277
        if (!dbid) {
277
278
                GString *name;
 
279
 
278
280
                // Creates a service name 14 characters long concatenating the hostname
279
281
                // hash hex value with itself.
280
282
                // Idea taken from stereo.
281
283
                name = g_string_new (NULL);
282
 
                g_string_printf (name, "%.8x", g_str_hash(g_get_host_name ()));
 
284
                g_string_printf (name, "%.8x",
 
285
                                 g_str_hash (g_get_host_name ()));
283
286
                g_string_ascii_up (name);
284
287
                g_string_append_len (name, name->str, 4);
285
288
 
286
289
                dbid = name->str;
287
 
                
 
290
 
288
291
                g_string_free (name, FALSE);
289
292
        }
290
293
        return dbid;
291
294
}
292
295
 
293
296
static void
294
 
dacp_share_update_txt_records (DACPShare *share)
 
297
dacp_share_update_txt_records (DACPShare * share)
295
298
{
296
299
        gchar *dbid_record;
297
300
        gchar *library_name_record;
298
301
 
299
 
        library_name_record = g_strdup_printf ("CtlN=%s", share->priv->library_name);
300
 
        dbid_record = g_strdup_printf("DbId=%s", get_dbid());
301
 
        
302
 
        gchar *txt_records[] = {"Ver=131073", 
303
 
                                "DvSv=2049",
304
 
                                dbid_record,
305
 
                                "DvTy=iTunes",
306
 
                                "OSsi=0x1F6",
307
 
                                "txtvers=1",
308
 
                                library_name_record,
309
 
                                NULL};
 
302
        library_name_record =
 
303
                g_strdup_printf ("CtlN=%s", share->priv->library_name);
 
304
        dbid_record = g_strdup_printf ("DbId=%s", get_dbid ());
 
305
 
 
306
        gchar *txt_records[] = { "Ver=131073",
 
307
                "DvSv=2049",
 
308
                dbid_record,
 
309
                "DvTy=iTunes",
 
310
                "OSsi=0x1F6",
 
311
                "txtvers=1",
 
312
                library_name_record,
 
313
                NULL
 
314
        };
310
315
 
311
316
        g_object_set (share, "txt-records", txt_records, NULL);
312
317
 
315
320
}
316
321
 
317
322
static void
318
 
dacp_share_set_property (GObject *object,
319
 
                            guint prop_id,
320
 
                            const GValue *value,
321
 
                            GParamSpec *pspec)
 
323
dacp_share_set_property (GObject * object,
 
324
                         guint prop_id,
 
325
                         const GValue * value, GParamSpec * pspec)
322
326
{
323
327
        DACPShare *share = DACP_SHARE (object);
324
328
 
331
335
        case PROP_PLAYER:
332
336
                if (share->priv->player)
333
337
                        g_object_unref (share->priv->player);
334
 
                share->priv->player = DACP_PLAYER (g_value_dup_object (value));
 
338
                share->priv->player =
 
339
                        DACP_PLAYER (g_value_dup_object (value));
335
340
                break;
336
341
        default:
337
342
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
340
345
}
341
346
 
342
347
static void
343
 
dacp_share_get_property (GObject *object,
344
 
                            guint prop_id,
345
 
                            GValue *value,
346
 
                            GParamSpec *pspec)
 
348
dacp_share_get_property (GObject * object,
 
349
                         guint prop_id, GValue * value, GParamSpec * pspec)
347
350
{
348
351
        DACPShare *share = DACP_SHARE (object);
349
352
 
361
364
}
362
365
 
363
366
static void
364
 
dacp_share_dispose (GObject *object)
 
367
dacp_share_dispose (GObject * object)
365
368
{
366
369
        DACPShare *share = DACP_SHARE (object);
367
370
 
374
377
                g_object_unref (share->priv->player);
375
378
 
376
379
        g_slist_free (share->priv->update_queue);
377
 
        
 
380
 
378
381
        g_hash_table_destroy (share->priv->remotes);
379
382
}
380
383
 
381
 
void 
382
 
mdns_remote_added (DMAPMdnsBrowser *browser, 
383
 
                   DMAPMdnsBrowserService *service,
384
 
                   DACPShare *share) 
 
384
void
 
385
mdns_remote_added (DMAPMdnsBrowser * browser,
 
386
                   DMAPMdnsBrowserService * service, DACPShare * share)
385
387
{
386
388
        DACPRemoteInfo *remote_info;
387
 
       
 
389
 
388
390
        remote_info = g_new (DACPRemoteInfo, 1);
389
391
        remote_info->host = g_strdup (service->host);
390
392
        remote_info->port = service->port;
391
393
        remote_info->connection = NULL;
392
394
        remote_info->pair_txt = g_strdup (service->pair);
393
 
        
 
395
 
394
396
        g_debug ("New Remote found: %s name=%s host=%s port=%u pair=%s",
395
 
                 service->service_name,
396
 
                 service->name,
397
 
                 remote_info->host,
398
 
                 remote_info->port,
399
 
                 remote_info->pair_txt);
400
 
        
 
397
                 service->service_name,
 
398
                 service->name,
 
399
                 remote_info->host, remote_info->port, remote_info->pair_txt);
 
400
 
401
401
        g_hash_table_insert (share->priv->remotes,
402
 
                             service->service_name,
403
 
                             remote_info);
404
 
        
405
 
        g_signal_emit (share, 
406
 
                       signals [REMOTE_FOUND], 
407
 
                       0, 
408
 
                       service->service_name,
409
 
                       service->name);
410
 
}
 
402
                             service->service_name, remote_info);
411
403
 
412
 
void
413
 
mdns_remote_removed (DMAPMdnsBrowser *browser,
414
 
                     const char *service_name,
415
 
                     DACPShare *share)
416
 
{
417
404
        g_signal_emit (share,
418
 
                       signals [REMOTE_LOST],
419
 
                       0,
420
 
                       service_name);
421
 
                       
422
 
        g_hash_table_remove (share->priv->remotes,
423
 
                             service_name);
 
405
                       signals[REMOTE_FOUND],
 
406
                       0, service->service_name, service->name);
 
407
}
 
408
 
 
409
void
 
410
mdns_remote_removed (DMAPMdnsBrowser * browser,
 
411
                     const char *service_name, DACPShare * share)
 
412
{
 
413
        g_signal_emit (share, signals[REMOTE_LOST], 0, service_name);
 
414
 
 
415
        g_hash_table_remove (share->priv->remotes, service_name);
424
416
}
425
417
 
426
418
DACPShare *
427
 
dacp_share_new (const gchar *library_name,
428
 
                DACPPlayer *player,
429
 
                DMAPDb *db,
430
 
                DMAPContainerDb *container_db)
 
419
dacp_share_new (const gchar * library_name,
 
420
                DACPPlayer * player,
 
421
                DMAPDb * db, DMAPContainerDb * container_db)
431
422
{
432
423
        DACPShare *share;
433
 
        
 
424
 
434
425
        g_object_ref (db);
435
426
        g_object_ref (container_db);
436
 
        
437
 
        share = DACP_SHARE (g_object_new (TYPE_DACP_SHARE,
438
 
                                          "name", get_dbid (),
439
 
                                          "library-name", library_name,
440
 
                                          "password", NULL,
441
 
                                          "db", db,
442
 
                                          "container-db", container_db,
443
 
                                          "player", G_OBJECT (player),
444
 
                                          "transcode-mimetype", NULL,
445
 
                                          NULL));
446
 
        
447
 
        g_debug("Starting DACP server");
 
427
 
 
428
        share = DACP_SHARE (g_object_new (DACP_TYPE_SHARE,
 
429
                                          "name", get_dbid (),
 
430
                                          "library-name", library_name,
 
431
                                          "password", NULL,
 
432
                                          "db", db,
 
433
                                          "container-db", container_db,
 
434
                                          "player", G_OBJECT (player),
 
435
                                          "transcode-mimetype", NULL, NULL));
 
436
 
 
437
        g_debug ("Starting DACP server");
448
438
        _dmap_share_server_start (DMAP_SHARE (share));
449
439
        _dmap_share_publish_start (DMAP_SHARE (share));
450
440
 
452
442
}
453
443
 
454
444
void
455
 
dacp_share_start_lookup (DACPShare *share) 
 
445
dacp_share_start_lookup (DACPShare * share)
456
446
{
457
447
        GError *error;
458
 
        
 
448
 
459
449
        if (share->priv->mdns_browser) {
460
450
                g_warning ("DACP browsing already started");
461
451
                return;
462
452
        }
463
 
        
464
 
        share->priv->mdns_browser = dmap_mdns_browser_new (DMAP_MDNS_BROWSER_SERVICE_TYPE_DACP);
465
 
        
 
453
 
 
454
        share->priv->mdns_browser =
 
455
                dmap_mdns_browser_new (DMAP_MDNS_BROWSER_SERVICE_TYPE_DACP);
 
456
 
466
457
        g_signal_connect_object (share->priv->mdns_browser,
467
458
                                 "service-added",
468
 
                                 G_CALLBACK (mdns_remote_added),
469
 
                                 share,
470
 
                                 0);
 
459
                                 G_CALLBACK (mdns_remote_added), share, 0);
471
460
        g_signal_connect_object (share->priv->mdns_browser,
472
461
                                 "service-removed",
473
 
                                 G_CALLBACK (mdns_remote_removed),
474
 
                                 share,
475
 
                                 0);
476
 
        
 
462
                                 G_CALLBACK (mdns_remote_removed), share, 0);
 
463
 
477
464
        error = NULL;
478
465
        dmap_mdns_browser_start (share->priv->mdns_browser, &error);
479
466
        if (error != NULL) {
480
 
                g_warning ("Unable to start Remote lookup: %s", error->message);
 
467
                g_warning ("Unable to start Remote lookup: %s",
 
468
                           error->message);
481
469
                g_error_free (error);
482
470
        }
483
471
}
484
472
 
485
473
static gboolean
486
 
remove_remotes_cb (gpointer service_name, gpointer remote_info, gpointer share)
 
474
remove_remotes_cb (gpointer service_name, gpointer remote_info,
 
475
                   gpointer share)
487
476
{
488
 
        g_signal_emit ((DACPShare*) share,
489
 
                       signals [REMOTE_LOST],
490
 
                       0,
491
 
                       (gchar *) service_name);
 
477
        g_signal_emit ((DACPShare *) share,
 
478
                       signals[REMOTE_LOST], 0, (gchar *) service_name);
492
479
        return TRUE;
493
480
}
494
481
 
495
482
void
496
 
dacp_share_stop_lookup (DACPShare *share) 
 
483
dacp_share_stop_lookup (DACPShare * share)
497
484
{
498
485
        GError *error;
499
 
        
 
486
 
500
487
        if (!share->priv->mdns_browser) {
501
488
                g_warning ("DACP browsing not started");
502
489
                return;
503
490
        }
504
 
        
505
 
        g_hash_table_foreach_remove (share->priv->remotes, remove_remotes_cb, share);
506
 
        
 
491
 
 
492
        g_hash_table_foreach_remove (share->priv->remotes, remove_remotes_cb,
 
493
                                     share);
 
494
 
507
495
        error = NULL;
508
496
        dmap_mdns_browser_stop (share->priv->mdns_browser, &error);
509
497
        if (error != NULL) {
510
 
                g_warning ("Unable to stop Remote lookup: %s", error->message);
 
498
                g_warning ("Unable to stop Remote lookup: %s",
 
499
                           error->message);
511
500
                g_error_free (error);
512
501
        }
513
 
        
 
502
 
514
503
        share->priv->mdns_browser = NULL;
515
504
}
516
505
 
517
506
const char *
518
 
dacp_share_get_type_of_service (DMAPShare *share)
 
507
dacp_share_get_type_of_service (DMAPShare * share)
519
508
{
520
509
        return DACP_TYPE_OF_SERVICE;
521
510
}
522
511
 
523
512
void
524
 
dacp_share_player_updated (DACPShare *share)
 
513
dacp_share_player_updated (DACPShare * share)
525
514
{
526
515
        share->priv->current_revision++;
527
516
        dacp_share_send_playstatusupdate (share);
528
517
}
529
518
 
530
519
static void
531
 
status_update_message_finished (SoupMessage *message, DACPShare *share)
 
520
status_update_message_finished (SoupMessage * message, DACPShare * share)
532
521
{
533
 
        share->priv->update_queue = g_slist_remove (share->priv->update_queue, message);
 
522
        share->priv->update_queue =
 
523
                g_slist_remove (share->priv->update_queue, message);
534
524
        g_object_unref (message);
535
525
}
536
526
 
537
527
static void
538
 
dacp_share_send_playstatusupdate (DACPShare *share)
 
528
dacp_share_send_playstatusupdate (DACPShare * share)
539
529
{
540
530
        GSList *list;
541
 
        SoupServer *server;
542
 
        g_object_get (share, "server", &server, NULL);
543
 
        for (list = share->priv->update_queue; list; list = list->next) {
544
 
                dacp_share_fill_playstatusupdate (share, (SoupMessage*) list->data);
545
 
                soup_server_unpause_message (server, (SoupMessage*) list->data);
546
 
        }
 
531
        SoupServer *server = NULL;
 
532
 
 
533
        g_object_get (share, "server-ipv4", &server, NULL);
 
534
        if (server) {
 
535
                for (list = share->priv->update_queue; list;
 
536
                     list = list->next) {
 
537
                        dacp_share_fill_playstatusupdate (share,
 
538
                                                          (SoupMessage *)
 
539
                                                          list->data);
 
540
                        soup_server_unpause_message (server,
 
541
                                                     (SoupMessage *)
 
542
                                                     list->data);
 
543
                }
 
544
                g_object_unref (server);
 
545
        }
 
546
 
 
547
        server = NULL;
 
548
 
 
549
        g_object_get (share, "server-ipv6", &server, NULL);
 
550
        if (server) {
 
551
                for (list = share->priv->update_queue; list;
 
552
                     list = list->next) {
 
553
                        dacp_share_fill_playstatusupdate (share,
 
554
                                                          (SoupMessage *)
 
555
                                                          list->data);
 
556
                        soup_server_unpause_message (server,
 
557
                                                     (SoupMessage *)
 
558
                                                     list->data);
 
559
                }
 
560
                g_object_unref (server);
 
561
        }
 
562
 
547
563
        g_slist_free (share->priv->update_queue);
548
564
        share->priv->update_queue = NULL;
549
 
        g_object_unref (server);
550
565
}
551
566
 
552
567
static void
553
 
dacp_share_fill_playstatusupdate (DACPShare *share, SoupMessage *message)
 
568
dacp_share_fill_playstatusupdate (DACPShare * share, SoupMessage * message)
554
569
{
555
570
        GNode *cmst;
556
571
        DAAPRecord *record;
558
573
        DACPRepeatState repeat_state;
559
574
        gboolean shuffle_state;
560
575
        guint playing_time;
561
 
        
562
 
        g_object_get (share->priv->player, 
563
 
                      "play-state", &play_state,
564
 
                      "repeat-state", &repeat_state,
565
 
                      "shuffle-state", &shuffle_state,
566
 
                      "playing-time", &playing_time,
567
 
                      NULL);
 
576
 
 
577
        g_object_get (share->priv->player,
 
578
                      "play-state", &play_state,
 
579
                      "repeat-state", &repeat_state,
 
580
                      "shuffle-state", &shuffle_state,
 
581
                      "playing-time", &playing_time, NULL);
568
582
 
569
583
        record = dacp_player_now_playing_record (share->priv->player);
570
584
 
571
585
        cmst = dmap_structure_add (NULL, DMAP_CC_CMST);
572
586
        dmap_structure_add (cmst, DMAP_CC_MSTT, (gint32) DMAP_STATUS_OK);
573
 
        dmap_structure_add (cmst, DMAP_CC_CMSR, share->priv->current_revision);
 
587
        dmap_structure_add (cmst, DMAP_CC_CMSR,
 
588
                            share->priv->current_revision);
574
589
        dmap_structure_add (cmst, DMAP_CC_CAPS, (gint32) play_state);
575
590
        dmap_structure_add (cmst, DMAP_CC_CASH, shuffle_state ? 1 : 0);
576
591
        dmap_structure_add (cmst, DMAP_CC_CARP, (gint32) repeat_state);
580
595
                gchar *album;
581
596
                gint duration;
582
597
                guint track_time;
583
 
                g_object_get (record, 
584
 
                              "title", &title,
585
 
                              "songartist", &artist,
586
 
                              "songalbum", &album,
587
 
                              "duration", &duration,
588
 
                              NULL);
 
598
 
 
599
                g_object_get (record,
 
600
                              "title", &title,
 
601
                              "songartist", &artist,
 
602
                              "songalbum", &album,
 
603
                              "duration", &duration, NULL);
589
604
                track_time = duration * 1000;
590
605
                //dmap_structure_add (cmst, DMAP_CC_CAVC, 1);
591
606
                dmap_structure_add (cmst, DMAP_CC_CAAS, 2);
600
615
                dmap_structure_add (cmst, DMAP_CC_CANG, "");
601
616
                dmap_structure_add (cmst, DMAP_CC_ASAI, 0);
602
617
                //dmap_structure_add (cmst, DMAP_CC_AEMK, 1);
603
 
                g_debug ("Playing time: %u, Track time: %u", playing_time, track_time);
604
 
                dmap_structure_add (cmst, DMAP_CC_CANT, track_time - playing_time);
 
618
                g_debug ("Playing time: %u, Track time: %u", playing_time,
 
619
                         track_time);
 
620
                dmap_structure_add (cmst, DMAP_CC_CANT,
 
621
                                    track_time - playing_time);
605
622
                dmap_structure_add (cmst, DMAP_CC_CAST, track_time);
606
623
 
607
624
                g_free (title);
610
627
 
611
628
                g_object_unref (record);
612
629
        }
613
 
        
614
 
        _dmap_share_message_set_from_dmap_structure (DMAP_SHARE (share), message, cmst);
 
630
 
 
631
        _dmap_share_message_set_from_dmap_structure (DMAP_SHARE (share),
 
632
                                                     message, cmst);
615
633
        dmap_structure_destroy (cmst);
616
634
}
617
635
 
618
636
static void
619
637
debug_param (gpointer key, gpointer val, gpointer user_data)
620
638
{
621
 
        g_debug ("%s %s", (char *) key, (char *) val);
 
639
        g_debug ("%s %s", (char *) key, (char *) val);
622
640
}
623
641
 
624
642
void
625
 
dacp_share_login (DMAPShare *share,
626
 
          SoupServer        *server,
627
 
          SoupMessage       *message,
628
 
          const char        *path,
629
 
          GHashTable        *query,
630
 
          SoupClientContext *context)
 
643
dacp_share_login (DMAPShare * share,
 
644
                  SoupServer * server,
 
645
                  SoupMessage * message,
 
646
                  const char *path,
 
647
                  GHashTable * query, SoupClientContext * context)
631
648
{
632
649
        gchar *pairing_guid;
633
 
        
634
 
        
 
650
 
635
651
        g_debug ("(DACP) Path is %s.", path);
636
652
        if (query) {
637
653
                g_hash_table_foreach (query, debug_param, NULL);
642
658
        if (pairing_guid != NULL) {
643
659
                gboolean allow_login;
644
660
 
645
 
                g_signal_emit (share, signals [LOOKUP_GUID], 0, pairing_guid, &allow_login);
 
661
                g_signal_emit (share, signals[LOOKUP_GUID], 0, pairing_guid,
 
662
                               &allow_login);
646
663
 
647
664
                if (!allow_login) {
648
665
                        g_debug ("Unknown remote trying to connect");
649
 
                        soup_message_set_status (message, SOUP_STATUS_FORBIDDEN);
 
666
                        soup_message_set_status (message,
 
667
                                                 SOUP_STATUS_FORBIDDEN);
650
668
                        return;
651
669
                }
652
670
        }
653
 
        
 
671
 
654
672
        _dmap_share_login (share, server, message, path, query, context);
655
673
}
656
674
 
657
675
void
658
 
dacp_share_ctrl_int (DMAPShare *share,
659
 
                      SoupServer        *server,
660
 
                      SoupMessage       *message,
661
 
                      const char        *path,
662
 
                      GHashTable        *query,
663
 
                      SoupClientContext *context)
 
676
dacp_share_ctrl_int (DMAPShare * share,
 
677
                     SoupServer * server,
 
678
                     SoupMessage * message,
 
679
                     const char *path,
 
680
                     GHashTable * query, SoupClientContext * context)
664
681
{
665
682
        const char *rest_of_path;
666
683
 
667
684
        DACPShare *dacp_share = DACP_SHARE (share);
668
 
        
 
685
 
669
686
        g_debug ("Path is %s.", path);
670
687
        if (query) {
671
688
                g_hash_table_foreach (query, debug_param, NULL);
672
689
        }
673
 
                
 
690
 
674
691
        rest_of_path = strchr (path + 1, '/');
675
692
 
676
693
        /* If calling /ctrl-int without args, the client doesnt need a 
677
 
           session-id, otherwise it does and it should be validated. */
678
 
        if ((rest_of_path != NULL) && (! _dmap_share_session_id_validate (share, context, message, query, NULL))) {
 
694
         * session-id, otherwise it does and it should be validated. */
 
695
        if ((rest_of_path != NULL)
 
696
            &&
 
697
            (!_dmap_share_session_id_validate
 
698
             (share, context, message, query, NULL))) {
679
699
                soup_message_set_status (message, SOUP_STATUS_FORBIDDEN);
680
700
                return;
681
701
        }
682
702
 
683
703
        if (rest_of_path == NULL) {
684
 
        /* CACI control-int
685
 
         *      MSTT status
686
 
         *      MUTY update type
687
 
         *      MTCO specified total count
688
 
         *      MRCO returned count
689
 
         *      MLCL listing
690
 
         *              MLIT listing item
691
 
         *                      MIID item id
692
 
         *                      CMIK Unknown (TRUE)
693
 
         *                      CMSP Unknown (TRUE)
694
 
         *                      CMSV Unknown (TRUE)
695
 
         *                      CASS Unknown (TRUE)
696
 
         *                      CASU Unknown (TRUE)
697
 
         *                      CASG Unknown (TRUE)
698
 
         */
699
 
        
 
704
                /* CACI control-int
 
705
                 *      MSTT status
 
706
                 *      MUTY update type
 
707
                 *      MTCO specified total count
 
708
                 *      MRCO returned count
 
709
                 *      MLCL listing
 
710
                 *              MLIT listing item
 
711
                 *                      MIID item id
 
712
                 *                      CMIK Unknown (TRUE)
 
713
                 *                      CMSP Unknown (TRUE)
 
714
                 *                      CMSV Unknown (TRUE)
 
715
                 *                      CASS Unknown (TRUE)
 
716
                 *                      CASU Unknown (TRUE)
 
717
                 *                      CASG Unknown (TRUE)
 
718
                 */
 
719
 
700
720
                GNode *caci;
701
721
                GNode *mlcl;
702
722
                GNode *mlit;
703
 
        
 
723
 
704
724
                // dacp.controlint
705
725
                caci = dmap_structure_add (NULL, DMAP_CC_CACI);
706
726
                // dmap.status
707
 
                dmap_structure_add (caci, DMAP_CC_MSTT, (gint32) DMAP_STATUS_OK);
 
727
                dmap_structure_add (caci, DMAP_CC_MSTT,
 
728
                                    (gint32) DMAP_STATUS_OK);
708
729
                // dmap.updatetype
709
730
                dmap_structure_add (caci, DMAP_CC_MUTY, 0);
710
731
                // dmap.specifiedtotalcount
730
751
                // Unknown (TRUE)
731
752
                dmap_structure_add (mlit, DMAP_CC_CASG, (gint32) 1);
732
753
 
733
 
                _dmap_share_message_set_from_dmap_structure (share, message, caci);
 
754
                _dmap_share_message_set_from_dmap_structure (share, message,
 
755
                                                             caci);
734
756
                dmap_structure_destroy (caci);
735
757
        } else if (g_ascii_strcasecmp ("/1/getproperty", rest_of_path) == 0) {
736
758
                gchar *properties_query, **properties, **property;
737
759
                GNode *cmgt;
738
 
                
 
760
 
739
761
                properties_query = g_hash_table_lookup (query, "properties");
740
 
                
 
762
 
741
763
                if (!properties_query) {
742
764
                        g_warning ("No property specified");
743
765
                        return;
745
767
 
746
768
                cmgt = dmap_structure_add (NULL, DMAP_CC_CMGT);
747
769
                dmap_structure_add (cmgt, DMAP_CC_MSTT, DMAP_STATUS_OK);
748
 
                
 
770
 
749
771
                properties = g_strsplit (properties_query, ",", -1);
750
772
                for (property = properties; *property; property++) {
751
 
                        if (g_ascii_strcasecmp (*property, "dmcp.volume") == 0) {
 
773
                        if (g_ascii_strcasecmp (*property, "dmcp.volume") ==
 
774
                            0) {
752
775
                                gulong volume;
753
 
                                g_object_get (dacp_share->priv->player, "volume", &volume, NULL);
 
776
 
 
777
                                g_object_get (dacp_share->priv->player,
 
778
                                              "volume", &volume, NULL);
754
779
                                //g_debug ("Sending volume: %lu", volume);
755
 
                                dmap_structure_add (cmgt, DMAP_CC_CMVO, volume);
 
780
                                dmap_structure_add (cmgt, DMAP_CC_CMVO,
 
781
                                                    volume);
756
782
                        } else {
757
 
                                g_warning ("Unhandled property %s", *property);
 
783
                                g_warning ("Unhandled property %s",
 
784
                                           *property);
758
785
                        }
759
786
                }
760
787
 
761
788
                g_strfreev (properties);
762
789
 
763
 
                _dmap_share_message_set_from_dmap_structure (share, message, cmgt);
 
790
                _dmap_share_message_set_from_dmap_structure (share, message,
 
791
                                                             cmgt);
764
792
                dmap_structure_destroy (cmgt);
765
793
        } else if (g_ascii_strcasecmp ("/1/setproperty", rest_of_path) == 0) {
766
794
                if (g_hash_table_lookup (query, "dmcp.volume")) {
767
 
                        gdouble volume = strtod (g_hash_table_lookup (query, "dmcp.volume"), NULL);
768
 
                        g_object_set (dacp_share->priv->player, "volume", (gulong) volume, NULL);
 
795
                        gdouble volume =
 
796
                                strtod (g_hash_table_lookup
 
797
                                        (query, "dmcp.volume"), NULL);
 
798
                        g_object_set (dacp_share->priv->player, "volume",
 
799
                                      (gulong) volume, NULL);
769
800
                }
770
801
                soup_message_set_status (message, SOUP_STATUS_NO_CONTENT);
771
802
        } else if (g_ascii_strcasecmp ("/1/getspeakers", rest_of_path) == 0) {
772
803
                GNode *casp;
773
804
                GNode *mdcl;
774
 
                
 
805
 
775
806
                casp = dmap_structure_add (NULL, DMAP_CC_CASP);
776
 
                dmap_structure_add (casp, DMAP_CC_MSTT, (gint32) DMAP_STATUS_OK);
 
807
                dmap_structure_add (casp, DMAP_CC_MSTT,
 
808
                                    (gint32) DMAP_STATUS_OK);
777
809
                mdcl = dmap_structure_add (casp, DMAP_CC_MDCL);
778
 
                
 
810
 
779
811
                dmap_structure_add (casp, DMAP_CC_CAIA, TRUE);
780
812
                dmap_structure_add (casp, DMAP_CC_MINM, "Computer");
781
813
                dmap_structure_add (casp, DMAP_CC_MSMA, (gint32) 0);
782
 
                
783
 
                _dmap_share_message_set_from_dmap_structure (share, message, casp);
 
814
 
 
815
                _dmap_share_message_set_from_dmap_structure (share, message,
 
816
                                                             casp);
784
817
                dmap_structure_destroy (casp);
785
 
        } else if (g_ascii_strcasecmp ("/1/playstatusupdate", rest_of_path) == 0) {
786
 
                gchar *revision = g_hash_table_lookup (query, "revision-number");
 
818
        } else if (g_ascii_strcasecmp ("/1/playstatusupdate", rest_of_path) ==
 
819
                   0) {
 
820
                gchar *revision =
 
821
                        g_hash_table_lookup (query, "revision-number");
787
822
                gint revision_number = atoi (revision);
788
823
 
789
824
                if (revision_number >= dacp_share->priv->current_revision) {
790
825
                        g_object_ref (message);
791
 
                        dacp_share->priv->update_queue = g_slist_prepend (dacp_share->priv->update_queue, message);
792
 
                        g_signal_connect_object (message, 
793
 
                                                 "finished", 
794
 
                                                 G_CALLBACK (status_update_message_finished), 
795
 
                                                 dacp_share, 0);
 
826
                        dacp_share->priv->update_queue =
 
827
                                g_slist_prepend (dacp_share->
 
828
                                                 priv->update_queue, message);
 
829
                        g_signal_connect_object (message, "finished",
 
830
                                                 G_CALLBACK
 
831
                                                 (status_update_message_finished),
 
832
                                                 dacp_share, 0);
796
833
                        soup_server_pause_message (server, message);
797
834
                } else {
798
 
                        dacp_share_fill_playstatusupdate (dacp_share, message);
 
835
                        dacp_share_fill_playstatusupdate (dacp_share,
 
836
                                                          message);
799
837
                }
800
838
        } else if (g_ascii_strcasecmp ("/1/playpause", rest_of_path) == 0) {
801
839
                dacp_player_play_pause (dacp_share->priv->player);
809
847
        } else if (g_ascii_strcasecmp ("/1/previtem", rest_of_path) == 0) {
810
848
                dacp_player_prev_item (dacp_share->priv->player);
811
849
                soup_message_set_status (message, SOUP_STATUS_NO_CONTENT);
812
 
        } else if (g_ascii_strcasecmp ("/1/nowplayingartwork", rest_of_path) == 0) {
 
850
        } else if (g_ascii_strcasecmp ("/1/nowplayingartwork", rest_of_path)
 
851
                   == 0) {
813
852
                guint width = 320;
814
853
                guint height = 320;
815
854
                gchar *artwork_filename;
816
855
                gchar *buffer;
817
856
                gsize buffer_len;
818
 
                
 
857
 
819
858
                if (g_hash_table_lookup (query, "mw"))
820
859
                        width = atoi (g_hash_table_lookup (query, "mw"));
821
860
                if (g_hash_table_lookup (query, "mh"))
822
861
                        height = atoi (g_hash_table_lookup (query, "mh"));
823
 
                artwork_filename = dacp_player_now_playing_artwork (dacp_share->priv->player, width, height);
 
862
                artwork_filename =
 
863
                        dacp_player_now_playing_artwork (dacp_share->
 
864
                                                         priv->player, width,
 
865
                                                         height);
824
866
                if (!artwork_filename) {
825
867
                        g_debug ("No artwork for currently playing song");
826
 
                        soup_message_set_status (message, SOUP_STATUS_NOT_FOUND);
 
868
                        soup_message_set_status (message,
 
869
                                                 SOUP_STATUS_NOT_FOUND);
827
870
                        return;
828
871
                }
829
872
#ifdef HAVE_GDKPIXBUF
830
 
                GdkPixbuf *artwork = gdk_pixbuf_new_from_file_at_scale (artwork_filename, width, height, TRUE, NULL);
 
873
                GdkPixbuf *artwork =
 
874
                        gdk_pixbuf_new_from_file_at_scale (artwork_filename,
 
875
                                                           width, height,
 
876
                                                           TRUE, NULL);
 
877
 
831
878
                if (!artwork) {
832
879
                        g_debug ("Error loading image file");
833
880
                        g_free (artwork_filename);
834
 
                        soup_message_set_status (message, SOUP_STATUS_INTERNAL_SERVER_ERROR);
 
881
                        soup_message_set_status (message,
 
882
                                                 SOUP_STATUS_INTERNAL_SERVER_ERROR);
835
883
                        return;
836
884
                }
837
 
                if (!gdk_pixbuf_save_to_buffer (artwork, &buffer, &buffer_len, "png", NULL, NULL)) {
 
885
                if (!gdk_pixbuf_save_to_buffer
 
886
                    (artwork, &buffer, &buffer_len, "png", NULL, NULL)) {
838
887
                        g_debug ("Error saving artwork to PNG");
839
888
                        g_object_unref (artwork);
840
889
                        g_free (artwork_filename);
841
 
                        soup_message_set_status (message, SOUP_STATUS_INTERNAL_SERVER_ERROR);
 
890
                        soup_message_set_status (message,
 
891
                                                 SOUP_STATUS_INTERNAL_SERVER_ERROR);
842
892
                        return;
843
893
                }
844
894
                g_object_unref (artwork);
845
895
#else
846
 
                if (!g_file_get_contents (artwork_filename, &buffer, &buffer_len, NULL)) {
 
896
                if (!g_file_get_contents
 
897
                    (artwork_filename, &buffer, &buffer_len, NULL)) {
847
898
                        g_debug ("Error getting artwork data");
848
899
                        g_free (artwork_filename);
849
 
                        soup_message_set_status (message, SOUP_STATUS_INTERNAL_SERVER_ERROR);
 
900
                        soup_message_set_status (message,
 
901
                                                 SOUP_STATUS_INTERNAL_SERVER_ERROR);
850
902
                        return;
851
903
                }
852
904
#endif
853
905
                g_free (artwork_filename);
854
906
                soup_message_set_status (message, SOUP_STATUS_OK);
855
 
                soup_message_set_response (message, "image/png", SOUP_MEMORY_TAKE, buffer, buffer_len);
 
907
                soup_message_set_response (message, "image/png",
 
908
                                           SOUP_MEMORY_TAKE, buffer,
 
909
                                           buffer_len);
856
910
        } else if (g_ascii_strcasecmp ("/1/cue", rest_of_path) == 0) {
857
911
                gchar *command;
858
 
                
 
912
 
859
913
                command = g_hash_table_lookup (query, "command");
860
914
 
861
915
                if (!command) {
862
916
                        g_debug ("No CUE command specified");
863
 
                        soup_message_set_status (message, SOUP_STATUS_NO_CONTENT);
 
917
                        soup_message_set_status (message,
 
918
                                                 SOUP_STATUS_NO_CONTENT);
864
919
                        return;
865
920
                } else if (g_ascii_strcasecmp ("clear", command) == 0) {
866
921
                        dacp_player_cue_clear (dacp_share->priv->player);
867
 
                        soup_message_set_status (message, SOUP_STATUS_NO_CONTENT);
 
922
                        soup_message_set_status (message,
 
923
                                                 SOUP_STATUS_NO_CONTENT);
868
924
                } else if (g_ascii_strcasecmp ("play", command) == 0) {
869
925
                        GNode *cacr;
870
926
                        gchar *record_query;
873
929
                        GList *sorted_records;
874
930
                        GSList *filter_def;
875
931
                        DMAPDb *db;
876
 
                        gint index = atoi (g_hash_table_lookup (query, "index"));
 
932
                        gint index =
 
933
                                atoi (g_hash_table_lookup (query, "index"));
877
934
 
878
935
                        g_object_get (share, "db", &db, NULL);
879
936
                        record_query = g_hash_table_lookup (query, "query");
882
939
                        sorted_records = g_hash_table_get_values (records);
883
940
                        sort_by = g_hash_table_lookup (query, "sort");
884
941
                        if (g_strcmp0 (sort_by, "album") == 0) {
885
 
                                sorted_records = g_list_sort_with_data (sorted_records, (GCompareDataFunc) daap_record_cmp_by_album, db);
 
942
                                sorted_records =
 
943
                                        g_list_sort_with_data (sorted_records,
 
944
                                                               (GCompareDataFunc)
 
945
                                                               daap_record_cmp_by_album,
 
946
                                                               db);
886
947
                        } else if (sort_by != NULL) {
887
 
                                g_warning ("Unknown sort column: %s", sort_by);
 
948
                                g_warning ("Unknown sort column: %s",
 
949
                                           sort_by);
888
950
                        }
889
 
                        
890
 
                        dacp_player_cue_play (dacp_share->priv->player, sorted_records, index);
 
951
 
 
952
                        dacp_player_cue_play (dacp_share->priv->player,
 
953
                                              sorted_records, index);
891
954
 
892
955
                        g_list_free (sorted_records);
893
956
                        g_hash_table_unref (records);
894
957
                        dmap_share_free_filter (filter_def);
895
958
 
896
959
                        cacr = dmap_structure_add (NULL, DMAP_CC_CACR);
897
 
                        dmap_structure_add (cacr, DMAP_CC_MSTT, DMAP_STATUS_OK);
 
960
                        dmap_structure_add (cacr, DMAP_CC_MSTT,
 
961
                                            DMAP_STATUS_OK);
898
962
                        dmap_structure_add (cacr, DMAP_CC_MIID, index);
899
963
 
900
 
                        _dmap_share_message_set_from_dmap_structure (share, message, cacr);
 
964
                        _dmap_share_message_set_from_dmap_structure (share,
 
965
                                                                     message,
 
966
                                                                     cacr);
901
967
                        dmap_structure_destroy (cacr);
902
968
                } else {
903
969
                        g_warning ("Unhandled cue command: %s", command);
904
 
                        soup_message_set_status (message, SOUP_STATUS_NO_CONTENT);
 
970
                        soup_message_set_status (message,
 
971
                                                 SOUP_STATUS_NO_CONTENT);
905
972
                        return;
906
973
                }
907
974
        } else {
914
981
#define PASSCODE_LENGTH 4
915
982
 
916
983
static gchar *
917
 
dacp_share_pairing_code(DACPShare *share, gchar* pair_txt, gchar passcode[4]) {
 
984
dacp_share_pairing_code (DACPShare * share, gchar * pair_txt,
 
985
                         gchar passcode[4])
 
986
{
918
987
        int i;
919
988
        GString *pairing_code;
920
989
        gchar *pairing_string;
921
990
        gchar *ret;
922
 
        
 
991
 
923
992
        /* The pairing code is the MD5 sum of the concatenation of pair_txt
924
 
           with the passcode, but the passcode takes 16-bits unicodes characters */
925
 
        pairing_string = g_strnfill(PAIR_TXT_LENGTH + PASSCODE_LENGTH * 2, '\0');
926
 
        g_strlcpy(pairing_string, pair_txt, PAIR_TXT_LENGTH + PASSCODE_LENGTH * 2);
 
993
         * with the passcode, but the passcode takes 16-bits unicodes characters */
 
994
        pairing_string =
 
995
                g_strnfill (PAIR_TXT_LENGTH + PASSCODE_LENGTH * 2, '\0');
 
996
        g_strlcpy (pairing_string, pair_txt,
 
997
                   PAIR_TXT_LENGTH + PASSCODE_LENGTH * 2);
927
998
        for (i = 0; i < 4; i++) {
928
999
                pairing_string[PAIR_TXT_LENGTH + i * 2] = passcode[i];
929
1000
        }
930
 
        
931
 
        pairing_code = g_string_new (
932
 
                g_compute_checksum_for_data(G_CHECKSUM_MD5, 
933
 
                                            (guchar*)pairing_string, 
934
 
                                            PAIR_TXT_LENGTH + PASSCODE_LENGTH * 2));
 
1001
 
 
1002
        pairing_code =
 
1003
                g_string_new (g_compute_checksum_for_data
 
1004
                              (G_CHECKSUM_MD5, (guchar *) pairing_string,
 
1005
                               PAIR_TXT_LENGTH + PASSCODE_LENGTH * 2));
935
1006
        g_string_ascii_up (pairing_code);
936
1007
        ret = pairing_code->str;
937
1008
        g_string_free (pairing_code, FALSE);
938
 
        
 
1009
 
939
1010
        return ret;
940
1011
}
941
1012
 
942
1013
void
943
 
connection_handler_cb (DMAPConnection *connection, guint status, GNode *structure, gpointer user_data) 
 
1014
connection_handler_cb (DMAPConnection * connection, guint status,
 
1015
                       GNode * structure, gpointer user_data)
944
1016
{
945
1017
        gboolean connected;
946
1018
        GHashTableIter iter;
957
1029
        } else {
958
1030
                connected = FALSE;
959
1031
        }
960
 
        
 
1032
 
961
1033
        /* Get the pairing-guid to identify this remote in the future. */
962
1034
        if (structure)
963
1035
                item = dmap_structure_find_item (structure, DMAP_CC_CMPG);
964
1036
        if (item) {
965
1037
                guint64 guid = g_value_get_int64 (&(item->content));
966
 
                pairing_guid = g_strdup_printf ("0x%.16" G_GINT64_MODIFIER "X", guid);
967
 
                g_signal_emit (share, signals [ADD_GUID], 0, pairing_guid);
 
1038
 
 
1039
                pairing_guid =
 
1040
                        g_strdup_printf ("0x%.16" G_GINT64_MODIFIER "X",
 
1041
                                         guid);
 
1042
                g_signal_emit (share, signals[ADD_GUID], 0, pairing_guid);
968
1043
                g_free (pairing_guid);
969
1044
        }
970
1045
 
971
1046
        /* Find the remote that initiated this connection */
972
1047
        g_hash_table_iter_init (&iter, share->priv->remotes);
973
 
        while (g_hash_table_iter_next (&iter, &key, &value)) 
974
 
        {
975
 
                if (((DACPRemoteInfo*) value)->connection == connection) {
 
1048
        while (g_hash_table_iter_next (&iter, &key, &value)) {
 
1049
                if (((DACPRemoteInfo *) value)->connection == connection) {
976
1050
                        service_name = (gchar *) key;
977
 
                        remote_info = (DACPRemoteInfo*) value;
 
1051
                        remote_info = (DACPRemoteInfo *) value;
978
1052
                        break;
979
1053
                }
980
1054
        }
985
1059
        }
986
1060
 
987
1061
        /* Frees the connection */
988
 
        remote_info->connection = NULL; 
 
1062
        remote_info->connection = NULL;
989
1063
        g_object_unref (connection);
990
1064
 
991
1065
        /* FIXME: Send more detailed error info, such as wrong pair code, etc */
992
 
        g_signal_emit (share, signals [REMOTE_PAIRED], 0, service_name, connected);
 
1066
        g_signal_emit (share, signals[REMOTE_PAIRED], 0, service_name,
 
1067
                       connected);
993
1068
}
994
1069
 
995
1070
void
996
 
dacp_share_pair (DACPShare *share, gchar *service_name, gchar passcode[4]) 
 
1071
dacp_share_pair (DACPShare * share, gchar * service_name, gchar passcode[4])
997
1072
{
998
1073
        gchar *pairing_code;
999
1074
        gchar *name;
1000
1075
        gchar *path;
1001
1076
        DACPRemoteInfo *remote_info;
1002
 
        
 
1077
 
1003
1078
        remote_info = g_hash_table_lookup (share->priv->remotes,
1004
 
                                           service_name);
1005
 
                                           
 
1079
                                           service_name);
 
1080
 
1006
1081
        if (remote_info == NULL) {
1007
1082
                g_warning ("Remote %s not found.", service_name);
1008
1083
                return;
1012
1087
                g_warning ("Already pairing remote %s.", service_name);
1013
1088
                return;
1014
1089
        }
1015
 
        
 
1090
 
1016
1091
        g_object_get (share, "name", &name, NULL);
1017
 
        
1018
 
        remote_info->connection = dmap_connection_new (name, 
1019
 
                                                       remote_info->host, 
1020
 
                                                       remote_info->port, 
1021
 
                                                       FALSE, 
1022
 
                                                       NULL, 
1023
 
                                                       NULL);
 
1092
 
 
1093
        remote_info->connection = dacp_connection_new (name,
 
1094
                                                       remote_info->host,
 
1095
                                                       remote_info->port,
 
1096
                                                       FALSE, NULL, NULL);
1024
1097
        /* This is required since we don't call DMAPConnection default handler */
1025
1098
        dmap_connection_setup (remote_info->connection);
1026
 
        
 
1099
 
1027
1100
        /* Get the remote path for pairing */
1028
 
        pairing_code = dacp_share_pairing_code (share, remote_info->pair_txt, passcode);
1029
 
        path = g_strdup_printf ("/pair?pairingcode=%s&servicename=%s", 
1030
 
                                pairing_code,
1031
 
                                name);
 
1101
        pairing_code =
 
1102
                dacp_share_pairing_code (share, remote_info->pair_txt,
 
1103
                                         passcode);
 
1104
        path = g_strdup_printf ("/pair?pairingcode=%s&servicename=%s",
 
1105
                                pairing_code, name);
1032
1106
        g_free (pairing_code);
1033
 
        
1034
 
        g_debug ("Pairing remote in %s:%d/%s", remote_info->host, remote_info->port, path);
 
1107
 
 
1108
        g_debug ("Pairing remote in %s:%d/%s", remote_info->host,
 
1109
                 remote_info->port, path);
1035
1110
 
1036
1111
        /* Let DMAPConnection do the heavy work */
1037
 
        dmap_connection_get (remote_info->connection, path, FALSE, connection_handler_cb, share);
 
1112
        dmap_connection_get (remote_info->connection, path, FALSE,
 
1113
                             connection_handler_cb, share);
1038
1114
 
1039
1115
        g_free (path);
1040
1116
}