1
0
mirror of https://github.com/SuperBFG7/ympd synced 2024-06-27 15:13:15 +00:00

Replaces jquery.cookie.js with version from https://github.com/js-cookie/js-cookie

Added streamport option to commandline
Fixed description of options in manpage and help and README
Removed mpd host options from settings menu
Removed http stream options from settings menu
This commit is contained in:
jcorporation 2018-05-24 18:50:05 +01:00
parent 361882d5af
commit f55fa51934
15 changed files with 254 additions and 493 deletions

View File

@ -11,7 +11,6 @@ else()
set(ASSETS_PATH "${PROJECT_SOURCE_DIR}/htdocs")
endif()
option(WITH_MPD_HOST_CHANGE "Let users of the web frontend change the MPD Host" ON)
option(WITH_IPV6 "enable IPv6 support" ON)
option(WITH_SSL "enable SSL support" ON)
@ -46,7 +45,7 @@ file(GLOB RESOURCES
)
set(SOURCES
src/ympd.c
src/mympd.c
src/mpd_client.c
src/mongoose.c
src/json_encode.c

View File

@ -14,6 +14,8 @@ UI Components
- Bootstrap Slider: https://github.com/seiyria/bootstrap-slider
- Material Design Icons: https://material.io/tools/icons/?style=baseline
- Sammy.js: http://sammyjs.org/
- jQuery: https://jquery.com/
- js-cookie: https://github.com/js-cookie/js-cookie
Dependencies
------------
@ -34,14 +36,17 @@ Unix Build Instructions
Run flags
---------
```
Usage: ./ympd [OPTION]...
Usage: ./mympd [OPTION]...
-D, --digest <htdigest> path to htdigest file for authorization
(realm ympd) [no authorization]
(realm mympd) [no authorization]
-h, --host <host> connect to mpd at host [localhost]
-p, --port <port> connect to mpd at port [6600]
-l, --localport <port> skip authorization for local port
-w, --webport [ip:]<port> listen interface/port for webserver [8080]
-s, --streamport <port> connect to mpd http stream at port [8000]
-u, --user <username> drop priviliges to user after socket bind
-m, --mpdpass <password> specifies the password to use when connecting to mpd
-V, --version get version
--help this help
```
@ -57,7 +62,7 @@ To run ympd with SSL support:
```
- tell ympd to use a webport using SSL and where to find the certificate:
```
# ./ympd -w "ssl://8081:/path/to/ssl.pem"
# ./mympd -w "ssl://8081:/path/to/ssl.pem"
```
Copyright

View File

@ -30,7 +30,7 @@
</form>
<div class="dropdown-divider"></div>
<a id="nav-addstream" class="dropdown-item text-light bg-dark" href="#" data-toggle="modal" data-target="#addstream">Add Stream</a>
<a id="nav-settings" class="dropdown-item text-light bg-dark" href="#" data-toggle="modal" data-target="#settings" onclick="getHost();">Settings</a>
<a id="nav-settings" class="dropdown-item text-light bg-dark" href="#" data-toggle="modal" data-target="#settings">Settings</a>
<a id="nav-updatedb" class="dropdown-item text-light bg-dark" href="#" onclick="updateDB();">Update Database</a>
<a id="nav-localplayer" class="dropdown-item text-light bg-dark" href="#" data-toggle="dropdown" onclick="window.open('/player.html','LocalPlayer');">Local Player</a>
</div>
@ -283,47 +283,6 @@
</button>
</div>
</div>
<hr/>
<form role="form">
<div class="row">
<div class="form-group col-md-9">
<label class="control-label" for="mpdhost">MPD Host/IP</label>
<input type="text" class="form-control" id="mpdhost" />
</div>
<div class="form-group col-md-3">
<label class="control-label" for="mpdport">MPD Port</label>
<input type="text" class="form-control" id="mpdport" />
</div>
</div>
<div class="row">
<div class="form-group col-md-6">
<label class="control-label" for="mpd_pw">MPD Password</label>
<input type="password" class="form-control" id="mpd_pw" placeholder="Password"/>
</div>
<div class="form-group col-md-6">
<label class="control-label" for="mpd_pw_con">MPD Password (Confirmation)</label>
<input type="password" class="form-control" id="mpd_pw_con" placeholder="Confirmation"
data-placement="right" data-toggle="popover" data-content="Password does not match!"
data-trigger="manual" />
</div>
<div class="form-group col-md-12">
<div id="mpd_password_set" class="hide alert alert-info">
<button type="button" class="close" aria-hidden="true">&times;</button>
MPD Password is set
</div>
</div>
</div>
<div class="row">
<div class="form-group col-md-12">
<label class="control-label" for="mpdstream">MPD Stream URL</label>
<input type="text" class="form-control" id="mpdstream" />
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-success" onclick="confirmSettings();">Save</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
@ -385,7 +344,7 @@
</div><!-- /.modal -->
<script src="js/jquery-3.3.1.min.js"></script>
<script src="js/jquery.cookie.js"></script>
<script src="js/js.cookie.js"></script>
<script src="js/bootstrap.min.js"></script>
<script src="js/bootstrap-slider.min.js"></script>
<script src="js/bootstrap-notify.min.js"></script>

File diff suppressed because one or more lines are too long

View File

@ -1,114 +0,0 @@
/*!
* jQuery Cookie Plugin v1.4.0
* https://github.com/carhartl/jquery-cookie
*
* Copyright 2013 Klaus Hartl
* Released under the MIT license
*/
(function (factory) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as anonymous module.
define(['jquery'], factory);
} else {
// Browser globals.
factory(jQuery);
}
}(function ($) {
var pluses = /\+/g;
function encode(s) {
return config.raw ? s : encodeURIComponent(s);
}
function decode(s) {
return config.raw ? s : decodeURIComponent(s);
}
function stringifyCookieValue(value) {
return encode(config.json ? JSON.stringify(value) : String(value));
}
function parseCookieValue(s) {
if (s.indexOf('"') === 0) {
// This is a quoted cookie as according to RFC2068, unescape...
s = s.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\');
}
try {
// Replace server-side written pluses with spaces.
// If we can't decode the cookie, ignore it, it's unusable.
// If we can't parse the cookie, ignore it, it's unusable.
s = decodeURIComponent(s.replace(pluses, ' '));
return config.json ? JSON.parse(s) : s;
} catch(e) {}
}
function read(s, converter) {
var value = config.raw ? s : parseCookieValue(s);
return $.isFunction(converter) ? converter(value) : value;
}
var config = $.cookie = function (key, value, options) {
// Write
if (value !== undefined && !$.isFunction(value)) {
options = $.extend({}, config.defaults, options);
if (typeof options.expires === 'number') {
var days = options.expires, t = options.expires = new Date();
t.setTime(+t + days * 864e+5);
}
return (document.cookie = [
encode(key), '=', stringifyCookieValue(value),
options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE
options.path ? '; path=' + options.path : '',
options.domain ? '; domain=' + options.domain : '',
options.secure ? '; secure' : ''
].join(''));
}
// Read
var result = key ? undefined : {};
// To prevent the for loop in the first place assign an empty array
// in case there are no cookies at all. Also prevents odd result when
// calling $.cookie().
var cookies = document.cookie ? document.cookie.split('; ') : [];
for (var i = 0, l = cookies.length; i < l; i++) {
var parts = cookies[i].split('=');
var name = decode(parts.shift());
var cookie = parts.join('=');
if (key && key === name) {
// If second argument (value) is a function it's a converter...
result = read(cookie, value);
break;
}
// Prevent storing a cookie that we couldn't decode.
if (!key && (cookie = read(cookie)) !== undefined) {
result[name] = cookie;
}
}
return result;
};
config.defaults = {};
$.removeCookie = function (key, options) {
if ($.cookie(key) === undefined) {
return false;
}
// Must not alter options, thus extending a fresh object...
$.cookie(key, '', $.extend({}, options, { expires: -1 }));
return !$.cookie(key);
};
}));

165
htdocs/js/js.cookie.js Normal file
View File

@ -0,0 +1,165 @@
/*!
* JavaScript Cookie v2.2.0
* https://github.com/js-cookie/js-cookie
*
* Copyright 2006, 2015 Klaus Hartl & Fagner Brack
* Released under the MIT license
*/
;(function (factory) {
var registeredInModuleLoader = false;
if (typeof define === 'function' && define.amd) {
define(factory);
registeredInModuleLoader = true;
}
if (typeof exports === 'object') {
module.exports = factory();
registeredInModuleLoader = true;
}
if (!registeredInModuleLoader) {
var OldCookies = window.Cookies;
var api = window.Cookies = factory();
api.noConflict = function () {
window.Cookies = OldCookies;
return api;
};
}
}(function () {
function extend () {
var i = 0;
var result = {};
for (; i < arguments.length; i++) {
var attributes = arguments[ i ];
for (var key in attributes) {
result[key] = attributes[key];
}
}
return result;
}
function init (converter) {
function api (key, value, attributes) {
var result;
if (typeof document === 'undefined') {
return;
}
// Write
if (arguments.length > 1) {
attributes = extend({
path: '/'
}, api.defaults, attributes);
if (typeof attributes.expires === 'number') {
var expires = new Date();
expires.setMilliseconds(expires.getMilliseconds() + attributes.expires * 864e+5);
attributes.expires = expires;
}
// We're using "expires" because "max-age" is not supported by IE
attributes.expires = attributes.expires ? attributes.expires.toUTCString() : '';
try {
result = JSON.stringify(value);
if (/^[\{\[]/.test(result)) {
value = result;
}
} catch (e) {}
if (!converter.write) {
value = encodeURIComponent(String(value))
.replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g, decodeURIComponent);
} else {
value = converter.write(value, key);
}
key = encodeURIComponent(String(key));
key = key.replace(/%(23|24|26|2B|5E|60|7C)/g, decodeURIComponent);
key = key.replace(/[\(\)]/g, escape);
var stringifiedAttributes = '';
for (var attributeName in attributes) {
if (!attributes[attributeName]) {
continue;
}
stringifiedAttributes += '; ' + attributeName;
if (attributes[attributeName] === true) {
continue;
}
stringifiedAttributes += '=' + attributes[attributeName];
}
return (document.cookie = key + '=' + value + stringifiedAttributes);
}
// Read
if (!key) {
result = {};
}
// To prevent the for loop in the first place assign an empty array
// in case there are no cookies at all. Also prevents odd result when
// calling "get()"
var cookies = document.cookie ? document.cookie.split('; ') : [];
var rdecode = /(%[0-9A-Z]{2})+/g;
var i = 0;
for (; i < cookies.length; i++) {
var parts = cookies[i].split('=');
var cookie = parts.slice(1).join('=');
if (!this.json && cookie.charAt(0) === '"') {
cookie = cookie.slice(1, -1);
}
try {
var name = parts[0].replace(rdecode, decodeURIComponent);
cookie = converter.read ?
converter.read(cookie, name) : converter(cookie, name) ||
cookie.replace(rdecode, decodeURIComponent);
if (this.json) {
try {
cookie = JSON.parse(cookie);
} catch (e) {}
}
if (key === name) {
result = cookie;
break;
}
if (!key) {
result[name] = cookie;
}
} catch (e) {}
}
return result;
}
api.set = api;
api.get = function (key) {
return api.call(api, key);
};
api.getJSON = function () {
return api.apply({
json: true
}, [].slice.call(arguments));
};
api.defaults = {};
api.remove = function (key, attributes) {
api(key, '', extend(attributes, {
expires: -1
}));
};
api.withConverter = init;
return api;
}
return init(function () {});
}));

View File

@ -162,10 +162,10 @@ $(document).ready(function(){
if(!notificationsSupported())
$('#btnnotifyWeb').addClass("disabled");
else
if ($.cookie("notificationWeb") === "true")
if (Cookies.get('notificationWeb') === 'true')
$('#btnnotifyWeb').removeClass('btn-secondary').addClass("btn-success")
if ($.cookie("notificationPage") === "true")
if (Cookies.get('notificationPage') === 'true')
$('#btnnotifyPage').removeClass('btn-secondary').addClass("btn-success")
add_filter();
@ -186,6 +186,8 @@ function webSocketConnect() {
app.run();
/* emit initial request for output names */
socket.send('MPD_API_GET_OUTPUTS');
/* emit request for mympd options */
socket.send('MPD_API_GET_OPTIONS');
}
socket.onmessage = function got_packet(msg) {
@ -517,15 +519,11 @@ function webSocketConnect() {
socket.send('MPD_API_GET_QUEUE,'+pagination);
}
break;
case "song_change":
case "song_change":
songChange(obj.data.title, obj.data.artist, obj.data.album, obj.data.uri);
break;
case 'mpdhost':
$('#mpdhost').val(obj.data.host);
setLocalStream(obj.data.host);
$('#mpdport').val(obj.data.port);
if(obj.data.passwort_set)
$('#mpd_password_set').removeClass('hide');
case 'mpdoptions':
setLocalStream(obj.data.mpdhost,obj.data.streamport);
break;
case 'error':
showNotification(obj.data,'','','danger');
@ -614,22 +612,14 @@ function clickPlay() {
socket.send('MPD_API_SET_PAUSE');
}
function setLocalStream(mpdhost) {
var mpdstream = $.cookie("mpdstream");
if ( !mpdstream ) {
mpdstream = "http://";
if ( mpdhost == "127.0.0.1" || mpdhost == "localhost")
mpdstream += window.location.hostname;
else
mpdstream += mpdhost;
mpdstream += ":8000/";
$.cookie("mpdstream", mpdstream, { expires: 424242 });
}
$("#mpdstream").val(mpdstream);
$("#mpdstream").change();
function setLocalStream(mpdhost,streamport) {
var mpdstream = 'http://';
if ( mpdhost == '127.0.0.1' || mpdhost == 'localhost')
mpdstream += window.location.hostname;
else
mpdstream += mpdhost;
mpdstream += ':'+streamport+'/';
Cookies.set('mpdstream', mpdstream, { expires: 424242 });
}
function trash(tr) {
@ -677,8 +667,8 @@ $('#trashmode').children("button").on('click', function(e) {
});
$('#btnnotifyWeb').on('click', function (e) {
if($.cookie('notificationWeb') === 'true') {
$.cookie('notificationWeb', false);
if(Cookies.get('notificationWeb') === 'true') {
Cookies.set('notificationWeb', false, { expires: 424242 });
$('#btnnotify').removeClass('btn-success').addClass('btn-secondary');
} else {
Notification.requestPermission(function (permission) {
@ -687,7 +677,7 @@ $('#btnnotifyWeb').on('click', function (e) {
}
if (permission === 'granted') {
$.cookie('notificationWeb', true, { expires: 424242 });
Cookies.set('notificationWeb', true, { expires: 424242 });
$('#btnnotifyWeb').removeClass('btn-secondary').addClass('btn-success');
}
});
@ -695,31 +685,15 @@ $('#btnnotifyWeb').on('click', function (e) {
});
$('#btnnotifyPage').on('click', function (e) {
if($.cookie("notificationPage") === 'true') {
$.cookie("notificationPage", false);
if(Cookies.get("notificationPage") === 'true') {
Cookies.set("notificationPage", false, { expires: 424242 });
$('#btnnotifyPage').removeClass('btn-success').addClass('btn-secondary');
} else {
$.cookie('notificationPage', true, { expires: 424242 });
Cookies.set('notificationPage', true, { expires: 424242 });
$('#btnnotifyPage').removeClass('btn-secondary').addClass('btn-success');
}
});
function getHost() {
socket.send('MPD_API_GET_MPDHOST');
function onEnter(event) {
if ( event.which == 13 ) {
confirmSettings();
}
}
$('#mpdhost').keypress(onEnter);
$('#mpdport').keypress(onEnter);
$('#mpdstream').keypress(onEnter);
$('#mpd_pw').keypress(onEnter);
$('#mpd_pw_con').keypress(onEnter);
}
$('#search > input').keypress(function (event) {
if ( event.which == 13 ) {
$('#mainMenu > a').dropdown('toggle');
@ -770,38 +744,14 @@ function saveQueue() {
$('#savequeue').modal('hide');
}
function confirmSettings() {
if($('#mpd_pw').val().length + $('#mpd_pw_con').val().length > 0) {
if ($('#mpd_pw').val() !== $('#mpd_pw_con').val())
{
$('#mpd_pw_con').popover('show');
setTimeout(function() {
$('#mpd_pw_con').popover('hide');
}, 2000);
return;
} else
socket.send('MPD_API_SET_MPDPASS,'+$('#mpd_pw').val());
}
socket.send('MPD_API_SET_MPDHOST,'+$('#mpdport').val()+','+$('#mpdhost').val());
$.cookie("mpdstream", $("#mpdstream").val(), { expires: 424242 });
$('#settings').modal('hide');
}
$('#mpd_password_set > button').on('click', function (e) {
socket.send('MPD_API_SET_MPDPASS,');
$('#mpd_pw').val("");
$('#mpd_pw_con').val("");
$('#mpd_password_set').addClass('hide');
})
function showNotification(notificationTitle,notificationText,notificationHtml,notificationType) {
if ($.cookie('notificationWeb') === 'true') {
if (Cookies.get('notificationWeb') === 'true') {
var notification = new Notification(notificationTitle, {icon: 'assets/favicon.ico', body: notificationText});
setTimeout(function(notification) {
notification.close();
}, 3000, notification);
}
if ($.cookie('notificationPage') === 'true') {
if (Cookies.get('notificationPage') === 'true') {
$.notify({ title: notificationTitle, message: notificationHtml},{ type: notificationType, offset: { y: 60, x:20 },
template: '<div data-notify="container" class="col-xs-11 col-sm-3 alert alert-{0}" role="alert">' +
'<button type="button" aria-hidden="true" class="close" data-notify="dismiss">×</button>' +

View File

@ -31,11 +31,11 @@
</main>
<script src="js/jquery-3.3.1.min.js"></script>
<script src="js/jquery.cookie.js"></script>
<script src="js/js.cookie.js"></script>
<script type="text/javascript">
var mpdstream = $.cookie("mpdstream");
var mpdstream = Cookies.get('mpdstream');
player.src = mpdstream;
console.log("playing mpd stream: " + player.src);
console.log('playing mpd stream: ' + player.src);
player.load();
player.play();
</script>

View File

@ -18,12 +18,18 @@ connect to mpd at host, defaults to localhost
\fB\-p\fR, \fB\-\-port PORT\fR
connect to mpd at port, defaults to 6600
.TP
\fB\-D\fR, \fB\-\-digest HTDIGEST\fR
path to htdigest file for authorization
.TP
\fB\-l\fR, \fB\-\-localport PORT\fR
skip authorization for local port
.TP
\fB\-w\fR, \fB\-\-webport PORT\fR
specifies the port for the webserver to listen to, defaults to 8080
.TP
\fB-s\fR, \fB\-\-streamport PORT
connect to mpd http stream at port [8000]
.TP
\fB\-u\fR, \fB\-\-user username\fR
drop privileges to the provided username after socket binding
.TP

View File

@ -1,4 +1,10 @@
/* ympd
/* myMPD
(c) 2018 Juergen Mang <mail@jcgames.de>
This project's homepage is: https://github.com/jcorporation/ympd
myMPD ist fork of:
ympd
(c) 2013-2014 Andrew Karpow <andy@ndyk.de>
This project's homepage is: http://www.ympd.org
@ -23,6 +29,4 @@
#define YMPD_VERSION_MINOR ${CPACK_PACKAGE_VERSION_MINOR}
#define YMPD_VERSION_PATCH ${CPACK_PACKAGE_VERSION_PATCH}
#define SRC_PATH "${ASSETS_PATH}"
#cmakedefine WITH_MPD_HOST_CHANGE
#endif

View File

@ -1,4 +1,10 @@
/* ympd
/* myMPD
(c) 2018 Juergen Mang <mail@jcgames.de>
This project's homepage is: https://github.com/jcorporation/ympd
myMPD ist fork of:
ympd
(c) 2013-2014 Andrew Karpow <andy@ndyk.de>
This project's homepage is: http://www.ympd.org

View File

@ -1,4 +1,10 @@
/* ympd
/* myMPD
(c) 2018 Juergen Mang <mail@jcgames.de>
This project's homepage is: https://github.com/jcorporation/ympd
myMPD ist fork of:
ympd
(c) 2013-2014 Andrew Karpow <andy@ndyk.de>
This project's homepage is: http://www.ympd.org

View File

@ -1,4 +1,10 @@
/* ympd
/* myMPD
(c) 2018 Juergen Mang <mail@jcgames.de>
This project's homepage is: https://github.com/jcorporation/ympd
myMPD ist fork of:
ympd
(c) 2013-2014 Andrew Karpow <andy@ndyk.de>
This project's homepage is: http://www.ympd.org
@ -63,9 +69,9 @@ int callback_mpd(struct mg_connection *c)
if(cmd_id == -1)
return MG_TRUE;
if(mpd.conn_state != MPD_CONNECTED && cmd_id != MPD_API_SET_MPDHOST &&
cmd_id != MPD_API_GET_MPDHOST && cmd_id != MPD_API_SET_MPDPASS)
return MG_TRUE;
// if(mpd.conn_state != MPD_CONNECTED && cmd_id != MPD_API_SET_MPDHOST &&
// cmd_id != MPD_API_GET_MPDHOST && cmd_id != MPD_API_SET_MPDPASS)
// return MG_TRUE;
switch(cmd_id)
{
@ -263,52 +269,11 @@ out_search:
out_send_message:
free(p_charbuf);
break;
#ifdef WITH_MPD_HOST_CHANGE
/* Commands allowed when disconnected from MPD server */
case MPD_API_SET_MPDHOST:
int_buf = 0;
p_charbuf = strdup(c->content);
if(strcmp(strtok(p_charbuf, ","), "MPD_API_SET_MPDHOST"))
goto out_host_change;
if((int_buf = strtol(strtok(NULL, ","), NULL, 10)) <= 0)
goto out_host_change;
if((token = strtok(NULL, ",")) == NULL)
goto out_host_change;
strncpy(mpd.host, token, sizeof(mpd.host));
mpd.port = int_buf;
mpd.conn_state = MPD_RECONNECT;
free(p_charbuf);
return MG_TRUE;
out_host_change:
free(p_charbuf);
case MPD_API_GET_OPTIONS:
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\":\"mpdoptions\", \"data\": "
"{\"mpdhost\" : \"%s\", \"mpdport\": \"%d\", \"passwort_set\": %s, \"streamport\": \"%d\"}"
"}", mpd.host, mpd.port, mpd.password ? "true" : "false", streamport);
break;
case MPD_API_GET_MPDHOST:
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\":\"mpdhost\", \"data\": "
"{\"host\" : \"%s\", \"port\": \"%d\", \"passwort_set\": %s}"
"}", mpd.host, mpd.port, mpd.password ? "true" : "false");
break;
case MPD_API_SET_MPDPASS:
p_charbuf = strdup(c->content);
if(strcmp(strtok(p_charbuf, ","), "MPD_API_SET_MPDPASS"))
goto out_set_pass;
if((token = strtok(NULL, ",")) == NULL)
goto out_set_pass;
if(mpd.password)
free(mpd.password);
mpd.password = strdup(token);
mpd.conn_state = MPD_RECONNECT;
free(p_charbuf);
return MG_TRUE;
out_set_pass:
free(p_charbuf);
break;
#endif
}
if(mpd.conn_state == MPD_CONNECTED && mpd_connection_get_error(mpd.conn) != MPD_ERROR_SUCCESS)

View File

@ -1,4 +1,10 @@
/* ympd
/* myMPD
(c) 2018 Juergen Mang <mail@jcgames.de>
This project's homepage is: https://github.com/jcorporation/ympd
myMPD ist fork of:
ympd
(c) 2013-2014 Andrew Karpow <andy@ndyk.de>
This project's homepage is: http://www.ympd.org
@ -39,7 +45,6 @@
#define MPD_CMDS(X) \
X(MPD_API_GET_QUEUE) \
X(MPD_API_GET_BROWSE) \
X(MPD_API_GET_MPDHOST) \
X(MPD_API_ADD_TRACK) \
X(MPD_API_ADD_PLAY_TRACK) \
X(MPD_API_ADD_PLAYLIST) \
@ -58,8 +63,6 @@
X(MPD_API_SET_SEEK) \
X(MPD_API_SET_NEXT) \
X(MPD_API_SET_PREV) \
X(MPD_API_SET_MPDHOST) \
X(MPD_API_SET_MPDPASS) \
X(MPD_API_UPDATE_DB) \
X(MPD_API_GET_OUTPUTS) \
X(MPD_API_TOGGLE_OUTPUT) \
@ -67,7 +70,8 @@
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_GET_OPTIONS)
enum mpd_cmd_ids {
MPD_CMDS(GEN_ENUM)
@ -99,6 +103,8 @@ struct t_mpd {
unsigned queue_version;
} mpd;
int streamport;
struct t_mpd_client_session {
int song_id;
unsigned queue_version;

View File

@ -1,191 +0,0 @@
/* ympd
(c) 2013-2014 Andrew Karpow <andy@ndyk.de>
This project's homepage is: http://www.ympd.org
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <getopt.h>
#include <sys/time.h>
#include <pthread.h>
#include "mongoose.h"
#include "http_server.h"
#include "mpd_client.h"
#include "config.h"
extern char *optarg;
int force_exit = 0;
void bye()
{
force_exit = 1;
}
static int server_callback(struct mg_connection *c, enum mg_event ev) {
int result = MG_FALSE;
FILE *fp = NULL;
switch(ev) {
case MG_CLOSE:
mpd_close_handler(c);
return MG_TRUE;
case MG_REQUEST:
if (c->is_websocket) {
c->content[c->content_len] = '\0';
if(c->content_len)
return callback_mpd(c);
else
return MG_TRUE;
} else
return MG_FALSE;
case MG_AUTH:
// no auth for websockets since mobile safari does not support it
if ( (mpd.gpass == NULL) || (c->is_websocket) || ((mpd.local_port > 0) && (c->local_port == mpd.local_port)) )
return MG_TRUE;
else {
if ( (fp = fopen(mpd.gpass, "r")) != NULL ) {
result = mg_authorize_digest(c, fp);
fclose(fp);
}
}
return result;
default:
return MG_FALSE;
}
}
int main(int argc, char **argv)
{
int n, option_index = 0;
struct mg_server *server = mg_create_server(NULL, server_callback);
unsigned int current_timer = 0, last_timer = 0;
char *run_as_user = NULL;
char const *error_msg = NULL;
char *webport = "8080";
atexit(bye);
mg_set_option(server, "document_root", SRC_PATH);
mg_set_option(server, "auth_domain", "mympd");
mpd.port = 6600;
mpd.local_port = 0;
mpd.gpass = NULL;
strcpy(mpd.host, "127.0.0.1");
static struct option long_options[] = {
{"digest", required_argument, 0, 'D'},
{"host", required_argument, 0, 'h'},
{"port", required_argument, 0, 'p'},
{"localport", required_argument, 0, 'l'},
{"webport", required_argument, 0, 'w'},
{"user", required_argument, 0, 'u'},
{"version", no_argument, 0, 'v'},
{"help", no_argument, 0, 0 },
{"mpdpass", required_argument, 0, 'm'},
{0, 0, 0, 0 }
};
while((n = getopt_long(argc, argv, "D:h:p:l:w:u:d:v:m",
long_options, &option_index)) != -1) {
switch (n) {
case 'D':
mpd.gpass = strdup(optarg);
break;
case 'h':
strncpy(mpd.host, optarg, sizeof(mpd.host));
break;
case 'p':
mpd.port = atoi(optarg);
break;
case 'l':
mpd.local_port = atoi(optarg);
break;
case 'w':
webport = strdup(optarg);
break;
case 'u':
run_as_user = strdup(optarg);
break;
case 'm':
if (strlen(optarg) > 0)
mpd.password = strdup(optarg);
break;
case 'v':
fprintf(stdout, "ympd %d.%d.%d\n"
"Copyright (C) 2014 Andrew Karpow <andy@ndyk.de>\n"
"built " __DATE__ " "__TIME__ " ("__VERSION__")\n",
YMPD_VERSION_MAJOR, YMPD_VERSION_MINOR, YMPD_VERSION_PATCH);
return EXIT_SUCCESS;
break;
default:
fprintf(stderr, "Usage: %s [OPTION]...\n\n"
" -D, --digest <htdigest>\tpath to htdigest file for authorization\n"
" \t(realm ympd) [no authorization]\n"
" -h, --host <host>\t\tconnect to mpd at host [localhost]\n"
" -p, --port <port>\t\tconnect to mpd at port [6600]\n"
" -l, --localport <port>\t\tskip authorization for local port\n"
" -w, --webport [ip:]<port>\tlisten interface/port for webserver [8080]\n"
" -u, --user <username>\t\tdrop priviliges to user after socket bind\n"
" -v, --version\t\t\tget version\n"
" -m, --mpdpass <password>\tspecifies the password to use when connecting to mpd\n"
" --help\t\t\t\tthis help\n"
, argv[0]);
return EXIT_FAILURE;
}
if(error_msg)
{
fprintf(stderr, "Mongoose error: %s\n", error_msg);
return EXIT_FAILURE;
}
}
error_msg = mg_set_option(server, "listening_port", webport);
if(error_msg) {
fprintf(stderr, "Mongoose error: %s\n", error_msg);
return EXIT_FAILURE;
}
/* drop privilges at last to ensure proper port binding */
if(run_as_user != NULL) {
error_msg = mg_set_option(server, "run_as_user", run_as_user);
free(run_as_user);
if(error_msg)
{
fprintf(stderr, "Mongoose error: %s\n", error_msg);
return EXIT_FAILURE;
}
}
while (!force_exit) {
mg_poll_server(server, 200);
current_timer = time(NULL);
if(current_timer - last_timer)
{
last_timer = current_timer;
mpd_poll(server);
}
}
mpd_disconnect();
mg_destroy_server(&server);
return EXIT_SUCCESS;
}