mirror of
https://github.com/owntone/owntone-server.git
synced 2024-12-26 23:25: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],
|
||||
[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
|
||||
FORK_ARG_WITH_CHECK([FORKED_OPTS], [libevent_pthreads support],
|
||||
[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.]])])])
|
||||
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
|
||||
FORK_ARG_ENABLE([Spotify support], [spotify], [SPOTIFY],
|
||||
[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_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
|
||||
FORK_ARG_DISABLE([MPD client protocol support], [mpd], [MPD])
|
||||
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
|
||||
AC_ARG_WITH([daapd_user],
|
||||
[AS_HELP_STRING([--with-daapd-user=USER],
|
||||
|
@ -25,6 +25,10 @@ if COND_MPD
|
||||
MPD_SRC=mpd.c mpd.h
|
||||
endif
|
||||
|
||||
if COND_RAOP_VERIFICATION
|
||||
RAOP_VERIFICATION_SRC=outputs/raop_verification.c outputs/raop_verification.h
|
||||
endif
|
||||
|
||||
if COND_ALSA
|
||||
ALSA_SRC=outputs/alsa.c
|
||||
endif
|
||||
@ -112,7 +116,8 @@ forked_daapd_SOURCES = main.c \
|
||||
input.h input.c \
|
||||
inputs/file_http.c inputs/pipe.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) \
|
||||
evrtsp/rtsp.c evrtsp/evrtsp.h evrtsp/rtsp-internal.h evrtsp/log.h \
|
||||
$(SPOTIFY_SRC) \
|
||||
|
92
src/db.c
92
src/db.c
@ -3824,21 +3824,21 @@ db_admin_delete(const char *key)
|
||||
|
||||
/* Speakers */
|
||||
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;
|
||||
|
||||
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);
|
||||
#undef Q_TMPL
|
||||
}
|
||||
|
||||
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;
|
||||
char *query;
|
||||
int ret;
|
||||
@ -3847,7 +3847,6 @@ db_speaker_get(uint64_t id, int *selected, int *volume)
|
||||
if (!query)
|
||||
{
|
||||
DPRINTF(E_LOG, L_DB, "Out of memory for query string\n");
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -3857,7 +3856,6 @@ db_speaker_get(uint64_t id, int *selected, int *volume)
|
||||
if (ret != SQLITE_OK)
|
||||
{
|
||||
DPRINTF(E_LOG, L_DB, "Could not prepare statement: %s\n", sqlite3_errmsg(hdl));
|
||||
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
@ -3867,15 +3865,20 @@ db_speaker_get(uint64_t id, int *selected, int *volume)
|
||||
{
|
||||
if (ret != SQLITE_DONE)
|
||||
DPRINTF(E_LOG, L_DB, "Could not step: %s\n", sqlite3_errmsg(hdl));
|
||||
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
*selected = sqlite3_column_int(stmt, 0);
|
||||
*volume = sqlite3_column_int(stmt, 1);
|
||||
device->id = id;
|
||||
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
|
||||
while (db_blocking_step(stmt) == SQLITE_ROW)
|
||||
@ -3893,73 +3896,6 @@ db_speaker_get(uint64_t id, int *selected, int *volume)
|
||||
#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
|
||||
db_speaker_clear_all(void)
|
||||
{
|
||||
|
13
src/db.h
13
src/db.h
@ -8,6 +8,7 @@
|
||||
|
||||
#include <sqlite3.h>
|
||||
|
||||
#include "outputs.h"
|
||||
|
||||
enum index_type {
|
||||
I_NONE,
|
||||
@ -675,18 +676,12 @@ db_admin_get(const char *key);
|
||||
int
|
||||
db_admin_delete(const char *key);
|
||||
|
||||
/* Speakers */
|
||||
/* Speakers/outputs */
|
||||
int
|
||||
db_speaker_save(uint64_t id, int selected, int volume, const char *name);
|
||||
db_speaker_save(struct output_device *device);
|
||||
|
||||
int
|
||||
db_speaker_get(uint64_t id, int *selected, int *volume);
|
||||
|
||||
int
|
||||
db_speaker_auth_save(uint64_t id, const char *authkey);
|
||||
|
||||
char *
|
||||
db_speaker_auth_get(uint64_t id);
|
||||
db_speaker_get(struct output_device *device, uint64_t id);
|
||||
|
||||
void
|
||||
db_speaker_clear_all(void);
|
||||
|
@ -141,7 +141,7 @@
|
||||
" selected INTEGER NOT NULL," \
|
||||
" volume INTEGER NOT NULL," \
|
||||
" name VARCHAR(255) DEFAULT NULL," \
|
||||
" authkey VARCHAR(2048) DEFAULT NULL" \
|
||||
" auth_key VARCHAR(2048) DEFAULT NULL" \
|
||||
");"
|
||||
|
||||
#define T_INOTIFY \
|
||||
|
@ -1548,7 +1548,7 @@ static const struct db_upgrade_query db_upgrade_v1903_queries[] =
|
||||
|
||||
|
||||
#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 \
|
||||
"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[] =
|
||||
{
|
||||
{ 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_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:
|
||||
if (flags & F_SCAN_BULK)
|
||||
DPRINTF(E_LOG, L_SCAN, "Bulk scan will ignore '%s' (to process, add it after startup)\n", file);
|
||||
// else
|
||||
// kickoff(player_raop_verification_kickoff, file, 1);
|
||||
else
|
||||
kickoff(player_raop_verification_kickoff, file, 1);
|
||||
break;
|
||||
|
||||
case FILE_CTRL_LASTFM:
|
||||
|
@ -606,6 +606,9 @@ main(int argc, char **argv)
|
||||
#ifdef MPD
|
||||
strcat(buildopts, " --enable-mpd");
|
||||
#endif
|
||||
#ifdef RAOP_VERIFICATION
|
||||
strcat(buildopts, " --enable-verification");
|
||||
#endif
|
||||
#ifdef HAVE_ALSA
|
||||
strcat(buildopts, " --with-alsa");
|
||||
#endif
|
||||
|
@ -100,23 +100,22 @@ outputs_device_probe(struct output_device *device, output_status_cb cb)
|
||||
void
|
||||
outputs_device_free(struct output_device *device)
|
||||
{
|
||||
if (!device)
|
||||
return;
|
||||
|
||||
if (outputs[device->type]->disabled)
|
||||
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)
|
||||
outputs[device->type]->device_free_extra(device);
|
||||
|
||||
if (device->name)
|
||||
free(device->name);
|
||||
|
||||
if (device->v4_address)
|
||||
free(device->v4_address);
|
||||
|
||||
if (device->v6_address)
|
||||
free(device->v6_address);
|
||||
|
||||
if (device->session)
|
||||
DPRINTF(E_LOG, L_PLAYER, "BUG! Freeing device with active session?\n");
|
||||
free(device->name);
|
||||
free(device->auth_key);
|
||||
free(device->v4_address);
|
||||
free(device->v6_address);
|
||||
|
||||
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
|
||||
outputs_priority(struct output_device *device)
|
||||
{
|
||||
|
@ -102,9 +102,11 @@ struct output_device
|
||||
unsigned advertised:1;
|
||||
unsigned has_password:1;
|
||||
unsigned has_video:1;
|
||||
unsigned requires_auth:1;
|
||||
|
||||
// Password if relevant
|
||||
// Credentials if relevant
|
||||
const char *password;
|
||||
char *auth_key;
|
||||
|
||||
// Device volume
|
||||
int volume;
|
||||
@ -187,6 +189,9 @@ struct output_definition
|
||||
// Flush all sessions, the return must be number of sessions pending the flush
|
||||
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
|
||||
void (*status_cb)(struct output_session *session, output_status_cb cb);
|
||||
|
||||
@ -242,6 +247,9 @@ outputs_metadata_prune(uint64_t rtptime);
|
||||
void
|
||||
outputs_metadata_free(struct output_metadata *omd);
|
||||
|
||||
void
|
||||
outputs_authorize(enum output_types type, const char *pin);
|
||||
|
||||
int
|
||||
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 speaker_auth_param
|
||||
{
|
||||
enum output_types type;
|
||||
char pin[5];
|
||||
};
|
||||
|
||||
union player_arg
|
||||
{
|
||||
struct volume_param vol_param;
|
||||
@ -150,6 +156,7 @@ union player_arg
|
||||
uint32_t *id_ptr;
|
||||
struct speaker_set_param speaker_set_param;
|
||||
enum repeat_mode mode;
|
||||
struct speaker_auth_param auth;
|
||||
uint32_t id;
|
||||
int intval;
|
||||
};
|
||||
@ -1235,7 +1242,7 @@ device_remove(struct output_device *remove)
|
||||
return;
|
||||
|
||||
/* Save device volume */
|
||||
ret = db_speaker_save(remove->id, remove->selected, remove->volume, remove->name);
|
||||
ret = db_speaker_save(remove);
|
||||
if (ret < 0)
|
||||
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;
|
||||
struct output_device *add;
|
||||
struct output_device *device;
|
||||
int selected;
|
||||
char *keep_name;
|
||||
int ret;
|
||||
|
||||
cmdarg = arg;
|
||||
@ -1290,15 +1297,21 @@ device_add(void *arg, int *retval)
|
||||
{
|
||||
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)
|
||||
{
|
||||
selected = 0;
|
||||
device->selected = 0;
|
||||
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);
|
||||
else
|
||||
device->selected = 0;
|
||||
|
||||
device->next = dev_list;
|
||||
dev_list = device;
|
||||
@ -1403,6 +1416,18 @@ device_remove_family(void *arg, int *retval)
|
||||
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
|
||||
metadata_send(void *arg, int *retval)
|
||||
{
|
||||
@ -3020,6 +3045,31 @@ player_device_remove(void *device)
|
||||
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 */
|
||||
static void
|
||||
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)
|
||||
{
|
||||
ret = db_speaker_save(device->id, device->selected, device->volume, device->name);
|
||||
ret = db_speaker_save(device);
|
||||
if (ret < 0)
|
||||
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
|
||||
player_device_remove(void *device);
|
||||
|
||||
void
|
||||
player_raop_verification_kickoff(char **arglist);
|
||||
|
||||
struct player_history *
|
||||
player_history_get(void);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user