diff --git a/CMakeLists.txt b/CMakeLists.txt index a4a515f6f..baa1760af 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -504,9 +504,8 @@ elseif (NOT ENABLE_UBUNTU) endif (WIN32) if (ENABLE_HTTP_SERVER) - set(CANTATA_SRCS ${CANTATA_SRCS} http/httpserversettings.cpp http/httpsocket.cpp) + set(CANTATA_SRCS ${CANTATA_SRCS} http/httpsocket.cpp) set(CANTATA_MOC_HDRS ${CANTATA_MOC_HDRS} http/httpserver.h http/httpsocket.h) - set(CANTATA_UIS ${CANTATA_UIS} http/httpserversettings.ui) endif (ENABLE_HTTP_SERVER) if (ENABLE_MODEL_TEST AND NOT ENABLE_KDE AND NOT ENABLE_QT5) diff --git a/ChangeLog b/ChangeLog index 00349da03..637291ca9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -63,6 +63,8 @@ 45. New options to add songs to play queue - 'add and play', 'append and play', and 'insert after current' 46. Custom actions. +47. Set HTTP server to listen on all addresses, but use IP address of socket + connected to MPD for HTTP URLs. 1.5.2 ----- diff --git a/gui/preferencesdialog.cpp b/gui/preferencesdialog.cpp index 691d5d009..193ce0058 100644 --- a/gui/preferencesdialog.cpp +++ b/gui/preferencesdialog.cpp @@ -28,9 +28,6 @@ #include "serversettings.h" #include "playbacksettings.h" #include "filesettings.h" -#ifdef ENABLE_HTTP_SERVER -#include "http/httpserversettings.h" -#endif #include "context/contextsettings.h" #include "cachesettings.h" #include "customactionssettings.h" @@ -84,20 +81,6 @@ PreferencesDialog::PreferencesDialog(QWidget *parent) addPage(QLatin1String("interface"), interface, i18n("Interface"), Icon("preferences-other"), i18n("Interface Settings")); addPage(QLatin1String("info"), context, i18n("Info"), Icons::self()->contextIcon, i18n("Info View Settings")); addPage(QLatin1String("scrobbling"), scrobbling, i18n("Scrobbling"), Icons::self()->lastFmIcon, i18n("Scrobbling Settings")); - #ifdef ENABLE_HTTP_SERVER - http = new HttpServerSettings(0); - if (http->haveMultipleInterfaces()) { - http->load(); - Icon icon("network-server"); - if (icon.isNull()) { - icon=Icons::self()->streamIcon; - } - addPage(QLatin1String("http"), http, i18n("HTTP Server"), icon, i18n("HTTP Server Settings")); - } else { - http->deleteLater(); - http=0; - } - #endif #if defined CDDB_FOUND || defined MUSICBRAINZ5_FOUND audiocd = new AudioCdSettings(0); audiocd->load(); @@ -155,11 +138,6 @@ void PreferencesDialog::writeSettings() playback->save(); files->save(); interface->save(); - #ifdef ENABLE_HTTP_SERVER - if (http) { - http->save(); - } - #endif #ifndef ENABLE_KDE_SUPPORT #ifdef ENABLE_PROXY_CONFIG proxy->save(); diff --git a/gui/preferencesdialog.h b/gui/preferencesdialog.h index 61c998ecf..d7f9437ea 100644 --- a/gui/preferencesdialog.h +++ b/gui/preferencesdialog.h @@ -37,7 +37,6 @@ class PlaybackSettings; class FileSettings; class InterfaceSettings; class ContextSettings; -class HttpServerSettings; struct MPDConnectionDetails; class CacheSettings; #if defined CDDB_FOUND || defined MUSICBRAINZ5_FOUND @@ -79,9 +78,6 @@ private: FileSettings *files; InterfaceSettings *interface; ContextSettings *context; - #ifdef ENABLE_HTTP_SERVER - HttpServerSettings *http; - #endif #ifdef ENABLE_PROXY_CONFIG ProxySettings *proxy; #endif diff --git a/http/httpserver.cpp b/http/httpserver.cpp index cd2e5ce22..cccc0c925 100644 --- a/http/httpserver.cpp +++ b/http/httpserver.cpp @@ -29,6 +29,7 @@ #include "gui/settings.h" #include "support/thread.h" #include "support/globalstatic.h" +#include "mpd-interface/mpdconnection.h" #include #include #include @@ -70,6 +71,7 @@ HttpServer::HttpServer() connect(MPDConnection::self(), SIGNAL(cantataStreams(QList,bool)), this, SLOT(cantataStreams(QList,bool))); connect(MPDConnection::self(), SIGNAL(cantataStreams(QStringList)), this, SLOT(cantataStreams(QStringList))); connect(MPDConnection::self(), SIGNAL(removedIds(QSet)), this, SLOT(removedIds(QSet))); + connect(MPDConnection::self(), SIGNAL(ifaceIp(QString)), this, SLOT(ifaceIp(QString))); } bool HttpServer::start() @@ -128,14 +130,29 @@ void HttpServer::readConfig() } } +static inline QString serverUrl(const QString &ip, quint16 port) +{ + return QLatin1String("http://")+ip+QLatin1Char(':')+QString::number(port); +} + QString HttpServer::address() const { - return socket ? socket->urlAddress() : QLatin1String("http://127.0.0.1:*"); + return socket ? serverUrl(currentIfaceIp, socket->serverPort()) : QLatin1String("http://127.0.0.1:*"); } bool HttpServer::isOurs(const QString &url) const { - return 0!=socket ? url.startsWith(address()+"/") : false; + if (0==socket || !url.startsWith(QLatin1String("http://"))) { + return false; + } + + foreach (const QString &ip, ipAddresses) { + if (url.startsWith(serverUrl(ip, socket->serverPort()))) { + return true; + } + } + + return false; } QByteArray HttpServer::encodeUrl(const Song &s) @@ -152,7 +169,7 @@ QByteArray HttpServer::encodeUrl(const Song &s) QUrlQuery query; #endif url.setScheme("http"); - url.setHost(socket->address()); + url.setHost(currentIfaceIp); url.setPort(socket->serverPort()); url.setPath(s.file); if (!s.album.isEmpty()) { @@ -348,4 +365,13 @@ void HttpServer::removedIds(const QSet &ids) } } +void HttpServer::ifaceIp(const QString &ip) +{ + if (ip.isEmpty()) { + return; + } + currentIfaceIp=ip; + ipAddresses.insert(ip); +} + #endif diff --git a/http/httpserver.h b/http/httpserver.h index a12dcc884..9422f6e8d 100644 --- a/http/httpserver.h +++ b/http/httpserver.h @@ -83,6 +83,7 @@ private Q_SLOTS: void cantataStreams(const QStringList &files); void cantataStreams(const QList &songs, bool isUpdate); void removedIds(const QSet &ids); + void ifaceIp(const QString &ip); Q_SIGNALS: void terminateSocket(); @@ -94,6 +95,9 @@ private: QSet streamIds; // Currently playing MPD stream IDs QTimer *closeTimer; + + QString currentIfaceIp; + QSet ipAddresses; #endif }; diff --git a/http/httpserversettings.cpp b/http/httpserversettings.cpp deleted file mode 100644 index e5fabeabd..000000000 --- a/http/httpserversettings.cpp +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Cantata - * - * Copyright (c) 2011-2015 Craig Drummond - * - * ---- - * - * 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; either version 2 of the License, or - * (at your option) any later version. - * - * 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; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#include "httpserversettings.h" -#include "gui/settings.h" -#include "support/localize.h" -#include "httpserver.h" -#include -#include - -static int isIfaceType(const QNetworkInterface &iface, const QString &prefix) -{ - return iface.name().length()>prefix.length() && iface.name().startsWith(prefix) && iface.name()[prefix.length()].isDigit(); -} - -static QString details(const QNetworkInterface &iface) -{ - QString d=iface.humanReadableName(); - QList addresses=iface.addressEntries(); - if (!addresses.isEmpty()) { - QString ipV4, ipV6; - foreach (const QNetworkAddressEntry &addr, addresses) { - QString ip=addr.ip().toString(); - if (ip.isEmpty()) { - continue; - } - if (ip.endsWith(QLatin1Char('%')+d)) { - ip=ip.left(ip.length()-(d.length()+1)); - } - if (QAbstractSocket::IPv4Protocol==addr.ip().protocol()) { - ipV4=ip; - } else if (QAbstractSocket::IPv6Protocol==addr.ip().protocol()) { - ipV6=ip; - } - } - - QString ipAddr; - if (!ipV4.isEmpty()) { - ipAddr=ipV4; - } else if (!ipV6.isEmpty()) { - ipAddr=ipV6; - } - d+=QLatin1String(" - ")+ipAddr; - } - return d; -} - -static QString displayName(const QNetworkInterface &iface) -{ - if (iface.name()==QLatin1String("lo") || isIfaceType(iface , "lo")) { - return i18n("Local loopback (%1)", details(iface)); - } - if (isIfaceType(iface, "eth")) { - return i18n("Wired (%1)", details(iface)); - } - if (isIfaceType(iface, "wlan")) { - return i18n("Wireless (%1)", details(iface)); - } - return details(iface); -} - -static void initInterfaces(QComboBox *combo) -{ - combo->addItem(i18n("First active interface"), QString()); - QList ifaces=QNetworkInterface::allInterfaces(); - foreach (const QNetworkInterface &iface, ifaces) { - if (iface.flags()&QNetworkInterface::IsUp && !isIfaceType(iface, "vboxnet") && !isIfaceType(iface, "vmnet")) { - combo->addItem(displayName(iface), iface.name()); - } - } -} - -HttpServerSettings::HttpServerSettings(QWidget *p) - : QWidget(p) -{ - setupUi(this); - initInterfaces(httpInterface); - - #ifdef Q_OS_MAC - expandingSpacer->changeSize(0, 0, QSizePolicy::Fixed, QSizePolicy::Fixed); - #endif -} - -void HttpServerSettings::load() -{ - QString iface=Settings::self()->httpInterface(); - for (int i=0; icount(); ++i) { - if (httpInterface->itemData(i)==iface) { - httpInterface->setCurrentIndex(i); - break; - } - } -} - -bool HttpServerSettings::haveMultipleInterfaces() const -{ - return httpInterface->count()>3; -} - -void HttpServerSettings::save() -{ - Settings::self()->saveHttpInterface(httpInterface->itemData(httpInterface->currentIndex()).toString()); - HttpServer::self()->readConfig(); -} diff --git a/http/httpserversettings.h b/http/httpserversettings.h deleted file mode 100644 index fb13240a5..000000000 --- a/http/httpserversettings.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Cantata - * - * Copyright (c) 2011-2015 Craig Drummond - * - * ---- - * - * 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; either version 2 of the License, or - * (at your option) any later version. - * - * 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; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef HTTPSERVERSETTINGS_H -#define HTTPSERVERSETTINGS_H - -#include "ui_httpserversettings.h" - -class HttpServerSettings : public QWidget, private Ui::HttpServerSettings -{ -public: - HttpServerSettings(QWidget *p); - virtual ~HttpServerSettings() { } - - void load(); - void save(); - bool haveMultipleInterfaces() const; -}; - -#endif diff --git a/http/httpserversettings.ui b/http/httpserversettings.ui deleted file mode 100644 index ea854184c..000000000 --- a/http/httpserversettings.ui +++ /dev/null @@ -1,85 +0,0 @@ - - - HttpServerSettings - - - - 0 - 0 - 1406 - 274 - - - - - QFormLayout::ExpandingFieldsGrow - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Network &interface: - - - httpInterface - - - - - - - - - - MPD usually only plays songs that are stored within its folders. Cantata contains a minimal HTTP server that can be used to serve other files to MPD. This, however, will only work whilst Cantata is running. - - - - - - - If you choose 'Local loopback' for 'Network interface', then MPD <b>must</b> also be on this computer. - - - - - - - Qt::Vertical - - - - 20 - 0 - - - - - - - - - BuddyLabel - QLabel -
support/buddylabel.h
-
- - NoteLabel - QLabel -
widgets/notelabel.h
-
-
- - -
diff --git a/http/httpsocket.cpp b/http/httpsocket.cpp index 77f352bff..9795c64b6 100644 --- a/http/httpsocket.cpp +++ b/http/httpsocket.cpp @@ -30,7 +30,6 @@ #include "devices/extractjob.h" #endif #include -#include #include #include #include @@ -141,18 +140,6 @@ static void writeMimeType(const QString &mimeType, QTcpSocket *socket, qint32 fr } } -static QHostAddress getAddress(const QNetworkInterface &iface) -{ - QList addresses=iface.addressEntries(); - foreach (const QNetworkAddressEntry &addr, addresses) { - QHostAddress hostAddress=addr.ip(); - if (QAbstractSocket::IPv4Protocol==hostAddress.protocol()) { - return hostAddress; - } - } - return QHostAddress(); -} - static int getSep(const QByteArray &a, int pos) { for (int i=pos+1; i ifaces=QNetworkInterface::allInterfaces(); - foreach (const QNetworkInterface &iface, ifaces) { - if (iface.flags()&QNetworkInterface::IsUp) { - if (QLatin1String("lo")==iface.name()) { - loIface=iface; - } else { - a=getAddress(iface); - if (!a.isNull()) { - break; - } - } - } - } - if (a.isNull() && !loIface.isValid()) { - // Get address of 'loopback' interface... - a=getAddress(loIface); - } - } - - if (!openPort(a, port)) { - openPort(a, 0); - } - - if (isListening() && ifaceAddress.isEmpty()) { - ifaceAddress=QLatin1String("127.0.0.1"); - } - setUrlAddress(); - - DBUG << isListening() << urlAddr; + DBUG << isListening(); connect(MPDConnection::self(), SIGNAL(socketAddress(QString)), this, SLOT(mpdAddress(QString))); connect(MPDConnection::self(), SIGNAL(cantataStreams(QList,bool)), this, SLOT(cantataStreams(QList,bool))); @@ -269,26 +219,13 @@ HttpSocket::HttpSocket(const QString &iface, quint16 port) connect(this, SIGNAL(newConnection()), SLOT(handleNewConnection())); } -bool HttpSocket::openPort(const QHostAddress &a, quint16 p) +bool HttpSocket::openPort(quint16 p) { - if (!a.isNull() && listen(a, p)) { - ifaceAddress=a.toString(); - return true; - } - // Listen probably failed due to proxy, so unset and try again! setProxy(QNetworkProxy::NoProxy); - if (!a.isNull() && listen(a, p)) { - ifaceAddress=a.toString(); - return true; - } - if (listen(QHostAddress::Any, p)) { - ifaceAddress=serverAddress().toString(); return true; } - if (listen(QHostAddress::LocalHost, p)) { - ifaceAddress=QLatin1String("127.0.0.1"); return true; } @@ -337,6 +274,7 @@ void HttpSocket::readClient() } QString peer=socket->peerAddress().toString(); + QString ifaceAddress=serverAddress().toString(); bool hostOk=peer==ifaceAddress || peer==mpdAddr || peer==QLatin1String("127.0.0.1"); DBUG << "peer:" << peer << "mpd:" << mpdAddr << "iface:" << ifaceAddress << "ok:" << hostOk; @@ -569,16 +507,3 @@ bool HttpSocket::write(QTcpSocket *socket, char *buffer, qint32 bytesRead, bool } return true; } - -void HttpSocket::setUrlAddress() -{ - if (ifaceAddress.isEmpty()) { - ifaceAddress=QString(); - } else { - QUrl url; - url.setScheme("http"); - url.setHost(ifaceAddress); - url.setPort(serverPort()); - urlAddr=url.toString(); - } -} diff --git a/http/httpsocket.h b/http/httpsocket.h index 817b0b9b7..ee8f5c61d 100644 --- a/http/httpsocket.h +++ b/http/httpsocket.h @@ -41,16 +41,15 @@ public: HttpSocket(const QString &iface, quint16 port); virtual ~HttpSocket() { } - QString address() const { return ifaceAddress; } QString configuredInterface() { return cfgInterface; } - QString urlAddress() const { return urlAddr; } + quint16 boundPort(); public Q_SLOTS: void terminate(); void mpdAddress(const QString &a); private: - bool openPort(const QHostAddress &a, quint16 p); + bool openPort(quint16 p); bool isCantataStream(const QString &file) const; void sendErrorResponse(QTcpSocket *socket, int code); @@ -70,8 +69,6 @@ private: QSet newlyAddedFiles; // Holds cantata strema filenames as added to MPD via "add" QMap streamIds; // Maps MPD playqueue song ID to fileName QString cfgInterface; - QString ifaceAddress; - QString urlAddr; QString mpdAddr; bool terminated; }; diff --git a/mpd-interface/mpdconnection.cpp b/mpd-interface/mpdconnection.cpp index ac9d1d362..434041506 100644 --- a/mpd-interface/mpdconnection.cpp +++ b/mpd-interface/mpdconnection.cpp @@ -45,6 +45,7 @@ #include #include #include +#include #include "support/thread.h" #include "cuefile.h" #if defined Q_OS_LINUX && defined QT_QTDBUS_FOUND @@ -565,6 +566,7 @@ void MPDConnection::setDetails(const MPDConnectionDetails &d) getUrlHandlers(); getTagTypes(); getStickerSupport(); + determineIfaceIp(); emit stateChanged(true); break; default: @@ -2169,6 +2171,24 @@ void MPDConnection::clearError() #endif } +void MPDConnection::determineIfaceIp() +{ + static const QLatin1String ip4Local("127.0.0.1"); + if (!details.isLocal() && !details.hostname.isEmpty() && ip4Local!=details.hostname && QLatin1String("localhost")!=details.hostname) { + QUdpSocket testSocket(this); + testSocket.connectToHost(details.hostname, 1, QIODevice::ReadOnly); + QString addr=testSocket.localAddress().toString(); + testSocket.close(); + if (!addr.isEmpty()) { + DBUG << addr; + emit ifaceIp(addr); + return; + } + } + DBUG << ip4Local; + emit ifaceIp(ip4Local); +} + MpdSocket::MpdSocket(QObject *parent) : QObject(parent) , tcp(0) diff --git a/mpd-interface/mpdconnection.h b/mpd-interface/mpdconnection.h index c7a52fc7a..f60f67355 100644 --- a/mpd-interface/mpdconnection.h +++ b/mpd-interface/mpdconnection.h @@ -382,6 +382,8 @@ Q_SIGNALS: void rating(const QString &file, quint8 val); void stickerDbChanged(); + void ifaceIp(const QString &addr); + private Q_SLOTS: void idleDataReady(); void onSocketStateChanged(QAbstractSocket::SocketState socketState); @@ -421,6 +423,7 @@ private: void getStickerSupport(); void playFirstTrack(bool emitErrors); void seek(bool fwd); + void determineIfaceIp(); private: Thread *thread;