var isTouch = Modernizr.touch ? 1 : 0;
var filter = '';
var scrobbler = '';
+var wss_auth_token = '';
var app = $.sammy(function () {
function runBrowse() {
$('#breadcrump').addClass('hide');
$('#filter').addClass('hide');
$('#salamisandwich').removeClass('hide').find('tr:gt(0)').remove();
- socket.send('MPD_API_GET_QUEUE,' + pagination);
+ if (wss_auth_token !== '')
+ socket.send('MPD_API_GET_QUEUE,' + pagination);
$('#panel-heading').text('Queue');
$('#panel-heading-info').empty();
);
});
+function webSocketAuthenticate() {
+ var u = document.URL.split('#');
+ var separator;
+
+ if (/\/$/.test(u[0])) {
+ separator = '';
+ } else {
+ separator = '/';
+ }
+
+ $.ajax({
+ url: u[0] + separator + 'wss-auth',
+ success: function (data) {
+ wss_auth_token = data;
+ socket.send('MPD_API_AUTHORIZE,' + wss_auth_token);
+ },
+ });
+}
+
function webSocketConnect() {
if (typeof MozWebSocket != 'undefined') {
socket = new MozWebSocket(get_appropriate_ws_url());
.show();
app.run();
- /* emit initial request for output names */
- socket.send('MPD_API_GET_OUTPUTS');
- socket.send('MPD_API_GET_CHANNELS');
+
+ if (wss_auth_token === '') webSocketAuthenticate();
};
socket.onmessage = function got_packet(msg) {
$('#mpd_password_set').removeClass('hide');
break;
+ case 'authorized':
+ if (obj.data === 'true') {
+ /* emit initial request for output names */
+ socket.send('MPD_API_GET_OUTPUTS');
+ socket.send('MPD_API_GET_CHANNELS');
+ } else webSocketAuthenticate();
+
+ break;
+
case 'error':
$('.top-right')
.notify({
socket.onclose = function () {
console.log('disconnected');
+ wss_auth_token = '';
$('.top-right')
.notify({
message: {
#include "http_server.h"
+#include <openssl/rand.h>
#include <string.h>
+#include "mpd_client.h"
+
int callback_http(struct mg_connection *c) {
const struct embedded_file *req_file;
return MG_TRUE;
}
+ if (!strcmp(c->uri, "/wss-auth")) {
+ unsigned char salt[WSS_AUTH_TOKEN_SIZE + 1];
+
+ RAND_bytes(salt, WSS_AUTH_TOKEN_SIZE);
+ for (int i = 0; i <= WSS_AUTH_TOKEN_SIZE; i++) salt[i] = salt[i] % 26 + 65;
+ salt[WSS_AUTH_TOKEN_SIZE] = 0;
+ if (mpd.wss_auth_token)
+ free(mpd.wss_auth_token);
+ mpd.wss_auth_token = strdup((char *)salt);
+
+ mg_send_header(c, "Content-Type", "text/plain");
+ mg_send_data(c, salt, WSS_AUTH_TOKEN_SIZE);
+
+ return MG_TRUE;
+ }
+
mg_send_status(c, 404);
mg_printf_data(c, "Not Found");
return MG_TRUE;
int int_buf;
char *p_charbuf = NULL, *token;
+ if (!c->connection_param)
+ c->connection_param = calloc(1, sizeof(struct t_mpd_client_session));
+
+ struct t_mpd_client_session *s = (struct t_mpd_client_session *)c->connection_param;
+
+ if (!s->authorized && (cmd_id != MPD_API_AUTHORIZE)) {
+ n = snprintf(mpd.buf, MAX_SIZE, "{\"type\":\"error\",\"data\":\"not authorized\"}");
+ mg_websocket_write(c, 1, mpd.buf, n);
+
+ return MG_TRUE;
+ }
+
if (cmd_id == -1)
return MG_TRUE;
return MG_TRUE;
switch (cmd_id) {
+ case MPD_API_AUTHORIZE:
+ p_charbuf = strdup(c->content);
+ if (strcmp(strtok(p_charbuf, ","), "MPD_API_AUTHORIZE"))
+ goto out_authorize;
+
+ if ((token = strtok(NULL, ",")) == NULL)
+ goto out_authorize;
+
+ free(p_charbuf);
+ p_charbuf = strdup(c->content);
+ s->auth_token = strdup(get_arg1(p_charbuf));
+ if (!strcmp(mpd.wss_auth_token, s->auth_token))
+ s->authorized = 1;
+
+ n = snprintf(mpd.buf, MAX_SIZE, "{\"type\":\"authorized\", \"data\":\"%s\"}",
+ s->authorized ? "true" : "false");
+ out_authorize:
+ free(p_charbuf);
+ break;
case MPD_API_UPDATE_DB:
mpd_run_update(mpd.conn, NULL);
break;
int mpd_close_handler(struct mg_connection *c) {
/* Cleanup session data */
- if (c->connection_param)
+ if (c->connection_param) {
+ struct t_mpd_client_session *s = (struct t_mpd_client_session *)c->connection_param;
+ if (s->auth_token)
+ free(s->auth_token);
free(c->connection_param);
+ }
+
return 0;
}
if (!c->is_websocket)
return MG_TRUE;
+ if (!c->connection_param)
+ return MG_TRUE;
+
+ struct t_mpd_client_session *s = (struct t_mpd_client_session *)c->connection_param;
+
+ if (!s->authorized)
+ return MG_TRUE;
+
if (c->callback_param) {
/* error message? */
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\":\"error\",\"data\":\"%s\"}",
return MG_TRUE;
}
- if (!c->connection_param)
- c->connection_param = calloc(1, sizeof(struct t_mpd_client_session));
-
- struct t_mpd_client_session *s = (struct t_mpd_client_session *)c->connection_param;
-
if (mpd.conn_state != MPD_CONNECTED) {
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\":\"disconnected\"}");
mg_websocket_write(c, 1, mpd.buf, n);
c->callback_param = NULL;
mpd_notify_callback(c, MG_POLL);
}
+ mpd.buf_size = mpd_put_channels(mpd.buf);
+ for (struct mg_connection *c = mg_next(s, NULL); c != NULL; c = mg_next(s, c)) {
+ c->callback_param = NULL;
+ mpd_notify_callback(c, MG_POLL);
+ }
break;
}
}
#define MAX_SIZE 1024 * 100
#define MAX_ELEMENTS_PER_PAGE 512
+#define WSS_AUTH_TOKEN_SIZE 50
+
#define GEN_ENUM(X) X,
#define GEN_STR(X) #X,
#define MPD_CMDS(X) \
X(MPD_API_TOGGLE_CONSUME) \
X(MPD_API_TOGGLE_SINGLE) \
X(MPD_API_TOGGLE_CROSSFADE) \
- X(MPD_API_TOGGLE_REPEAT)
+ X(MPD_API_TOGGLE_REPEAT) \
+ X(MPD_API_AUTHORIZE)
enum mpd_cmd_ids { MPD_CMDS(GEN_ENUM) };
char host[128];
char *password;
char *gpass;
+ char *wss_auth_token;
struct mpd_connection *conn;
enum mpd_conn_states conn_state;
struct t_mpd_client_session {
int song_id;
unsigned queue_version;
+ int authorized;
+ char *auth_token;
};
void mpd_poll(struct mg_server *s);