/* * Cantata * * Copyright (c) 2011-2013 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 "config.h" #include "httpsocket.h" #include "httpserver.h" #include "settings.h" #include #include #include #include #include #include #if QT_VERSION >= 0x050000 #include #endif #ifdef ENABLE_KDE_SUPPORT #include #endif #include #ifdef TAGLIB_FOUND #include #include #include #include #endif #ifdef CDDB_PLAYBACK #include "cdparanoia.h" #include "extractjob.h" #endif static QString detectMimeType(const QString &file) { #ifdef ENABLE_KDE_SUPPORT QString km=KMimeType::findByPath(file)->name(); if (!km.isEmpty() && (km.startsWith("audio/") || km.startsWith("application/"))) { return QLatin1String("audio/x-ape")==km ? QLatin1String("audio/x-monkeys-audio") : km; } #endif QString suffix = QFileInfo(file).suffix().toLower(); if (suffix == QLatin1String("mp3")) { return QLatin1String("audio/mpeg"); } #ifdef TAGLIB_FOUND if (suffix == QLatin1String("ogg")) { #ifdef Q_OS_WIN32 const wchar_t *encodedName = reinterpret_cast< const wchar_t * >(file.utf16()); #elif defined COMPLEX_TAGLIB_FILENAME const wchar_t *encodedName = reinterpret_cast< const wchar_t * >(file.utf16()); #else QByteArray fileName = QFile::encodeName(file); const char *encodedName = fileName.constData(); // valid as long as fileName exists #endif QString mime; TagLib::File *result = new TagLib::Ogg::Vorbis::File(encodedName, false, TagLib::AudioProperties::Fast); if (result->isValid()) { mime=QLatin1String("audio/x-vorbis+ogg"); } delete result; if (mime.isEmpty()) { result = new TagLib::Ogg::FLAC::File(encodedName, false, TagLib::AudioProperties::Fast); if (result->isValid()) { mime=QLatin1String("audio/x-flac+ogg"); } delete result; } if (mime.isEmpty()) { result = new TagLib::TrueAudio::File(encodedName, false, TagLib::AudioProperties::Fast); if (result->isValid()) { mime=QLatin1String("audio/x-speex+ogg"); } delete result; } return QLatin1String("audio/ogg"); } #endif else if (suffix == QLatin1String("flac")) { return QLatin1String("audio/x-flac"); } else if (suffix == QLatin1String("wma")) { return QLatin1String("audio/x-ms-wma"); } else if (suffix == QLatin1String("m4a") || suffix == QLatin1String("m4b") || suffix == QLatin1String("m4p") || suffix == QLatin1String("mp4")) { return QLatin1String("audio/mp4"); } else if (suffix == QLatin1String("wav")) { return QLatin1String("audio/x-wav"); } else if (suffix == QLatin1String("wv") || suffix == QLatin1String("wvp")) { return QLatin1String("audio/x-wavpack"); } else if (suffix == QLatin1String("ape")) { return QLatin1String("audio/x-monkeys-audio"); // "audio/x-ape"; } else if (suffix == QLatin1String("spx")) { return QLatin1String("audio/x-speex"); } else if (suffix == QLatin1String("tta")) { return QLatin1String("audio/x-tta"); } else if (suffix == QLatin1String("aiff") || suffix == QLatin1String("aif") || suffix == QLatin1String("aifc")) { return QLatin1String("audio/x-aiff"); } else if (suffix == QLatin1String("mpc") || suffix == QLatin1String("mpp") || suffix == QLatin1String("mp+")) { return QLatin1String("audio/x-musepack"); } else if (suffix == QLatin1String("dff")) { return QLatin1String("application/x-dff"); } else if (suffix == QLatin1String("dsf")) { return QLatin1String("application/x-dsf"); } return QString(); } static void writeMimeType(const QString &mimeType, QTcpSocket *socket) { if (!mimeType.isEmpty()) { QTextStream os(socket); os.setAutoDetectUnicode(true); os << "HTTP/1.0 200 OK\r\nContent-Type: " << mimeType << "\r\n\r\n"; } } // static int level(const QString &s) // { // return QLatin1String("Link-local")==s // ? 1 // : QLatin1String("Site-local")==s // ? 2 // : QLatin1String("Global")==s // ? 3 // : 0; // } HttpSocket::HttpSocket(const QString &addr, quint16 p) : QTcpServer(0) , cfgAddr(addr) , terminated(false) { QHostAddress a; if (!addr.isEmpty()) { a=QHostAddress(addr); if (a.isNull()) { QString ifaceName=addr; // bool ipV4=true; // // if (ifaceName.endsWith("::6")) { // ifaceName=ifaceName.left(ifaceName.length()-3); // ipV4=false; // } QNetworkInterface iface=QNetworkInterface::interfaceFromName(ifaceName); if (iface.isValid()) { QList addresses=iface.addressEntries(); // int ip6Scope=-1; foreach (const QNetworkAddressEntry &addr, addresses) { QHostAddress ha=addr.ip(); if (QAbstractSocket::IPv4Protocol==ha.protocol()) { // if ((ipV4 && QAbstractSocket::IPv4Protocol==ha.protocol()) || (!ipV4 && QAbstractSocket::IPv6Protocol==ha.protocol())) { // if (ipV4) { a=ha; break; // } else { // int scope=level(a.scopeId()); // if (scope>ip6Scope) { // ip6Scope=scope; // a=ha; // } // } } } } } } listen(a.isNull() ? QHostAddress::LocalHost : a, p); } void HttpSocket::terminate() { terminated=true; deleteLater(); } void HttpSocket::incomingConnection(int socket) { QTcpSocket *s = new QTcpSocket(this); connect(s, SIGNAL(readyRead()), this, SLOT(readClient())); connect(s, SIGNAL(disconnected()), this, SLOT(discardClient())); s->setSocketDescriptor(socket); } int getSep(const QByteArray &a, int pos) { for (int i=pos+1; i split(const QByteArray &a) { QList rv; int lastPos=-1; for (;;) { int pos=getSep(a, lastPos); if (pos==(lastPos+1)) { lastPos++; } else if (pos>-1) { lastPos++; rv.append(a.mid(lastPos, pos-lastPos)); lastPos=pos; } else { lastPos++; rv.append(a.mid(lastPos)); break; } } return rv; } void HttpSocket::readClient() { if (terminated) { return; } QTcpSocket *socket = (QTcpSocket*)sender(); if (socket->canReadLine()) { QList tokens = split(socket->readLine()); // QRegExp("[ \r\n][ \r\n]*")); if (tokens.length()>=2 && "GET"==tokens[0]) { #if QT_VERSION < 0x050000 QUrl url(QUrl::fromEncoded(tokens[1])); QUrl &q=url; #else QUrl url(QUrl::fromEncoded(tokens[1])); QUrlQuery q(url); #endif bool ok=false; if (q.hasQueryItem("cantata")) { #ifdef CDDB_PLAYBACK // Not working :-( No sound is played, plus thi seems to continue until Cantata is stopped?? Song song=HttpServer::self()->decodeUrl(url); if (song.file.startsWith(QLatin1String("cdda:/"))) { QStringList parts=song.file.split("/", QString::SkipEmptyParts); if (parts.length()>=3) { QString dev=QLatin1Char('/')+parts.at(1)+QLatin1Char('/')+parts.at(2); CdParanoia cdparanoia(dev, false, false); if (cdparanoia) { int firstSector = cdparanoia.firstSectorOfTrack(song.id); int lastSector = cdparanoia.lastSectorOfTrack(song.id); int count = 0; cdparanoia.seek(firstSector, SEEK_SET); ok=true; writeMimeType(QLatin1String("audio/x-wav"), socket); ExtractJob::writeWavHeader(*socket); while (!terminated && (firstSector+count) <= lastSector) { qint16 *buf = cdparanoia.read(); if (!buf) { ok=false; break; } char *buffer=(char *)buf; qint64 writePos=0; do { qint64 bytesWritten = socket->write(&buffer[writePos], CD_FRAMESIZE_RAW - writePos); if (-1==bytesWritten) { ok=false; break; } writePos+=bytesWritten; } while (!terminated && writePoswrite(&buffer[writePos], bytesRead - writePos); if (terminated) { break; } if (-1==bytesWritten) { ok=false; break; } writePos+=bytesWritten; } while (writePosNothing to see here\n"; } socket->close(); if (QTcpSocket::UnconnectedState==socket->state()) { delete socket; } } } } void HttpSocket::discardClient() { QTcpSocket *socket = (QTcpSocket*)sender(); socket->deleteLater(); }