mirror of
https://github.com/owntone/owntone-server.git
synced 2024-12-27 15:45:56 -05:00
[raop] Add support for Apple TV device verification, required by tvOS 10.2 (fix for issue #377)
- also change how speakers are saved/retrieved from the db - add generic authorization methods in outputs.c and player.c - let filescanner read *.verification files (containing PIN) - configure options to enable and disable, since libsodium is required
This commit is contained in:
parent
736979a9a2
commit
f63d103753
31
configure.ac
31
configure.ac
@ -273,6 +273,14 @@ dnl Build with libcurl
|
|||||||
FORK_ARG_WITH_CHECK([FORKED_OPTS], [libcurl support], [libcurl], [LIBCURL],
|
FORK_ARG_WITH_CHECK([FORKED_OPTS], [libcurl support], [libcurl], [LIBCURL],
|
||||||
[libcurl], [curl_global_init], [curl/curl.h])
|
[libcurl], [curl_global_init], [curl/curl.h])
|
||||||
|
|
||||||
|
dnl Build with libsodium
|
||||||
|
FORK_ARG_WITH_CHECK([FORKED_OPTS], [libsodium support], [libsodium], [LIBSODIUM],
|
||||||
|
[libsodium], [sodium_init], [sodium.h])
|
||||||
|
|
||||||
|
dnl Build with libplist
|
||||||
|
FORK_ARG_WITH_CHECK([FORKED_OPTS], [libplist support], [libplist], [LIBPLIST],
|
||||||
|
[libplist >= 0.16], [plist_dict_get_item], [plist/plist.h])
|
||||||
|
|
||||||
dnl Build with libevent_pthreads
|
dnl Build with libevent_pthreads
|
||||||
FORK_ARG_WITH_CHECK([FORKED_OPTS], [libevent_pthreads support],
|
FORK_ARG_WITH_CHECK([FORKED_OPTS], [libevent_pthreads support],
|
||||||
[libevent_pthreads], [LIBEVENT_PTHREADS], [libevent_pthreads],
|
[libevent_pthreads], [LIBEVENT_PTHREADS], [libevent_pthreads],
|
||||||
@ -300,12 +308,6 @@ AS_IF([[test "x$with_avahi" = "xno"]],
|
|||||||
[AC_MSG_ERROR([[Avahi client or Bonjour DNS_SD required, please install one.]])])])
|
[AC_MSG_ERROR([[Avahi client or Bonjour DNS_SD required, please install one.]])])])
|
||||||
AM_CONDITIONAL([COND_AVAHI], [[test "x$with_avahi" = "xyes"]])
|
AM_CONDITIONAL([COND_AVAHI], [[test "x$with_avahi" = "xyes"]])
|
||||||
|
|
||||||
dnl iTunes playlists with libplist
|
|
||||||
FORK_ARG_ENABLE([iTunes Music Library XML support], [itunes], [ITUNES],
|
|
||||||
[FORK_MODULES_CHECK([FORKED_OPTS], [LIBPLIST], [libplist >= 0.16],
|
|
||||||
[plist_dict_get_item], [plist/plist.h])])
|
|
||||||
AM_CONDITIONAL([COND_ITUNES], [[test "x$enable_itunes" = "xyes"]])
|
|
||||||
|
|
||||||
dnl Spotify with dynamic linking to libspotify
|
dnl Spotify with dynamic linking to libspotify
|
||||||
FORK_ARG_ENABLE([Spotify support], [spotify], [SPOTIFY],
|
FORK_ARG_ENABLE([Spotify support], [spotify], [SPOTIFY],
|
||||||
[AS_IF([[test "x$with_json" = "xno"]],
|
[AS_IF([[test "x$with_json" = "xno"]],
|
||||||
@ -352,10 +354,27 @@ FORK_ARG_ENABLE([Chromecast support], [chromecast], [CHROMECAST],
|
|||||||
AM_CONDITIONAL([COND_CHROMECAST], [[test "x$enable_chromecast" = "xyes"]])
|
AM_CONDITIONAL([COND_CHROMECAST], [[test "x$enable_chromecast" = "xyes"]])
|
||||||
AM_CONDITIONAL([COND_PROTOBUF_OLD], [[test "x$protobuf_old" = "xyes"]])
|
AM_CONDITIONAL([COND_PROTOBUF_OLD], [[test "x$protobuf_old" = "xyes"]])
|
||||||
|
|
||||||
|
dnl iTunes playlists with libplist
|
||||||
|
FORK_ARG_DISABLE([iTunes Music Library XML support], [itunes], [ITUNES],
|
||||||
|
[AS_IF([[test "x$with_libplist" = "xno"]],
|
||||||
|
[AC_MSG_ERROR([[iTunes Music Library XML support requires libplist]])])
|
||||||
|
])
|
||||||
|
AM_CONDITIONAL([COND_ITUNES], [[test "x$enable_itunes" = "xyes"]])
|
||||||
|
|
||||||
dnl MPD support
|
dnl MPD support
|
||||||
FORK_ARG_DISABLE([MPD client protocol support], [mpd], [MPD])
|
FORK_ARG_DISABLE([MPD client protocol support], [mpd], [MPD])
|
||||||
AM_CONDITIONAL([COND_MPD], [[test "x$enable_mpd" = "xyes"]])
|
AM_CONDITIONAL([COND_MPD], [[test "x$enable_mpd" = "xyes"]])
|
||||||
|
|
||||||
|
dnl Apple device verification
|
||||||
|
FORK_ARG_DISABLE([Apple TV device verification], [verification], [RAOP_VERIFICATION],
|
||||||
|
[
|
||||||
|
AS_IF([[test "x$with_libsodium" = "xno"]],
|
||||||
|
[AC_MSG_ERROR([[Apple TV device verification requires libsodium]])])
|
||||||
|
AS_IF([[test "x$with_libplist" = "xno"]],
|
||||||
|
[AC_MSG_ERROR([[Apple TV device verification requires libplist]])])
|
||||||
|
])
|
||||||
|
AM_CONDITIONAL([COND_RAOP_VERIFICATION], [[test "x$enable_verification" = "xyes"]])
|
||||||
|
|
||||||
dnl Defining users and groups
|
dnl Defining users and groups
|
||||||
AC_ARG_WITH([daapd_user],
|
AC_ARG_WITH([daapd_user],
|
||||||
[AS_HELP_STRING([--with-daapd-user=USER],
|
[AS_HELP_STRING([--with-daapd-user=USER],
|
||||||
|
@ -25,6 +25,10 @@ if COND_MPD
|
|||||||
MPD_SRC=mpd.c mpd.h
|
MPD_SRC=mpd.c mpd.h
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
if COND_RAOP_VERIFICATION
|
||||||
|
RAOP_VERIFICATION_SRC=outputs/raop_verification.c outputs/raop_verification.h
|
||||||
|
endif
|
||||||
|
|
||||||
if COND_ALSA
|
if COND_ALSA
|
||||||
ALSA_SRC=outputs/alsa.c
|
ALSA_SRC=outputs/alsa.c
|
||||||
endif
|
endif
|
||||||
@ -112,7 +116,8 @@ forked_daapd_SOURCES = main.c \
|
|||||||
input.h input.c \
|
input.h input.c \
|
||||||
inputs/file_http.c inputs/pipe.c \
|
inputs/file_http.c inputs/pipe.c \
|
||||||
outputs.h outputs.c \
|
outputs.h outputs.c \
|
||||||
outputs/raop.c outputs/streaming.c outputs/dummy.c outputs/fifo.c \
|
outputs/raop.c $(RAOP_VERIFICATION_SRC) \
|
||||||
|
outputs/streaming.c outputs/dummy.c outputs/fifo.c \
|
||||||
$(ALSA_SRC) $(PULSEAUDIO_SRC) $(CHROMECAST_SRC) \
|
$(ALSA_SRC) $(PULSEAUDIO_SRC) $(CHROMECAST_SRC) \
|
||||||
evrtsp/rtsp.c evrtsp/evrtsp.h evrtsp/rtsp-internal.h evrtsp/log.h \
|
evrtsp/rtsp.c evrtsp/evrtsp.h evrtsp/rtsp-internal.h evrtsp/log.h \
|
||||||
$(SPOTIFY_SRC) \
|
$(SPOTIFY_SRC) \
|
||||||
|
92
src/db.c
92
src/db.c
@ -3824,21 +3824,21 @@ db_admin_delete(const char *key)
|
|||||||
|
|
||||||
/* Speakers */
|
/* Speakers */
|
||||||
int
|
int
|
||||||
db_speaker_save(uint64_t id, int selected, int volume, const char *name)
|
db_speaker_save(struct output_device *device)
|
||||||
{
|
{
|
||||||
#define Q_TMPL "INSERT OR REPLACE INTO speakers (id, selected, volume, name) VALUES (%" PRIi64 ", %d, %d, '%q');"
|
#define Q_TMPL "INSERT OR REPLACE INTO speakers (id, selected, volume, name, auth_key) VALUES (%" PRIi64 ", %d, %d, %Q, %Q);"
|
||||||
char *query;
|
char *query;
|
||||||
|
|
||||||
query = sqlite3_mprintf(Q_TMPL, id, selected, volume, name);
|
query = sqlite3_mprintf(Q_TMPL, device->id, device->selected, device->volume, device->name, device->auth_key);
|
||||||
|
|
||||||
return db_query_run(query, 1, 0);
|
return db_query_run(query, 1, 0);
|
||||||
#undef Q_TMPL
|
#undef Q_TMPL
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
db_speaker_get(uint64_t id, int *selected, int *volume)
|
db_speaker_get(struct output_device *device, uint64_t id)
|
||||||
{
|
{
|
||||||
#define Q_TMPL "SELECT s.selected, s.volume FROM speakers s WHERE s.id = %" PRIi64 ";"
|
#define Q_TMPL "SELECT s.selected, s.volume, s.name, s.auth_key FROM speakers s WHERE s.id = %" PRIi64 ";"
|
||||||
sqlite3_stmt *stmt;
|
sqlite3_stmt *stmt;
|
||||||
char *query;
|
char *query;
|
||||||
int ret;
|
int ret;
|
||||||
@ -3847,7 +3847,6 @@ db_speaker_get(uint64_t id, int *selected, int *volume)
|
|||||||
if (!query)
|
if (!query)
|
||||||
{
|
{
|
||||||
DPRINTF(E_LOG, L_DB, "Out of memory for query string\n");
|
DPRINTF(E_LOG, L_DB, "Out of memory for query string\n");
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3857,7 +3856,6 @@ db_speaker_get(uint64_t id, int *selected, int *volume)
|
|||||||
if (ret != SQLITE_OK)
|
if (ret != SQLITE_OK)
|
||||||
{
|
{
|
||||||
DPRINTF(E_LOG, L_DB, "Could not prepare statement: %s\n", sqlite3_errmsg(hdl));
|
DPRINTF(E_LOG, L_DB, "Could not prepare statement: %s\n", sqlite3_errmsg(hdl));
|
||||||
|
|
||||||
ret = -1;
|
ret = -1;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
@ -3867,15 +3865,20 @@ db_speaker_get(uint64_t id, int *selected, int *volume)
|
|||||||
{
|
{
|
||||||
if (ret != SQLITE_DONE)
|
if (ret != SQLITE_DONE)
|
||||||
DPRINTF(E_LOG, L_DB, "Could not step: %s\n", sqlite3_errmsg(hdl));
|
DPRINTF(E_LOG, L_DB, "Could not step: %s\n", sqlite3_errmsg(hdl));
|
||||||
|
|
||||||
sqlite3_finalize(stmt);
|
sqlite3_finalize(stmt);
|
||||||
|
|
||||||
ret = -1;
|
ret = -1;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
*selected = sqlite3_column_int(stmt, 0);
|
device->id = id;
|
||||||
*volume = sqlite3_column_int(stmt, 1);
|
device->selected = sqlite3_column_int(stmt, 0);
|
||||||
|
device->volume = sqlite3_column_int(stmt, 1);
|
||||||
|
|
||||||
|
free(device->name);
|
||||||
|
device->name = safe_strdup((char *)sqlite3_column_text(stmt, 2));
|
||||||
|
|
||||||
|
free(device->auth_key);
|
||||||
|
device->auth_key = safe_strdup((char *)sqlite3_column_text(stmt, 3));
|
||||||
|
|
||||||
#ifdef DB_PROFILE
|
#ifdef DB_PROFILE
|
||||||
while (db_blocking_step(stmt) == SQLITE_ROW)
|
while (db_blocking_step(stmt) == SQLITE_ROW)
|
||||||
@ -3893,73 +3896,6 @@ db_speaker_get(uint64_t id, int *selected, int *volume)
|
|||||||
#undef Q_TMPL
|
#undef Q_TMPL
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
|
||||||
db_speaker_auth_save(uint64_t id, const char *authkey)
|
|
||||||
{
|
|
||||||
#define Q_TMPL "UPDATE speakers SET authkey = '%q' WHERE id = %" PRIi64 ";"
|
|
||||||
char *query;
|
|
||||||
|
|
||||||
query = sqlite3_mprintf(Q_TMPL, authkey, id);
|
|
||||||
|
|
||||||
return db_query_run(query, 1, 0);
|
|
||||||
#undef Q_TMPL
|
|
||||||
}
|
|
||||||
|
|
||||||
char *
|
|
||||||
db_speaker_auth_get(uint64_t id)
|
|
||||||
{
|
|
||||||
#define Q_TMPL "SELECT authkey FROM speakers WHERE id = %" PRIi64 ";"
|
|
||||||
sqlite3_stmt *stmt;
|
|
||||||
char *query;
|
|
||||||
char *out;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
out = NULL;
|
|
||||||
|
|
||||||
query = sqlite3_mprintf(Q_TMPL, id);
|
|
||||||
if (!query)
|
|
||||||
{
|
|
||||||
DPRINTF(E_LOG, L_DB, "Out of memory for query string\n");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
DPRINTF(E_DBG, L_DB, "Running query '%s'\n", query);
|
|
||||||
|
|
||||||
ret = db_blocking_prepare_v2(query, -1, &stmt, NULL);
|
|
||||||
if (ret != SQLITE_OK)
|
|
||||||
{
|
|
||||||
DPRINTF(E_LOG, L_DB, "Could not prepare statement: %s\n", sqlite3_errmsg(hdl));
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = db_blocking_step(stmt);
|
|
||||||
if (ret != SQLITE_ROW)
|
|
||||||
{
|
|
||||||
if (ret != SQLITE_DONE)
|
|
||||||
DPRINTF(E_LOG, L_DB, "Could not step: %s\n", sqlite3_errmsg(hdl));
|
|
||||||
|
|
||||||
sqlite3_finalize(stmt);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
out = (char *)sqlite3_column_text(stmt, 0);
|
|
||||||
if (out)
|
|
||||||
out = strdup(out);
|
|
||||||
|
|
||||||
#ifdef DB_PROFILE
|
|
||||||
while (db_blocking_step(stmt) == SQLITE_ROW)
|
|
||||||
; /* EMPTY */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
sqlite3_finalize(stmt);
|
|
||||||
|
|
||||||
out:
|
|
||||||
sqlite3_free(query);
|
|
||||||
return out;
|
|
||||||
|
|
||||||
#undef Q_TMPL
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
db_speaker_clear_all(void)
|
db_speaker_clear_all(void)
|
||||||
{
|
{
|
||||||
|
13
src/db.h
13
src/db.h
@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
#include <sqlite3.h>
|
#include <sqlite3.h>
|
||||||
|
|
||||||
|
#include "outputs.h"
|
||||||
|
|
||||||
enum index_type {
|
enum index_type {
|
||||||
I_NONE,
|
I_NONE,
|
||||||
@ -675,18 +676,12 @@ db_admin_get(const char *key);
|
|||||||
int
|
int
|
||||||
db_admin_delete(const char *key);
|
db_admin_delete(const char *key);
|
||||||
|
|
||||||
/* Speakers */
|
/* Speakers/outputs */
|
||||||
int
|
int
|
||||||
db_speaker_save(uint64_t id, int selected, int volume, const char *name);
|
db_speaker_save(struct output_device *device);
|
||||||
|
|
||||||
int
|
int
|
||||||
db_speaker_get(uint64_t id, int *selected, int *volume);
|
db_speaker_get(struct output_device *device, uint64_t id);
|
||||||
|
|
||||||
int
|
|
||||||
db_speaker_auth_save(uint64_t id, const char *authkey);
|
|
||||||
|
|
||||||
char *
|
|
||||||
db_speaker_auth_get(uint64_t id);
|
|
||||||
|
|
||||||
void
|
void
|
||||||
db_speaker_clear_all(void);
|
db_speaker_clear_all(void);
|
||||||
|
@ -141,7 +141,7 @@
|
|||||||
" selected INTEGER NOT NULL," \
|
" selected INTEGER NOT NULL," \
|
||||||
" volume INTEGER NOT NULL," \
|
" volume INTEGER NOT NULL," \
|
||||||
" name VARCHAR(255) DEFAULT NULL," \
|
" name VARCHAR(255) DEFAULT NULL," \
|
||||||
" authkey VARCHAR(2048) DEFAULT NULL" \
|
" auth_key VARCHAR(2048) DEFAULT NULL" \
|
||||||
");"
|
");"
|
||||||
|
|
||||||
#define T_INOTIFY \
|
#define T_INOTIFY \
|
||||||
|
@ -1548,7 +1548,7 @@ static const struct db_upgrade_query db_upgrade_v1903_queries[] =
|
|||||||
|
|
||||||
|
|
||||||
#define U_V1904_ALTER_SPEAKERS_ADD_AUTHKEY \
|
#define U_V1904_ALTER_SPEAKERS_ADD_AUTHKEY \
|
||||||
"ALTER TABLE speakers ADD COLUMN authkey VARCHAR(2048) DEFAULT NULL;"
|
"ALTER TABLE speakers ADD COLUMN auth_key VARCHAR(2048) DEFAULT NULL;"
|
||||||
|
|
||||||
#define U_V1904_SCVER_MAJOR \
|
#define U_V1904_SCVER_MAJOR \
|
||||||
"UPDATE admin SET value = '19' WHERE key = 'schema_version_major';"
|
"UPDATE admin SET value = '19' WHERE key = 'schema_version_major';"
|
||||||
@ -1557,7 +1557,7 @@ static const struct db_upgrade_query db_upgrade_v1903_queries[] =
|
|||||||
|
|
||||||
static const struct db_upgrade_query db_upgrade_v1904_queries[] =
|
static const struct db_upgrade_query db_upgrade_v1904_queries[] =
|
||||||
{
|
{
|
||||||
{ U_V1904_ALTER_SPEAKERS_ADD_AUTHKEY, "alter table speakers add column authkey" },
|
{ U_V1904_ALTER_SPEAKERS_ADD_AUTHKEY, "alter table speakers add column auth_key" },
|
||||||
|
|
||||||
{ U_V1904_SCVER_MAJOR, "set schema_version_major to 19" },
|
{ U_V1904_SCVER_MAJOR, "set schema_version_major to 19" },
|
||||||
{ U_V1904_SCVER_MINOR, "set schema_version_minor to 04" },
|
{ U_V1904_SCVER_MINOR, "set schema_version_minor to 04" },
|
||||||
|
@ -551,8 +551,8 @@ process_file(char *file, struct stat *sb, int type, int flags, int dir_id)
|
|||||||
case FILE_CTRL_RAOP_VERIFICATION:
|
case FILE_CTRL_RAOP_VERIFICATION:
|
||||||
if (flags & F_SCAN_BULK)
|
if (flags & F_SCAN_BULK)
|
||||||
DPRINTF(E_LOG, L_SCAN, "Bulk scan will ignore '%s' (to process, add it after startup)\n", file);
|
DPRINTF(E_LOG, L_SCAN, "Bulk scan will ignore '%s' (to process, add it after startup)\n", file);
|
||||||
// else
|
else
|
||||||
// kickoff(player_raop_verification_kickoff, file, 1);
|
kickoff(player_raop_verification_kickoff, file, 1);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case FILE_CTRL_LASTFM:
|
case FILE_CTRL_LASTFM:
|
||||||
|
@ -606,6 +606,9 @@ main(int argc, char **argv)
|
|||||||
#ifdef MPD
|
#ifdef MPD
|
||||||
strcat(buildopts, " --enable-mpd");
|
strcat(buildopts, " --enable-mpd");
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef RAOP_VERIFICATION
|
||||||
|
strcat(buildopts, " --enable-verification");
|
||||||
|
#endif
|
||||||
#ifdef HAVE_ALSA
|
#ifdef HAVE_ALSA
|
||||||
strcat(buildopts, " --with-alsa");
|
strcat(buildopts, " --with-alsa");
|
||||||
#endif
|
#endif
|
||||||
|
@ -100,24 +100,23 @@ outputs_device_probe(struct output_device *device, output_status_cb cb)
|
|||||||
void
|
void
|
||||||
outputs_device_free(struct output_device *device)
|
outputs_device_free(struct output_device *device)
|
||||||
{
|
{
|
||||||
|
if (!device)
|
||||||
|
return;
|
||||||
|
|
||||||
if (outputs[device->type]->disabled)
|
if (outputs[device->type]->disabled)
|
||||||
DPRINTF(E_LOG, L_PLAYER, "BUG! Freeing device from a disabled output?\n");
|
DPRINTF(E_LOG, L_PLAYER, "BUG! Freeing device from a disabled output?\n");
|
||||||
|
|
||||||
|
if (device->session)
|
||||||
|
DPRINTF(E_LOG, L_PLAYER, "BUG! Freeing device with active session?\n");
|
||||||
|
|
||||||
if (outputs[device->type]->device_free_extra)
|
if (outputs[device->type]->device_free_extra)
|
||||||
outputs[device->type]->device_free_extra(device);
|
outputs[device->type]->device_free_extra(device);
|
||||||
|
|
||||||
if (device->name)
|
|
||||||
free(device->name);
|
free(device->name);
|
||||||
|
free(device->auth_key);
|
||||||
if (device->v4_address)
|
|
||||||
free(device->v4_address);
|
free(device->v4_address);
|
||||||
|
|
||||||
if (device->v6_address)
|
|
||||||
free(device->v6_address);
|
free(device->v6_address);
|
||||||
|
|
||||||
if (device->session)
|
|
||||||
DPRINTF(E_LOG, L_PLAYER, "BUG! Freeing device with active session?\n");
|
|
||||||
|
|
||||||
free(device);
|
free(device);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -313,6 +312,16 @@ outputs_metadata_free(struct output_metadata *omd)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
outputs_authorize(enum output_types type, const char *pin)
|
||||||
|
{
|
||||||
|
if (outputs[type]->disabled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (outputs[type]->authorize)
|
||||||
|
outputs[type]->authorize(pin);
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
outputs_priority(struct output_device *device)
|
outputs_priority(struct output_device *device)
|
||||||
{
|
{
|
||||||
|
@ -102,9 +102,11 @@ struct output_device
|
|||||||
unsigned advertised:1;
|
unsigned advertised:1;
|
||||||
unsigned has_password:1;
|
unsigned has_password:1;
|
||||||
unsigned has_video:1;
|
unsigned has_video:1;
|
||||||
|
unsigned requires_auth:1;
|
||||||
|
|
||||||
// Password if relevant
|
// Credentials if relevant
|
||||||
const char *password;
|
const char *password;
|
||||||
|
char *auth_key;
|
||||||
|
|
||||||
// Device volume
|
// Device volume
|
||||||
int volume;
|
int volume;
|
||||||
@ -187,6 +189,9 @@ struct output_definition
|
|||||||
// Flush all sessions, the return must be number of sessions pending the flush
|
// Flush all sessions, the return must be number of sessions pending the flush
|
||||||
int (*flush)(output_status_cb cb, uint64_t rtptime);
|
int (*flush)(output_status_cb cb, uint64_t rtptime);
|
||||||
|
|
||||||
|
// Authorize an output with a pin-code (probably coming from the filescanner)
|
||||||
|
void (*authorize)(const char *pin);
|
||||||
|
|
||||||
// Change the call back associated with a session
|
// Change the call back associated with a session
|
||||||
void (*status_cb)(struct output_session *session, output_status_cb cb);
|
void (*status_cb)(struct output_session *session, output_status_cb cb);
|
||||||
|
|
||||||
@ -242,6 +247,9 @@ outputs_metadata_prune(uint64_t rtptime);
|
|||||||
void
|
void
|
||||||
outputs_metadata_free(struct output_metadata *omd);
|
outputs_metadata_free(struct output_metadata *omd);
|
||||||
|
|
||||||
|
void
|
||||||
|
outputs_authorize(enum output_types type, const char *pin);
|
||||||
|
|
||||||
int
|
int
|
||||||
outputs_priority(struct output_device *device);
|
outputs_priority(struct output_device *device);
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
1344
src/outputs/raop_verification.c
Normal file
1344
src/outputs/raop_verification.c
Normal file
File diff suppressed because it is too large
Load Diff
66
src/outputs/raop_verification.h
Normal file
66
src/outputs/raop_verification.h
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
#ifndef __VERIFICATION_H__
|
||||||
|
#define __VERIFICATION_H__
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
struct verification_setup_context;
|
||||||
|
struct verification_verify_context;
|
||||||
|
|
||||||
|
/* When you have the pin-code (must be 4 bytes), create a new context with this
|
||||||
|
* function and then call verification_setup_request1()
|
||||||
|
*/
|
||||||
|
struct verification_setup_context *
|
||||||
|
verification_setup_new(const char *pin);
|
||||||
|
void
|
||||||
|
verification_setup_free(struct verification_setup_context *sctx);
|
||||||
|
|
||||||
|
/* Returns last error message
|
||||||
|
*/
|
||||||
|
const char *
|
||||||
|
verification_setup_errmsg(struct verification_setup_context *sctx);
|
||||||
|
|
||||||
|
uint8_t *
|
||||||
|
verification_setup_request1(uint32_t *len, struct verification_setup_context *sctx);
|
||||||
|
uint8_t *
|
||||||
|
verification_setup_request2(uint32_t *len, struct verification_setup_context *sctx);
|
||||||
|
uint8_t *
|
||||||
|
verification_setup_request3(uint32_t *len, struct verification_setup_context *sctx);
|
||||||
|
|
||||||
|
int
|
||||||
|
verification_setup_response1(struct verification_setup_context *sctx, const uint8_t *data, uint32_t data_len);
|
||||||
|
int
|
||||||
|
verification_setup_response2(struct verification_setup_context *sctx, const uint8_t *data, uint32_t data_len);
|
||||||
|
int
|
||||||
|
verification_setup_response3(struct verification_setup_context *sctx, const uint8_t *data, uint32_t data_len);
|
||||||
|
|
||||||
|
/* Returns a 0-terminated string that is the authorisation key. The caller
|
||||||
|
* should save it and use it later to initialize verification_verify_new().
|
||||||
|
* Note that the pointer becomes invalid when you free sctx.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
verification_setup_result(const char **authorisation_key, struct verification_setup_context *sctx);
|
||||||
|
|
||||||
|
|
||||||
|
/* When you have completed the setup you can extract a key with
|
||||||
|
* verification_setup_result(). Give the string as input to this function to
|
||||||
|
* create a verification context and then call verification_verify_request1()
|
||||||
|
*/
|
||||||
|
struct verification_verify_context *
|
||||||
|
verification_verify_new(const char *authorisation_key);
|
||||||
|
void
|
||||||
|
verification_verify_free(struct verification_verify_context *vctx);
|
||||||
|
|
||||||
|
/* Returns last error message
|
||||||
|
*/
|
||||||
|
const char *
|
||||||
|
verification_verify_errmsg(struct verification_verify_context *vctx);
|
||||||
|
|
||||||
|
uint8_t *
|
||||||
|
verification_verify_request1(uint32_t *len, struct verification_verify_context *vctx);
|
||||||
|
uint8_t *
|
||||||
|
verification_verify_request2(uint32_t *len, struct verification_verify_context *vctx);
|
||||||
|
|
||||||
|
int
|
||||||
|
verification_verify_response1(struct verification_verify_context *vctx, const uint8_t *data, uint32_t data_len);
|
||||||
|
|
||||||
|
#endif /* !__VERIFICATION_H__ */
|
62
src/player.c
62
src/player.c
@ -138,6 +138,12 @@ struct metadata_param
|
|||||||
struct output_metadata *output;
|
struct output_metadata *output;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct speaker_auth_param
|
||||||
|
{
|
||||||
|
enum output_types type;
|
||||||
|
char pin[5];
|
||||||
|
};
|
||||||
|
|
||||||
union player_arg
|
union player_arg
|
||||||
{
|
{
|
||||||
struct volume_param vol_param;
|
struct volume_param vol_param;
|
||||||
@ -150,6 +156,7 @@ union player_arg
|
|||||||
uint32_t *id_ptr;
|
uint32_t *id_ptr;
|
||||||
struct speaker_set_param speaker_set_param;
|
struct speaker_set_param speaker_set_param;
|
||||||
enum repeat_mode mode;
|
enum repeat_mode mode;
|
||||||
|
struct speaker_auth_param auth;
|
||||||
uint32_t id;
|
uint32_t id;
|
||||||
int intval;
|
int intval;
|
||||||
};
|
};
|
||||||
@ -1235,7 +1242,7 @@ device_remove(struct output_device *remove)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
/* Save device volume */
|
/* Save device volume */
|
||||||
ret = db_speaker_save(remove->id, remove->selected, remove->volume, remove->name);
|
ret = db_speaker_save(remove);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
DPRINTF(E_LOG, L_PLAYER, "Could not save state for %s device '%s'\n", remove->type_name, remove->name);
|
DPRINTF(E_LOG, L_PLAYER, "Could not save state for %s device '%s'\n", remove->type_name, remove->name);
|
||||||
|
|
||||||
@ -1273,7 +1280,7 @@ device_add(void *arg, int *retval)
|
|||||||
union player_arg *cmdarg;
|
union player_arg *cmdarg;
|
||||||
struct output_device *add;
|
struct output_device *add;
|
||||||
struct output_device *device;
|
struct output_device *device;
|
||||||
int selected;
|
char *keep_name;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
cmdarg = arg;
|
cmdarg = arg;
|
||||||
@ -1290,15 +1297,21 @@ device_add(void *arg, int *retval)
|
|||||||
{
|
{
|
||||||
device = add;
|
device = add;
|
||||||
|
|
||||||
ret = db_speaker_get(device->id, &selected, &device->volume);
|
keep_name = strdup(device->name);
|
||||||
|
ret = db_speaker_get(device, device->id);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
selected = 0;
|
device->selected = 0;
|
||||||
device->volume = (master_volume >= 0) ? master_volume : PLAYER_DEFAULT_VOLUME;
|
device->volume = (master_volume >= 0) ? master_volume : PLAYER_DEFAULT_VOLUME;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selected && (player_state != PLAY_PLAYING))
|
free(device->name);
|
||||||
|
device->name = keep_name;
|
||||||
|
|
||||||
|
if (device->selected && (player_state != PLAY_PLAYING))
|
||||||
speaker_select_output(device);
|
speaker_select_output(device);
|
||||||
|
else
|
||||||
|
device->selected = 0;
|
||||||
|
|
||||||
device->next = dev_list;
|
device->next = dev_list;
|
||||||
dev_list = device;
|
dev_list = device;
|
||||||
@ -1403,6 +1416,18 @@ device_remove_family(void *arg, int *retval)
|
|||||||
return COMMAND_END;
|
return COMMAND_END;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static enum command_state
|
||||||
|
device_auth_kickoff(void *arg, int *retval)
|
||||||
|
{
|
||||||
|
union player_arg *cmdarg = arg;
|
||||||
|
|
||||||
|
outputs_authorize(cmdarg->auth.type, cmdarg->auth.pin);
|
||||||
|
|
||||||
|
*retval = 0;
|
||||||
|
return COMMAND_END;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static enum command_state
|
static enum command_state
|
||||||
metadata_send(void *arg, int *retval)
|
metadata_send(void *arg, int *retval)
|
||||||
{
|
{
|
||||||
@ -3020,6 +3045,31 @@ player_device_remove(void *device)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
player_device_auth_kickoff(enum output_types type, char **arglist)
|
||||||
|
{
|
||||||
|
union player_arg *cmdarg;
|
||||||
|
|
||||||
|
cmdarg = calloc(1, sizeof(union player_arg));
|
||||||
|
if (!cmdarg)
|
||||||
|
{
|
||||||
|
DPRINTF(E_LOG, L_PLAYER, "Could not allocate player_command\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmdarg->auth.type = type;
|
||||||
|
memcpy(cmdarg->auth.pin, arglist[0], 4);
|
||||||
|
|
||||||
|
commands_exec_async(cmdbase, device_auth_kickoff, cmdarg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Thread: filescanner */
|
||||||
|
void
|
||||||
|
player_raop_verification_kickoff(char **arglist)
|
||||||
|
{
|
||||||
|
player_device_auth_kickoff(OUTPUT_TYPE_RAOP, arglist);
|
||||||
|
}
|
||||||
|
|
||||||
/* Thread: worker */
|
/* Thread: worker */
|
||||||
static void
|
static void
|
||||||
player_metadata_send(struct input_metadata *imd, struct output_metadata *omd)
|
player_metadata_send(struct input_metadata *imd, struct output_metadata *omd)
|
||||||
@ -3057,7 +3107,7 @@ player(void *arg)
|
|||||||
|
|
||||||
for (device = dev_list; device; device = device->next)
|
for (device = dev_list; device; device = device->next)
|
||||||
{
|
{
|
||||||
ret = db_speaker_save(device->id, device->selected, device->volume, device->name);
|
ret = db_speaker_save(device);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
DPRINTF(E_LOG, L_PLAYER, "Could not save state for %s device '%s'\n", device->type_name, device->name);
|
DPRINTF(E_LOG, L_PLAYER, "Could not save state for %s device '%s'\n", device->type_name, device->name);
|
||||||
}
|
}
|
||||||
|
@ -141,6 +141,9 @@ player_device_add(void *device);
|
|||||||
int
|
int
|
||||||
player_device_remove(void *device);
|
player_device_remove(void *device);
|
||||||
|
|
||||||
|
void
|
||||||
|
player_raop_verification_kickoff(char **arglist);
|
||||||
|
|
||||||
struct player_history *
|
struct player_history *
|
||||||
player_history_get(void);
|
player_history_get(void);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user