/* * 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 #include #include #include #include #include #ifdef ENABLE_KDE_SUPPORT #include #endif #include #ifdef TAGLIB_FOUND #include #include #include #include #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 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]) { QUrl url(QUrl::fromEncoded(tokens[1])); bool ok=false; if (url.hasQueryItem("cantata")) { QFile f(url.path()); if (f.open(QIODevice::ReadOnly)) { QString mimeType=detectMimeType(url.path()); if (!mimeType.isEmpty()) { QTextStream os(socket); os.setAutoDetectUnicode(true); os << "HTTP/1.0 200 OK\r\nContent-Type: " << mimeType << "\r\n\r\n"; } ok=true; static const int constChunkSize=8192; char buffer[constChunkSize]; qint64 totalBytes = f.size(); qint64 readPos = 0; qint64 bytesRead = 0; do { if (terminated) { break; } bytesRead = f.read(buffer, constChunkSize); readPos+=bytesRead; if (bytesRead<0 || terminated) { break; } qint64 writePos=0; do { qint64 bytesWritten = socket->write(&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(); }