~ubuntu-branches/ubuntu/raring/forked-daapd/raring-proposed

« back to all changes in this revision

Viewing changes to src/httpd_dacp.c

  • Committer: Bazaar Package Importer
  • Author(s): Julien BLACHE
  • Date: 2011-04-09 11:41:55 UTC
  • mfrom: (1.1.6 upstream)
  • Revision ID: james.westby@ubuntu.com-20110409114155-5i6vbc4v0eqtvcsg
Tags: 0.15-1
* New upstream release.

* debian/control:
  + Add build-dependency on gperf.
  + Bump Standards-Version to 3.9.2 (no changes).
* debian/copyright:
  + Update list of copyright holders.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
/*
2
 
 * Copyright (C) 2010 Julien BLACHE <jb@jblache.org>
 
2
 * Copyright (C) 2010-2011 Julien BLACHE <jb@jblache.org>
3
3
 *
4
4
 * This program is free software; you can redistribute it and/or modify
5
5
 * it under the terms of the GNU General Public License as published by
39
39
 
40
40
#include <event.h>
41
41
#include "evhttp/evhttp.h"
42
 
#include <avl.h>
43
42
 
44
43
#include "logger.h"
45
44
#include "misc.h"
47
46
#include "artwork.h"
48
47
#include "httpd.h"
49
48
#include "httpd_dacp.h"
50
 
#include "dmap_helpers.h"
 
49
#include "dmap_common.h"
51
50
#include "db.h"
52
51
#include "player.h"
53
52
 
78
77
typedef void (*dacp_propset)(const char *value, struct evkeyvalq *query);
79
78
 
80
79
struct dacp_prop_map {
81
 
  uint32_t hash;
82
80
  char *desc;
83
81
  dacp_propget propget;
84
82
  dacp_propset propset;
117
115
static void
118
116
dacp_propset_userrating(const char *value, struct evkeyvalq *query);
119
117
 
120
 
static struct dacp_prop_map dacp_props[] =
121
 
  {
122
 
    { 0, "dmcp.volume",                 dacp_propget_volume,                 dacp_propset_volume },
123
 
    { 0, "dacp.playerstate",            dacp_propget_playerstate,            NULL },
124
 
    { 0, "dacp.nowplaying",             dacp_propget_nowplaying,             NULL },
125
 
    { 0, "dacp.playingtime",            dacp_propget_playingtime,            dacp_propset_playingtime },
126
 
    { 0, "dacp.volumecontrollable",     dacp_propget_volumecontrollable,     NULL },
127
 
    { 0, "dacp.availableshufflestates", dacp_propget_availableshufflestates, NULL },
128
 
    { 0, "dacp.availablerepeatstates",  dacp_propget_availablerepeatstates,  NULL },
129
 
    { 0, "dacp.shufflestate",           dacp_propget_shufflestate,           dacp_propset_shufflestate },
130
 
    { 0, "dacp.repeatstate",            dacp_propget_repeatstate,            dacp_propset_repeatstate },
131
 
    { 0, "dacp.userrating",             NULL,                                dacp_propset_userrating },
132
118
 
133
 
    { 0, NULL, NULL, NULL }
134
 
  };
 
119
/* gperf static hash, dacp_prop.gperf */
 
120
#include "dacp_prop_hash.c"
135
121
 
136
122
 
137
123
/* Play status update */
146
132
/* Play status update requests */
147
133
static struct dacp_update_request *update_requests;
148
134
 
149
 
/* Properties */
150
 
static avl_tree_t *dacp_props_hash;
151
 
 
152
135
/* Seek timer */
153
136
static struct event seek_timer;
154
137
static int seek_target;
394
377
}
395
378
 
396
379
 
397
 
/* Properties helpers */
398
 
static int
399
 
dacp_prop_map_compare(const void *aa, const void *bb)
400
 
{
401
 
  struct dacp_prop_map *a = (struct dacp_prop_map *)aa;
402
 
  struct dacp_prop_map *b = (struct dacp_prop_map *)bb;
403
 
 
404
 
  if (a->hash < b->hash)
405
 
    return -1;
406
 
 
407
 
  if (a->hash > b->hash)
408
 
    return 1;
409
 
 
410
 
  return 0;
411
 
}
412
 
 
413
 
static struct dacp_prop_map *
414
 
dacp_find_prop(uint32_t hash)
415
 
{
416
 
  struct dacp_prop_map dpm;
417
 
  avl_node_t *node;
418
 
 
419
 
  dpm.hash = hash;
420
 
 
421
 
  node = avl_search(dacp_props_hash, &dpm);
422
 
  if (!node)
423
 
    return NULL;
424
 
 
425
 
  return (struct dacp_prop_map *)node->item;
426
 
}
427
 
 
428
 
static void
429
 
parse_properties(struct evhttp_request *req, char *tag, const char *param, uint32_t **out_prop, int *out_nprop)
430
 
{
431
 
  char *ptr;
432
 
  char *prop;
433
 
  char *propstr;
434
 
  uint32_t *hashes;
435
 
  int nprop;
436
 
  int i;
437
 
 
438
 
  *out_nprop = -1;
439
 
 
440
 
  propstr = strdup(param);
441
 
  if (!propstr)
442
 
    {
443
 
      DPRINTF(E_LOG, L_DACP, "Could not duplicate properties parameter; out of memory\n");
444
 
 
445
 
      dmap_send_error(req, tag, "Out of memory");
446
 
      return;
447
 
    }
448
 
 
449
 
  nprop = 1;
450
 
  ptr = propstr;
451
 
  while ((ptr = strchr(ptr + 1, ',')))
452
 
    nprop++;
453
 
 
454
 
  DPRINTF(E_DBG, L_DACP, "Asking for %d properties\n", nprop);
455
 
 
456
 
  hashes = (uint32_t *)malloc((nprop + 1) * sizeof(uint32_t));
457
 
  if (!hashes)
458
 
    {
459
 
      DPRINTF(E_LOG, L_DACP, "Could not allocate properties array; out of memory\n");
460
 
 
461
 
      dmap_send_error(req, tag, "Out of memory");
462
 
 
463
 
      free(propstr);
464
 
      return;
465
 
    }
466
 
  memset(hashes, 0, (nprop + 1) * sizeof(uint32_t));
467
 
 
468
 
  prop = strtok_r(propstr, ",", &ptr);
469
 
  for (i = 0; i < nprop; i++)
470
 
    {
471
 
      hashes[i] = djb_hash(prop, strlen(prop));
472
 
 
473
 
      prop = strtok_r(NULL, ",", &ptr);
474
 
      if (!prop)
475
 
        break;
476
 
    }
477
 
 
478
 
  DPRINTF(E_DBG, L_DACP, "Found %d properties\n", nprop);
479
 
 
480
 
  *out_nprop = nprop;
481
 
  *out_prop = hashes;
482
 
 
483
 
  free(propstr);
484
 
}
485
 
 
486
380
/* Properties getters */
487
381
static void
488
382
dacp_propget_volume(struct evbuffer *evbuf, struct player_status *status, struct media_file_info *mfi)
1220
1114
  char clen[32];
1221
1115
  struct daap_session *s;
1222
1116
  const char *param;
 
1117
  char *ctype;
1223
1118
  uint32_t id;
1224
1119
  int max_w;
1225
1120
  int max_h;
1269
1164
  if (ret < 0)
1270
1165
    goto no_artwork;
1271
1166
 
1272
 
  ret = artwork_get_item(id, max_w, max_h, evbuf);
1273
 
  if (ret < 0)
 
1167
  ret = artwork_get_item(id, max_w, max_h, ART_CAN_PNG | ART_CAN_JPEG, evbuf);
 
1168
  switch (ret)
1274
1169
    {
1275
 
      if (EVBUFFER_LENGTH(evbuf) > 0)
1276
 
        evbuffer_drain(evbuf, EVBUFFER_LENGTH(evbuf));
1277
 
 
1278
 
      goto no_artwork;
 
1170
      case ART_FMT_PNG:
 
1171
        ctype = "image/png";
 
1172
        break;
 
1173
 
 
1174
      case ART_FMT_JPEG:
 
1175
        ctype = "image/jpeg";
 
1176
        break;
 
1177
 
 
1178
      default:
 
1179
        if (EVBUFFER_LENGTH(evbuf) > 0)
 
1180
          evbuffer_drain(evbuf, EVBUFFER_LENGTH(evbuf));
 
1181
 
 
1182
        goto no_artwork;
1279
1183
    }
1280
1184
 
1281
1185
  evhttp_remove_header(req->output_headers, "Content-Type");
1282
 
  evhttp_add_header(req->output_headers, "Content-Type", "image/png");
 
1186
  evhttp_add_header(req->output_headers, "Content-Type", ctype);
1283
1187
  snprintf(clen, sizeof(clen), "%ld", (long)EVBUFFER_LENGTH(evbuf));
1284
1188
  evhttp_add_header(req->output_headers, "Content-Length", clen);
1285
1189
 
1296
1200
{
1297
1201
  struct player_status status;
1298
1202
  struct daap_session *s;
1299
 
  struct dacp_prop_map *dpm;
 
1203
  const struct dacp_prop_map *dpm;
1300
1204
  struct media_file_info *mfi;
1301
1205
  struct evbuffer *proplist;
1302
1206
  const char *param;
1303
 
  uint32_t *prop;
1304
 
  int nprop;
1305
 
  int i;
 
1207
  char *ptr;
 
1208
  char *prop;
 
1209
  char *propstr;
1306
1210
  int ret;
1307
1211
 
1308
1212
  s = daap_session_find(req, query, evbuf);
1318
1222
      return;
1319
1223
    }
1320
1224
 
1321
 
  parse_properties(req, "cmgt", param, &prop, &nprop);
1322
 
  if (nprop < 0)
 
1225
  propstr = strdup(param);
 
1226
  if (!propstr)
1323
1227
    {
1324
 
      DPRINTF(E_LOG, L_DACP, "Failed to parse properties parameter in getproperty call\n");
 
1228
      DPRINTF(E_LOG, L_DACP, "Could not duplicate properties parameter; out of memory\n");
1325
1229
 
 
1230
      dmap_send_error(req, "cmgt", "Out of memory");
1326
1231
      return;
1327
1232
    }
1328
1233
 
1332
1237
      DPRINTF(E_LOG, L_DACP, "Could not allocate evbuffer for properties list\n");
1333
1238
 
1334
1239
      dmap_send_error(req, "cmgt", "Out of memory");
1335
 
      goto out_free_prop;
 
1240
      goto out_free_propstr;
1336
1241
    }
1337
1242
 
1338
1243
  player_get_status(&status);
1351
1256
  else
1352
1257
    mfi = NULL;
1353
1258
 
1354
 
  for (i = 0; i < nprop; i++)
 
1259
  prop = strtok_r(propstr, ",", &ptr);
 
1260
  while (prop)
1355
1261
    {
1356
 
      dpm = dacp_find_prop(prop[i]);
 
1262
      dpm = dacp_find_prop(prop, strlen(prop));
1357
1263
      if (!dpm)
1358
1264
        {
1359
 
          DPRINTF(E_LOG, L_DACP, "Could not find requested property (%d)\n", i + 1);
 
1265
          DPRINTF(E_LOG, L_DACP, "Could not find requested property '%s'\n", prop);
1360
1266
          continue;
1361
1267
        }
1362
1268
 
1363
1269
      if (dpm->propget)
1364
1270
        dpm->propget(proplist, &status, mfi);
1365
1271
      else
1366
 
        DPRINTF(E_WARN, L_DACP, "No getter method for DACP property %s\n", dpm->desc);
 
1272
        DPRINTF(E_WARN, L_DACP, "No getter method for DACP property %s\n", prop);
 
1273
 
 
1274
      prop = strtok_r(NULL, ",", &ptr);
1367
1275
    }
1368
1276
 
 
1277
  free(propstr);
 
1278
 
1369
1279
  if (mfi)
1370
1280
    free_mfi(mfi, 0);
1371
1281
 
1372
 
  if (nprop > 0)
1373
 
    free(prop);
1374
 
 
1375
1282
  dmap_add_container(evbuf, "cmgt", 12 + EVBUFFER_LENGTH(proplist)); /* 8 + len */
1376
1283
  dmap_add_int(evbuf, "mstt", 200);      /* 12 */
1377
1284
 
1392
1299
 out_free_proplist:
1393
1300
  evbuffer_free(proplist);
1394
1301
 
1395
 
 out_free_prop:
1396
 
  if (nprop > 0)
1397
 
    free(prop);
 
1302
 out_free_propstr:
 
1303
  free(propstr);
1398
1304
}
1399
1305
 
1400
1306
static void
1401
1307
dacp_reply_setproperty(struct evhttp_request *req, struct evbuffer *evbuf, char **uri, struct evkeyvalq *query)
1402
1308
{
1403
1309
  struct daap_session *s;
1404
 
  struct dacp_prop_map *dpm;
 
1310
  const struct dacp_prop_map *dpm;
1405
1311
  struct evkeyval *param;
1406
 
  uint32_t hash;
1407
1312
 
1408
1313
  s = daap_session_find(req, query, evbuf);
1409
1314
  if (!s)
1420
1325
 
1421
1326
  TAILQ_FOREACH(param, query, next)
1422
1327
    {
1423
 
      hash = djb_hash(param->key, strlen(param->key));
1424
 
 
1425
 
      dpm = dacp_find_prop(hash);
 
1328
      dpm = dacp_find_prop(param->key, strlen(param->key));
1426
1329
 
1427
1330
      if (!dpm)
1428
1331
        {
1441
1344
}
1442
1345
 
1443
1346
static void
1444
 
speaker_enum_cb(uint64_t id, const char *name, int relvol, int selected, int has_password, void *arg)
 
1347
speaker_enum_cb(uint64_t id, const char *name, int relvol, struct spk_flags flags, void *arg)
1445
1348
{
1446
1349
  struct evbuffer *evbuf;
1447
1350
  int len;
1449
1352
  evbuf = (struct evbuffer *)arg;
1450
1353
 
1451
1354
  len = 8 + strlen(name) + 28;
1452
 
  if (selected)
1453
 
    len += 9;
1454
 
  if (has_password)
 
1355
  if (flags.selected)
 
1356
    len += 9;
 
1357
  if (flags.has_password)
 
1358
    len += 9;
 
1359
  if (flags.has_video)
1455
1360
    len += 9;
1456
1361
 
1457
1362
  dmap_add_container(evbuf, "mdcl", len); /* 8 + len */
1458
 
  if (selected)
 
1363
  if (flags.selected)
1459
1364
    dmap_add_char(evbuf, "caia", 1);      /* 9 */
1460
 
  if (has_password)
 
1365
  if (flags.has_password)
1461
1366
    dmap_add_char(evbuf, "cahp", 1);      /* 9 */
 
1367
  if (flags.has_video)
 
1368
    dmap_add_char(evbuf, "caiv", 1);      /* 9 */
1462
1369
  dmap_add_string(evbuf, "minm", name);   /* 8 + len */
1463
1370
  dmap_add_long(evbuf, "msma", id);       /* 16 */
1464
1371
 
1785
1692
dacp_init(void)
1786
1693
{
1787
1694
  char buf[64];
1788
 
  avl_node_t *node;
1789
 
  struct dacp_prop_map *dpm;
1790
1695
  int i;
1791
1696
  int ret;
1792
1697
 
1827
1732
        }
1828
1733
    }
1829
1734
 
1830
 
  dacp_props_hash = avl_alloc_tree(dacp_prop_map_compare, NULL);
1831
 
  if (!dacp_props_hash)
1832
 
    {
1833
 
      DPRINTF(E_FATAL, L_DACP, "DACP init could not allocate DACP props AVL tree\n");
1834
 
 
1835
 
      goto dacp_avl_alloc_fail;
1836
 
    }
1837
 
 
1838
 
  for (i = 0; dacp_props[i].desc; i++)
1839
 
    {
1840
 
      dacp_props[i].hash = djb_hash(dacp_props[i].desc, strlen(dacp_props[i].desc));
1841
 
 
1842
 
      node = avl_insert(dacp_props_hash, &dacp_props[i]);
1843
 
      if (!node)
1844
 
        {
1845
 
          if (errno != EEXIST)
1846
 
            DPRINTF(E_FATAL, L_DACP, "DACP init failed; AVL insert error: %s\n", strerror(errno));
1847
 
          else
1848
 
            {
1849
 
              node = avl_search(dacp_props_hash, &dacp_props[i]);
1850
 
              dpm = node->item;
1851
 
 
1852
 
              DPRINTF(E_FATAL, L_DACP, "DACP init failed; WARNING: duplicate hash key\n");
1853
 
              DPRINTF(E_FATAL, L_DACP, "Hash %x, string %s\n", dacp_props[i].hash, dacp_props[i].desc);
1854
 
 
1855
 
              DPRINTF(E_FATAL, L_DACP, "Hash %x, string %s\n", dpm->hash, dpm->desc);
1856
 
            }
1857
 
 
1858
 
          goto dacp_avl_insert_fail;
1859
 
        }
1860
 
    }
1861
 
 
1862
1735
#ifdef USE_EVENTFD
1863
1736
  event_set(&updateev, update_efd, EV_READ, playstatusupdate_cb, NULL);
1864
1737
#else
1871
1744
 
1872
1745
  return 0;
1873
1746
 
1874
 
 dacp_avl_insert_fail:
1875
 
  avl_free_tree(dacp_props_hash);
1876
 
 dacp_avl_alloc_fail:
1877
 
  for (i = 0; dacp_handlers[i].handler; i++)
1878
 
    regfree(&dacp_handlers[i].preg);
1879
1747
 regexp_fail:
1880
1748
#ifdef USE_EVENTFD
1881
1749
  close(update_efd);
1912
1780
 
1913
1781
  event_del(&updateev);
1914
1782
 
1915
 
  avl_free_tree(dacp_props_hash);
1916
 
 
1917
1783
#ifdef USE_EVENTFD
1918
1784
  close(update_efd);
1919
1785
#else