[spotify] Thread safety for the webapi access

The web api might be accessed from different threads (library, worker,
dacp), therefor protect from concurrently running refresh-token requests
(accessing the globals in spotify_webapi.c)
This commit is contained in:
chme 2017-12-22 07:47:15 +01:00 committed by ejurgensen
parent 997b4da4ad
commit 2da993cc7b
3 changed files with 66 additions and 25 deletions

View File

@ -1967,9 +1967,10 @@ spotify_init(void)
CHECK_ERR(L_SPOTIFY, mutex_init(&login_lck)); CHECK_ERR(L_SPOTIFY, mutex_init(&login_lck));
CHECK_ERR(L_SPOTIFY, pthread_cond_init(&login_cond, NULL)); CHECK_ERR(L_SPOTIFY, pthread_cond_init(&login_cond, NULL));
CHECK_ERR(L_SPOTIFY, mutex_init(&status_lck)); CHECK_ERR(L_SPOTIFY, mutex_init(&status_lck));
spotifywebapi_init();
/* Spawn thread */ /* Spawn thread */
ret = pthread_create(&tid_spotify, NULL, spotify, NULL); ret = pthread_create(&tid_spotify, NULL, spotify, NULL);
if (ret < 0) if (ret < 0)
@ -1988,6 +1989,8 @@ spotify_init(void)
return 0; return 0;
thread_fail: thread_fail:
spotifywebapi_deinit();
CHECK_ERR(L_SPOTIFY, pthread_mutex_destroy(&status_lck)); CHECK_ERR(L_SPOTIFY, pthread_mutex_destroy(&status_lck));
CHECK_ERR(L_SPOTIFY, pthread_cond_destroy(&login_cond)); CHECK_ERR(L_SPOTIFY, pthread_cond_destroy(&login_cond));
CHECK_ERR(L_SPOTIFY, pthread_mutex_destroy(&login_lck)); CHECK_ERR(L_SPOTIFY, pthread_mutex_destroy(&login_lck));
@ -2054,6 +2057,9 @@ spotify_deinit(void)
CHECK_ERR(L_SPOTIFY, pthread_cond_destroy(&login_cond)); CHECK_ERR(L_SPOTIFY, pthread_cond_destroy(&login_cond));
CHECK_ERR(L_SPOTIFY, pthread_mutex_destroy(&login_lck)); CHECK_ERR(L_SPOTIFY, pthread_mutex_destroy(&login_lck));
/* Deinit web api */
spotifywebapi_deinit();
/* Free audio buffer */ /* Free audio buffer */
evbuffer_free(spotify_audio_buffer); evbuffer_free(spotify_audio_buffer);

View File

@ -43,6 +43,8 @@ static char *spotify_user;
static int32_t expires_in = 3600; static int32_t expires_in = 3600;
static time_t token_requested = 0; static time_t token_requested = 0;
static pthread_mutex_t token_lck;
// Endpoints and credentials for the web api // Endpoints and credentials for the web api
static const char *spotify_client_id = "0e684a5422384114a8ae7ac020f01789"; static const char *spotify_client_id = "0e684a5422384114a8ae7ac020f01789";
static const char *spotify_client_secret = "232af95f39014c9ba218285a5c11a239"; static const char *spotify_client_secret = "232af95f39014c9ba218285a5c11a239";
@ -73,14 +75,14 @@ free_http_client_ctx(struct http_client_ctx *ctx)
} }
static int static int
request_uri(struct spotify_request *request, const char *uri) request_uri(struct spotify_request *request, const char *uri, bool check_token)
{ {
char bearer_token[1024]; char bearer_token[1024];
int ret; int ret;
memset(request, 0, sizeof(struct spotify_request)); memset(request, 0, sizeof(struct spotify_request));
if (0 > spotifywebapi_token_refresh(NULL)) if (check_token && (0 > spotifywebapi_token_refresh(NULL)))
{ {
return -1; return -1;
} }
@ -166,7 +168,7 @@ spotifywebapi_request_next(struct spotify_request *request, const char *uri, boo
spotifywebapi_request_end(request); spotifywebapi_request_end(request);
} }
ret = request_uri(request, next_uri); ret = request_uri(request, next_uri, true);
free(next_uri); free(next_uri);
if (ret < 0) if (ret < 0)
@ -498,7 +500,7 @@ spotifywebapi_playlist_start(struct spotify_request *request, const char *path,
return -1; return -1;
} }
ret = request_uri(request, uri); ret = request_uri(request, uri, true);
if (ret < 0) if (ret < 0)
{ {
free(owner); free(owner);
@ -540,7 +542,7 @@ spotifywebapi_track_start(struct spotify_request *request, const char *path, str
return -1; return -1;
} }
ret = request_uri(request, uri); ret = request_uri(request, uri, true);
if (ret < 0) if (ret < 0)
{ {
return -1; return -1;
@ -627,7 +629,7 @@ spotifywebapi_artwork_get(const char *path, int max_w, int max_h)
} }
static int static int
request_user_info() request_user_info(char **user)
{ {
struct spotify_request request; struct spotify_request request;
int ret; int ret;
@ -637,7 +639,7 @@ request_user_info()
free(spotify_user); free(spotify_user);
spotify_user = NULL; spotify_user = NULL;
ret = request_uri(&request, spotify_me_uri); ret = request_uri(&request, spotify_me_uri, false);
if (ret < 0) if (ret < 0)
{ {
@ -649,6 +651,9 @@ request_user_info()
spotify_user_country = safe_strdup(jparse_str_from_obj(request.haystack, "country")); spotify_user_country = safe_strdup(jparse_str_from_obj(request.haystack, "country"));
DPRINTF(E_DBG, L_SPOTIFY, "User '%s', country '%s'\n", spotify_user, spotify_user_country); DPRINTF(E_DBG, L_SPOTIFY, "User '%s', country '%s'\n", spotify_user, spotify_user_country);
if (user)
*user = safe_strdup(spotify_user);
} }
spotifywebapi_request_end(&request); spotifywebapi_request_end(&request);
@ -695,7 +700,7 @@ spotifywebapi_oauth_uri_get(const char *redirect_uri)
} }
static int static int
tokens_get(struct keyval *kv, const char **err) tokens_get(struct keyval *kv, char **user, const char **err)
{ {
struct http_client_ctx ctx; struct http_client_ctx ctx;
char *param; char *param;
@ -779,7 +784,7 @@ tokens_get(struct keyval *kv, const char **err)
if (spotify_refresh_token) if (spotify_refresh_token)
db_admin_set(DB_ADMIN_SPOTIFY_REFRESH_TOKEN, spotify_refresh_token); db_admin_set(DB_ADMIN_SPOTIFY_REFRESH_TOKEN, spotify_refresh_token);
request_user_info(); request_user_info(user);
ret = 0; ret = 0;
@ -797,6 +802,8 @@ spotifywebapi_token_get(const char *code, const char *redirect_uri, char **user,
struct keyval kv; struct keyval kv;
int ret; int ret;
CHECK_ERR(L_SPOTIFY, pthread_mutex_lock(&token_lck));
*err = ""; *err = "";
memset(&kv, 0, sizeof(struct keyval)); memset(&kv, 0, sizeof(struct keyval));
ret = ( (keyval_add(&kv, "grant_type", "authorization_code") == 0) && ret = ( (keyval_add(&kv, "grant_type", "authorization_code") == 0) &&
@ -811,14 +818,12 @@ spotifywebapi_token_get(const char *code, const char *redirect_uri, char **user,
ret = -1; ret = -1;
} }
else else
ret = tokens_get(&kv, err); ret = tokens_get(&kv, user, err);
if (user && ret == 0)
{
*user = safe_strdup(spotify_user);
}
keyval_clear(&kv); keyval_clear(&kv);
CHECK_ERR(L_SPOTIFY, pthread_mutex_unlock(&token_lck));
return ret; return ret;
} }
@ -826,13 +831,19 @@ int
spotifywebapi_token_refresh(char **user) spotifywebapi_token_refresh(char **user)
{ {
struct keyval kv; struct keyval kv;
char *refresh_token; char *refresh_token = NULL;
const char *err; const char *err;
int ret; int ret;
memset(&kv, 0, sizeof(struct keyval));
CHECK_ERR(L_SPOTIFY, pthread_mutex_lock(&token_lck));
if (token_requested && difftime(time(NULL), token_requested) < expires_in) if (token_requested && difftime(time(NULL), token_requested) < expires_in)
{ {
DPRINTF(E_DBG, L_SPOTIFY, "Spotify token still valid\n"); DPRINTF(E_DBG, L_SPOTIFY, "Spotify token still valid\n");
CHECK_ERR(L_SPOTIFY, pthread_mutex_unlock(&token_lck));
return 0; return 0;
} }
@ -840,12 +851,11 @@ spotifywebapi_token_refresh(char **user)
if (!refresh_token) if (!refresh_token)
{ {
DPRINTF(E_LOG, L_SPOTIFY, "No spotify refresh token found\n"); DPRINTF(E_LOG, L_SPOTIFY, "No spotify refresh token found\n");
return -1; goto error;
} }
DPRINTF(E_DBG, L_SPOTIFY, "Spotify refresh-token: '%s'\n", refresh_token); DPRINTF(E_DBG, L_SPOTIFY, "Spotify refresh-token: '%s'\n", refresh_token);
memset(&kv, 0, sizeof(struct keyval));
ret = ( (keyval_add(&kv, "grant_type", "refresh_token") == 0) && ret = ( (keyval_add(&kv, "grant_type", "refresh_token") == 0) &&
(keyval_add(&kv, "client_id", spotify_client_id) == 0) && (keyval_add(&kv, "client_id", spotify_client_id) == 0) &&
(keyval_add(&kv, "client_secret", spotify_client_secret) == 0) && (keyval_add(&kv, "client_secret", spotify_client_secret) == 0) &&
@ -853,18 +863,38 @@ spotifywebapi_token_refresh(char **user)
if (!ret) if (!ret)
{ {
DPRINTF(E_LOG, L_SPOTIFY, "Add parameters to keyval failed"); DPRINTF(E_LOG, L_SPOTIFY, "Add parameters to keyval failed");
ret = -1; goto error;
} }
else
ret = tokens_get(&kv, &err);
if (user && ret == 0) ret = tokens_get(&kv, user, &err);
{
*user = safe_strdup(spotify_user);
}
free(refresh_token); free(refresh_token);
keyval_clear(&kv); keyval_clear(&kv);
CHECK_ERR(L_SPOTIFY, pthread_mutex_unlock(&token_lck));
return ret; return ret;
error:
free(refresh_token);
keyval_clear(&kv);
CHECK_ERR(L_SPOTIFY, pthread_mutex_unlock(&token_lck));
return -1;
}
int
spotifywebapi_init()
{
CHECK_ERR(L_SPOTIFY, mutex_init(&token_lck));
return 0;
}
int
spotifywebapi_deinit()
{
CHECK_ERR(L_SPOTIFY, pthread_mutex_destroy(&token_lck));
return 0;
} }

View File

@ -126,4 +126,9 @@ spotifywebapi_playlist_start(struct spotify_request *request, const char *path,
char * char *
spotifywebapi_artwork_get(const char *path, int max_w, int max_h); spotifywebapi_artwork_get(const char *path, int max_w, int max_h);
int
spotifywebapi_init(void);
int
spotifywebapi_deinit(void);
#endif /* SRC_SPOTIFY_WEBAPI_H_ */ #endif /* SRC_SPOTIFY_WEBAPI_H_ */