/thirdparty/liblastfm2/src/ws/ws.cpp
http://github.com/tomahawk-player/tomahawk · C++ · 237 lines · 161 code · 39 blank · 37 comment · 22 complexity · b4eddc34bf6aeed8f502602978d1bcdf MD5 · raw file
- /*
- Copyright 2009 Last.fm Ltd.
- - Primarily authored by Max Howell, Jono Cole and Doug Mansell
- This file is part of liblastfm.
- liblastfm 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 3 of the License, or
- (at your option) any later version.
- liblastfm 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 liblastfm. If not, see <http://www.gnu.org/licenses/>.
- */
- #include "ws.h"
- #include "../core/misc.h"
- #include "NetworkAccessManager.h"
- #include <QCoreApplication>
- #include <QDomDocument>
- #include <QDomElement>
- #include <QLocale>
- #include <QStringList>
- #include <QUrl>
- #include <QThread>
- #include <QMutex>
- static QMap< QThread*, QNetworkAccessManager* > threadNamHash;
- static QSet< QThread* > ourNamSet;
- static QMutex namAccessMutex;
- QString
- lastfm::ws::host()
- {
- QStringList const args = QCoreApplication::arguments();
- if (args.contains( "--debug"))
- return "ws.staging.audioscrobbler.com";
- int const n = args.indexOf( "--host" );
- if (n != -1 && args.count() > n+1)
- return args[n+1];
- return LASTFM_WS_HOSTNAME;
- }
- static QUrl baseUrl()
- {
- QUrl url;
- url.setScheme( "http" );
- url.setHost( lastfm::ws::host() );
- url.setEncodedPath( "/2.0/" );
- return url;
- }
- static QString iso639()
- {
- return QLocale().name().left( 2 ).toLower();
- }
- void autograph( QMap<QString, QString>& params )
- {
- params["api_key"] = lastfm::ws::ApiKey;
- params["lang"] = iso639();
- }
- static void sign( QMap<QString, QString>& params, bool sk = true )
- {
- autograph( params );
- // it's allowed for sk to be null if we this is an auth call for instance
- if (sk && lastfm::ws::SessionKey.size())
- params["sk"] = lastfm::ws::SessionKey;
- QString s;
- QMapIterator<QString, QString> i( params );
- while (i.hasNext()) {
- i.next();
- s += i.key() + i.value();
- }
- s += lastfm::ws::SharedSecret;
- params["api_sig"] = lastfm::md5( s.toUtf8() );
- }
- QUrl
- lastfm::ws::url( QMap<QString, QString> params )
- {
- sign( params );
- QUrl url = ::baseUrl();
- // Qt setQueryItems doesn't encode a bunch of stuff, so we do it manually
- QMapIterator<QString, QString> i( params );
- while (i.hasNext()) {
- i.next();
- QByteArray const key = QUrl::toPercentEncoding( i.key() );
- QByteArray const value = QUrl::toPercentEncoding( i.value() );
- url.addEncodedQueryItem( key, value );
- }
- return url;
- }
- QNetworkReply*
- lastfm::ws::get( QMap<QString, QString> params )
- {
- return nam()->get( QNetworkRequest( url( params ) ) );
- }
- QNetworkReply*
- lastfm::ws::post( QMap<QString, QString> params, bool sk )
- {
- sign( params, sk );
- QByteArray query;
- QMapIterator<QString, QString> i( params );
- while (i.hasNext()) {
- i.next();
- query += QUrl::toPercentEncoding( i.key() )
- + '='
- + QUrl::toPercentEncoding( i.value() )
- + '&';
- }
- QNetworkRequest req( baseUrl() );
- req.setHeader( QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded" );
- return nam()->post( req, query );
- }
- QNetworkAccessManager*
- lastfm::nam()
- {
- QMutexLocker l( &namAccessMutex );
- QThread* thread = QThread::currentThread();
- if ( !threadNamHash.contains( thread ) )
- {
- NetworkAccessManager* newNam = new NetworkAccessManager();
- threadNamHash[thread] = newNam;
- ourNamSet.insert( thread );
- return newNam;
- }
- return threadNamHash[thread];
- }
- void
- lastfm::setNetworkAccessManager( QNetworkAccessManager* nam )
- {
- if ( !nam )
- return;
- QMutexLocker l( &namAccessMutex );
- QThread* thread = QThread::currentThread();
- QNetworkAccessManager* oldNam = 0;
- if ( threadNamHash.contains( thread ) && ourNamSet.contains( thread ) )
- oldNam = threadNamHash[thread];
- if ( oldNam == nam )
- {
- // If we're being passed back our own NAM, assume they want to
- // ensure that we don't delete it out from under them
- ourNamSet.remove( thread );
- return;
- }
- threadNamHash[thread] = nam;
- ourNamSet.remove( thread );
- if ( oldNam )
- delete oldNam;
- }
- /** This useful function, fromHttpDate, comes from QNetworkHeadersPrivate
- * in qnetworkrequest.cpp. Qt copyright and license apply. */
- static QDateTime QByteArrayToHttpDate(const QByteArray &value)
- {
- // HTTP dates have three possible formats:
- // RFC 1123/822 - ddd, dd MMM yyyy hh:mm:ss "GMT"
- // RFC 850 - dddd, dd-MMM-yy hh:mm:ss "GMT"
- // ANSI C's asctime - ddd MMM d hh:mm:ss yyyy
- // We only handle them exactly. If they deviate, we bail out.
- int pos = value.indexOf(',');
- QDateTime dt;
- if (pos == -1) {
- // no comma -> asctime(3) format
- dt = QDateTime::fromString(QString::fromLatin1(value), Qt::TextDate);
- } else {
- // eat the weekday, the comma and the space following it
- QString sansWeekday = QString::fromLatin1(value.constData() + pos + 2);
- QLocale c = QLocale::c();
- if (pos == 3)
- // must be RFC 1123 date
- dt = c.toDateTime(sansWeekday, QLatin1String("dd MMM yyyy hh:mm:ss 'GMT"));
- else
- // must be RFC 850 date
- dt = c.toDateTime(sansWeekday, QLatin1String("dd-MMM-yy hh:mm:ss 'GMT'"));
- }
- if (dt.isValid())
- dt.setTimeSpec(Qt::UTC);
- return dt;
- }
- QDateTime
- lastfm::ws::expires( QNetworkReply* reply )
- {
- return QByteArrayToHttpDate( reply->rawHeader( "Expires" ) );
- }
- namespace lastfm
- {
- namespace ws
- {
- QString SessionKey;
- QString Username;
- /** we leave these unset as you can't use the webservices without them
- * so lets make the programmer aware of it during testing by crashing */
- const char* SharedSecret;
- const char* ApiKey;
- /** if this is found set to "" we conjure ourselves a suitable one */
- const char* UserAgent = 0;
- }
- }
- QDebug operator<<( QDebug, lastfm::ws::Error );